renadexer/listdir.php

465 lines
9.2 KiB
PHP

<?php
// do NOT append / at the end
// Eg: /home/will/Downloads
$folder_base = "/home/will/Downloads";
$items_per_page = 20;
$max_results = 100; // will break; once we reach limit, -1 for no limit
$motd = "get fucked";
$script_name = "listdir.php";
function do_error($code, $title, $text, $motd, $script_name){
http_response_code($code);
echo
'<!DOCTYPE html>' .
'<html lang="en">' .
'<head>' .
'<title>' . $title . '</title>' .
'</head>' .
'<body>' .
'<h1>' . $title . '</h1>' .
'<p>' . $text . '<br><ul><li><a href="' . $script_name . '">Go home yankee!</a></ul></li></p>' .
'<hr>' .
$motd .
'</body>' .
'</html>';
die();
}
function do_filesize($path, $is_dir){
if($is_dir){
return null;
}
$filesize = filesize($path);
if($filesize === 0){
echo "0.00 B";
}
$s = ["B", "KB", "MB", "GB", "TB", "PB"];
$e = floor(log($filesize, 1024));
return round($filesize / pow(1024, $e), 2) . " " . $s[$e];
}
function do_header($title, $search, $check, $is_search, $script_name, $up){
echo
'<!DOCTYPE html>' .
'<html lang="en">' .
'<head>' .
'<title>' . $title . '</title>' .
'</head>' .
'<body>' .
'<h1>' . $title . '</h1>' .
'<form autocomplete="off" method="GET">' .
'<input type="text" name="query" placeholder="search query" value="' . $search . '">' .
' <input type="submit" value="search"><br>' .
'<input id="and" type="radio" name="filter" value="and"' . ($check === "and" ? " checked" : "") . '>' .
'<label for="and">AND </label>' .
'<input id="or" type="radio" name="filter" value="or"' . ($check === "or" ? " checked" : "") . '>' .
'<label for="or">OR </label>' .
'<input id="pcre" type="radio" name="filter" value="pcre"' . ($check === "pcre" ? " checked" : "") . '>' .
'<label for="pcre">PCRE </label>' .
'</form><br>' .
($is_search === true ? '<a href="' . $script_name . '">&lt; Go back to index</a><br><br>' : "") .
($up !== false ? '<a href="' . $up . '">^ Go up</a><br><br>' : "") .
'<table>' .
'<tr>' .
($is_search === false ? '<th></th>' : "") .
'<th>' .
'type' .
'</th>' .
'<th>' .
'name' .
'</th>' .
'<th>' .
'size' .
'</th>' .
($is_search === false ? '<th></th>' : "") .
'</tr>' .
'<tr>' .
'<th colspan="5"><hr></th>' .
'</tr>';
}
// handle search
if(isset($_GET["query"])){
$query =
(
isset($_GET["query"]) &&
!empty(trim($_GET["query"])) &&
is_string($_GET["query"])
) ? trim($_GET["query"]) : false;
$filter =
(
isset($_GET["filter"]) &&
(
$_GET["filter"] == "and" ||
$_GET["filter"] == "or" ||
$_GET["filter"] == "pcre"
)
) ? $_GET["filter"] : "and";
$folder_base_strlen = strlen($folder_base);
if($query === false){
do_error(
400,
"Search query is empty",
"You need to search for something you dumb shit!",
$motd,
$script_name
);
}
$query_escaped = htmlspecialchars($query);
do_header("search results for &quot;" . $query_escaped . "&quot;", $query_escaped, $filter, true, $script_name, false);
// prepare search cmp
$query_arr =
explode(
" ",
preg_replace(
'/ +/',
" ",
$query
)
);
$dir_iterator = new RecursiveDirectoryIterator($folder_base, FilesystemIterator::SKIP_DOTS);
$iterator = new RecursiveIteratorIterator($dir_iterator, RecursiveIteratorIterator::SELF_FIRST);
$matches = 0;
while(true){
$iterator->next();
if(!$iterator->valid()){
break;
}
$filename = $iterator->getFilename();
switch($filter){
case "and":
$check = true;
foreach($query_arr as $q){
if(stripos($filename, $q) === false){
$check = false;
break;
}
}
break;
case "or":
$check = false;
foreach($query_arr as $q){
if(stripos($filename, $q) !== false){
$check = true;
break;
}
}
break;
case "pcre":
$match = preg_match($query, $filename);
if($match === false){
if(preg_last_error() !== PREG_NO_ERROR){
// regex error
echo
'<tr><td colspan="3"><b>RegEx error: ' . preg_last_error_msg() . '</b></td></tr><tr><td colspan="3"><hr></td></tr>' .
'</table><br>' .
$motd .
'</body>' .
'</html>';
die();
}
continue 2;
}elseif($match === 1){
$check = true;
}else{
$check = false;
}
break;
}
if($check){
$matches++;
$internal_path = $iterator->getPath() . "/" . $filename;
$fullpath =
substr_replace(
$internal_path,
"",
0,
$folder_base_strlen
);
$is_dir = is_dir($internal_path);
echo
'<tr>' .
'<td>' . ($is_dir ? "&lt;DIR&gt;" : "&lt;FILE&gt;") . '</td>' .
'<td>' .
'<a href="' . $script_name . '?path=' . urlencode($fullpath) . '">' .
htmlspecialchars(
$filename
) .
'</a>' .
'</td>' .
'<td>' . do_filesize($internal_path, $is_dir) . '</td>' .
'</tr>';
}
if($matches === $max_results){
break;
}
}
// pagination
echo
'<tr><td colspan="3"><hr></td></tr>' .
'<td colspan="3"><center>Found ' . $matches . ' matches (max=' . $max_results . ')</center></td>' .
'</tr></table><br>' .
$motd .
'</body>' .
'</html>';
die();
}
$path =
(
isset($_GET["path"]) &&
is_string($_GET["path"]) &&
!empty($_GET["path"])
)
? $_GET["path"] : "";
$realpath = realpath($folder_base . "/" . $path);
// file or folder does not exist
if($realpath === false){
do_error(
404,
"Nobody here but us chickens!",
'Nothing exists under <b>' . htmlspecialchars("/" . ltrim($path, "/")) . '</b>',
$motd,
$script_name
);
}
// path traversal exploit
if(strpos($realpath, $folder_base) !== 0){
do_error(
403,
"Get off my lawn",
'You do not have access to this shit',
$motd,
$script_name
);
}
// handle GETs to files and folders
if(is_dir($realpath)){
// handle directory
$page =
(
isset($_GET["page"]) &&
is_numeric($_GET["page"]) &&
$_GET["page"] > 0
)
? (int)$_GET["page"] : 1;
$relative_path = htmlspecialchars("/" . ltrim(str_replace($folder_base, "", $realpath), "/"));
$iterator = new FilesystemIterator($realpath);
try{
$iterator->seek(($page - 1) * $items_per_page);
}catch(OutOfBoundsException){
do_error(
400,
"Out of bounds",
"You requested a page index that does not exist you overweight buffoon",
$motd,
$script_name
);
}
if($relative_path == "/"){
$up = false;
}else{
$up = explode("/", $relative_path);
unset($up[count($up) - 1]);
$up = $script_name . "?path=" . urlencode(implode("/", $up));
}
do_header("index of " . $relative_path, "", "and", false, "", $up);
for($i=0; $i<$items_per_page; $i++){
if(!$iterator->valid()){
// reached end of file list
break;
}
$filename = $iterator->getFilename();
$absolute = $realpath . "/" . $filename;
$is_dir = is_dir($absolute);
echo
'<tr><td></td>' .
'<td>' . ($is_dir ? "&lt;DIR&gt;" : "&lt;FILE&gt;") . '</td>' .
'<td>' .
'<a href="' . $script_name . '?path=' . urlencode(rtrim($relative_path, "/") . "/" . $filename) . '">' .
htmlspecialchars(
$filename
) .
'</a>' .
'</td>' .
'<td>' . do_filesize($absolute, $is_dir) .
'</td><td></td>' .
'</tr>';
$iterator->next();
}
// pagination
echo
'<tr>' .
'<td colspan="5"><hr></td>' .
'</tr>' .
'<tr>';
// previous page check
if($page !== 1){
echo
'<td>' .
'<a href="' . $script_name . '?path=' . urlencode($path) . '&page=' . ($page - 1) . '">&lt;PREV&gt;</a>' .
'</td>';
}else{
echo '<td>&lt;END&gt;</td>';
}
echo '<td colspan="3"><center>Page ' . $page . '</center></td>';
// next page check
$iterator->next();
if($iterator->valid()){
echo
'<td>' .
'<a href="' . $script_name . '?path=' . urlencode($path) . '&page=' . ($page + 1) . '">&lt;NEXT&gt;</a>' .
'</td>';
}else{
echo '<td>&lt;END&gt;</td>';
}
echo
'</tr></table><br>' .
$motd .
'</body>' .
'</html>';
die();
}elseif(is_file($realpath)){
// handle file
set_time_limit(0);
ob_end_clean();
// tell browser you can seek the file
header("Accept-Ranges: bytes");
header('Content-Disposition: inline; filename="' . rawurlencode(basename($realpath)) . '"');
// report right file type
header("Content-Type: " . mime_content_type($realpath));
$filesize = filesize($realpath);
// detect content range
$headers = getallheaders();
$handle = fopen($realpath, "r");
if(isset($headers["Range"])){
// do range
preg_match(
'/^bytes=([0-9]*)-/',
$headers["Range"],
$matches
);
$range = isset($matches[1]) ? (int)$matches[1] : null;
if(
$range !== null &&
$range >= 0 &&
$range < $filesize
){
http_response_code(206); // partial content
header("Content-Range: bytes " . $range . "-" . ($filesize - 1) . "/" . $filesize);
header("Content-Length: " . $filesize - $range);
fseek($handle, $range);
}else{
http_response_code(416); // range not satisfiable
die();
}
}else{
header("Content-Length: " . $filesize);
}
fpassthru($handle);
die();
}else{
// what the fuck
do_error(
400,
"What the fuck",
'You requested something that is neither a file nor a directory, what the actual fuck',
$motd,
$script_name
);
}
?>