wfe/wfe.php
2021-12-20 18:18:08 +01:00

398 lines
12 KiB
PHP

<?php
/*
* __
* __ __/ _| ___
* \ \ /\ / / |_ / _ \
* \ V V /| _| __/
* \_/\_/ |_| \___|
*
* Web File Explorer
* This is free software!
*
* TODO
* - Sorting
* - Upload
* - Download of gzipped folders
* - REST API (?)
* - Mobile version
*/
# Debug
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
# Configuration
// Code version
$version = "1.1.0";
// Set to 'false' to disable security
$jail = false;
# Constants
// Folder in which the script runs
define("FOLDER", getcwd());
# Main variables (warning! Editing below this line is potentially dangerous!)
// The script itself
$url = $_SERVER["PHP_SELF"];
$script = basename($url);
// Working directory
$path = getcwd();
// Download file
$download = null;
// Open directory
$directory = null;
// Place for 'home' and 'parent directory' items
$default = array();
// Place for directories
$directories = array();
// Place for files
$files = array();
# Security stuff
// Sanitize and deobfuscate qs parameters
if (isset($_GET["f"])) {
$download = filter_var($_GET["f"], FILTER_SANITIZE_STRING);
$download = deobfuscate($download);
}
if (isset($_GET["d"])) {
$directory = filter_var($_GET["d"], FILTER_SANITIZE_STRING);
$directory = deobfuscate($directory);
}
// Canonicalized absolute pathname
// Does not follow symbolic links
$path = realpath($path) . "/";
if ($directory !== null) {
$path = realpath($directory) . "/";
}
// Document root directory
$root = substr($path, 0, strlen(FOLDER));
if ($download !== null) {
$root = substr($download, 0, strlen(FOLDER));
}
// Do not access the entire file system!
if ($jail === true && $root !== FOLDER) {
header("Location: $url");
exit;
}
# Main part
// Download file
if ($download !== null) {
// See https://code-boxx.com/php-read-file/
// Start output buffer
ob_start();
// HTTP headers to force download
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"" . basename($download) . "\"");
header("Expires: 0");
header("Cache-Control: must-revalidate");
header("Pragma: public");
header("Content-Length: " . filesize($download));
// Output all the headers and stop buffering
ob_end_flush();
// Read and output file directly
readfile($download);
exit();
}
// Open directory
// See https://www.php.net/manual/en/function.readdir.php
if ($handle = opendir($path)) {
// Get each entry
while (false !== ($item = readdir($handle))) {
$items[] = $item;
}
// Close directory
closedir($handle);
} else {
// Unreadable data, or something worse
header("Location: $url");
exit;
}
// Loops through the array of items
for ($i = 0; $i < count($items); $i++) {
// Get item name
$name = $items[$i];
if ($name !== "." && $name !== "..") {
// Directory
if (is_dir($path . $name)) {
if (is_readable($path . $name)) {
$link = "?d=" . obfuscate($path . $name);
} else {
$link = null;
}
$directories[$i]["link"] = $link;
$directories[$i]["name"] = "<b>&#8600;</b> " . $name;
$directories[$i]["type"] = "Directory";
$directories[$i]["size"] = 0;
$directories[$i]["date"] = prettydate($path . $items[$i]);
}
// File
if (is_file($path . $name) && $name !== $script) {
if (is_readable($path . $name)) {
$link = "?f=" . obfuscate($path . $name);
} else {
$link = null;
}
$files[$i]["link"] = $link;
$files[$i]["name"] = "<b>&#9733;</b> " . $name;
$files[$i]["type"] = prettytype($path . $items[$i]);
$files[$i]["size"] = prettysize(filesize($path . $items[$i]));
$files[$i]["date"] = prettydate($path . $items[$i]);
}
}
}
// Home directory
$default[0]["link"] = $url;
$default[0]["name"] = "<b>&#9873;</b> Home";
$default[0]["type"] = "Directory";
$default[0]["size"] = 0;
$default[0]["date"] = prettydate(__DIR__);
// Parent directory
$default[1]["link"] = $url . "?d=" . obfuscate($path . "../");
$default[1]["name"] = "<b>&#8598;</b> Parent directory";
$default[1]["type"] = "Directory";
$default[1]["size"] = 0;
$default[1]["date"] = prettydate($path . "..");
// Sort arrays
$n = array_column($directories, "name");
$d = array_column($directories, "date");
array_multisort($n, SORT_ASC, $d, SORT_ASC, $directories);
$n = array_column($files, "name");
$d = array_column($files, "date");
array_multisort($n, SORT_ASC, $d, SORT_ASC, $files);
# Output
// Favicon
$png = <<<PNG
iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABHNCSV
QICAgIfAhkiAAAAAlwSFlzAAABGwAAARsBjfdO5QAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYX
BlLm9yZ5vuPBoAAAJMSURBVEiJxdZfaI9RGAfwj22IjGEthiimxJ3SSk2SXCk3lOLChQs3qymS3Y
ioFbmZGxcrV4ikkCTjRrlxofn/J4Qp/zMt82dzcc7b791se3+/31751uk973Oe5/mec55znufwnz
CuRP0pWIMlqIv27/EQnfiS6+xQi2P4joER2k90YHZepPPxbBTCoe0FFuRBfLEE0qSdHCtpNX6VQf
w5y3FFxng9KsuY8GQZBzeL+GuqP4A7eDyMXlccS/Ah6o8Jb6OT1pTsjMK2Hk7Jj0bZ2SynWSuGq/
F7LSXrSvUvpfpP4vd4EX4zUYdPOIUazMF9/EZ/JJkU9e7gQh6kCS75++TuxP5h5HvyJG4WVpc4fy
qc3Bq8S8n7sDZPYmjAPrzy9yofoQUz8iZNcBdbIlGfwuq3yjm2Q3ERt7EK04WtbsQ9Ocd2KGpxzu
Bt7kc7JuZBUIkVmDDCeIsQ6yfYOILONOwW8n3R2C6s5jlWlmIYUadQSleXYnja4AJ/HutRNYpNBZ
pwQEgwiX3rKDaDME8hdp14nXLyBoewKKXfgDZ0R51vuI6bCne76ArXiMWp/2XYiwepSd0Q8ne/kD
4vY4OQPhMsxtJiSbOwKxIlu/ADm/JyPhyW40ok68UR4XHXFydyQgjTmFElxK9JqL/9sXVgbkpvoc
Ld7sVBTC2HsEJ4yvYYnCi6sW4Uu834GHV/46WQ6bYo8v0+K848TXpLyFpZqFd49iStBzOLISZcl2
bhtTggFIFi0Z4ibVNm3BuwA+NLsKkWyue2cgj/Of4AiebN88P8fu4AAAAASUVORK5CYII=
PNG;
// CSS3 code
$css3 = <<<CSS3
body{background-color:DarkSlateGrey;color:FloralWhite;font:12pt "Lucida Console","Courier New",Monospace;margin:15pt}
a{text-decoration:none;color:Chartreuse}
b{color:BlanchedAlmond;font:lighter 14pt Sans-serif}
h1{background-color:LightSlateGray;border-radius:15pt 15pt 0 0;margin-bottom:-12pt;padding:4pt 0 16pt;text-align:center}
h1>span{color:DarkSlateGrey;font:35pt Sans-serif}
footer{font:11pt Sans-serif;margin-top:10pt;text-align:center}
footer>p{margin:0;padding-top:1pt}
.container{background-color:DarkSlateGrey;border:3pt solid LightSlateGray;border-radius:15pt;padding:20pt 0 24pt 28pt}
.flex-container{display:flex}
.flex-container>div{height:16pt;line-height:16pt;overflow:hidden}
CSS3;
// HTML5 code
// Useful link for SVG encoding: https://yoksel.github.io/url-encoder/
$html5 = <<<HTML5
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="data:image/png;base64,$png">
<title>$script</title>
<style>$css3</style>
</head>
<body>
<h1><span>$script</span></h1>
<div class="container">
<div class="flex-container" style="margin-bottom: 8pt">
<div style="flex-basis: 50%; font-weight: bolder; margin-left: 15pt">Name</div>
<div style="flex-basis: 20%; font-weight: bolder">Type</div>
<div style="flex-basis: 10%; font-weight: bolder">Size</div>
<div style="flex-basis: 20%; font-weight: bolder">Date</div>
</div>
HTML5;
echo $html5;
foreach ($default as $value) {
echo '<div class="flex-container">';
echo '<div style="flex-basis: 50%"><a href="' . $value["link"] . '">' . $value["name"] . '</a></div>';
echo '<div style="flex-basis: 20%; margin-left: 15pt">' . $value["type"] . '</div>';
echo '<div style="flex-basis: 10%">' . $value["size"] . '</div>';
echo '<div style="flex-basis: 20%">' . $value["date"] . '</div>';
echo '</div>';
}
foreach ($directories as $value) {
echo '<div class="flex-container">';
if ($value["link"] === null) {
echo '<div style="flex-basis: 50%; color: Pink">' . $value["name"] . '</div>';
} else {
echo '<div style="flex-basis: 50%"><a href="' . $value["link"] . '">' . $value["name"] . '</a></div>';
}
echo '<div style="flex-basis: 20%; margin-left: 15pt">' . $value["type"] . '</div>';
echo '<div style="flex-basis: 10%">' . $value["size"] . '</div>';
echo '<div style="flex-basis: 20%">' . $value["date"] . '</div>';
echo '</div>';
}
foreach ($files as $value) {
echo '<div class="flex-container">';
if ($value["link"] === null) {
echo '<div style="flex-basis: 50%; color: Pink">' . $value["name"] . '</div>';
} else {
echo '<div style="flex-basis: 50%"><a href="' . $value["link"] . '">' . $value["name"] . '</a></div>';
}
echo '<div style="flex-basis: 20%; margin-left: 13pt">' . $value["type"] . '</div>';
echo '<div style="flex-basis: 10%">' . $value["size"] . '</div>';
echo '<div style="flex-basis: 20%">' . $value["date"] . '</div>';
echo '</div>';
}
$html5 = <<<HTML5
</div>
<footer>
<p>Current path: <b>$path</b></p>
<p>$script &bull; Web File Explorer &bull; v$version</p>
</footer>
</body>
</html>
HTML5;
echo $html5;
# Functions
// String obfuscation
// ROT-13 + Zlib compression + base64 encoding
// See: https://stackoverflow.com/questions/2996049/how-to-compress-decompress-a-long-query-string-in-php
function obfuscate($str)
{
return rtrim(strtr(base64_encode(gzdeflate(str_rot13($str), 9)), '+/', '-_'), '=');
}
function deobfuscate($str)
{
return str_rot13(gzinflate(base64_decode(strtr($str, '-_', '+/'))));
}
// Prettifies file size
function prettysize($size)
{
if ($size < 1024) {
$size = $size;
} elseif (($size < 1048576) && ($size > 1023)) {
$size = round($size / 1024, 1) . "K";
} elseif (($size < 1073741824) && ($size > 1048575)) {
$size = round($size / 1048576, 1) . "M";
} else {
$size = round($size / 1073741824, 1) . "G";
}
return $size;
}
// Prettifies file type
function prettytype($file)
{
// Gets file extension
$extn = pathinfo($file, PATHINFO_EXTENSION);
switch ($extn) {
case "png":
$type = "PNG image";
break;
case "jpg":
$type = "JPEG image";
break;
case "jpeg":
$type = "JPEG image";
break;
case "svg":
$type = "SVG image";
break;
case "gif":
$type = "GIF image";
break;
case "ico":
$type = "Windows icon";
break;
case "txt":
$type = "Text file";
break;
case "log":
$type = "Log file";
break;
case "htm":
$type = "HTML file";
break;
case "html":
$type = "HTML file";
break;
case "xhtml":
$type = "HTML file";
break;
case "shtml":
$type = "HTML file";
break;
case "php":
$type = "PHP script";
break;
case "js":
$type = "Javascript file";
break;
case "css":
$type = "Stylesheet";
break;
case "pdf":
$type = "PDF document";
break;
case "xls":
$type = "Spreadsheet";
break;
case "xlsx":
$type = "Spreadsheet";
break;
case "doc":
$type = "Microsoft Word document";
break;
case "docx":
$type = "Microsoft Word document";
break;
case "zip":
$type = "ZIP archive";
break;
case "htaccess":
$type = "Apache config file";
break;
case "exe":
$type = "Windows executable";
break;
case "torrent":
$type = "BitTorrent file";
break;
default:
if ($extn !== "") {
$type = strtoupper($extn) . " file";
} else {
$type = "Unknown";
}
break;
}
return $type;
}
// Prettifies modification date
function prettydate($file)
{
return date("Y-m-d H:i:s", filemtime($file));
}