PageRenderTime 68ms CodeModel.GetById 11ms app.highlight 48ms RepoModel.GetById 2ms app.codeStats 0ms

/www/_lib/compare_classes.php

http://s-audit.googlecode.com/
PHP | 1113 lines | 620 code | 313 blank | 180 comment | 62 complexity | 5c7eb70a138d81ada9eb7c64639c41a6 MD5 | raw file
   1<?php
   2
   3//============================================================================
   4//
   5// compare_classes.php
   6// -------------------
   7//
   8// Classes for server comparision
   9//
  10// Part of s-audit. (c) 2011 SearchNet Ltd
  11//  see http://snltd.co.uk/s-audit for licensing and documentation
  12//
  13//============================================================================
  14
  15//============================================================================
  16// SERVER LIST PAGE
  17
  18class compareListPage extends audPage {
  19
  20	// This is the default landing page for the compare servers page. (As
  21	// opposed to comparePage, which is used for the comparisons proper.)
  22
  23    protected $no_class_link = true;
  24		// There's no "this class" documentation link
  25
  26	protected $group;
  27		// The audit group we're looking at
  28
  29	protected $ff;
  30		// Path to friends file
  31
  32	protected $map;
  33		// as usual
  34
  35	// We need the "compare" stylesheet
  36
  37	protected $styles = array("basic.css", "audit.css", "compare.css");
  38	
  39	public function __construct($title, $map)
  40	{
  41		if (!isset($_GET["g"]))
  42			die("no group");
  43
  44		$this->group = $_GET["g"];
  45		$this->map = $map;
  46
  47		$this->ff = AUDIT_DIR . "/$this->group/friends.txt";
  48
  49		// Expand the map. We need all the local zone names, so create the
  50		// servers[] array in the map, just like GetServers normally does
  51
  52		foreach($map->map as $global=>$file) {
  53			$this->map->servers[$global] =
  54			array_slice(array_unique(preg_grep("/^hostname=/",
  55			file($file))), 1);
  56		}
  57
  58		parent::__construct($title, false);
  59	}
  60
  61	public function ff_list()
  62	{
  63		// Say whether or not we're using a friends file. If we are, display
  64		// the list
  65
  66		$ff_link = "<a href=\"" . DOC_URL .
  67		"/04_extras/friends.php\">friends file</a>";
  68
  69		$ret = "\n\n<p class=\"center\">";
  70		
  71		if (file_exists($this->ff)) {
  72
  73			$friends = file($this->ff);
  74
  75			// Read in the friends file and make a key=>value array of the
  76			// pairs inside it
  77
  78			foreach($friends as $row) {
  79
  80				if (preg_match("/^\w/", $row)) {
  81					$a = preg_split("/\s+/", $row);
  82
  83					if (count($a) == 3) $pairs[$a[0]] = $a[1];
  84				}
  85
  86			}
  87
  88		    $ret .= "The following server comparisons are defined in the
  89			$ff_link at <tt>$this->ff</tt>.</p>";
  90
  91			$ret .= "\n\n<ul>";
  92
  93			foreach($pairs as $key=>$val) {
  94
  95				// Only display pairs which both exist in the map
  96
  97				if ($this->map->has_data($key) &&
  98					$this->map->has_data($val))
  99						$ret .= "\n  <li><a href=\"$_SERVER[PHP_SELF]?g="
 100						.  $this->group . "&amp;z1=$key&amp;z2=$val\">"
 101						."$key and $val</a></li>";
 102			}
 103
 104			$ret .= "\n</ul>";
 105		}
 106		else
 107			$ret .= "You do not have a $ff_link for this audit group.</p>";
 108
 109		return $ret . new compareCyc($this->map->list_all(), $this->group);
 110	}
 111
 112}
 113
 114//----------------------------------------------------------------------------
 115
 116class compareCyc {
 117
 118	// The dual cycle gadget on the server comparison page
 119
 120	private $html;
 121		// The HTML generated by the constructor
 122
 123    public function __construct($z_list, $group, $z1 = false, $z2 = false)
 124    {
 125		// This is the bar at the bottom of the screen that lets you choose
 126		// the servers to compare from cycle gadgets.
 127		
 128		$h = new html;
 129
 130        $this->html = "<p class=\"center\">Select a pair of servers or zones
 131		to compare with the following gadgets.</p>"
 132		. "\n<div id=\"cycle_row\">"
 133        . $h->dialog_form($_SERVER["PHP_SELF"])
 134        . $h->dialog_submit("c", "compare")
 135        . $h->dialog_cycle("z1", $z_list, $z1, false) . " with "
 136        . $h->dialog_cycle("z2", $z_list, $z2, false)
 137		. $h->dialog_hidden("g", $group)
 138        . "</form>\n</div>";
 139    }
 140
 141	public function __toString() 
 142	{
 143		return $this->html;
 144	}
 145
 146}
 147
 148//============================================================================
 149// COMPARISON PAGE
 150
 151class comparePage extends audPage {
 152
 153	// The basic HTML template for the page used for the actual comparison.
 154
 155    protected $no_class_link = true;
 156		// There's no "this class" documentation link
 157
 158	protected $styles = array("basic.css", "audit.css", "compare.css");
 159
 160}
 161
 162//----------------------------------------------------------------------------
 163
 164class compareView {
 165	
 166	// This class groups together functions needed to compare two servers,
 167	// and display the results of that comparison. It works like the
 168	// serverView class does for single server pages.
 169	
 170	// We have to extend the HostGrid because we use its methods to display
 171	// data
 172
 173    private $a;
 174    private $b;
 175        // The two servers on which we're doing an a/b comparison
 176
 177	private $classes;
 178		// Audit classes we're going to compare
 179
 180    public function __construct($data, $map)
 181    {
 182		// Call this constructor to create the whole compare grid.
 183
 184		$this->map = $map;
 185
 186		// Populate the $fields array with the hostnames of the servers to
 187		// compare and get a list of classes. Servers should always have the
 188		// same classes, but make sure we get everything anyway.
 189
 190		$classes = array();
 191
 192		// create $this->data, which puts the class data for each machine in
 193		// the same level of the data structure
 194		//
 195		// [class] -> [server_a]
 196		//            [server_b]
 197
 198		foreach($data as $host=>$h_data) {
 199			$this->fields[] = $host;
 200
 201			foreach($h_data as $class=>$c_data) {
 202
 203				$this->data[$class][$host] = $c_data;
 204
 205				if (!in_array($class, $classes))
 206					$classes[] = $class;
 207			}
 208
 209		}
 210
 211		$this->classes = $classes;
 212    }
 213
 214    public function show_grid()
 215    {
 216		// The grid in this case is a list of tables, one for each audit
 217		// type. Each table is created by its own class
 218
 219        $ret = false;
 220
 221        foreach($this->classes as $type) {
 222
 223            $class = (class_exists("compare$type"))
 224				? "compare$type"
 225                : "compareGeneric";
 226
 227			// Call the class with its name, the data for servers, and the
 228			// map
 229
 230            $ret .= new $class($type, $this->data[$type], $this->map);
 231        }
 232
 233        return $ret;
 234    }
 235
 236}
 237
 238//----------------------------------------------------------------------------
 239// Generic compare. Every audit class has its own compareClass which is an
 240// extension of this
 241
 242class compareGeneric {
 243
 244	// Some fields need pre-processing. This could be because they, like
 245	// NICs, come machine-parseable so aren't easy to read, or it could be
 246	// because, like network port information, we discard some data. For
 247	// that reason we can create preproc_field() methods. If they exist,
 248	// they're called. 
 249
 250	private $hosts;
 251
 252	protected $cols;
 253		// columns, excluding the key - size of the hosts array
 254
 255	private $html;
 256
 257	protected $no_colour = array();
 258		// Don't colour these even if they're different. Used for things
 259		// like IP addresses, or for numbers of packages, where one number
 260		// isn't necessarily "better" than the other.
 261
 262	protected $no_compare = array();
 263		// Don't compare fields in this array. You won't see them on the
 264		// grid at all
 265
 266	protected $sparse_list = array();
 267		// Fields which are displayed with "holes" in them
 268
 269	private $width = "width:80em";
 270		// The width of the compare grid (all columns combined)
 271
 272	private $colwidth;
 273		// the % width of each column in the comparison table
 274
 275	public function __construct($type, $data, $map)
 276	{
 277		// Start off by printing the header
 278
 279		if (!isset($this->disp_name))
 280			$this->disp_name = $type;
 281
 282		$this->html = "\n\n<table align=\"center\" style=\"$this->width\">"
 283		. "\n<tr><td class=\"nocol\"><h1>" . ucfirst($this->disp_name)
 284		. " audit comparison</h1></td>"
 285		. "</tr>\n</table>\n";
 286		
 287		// If all we have is hostname and audit completed, we're done
 288
 289		$i = 0;
 290
 291		foreach($data as $svr => $dat) {
 292			$i += count($dat);
 293		}
 294
 295		if ($i == 4) {
 296			$this->html .= "<p class=\"center\">No data to compare.</p>";
 297			return;
 298		}
 299
 300		$this->map = $map;
 301
 302		// Get the hostnames and all the rows we're going to compare
 303
 304		$this->hosts = array_keys($data);
 305		$this->cols = count($this->hosts);
 306
 307		// This is slow and messy, but I can't think of a better way to do
 308		// it right now
 309
 310		$rows = array();
 311
 312		foreach($this->hosts as $host) {
 313			
 314			foreach($data[$host] as $key=>$value) {
 315
 316				if (!in_array($key, $rows)) $rows[] = $key;
 317			}
 318
 319		}
 320
 321		// We may need to move "audit completed" to the last row
 322
 323		if (end($rows) != "audit completed") {
 324			$k = array_search("audit completed", $rows);
 325			unset($rows[$k]);
 326			$rows[] = "audit completed";
 327		}
 328
 329		$this->data = $data;
 330		$this->rows = $rows;
 331		$this->colwidth = round(100 / $this->cols) . "%";
 332		$this->html .= $this->compare_class();
 333
 334
 335		if (method_exists($this, "cmp_key")) {
 336			$this->html .= "\n\n<table align=\"center\" style=\""
 337			. $this->width . "\">\n<tr><td class=\"nocol\"><p class=\"center\">"
 338			. $this->cmp_key() .  "</p></td></tr></table>";
 339		}
 340	}
 341
 342	protected function compare_class()
 343	{
 344		// Print the comparison tables for the class
 345
 346		$ret = "\n\n<table cellspacing=\"1\" class=\"audit\" align=\"center\""
 347		. " style=\"$this->width\">\n";
 348
 349		// Step through each row
 350
 351		foreach($this->rows as $row) {
 352
 353			// Some fields have can special functions which we use to
 354			// compare them.  The rest use the compare_generic() method.
 355			// Work out what that method should be called, see if it exists,
 356			// and call
 357
 358			$method_base = preg_replace("/\s/", "_", $row);
 359			$pre_method = "preproc_$method_base";
 360			$cmp_method = "compare_$method_base";
 361			$post_method = "postproc_$method_base";
 362
 363			// Application and tool have a preproc_generic
 364
 365			if (!method_exists($this, $pre_method) && method_exists($this,
 366				"preproc_generic"))
 367				$pre_method = "preproc_generic";
 368
 369			$cmp_data =$this->get_cmp_data($row);
 370
 371			if (method_exists($this, $pre_method)) {
 372
 373				// We have at least two servers to compare, so there are at
 374				// least two arrays of data to process. Put a loop here to
 375				// do them all, rather than having to loop in every single
 376				// preproc_() method
 377
 378				$proc_arr = array();
 379
 380				foreach($cmp_data as $svr) {
 381					$proc_arr[] = $this->$pre_method($svr);
 382				}
 383
 384				$cmp_data = $proc_arr;
 385			}
 386
 387			// the $no_compare[] array may tell us we don't wish to compare
 388			// this row.
 389
 390			if (in_array($row, $this->no_compare)) {
 391				$rowdat = "don't compare";
 392				continue;
 393			}
 394			elseif(method_exists($this, $cmp_method)) {
 395				$rowdat = $this->$cmp_method($cmp_data);
 396			}
 397			else {
 398
 399				$rowdat = (in_array($row, $this->sparse_list))
 400					? $this->compare_sparse($cmp_data, $row)
 401					: $this->compare_generic($cmp_data, $row);
 402			}
 403
 404			// Do we need to post-process the compared data?
 405
 406			if (method_exists($this, $post_method))
 407				$rowdat = $this->$post_method($rowdat);
 408
 409			// We colour the left-hand column red or green, depending on
 410			// whether we found a difference or not. First, though, we work
 411			// out all the host rows 
 412
 413			$d = "";
 414			$keycol = "solidgreen";
 415
 416			foreach($rowdat as $line) {
 417
 418				if ($d != "")
 419					$d .= "\n<tr>";
 420
 421				if (isset($line["all"])) {
 422
 423					$cl = (isset($line["call"]))
 424						? $line["call"]
 425						: false;
 426
 427					$d .= new Cell($line["all"], $cl, false, false,
 428					$this->cols);
 429				}
 430				else {
 431
 432					if (!isset($line["ca"])) $line["ca"] = false;
 433					if (!isset($line["cb"])) $line["cb"] = false;
 434
 435					$d .= new Cell($line["a"], $line["ca"], false,
 436					$this->colwidth) . new Cell($line["b"], $line["cb"], false,
 437					$this->colwidth);
 438					$keycol = "solidred";
 439				}
 440
 441				$d .= "</tr>";
 442			}
 443
 444			if ($row == "hostname") {
 445				$ret .= "\n\n<tr><td class=\"blank\"></td>$d";
 446			}
 447			else {
 448				$ret .= "\n<tr><th class=\"$keycol\" ";
 449			
 450				if (count($rowdat) > 1)
 451					$ret .= "rowspan=\"" . count($rowdat) . "\"";
 452
 453				$ret .= ">$row</th>$d";
 454			}
 455			
 456		}
 457
 458		return $ret . "\n</table>";
 459	}
 460
 461	protected function get_cmp_data($row)
 462	{
 463		// Get the "row" data for all hosts, and put it in an array. For
 464		// now, this only operates on two hosts. May increase later, which
 465		// is why it's in a separate method
 466
 467		$ret = array();
 468
 469		foreach($this->hosts as $host) {
 470
 471			$ret[] = isset($this->data[$host][$row])
 472				? $this->data[$host][$row]
 473				: array(false);
 474		}
 475
 476		return $ret;
 477	}
 478
 479	protected function compare_generic($data, $row)
 480	{
 481		// This is the method which does 90% of the comparisons. Returns an
 482		// array of arrays, each of which has these possible elements:
 483		// [all] = value  : both values are the same
 484		// [a] = value in LH column
 485		// [b] = value in RH column
 486		// [ca] = class for LH column
 487		// [cb] = class for RH column
 488
 489		// First find things that are the same
 490
 491		$ret = array();
 492
 493		foreach(array_intersect($data[0], $data[1]) as $match) {
 494			$ret[] = array("all" => $match, "ca" => false, "cb" => false);
 495		}
 496
 497		// Now get the differences looking both ways. We do the
 498		// array_values() call so the indicies start at zero
 499
 500		$ma = array_values(array_diff($data[0], $data[1]));
 501		$mb = array_values(array_diff($data[1], $data[0]));
 502
 503		// We have to make the arrays the same size
 504		
 505		$sa = $diffs = count($ma);
 506		$sb = count($mb);
 507
 508		if ($sa > $sb)
 509			$mb = array_pad($mb, $sa, false);
 510		elseif ($sb > $sa) {
 511			$ma = array_pad($ma, $sa, false);
 512			$diffs = $sb;
 513		}
 514
 515		// Do we need to get the highest version?
 516
 517		if (method_exists($this, "get_highver") && !in_array($row,
 518			$this->no_colour))
 519			$hv = $this->get_highver($data);
 520		
 521		// Go through the differences, colouring (or not) as we go
 522
 523		for($i = 0; $i < $diffs; $i++) {
 524
 525			if (!isset($ma[$i])) $ma[$i] = false;
 526			if (!isset($mb[$i])) $mb[$i] = false;
 527
 528			$tmp_arr = array("a" => $ma[$i], "b" => $mb[$i]);
 529
 530			if (in_array($row, $this->no_colour)) {
 531				$tmp_arr["ca"] = $tmp_arr["cb"] = false;
 532			}
 533			elseif(isset($hv)) {
 534
 535				if (preg_match("/$hv/", $ma[$i]))
 536					$tmp_arr["ca"] = "ver_l";
 537				elseif(!empty($ma[$i]))
 538					$tmp_arr["ca"] = "ver_o";
 539
 540				if (preg_match("/$hv/", $mb[$i]))
 541					$tmp_arr["cb"] = "ver_l";
 542				elseif(!empty($mb[$i]))
 543					$tmp_arr["cb"] = "ver_o";
 544
 545			}
 546			else {
 547
 548				if ($this->safe_compare($ma[$i], $mb[$i])) {
 549					$tmp_arr["ca"] = "ver_l";
 550					$tmp_arr["cb"] = "ver_o";
 551				}
 552				else {
 553					$tmp_arr["ca"] = "ver_o";
 554					$tmp_arr["cb"] = "ver_l";
 555				}
 556				
 557			}
 558
 559			$ret[] = $tmp_arr;
 560		}
 561
 562		return $ret;
 563	}
 564
 565	protected function compare_sparse($data)
 566	{
 567		// Make a big array of everything, sort it, then see what's in
 568		// what. It's not really a comparison, just separates everthing on
 569		// both hosts into one, the other, or both.
 570
 571		$whole = $tmp_arr = $ret = array();
 572
 573		$a = $data[0];
 574		$b = $data[1];
 575		
 576		$whole = array_merge($a, $b);
 577
 578		$whole = array_unique($whole);
 579		natsort($whole);
 580
 581		foreach($whole as $el) {
 582			
 583			if (in_array($el, $a) && in_array($el, $b))
 584				$tmp_arr["all"] = $el;
 585			elseif (in_array($el, $a))
 586				$tmp_arr = array("a" => $el, "b" => false);
 587			else
 588				$tmp_arr = array("a" => false, "b" => $el);
 589
 590			$ret[] = $tmp_arr;
 591		}
 592
 593		return $ret;
 594
 595	}
 596
 597	protected function compare_hostname($data)
 598	{
 599		// Hostname gets a special row so we can set classes
 600
 601		$a = array(
 602
 603			array(
 604				"a" => $data[0][0],
 605				"b" => $data[1][0],
 606				"ca" => "keyhead",
 607				"cb" => "keyhead"
 608			)
 609
 610		);
 611
 612		return $a;
 613	}
 614
 615	protected function compare_audit_completed($data)
 616	{
 617		// Work out the time difference between oldest and newest audits,
 618		// and colour accordingly. First row spans both columns and tells
 619		// you the time difference between the oldest and newest audits.
 620		// Seconds row displays the time of each
 621
 622		$d_arr = array();
 623
 624		$now = mktime();
 625
 626		foreach($data as $datum) {
 627			$d = preg_split("/[:\s\/]+/", $datum[0]);
 628			$d_arr[] = mktime($d[0], $d[1], $d[2], $d[3], $d[4], $d[5]);
 629		}
 630		
 631		sort($d_arr);
 632		reset($d_arr);
 633		$first = current($d_arr);
 634		$last = end($d_arr);
 635		$diff = $last - $first;
 636
 637		// If we're comparing two dates, say they're x apart, if it's more
 638		// than two, talk about a spread
 639
 640		$string = (count($data) == 2)
 641			? "apart"
 642			: "spread";
 643
 644		$diff_col = "green";
 645
 646		if ($diff == 0) {
 647			$txt = "identical times";
 648		}
 649		elseif ($diff < 60) {
 650			$txt = "$diff second(s) $string";
 651		}
 652		elseif ($diff < 3600) {
 653			$txt = round($diff / 60) . " minute(s) $string";
 654		}
 655		elseif ($diff < 216000) {
 656			$txt = round(($diff / 3660), 1) . " hour(s) $string";
 657			$diff_col = "amber";
 658		}
 659		else {
 660			$txt = round($diff / 216000) . " day(s) $string";
 661			$diff_col = "red";
 662		}
 663	
 664		return array(
 665			array("all" => "$txt", "call" => "solid$diff_col"),
 666			array("a" => $data[0][0], "b" => $data[1][0], "ca" => false,
 667			"cb" => false)
 668		);
 669	}
 670
 671	public function safe_compare($a, $b)
 672	{
 673		// If you're comparing version numbers and you hit, say, 2.2.4 and
 674		// 2.2.11, a normal >/< type comparison will tell you 2.2.4 is the
 675		// later version, which it plainly isn't. This functon uses PHP's
 676		// natural sort algorithm to get the higher version. It also ignores
 677		// anything that's not part of the version string. (i.e. anything
 678		// after the first space)
 679		
 680		// returns true if a > b
 681		// returns false otherwise
 682
 683		$ea = preg_replace("/ .*$/", "", $a);
 684		$eb = preg_replace("/ .*$/", "", $b);
 685
 686		$arr = array($a, $b);
 687		natsort($arr);
 688
 689		return (current($arr) == $a) ? false : true;
 690	}
 691
 692	protected function preproc_bold_first_word($data, $sep = ":")
 693	{
 694		// For things that start "something:", put "something" in bold
 695
 696		return preg_replace("/^(.*)($sep)/U", "<strong>$1$2</strong>",
 697		$data);
 698	}
 699
 700	public function __toString()
 701	{
 702		return $this->html;
 703	}
 704
 705}
 706
 707//----------------------------------------------------------------------------
 708// PLATFORM AUDITS
 709
 710class comparePlatform extends compareGeneric {
 711
 712	protected $no_colour = array("hostname", "audit completed", "hardware",
 713	"virtualization", "serial number", "ALOM IP", "card", "memory",
 714	"storage", "EEPROM");
 715
 716	protected function preproc_storage($data)
 717	{
 718		return $this->preproc_bold_first_word($data);
 719	}
 720
 721	protected function preproc_EEPROM($data)
 722	{
 723		return $this->preproc_bold_first_word($data, "=");
 724	}
 725
 726}
 727
 728//----------------------------------------------------------------------------
 729// O/S AUDITS
 730
 731class compareOS extends compareGeneric {
 732
 733	protected $no_colour = array("hostname", "audit completed", "hostid",
 734	"uptime", "local zone", "distribution", "VM", "scheduler",
 735	"SMF services", "packages", "patches", "boot env", "publisher");
 736
 737	protected $disp_name = "O/S";
 738
 739	public function __construct($type, $data, $map)
 740	{
 741		// If the distributions aren't the same. don't colour the version or
 742		// kernel
 743
 744		$dist_arr = array();
 745
 746		foreach($data as $svr=>$dat) {
 747			$dist_arr[] = $dat["distribution"];
 748		}
 749
 750		$dists = array_unique($dist_arr);
 751
 752		if (count($dists > 1)) {
 753			$this->no_colour[] = "version";
 754			$this->no_colour[] = "kernel";
 755			$this->no_colour[] = "release";
 756		}
 757
 758		parent::__construct($type, $data, $map);
 759
 760	}
 761
 762	protected function preproc_boot_env($data)
 763	{
 764		return $this->preproc_bold_first_word($data);
 765	}
 766
 767	protected function preproc_VM($data)
 768	{
 769		// For now I'm just going to bold the VM type and strip out any [].
 770		// Might do more with this in future
 771
 772		return $this->preproc_bold_first_word(preg_replace("/\[\]/", "",
 773		$data));
 774	}
 775
 776}
 777
 778//----------------------------------------------------------------------------
 779// NETWORK AUDITS
 780
 781class compareNet extends compareGeneric {
 782
 783	protected $no_colour = array("hostname", "audit completed", "NTP",
 784	"name service", "DNS server", "port", "route", "NIC", "routing");
 785
 786	protected $sparse_list = array("port");
 787
 788	protected function preproc_name_service($data)
 789	{
 790		return $this->preproc_bold_first_word($data);
 791	}
 792
 793	protected function preproc_route($data)
 794	{
 795		return $this->preproc_bold_first_word($data, "\s");
 796	}
 797
 798	protected function preproc_port($data)
 799	{
 800		// As on the net audit, we may want to remove high numbered ports.
 801		// Process the info in the same way too
 802
 803		$ret = array();
 804
 805		foreach($data as $datum) {
 806			$a = explode(":", $datum);
 807
 808			if ((defined("OMIT_PORT_THRESHOLD")) && ($a[0] >
 809				OMIT_PORT_THRESHOLD))
 810				continue;
 811
 812			if (empty($a[1])) $a[1] = "-";
 813			if (empty($a[2])) $a[2] = "-";
 814				
 815			$ret[] = "<strong>$a[0]</strong> ($a[1]/$a[2])";
 816		}
 817
 818		return $ret;
 819	}
 820
 821	protected function preproc_nic($data)
 822	{
 823
 824		$ret = array();
 825
 826		foreach($data as $datum) {
 827			$a = explode("|", $datum);
 828			$speed = "unknown speed";
 829
 830			// Split the speed/duplex into two parts
 831
 832			$sa = preg_split("/:|-/", $a[4]);
 833			
 834			// Now $sa[0] is the speed, $sa[1] is the duplex. I don't want
 835			// the "b" on the speed.
 836			
 837			$sa[0] = str_replace("b", "", $sa[0]);
 838			
 839			// Make the speed "1G" if it's 1000M. Also look out for long
 840			// strings from kstat.
 841			
 842			if ($sa[0] == "1000M" || $sa[0] == "1000000000")
 843				$sa[0] = "1G";
 844			elseif ($sa[0] == "100000000")
 845				$sa[0] = "100M";
 846			elseif ($sa[0] == "10000000")
 847				$sa[0] = "10M";
 848			
 849			// Make the duplex part "full" if it's only "f", and "half" if
 850			// it's only "h"
 851			
 852			if (sizeof($sa) > 1) {
 853			
 854				if ($sa[1] == "f")
 855					$sa[1] = "full";
 856				elseif ($sa[1] == "h")
 857					$sa[1] = "half";
 858			
 859				$speed = "${sa[0]}bit/$sa[1] duplex";
 860			}
 861			
 862			$ret[] = "<strong>$a[0]:</strong> $a[1] ($speed) $a[5]";
 863		
 864		}
 865
 866		return $ret;
 867	}
 868
 869	protected function cmp_key()
 870	{
 871
 872		// If we've stripped out high numbered ports, say so
 873
 874		if (defined("OMIT_PORT_THRESHOLD"))
 875			return "NOTE: in the &quot;port&quot; comparison, open ports
 876			above " .  OMIT_PORT_THRESHOLD . " are not being displayed.";
 877	}
 878}
 879
 880//----------------------------------------------------------------------------
 881// FILESYSTEM AUDITS
 882
 883class compareFS extends compareGeneric {
 884
 885	protected $no_colour = array("hostname", "audit completed", "zpool",
 886	"root fs", "fs", "export", "capacity", "disk group");
 887
 888	protected $disp_name = "Filesystem";
 889
 890	protected function preproc_zpool($data)
 891	{
 892		// Bold the pool name and bin the scrub info
 893
 894		$data = preg_replace("/\(.*\) /", "", $data);
 895		return $this->preproc_bold_first_word($data, "\s");
 896
 897	}
 898
 899	protected function preproc_capacity($data)
 900	{
 901		// Bold the capacity. Might do more with this one day. Can't decide
 902		// now.
 903
 904		return $this->preproc_bold_first_word($data, "\s");
 905	}
 906
 907	protected function preproc_disk_group($data)
 908	{
 909		// Just bold the disk group name and show the number of disks in it.
 910		// Bin the rest.
 911
 912		$data = preg_replace("/ .*\[(\d+ disk).*$/", " [$1(s)]", $data);
 913		return $this->preproc_bold_first_word($data, "\s");
 914	}
 915
 916	protected function preproc_fs($data)
 917	{
 918		// Keep the mountpoint, filesystem type and device. Leaving anything
 919		// else makes comparisons pointless because it'll never match. Omit
 920		// options.
 921
 922		$ret = array();
 923
 924		foreach($data as $datum) {
 925			if (!preg_match("/unmounted/", $datum))
 926				$ret[] = $datum;
 927		}
 928
 929		return preg_replace("/^(\S+) (\w+).*\(([^;]*);.*$/",
 930		"<strong>$1</strong> $3 ($2)", $ret);
 931	}
 932
 933	protected function preproc_export($data)
 934	{
 935		// Bold the export name, keep the export type, bin the rest
 936	
 937		return preg_replace("/^(\S+) (\(\w+\)).*$/", "<strong>$1</strong> $2",
 938		$data);
 939	}
 940
 941
 942}
 943
 944//----------------------------------------------------------------------------
 945// APPLICATION AUDITS
 946
 947class compareApp extends compareGeneric {
 948
 949	protected $disp_name = "Application";
 950
 951	protected $no_colour = array("AI server");
 952
 953	private $cmp_vals = array();
 954
 955	protected function preproc_generic($data)
 956	{
 957		// Split up the version and the path info
 958	
 959		return preg_replace("/@=(.*).*$/", " [$1]$2", $data);
 960
 961	}
 962
 963	protected function postproc_sshd($data)
 964	{
 965		// Don't colour if we're comparing SunSSH and OpenSSH. Just look at
 966		// the first letter 
 967	
 968		$cmp_arr = array();
 969
 970		foreach($data as $row => $dat) {
 971			if (!empty($dat["a"])) $cmp_arr[] = $dat["a"][0];
 972			if (!empty($dat["b"])) $cmp_arr[] = $dat["b"][0];
 973		}
 974
 975		$cmp_arr = array_unique($cmp_arr);
 976
 977		return (count($cmp_arr) == 1)
 978			? $data
 979			: $this->uncolour($data);
 980
 981	}
 982
 983	protected function postproc_x_server($data)
 984	{
 985		// Don't colour if we have XSun. (We'd be either comparing to Xorg,
 986		// or always get an identical match as XSun doesn't report a
 987		// version.)
 988
 989		$xsun = false;
 990
 991		foreach($data as $row => $dat) {
 992
 993			if (!isset($dat["a"])) $dat["a"] = false;
 994			if (!isset($dat["b"])) $dat["b"] = false;
 995
 996			if (preg_match("/Xsun/", $dat["a"]) || preg_match("/Xsun/",
 997				$dat["b"])) {
 998				$xsun = true;
 999				break;
1000			}
1001
1002		}
1003
1004		return ($xsun)
1005			? $this->uncolour($data)
1006			: $data;
1007
1008	}
1009
1010	private function uncolour($data)
1011	{
1012		// strip colouring info out of an array
1013
1014		$ret = array();
1015		$cols = array("ca" => true, "cb" => true);
1016
1017		foreach($data as $datum) {
1018			$ret[] = array_diff_key($datum, $cols);
1019		}
1020
1021		return $ret;
1022	}
1023
1024
1025	protected function get_highver($data)
1026	{
1027		// We may have two versions of python on one box and four on
1028		// another. This attempts to handle that by finding the highest
1029		// version. This information is fed back into the compare_generic()
1030		// method.
1031		
1032		$vers = array();
1033
1034		foreach($data as $svr) {
1035			$vers = array_merge($vers, $svr);
1036		}
1037
1038		// strip off the path, and sort. Get the highest version
1039
1040		$vers = preg_replace("/\s*[\(\[].*$/", "", $vers);
1041		natsort($vers);
1042
1043		return end($vers);
1044	}
1045
1046}
1047
1048//----------------------------------------------------------------------------
1049// TOOL AUDITS
1050
1051class compareTool extends compareApp {
1052
1053	protected $disp_name = "Tool";
1054
1055	protected $no_colour = array();
1056}
1057
1058//----------------------------------------------------------------------------
1059// SECURITY AUDITS
1060
1061class compareSecurity extends compareGeneric {
1062
1063	protected $sparse_list = array("user");
1064
1065	protected $no_colour = array("user_attr", "root shell", "dtlogin",
1066	"cron job");
1067
1068	protected function preproc_user_attr($data)
1069	{
1070		return $this->preproc_bold_first_word($data);
1071	}
1072
1073	protected function preproc_dtlogin($data)
1074	{
1075		// Split up the version and the path info
1076	
1077		return preg_replace("/@=(.*).*$/", " [$1]$2", $data);
1078
1079	}
1080
1081	protected function preproc_cron_job($data)
1082	{
1083		// Cron jobs need HTMLizing. Also put the user in bold
1084
1085		$ret = array();
1086
1087		foreach($data as $datum) {
1088			$ret[] = htmlentities($datum);
1089		}
1090
1091		return $this->preproc_bold_first_word($ret);
1092	}
1093}
1094
1095//----------------------------------------------------------------------------
1096// HOSTED SERVICES AUDITS
1097
1098class compareHosted extends compareGeneric {
1099
1100	protected $disp_name = "Hosted Services";
1101}
1102
1103//----------------------------------------------------------------------------
1104// PATCH AND PACKAGE AUDITS
1105
1106class comparePatch extends compareGeneric {
1107
1108	protected $disp_name = "Patch and Package";
1109
1110	protected $sparse_list = array("patch", "package");
1111}
1112
1113?>