<?php
// Minimal, read-only file viewer with ACE (no save, no explorer, no execute)
$rootDir = __DIR__; // Root sandbox
$req = isset($_GET['file']) ? $_GET['file'] : ''; // e.g. ?file=path/to/file.php
// Sanitize: strip traversal & odd bytes
$req = str_replace(["\0", "..\\", "../", "\\", "\r", "\n"], "", $req);
$req = ltrim($req, "/"); // avoid absolute paths
$fullPath = realpath($rootDir . "/" . $req);
// Enforce jail: must resolve within $rootDir and be a regular file
$withinRoot = $fullPath && strpos($fullPath, realpath($rootDir)) === 0;
$isFile = $withinRoot && is_file($fullPath);
// Helpers
function aceMode($filename) {
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$m = [
'php'=>'php','js'=>'javascript','ts'=>'typescript','html'=>'html','htm'=>'html','css'=>'css',
'json'=>'json','xml'=>'xml','md'=>'markdown','py'=>'python','java'=>'java','cpp'=>'c_cpp',
'c'=>'c_cpp','h'=>'c_cpp','sql'=>'sql','yml'=>'yaml','yaml'=>'yaml','rb'=>'ruby','go'=>'golang',
'sh'=>'sh'
];
return $m[$ext] ?? 'text';
}
function isImageExt($filename) {
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
return in_array($ext, ['jpg','jpeg','png','gif','bmp','webp','svg']);
}
$filename = $isFile ? basename($fullPath) : '';
$isImage = $isFile && isImageExt($filename);
$fileContent = $isFile && !$isImage ? file_get_contents($fullPath) : '';
$aceMode = $isFile ? aceMode($filename) : 'text';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title><?= $isFile ? htmlspecialchars($filename) : 'Viewer' ?></title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.9/ace.js" integrity="sha512-y4mGQ0z0nQbI7oH2o+o4/6bQ8pcoE8JwGqchmF4Jm8a3s1aP3x8Sx2p3h2W9e4G6+o3bq0J1+Hu6lZy6VQ9x1w==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<style>
:root { --bg:#0f172a; --panel:#111827; --text:#e5e7eb; --muted:#9ca3af; --border:#334155; }
*{box-sizing:border-box} html,body{height:100%}
body{margin:0;background:var(--bg);color:var(--text);font:14px/1.5 ui-sans-serif,system-ui,Segoe UI,Roboto,Inter,sans-serif}
.wrap{display:flex;flex-direction:column;height:100%}
.bar{display:flex;align-items:center;gap:.75rem;padding:.75rem 1rem;background:var(--panel);border-bottom:1px solid var(--border)}
.name{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hint{margin-left:auto;font-size:.85rem;color:var(--muted)}
.pane{flex:1;min-height:0;display:flex}
#editor{flex:1}
.imgbox{flex:1;display:flex;align-items:center;justify-content:center;padding:1rem}
.imgbox img{max-width:100%;max-height:100%;border:1px solid var(--border);border-radius:8px;background:#fff}
.empty{padding:2rem;color:var(--muted)}
.bad{padding:1rem;color:#fecaca;background:#7f1d1d;border-bottom:1px solid #991b1b}
</style>
</head>
<body>
<div class="wrap">
<div class="bar">
<div class="name"><?= $isFile ? htmlspecialchars($req) : 'No file selected' ?></div>
<div class="hint">Read-only • Press Esc to close tab</div>
</div>
<?php if (!$isFile): ?>
<div class="bad">Invalid or missing file. Use <code>?file=relative/path.ext</code> within this directory.</div>
<div class="empty">Example: <code>?file=index.php</code></div>
<?php elseif ($isImage): ?>
<div class="pane">
<div class="imgbox">
<img
alt="<?= htmlspecialchars($filename) ?>"
src="data:image/<?= htmlspecialchars(pathinfo($filename, PATHINFO_EXTENSION)) ?>;base64,<?= base64_encode(file_get_contents($fullPath)) ?>">
</div>
</div>
<?php else: ?>
<div class="pane"><div id="editor"><?= htmlspecialchars($fileContent) ?></div></div>
<script>
const editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.session.setMode("ace/mode/<?= $aceMode ?>");
editor.setOptions({
readOnly: true,
highlightActiveLine: false,
highlightGutterLine: false,
showPrintMargin: false,
wrap: true,
});
editor.renderer.$cursorLayer.element.style.display = "none"; // hide caret
// Basic shortcuts: find only (read-only)
editor.commands.addCommand({
name:'find', bindKey:{win:'Ctrl-F',mac:'Command-F'},
exec: ed => ed.execCommand('find')
});
// Escape closes tab (no redirects)
document.addEventListener('keydown', e => {
if (e.key === 'Escape') window.close();
});
</script>
<?php endif; ?>
</div>
</body>
</html>