<?php

const HIGHLIGHTJS_DIR = '.highlightjs';  // https://highlightjs.org/
const FILE_EXTENSIONS = array(
    'bat'  => 'language-dos',
    'cpp'  => 'language-cpp',
    'css'  => 'language-css',
    'gcode' => 'language-gcode',
    'geojson' => 'language-json',
    'hpp'  => 'language-cpp',
    'html' => 'language-xml',
    'ini'  => 'language-ini',
    'java' => 'language-java',
    'js'   => 'language-javascript',
    'json' => 'language-json',
    'md'   => 'language-markdown',
    'php'  => 'language-php',
    'phps' => 'language-php',
    'py'   => 'language-python',
    'sh'   => 'language-bash',
    'sql'  => 'language-sql',
    'svg'  => 'language-xml',
    'tex'  => 'language-latex',
    'txt'  => 'language-plaintext',
    'xml'  => 'language-xml',
    'xspf' => 'language-xml');
const STYLES = array(
    'ascetic',
    'androidstudio',
    'dark',
    'default',
    'default-auto',
    'github',
    'github-auto',
    'github-dark',
    'qtcreator-auto',
    'qtcreator-dark',
    'qtcreator-light',
    'vs',
    'vs2015',
    'xcode');

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

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

    function print_head($filename='', $style='') {
        $this->println('<!DOCTYPE html>');
        $this->println('<html lang="en">');
        $this->println('<head>');
        $this->println('<meta charset="utf-8" />');
        $this->println('<meta name="viewport" '.
            'content="width=device-width, initial-scale=1.0" />');
        if (empty($filename)) {
            $this->println('<title>Syntax Highlighting</title>');
        } else {
            $this->println('<title>'.basename($filename).'</title>');
            if (!empty($style)) {
                $stylesheet = HIGHLIGHTJS_DIR.'/styles/'.$style.'.min.css';
                $href = $stylesheet.'?mtime='.filemtime($stylesheet);
                $this->println('<link rel="stylesheet" href="'.$href.'" />');
                $script = HIGHLIGHTJS_DIR.'/highlight.min.js';
                $src = $script.'?mtime='.filemtime($script);
                $integrity = 'sha256-'.base64_encode(
                    hash_file('sha256', $script, true));
                $this->println('<script src="'.$src.'" '.
                    'integrity="'.$integrity.'"></script>');
                $this->println('<script>hljs.highlightAll();</script>');
            }
        }
        $this->println('</head>');
        if (empty($filename) or empty($style)) {
            $this->println('<body>');
        } else {
            $this->println('<body class="hljs" style="margin: 0;">');
        }
    }

    function print_foot() {
        $this->println('</body>');
        $this->println('</html>');
    }

    function print_form($filename='', $style_selected='') {
        if (empty($filename)) {
            $filename = basename(__FILE__);
        }
        $this->println('<form method="get" action="'.$_SERVER['PHP_SELF'].'">');
        $this->println('<table>');
        $this->println('<tr>');
        $this->println('<td><label for="filename">Filename:</label></td>');
        $this->println('<td><input type="text" id="filename" name="filename" '.
            'value="'.htmlentities($filename).'" /></td>');
        $this->println('</tr>');
        $this->println('<tr>');
        $this->println('<td><label for="style">Style:</label></td>');
        $this->println('<td><select id="style" name="style">');
        $this->println('<option value="">nohighlight</option>');
        foreach (STYLES as $style) {
            if ($style == $style_selected) {
                $this->println('<option value="'.$style.'" selected>'.
                    $style.'</option>');
            } else {
                $this->println('<option value="'.$style.'">'.
                    $style.'</option>');
            }
        }
        $this->println('</select></td>');
        $this->println('</tr>');
        $this->println('</table>');
        $this->println('<p><input type="submit" value="Show Source" /></p>');
        $this->println('</form>');
    }

    function print_info() {
        $this->println('<h1>Syntax Highlighting</h1>');
        $this->println('<p>Show source code with highlighted syntax<br />');
        $this->println('Uses <a href="https://highlightjs.org/" target="_blank">'.
            'highlight.js</a></p>');
    }

    function print_error() {
        $this->println('<p><strong>Error:</strong> '.
            'The given filename is not valid.</p>');
    }

    function print_supported_files() {
        $this->println('<h2>Supported Files</h2>');
        $this->println('<table>');
        $this->println('<tr>');
        $this->println('<th>Extension</th>');
        $this->println('<th>Language</th>');
        $this->println('</tr>');
        foreach (FILE_EXTENSIONS as $ext => $language) {
            $this->println('<tr>');
            $this->println('<td>'.$ext.'</td>');
            $this->println('<td>'.str_replace(
                'language-', '', $language).'</td>');
            $this->println('</tr>');
        }
        $this->println('</table>');
    }

    function print_code($filename) {
        $language = 'nohighlight';
        $ext = pathinfo($filename, PATHINFO_EXTENSION);
        if (array_key_exists($ext, FILE_EXTENSIONS)) {
            $language = FILE_EXTENSIONS[$ext];
        }
        print('<pre style="margin: 0;">'.
            '<code class="'.$language.'" style="min-width: fit-content;">');
        $filecontents = file_get_contents($filename);
        print(htmlentities($filecontents));
        $this->println('</code></pre>');
    }

}

function is_valid_filename($filename) {
    $result = false;
    if (!empty($filename)) {
        if (!strstr($filename, '..')) {
            $full_filename = dirname(__FILE__).'/'.$filename;
            if (is_file($full_filename) and is_readable($full_filename)) {
                $result = true;
            }
        }
    }
    return $result;
}

function is_allowed_filename($filename) {
    $result = false;
    $forbidden = array('wp-config.php', 'config.inc.php');
    if (!empty($filename)) {
        $base = basename($filename);
        if (substr($base, 0, 1) != '.') {
            if (!in_array($base, $forbidden)) {
                $ext = pathinfo($filename, PATHINFO_EXTENSION);
                if (array_key_exists($ext, FILE_EXTENSIONS)) {
                    $result = true;
                }
            }
        }
    }
    return $result;
}

$filename = '';
if (!empty($_GET['filename'])) {
    $filename = $_GET['filename'];
}
$style = 'github-auto';
if (isset($_GET['style'])) {
    $style = '';
    if (in_array($_GET['style'], STYLES)) {
        $style = $_GET['style'];
    }
}

$html_highlight = new HighlightHTML();
if (is_valid_filename($filename) and is_allowed_filename($filename)) {
    $html_highlight->print_head($filename, $style);
    $html_highlight->print_code(dirname(__FILE__).'/'.$filename);
} else {
    $html_highlight->print_head();
    $html_highlight->print_info();
    if (!empty($filename)) {
        $html_highlight->print_error();
    }
    $html_highlight->print_form($filename, $style);
    $html_highlight->print_supported_files();
}
$html_highlight->print_foot();

?>