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

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

    protected $config = null;

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

    function authenticate() {
        header('WWW-Authenticate: Basic');
        header('HTTP/1.0 401 Unauthorized');
        return false;  // if user hits cancel
    }

    function verify($username, $password) {
        $result = false;
        if (isset($this->config->pass_hashes[$username])) {
            $pass_hash = $this->config->pass_hashes[$username];
            $result = password_verify($password, $pass_hash);
        }
        return $result;
    }

}

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

    function order_by(&$table, $field, $sort_type=SORT_ASC) {
        $keys = array_column($table, $field);
        array_multisort($keys, $sort_type, $table);
    }

    function get_local_uptime() {
        $filepath = '/proc/uptime';
        $result = array();
        $result['hostname'] = 'localhost';
        $result['last_update'] = time();
        $result['uptime'] = strtok(file_get_contents($filepath), ' ');
        return $result;
    }

    function get_uptime($hostname) {
        if (false !== strpos($hostname, '..')) {
            return null;
        }
        $filepath = dirname(__FILE__).'/.uptime/'.$hostname.'.uptime';
        $result = null;
        if (is_file($filepath)) {
            $result = array();
            $result['hostname'] = $hostname;
            $result['last_update'] = filemtime($filepath);
            $result['uptime'] = trim(file_get_contents($filepath));
        }
        return $result;
    }

    function get_uptimes() {
        $result = null;
        $path = dirname(__FILE__).'/.uptime';
        if ($handle = opendir($path)) {
            $result = array();
            while (false !== ($entry = readdir($handle))) {
                $filepath = $path.'/'.$entry;
                if (is_file($filepath)) {
                    $path_parts = pathinfo($filepath);
                    if ($path_parts['extension'] === 'uptime') {
                        $record = $this->get_uptime($path_parts['filename']);
                        array_push($result, $record);
                    }
                }
            }
            closedir($handle);
            $this->order_by($result, 'hostname');
            $record = $this->get_local_uptime();
            array_unshift($result, $record);
        }
        return $result;
    }

    function update_uptime($hostname, $uptime) {
        if (false !== strpos($hostname, '..')) {
            return false;
        }
        $filename = dirname(__FILE__).'/.uptime/'.$hostname.'.uptime';
        $result = false;
        $fp = fopen($filename, 'w');
        if ($fp) {
            if (fwrite($fp, sprintf("%0.2f", $uptime)."\n") > 0) {
                $result = true;
            }
            fclose($fp);
        }
        return $result;
    }

}

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

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

    function print_head($title='Uptime') {
        $this->println('<!DOCTYPE html>');
        $this->println('<html lang="en">');
        $this->println('<head>');
        $this->println('<meta charset="utf-8" />');
        $this->println('<title>'.$title.'</title>');
        $this->println('<meta name="viewport" '.
            'content="width=device-width, initial-scale=1.0" />');
        $stylesheet = '.uptime/style.css';
        $href = $stylesheet.'?mtime='.filemtime($stylesheet);
        $this->println('<link rel="stylesheet" href="'.$href.'" />');
        $this->println('</head>');
        $this->println('<body>');
        $this->println('<h1>'.$title.'</h1>');
    }

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

    function print_unauthorized() {
        $this->println('<p>This server could not verify that you are '.
            'authorized to access the document requested.</p>');
    }

    function print_usage() {
        $this->println('<h2>Usage</h2>');
        $this->println('<pre><code class="language-bash">');
?>
UPTIME=$(cut -d' ' -f1 /proc/uptime)

wget -q -O - --user="$USERNAME" --password="$PASSWORD" \
    "https://gernot-walzl.at/uptime.php?update&hostname=$HOSTNAME&uptime=$UPTIME"
<?php
        $this->println('</code></pre>');
    }

    function determine_state($last_update) {
        $result = 'unknown';
        $age = time() - intval($last_update);
        if ($age <= 43200) {
            $result = 'ok';
        } else if ($age <= 86400) {
            $result = 'warning';
        } else {
            $result = 'critical';
        }
        return $result;
    }

    function format_duration($seconds) {
        $days = intval($seconds / 86400);
        $seconds -= $days * 86400;
        $hours = intval($seconds / 3600);
        $seconds -= $hours * 3600;
        $minutes = intval($seconds / 60);
        $seconds -= $minutes * 60;
        $seconds = intval($seconds);
        return $days.'d '.$hours.'h '.$minutes.'m '.$seconds.'s';
    }

    function print_table_uptimes($table) {
        $this->println('<table class="cellborders">');
        $this->println('<tr>');
        $this->println('<th>Hostname</th>');
        $this->println('<th>Last Update ('.date('e').')</th>');
        $this->println('<th>Duration</th>');
        $this->println('</tr>');
        foreach ($table as $row) {
            $this->println('<tr>');
            $this->println('<td>'.$row['hostname'].'</td>');
            $state = $this->determine_state($row['last_update']);
            $this->println('<td class="'.$state.'">'.
                date('Y-m-d H:i:s', $row['last_update']).'</td>');
            $this->println('<td>'.
                $this->format_duration($row['uptime']).'</td>');
            $this->println('</tr>');
        }
        $this->println('</table>');
    }

}


if (!isset($_SERVER['HTTPS'])) {
    header('Location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
    exit();
}
$config = new UptimeConfig();
$html = new UptimeHTML();
$auth = new Authenticator($config);
$user = '';
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
    if ($auth->verify($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])) {
        $user = $_SERVER['PHP_AUTH_USER'];
    }
}
if (empty($user)) {
    if (!$auth->authenticate()) {
        $html->print_head('401 Unauthorized');
        $html->print_unauthorized();
        $html->print_foot();
    }
} else {
    $html->print_head();
    if (isset($_GET['update'])) {
        $hostname = '';
        if (isset($_GET['hostname'])) {
            $hostname = $_GET['hostname'];
        } else if (isset($_POST['hostname'])) {
            $hostname = $_POST['hostname'];
        }
        if (empty($hostname)) {
            $html->print_usage();
        } else {
            if (in_array($hostname, $config->hostnames[$user])) {
                $uptime = 0.0;
                if (isset($_GET['uptime'])) {
                    $uptime = floatval($_GET['uptime']);
                } else if (isset($_POST['uptime'])) {
                    $uptime = floatval($_POST['uptime']);
                }
                $dao = new UptimeDAO();
                $dao->update_uptime($hostname, $uptime);
                $table[] = $dao->get_uptime($hostname);
                $html->print_table_uptimes($table);
            } else {
                $html->println('<p>ERROR: Hostname not accepted.</p>');
            }
        }
    } else {
        $dao = new UptimeDAO();
        $table = $dao->get_uptimes();
        $html->print_table_uptimes($table);
    }
    $html->print_foot();
}

?>