398 lines
12 KiB
PHP
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>↘</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>★</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>⚑</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>↖</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 • Web File Explorer • 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));
|
|
}
|