472 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			472 lines
		
	
	
		
			9.4 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 encode_path($path){
 | 
						|
		
 | 
						|
	return implode("/", array_map(function($v){
 | 
						|
		return urlencode($v);
 | 
						|
	}, explode("/", $path)));
 | 
						|
}
 | 
						|
 | 
						|
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){
 | 
						|
		
 | 
						|
		return "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=' . encode_path($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=" . encode_path(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=' . encode_path(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=' . encode_path($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=' . encode_path($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
 | 
						|
	);
 | 
						|
}
 | 
						|
?>
 |