<?php
require_once('config.php');

/**
 * @author Gernot WALZL
 */
class FileSystemObject {

    function is_hidden($hidden_dirnames, $dir) {
        $result = false;
        if (is_array($hidden_dirnames)) {
            if (in_array($dir, $hidden_dirnames)) {
                $result = true;
            }
        }
        return $result;
    }

    function read_dirs($path='.', $hidden_dirnames=null) {
        $result = null;
        if ($handle = opendir(dirname(__FILE__).'/'.$path)) {
            $result = array();
            while (false !== ($entry = readdir($handle))) {
                if (substr($entry,0,1) != '.') {
                    if (is_dir($path.'/'.$entry)) {
                        if (!$this->is_hidden($hidden_dirnames, $entry)) {
                            array_push($result, $path.'/'.$entry);
                        }
                    }
                }
            }
            closedir($handle);
            sort($result);
        }
        return $result;
    }

    function read_files($path='.') {
        $result = null;
        if ($handle = opendir(dirname(__FILE__).'/'.$path)) {
            $result = array();
            while (false !== ($entry = readdir($handle))) {
                if (substr($entry,0,1) != '.') {
                    if (is_file($path.'/'.$entry) &&
                            is_readable($path.'/'.$entry)) {
                        array_push($result, $path.'/'.$entry);
                    }
                }
            }
            closedir($handle);
            sort($result);
        }
        return $result;
    }

    function get_file_ext($filepath) {
        $result = null;
        $pathinfo = pathinfo($filepath);
        if (isset($pathinfo['extension'])) {
            $result = $pathinfo['extension'];
        }
        return $result;
    }

    function read_preview($filepath, $num_lines) {
        $result = '';
        $comment = null;
        $handle = fopen($filepath, 'r');
        $cnt = 0;
        while ($cnt < $num_lines && !feof($handle)) {
            $buffer = fgets($handle, 4096);
            if ($cnt == 0 && substr($buffer,0,2) == '#!') {
                $comment = '#';
            } else {
                if (empty($comment)) {
                    $buffer = trim($buffer);
                    if (strlen($buffer) > 0) {
                        $result .= $buffer."\n";
                        $cnt += 1;
                    }
                } else {
                    if (substr($buffer,0,strlen($comment)) == $comment) {
                        $buffer = trim(str_replace($comment, '', $buffer));
                        if (strlen($buffer) > 0) {
                            $result .= $buffer."\n";
                            $cnt += 1;
                        }
                    }
                }
            }
        }
        fclose($handle);
        return $result;
    }

    function exists_file($path, $filenames) {
        $result = false;
        foreach ($filenames as $filename) {
            if (is_readable($path.'/'.$filename)) {
                $result = true;
                break;
            }
        }
        return $result;
    }

}

/**
 * @author Gernot WALZL
 */
class HTML {

    protected $config = null;
    protected $fso = null;
    protected $list_files = true;

    function __construct($config, $fso) {
        $this->config = $config;
        $this->fso = $fso;
        $list_files = $this->config->list_files;
    }

    function println($msg='') {
        print($msg."\n");
    }

    function print_head($nav='.') {
        $this->println('<!DOCTYPE html>');
        $this->println('<html lang="en">');
        $this->println('<head>');
        $this->println('<meta charset="utf-8" />');
        $title = $this->config->title;
        if (!empty($nav)) {
            $subtitle = str_replace('_', ' ', basename($nav));
            if (substr($subtitle, 0, 1) != '.') {
                $title = $subtitle.' - '.$title;
            }
        }
        $this->println('<title>'.$title.'</title>');
        if (!empty($this->config->meta_description)) {
            $this->println('<meta name="description" content="'.
                    $this->config->meta_description.'" />');
        }
        if (!empty($this->config->meta_keywords)) {
            $this->println('<meta name="keywords" content="'.
                    $this->config->meta_keywords.'" />');
        }
        $this->println('<meta name="viewport" '.
                'content="width=device-width, initial-scale=1.0" />');
        if (!empty($this->config->stylesheets)) {
            foreach ($this->config->stylesheets as $stylesheet) {
                $href = $stylesheet.'?mtime='.filemtime($stylesheet);
                $this->println('<link rel="stylesheet" href="'.$href.'" />');
            }
        }
        if (!empty($this->config->rss)) {
            $this->println('<link rel="alternate" type="application/rss+xml" '.
                    'title="RSS Feed" href="'.$this->config->rss.'" />');
        }
        if (!empty($this->config->scripts)) {
            foreach ($this->config->scripts as $script) {
                $src = $script.'?mtime='.filemtime($script);
                $integrity = 'sha256-'.base64_encode(
                    hash_file('sha256', $script, true));
                $this->println('<script src="'.$src.'" '.
                    'integrity="'.$integrity.'"></script>');
            }
        }
        $this->println('</head>');
        $this->println('<body>');
        if (isset($this->config->header)) {
            if (is_readable($this->config->header)) {
                $this->println('<header>');
                readfile($this->config->header);
                $this->println('</header>');
            }
        }
    }

    function print_foot() {
        if (isset($this->config->footer)) {
            if (is_readable($this->config->footer)) {
                $this->println('<footer>');
                readfile($this->config->footer);
                $this->println('</footer>');
            }
        }
        $this->println('</body>');
        $this->println('</html>');
    }

    function print_rootnode() {
        print('<a href=".">'.$this->config->rootnode.'</a>');
    }

    function print_dirs_rec($nav, $path='.') {
        $dirs = $this->fso->read_dirs($path, $this->config->hidden_dirnames);
        if (!empty($dirs)) {
            $this->println();
            $this->println('<ul>');
            foreach ($dirs as $dir) {
                print('<li>');
                $name = str_replace('_', ' ', basename($dir));
                if ($this->fso->exists_file($dir, $this->config->indices)) {
                    print('<a href="'.$dir.'/">'.$name.'</a>');
                } else {
                    if ($nav === $dir || $nav === substr($dir, 2)) {
                        print('<a class="selected" href="'.$_SERVER['PHP_SELF'].
                                '?nav='.urlencode($dir).'">'.$name.'</a>');
                    } else {
                        print('<a href="'.$_SERVER['PHP_SELF'].
                                '?nav='.urlencode($dir).'">'.$name.'</a>');
                    }
                    $this->print_dirs_rec($nav, $dir);
                }
                $this->println('</li>');
            }
            $this->println('</ul>');
        }
    }

    function print_navigation($nav='.') {
        $this->println('<div id="navigation">');
        $this->println('<a href="#navigation" id="show_nav">Navigation</a>');
        $this->println('<nav>');
        $this->print_rootnode();
        $this->print_dirs_rec($nav);
        $this->println('</nav>');
        $this->println('</div>');
    }

    function print_advertisement($_filepath) {
        $this->println('<div id="advertisement">');
        $fileext = $this->fso->get_file_ext($_filepath);
        if ($fileext == 'php') {
            include($_filepath);
        } else {
            $filecontents = file_get_contents($_filepath);
            print($filecontents);
        }
        $this->println('</div>');
    }

    function print_sidebar($nav='.') {
        $this->println('<div id="sidebar">');
        $this->print_navigation($nav);
        if (isset($this->config->advertisement)) {
            if (is_readable($this->config->advertisement)) {
                $this->print_advertisement($this->config->advertisement);
            }
        }
        $this->println('</div>');
    }

    function print_readme($_filepath) {
        $_path = dirname($_filepath);
        $fileext = $this->fso->get_file_ext($_filepath);
        if ($fileext == 'php') {
            $_list_files = $this->list_files;
            // Shared variables with included php script:
            // $_path, $_filepath, $_list_files
            include($_filepath);
            $this->list_files = $_list_files;
        } else {
            $filecontents = file_get_contents($_filepath);
            $filecontents = str_replace('href="./', 'href="'.$_path.'/',
                    $filecontents);
            $filecontents = str_replace('src="./', 'src="'.$_path.'/',
                    $filecontents);
            print($filecontents);
        }
    }

    function print_readmes($path='.') {
        if ($this->fso->exists_file($path, $this->config->readmes)) {
            $this->println('<div id="readme">');
            foreach ($this->config->readmes as $readme) {
                $filepath = $path.'/'.$readme;
                if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
                    $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'],0,2);
                    $pathinfo = pathinfo($filepath);
                    $filepath_lang = $path.'/'.$pathinfo['filename'].'.'.$lang;
                    if (isset($pathinfo['extension'])) {
                        $filepath_lang .= '.'.$pathinfo['extension'];
                    }
                    if (is_file($filepath_lang) and
                            is_readable($filepath_lang)) {
                        $this->print_readme($filepath_lang);
                    } else if (is_file($filepath) and
                            is_readable($filepath)) {
                        $this->print_readme($filepath);
                    }
                } else if (is_file($filepath) and
                        is_readable($filepath)) {
                    $this->print_readme($filepath);
                }
            }
            $this->println('</div>');
        }
    }

    function print_tr_file($filepath) {
        $this->println('<tr>');
        $this->println('<td class="filename">'.
                '<a href="'.$filepath.'" target="_blank">'.
                basename($filepath).'</a></td>');

        $fileapps = '';
        $ext = pathinfo($filepath, PATHINFO_EXTENSION);
        if (array_key_exists($ext, $this->config->file_apps)) {
            $apps = $this->config->file_apps[$ext];
            foreach ($apps as $app) {
                if (!empty($fileapps)) {
                    $fileapps .= ' ';
                }
                $fileapps .= '<a href="'.$app['url'].
                    urlencode($filepath).'" target="_blank">'.
                    $app['label'].'</a>';
            }
        }
        $this->println('<td class="fileapps">'.$fileapps.'</td>');

        $filedate_hr = date('Y-m-d',filemtime($filepath));
        $this->println('<td class="filedate">'.$filedate_hr.'</td>');

        $size = filesize($filepath);
        $mod = 1024;
        $units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
        for ($cnt = 0; $size > $mod; $cnt++) {
            $size /= $mod;
        }
        $filesize_hr = round($size, 1).'&nbsp;'.$units[$cnt];
        $this->println('<td class="filesize">'.$filesize_hr.'</td>');

        $this->println('</tr>');
    }

    function print_tr_filepreview($preview) {
        $preview_html = htmlentities($preview);
        $this->println('<tr>');
        $this->println('<td colspan="4" class="filepreview">');
        print('<pre>');
        print($preview_html);
        $this->println('</pre>');
        $this->println('</td>');
        $this->println('</tr>');
    }

    function print_files($path='.') {
        $files = $this->fso->read_files($path);
        if (!empty($files)) {
            $this->println('<div id="files">');
            $this->println('<table class="files">');
            foreach ($files as $filepath) {
                $this->println('<tbody class="file">');
                $this->print_tr_file($filepath);
                if (in_array($this->fso->get_file_ext($filepath),
                        $this->config->preview_file_exts)) {
                    $preview = $this->fso->read_preview($filepath,
                            $this->config->preview_size);
                    if (!empty($preview)) {
                        $this->print_tr_filepreview($preview);
                    }
                }
                $this->println('</tbody>');
            }
            $this->println('</table>');
            $this->println('</div>');
        }
    }

    function print_content($nav='.') {
        $this->println('<div id="content">');
        if (file_exists(dirname(__FILE__).'/'.$nav)) {
            $name = str_replace('_', ' ', basename($nav));
            if ($name != '.') {
                $this->println('<h2>'.$name.'</h2>');
            }
            $this->print_readmes($nav);
            if ($this->list_files) {
                $this->print_files($nav);
            }
        } else {
            $this->println('<p>Error: Variable nav="'.$nav.'" is invalid.</p>');
        }
        $this->println('</div>');
    }

    function print_not_found($url) {
        $this->println('<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">');
        $this->println('<html><head>');
        $this->println('<title>404 Not Found</title>');
        $this->println('</head><body>');
        $this->println('<h1>Not Found</h1>');
        $this->println('<p>The requested URL '.$url.' was not found on this server.</p>');
        $this->println('</body></html>');
    }

}


if (!empty($_SERVER['PATH_INFO'])) {
    header("HTTP/1.0 404 Not Found");
}

$nav = '.';
if (!empty($_GET['nav'])) {
    $nav = $_GET['nav'];
    if (stristr($nav, '..')) {
        $nav = '.';
    }
    if (!file_exists(dirname(__FILE__).'/'.$nav)) {
        $nav = '.';
    }
}

$config = new Config();
$fso = new FileSystemObject();
$html = new HTML($config, $fso);

if (!empty($_SERVER['PATH_INFO'])) {
    $html->print_not_found($_SERVER['PHP_SELF']);
    exit();
}

$html->print_head($nav);
$html->print_sidebar($nav);
$html->print_content($nav);
$html->print_foot();

?>