465 lines
9.2 KiB
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 . '">< 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 "" . $query_escaped . """, $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 ? "<DIR>" : "<FILE>") . '</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 ? "<DIR>" : "<FILE>") . '</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) . '"><PREV></a>' .
|
|
'</td>';
|
|
}else{
|
|
|
|
echo '<td><END></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) . '"><NEXT></a>' .
|
|
'</td>';
|
|
}else{
|
|
|
|
echo '<td><END></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
|
|
);
|
|
}
|
|
?>
|