snips/abysslistv1.php

616 lines
17 KiB
PHP

<?php
ini_set('display_errors', 'on');
error_reporting(-1);
define('FL_TITLE', 'Abyss');
define('FL_ROOT_NAME', 'abyss');
define('FL_FILE_DATE_FORMAT', 'Y-m-d H:i:s P');
define('FL_ALLOW_PATH_INFO', true);
define('FL_HIDDEN_PATH', [
'robots.txt',
'index.php',
'favicon.ico',
]);
define('FL_ROOT', __DIR__);
define('FL_DIR_SEP', DIRECTORY_SEPARATOR);
define('FL_ICON_ICO', "MSIcon\0");
define('FL_ICON_ICD', 'image');
define('FL_ICON_ICE', [
'ico',
'image/x-icon',
'image/vnd.microsoft.icon',
]);
define('FL_ICON_EXE', '/?_flst_pe_ico=%s');
define('FL_ICON_EXP', "Win32\0");
define('FL_ICON_EXD', 'application_xp');
define('FL_ICON_EXW', [
'exe',
'msi',
'application/x-dosexec',
'application/x-msi',
]);
define('FL_ICON_FMT', '//static.flash.moe/images/silk/%s.png');
define('FL_ICON_DIR', 'folder');
define('FL_ICON_DEF', 'page_white');
define('FL_ICON_ARC', 'page_white_zip');
define('FL_ICON_MAP', [
'css' => 'page_white_code',
'js' => 'script_code',
'sql' => 'page_white_database',
'ftm' => 'music',
'htm' => 'page_white_code',
'html' => 'page_white_code',
'text/html' => 'page_white_code',
'xml' => 'page_white_code',
'text/xml' => 'page_white_code',
'png' => 'image',
'jpg' => 'image',
'jpeg' => 'image',
'gif' => 'image',
'ico' => 'image',
'bmp' => 'image',
'image/png' => 'image',
'image/jpeg' => 'image',
'image/gif' => 'image',
'image/x-icon' => 'image',
'image/vnd.microsoft.icon' => 'image',
'ttf' => 'font',
'application/font-sfnt' => 'font',
'php' => 'page_white_php',
'text/x-php' => 'page_white_php',
'zip' => 'page_white_zip',
'7z' => 'page_white_zip',
'rar' => 'page_white_zip',
'xz' => 'page_white_zip',
'application/x-7z-compressed' => 'page_white_zip',
'application/x-xz' => 'page_white_zip',
'application/x-rar' => 'page_white_zip',
'application/zip' => 'page_white_zip',
'txt' => 'page_white_text',
'text/plain' => 'page_white_text',
'msi' => 'application_xp',
'exe' => 'application_xp',
'application/x-dosexec' => 'application_xp',
'application/x-msi' => 'application_xp',
'cab' => 'box',
'themepack' => 'box',
'application/vnd.ms-cab-compressed' => 'box',
'application/x-windows-themepack' => 'box',
'iso' => 'page_white_cd',
'application/x-iso9660-image' => 'page_white_cd',
'mkv' => 'film',
'mp4' => 'film',
'm4v' => 'film',
'video/mp4' => 'film',
'rm' => 'film',
'pdf' => 'page_white_acrobat',
'application/pdf' => 'page_white_acrobat',
'swf' => 'page_white_flash',
'ogg' => 'music',
'audio/ogg' => 'music',
'mid' => 'music',
]);
function flGetIcon(string $path, bool $skipSpecial = false): string {
if(is_dir($path)) {
if($path !== '..') {
$icoPath = $path . '/_dir.ico';
if(is_file($icoPath))
return FL_ICON_ICO . $icoPath;
$icoPath = $path . '/_icon.txt';
if(is_file($icoPath)) {
$icoName = trim(file_get_contents($icoPath));
if($icoName !== '')
return $icoName;
}
}
return FL_ICON_DIR;
}
$mime = @mime_content_type($path);
if(!$skipSpecial) {
if(in_array($mime, FL_ICON_ICE))
return FL_ICON_ICO . $path;
if(in_array($mime, FL_ICON_EXW))
return FL_ICON_EXP . $path;
}
$name = array_key_exists($mime, FL_ICON_MAP) ? FL_ICON_MAP[$mime] : '';
if($name !== '')
return $name;
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if($ext === '')
return FL_ICON_DEF;
if(!$skipSpecial) {
if(in_array($ext, FL_ICON_ICE))
return FL_ICON_ICO . $path;
if(in_array($ext, FL_ICON_EXW))
return FL_ICON_EXP . $path;
}
// for split archives
if(ctype_digit($ext))
return FL_ICON_ARC;
if(array_key_exists($ext, FL_ICON_MAP))
return FL_ICON_MAP[$ext];
return FL_ICON_DEF;
}
function flGetIconPath(string $name): string {
if(str_starts_with($name, FL_ICON_ICO))
return '/' . substr($name, strlen(FL_ICON_ICO));
if(str_starts_with($name, FL_ICON_EXP))
return sprintf(FL_ICON_EXE, substr($name, strlen(FL_ICON_EXP)));
return sprintf(FL_ICON_FMT, $name);
}
function flGetIconAlt(string $name): string {
if(str_starts_with($name, FL_ICON_ICO))
return FL_ICON_ICD;
if(str_starts_with($name, FL_ICON_EXP))
return FL_ICON_EXD;
return $name;
}
function flPathClean(string $path): string {
return trim(parse_url(rawurldecode($path), PHP_URL_PATH), FL_DIR_SEP);
}
function flPathExplode(string $path): array {
return explode(FL_DIR_SEP, $path);
}
function flPathImplode(array $parts): string {
return implode(FL_DIR_SEP, $parts);
}
function flPathResolveAbsolute(array $parts): array {
$absParts = [];
foreach($parts as $part) {
if($part === '.')
continue;
else if($part === '..')
array_pop($absParts);
else
$absParts[] = $part;
}
return $absParts;
}
function flPathEnsureAbsolute(array $parts): bool {
foreach($parts as $part)
if($part === '.' || $part === '..')
return false;
return true;
}
if(isset($_GET['_flst_pe_ico']) && is_string($_GET['_flst_pe_ico'])) {
$flExePathParts = flPathExplode(flPathClean($_GET['_flst_pe_ico']));
if(!flPathEnsureAbsolute($flExePathParts)) {
http_response_code(404);
exit;
}
$flExePath = FL_ROOT . FL_DIR_SEP . flPathImplode($flExePathParts);
if(!is_file($flExePath)) {
http_response_code(404);
exit;
}
$output = shell_exec('wrestool -x -t14 ' . escapeshellarg($flExePath));
if(empty($output)) {
$iconName = FL_ICON_MAP[pathinfo($flExePath, PATHINFO_EXTENSION)] ?? FL_ICON_DEF;
header('Location: ' . sprintf(FL_ICON_FMT, $iconName));
exit;
}
header('Content-Type: image/x-icon');
echo $output;
exit;
}
if(isset($_GET['_flst_swf']) && is_string($_GET['_flst_swf'])) {
$swfPath = htmlspecialchars(trim($_GET['_flst_swf'], '/'));
$baseName = basename($swfPath);
echo <<<HTML
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>{$baseName}</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
box-sizing: border-box;
position: relative;
outline-style: none;
}
html, body {
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="//static.flash.moe/lib/ruffle/2023-06-08/ruffle.js"></script>
<script>
window.RufflePlayer = window.RufflePlayer || {};
window.addEventListener('DOMContentLoaded', function() {
var ruffle = window.RufflePlayer.newest();
var player = ruffle.createPlayer();
document.body.appendChild(player);
player.load('{$swfPath}');
player.style.width = '100%';
player.style.height = '100%';
});
</script>
</body>
</html>
HTML;
return;
}
if(isset($_GET['_flst_asset']) && is_string($_GET['_flst_asset'])) {
$flAssetName = $_GET['_flst_asset'];
unset($flAssetBody, $flAssetType);
if($flAssetName === 'style.css') {
$flAssetType = 'text/css; charset=utf-8';
$flAssetBody = <<<EOF
* {
margin: 0;
padding: 0;
box-sizing: border-box;
position: relative;
outline-style: none !important;
}
html, body {
width: 100%;
height: 100%;
}
body {
font: 12px/20px Tahoma, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif;
background-color: #111;
color: #fff;
}
.clear {
clear: both;
}
h1, h2 {
line-height: 1.5em;
}
h2 a {
color: #fff;
text-decoration: none;
}
h2 a:hover,
h2 a:focus {
text-decoration: underline;
}
.header {
margin: 0 auto;
max-width: 1000px;
padding: 10px 0;
}
.footer {
font-size: .9em;
margin: 5px auto;
text-align: center;
opacity: .4;
}
.listing {
margin: 0 auto;
max-width: 1000px;
background-color: #222;
}
.entry {
display: block;
text-decoration: none;
color: inherit;
margin: 2px;
border-width: 0;
background-color: #222;
transition: background-color .2s, box-shadow .2s;
}
div.entry {
border-bottom: 1px solid #444;
position: sticky;
top: 0;
z-index: 1000;
}
a.entry:hover,
a.entry:focus {
background-color: #444;
box-shadow: 0 0 10px #222;
}
a.entry:active {
background-color: #333;
box-shadow: 0 0 10px #222;
}
.entry-icon {
width: 24px;
height: 24px;
line-height: 20px;
text-align: center;
margin: 3px;
float: left;
}
.entry-icon img {
vertical-align: middle;
border: 0;
}
.entry-name,
.entry-size,
.entry-mod {
padding: 5px;
}
.entry-name {
float: left;
min-width: 300px;
}
.entry-size {
float: right;
width: 100px;
text-align: right;
}
.entry-mod {
float: right;
max-width: 250px;
width: 100%;
text-align: center;
}
div > .entry-mod,
div > .entry-size {
text-align: left;
}
@media (max-width: 700px) {
.entry-mod,
.entry-size {
display: none;
}
}
EOF;
}
if(isset($flAssetBody)) {
// todo: cache control
http_response_code(200);
header('Content-Disposition: inline; filename="' . $flAssetName . '"');
if(isset($flAssetType)) {
header('Content-Type: ' . $flAssetType);
}
echo $flAssetBody;
} else {
http_response_code(404);
}
exit;
}
$flRoot = FL_ROOT . FL_DIR_SEP;
$flForceBrowse = FL_ALLOW_PATH_INFO && !empty($_GET['browse']);
$flServerKey = FL_ALLOW_PATH_INFO && substr($_SERVER['REQUEST_URI'], 0, strlen($_SERVER['SCRIPT_NAME'])) === $_SERVER['SCRIPT_NAME'] ? 'PATH_INFO' : 'REQUEST_URI';
$flLocationNeedsPrefix = $flForceBrowse || $flServerKey === 'PATH_INFO';
$flLocationPrefix = $flLocationNeedsPrefix ? $_SERVER['SCRIPT_NAME'] : '';
$flOriginalLocation = flPathClean(
isset($_GET['path']) && is_string($_GET['path'])
? $_GET['path']
: $_SERVER[$flServerKey]
);
$flLocationParts = flPathExplode($flOriginalLocation);
$flLocationAbsolute = flPathImplode(flPathResolveAbsolute($flLocationParts));
$flLocation = flPathImplode($flLocationParts);
if($flLocation !== $flLocationAbsolute) {
header(sprintf('Location: %s%s%s', $flLocationPrefix, FL_DIR_SEP, $flLocationAbsolute));
exit;
}
$flLocationParent = dirname($flLocation);
$flFullLocation = rtrim($flRoot . $flLocation, '/');
$flHeaderFile = $flFullLocation . '/_header.html';
if(!is_dir($flFullLocation)) {
http_response_code(404);
echo 'Not Found';
exit;
}
$flDirs = [];
$flFiles = [];
foreach(glob($flFullLocation . '/*') as $entry) {
$entryRelative = substr($entry, strlen($flRoot));
if(in_array($entryRelative, FL_HIDDEN_PATH))
continue;
$isDir = is_dir($entry);
if($isDir && is_file($entryRelative . '/_hidden.txt'))
continue;
$info = [
'type' => $isDir ? 'dir' : (is_file($entry) ? 'fil' : 'unk'),
'path' => $entryRelative,
'path_full' => $entry,
'name' => basename($entry),
];
if($info['type'] === 'fil') {
$info['size'] = filesize($entry);
$info['ext'] = strtolower(pathinfo($entry, PATHINFO_EXTENSION));
$info['mod'] = filemtime($entry);
if($info['ext'] === 'swf')
$info['path'] = '?_flst_swf=' . rawurlencode($info['path']);
}
if($info['type'] === 'dir') {
$info['index'] = !(is_file($entry . '/index.php') || is_file($entry . '/index.html'));
$flDirs[] = $info;
} else
$flFiles[] = $info;
}
function sort_insensitive($a, $b) { return strcmp(strtolower($a['path']), strtolower($b['path'])); }
usort($flDirs, 'sort_insensitive');
usort($flFiles, 'sort_insensitive');
$flEntries = array_merge($flDirs, $flFiles);
function flByteSymbol(int $bytes, bool $decimal = true, array $symbols = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']): string {
if($bytes < 1)
return 'Zero Bytes';
$divider = $decimal ? 1000 : 1024;
$exp = floor(log($bytes) / log($divider));
$bytes = $bytes / pow($divider, floor($exp));
$symbol = $symbols[$exp];
return sprintf("%.2f %s%sB", $bytes, $symbol, $symbol !== '' && !$decimal ? 'i' : '');
}
function flBuildListingEntry(array $columns, string $linkTarget = ''): string {
$eEntries = [];
foreach($columns as $name => $body)
$eEntries[] = sprintf('<div class="entry-%s">%s</div>', $name, $body);
return sprintf(
"<%1\$s class=\"entry\"%2\$s>\r\n%3\$s\r\n<div class=\"clear\"></div>\r\n</%1\$s>",
$linkTarget === '' ? 'div' : 'a',
$linkTarget === '' ? '' : sprintf(' href="%s"', $linkTarget),
implode("\r\n", $eEntries)
);
}
function flBuildListingFileEntry($item): string {
return '';
}
function flBuildListing(array $columns, array $items): string {
// columns -> [name, title, itemKey, itemFormat]
$header = [];
foreach($columns as $column)
$header[$column['name']] = $column['title'];
$lEntries = [flBuildListingEntry($header)];
foreach($items as $item)
$lEntries[] = flBuildListingFileEntry($item);
return sprintf("<div class=\"listing\">\r\n%s\r\n</div>", implode("\r\n", $lEntries));
}
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title><?=(empty($flLocation) ? FL_TITLE : ('/' . $flLocation . ' - ' . FL_TITLE));?></title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
<link href="/?_flst_asset=style.css" type="text/css" rel="stylesheet"/>
</head>
<body>
<div class="header">
<?php if(!empty($flLocation)): ?>
<h2>[<a href="/"><?=FL_ROOT_NAME;?></a>] <?php $flLocationSoFar = ''; foreach($flLocationParts as $flLocationPart) { $flLocationSoFar .= '/' . $flLocationPart; echo '/<a href="' . $flLocationSoFar . '">' . $flLocationPart . '</a>'; } ?></h2>
<?php endif;
if(is_file($flHeaderFile))
echo file_get_contents($flHeaderFile);
?>
</div>
<div class="listing">
<div class="entry">
<div class="entry-icon"></div>
<div class="entry-name">Name</div>
<div class="entry-size">Size</div>
<div class="entry-mod">Last modified</div>
<div class="clear"></div>
</div>
<?php
if(!empty($flLocation)):
$iconName = flGetIcon('..');
?>
<a class="entry" href="<?='/' . trim($flLocationPrefix . '/' . ($flLocationParent === '.' ? '' : $flLocationParent), '/');?>">
<div class="entry-icon"><img src="<?=flGetIconPath($iconName);?>" alt="<?=flGetIconAlt($iconName);?>"/></div>
<div class="entry-name">../</div>
<div class="entry-size"></div>
<div class="entry-type"></div>
<div class="clear"></div>
</a>
<?php
endif;
foreach($flEntries as $entry):
$isDir = $entry['type'] === 'dir';
$isFile = $entry['type'] === 'fil';
if(!$isDir && !$isFile)
continue;
$name = $entry['name'] . ($isDir ? '/' : '');
if(str_starts_with($name, '_'))
continue;
$iconName = flGetIcon($entry['path']);
$href = ($isDir && ($flForceBrowse || ($flLocationNeedsPrefix && $entry['index'])) ? $flLocationPrefix : '') . '/' . $entry['path'];
?>
<a class="entry" href="<?=$href;?>">
<div class="entry-icon">
<img src="<?=flGetIconPath($iconName);?>" alt="<?=flGetIconAlt($iconName);?>" width="16" height="16"/>
</div>
<div class="entry-name"><?=$name;?></div>
<div class="entry-size"><?php if($isFile) echo flByteSymbol($entry['size']); ?></div>
<div class="entry-mod"><?php if($isFile) echo date(FL_FILE_DATE_FORMAT, $entry['mod']); ?></div>
<div class="clear"></div>
</a>
<?php endforeach; ?>
</div>
<div class="footer">
flash.moe 2014-<?=date('Y');?>
</div>
</body>
</html>