1#!/usr/local/bin/php 2<?php 3 chdir(dirname($argv[0])); 4 require("../libraries/banshee.php"); 5 require("../libraries/security.php"); 6 require("../libraries/settings.php"); 7 8 /* Dummy user class 9 */ 10 class dummy_user { 11 private $id = null; 12 13 public function __construct($user_id) { 14 $this->id = $user_id; 15 } 16 17 public function __get($key) { 18 switch ($key) { 19 case "id": return $this->id; 20 } 21 22 return null; 23 } 24 } 25 26 /* Model class 27 */ 28 class stats_model extends graph_model { 29 public function __construct($db, $user, $table, $columns, $hostnames) { 30 $this->table = $table; 31 $this->columns = $columns; 32 $this->hostnames = $hostnames; 33 34 parent::__construct($db, null, $user, null, null); 35 } 36 } 37 38 /* Filter class 39 */ 40 class filter { 41 private $webserver_id = null; 42 43 public function __construct($webserver_id) { 44 $this->webserver_id = $webserver_id; 45 } 46 47 public function __get($key) { 48 switch ($key) { 49 case "webserver": return $this->webserver_id; 50 case "hostname" : return 0; 51 case "hour_from": return 0; 52 case "hour_to" : return 23; 53 } 54 55 return null; 56 } 57 } 58 59 /* Calculate median 60 */ 61 function get_median($data, $key) { 62 $values = array(); 63 64 foreach ($data as $item) { 65 array_push($values, $item[$key]); 66 } 67 sort($values); 68 69 $count = count($values); 70 if ($count & 1 == 1) { 71 $center = ($count + 1) / 2; 72 $median = $values[$center]; 73 } else { 74 $center = $count / 2; 75 $median = ($values[$center] + $values[$center + 1]) / 2; 76 } 77 78 return $median; 79 } 80 81 /* Main program 82 */ 83 error_reporting(E_ALL & ~E_NOTICE); 84 85 /* Connect to database 86 */ 87 $db = new MySQLi_connection(DB_HOSTNAME, DB_DATABASE, DB_USERNAME, DB_PASSWORD); 88 if ($db->connected == false) { 89 exit("Internal error: database not available.\n"); 90 } 91 92 $settings = new settings($db); 93 94 /* Get information about webserver 95 */ 96 function get_information($db, $settings, $table, $columns, $user_id, $webserver_id, $hostnames) { 97 $user = new dummy_user($user_id); 98 $model = new stats_model($db, $user, $table, $columns, $hostnames); 99 $filter = new filter($webserver_id); 100 101 $begin = date("Y-m-d", strtotime("-".($settings->report_history_days - 1)." days")); 102 $today = date("Y-m-d"); 103 104 if (($stats = $model->get_statistics($begin, $today, $filter)) === false) { 105 return false; 106 } 107 108 $result = array(); 109 foreach ($columns as $column) { 110 $result[$column] = array("today" => 0, "previous" => 0); 111 } 112 113 foreach ($stats as $day => $stat) { 114 $type = ($day == $today) ? "today" : "previous"; 115 foreach ($stat as $column => $value) { 116 $result[$column][$type] += $value; 117 } 118 } 119 120 foreach ($result as $idx => $stat) { 121 if ($settings->report_use_median) { 122 $result[$idx]["previous"] = get_median($stats, $idx); 123 } else { 124 $result[$idx]["previous"] = round($stat["previous"] / ($settings->report_history_days - 1)); 125 } 126 127 if ($result[$idx]["previous"] != 0) { 128 $change = round(($stat["today"] - $result[$idx]["previous"]) / $result[$idx]["previous"], 2); 129 $result[$idx]["change"] = $change." x"; 130 } else if ($result["today"] == 0) { 131 $change = $stat["today"]; 132 $result[$idx]["change"] = $change." x"; 133 } else { 134 $change = 0; 135 $result[$idx]["change"] = "∞"; 136 } 137 138 if ($change >= $settings->report_alert_high) { 139 $result[$idx]["alert"] = "high"; 140 } else if ($change >= $settings->report_alert_medium) { 141 $result[$idx]["alert"] = "medium"; 142 } else { 143 $result[$idx]["alert"] = "none"; 144 } 145 } 146 147 return $result; 148 } 149 150 /* Generate report for webserver 151 */ 152 function generate_report($db, $settings, $user, $webserver) { 153 static $reports = array(); 154 155 if (isset($reports[$webserver["id"]])) { 156 return $reports[$webserver["id"]]; 157 } 158 159 $stats = array( 160 "Request statistics" => array( 161 "table" => "host_statistics", 162 "hostnames" => true, 163 "details" => array( 164 "requests" => "Requests", 165 "bytes_sent" => "Bytes sent", 166 "result_forbidden" => "Forbidden", 167 "result_not_found" => "Not Found", 168 "result_internal_error" => "Internal Server Error")), 169 "Security statistics" => array( 170 "table" => "host_statistics", 171 "hostnames" => true, 172 "details" => array( 173 "exploit_attempts" => "Exploit attempts", 174 "failed_logins" => "Failed logins", 175 "bans" => "Bans")), 176 "CGI statistics" => array( 177 "table" => "cgi_statistics", 178 "hostnames" => true, 179 "details" => array( 180 "cgi_errors" => "CGI errors", 181 "time_0_1" => "0 - 1 second", 182 "time_1_3" => "1 - 3 seconds", 183 "time_3_10" => "3 - 10 seconds", 184 "time_10_x" => "More than 10 seconds")), 185 "Server statistics" => array( 186 "table" => "server_statistics", 187 "hostnames" => false, 188 "details" => array( 189 "connections" => "Connections", 190 "result_bad_request" => "Bad Requests"))); 191 192 $report = array( 193 "content" => "<h2>".$webserver["name"]."</h2>\n", 194 "alerts" => array()); 195 $type = $settings->report_use_median ? "Median" : "Average"; 196 197 $add_count = 0; 198 foreach ($stats as $label => $stat) { 199 $header = "<h3>".$label."</h3>\n". 200 "<table class=\"stats\">\n". 201 "<thead>\n<tr><th>Type</th><th>Value</th><th>= Δ of</th><th>".$type."</th></tr>\n</thead>\n<tbody>\n"; 202 $header_set = false; 203 204 $columns = array_keys($stat["details"]); 205 if (($information = get_information($db, $settings, $stat["table"], $columns, $user["id"], $webserver["id"], $stat["hostnames"])) == false) { 206 continue; 207 } 208 209 foreach ($information as $column => $value) { 210 if ($value["change"] < $settings->report_alert_medium) { 211 if ($settings->report_skip_normal) { 212 continue; 213 } 214 } 215 216 $add_count++; 217 218 if ($header_set == false) { 219 $report["content"] .= $header; 220 $header_set = true; 221 } 222 223 $today = graph_model::readable_number($value["today"]); 224 $previous = graph_model::readable_number($value["previous"]); 225 $report["content"] .= "<tr><td>".$stat["details"][$column].":</td>". 226 "<td class=\"alert_".$value["alert"]."\">".$today."</td>". 227 "<td>".$value["change"]."</td><td>".$previous."</td></tr>\n"; 228 if ($value["alert"] != "none") { 229 array_push($report["alerts"], $webserver["name"]); 230 } 231 } 232 233 if ($header_set) { 234 $report["content"] .= "</tbody>\n</table>\n"; 235 } 236 } 237 238 $query = "select event, UNIX_TIMESTAMP(timestamp) as timestamp from events ". 239 "where date(timestamp)=date(now()) and webserver_id=%d order by timestamp"; 240 if (($events = $db->execute($query, $webserver["id"])) != false) { 241 $report["content"] .= "<h3>Events</h3>\n"; 242 $report["content"] .= "<table class=\"events\">\n"; 243 $report["content"] .= "<thead>\n<tr><th>Time</th><th>Event</th></tr>\n</thead>\n<tbody>\n"; 244 245 foreach ($events as $event) { 246 $report["content"] .= "<tr><td>".date("H:i:s", $event["timestamp"])."</td><td>".htmlentities($event["event"])."</td></tr>\n"; 247 } 248 249 $report["content"] .= "</tbody>\n</table>\n"; 250 251 $add_count++; 252 } 253 254 if ($add_count == 0) { 255 $report["content"] .= "<p>Nothing to report.</p>\n"; 256 } 257 258 $reports[$webserver["id"]] = $report; 259 260 return $report; 261 } 262 263 /* Main program 264 */ 265 error_reporting(E_ALL & ~E_NOTICE); 266 267 /* Select users which want to receive daily reports 268 */ 269 if (count($argv) > 1) { 270 $args = array_slice($argv, 1); 271 $filter = array_fill(0, count($args), "%s"); 272 $query = "select * from users where username in (".implode(", ", $filter).")"; 273 } else { 274 $query = "select * from users where daily_report=%d"; 275 $args = array(YES); 276 } 277 278 if (($users = $db->execute($query, $args)) == false) { 279 return; 280 } 281 282 /* Send reports per user 283 */ 284 if (($template = file_get_contents("../extra/report.html")) === false) { 285 return; 286 } 287 288 $query = "select w.* from webservers w, webserver_user l ". 289 "where w.id=l.webserver_id and l.user_id=%d order by w.name"; 290 foreach ($users as $user) { 291 if (($webservers = $db->execute($query, $user["id"])) == false) { 292 continue; 293 } 294 295 $report_content = ""; 296 $report_alerts = array(); 297 foreach ($webservers as $webserver) { 298 if (($report = generate_report($db, $settings, $user, $webserver)) === false) { 299 continue; 300 } 301 $report_content .= $report["content"]; 302 $report_alerts = array_unique(array_merge($report_alerts, $report["alerts"]), SORT_STRING); 303 } 304 305 $count = count($report_alerts); 306 if ($count == 0) { 307 $report_alerts = "none"; 308 } else { 309 if ($count >= 2) { 310 $last = " and ".array_pop($report_alerts); 311 } else { 312 $last = ""; 313 } 314 $report_alerts = implode(", ", $report_alerts).$last; 315 } 316 317 $replace = array( 318 "ALERTS" => $report_alerts, 319 "CONTENT" => $report_content, 320 "TYPE" => $settings->report_use_median ? "median" : "average", 321 "HISTORY" => $settings->report_history_days - 1, 322 "TIMESTAMP" => date("j F Y, H:i (O)")); 323 324 $email = new email("Daily Hiawatha Monitor report", $settings->webmaster_email); 325 $email->set_message_fields($replace); 326 $email->message($template); 327 $email->send($user["email"], $user["fullname"]); 328 unset($email); 329 } 330?> 331