<?php 
    ###############################
    # Run with the following format
    ###############################
    # php -S HOST:PORT [-t DOC_ROOT] index.php
    #    e.g. php -S localhost:8080 -t /var/www/ index.php
    #    e.g. php -S localhost:8080 index.php # (uses current directory as DOC_ROOT)

    # Some other examples
    # https://stackoverflow.com/questions/14946964/php-built-in-server-any-way-to-configure-it-to-show-files-of-directory

    $BASE_DIR = $_SERVER['DOCUMENT_ROOT'];
    $request_url = explode("?",$_SERVER['REQUEST_URI'])[0];
    
    $RELATIVE_PATH = getFilePath(urldecode($request_url));
    $ABSOLUTE_PATH = realpath($BASE_DIR . $RELATIVE_PATH);
    $ERROR_PATH = "";
    $DEBUG = false;
    
    // For Windows (disclaimer: haven't actaully tested)
    function getFilePath($dir){
        return str_replace("/", DIRECTORY_SEPARATOR, urldecode($dir));
    }

    function getParents($file){
        if($file == DIRECTORY_SEPARATOR) return array();
        $base = basename($file);
        $parent = dirname($file);

        return array_merge(getParents($parent), array($base));
    }

    function urlencodePaths($paths){
        $output = "";
        foreach ($paths as $path) {
            $output .= urlencode($path) . "/";
        }
        return $output;
    }


    if($DEBUG){
        echo "<PRE>"; print_r($_SERVER);
        echo "RELATIVE_PATH: $RELATIVE_PATH<BR>";
        echo "ABSOLUTE_PATH: $ABSOLUTE_PATH<BR>";
        echo "REAL: " . realpath($ABSOLUTE_PATH) . "<BR>";        
    }

    # handle 404s and prevent escaping doc_root
    if(!file_exists($ABSOLUTE_PATH) || strpos($ABSOLUTE_PATH, $BASE_DIR) === FALSE){
        $ERROR_PATH = $RELATIVE_PATH;
        $RELATIVE_PATH = DIRECTORY_SEPARATOR;
        $ABSOLUTE_PATH = $BASE_DIR . $RELATIVE_PATH;
        http_response_code(404);
    }

    if($DEBUG){
        echo "RELATIVE_PATH: $RELATIVE_PATH<BR>";
        echo "ABSOLUTE_PATH: $ABSOLUTE_PATH<BR>";
        echo "REAL: " . realpath($ABSOLUTE_PATH) . "<BR>";        
    }

    if(!is_dir($ABSOLUTE_PATH)) {
        if(@$_GET['dl']) {
            header('Content-Disposition: attachment; filename="'. basename($ABSOLUTE_PATH) .'"');
            $mime = "application/octet-stream";
        }
        else {
            $mime = mime_content_type($ABSOLUTE_PATH);
        }
        header('Content-type: ' . $mime);
        readfile($ABSOLUTE_PATH);
        return true;
    }
    else if($RELATIVE_PATH != "" && $RELATIVE_PATH[strlen($RELATIVE_PATH)-1] != DIRECTORY_SEPARATOR){
        $RELATIVE_PATH .= DIRECTORY_SEPARATOR;
        $ABSOLUTE_PATH = $BASE_DIR . $RELATIVE_PATH;
    }

    $FOLDER_PATH = $RELATIVE_PATH;
    if(is_dir($ABSOLUTE_PATH))
        $PARENT_FOLDER_PATH = $FOLDER_PATH;
    else
        $PARENT_FOLDER_PATH = dirname($FOLDER_PATH);
    $PARENT_FOLDER_PATH_PARTS = getParents($FOLDER_PATH);

    if($DEBUG){
        echo "PARENT_FOLDER_PATH: $PARENT_FOLDER_PATH<BR>";
        echo "PARENT_FOLDER_PATH_PARTS: " . print_r($PARENT_FOLDER_PATH_PARTS, true) . "<BR>";
    }
?>
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Simple PHP Server</title>
    </head>
    <body>
        <h2>Directory listing for <?php echo $FOLDER_PATH; ?></h2>
        <HR>
        <?php if($ERROR_PATH) : ?>
            <h3><?php echo $ERROR_PATH; ?> does not exist</h3>
        <?php endif ?>
        <ul>
        <?php
            // order alphabetically but folders first
            $folders = [];
            $files = [];
            foreach (scandir($ABSOLUTE_PATH) as $file) {
                if(is_dir($ABSOLUTE_PATH . DIRECTORY_SEPARATOR . $file)){
                    $folders[] = $file;
                }
                else {
                    $files[] = $file;
                }
            }
            
            foreach (array_merge($folders,$files) as $file) {
                $file_path = $RELATIVE_PATH . $file;
                $absolute_file_path = realpath($BASE_DIR . $file_path);
                $display_name = htmlentities($file);

                if(is_dir($absolute_file_path)) {
                    if($file == ".") continue;
                    if($file == "..") $file_path = $PARENT_FOLDER_PATH;
                }

                $url = "/" . urlencodePaths($PARENT_FOLDER_PATH_PARTS) . urlencode($file);

                if(is_dir($absolute_file_path)){
                    echo "<li><a href='$url'>" . $display_name . "</a>";
                }
                else {
                    echo "<li>[<a href='$url?dl=1'>Download</a>] <a href='$url'>" . $display_name . "</a>";
                }

                if($DEBUG){
                    echo " || file: $file <BR>";
                    echo " || display_name: $display_name <BR>";
                    echo " ||   RELATIVE_PATH: $RELATIVE_PATH <BR>";
                    echo " ||   file_path: $file_path <BR>";
                    echo " ||  url: $url<BR>";
                    echo " ||    urlencode(parents): " . urlencodePaths($PARENT_FOLDER_PATH_PARTS) . " <BR>";
                    echo " ||    urlencode(file): " . urlencode($file) . " <BR>";
                    echo " || parent: "  . print_r($PARENT_FOLDER_PATH_PARTS, true). " </li>";
                }
            }
        ?>
        </ul>
        <HR>
    </body>
</html>