#!/usr/bin/php
<?php
	define("MAX_CACHE_SIZE", 10);

	chdir(dirname($argv[0]));
	require("../libraries/configuration.php");
	require("../libraries/general.php");
	require("../libraries/http.php");
	require("../libraries/prowl.php");

	/* Database functions
	 */
	function get_foreign_key($db, $table, $key, $value) {
		static $cache = array();

		$index = implode(":", array($table, $key, $value));
		if (isset($cache[$index])) {
			return $cache[$index];
		}

		$query = "select id from %S where %S=%s";
		if (($result = $db->execute($query, $table, $key, $value)) != false) {
			$cache[$index] = (int)$result[0]["id"];
		} else {
			if ($db->insert($table, array("id" => null, $key => $value)) == false) {
				return false;
			}
			$cache[$index] = $db->last_insert_id;
		}

		return $cache[$index];
	}

	function get_hostname_id($db, $value) {
		return get_foreign_key($db, "hostnames", "hostname", $value);
	}

	function log_event($db, $event, $webserver_id, $timestamp = null) {
		if ($timestamp == null) {
			$timestamp = time();
		}

		$data = array(
			"id"           => null,
			"timestamp"    => date("Y-m-d H:i:s", $timestamp),
			"webserver_id" => (int)$webserver_id,
			"event"        => $event);

		return $db->insert("events", $data) !== false;
	}

	function send_prowl_notification($db, $message) {
		static $keys = null;
		static $prowl = null;

		if ($keys === null) {
			$query = "select * from users where prowl_key!=%s";
			if (($users = $db->execute($query, "")) == false) {
				return;
			}
			$keys = array();
			foreach ($users as $user) {
				array_push($keys, $user["prowl_key"]);
			}

			$prowl = new prowl("Hiawatha Monitor", $keys);
		}

		if ($prowl === null) {
			return;
		}

		$prowl->send_notification("Event log", $message);
	}

	/* HTTP class extensions
	 */
	class HTTP_monitor extends HTTP {
		public function __call($method, $parameters) {
			$this->host = "monitor";
			return parent::__call($method, $parameters);
		}
	}

	class HTTPS_monitor extends HTTPS {
		public function __call($method, $parameters) {
			$this->host = "monitor";
			return parent::__call($method, $parameters);
		}
	}

	/* Main program
	 */
	error_reporting(E_ALL & ~E_NOTICE);

	$debug_mode = ($argv[1] == "-d");

	$db = new MySQLi_connection(DB_HOSTNAME, DB_DATABASE, DB_USERNAME, DB_PASSWORD);
	if ($db->connected == false) {
		exit("Internal error: database not available.\n");
	}

	if (($webservers = $db->execute("select * from webservers where active=%d", 1)) === false) {
		exit("Error retrieving webserver IP addresses.\n");
	}

	foreach ($webservers as $webserver) {
		if (isset($http)) {
			unset($http);
		}
		if (is_true($webserver["ssl"]) == false) {
			$http = new HTTP_monitor($webserver["ip_address"], $webserver["port"]);;
		} else {
			$http = new HTTPS_monitor($webserver["ip_address"], $webserver["port"]);;
		}

		$webserver_id = (int)$webserver["id"];

		if ($debug_mode) {
			printf("Retrieving index from %s...\n", $webserver["name"]);
		}
		$result = $http->GET("/");
		if ($result["status"] != 200) {
			$db->update("webservers", $webserver_id, array("errors" => $webserver["errors"] + 1));
			$event = "Error while getting statistics logfile index from webserver";
			log_event($db, $event, $webserver_id);

			$message = sprintf("Webserver %s appears to be offline.", $webserver["name"]);
			send_prowl_notification($db, $message);
			continue;
		}

		if ($webserver["errors"] != 0) {
			$db->update("webservers", $webserver_id, array("errors" => 0));
		}

		if ($result["headers"]["Content-Type"] != "text/xml") {
			continue;
		}

		$index = new DomDocument();
		if ($index->loadXML($result["body"]) == false) {
			continue;
		}

		$files = $index->getElementsByTagName("file");

		foreach ($files as $file) {
			if ($debug_mode) {
				printf("Downloading %s...\n", $file->nodeValue);
			}
			$result = $http->GET("/".$file->nodeValue);
			if ($result["status"] != 200) {
				$event = "error while getting data file from webserver";
				log_event($db, $event, $webserver_id);
				continue;
			}
			if ($result["headers"]["Content-Type"] == "application/x-gzip") {
				$result["body"] = gzdecode($result["body"]);
			}

			$loglines = explode("\n", chop($result["body"]));

			foreach ($loglines as $logline) {
				if ($debug_mode) {
					printf("Processing [%s]\n", $logline);
				}
				$field = explode("\t", chop($logline));

				switch ($field[0]) {
					/* Request (obsolete)
					 */
					case "request":
						break;
					/* CGI runtime
					 */
					case "cgi":
						list(, $timestamp_begin, $timestamp_end, $hostname, $time_0_1,
						       $time_1_3, $time_3_10, $time_10_x) = $field;

						if (($hostname_id = get_hostname_id($db, $hostname)) == false) {
							print "Error getting hostname id\n";
							continue;
						}

						$day = date("Y-m-d", (int)$timestamp_begin);
						if ($day === date("Y-m-d", (int)$timestamp_end)) {
							/* Check for existing log on same day
							 */
							$query = "select * from cgi_statistics where webserver_id=%d and hostname_id=%d and ".
									 "timestamp_begin>=%s and timestamp_end<=%s limit 1";
							$result = $db->execute($query, $webserver_id, $hostname_id, $day." 00:00:00", $day." 23:59:59");
						} else {
							/* Log overlaps midnight
							 */
							$result = false;
						}

						if ($result == false) {
							/* Insert new CGI record
							 */
							$db->insert("cgi_statistics", array(
								"id"                    => null,
								"timestamp_begin"       => date("Y-m-d H:i:s", (int)$timestamp_begin),
								"timestamp_end"         => date("Y-m-d H:i:s", (int)$timestamp_end),
								"webserver_id"          => (int)$webserver_id,
								"hostname_id"           => (int)$hostname_id,
								"time_0_1"              => (int)$time_0_1,
								"time_1_3"              => (int)$time_1_3,
								"time_3_10"             => (int)$time_3_10,
								"time_10_x"             => (int)$time_10_x));
						} else {
							/* Update existing CGI record
							 */
							$current = $result[0];
							$data = array(
								"timestamp_end"         => date("Y-m-d H:i:s", (int)$timestamp_end),
								"time_0_1"              => (int)($current["time_0_1"] + $time_0_1),
								"time_1_3"              => (int)($current["time_1_3"] + $time_1_3),
								"time_3_10"             => (int)($current["time_3_10"] + $time_3_10),
								"time_10_x"             => (int)($current["time_10_x"] + $time_10_x));
							$db->update("cgi_statistics", $current["id"], $data);
						}
						break;
					/* Server statistic
					 */
					case "server":
						list(, $timestamp_begin, $timestamp_end, $connections) = $field;

						$db->insert("server_statistics", array(
							"id"              => null,
							"timestamp_begin" => date("Y-m-d H:i:s", (int)$timestamp_begin),
							"timestamp_end"   => date("Y-m-d H:i:s", (int)$timestamp_end),
							"webserver_id"    => (int)$webserver_id,
							"simult_conns"    => (int)$connections));
						break;
					/* Host statistic
					 */
					case "host":
						list(, $timestamp_begin, $timestamp_end, $hostname, $requests, $bytes_sent, $bans, $exploit_attempts,
						       $result_forbidden, $result_not_found, $result_internal_error) = $field;

						if (($hostname_id = get_hostname_id($db, $hostname)) == false) {
							print "Error getting hostname id\n";
							continue;
						}

						$day = date("Y-m-d", (int)$timestamp_begin);
						if ($day === date("Y-m-d", (int)$timestamp_end)) {
							/* Check for existing log on same day
							 */
							$query = "select * from host_statistics where webserver_id=%d and hostname_id=%d and ".
									 "timestamp_begin>=%s and timestamp_end<=%s limit 1";
							$result = $db->execute($query, $webserver_id, $hostname_id, $day." 00:00:00", $day." 23:59:59");
						} else {
							/* Log overlaps midnight
							 */
							$result = false;
						}

						if ($result == false) {
							/* Insert new host record
							 */
							$db->insert("host_statistics", array(
								"id"                    => null,
								"timestamp_begin"       => date("Y-m-d H:i:s", (int)$timestamp_begin),
								"timestamp_end"         => date("Y-m-d H:i:s", (int)$timestamp_end),
								"webserver_id"          => (int)$webserver_id,
								"hostname_id"           => (int)$hostname_id,
								"requests"              => (int)$requests,
								"bytes_sent"            => (int)$bytes_sent,
								"bans"                  => (int)$bans,
								"exploit_attempts"      => (int)$exploit_attempts,
								"result_forbidden"      => (int)$result_forbidden,
								"result_not_found"      => (int)$result_not_found,
								"result_internal_error" => (int)$result_internal_error));
						} else {
							/* Update existing host record
							 */
							$current = $result[0];
							$data = array(
								"timestamp_end"         => date("Y-m-d H:i:s", (int)$timestamp_end),
								"requests"              => (int)($current["requests"] + $requests),
								"bytes_sent"            => (int)($current["bytes_sent"] + $bytes_sent),
								"bans"                  => (int)($current["bans"] + $bans),
								"exploit_attempts"      => (int)($current["exploit_attempts"] + $exploit_attempts),
								"result_forbidden"      => (int)($current["result_forbidden"] + $result_forbidden),
								"result_not_found"      => (int)($current["result_not_found"] + $result_not_found),
								"result_internal_error" => (int)($current["result_internal_error"] + $result_internal_error));
							$db->update("host_statistics", $current["id"], $data);
						}
						break;
					/* Event
					 */
					case "event";
						list(, $event, $timestamp) = $field;
						log_event($db, $event, $webserver_id, $timestamp);
						break;
					/* Rest
					 */
					default:
						list($event, $timestamp) = $field;
						log_event($db, $event, $webserver_id, $timestamp);
				}
			}
		}
	}
?>
