#!/usr/local/bin/perl

use SNMP;
use DBI;
use CGI;
$SNMP::save_descriptions=1;
init_mib;

%myorder = qw(id 0 oidindex 1 host 2 updated 3);

#===========================================================================
#  Global defines
#===========================================================================

$hostname = 'localhost';          # Host that serves the mSQL Database
$dbname = 'snmp';                 # mySQL Database name
$user = 'root';
#$pass = "password";
$redimage = "/graphics/1.gif";
$greenimage = "/graphics/0.gif";
#$verbose = 1;
$tableparms = "border=3 ipad=3 bgcolor=#d0d0d0";

$remuser = $ENV{'REMOTE_USER'};
$remuser = "guest" if (!defined($remuser) || $remuser eq "");

# read in a configuration file if it exists at .FILE.conf.
$conffile = "$0.conf";
$conffile =~ s/(\\|\/)([^\\\/]*)$/$1.$2/;
if (-f $conffile) {
    do "$conffile";
}

#===========================================================================
# Connect to the mSQL database with the appropriate driver
#===========================================================================
( $dbh = DBI->connect("DBI:mysql:database=$dbname;host=$hostname", $user, $pass))
    or die "\tConnect not ok: $DBI::errstr\n";

$query = new CGI;

#===========================================================================
# Start HTML.
#===========================================================================
print "Content-type:  text/html\n\n<body bgcolor=\"#ffffff\">\n";

#===========================================================================
# Display mib related information
#===========================================================================
if ($dispinfo = $query->param('displayinfo')) {
    makemibtable($dispinfo);
    Exit(0);
}

#===========================================================================
# Display a generic sql table of any kind.
#===========================================================================
if ($disptable = $query->param('displaytable')) {
    if ($query->param('editable') == 1) {
	print "<form submit=dont>\n";
	displaytable($disptable, -editable, 1);
	print "</form>\n";
    } else {
	displaytable($disptable);
    }
    Exit(0);
}

#===========================================================================
# Get host and group from CGI query.
#===========================================================================
$host = $query->param('host');
$group = $query->param('group');


#===========================================================================
# show the list of groups a user belongs to.
#===========================================================================
if (!defined($group)) {
    @groups = getgroupsforuser($remuser);
    if ($#groups > 0) {
	displaytable('usergroups', 
		     '-clauses', "where (user = '$remuser')",
		     '-select', 'distinct groupname',
		     '-printonly', ['groupname'],
		     '-datalink', sub { my $q = $query->self_url;
					my $key = shift;
					my $h = shift;
					return if ($key ne "groupname");
					return addtoken($q,"group=$h");
				    },
		     '-beginhook', 
		     sub { 
			 my $q = $query->self_url;
			 my($junk, $data) = @_;
			 if (!defined($data)) {
			     print "<td></td>";
			     return;
			 }
			 my ($cur, $row);
			 $cur = getcursor("select host from hostgroups where groupname = '$data->{groupname}'");
			 while (  $row = $cur->fetchrow_hashref ) {
			     if (checkhost($data->{'groupname'}, 
					   $row->{'host'})) {
				 print "<td><a href=\"" . addtoken($q,"group=$data->{groupname}&summarizegroup=1") . "\"><img border=0 src=$redimage></a></td>\n";
				 return;
			     }
			 }
			 print "<td><img src=$greenimage></td>\n";
		     }
		     );
	$dbh->disconnect();
	Exit(0);
    } else {
	if ($#groups == -1) {
	    print "You are not configured to use the ucd-snmp-manager, please contact your system administrator.";
	    Exit(0);
	}
	$group = $groups[0];
    }
}

#===========================================================================
# reject un-authorized people accessing a certain group
#===========================================================================
if (!isuser()) {
    print "Unauthorized access to that group ($group)\n";
    Exit(0);
}    

#===========================================================================
# add a new host to a group
#===========================================================================
if (defined($newhost = $query->param('newhost'))) {
    if (isadmin()) {
	if ($dbh->do("select * from hostgroups where host = '$newhost' and groupname = '$group'") eq "0E0") {
	    $dbh->do("insert into hostgroups(host,groupname) values('$newhost','$group')") ;
	} else {
	    print "<b>ERROR: host $newhost already in $group</b>\n";
	}
	$query->delete('newhost');
    }
}

#===========================================================================
# display setup configuration for a group
#===========================================================================
if (defined($query->param('setupgroup')) && 
    isadmin()) {
    setupgroup($group);
    Exit(0);
}

#===========================================================================
# save configuration information submitted about a group
#===========================================================================
if (defined($query->param('setupgroupsubmit')) && 
    isadmin()) {
    setupgroupsubmit($group);
    $query->delete_all();
    $query->param(-name => 'group', -value => $group);
    print "<a href=\"" . $query->self_url . "\">Entries submitted</a>";
    Exit(0);
}

#===========================================================================
# user preferences
#===========================================================================
if (defined($query->param('userprefs'))) {
    setupuserpreferences($group);
    Exit(0);
}

#===========================================================================
# save submitted user preferences
#===========================================================================
if (defined($query->param('setupuserprefssubmit')) && 
    isadmin()) {
    setupusersubmit($group);
    $query->delete_all();
    $query->param(-name => 'group', -value => $group);
    print "<a href=\"" . $query->self_url . "\">Entries submitted</a>";
    Exit(0);
}

#===========================================================================
# summarize problems in a group
#===========================================================================
if (defined($query->param('summarizegroup'))) {
    print "<title>group summary: $group</title>\n";
    summarizeerrors("where groupname = '$group'");
    Exit(0);
}

#===========================================================================
# summarize problems on a host
#===========================================================================
if (defined($host) && defined($query->param('summarizehost'))) {
    print "<title>host summary: $host</title>\n";
    summarizeerrors("where groupname = '$group' and host = '$host'");
    Exit(0);
}

#===========================================================================
# display a list of hosts in a group
#===========================================================================
if (!defined($host)) {
    displaytable('hostgroups', 
		 '-notitle',0,
		 '-clauses', "where (groupname = '$group')",
		 '-select', 'distinct host',
		 '-datalink', sub { my $q = $query->self_url;
				    my $key = shift;
				    my $h = shift;
				    return if ($key ne "host");
				    return addtoken($q,"host=$h");
				},
		 '-beginhook', 
		 sub { 
		     my $q = $query->self_url;
		     my($junk, $data) = @_;
		     if (!defined($data)) {
			 print "<td></td>";
			 return;
		     }
		     if (checkhost($group, $data->{'host'})) {
			 print "<td><a href=\"" . addtoken($q,"group=$group&summarizehost=1&host=$data->{host}") . "\"><img border=0 src=$redimage></a></td>\n";
		     } else {
			 print "<td><img src=$greenimage></td>\n";
		     }
		 }
		 );
    if (isadmin()) {
	addhostentryform();
	my $q = $query->self_url;
	$q =~ s/\?.*//;
	print "<a href=\"" . addtoken($q,"group=$group&setupgroup=1") . "\">setup group $group</a>\n";
    }
    Exit(0);
}

#===========================================================================
# display inforamation about a host
#===========================================================================
showhost($host);
Exit(0);

#===========================================================================
# END of main
#===========================================================================


# add a token to a url string.  Use either a ? or an & depending on
# existence of ?.
sub addtoken {
    my $url = shift;
    my $token = shift;
    return "$url&$token" if ($url =~ /\?/);
    return "$url?$token";
}

#
# summarizeerrors(CLAUSE):
#   summarize the list of errors in a given CLAUSE
#
sub summarizeerrors {
    my $clause = shift;
    print "<table $tableparms><tr><td><b>Host</b></td><td><b>Table</b></td><td><b>Description</b></td></tr>\n";
    my $cursor = 
	getcursor("SELECT * FROM hosttables $clause");

    while (  $row = $cursor->fetchrow_hashref ) {
	my $exprs = getcursor("SELECT * FROM errorexpressions where (tablename = '$row->{tablename}')");
	
	while (  $expr = $exprs->fetchrow_hashref ) {
	    my $errors = getcursor("select * from $row->{tablename} where $expr->{expression} and host = '$row->{host}'");
	    while (  $error = $errors->fetchrow_hashref ) {
		print "<tr><td>$row->{host}</td><td>$row->{tablename}</td><td>$expr->{returnfield}: $error->{$expr->{returnfield}}</td></tr>";
	    }
	}
    }
    print "</table>";
}

#
# getcursor(CMD):
#    genericlly get a cursor for a given sql command, displaying and
#    printing errors where necessary.
#
sub getcursor {
    my $cmd = shift;
    my $cursor;
    ( $cursor = $dbh->prepare( $cmd ))
	or die "\nnot ok: $DBI::errstr\n";
    ( $cursor->execute )
	or print( "\tnot ok: $DBI::errstr\n" );
    return $cursor;
}

#
# mykeysort($a, $b)
#    sorts $a and $b against the order in the mib or against the hard
#    coded special list.
#
sub mykeysort {
    $mb = $SNMP::MIB{SNMP::translateObj($b)};
    $ma = $SNMP::MIB{SNMP::translateObj($a)};
    return $myorder{$a} <=> $myorder{$b} if ((defined($myorder{$a}) || !defined($ma->{'subID'})) && (defined($myorder{$b}) || !defined($mb->{'subID'})));
    return 1 if (defined($myorder{$b}) || !defined($mb->{'subID'}));
    return -1 if (defined($myorder{$a}) || !defined($ma->{'subID'}));

    $ma->{'subID'} <=> $mb->{'subID'};
}

#
# checkhost(GROUP, HOST):
#    if anything in a host is an error, as defined by the
#    errorexpressions table, return 1, else 0
#
sub checkhost {
    my $group = shift;
    my $host = shift;
    my ($tblh);

    return 2 if ($dbh->do("select * from hosterrors where host = '$host'") ne "0E0");

    # get a list of tables we want to display
    $tblh = getcursor("SELECT * FROM hosttables where (host = '$host' and groupname = '$group')");

    # table data
    my($exprs, $tablelist);
    while ( $tablelist = $tblh->fetchrow_hashref ) {
	$exprs = getcursor("SELECT * FROM errorexpressions where (tablename = '$tablelist->{tablename}')");
	while($expr = $exprs->fetchrow_hashref) {
	    if ($dbh->do("select * from $tablelist->{tablename} where $expr->{expression} and host = '$host'") ne "0E0") {
		return 1;
	    }
	}
    }
    return 0;
}

#
#  showhost(HOST):
#
#    display all the tables monitored for a given host (in a group).
#
sub showhost {
    my $host = shift;
    # host header
    print "<title>ucd-snmp manager report for host: $host</title>\n";
    print "<h3>host: $host</h3>\n";

    # does the host have a serious error?

    my $errlist = getcursor("SELECT * FROM hosterrors where (host = '$host')");
    if ( $dbh->do("SELECT * FROM hosterrors where (host = '$host')") ne "0E0") {
	displaytable('hosterrors', 
		     '-clauses', "where (host = '$host')",
		     '-dontdisplaycol', "select * from userprefs where user = '$remuser' and groupname = '$group' and tablename = ? and columnname = ? and displayit = 'N'",
		     '-beginhook', sub {
			 if ($#_ < 1) {
			     #doing header;
			     print "<td></td>";
			 } else {
			     print "<td><img src=\"$redimage\"></td>\n";
			 }});
    }

    # get a list of tables we want to display
    my $tblh = getcursor("SELECT * FROM hosttables where (host = '$host' and groupname = '$group')");

    # table data
    my($tablelist);
    while (  $tablelist = $tblh->fetchrow_hashref ) {

	displaytable($tablelist->{'tablename'}, 
		     '-clauses', "where (host = '$host') order by oidindex",
		     '-dontdisplaycol', "select * from userprefs where user = '$remuser' and groupname = '$group' and tablename = ? and columnname = ? and displayit = 'N'",
		     '-sort', "mykeysort",
		     '-dolink', \&linktodisplayinfo,
		     '-beginhook', \&printredgreen);
    }
}

#
#  linktodisplayinfo(STRING):
#
#    returns a url to the appropriate displayinfo link if STRING is a
#    mib node.
#
sub linktodisplayinfo {
    return if (exists($myorder{$l}));
    return $query->self_url . "&displayinfo=" . shift;
}

# printredgreen(TABLENAME, DATA):
#
#   display a red or a green dot in a table dependent on the table's
#   values and associated expression
#
#   DATA is NULL when in a header row (displaying header names).
#
sub printredgreen {
    my $tablename = shift;
    my $data = shift;
    my ($exprs, $expr, $img);

    if (!defined($data)) {
	#doing header;
	print "<td></td>";
	return;
    }

    $cmd = "SELECT * FROM errorexpressions where (tablename = '$tablename')";
    print " $cmd\n" if ($verbose);
    ( $exprs = $dbh->prepare( $cmd ) )
	or die "\nnot ok: $DBI::errstr\n";
    ( $exprs->execute )
	or print( "\tnot ok: $DBI::errstr\n" );

    $img = $greenimage;
    while($expr = $exprs->fetchrow_hashref) {
	if ($dbh->do("select oidindex from $tablename where host = '$data->{host}' and oidindex = '$data->{oidindex}' and $expr->{expression}") ne "0E0") {
	    $img = $redimage;
	}
    }
    print "<td><img src=$img></td>";
}

# displaytable(TABLENAME, CONFIG...):
#
#   genericly displays any sql table in existence.
#
sub displaytable {
    my $tablename = shift;
    my %config = @_;
    my $clauses = $config{'-clauses'};
    my $sort = $config{'-sort'};
    my $dolink = $config{'-dolink'};
    my $datalink = $config{'-datalink'};
    my $beginhook = $config{'-beginhook'};
    my $selectwhat = $config{'-select'};
    my $printonly = $config{'-printonly'};
    $selectwhat = "*" if (!defined($selectwhat));
    my ($thetable, $data, $ref);

    # get a list of data from the table we want to display
    $thetable = getcursor("SELECT $selectwhat FROM $tablename $clauses");

    my $prefs = $dbh->prepare($config{'-dontdisplaycol'}) if ($config{'-dontdisplaycol'});

    # table header
    my $doheader = 1;
    my @keys;
    while( $data = $thetable->fetchrow_hashref ) {
	if ($doheader) {
	    if (defined($sort)) {
		@keys = (sort $sort keys(%$data));
	    } else {
		@keys = (sort keys(%$data));
	    }
	    if (!defined($config{'-notitle'})) {
		print "<br><b>";
		print "<a href=\"$ref\">" if (defined($dolink) && 
					      defined($ref = &$dolink($tablename)));
		print "$tablename";
		print "</a>" if (defined($ref));
		print "</b>\n";
	    }
	    print "<br>\n";
	    print "<table $tableparms>\n<tr>";
	    if (defined($beginhook)) {
		&$beginhook($tablename);
	    }
	    foreach $l (@keys) {
		if (!defined($prefs) || 
		    $prefs->execute($tablename, $l) eq "0E0") {
		    print "<td>";
		    print "<a href=\"$ref\">" if (defined($dolink) && 
						  defined($ref = &$dolink($l)));
		    print "$l";
		    print "</a>" if (defined($ref));
		    print "</td>";
		}
	    }
	    "</tr>\n";
	    $doheader = 0;
	}

	print "<tr>";
	if (defined($beginhook)) {
	    &$beginhook($tablename, $data);
	}
	foreach $key (@keys) {
	    if (!defined($prefs) || 
		$prefs->execute($tablename, $key) eq "0E0") {
		print "<td>";
		print "<a href=\"$ref\">" if (defined($datalink) && 
					      defined($ref = &$datalink($key, $data->{$key})));
		if (defined($config{'-editable'})) {
		    print "<input type=text name=\"$key.x.$data->{$key}\" value=\"$data->{$key}\">";
		} else {
		    print $data->{$key};
		}
		print "</a>" if (defined($ref));
		print "</td>";
	    }
	}
	print "</tr>\n";
    }
    print "</table>\n";
}
    

#
# display information about a given mib node as a table.
#
sub makemibtable {
    my $dispinfo = shift;
    # display information about a data type in a table
    my $mib = $SNMP::MIB{SNMP::translateObj($dispinfo)};
    print "<table $tableparms><tr><td>\n";
    foreach $i (qw(label type access status units hint moduleID description enums)) {
#    foreach $i (keys(%$mib)) {
	next if (!defined($$mib{$i}) || $$mib{$i} eq "");
	next if (ref($$mib{$i}) eq "HASH" && $#{keys(%{$$mib{$i}})} == -1);
	print "<tr><td>$i</td><td>";
	if (ref($$mib{$i}) eq "HASH") {
	    print "<table $tableparms><tr><td>\n";
	    foreach $j (sort { $$mib{$i}{$a} <=> $$mib{$i}{$b} } keys(%{$$mib{$i}})) {
 		print "<tr><td>$$mib{$i}{$j}</td><td>$j</td></tr>";
	    }
	    print "</table>\n";
	} else {
	    print "$$mib{$i}";
	}
	print "</td></tr>\n";
    }
    print "</table>\n";
}

# given a user, get all the groups he belongs to.
sub getgroupsforuser {
    my (@ret, $cursor, $row);
    my $remuser = shift;
    ( $cursor = $dbh->prepare( "SELECT * FROM usergroups where (user = '$remuser')"))
	or die "\nnot ok: $DBI::errstr\n";
    ( $cursor->execute )
	or print( "\tnot ok: $DBI::errstr\n" );

    while (  $row = $cursor->fetchrow_hashref ) {
	push(@ret, $row->{'groupname'});
    }
    @ret;
}

# given a host, get all the groups it belongs to.
sub gethostsforgroup {
    my (@ret, $cursor, $row);
    my $group = shift;
    ( $cursor = $dbh->prepare( "SELECT * FROM hostgroups where (groupname = '$group')"))
	or die "\nnot ok: $DBI::errstr\n";
    ( $cursor->execute )
	or print( "\tnot ok: $DBI::errstr\n" );

    while (  $row = $cursor->fetchrow_hashref ) {
	push(@ret, $row->{'host'});
    }
    @ret;
}

# display the host add entry box
sub addhostentryform {
    print "<form method=\"get\" action=\"" . $query->self_url . "\">\n";
    print "Add new host to group: <input type=\"text\" name=\"newhost\">";
    print "<input type=\"hidden\" name=\"group\" value=\"$group\">";
    print "</form>";
}

#is remuser a admin?
sub isadmin {
    my $user = shift;
    $user = $remuser if (!defined($user));
    return 0 if (!defined($user) || !defined($group));
    return 1 if ($dbh->do("select * from usergroups where user = '$user' and groupname = '$group' and isadmin = 'Y'") ne "0E0");
    return 0;
}

#is user a member of this group?
sub isuser {
    my $user = shift;
    $user = $remuser if (!defined($user));
    return 0 if (!defined($user) || !defined($group));
    return 1 if ($dbh->do("select * from usergroups where user = '$user' and groupname = '$group'") ne "0E0");
    return 0;
}

# displayconfigarray(HOSTS, NAMES, CONFIG):
#
#   displays an array of generic check buttons to turn on/off certain
#   variables.
sub displayconfigarray {
    my $hosts = shift;
    my $names = shift;
    my %config = @_;

    my $cmd;
    if ($config{'-check'}) {
	( $cmd = $dbh->prepare( $config{'-check'} ) )
	    or die "\nnot ok: $DBI::errstr\n";
    }

    print "<table $tableparms>\n";
    print "<tr><td></td>";
    my ($i, $j);
    foreach $j (@$names) {
	my $nj = $j;
	$nj = $j->[0] if ($config{'-arrayrefs'} || $config{'-arrayref2'});
	print "<td>$nj</td>";
    }
    foreach $i (@$hosts) {
	my $ni = $i;
	$ni = $i->[0] if ($config{'-arrayrefs'} || $config{'-arrayref1'});
	print "<tr><td>$ni</td>";
	foreach $j (@$names) {
	    my $nj = $j;
	    $nj = $j->[0] if ($config{'-arrayrefs'} || $config{'-arrayref2'});
	    my $checked = "checked" if (defined($cmd) && $cmd->execute($ni,$nj) ne "0E0");
	    print "<td><input type=checkbox $checked value=y name=" . $config{prefix} . $ni . $nj . "></td>\n";
	}
	print "</tr>\n";
    }	
    print "</tr>";
    print "</table>";
}


#
# display the setup information page for a given group.
#
sub setupgroup {
    my $group = shift;
    
    my ($hosts, $names) = gethostandgroups($group);

    print "<form method=\"post\" action=\"" . $query->self_url . "\">\n";
    print "<input type=hidden text=\"setupgroupsubmit\" value=\"y\">";
    displayconfigarray($hosts, $names, 
		       -arrayrefs, 1,
		       -check, "select * from hosttables where (host = ? and tablename = ? and groupname = '$group')");
    print "<input type=hidden name=group value=\"$group\">\n";
    print "<input type=submit value=submit name=\"setupgroupsubmit\">\n";
    print "</form>";
}

# a wrapper around fetching arrays of everything in a table.
sub getarrays {
    my $table = shift;
    my %config = @_;
    my $selectwhat = $config{'-select'} || "*";
    my $handle;
    
    $handle = getcursor("SELECT $selectwhat FROM $table $config{-clauses}");
    return $handle->fetchall_arrayref;
}

#
# get a list of all tablenames and hostnames for a given group.
#
sub gethostandgroups {
    my $group = shift;
    my ($tbnms);

    my $names = getarrays('hosttables', 
			  "-select", 'distinct tablename',
			  "-clauses", "where groupname = '$group'");

    my $hosts = getarrays('hostgroups', 
			  "-select", 'distinct host',
			  "-clauses", "where groupname = '$group'");
    
    return ($hosts, $names);
}

sub setupgroupsubmit {
    my $group = shift;
    
    my ($hosts, $names) = gethostandgroups($group);
    foreach $i (@$hosts) {
	$dbh->do("delete from hosttables where host = '${$i}[0]' and groupname = '$group'");
    }
    my $rep = $dbh->prepare("insert into hosttables(host,tablename,groupname) values(?,?,'$group')");

    foreach $i (@$hosts) {
	foreach $j (@$names) {
	    if ($query->param("${$i}[0]" . "${$j}[0]")) {
		$rep->execute("${$i}[0]", "${$j}[0]");
            }
	}
    }
    
}

#
# save user pref data submitted by the user
#
sub setupusersubmit {
    my $tables = getarrays('hosttables', 
			   "-select", 'distinct tablename',
			   "-clauses", "where groupname = '$group'");
    
    $dbh->do("delete from userprefs where user = '$remuser' and groupname = '$group'");
    my $rep = $dbh->prepare("insert into userprefs(user, groupname, tablename, columnname, displayit) values('$remuser', '$group', ?, ?, 'N')");

    my ($i, $j);
    foreach $i (@$tables) {
	my $sth = $dbh->prepare("select * from ${$i}[0] where 1 = 0");
	$sth->execute();

	foreach $j (@{$sth->{NAME}}) {
	    if ($query->param("${$i}[0]" . "$j")) {
		$rep->execute("${$i}[0]", "$j");
	    }
	}
    }
}

sub Exit {
    my $tq = $query->self_url;
    $tq =~ s/\?.*//;
    print "<hr>\n";
    print "<a href=\"$tq\">[TOP]</a>\n";
    print "<a href=\"$tq?userprefs=1\">[options]</a>\n";
    if (defined($group)) {
	print "<a href=\"$tq?group=$group\">[group: $group]</a>\n";
	print "<a href=\"$tq?group=$group&summarizegroup=1\">[summarize $group]</a>\n";
    }
    $dbh->disconnect() if (defined($dbh));
    exit shift;
}

#
# setup user preferences by displaying a configuration array of
# checkbuttons for each table.
#
sub setupuserpreferences {
    my $tables = getarrays('hosttables', 
			   "-select", 'distinct tablename',
			   "-clauses", "where groupname = '$group'");

    print "<h3>Select the columns from the tables that you want to <b>hide</b> below and click on submit:</h3>\n";
    print "<form method=\"post\" action=\"" . $query->self_url . "\">\n";

    my ($i, $j);
    foreach $i (@$tables) {
	my $sth = $dbh->prepare("select * from ${$i}[0] where 1 = 0");
	$sth->execute();
	displayconfigarray([${$i}[0]], $sth->{NAME},
			   -check, "select * from userprefs where (tablename = ? and columnname = ? and user = '$remuser' and groupname = '$group' and displayit = 'N')");
    print "<br>\n";
    }
    print "<input type=hidden name=group value=\"$group\">\n";
    print "<input type=submit value=submit name=\"setupuserprefssubmit\">\n";
    print "</form>";
}
