#!/usr/pkg/bin/perl
#
# $Header: /home/vikas/src/nocol/webnocol/RCS/webnocol.cgi,v 2.7 2000/03/21 21:11:07 vikas Exp $
#
#			webnocol.cgi
#
# A CGI script which interfaces with the html generated by the nocol web
# script 'genweb.pl'.
# This script is the companion to genweb.pl. It allows administrator to add
# status messages and to 'hide' devices as well as rudimentary troubleshooting.
#
####
# IMPORTANT: You MUST RESTRICT ACCESS TO THIS SCRIPT USING .htaccess or
#	the $authfile since this file gives access to various files and
#	programs on your system.
####
# Status Messages:
# 	A status message may be displayed next to a device alerting others
# as to the outage reason and duration. These status messages are, by default
# stored in the file $nocolroot/etc/updates. This file must be writable by
# your web server (often user nobody).
#
# Hiding Devices:
# 	If a device is going to be down for an extended time, you can 'hide'
# it from the critical view to keep it uncluttered. It still shows up in the
# other views. This program inserts a (H) in the 'updates' file to indicate
# that the event should be hidden.
#
# Device history:
#	This script can 'grep' for the sitename + siteaddr in the nocol
# logs (generated by noclogd). You should set $logfile to point to any
# logfile which has the most complete information about all the events
# (typically the one at the info level since that will have info about
# all the levels).
#
# Troubleshoot:
#	You can do a ping or traceroute or nslookup on the siteaddr. These
# commands can take a lot of time (and html will not return until the command
# is complete). Customize these commands, and make sure you specify the proper
# options to prevent these commands from running forever (e.g.ping -s on SunOS)
#
# Device Help:
#	This program searches for helpfiles under $nocolroot/helpfiles. It
# searches for $sitename:$siteaddr, $sitename:default, default:$siteaddr
# default:$variable default:$sender and finally for default. You should
# create help files (which are HTML files and hence can have html tags
# in them).
#
#
# AUTHOR:
#	Originally written by Richard Beebe (richard.beebe@yale.edu) 1996
#      	Rewritten Vikas Aggarwal (vikas@navya.com) 1998
#
#   This script was distributed as part of the NOCOL package.
#
# INSTALLATION:
# 	- Put this in your CGI-BIN directory. If you put it in some other
#	  location, you must edit the value of $processor here and in genweb.pl
#	- Create a null $updatesfile $cookiefile and ensure that both are
#	  writable by httpd (or owner of your web daemon).
#	- Create a valid $authfile which lists the valid users in it.
#	- Customize the locations and program syntaxes in the routine
#	  doTroubleshoot() below. Disable the commands if you dont want to
#	  offer access to them (search for check_this).
#	- Change $logfile to point to the 'info' or 'critical' level LOG file
#	  You can get this filename from your /nocol/etc/noclogd-conf entry
#	  for info level logging.
#
####
#############################################################################

## CUSTOMIZE THE LOCATION OF THIS CGI-SCRIPT AS SEEN BY httpd. This MUST
## run on the same host as NOCOL since this script needs to access the
## nocol data files.

# Remember to create the $updatesfile and the $authfile and also the
# help files.

#$processor = "http://www.host.com/cgi-bin/webnocol.cgi";
$processor = "/cgi-bin/webnocol.cgi";		# as seen by the browser

# The Makefile will edit the $nocolroot.
$nocolroot =  "/usr/pkg"   unless $nocolroot;	# SET_THIS
# this should be the info level noclogd log file.
$logfile = "$nocolroot/logs/info";		# check_this

$etcdir  = "$nocolroot/etc";
$bindir  = "$nocolroot/bin";

local ($helpdir) = "$nocolroot/help";		# dir for device help files
local ($updatesfile) = "$etcdir/updates";	# all update messages
local ($authfile) = "$nocolroot/etc/webusers";	# user:passwd:userlevel
local ($cookiefile) = "$nocolroot/etc/webcookies";

local ($ldebug) = 0;		# debugging flag, max level 5

push(@INC, $bindir);

#####
##### No customizations should be required below this
#####

# Check that the POST method is used
if ($ENV{'REQUEST_METHOD'} eq 'POST') {
    # POST method dicatates that we get the form input
    # from standard input
    read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
}
else {
    $buffer = $ENV{'QUERY_STRING'};
}	

# Split the the name-value pairs on &
@pairs = split(/&/,$buffer);

foreach $pair (@pairs) {
    ($name, $value) = split(/=/, $pair);
    $value =~ tr/+/ /;
    # Now convert any HTML'ized characters into their real world
    # equivalent. e.g. a %20 becomes a space. 
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
    $FORM{$name} = $value;
}
## Now all the form variables are in the $FORM associative array

local ($command)  = $FORM{'command'};	# what we are supposed to do
local ($sitename) = $FORM{'sitename'};
local ($siteaddr) = $FORM{'siteaddr'};
local ($variable) = $FORM{'variable'};

local ($done_footer) = 0;	# boolean flag

# Maintain a $userfile to set these (see sub authcheck() ). Lower number is
# higher priority. Basic level is '9'.
local ($userlevel) = 9;			# default authority is basic-level

## we dont need any userlevel for these following commands
if    ($command =~ /^Authenticate/i) { &doAuthenticate; exit 0; }
elsif ($command =~ /^About/i) { &aboutNocol; exit 0; }	# generic blurb
elsif ($command =~ /^Help/i)  { &aboutNocol; exit 0; }	# generic blurb
elsif ($command =~ /^UserHelp/i)  { &aboutNocol; exit 0; } # user level help
elsif ($command =~ /^NocolView/i)  { &restoreView; exit 0; }# nocol display

&set_userlevel;		# get and set the user privelege level

if    ($command =~ /^SiteHelp/i) { &doSiteHelp; }	# show site Help
elsif ($command =~ /^Update/i)  { &doUpdates; }	# maintain 'updates' file
elsif ($command =~ /^History/i) { &doHistory; }	# noclogd logs
elsif ($command =~ /^Device\s+Logs/i)    { &doHistory; }	# noclogd logs
elsif ($command =~ /^Troubleshoot/i) { &doTroubleShoot; }
else { &restoreView; }	# show nocol screen

&print_footer;
exit 0;

##################
##################
##################

#------------------------------------------------------------
## Look in authfile for username and determine userlevel. Format of 
# this file is:
#	user : password : userLevel
# If REMOTE_USER is set by the httpd daemon, then assume that the
# user was authenticated already by the .htaccess or equivalent.
# Else if cookie is set, check in 'cookiefile' and use that.
#
# If authfile does not exist it denies access. If not authenticated, it calls
# &print_auth_form(). If browser does not support cookies, then you *must*
# use the httpd daemons authentication method since there is no other choice.
# 
sub set_userlevel {

  # quick and fast,, however the HTTP_REFERER needs to be trimmed
  # before checking.
  if ($ENV{'HTTP_REFERER'} eq $processor  && 
      $FORM{'userlevel'} ne "" && $FORM{'user'} ne "")
  {
    $userlevel = int($FORM{'userlevel'});
    return;
  }

  if ($ENV{'REMOTE_USER'} ne "")	# user already authenticated by httpd
  {
    local ($user) = $ENV{'REMOTE_USER'};
    $FORM{'user'} = $ENV{'REMOTE_USER'};
    if (! open (AUTH, "< $authfile")) {
      &denyAccess("Cannot open $authfile $!, please contact site admin\n");
    }
    while (<AUTH>) {
      if (/^$user\:/)
      {
	local ($junk, $junk, $file_userlevel) = split /:/;
	$userlevel = int($file_userlevel);
	$FORM{'userlevel'} = $file_userlevel;
	close(AUTH);
	return;
      }
    }
    close(AUTH);
  }

  # See if our cookie is set, check and use that
  &print_auth_form if ($ENV{'HTTP_COOKIE'} eq "");	# if no cookie
  open(COOKIE, "< $cookiefile") || &print_auth_form;
  local ($cookie) = (split(';', $ENV{'HTTP_COOKIE'}))[0];	# remove trailing hostid
  while (<COOKIE>) {
    if (/^$cookie\:/)
    {
      local ($file_cookie, $file_user, $file_userlevel, $file_age) = split /:/;
      $FORM{'user'} = $file_user;
      $userlevel = int($file_userlevel);
      $FORM{'userlevel'} = $file_userlevel;
      close(COOKIE);
      return;
    }
  }
  close(COOKIE);

  # if we reach here, we need to get the user's information since he/she
  # was not authenticated by httpd or with a valid cookie
  &print_auth_form;
  return;
}	# authcheck()

#------------------------------------------------------------
## Check username and password and return a cookie if valid
#  Store the cookie in $cookiefile. Also expire old cookies while
#  we are working with the cookie file.
sub doAuthenticate {
  local ($user) = $FORM{'user'};
  local ($found) = 0;
  local ($localtime) = time;
  local ($max_cookie_age) = 86400;	# delete if one day old

  if ($FORM{'subcommand'} eq "Cancel") { &restoreView; exit 0; }

  &denyAccess('No username specified') if ($user eq "");
  open (AUTH, "< $authfile") || &denyAccess("Cannot open $authfile, contact site administrator $ENV{'SERVER_ADMIN'}");
  while (<AUTH>)
  {
    next if (! /^$user\:/);
    local ($file_user, $file_passwd, $file_userlevel) = split /:/;
    if ($file_passwd eq "")
    {
      $userlevel = int($file_userlevel);
      $found = 1;
    }
    else
    {
      local ($salt) = substr($file_passwd, 0, 2);
      local ($encrypt_pass) = crypt($FORM{'password'}, $salt);
      if ($encrypt_pass eq $file_passwd)
      {
	$userlevel = int($file_userlevel);
	$found = 1;
      }
      else { &denyAccess("Invalid password"); }
    }
    last;
  }	# while(AUTH)
  close(AUTH);

  $FORM{'userlevel'} = $userlevel;
  if (!$found) { &print_auth_form; }

  ## now generate a cookie and store in the cookie file
  local ($newcookie) = $localtime ^ $$ ^ $ENV{'REMOTE_PORT'};
  $newcookie .= "snips";
  # header string used by '&print_button_header'
  $cookiehdr = "Set-Cookie: $newcookie; path=$processor;";

  local (@cookies);
  if (open(COOKIE, "< $cookiefile")) {
    @cookies = <COOKIE>;	# slurp into memory
    close (COOKIE);
  }
  if (! open (COOKIE, ">> $cookiefile"))
  { 
    &denyAccess("Error- cannot open $cookiefile for writing, $!. Report this error to this web site administrator $ENV{'SERVER_ADMIN'}");
  }

  foreach (1..3) {	# try locking the file three times...
    if (flock(COOKIE, 2)) {
      seek(COOKIE, 0, 0);
      truncate(COOKIE, 0);
      last;
    }
    &denyAccess("Error- cannot open $cookiefile for flock access, report error to admin")     if ($_ == 3);
    sleep 1;
  }
  
  foreach (@cookies) {		# clean out old & expired cookies
    chop;
    # skip comments and blank lines
    if (/^\s*\#/ || /^\s*$/) { print COOKIE; next; }
    local ($file_cookie, $file_user, $file_userlevel, $file_age) = split /:/;
    next if ($file_age + $max_cookie_age < $localtime);
    print COOKIE "$file_cookie:$file_user:$file_userlevel:$file_age\n";
  }
  print COOKIE "$newcookie:$user:$userlevel:$localtime\n";
  close (COOKIE);

  ## here if user has been authenticated.
  &print_button_header;
  print "<b>User $user authenticated, user level $userlevel </b>\n";
  &print_footer;
  return;
}	# doAuthenticate()

#------------------------------------------------------------
## generate authentication form that the user has to fill out and EXIT.
sub print_auth_form {
  print "Content-type: text/html\n\n";
  print <<EoX;
  <html> <head> <title>Nocol User Authorization </title> </head>
    <body>
      <h4> NOCOL User Authorization required: </h4>
	<FORM action="$processor" method="post">
EoX
  &print_state_info;

  print <<EoXa;
  <b>Username:</b> &nbsp;
  <input type=text size=12 maxlength=12 name=user value=$FORM{'user'}> <br>
  <b>Password:</b> &nbsp;
  <input type=password size=12 maxlength=20 name=password value=""> <br>
  <input type=hidden name=command value="Authenticate">
  <input type=submit name=subcommand value="Submit">
  <input type=submit name=subcommand value="Cancel">
  </FORM>
EoXa

  &print_footer();
  exit 0;		# dont return, just exit
}

#------------------------------------------------------------
## print deny message and exit.
sub denyAccess {
  local ($denymesg) = @_;

  if ($denymesg =~ /^\s*$/) {
    $denymesg = "Command $command not permitted for $FORM{'user'} at user level $userlevel";
  }

  &print_button_header;
  print <<EoAUTH;
  <p> <center> <font color="red" size="+1">
    <b> $denymesg </b>
  </font></center> </p>
EoAUTH

  &print_footer;
  exit 0;
}

#------------------------------------------------------------
# print form buttons at the top of the page
#
sub print_button_header {
  print "Content-type: text/html\n";
  if ($cookiehdr ne "") {print "$cookiehdr\n"; }
  print "\n";

  print <<EoHeader;
  <html>
    <head><title>Nocol View $FORM{'sitename'} </title></head>
      <body bgcolor="#ffffff">
	<center>
	  <FORM action="$processor" method="post">

EoHeader
  &print_state_info;
  if ($sitename ne "" || $siteaddr ne "")	# only if some site
  {
    print <<EoHeadera;
	    <input type=submit name=command value="SiteHelp">
	    <input type=submit name=command value="Updates">
	    <input type=submit name=command value="Device Logs">
	    <input type=submit name=command value="Troubleshoot">
	    &nbsp;
EoHeadera
  }
  print <<EoHeaderb;
	    <input type=submit name=command value="NocolView">
	    &nbsp; &nbsp;
	    <input type=submit name=command value="About Nocol">
	  </FORM>
          <hr noshade width="100%">
       </center> <P>
       <P align="right">
       <font size=\"+2\" color=\"#003366\">$command</font><p>

EoHeaderb
}	# print_button_header()

#------------------------------------------------------------
#
sub print_footer {
  return if ($done_footer == 1);
  local ($foot) = "$helpdir" . "/stdfoot";
  print "\n<P> <!-- begin stdfoot -->\n";
  if (open(FILE, "< $foot")) { print while (<FILE>) ; }

  if ($ldebug > 2 && $userlevel < 3) {
    print "<!-- debug output -->\n";
    print "<p><b>Current userlevel = $userlevel</b> </p>";
    print "<p><h4>FORM Variables</h4>\n";
    for (keys %FORM) { print "<tt>$_ = $FORM{$_} </tt><br>" ;}
    print "<p><hr>\n <h4>ENV Variables</h4>\n";
    for (keys %ENV) { print "<tt>$_ = $ENV{$_} </tt><br>" ;}
  }

  print "\n </body>\n</html>\n";	# final closing
  $done_footer = 1;
}	# end sub(sitehelp)

#------------------------------------------------------------
# Print out the common FORM lines rquired to preserve the state  info
# across the various screens.
sub print_state_info {
  print <<EoState;
	 <input type=hidden name=sitename value="$FORM{'sitename'}">
	 <input type=hidden name=siteaddr value="$FORM{'siteaddr'}">
	 <input type=hidden name=variable value="$FORM{'variable'}">
	 <input type=hidden name=sender value="$FORM{'sender'}">
	 <input type=hidden name=user value="$FORM{'user'}">
	 <input type=hidden name=userlevel value="$FORM{'userlevel'}">
	 <input type=hidden name=return value="$FORM{'return'}">
	 <input type=hidden name=displaylevel value="$FORM{'displaylevel'}">
EoState
}

#------------------------------------------------------------
# The viewer has clicked on a device looking for help on that
# device. Files are named by the sitename : siteaddr
# If a file is not found for a device, we'll display the file "default:type"
# or just 'default'.
sub doSiteHelp {
  local ($helpfile);
  local ($sender)   = $FORM{'sender'};

  &denyAccess if ($userlevel > 4);
  &print_button_header();
  return if ($sitename eq "" || $siteaddr eq "");
  print "<center><H3>$sitename : $siteaddr : $variable</H3>\n";
  print "  <hr width=50\% align=center> <P>\n</center>\n";

  # find a help file. First a specific one, then try defaults
  foreach ("${sitename}:${siteaddr}", "${sitename}:default",
	   "default:$siteaddr", "default:$variable", "default:$sender",
	   "${sitename}", 'default:default', 'default')
  {
    if (-f "$helpdir/$_") { $helpfile = $_; last; }
  }

  if ($helpfile ne "" && open(HELP, "< $helpdir/$helpfile")) {
    print while (<HELP>) ;
  }
  else { print "<H3>Sorry, no device help is available!</H3>\n"; }

  &print_footer;

}  # doSiteHelp()

#------------------------------------------------------------
## Print out the log entries from the 'info' level file. Cannot
#  grep through all the log files since the syslog style logging
#  means that the same entry might exist in multiple log files.
#
sub doHistory {

  local ($cnt) = 1;
  local ($str);
  local (@rowcolor) = ("#CCCC99", "#D8D8D8");

  &denyAccess if ($userlevel > 3);
  &print_button_header();
  return if ($sitename eq "" || $siteaddr eq "");
  print "<center><H3>$sitename : $siteaddr : $variable</H3>\n";
  print "  <hr width=50\% align=center> <P>\n</center>\n";

  if ( (! -f "$logfile") || (! open(FILE, "< $logfile")) ) {
    print "<H3>Sorry, log file $logfile not available.<H3>\n";
    &print_footer;
    return;
  }
  print "<h4>Logfile: $logfile</h4>\n";
  print "<table cellpadding=3 cellspacing=1 border=0>\n<tr> ";
  foreach $str ('#', 'date', 'monitor', 'variable', 'value',
		'threshold', 'units', 'level')
  {
    print "<td><font face=\"arial,helvetica\" size=\"2\"><b>$str</b></font></td>\n";
  }
  print "</tr> <!-- $sitename $siteaddr -->\n";

  while (<FILE>)
  {
    if ( /^(.*)\s+\[(.*)\]:\s+SITE\s+$sitename.*\s+$siteaddr.*\s+VAR\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+LEVEL\s+(\S+)\s+/i )
    {
      print "<TR bgcolor=\"$rowcolor[$cnt % 2]\"> \n";
      foreach $str ($cnt, "$1", "$2", "$3", "$4", "$5", "$6", "$7") {
	print "\t <td><font face=\"arial,helvetica\" size=\"2\"> $str </font> </td> \n";
      }
      print "</TR>\n";
      ++$cnt;
    }
  }	# end while(<FILE>)

  print "</table>\n";

  close (FILE);
  &print_footer;

}  # doLogs()

#------------------------------------------------------------
# Edit the updatesFile (used to add a comment or hide a particular site
# from the display.
sub doUpdates {
  local ($explain) = '';
  local ($ishideChecked) = '';

  &denyAccess if ($userlevel > 1);

  &print_button_header();
  return if ($sitename eq "" || $siteaddr eq "");
  print "<center><H3>$sitename : $siteaddr : $variable</H3>\n";
  print "  <hr width=50\% align=center> <P>\n</center>\n";

  # update the status if we have a subcommand.
  if ($FORM{'subcommand'} ne "") { &updateStatus; }

  # now that we have updated the status file if required, get the
  # latest updated status from the updates file.
  if (open (INPUT, "< $updatesfile")) {
    while (<INPUT>) {
      chop;
      next if (/^\s*\#/);   # skip comments
      next if (/^\s*$/);   # skip blank lines
      ($junk, $explain) = split /\t/;
      last if ($junk =~ /^$sitename\:$siteaddr\:$variable/);
      $explain = "";	# reset/clear if not matched
    }
    close (INPUT);
  }

  if ($explain =~ /^\(H\)/) {
    $ishideChecked = 'CHECKED';
    $explain =~ s/^\(H\)\s+//o;	# strip out the (H) hidden part
  }
  $explain =~ s/^\s+// ; $explain =~ s/\s+$//;	# clean leading/trailing spaces
  $explain =~ s/\-\S*$//;	# remove -user from the end

  print <<UPDATEFORM;

	<H4>UPDATE STATUS:</H4>
	<P> Enter text that you would like to appear in the 'status' column
	of the nocol display. Select the 'Hide' checkbox if this device is
	going to be down for an extended period and you would like to remove
	it from the CRITICAL view. It will still appear in other views.
	<P>
	<FORM action="$processor" method="post">
	<P><input type=text size=30 maxlength=30 name=status value="$explain">
	<input type=checkbox name=Hide $ishideChecked >
	Hide this entry from Critical view
UPDATEFORM
   &print_state_info;
   print <<UPDATEFORMa;
	<input type=hidden name=command value="$command">
	<P><input type=submit name=subcommand value="change">
		Update this device to use this status
	<P><input type=submit name=subcommand value="clear">
		Remove this status message from this device 
	</FORM>
UPDATEFORMa

  &print_footer();

}	# doupdates()

#------------------------------------------------------------
# The 'action' called by the above form. This is the routine
# that actually updates the 'updates' file. Possibilities are:
#	- change entry (also can be marked as Hide)
#	- clear entry
sub updateStatus {

  return if ($sitename eq "" || $siteaddr eq "");

  if (! open(SFILE, "< $updatesfile")) {
      print "<b>Cannot open $updatesfile - $!<b><p>";
      return;
  }

  local ($newstatus) = $FORM{'status'};
  $newstatus =~ s/^\s+// ;
  if ($newstatus eq "" && ! $FORM{'Hide'}) { $FORM{'subcommand'} = "clear"; }

  local (@list) = <SFILE>;	# slurp into memory
  close (SFILE);
  if (! open (SFILE, ">> $updatesfile") ) {
    print "<h4> Sorry, error writing $updatesfile - $! </h4>\n";
    return ;
  }
  foreach (1..3) {	# try locking the file three times...
    if (flock(SFILE, 2)) {
      seek(SFILE, 0, 0);
      truncate(SFILE, 0);
      last;
    }
    if ($_ == 3) {
      print "<font color=red><h4> Sorry, error locking $updatesfile </h4>\n";
      return;
    }
    sleep 1;	# try locking after a second
  }

  foreach (@list) {
    next if (/^$sitename\:$siteaddr\:$variable/);	# delete/skip old entry
    print SFILE;
  }
  if ($FORM{'subcommand'} =~ /^clear/i) {
    print "<p><b> &nbsp; (entry cleared)</b> Will clear after a minute<p>\n";
  }
  else {
    print SFILE "$sitename\:$siteaddr\:$variable\t";
    if ($FORM{'Hide'}) { print SFILE "(H) "; }	# value of Hide checkbox
    if ($newstatus ne "") {print SFILE "$newstatus -$FORM{'user'}\n";}
    print "<p> <b> &nbsp; (entry updated)</b> Will display after a minute<p>\n";
  }

  close (SFILE);

}	# updateStatus()

#------------------------------------------------------------
## troubleshooting
# This runs external system commands, so please make sure that the
# external commands dont run forever. e.g ping has an option where it
# runs forever, so DONT use that option. Set the syntax, path.
# The keyword 'DEVICE' will be replaced by $siteaddr. You can disable any
# command or add new ones by changing $cmdlist.
#
# e.g. on Sun's use '/usr/sbin/ping -s DEVICE 500 3' if you are not using
# multiping
sub doTroubleShoot {
  local (@traceroutepaths) = ('/usr/sbin', '/usr/local/bin');
  local ($traceroute) = "traceroute -m 15 DEVICE";
  local ($ping) = "$nocolroot/bin/multiping -c 3 -i 2 DEVICE";	# check_this
  local ($nslookup) = "nslookup -query=any DEVICE";
  local ($nslookup) = ($siteaddr =~ /^[\d\.]+$/) ?
                         "nslookup DEVICE" : "nslookup -query=any DEVICE";

  foreach (@traceroutepaths)
  {
    if ( -x "$_/traceroute")
    {
      $traceroute = "$_/traceroute";
      last;
    }
  }

  local ($subcmd);
  local (%cmdlist) = ("ping", $ping, "traceroute", $traceroute,
		  "nslookup", $nslookup);

  &denyAccess if ($userlevel > 2);
  &print_button_header;
  return if ($sitename eq "" || $siteaddr eq "");
  print "<center><H3>$sitename : $siteaddr : $variable</H3>\n";
  print "  <hr width=50\% align=center>\n</center>\n";
  print "<p> <i>Remember that you have to wait (for about 10 secs) while the command executes</i></p>\n";

  select((select(STDOUT), $| = 1)[0]);

  ## if the user has selected a subcommand for us to execute
  ($subcmd = $FORM{'subcommand'}) =~ tr/A-Z/a-z/;	# lowercase
  if ($subcmd ne "")
  {
    local ($cmd) = $cmdlist{$subcmd};
    local ($linecnt) = 0;	# to prevent runaway commands

    print "debug ($subcmd) Trying $cmd $siteaddr<br>" if $ldebug;
    if ($cmd) {
      $siteaddr =~ tr/[a-zA-Z0-9_.\-]//cd;	# strip unwanted characters
      if (!$siteaddr || $siteaddr eq '-') {
        $cmd =~ s/DEVICE/$sitename/ ;	# replace keyword with name
      } else {
        $cmd =~ s/DEVICE/$siteaddr/ ;	# replace keyword with IP address
      }

      if (! open (CMD, "$cmd 2>&1 |") ) { 
	print "Command $cmd error  <p>\n";
	&print_footer();
	return;
      }
      # break after 10 lines are read to avoid runaway commands.
      print "<table cellpadding=5 cellspacing=0 border = 0> <tr>\n";
      print "<td bgcolor=\"#D8D8D8\">  <pre>";
      while (<CMD>) { print; ++$linecnt; last if ($linecnt > 10); }
      print "</pre> </td></tr></table>";
      close (CMD);
    }
    else {
      print "<h4><i> Dont know how to handle $subcmd </i></h4>\n";
    }
  }	# if (subcmd ne "")

  ##
  ## Now generate the form
  print "\t<FORM action=\"$processor\" method=\"post\">\n";
  &print_state_info;
  print <<TROUBLESHOOT;
	<input type=hidden name=command value="$command">
	<P><input type=submit name=subcommand value="traceroute"> <tt>$traceroute</tt>
	<P><input type=submit name=subcommand value="ping"> <tt>$ping</tt>
	<P><input type=submit name=subcommand value="nslookup"> <tt>$nslookup</tt>
	</FORM>
TROUBLESHOOT

  &print_footer();

}	# doTroubleShoot()

## return the user to the 'return' page and EXIT
sub restoreView {
  print "Content-type: text/html\n\n";
  print <<ReSTORE;
  <html> <head>
    <meta http-equiv="refresh" content="0;URL=$FORM{'return'}">
    <title>NOCOL</title>
   <head>
   <body> <h2>Please wait as screen reloads...</h2> </body>
   </html>
ReSTORE

  exit 0;
}
  
#------------------------------------------------------------
# Info blurb about nocol.
sub aboutNocol {

  # &denyAccess if ($userlevel > 9);
  if ($command =~ /^UserHelp/i) {	# no special buttons for userlevel
    print "Content-type: text/html\n\n <html> <head>\n";
    print "<title>Nocol Help</title> </head> <body bgcolor=\"#ffffff\">\n";
  }
  else { &print_button_header(); }

  print <<EOHELP;

   <center><H2>Network Operations Center On-Line</H2>
     <HR width=60% align=center>
   </center>

<A href="http://www.netplex-tech.com/software/nocol/">NOCOL/NetConsole
(Network Operation Center On-Line) </A> is a network monitoring 
package that runs on Unix platforms and capable of monitoring network and
system variables such as ICMP or RPC reachability, RMON variables,
nameservers, ethernet load, port reachability, host performance, SNMP traps,
radius, NTP, modem line usage, appletalk & novell routes/services, BGP peers,
etc. 
There is just one set of monitoring agents and <em>any</em> number of 
display agents, and all
of the displays see the same consistent set of data.
Additionally, each event
is assigned a severity (determined by comparing against user defined threshold
values) which is gradually escalated, thus preventing false alarms and a
customized priority notification based on the severity. There are four
severity levels ranging from Critical thru Info, and each event typically
steps through each one of these severities until it reaches its maximum
allowed level.
<P>
This display uses a dynamically generated web page and so can display on a 
variety of terminals. The user running the display can select the minimum
display severity--only events above this minimum severity level are displayed.
If you\'re running a Netscape-compatible web browser, the display will 
automatically update itself every minute.
<P>
More information about nocol is available
<a href="http://www.netplex-tech.com/software/nocol/"> here. </a>
<P>
NOCOL was written by:
<BLOCKQUOTE>Vikas Aggarwal
<BR><a href="mailto:vikas\@navya.com">vikas\@navya.com</a>
<BR>December 1994
</BLOCKQUOTE>
This web interface was written by:
<BLOCKQUOTE>Rick Beebe
<BR><a href="mailto:richard.beebe\@yale.edu">richard.beebe\@yale.edu</a>
<BR>March, 1998
</BLOCKQUOTE>
EOHELP

&print_footer();

}

