#!/usr/bin/perl
#
# This program is used to display information collected by the spong server to
# people using web based clients.  This provides the same type of interface to
# the data as the text based client does.  You can run this program as a CGI
# program to provide an interactive experience, or you can use it from a cron
# job, or some other automated process to just generate static web pages.
#
# History:
# (1) Ported mkbb.* scripts to perl. (Ed Hill, Mar 1, 1997)
# (2) Re-did in a more generic and OO way (Ed Hill, May 9, 1997)
# (3) Re-did as a client which gets info from the spong-server (07/24/1997)
# (4) Did a whole bunch of stuff (Ed Hill, 06/18/1998)
#
# $Id: www-spong.pl,v 1.20 2001/04/17 02:58:12 sljohnson Exp $

use Sys::Hostname;
use Getopt::Long;
use Socket;
use POSIX;

# Load our configuration variables, including the user specified configuration
# information (spong.conf, spong.hosts, and spong.groups files).

$|++;
$conf_file   = "/etc/spong/spong.conf"; 
$hosts_file  = "/etc/spong/spong.hosts";
$groups_file = "/etc/spong/spong.groups";
$SPONGVER    = "2.7";
($HOST)      = gethostbyname(&Sys::Hostname::hostname());
$HOST        =~ tr/A-Z/a-z/;
$view        = "";
$actionbar   = 1;
$interactive = 0;

%HUMANS      = ();
%HOSTS       = ();
%GROUPS      = ();
 
&load_config_files();  # Loads the user specified configuration information

$main::WWW_DEFAULT_VIEW = "HOSTS" unless $main::WWW_DEFAULT_VIEW;

# Find our SPONGSLEEP value
$SPONGSLEEP = $SPONGSLEEP{'www-spong'} || $SPONGSLEEP{'DEFAULT'} ||
              $SPONGSLEEP || 300;

# Check to see if I am being run as a command line program (in which case I
# just generate static HTML documents), or a CGI program (in which case I 
# present back an interactive web interface).  The command line arguments are
# the same as the text based spong program, and produce similar results

if( $ENV{'SCRIPT_NAME'} eq "" ) {
   # Get the user options and spit back a message if they do something silly

   my %opt;
   my @options = ( "help", "summary:s", "problems:s", "history:s", "host=s", 
		   "services=s", "stats=s", "config=s", "info=s", "service=s",
                   "histservice=s", "grp-summary:s", "grp-problems",
		   "brief", "standard", "full" );

   $actionbar = 0;

   if( ! GetOptions( \%opt, @options )) {warn "Incorrect usage:\n\n"; &help();}

   &help if defined $opt{'help'};

   if( defined $opt{'brief'} )    { $view = "brief"; }
   if( defined $opt{'standard'} ) { $view = "standard"; }
   if( defined $opt{'full'} )     { $view = "full"; }

   if( defined $opt{'problems'} ) { &problems( $opt{'problems'} ); $opt = 1; }
   if( defined $opt{'summary'} )  { &summary( $opt{'summary'} );   $opt = 1; }
   if( defined $opt{'history'} )  { &history( $opt{'history'} );   $opt = 1; }

   if( defined $opt{'host'} )     { &host( $opt{'host'} );         $opt = 1; }
   if( defined $opt{'services'} ) { &services( $opt{'services'} ); $opt = 1; }
   if( defined $opt{'stats'} )    { &stats( $opt{'stats'} );       $opt = 1; }
   if( defined $opt{'config'} )   { &config( $opt{'config'} );     $opt = 1; }
   if( defined $opt{'info'} )     { &info( $opt{'info'} );         $opt = 1; }

   if( defined $opt{'service'} )  {
      my( $host, $service ) = split( ':', $opt{'service'} );
      &service( $host, $service );
      $opt = 1;
   }

   if ( defined $opt{'histservice'} ) {
      my( $host, $service, $time ) = split ( ':',  $opt{'histservice'} );
      &histservice( $host, "$service $time" );
      $opt=1;
   }

   if ( defined $opt{'grp-summary'} ) { &grp_summary( $opt{'grp-summary'} ); $opt= 1; } 
   if ( defined $opt{'grp-problems'} ) { &grp_problems( $opt{'grp-problems'} ); $opt= 1; } 

   if( ! $opt ) { &summary( "all" ); }

   exit(0);
}


# If we make it to this point, then we are a CGI program so pull apart the
# commands given to us via the URL path, and treat them as if they are command
# line options

$interactive = 1;

$cmd = $ENV{'PATH_INFO'};

# Commands that are more applicable to spong running in interactive mode.  
# These commands control the frame interface, etc...

if( $cmd eq "" || $cmd eq "/" )   {
   if ($WWWFRAMES == 3) { &toplevel(); }
   else {
      &interactive( "all" ) if $main::WWW_DEFAULT_VIEW eq "HOSTS";
      &ovinteractive( "all" ) if $main::WWW_DEFAULT_VIEW eq "GROUPS";
   }
   exit;
}
if ($cmd =~ m!^/title$! )          { &title(); exit; } 

if( $cmd =~ m!^/group/(.*)$! )    { &interactive( $1 );    exit; }
if( $cmd =~ m!^/bygroup/(.*)$! )    { &ovinteractive( $1 );    exit; }
if( $cmd =~ m!^/commands/(.*)$! ) { &commands( $1 );       exit; }

if( $cmd =~ m!^/ovcommands/(.*)$! ) { &ovcommands( $1 );   exit; }
if( $cmd =~ m!^/igrp-summary/(.*)$! ) { &igrp_summary( $1 );   exit; }
if( $cmd =~ m!^/igrp-overview/(.*)$! ) { &igrp_overview( $1 );   exit; }

if( $cmd =~ m!^/isummary/(.*)$! ) { &isummary( $1 );       exit; }
if( $cmd =~ m!^/ihistory/(.*)$! ) { &ihistory( $1 );       exit; }

if( $cmd =~ m!^/groups$! )        { &groups();             exit; }
if( $cmd =~ m!^/groups-doit$! )   { &groups_doit();        exit; }

if( $cmd =~ m!^/help$! )          { &show( "help" );       exit; }
if( $cmd =~ m!^/help/(.*)$! )     { &show( "$1" );         exit; }

# Simple commands to just display a specific host attribute, or other specific
# information.  These commands can easily be called by pages outside of spong

if( $cmd =~ m!^/brief/(.*)$! )    { $view = "brief";    $cmd = "/$1"; }
if( $cmd =~ m!^/standard/(.*)$! ) { $view = "standard"; $cmd = "/$1"; }
if( $cmd =~ m!^/full/(.*)$! )     { $view = "full";     $cmd = "/$1"; }

if( $cmd =~ m!^/problems/(.*)$! ) { &problems($1); exit;}
if( $cmd =~ m!^/summary/(.*)$! )  { &summary($1);  exit;}
if( $cmd =~ m!^/history/(.*)$! )  { &history($1);  exit;}

if( $cmd =~ m!^/host/(.*)$! )     { &host($1);     exit;}
if( $cmd =~ m!^/services/(.*)$! ) { &services($1); exit;}
if( $cmd =~ m!^/stats/(.*)$! )    { &stats($1);    exit;}
if( $cmd =~ m!^/config/(.*)$! )   { &config($1);   exit;}
if( $cmd =~ m!^/info/(.*)$! )     { &info($1);     exit;}

if( $cmd =~ m!^/service/(.*)/(.*)$! ) { &service( $1, $2 ); exit; }

if( $cmd =~ m!^/histservice/(.*)/(.*)/(.*)$! ) { &histservice( $1, "$2 $3" ); exit; }

if( $cmd =~ m!^/grp-summary/(.*)$! ) { &grp_summary( $1 ); exit;}
if( $cmd =~ m!^/grp-problems/(.*)$! ) { &grp_problems( $1 ); exit;}

# Need to do something when an invalid request comes through...
exit(0);



# ---------------------------------------------------------------------------
# Functions that support the interactive WWWSPONG client
# ---------------------------------------------------------------------------

# This sets up the wwwspong interface, it defines the frame that both the 
# commands & error summary information is shown, and the frame for more 
# detailed host information.


sub toplevel {

   # Default to a reasonable value if not defined.
   $main::WWW_TITLE_SIZE = 40 if( $main::WWW_TITLE_SIZE <= 0 ); 

   print "Content-type: text/html\n\n";
   print "<html><head>\n";
   print "<title>Spong v$SPONGVER - System Status Monitor</title></head>\n";
   print "<frameset rows=\"$main::WWW_TITLE_SIZE,*\" border=5 frameboard=no>";
   print "<frame src=\"$main::WWWSPONG/title\" marginwidth=5 ";
   print "marginheight=5 noshade name=\"title\">\n";
   print "<frame src=\"";

   print "$main::WWWSPONG/bygroup/" if $main::WWW_DEFAULT_VIEW eq "GROUPS";
   print "$main::WWWSPONG/group/all" if $main::WWW_DEFAULT_VIEW eq "HOSTS";

   print "\" marginwidth=10 ";
   print "marginheight=5 noshade name=\"view\" border=1>\n";
   print "</frameset>\n";
   print "<noframe>\n";
   print "Frameless version not currently available.\n";
   print "</noframes>\n</html>\n";
}

sub interactive { 
   my $group = shift;

   print "Content-type: text/html\n\n";
   print "<html><head>\n";
   print "<title>Spong v$SPONGVER - System Status Monitor</title></head>\n";
   print "<frameset cols=\"240,*\" border=5 frameboard=no>";
   print "<frame src=\"$main::WWWSPONG/commands/$group\" marginwidth=5 ";
   print "marginheight=5 noshade>\n";
   print "<frame src=\"$main::WWWSPONG/isummary/$group\" marginwidth=10 ";
   print "marginheight=5 noshade name=\"right\" border=1>\n";
   print "</frameset>\n";
   print "<noframe>\n";
   print "Frameless version not currently available.\n";
   print "</noframes>\n</html>\n";
}

sub ovinteractive { 
   my $group = shift;

   print "Content-type: text/html\n\n";
   print "<html><head>\n";
   print "<title>Spong v$SPONGVER - System Status Monitor</title></head>\n";
   print "<frameset cols=\"240,*\" border=5 frameboard=no>";
   print "<frame src=\"$main::WWWSPONG/ovcommands/\" marginwidth=5 ";
   print "marginheight=5 noshade>\n";
   print "<frame src=\"$main::WWWSPONG/igrp-summary/\" marginwidth=10 ";
   print "marginheight=5 noshade name=\"right\" border=1>\n";
   print "</frameset>\n";
   print "<noframe>\n";
   print "Frameless version not currently available.\n";
   print "</noframes>\n</html>\n";
}

# This function fills out the Title header. It had a command bar that changes
# the the current View type in the 'view' frame

sub title {
   &header(0);

   my $me = $main::WWWSPONG;

   print "<base target=view>\n";
#   print "<center>";
   print "<font size=+2><b>Spong v$SPONGVER</b></font>\n";
#   print "</center>\n";
#   print "<br>";
   print "<a href=\"$me/bygroup/\">Groups</a> || \n"; 
   print "<a href=\"$me/group/all\">Hosts</a> \n"; 

#   print "<b>Extra Tool Bar Commands:</b> ";
#   print " <a href=\"/spong-rrd/index.html\">Spong RRD Charts</a>\n";
#   print " || <a href=\"/cgi-bin/sysquery.pl?target=showall\">System Summaries</a>\n";

   if ( $main::WWW_TITLE_ACTIONBAR ) { print $main::WWW_TITLE_ACTIONBAR,"\n"; }
}


# This function fills out the action bar of the interactive spong display.
# This lists the functions that you can perform via the web interface.

sub commands {
   my $group = shift;
   my $me = $main::WWWSPONG;

   $group = "all" unless $group;
   my $gname = $main::GROUPS{$group}->{'name'} if $main::GROUPS{$group};
   $gname = "Selected Hosts" unless $gname;
    
   &header( 1 );

   print "<base target=right>\n";
#   print "<font size=+2><b>Spong v$SPONGVER</b></font>\n";
   print "<font size=+2><b>Hosts View</b></font>\n";
   unless ($WWWFRAMES == 3) {
      print "<hr>\n";
      print "<b>Views:</b> <a href=\"$me/bygroup/\" target=_top>Groups</a>";
      print " || \n<a href=\"$me/group/all\" target=_top>Hosts</a> \n"; 
   }
   print "<hr>\n";

   print "<a href=\"$main::WWWACK\">Ack</a> || \n"; 
   print "<a href=\"$me/isummary/$group\">Summary</a> || \n"; 
   print "<a href=\"$me/ihistory/$group\">History</a> || \n"; 
   print "<a href=\"$me/help\">Help</a>\n";
   print "<hr>\n<p>\n";

   &problems( $group );

   print "<p><hr><a href=\"$me/groups\">Group</a>: <b>$gname</b>\n<hr>\n";
   print "Updated at ", POSIX::strftime( "$TIMEFMTNOSEC, on $DATEFMT", localtime() ), "\n";
   &footer();
}


sub ovcommands {
   my $group = shift;
   my $me = $main::WWWSPONG;

   $group = "all" unless $group;
#   my $gname = $main::GROUPS{$group}->{'name'} if $main::GROUPS{$group};
#   $gname = "Selected Hosts" unless $gname;
    
   &header( 1 );

   print "<base target=right>\n";
#   print "<font size=+2><b>Spong v$SPONGVER</b></font>\n";
   print "<font size=+2><b>Groups View</b></font>\n";
   unless ($WWWFRAMES == 3) {
      print "<hr>\n";
      print "<b>Views: </b><a href=\"$me/bygroup/\" target=_top>Groups</a> || \n"; 
      print "<a href=\"$me/group/all\" target=_top>Hosts</a> \n"; 
   }
   print "<hr>\n";

   print "<a href=\"$me/igrp-summary/\">Groups</a> || \n"; 
   print "<a href=\"$me/igrp-overview/\">Group Summary</a>\n"; 
   print "<hr>\n";

   print "<a href=\"$main::WWWACK\">Ack</a> || \n"; 
#   print "<a href=\"$me/isummary/$group\">Summary</a> || \n"; 
   print "<a href=\"$me/ihistory/all\">History</a> || \n"; 
   print "<a href=\"$me/help\">Help</a>\n";
   print "<hr>\n<p>\n";

   &grp_problems( $group );

   print "<p><hr>";
#   print "<a href=\"$me/groups\">Group</a>: <b>$gname</b>\n<hr>\n";
   print "Updated at ", POSIX::strftime( "$TIMEFMTNOSEC, on $DATEFMT", localtime() ), "\n";
   &footer();
}


# A couple of slightly different functions to display summary and history
# information for people using the wwwspong program interactivly.  This just
# puts a little header above each output, so that you know what group it
# corresponds to.

sub isummary {
   my $group = shift;
   my $gname = $main::GROUPS{$group}->{'name'} if $main::GROUPS{$group};
   $gname = "Selected Hosts" unless $gname;

   &header( 1 );
   print "<font size=+2><b>$gname</b></font>\n<hr>\n";
   &summary( $group );
   &footer();
}

sub ihistory {
   my $group = shift;
   my $gname = $main::GROUPS{$group}->{'name'} if $main::GROUPS{$group};
   $gname = "Selected Hosts" unless $gname;

   &header( 1 );
   print "<font size=+2><b>$gname</b></font>\n<hr>\n";
   &history( $group );
   &footer();
}

sub igrp_summary {
   my $group = shift;
#   my $gname = $main::GROUPS{$group}->{'name'} if $main::GROUPS{$group};
#   $gname = "Selected Hosts" unless $gname;

   &header( 1 );
   print "<font size=+2><b>Host Groups</b></font>\n<hr>\n";
   $main::view = "full";
   &grp_summary($group);
   &footer();
}

sub igrp_overview {
   my $group = shift;
#   my $gname = $main::GROUPS{$group}->{'name'} if $main::GROUPS{$group};
#   $gname = "Selected Hosts" unless $gname;

   &header( 1 );
   print "<font size=+2><b>Host Groups Summary</b></font>\n<hr>\n";
   $view = "standard";
   &grp_summary($group);
   &footer();
}



# This provides a page that lists the groups that are defined in spong, and
# you can select a group to monitor (summary information will then only be
# shown about that group).

sub groups {
   my $group;

   &header( $group, "Groups", '', 0 );
   print "<base target=view>\n";
   print "<font size=+2><b>Spong Groups</b></font>\n<hr>\n";

   print "You can select a specific group to show only information about ";
   print "those hosts.  The groups below have been defined by the spong ";
   print "administrator.<p>\n";

   print "<ul>\n";
   foreach $group ( @main::GROUPS_LIST ) {
      my $name    = $main::GROUPS{$group}->{'name'};
      my $summary = $main::GROUPS{$group}->{'summary'};
      
      print "<li><a href=\"$main::WWWSPONG/group/$group\">$name</a> ";
      print "($group)<br>\n$summary<p>\n";
   }
   print "</ul><p>\n";
   print "<hr noshade>\n";

   print "You can also build a custom group to monitor by selecting one or ";
   print "hosts from the list below.<p>\n";
   print "<form action=\"$WWWSPONG/groups-doit\" method=get>\n";
   print "<center><table width=80% border=0>\n";
   print "<tr><td width=50% valign=top align=center>\n";
   print "<select name=\"hosts\" size=10 multiple>\n";

   foreach $host ( @main::HOSTS_LIST ) { print "<option>$host\n"; }

   print "</select>\n";
   print "</td><td width=50% valign=center align=center>\n";
   print "<input type=submit name=\" Show Hosts \" value=\" Show Hosts \">\n";
   print "</td></tr></table>\n";
   print "</form>\n";
   print "<hr>\n";
   
   &footer();
}

# The action part of the above form.  The hosts are pulled out of the query
# string and passed as a group name to the &interactive() functions.

sub groups_doit {
   my $group = "";
   while( $ENV{'QUERY_STRING'} =~ /hosts=([^\&]+)/isg ) { $group .= "$1,"; }
   chop $group;
   &interactive( $group );
}   


# ---------------------------------------------------------------------------
# Functions that correspond to command line/URL line arguments
# ---------------------------------------------------------------------------

sub problems {
   my $group = shift;
   my $view = $main::view || "full";

   $group = "all" unless $group;

   &header( 1 );
   print &query( $SPONGSERVER, "problems", $group, "html", $view );
   &footer();
}

sub summary {
   my $group = shift;
   my $view = $main::view || "standard";

   $group = "all" unless $group;

   &header( 1 );
   print &query( $SPONGSERVER, "summary", $group, "html", $view ); 
   &footer();
}

sub history {
   my $group = shift;
   my $view = $main::view || "standard";

   $group = "all" unless $group;

   &header( 1 );
   print &query( $SPONGSERVER, "history", $group, "html", $view ); 
   &footer();
}


sub host {
   my $host = shift;
   my $view   = $main::view || "standard";

   &header( 0 );
   print &query( $SPONGSERVER, "host", $host, "html", $view );
   &footer();
}

sub services {
   my $host = shift;
   my $view = $main::view || "standard";

   &header( 0 );
   print &query( $SPONGSERVER, "services", $host, "html", $view ); 
   &footer();
}

sub stats {
   my $host = shift;
   my $view = $main::view || "standard";

   &header( 0 );
   print &query( $SPONGSERVER, "stats", $host, "html", $view );    
   &footer();
}

sub config {
   my $host = shift;
   my $view = $main::view || "standard";

   &header( 0 );
   print &query( $SPONGSERVER, "config", $host, "html", $view ); 
   &footer();
}

sub info {
   my $host = shift;
   my $view = $main::view || "standard";

   &header( 0 );
   print &query( $SPONGSERVER, "info", $host, "html", $view ); 
   &footer();
}


sub service {
   my( $host, $service ) = @_;
   my $view = $main::view || "full";

   &header( 0 );
   print &query( $SPONGSERVER, "service", $host, "html", $view, $service );
   &footer();
}

sub histservice {
   my ($host, $service, $time ) = @_;
   my $view = $main::view || "full";

   &header( 0 );
   print &query( $SPONGSERVER, "histservice", $host, "html", $view, $service,
                 $time );
   &footer();
}

sub grp_summary {
   my( $other ) = @_;
   my $view = $main::view || 'full';

   &header(0);
   print &query( $SPONGSERVER, "grpsummary", '', "html", $view, $other);
   &footer();

}

sub grp_problems {
   my( $other ) = @_;
   my $view = $main::view || 'full';

   &header(0);
   print &query( $SPONGSERVER, "grpproblems", $other, "html", $view );
   &footer();
}

# Just print a little message to stdout showing what valid options are to 
# the command line interface to spong, and then exit the program.

sub help {
   print <<'_EOF_';
Usage: wwwspong [options]

Where "options" are one of the arguments listed below.  If no arguments are
supplied, then a table showing the status of all hosts is shown.

   --summary  [hostlist]    Summarizes the status of services on the host(s)
   --problems [hostlist]    Shows a summary of problems on the host(s)
   --history  [hostlist]    Show history information for the host(s)

   --host     host          Shows all information available for the given host
   --services host          Shows detailed service info for the given host
   --stats    host          Shows statistical information for the given host
   --config   host          Shows configuration information for the given host
   --info     host          Shows admin supplied text for the given host

   --service  host:service  Shows detailed info for the given service/host

   --brief                  Display output in a brief format
   --standard               Display output in standard format (the default)
   --full                   Display more information then you probably want

All host names used as options must be fully qualified domain names. For the
options above that take an optional hostlist, the hosts listed should be
either a group name, or a list of individual hosts seperated by commas.  If
the host list is omitted, then information about all hosts monitored by spong
is returned.

_EOF_
   exit(0);
}

# ---------------------------------------------------------------------------
# Private/Internal functions
# ---------------------------------------------------------------------------

# This function just loads in all the configuration information from the 
# spong.conf, spong.hosts, and spong.groups files.

sub load_config_files {
   my( $evalme, $inhosts, $ingroups );

   require $conf_file || die "Can't load $conf_file: $!";
   if( -f "$conf_file.$HOST" ) {
      require "$conf_file.$HOST" || die "Can't load $conf_file.$HOST: $!";
   } else {
      my $tmp = (split( /\./, $HOST ))[0];
      if( -f "$conf_file.$tmp" ) { # for lazy typist
	 require "$conf_file.$tmp" || die "Can't load $conf_file.$tmp: $!";
      }
   }

   # Read in the spong.hosts file.  We are a little nasty here in that we do
   # some junk to scan through the file so that we can maintain the order of
   # the hosts as they appear in the file.

   open( HOSTS, $hosts_file ) || die "Can't load $hosts_file: $!";
   while( <HOSTS> ) {
      $evalme .= $_;
      if( /^\s*%HOSTS\s*=\s*\(/ ) { $inhosts = 1; }
      if( $inhosts && /^\s*[\'\"]?([^\s\'\"]+)[\'\"]?\s*\=\>\s*\{/ ) {
	 push( @HOSTS_LIST, $1 ); }
   }
   close( HOSTS );
   eval $evalme || die "Invalid spong.hosts file: $@";

   # Fallback, if we didn't read things correctly...
   
   if( sort ( @HOSTS_LIST ) != sort ( keys %HOSTS ) ) { 
      @HOSTS_LIST = sort keys %HOSTS; }

   # Do the same thing for the groups file.

   $evalme = "";
   open( GROUPS, $groups_file ) || die "Can't load $groups_file: $!";
   while( <GROUPS> ) {
      $evalme .= $_;
      if( /^\s*%GROUPS\s*=\s*\(/ ) { $ingroups = 1; }
      if( $ingroups && /^\s*[\'\"]?([^\s\'\"]+)[\'\"]?\s*\=\>\s*\{/ ) {
	 push( @GROUPS_LIST, $1 ); }
   }
   close( GROUPS );
   eval $evalme || die "Invalid spong.groups file: $@";

   if( sort ( @GROUPS_LIST ) != sort ( keys %GROUPS ) ) { 
      @GROUPS_LIST = sort keys %GROUPS; }
}


# ----------------------------------------------------------------------------
# Display helper functions
# ----------------------------------------------------------------------------

# These allow users to easily customize some aspects of spong, by providing 
# their own header and footer information for each page.

sub header { 
   my( $reload ) = shift;

   if( $main::header_printed == 1 ) { return; }
   $main::header_printed = 1;

   print "Content-type: text/html\n\n"; 
   if( $reload == 1 && &can_reload() ) {
      print "<meta http-equiv=\"REFRESH\" content=\"$main::SPONGSLEEP\">\n"; }

   &show( "header", 1 ) if -f "$main::WWWHTML/header.html"; 
}

sub footer { 
   if ($main::footer_printer == 1 ) { return; }
   $main::footer_printer = 1;
   &show( "footer", 1 ) if -f "$main::WWWHTML/footer.html"; }


# This just takes a HTML template with a given name, and sends it to STDOUT.
# This is used primarily for the help documentation.

sub show {
   my ($file, $hf) = @_;
   my $show = $main::WWWSPONG . "/help";

   if( -f "$main::WWWHTML/$file.html" ) {
      &header( '', "Help", '', 0 ) unless $hf; 
      open( FILE, "$main::WWWHTML/$file.html" );
      while( <FILE> ) {
         s/!!WWWSHOW!!/$show/g;
         s/!!WWWDOCS!!/$WWWDOCS/g;
         print $_;
      }
      close( FILE );
      &footer() unless $hf;
   } else {
      &header( '', "Help", '', 0 ) unless $hf; 
      print "<h1>Help Not Available</h1>\n";
      print "Sorry, but no help has been provided for that topic.\n";
      &footer() unless $hf;
   }
}

# This checks to see if the person connecting should be given back pages that
# auto-matically reload (we don't want everyone to be banging against the
# server).

sub can_reload {
   my $ok = 0;
   my $regex;

   foreach $regex ( @main::WWW_REFRESH_ALLOW ) {
      if( $ENV{'REMOTE_ADDR'} =~ m/$regex/i ) { $ok = 1; }
      if( $ENV{'REMOTE_HOST'} =~ m/$regex/i ) { $ok = 1; }
      if( $ENV{'REMOTE_USER'} =~ m/$regex/i ) { $ok = 1; }
   }

   foreach $regex ( @main::WWW_REFRESH_DENY ) {
      if( $ENV{'REMOTE_ADDR'} =~ m/$regex/i ) { $ok = 0; last; }
      if( $ENV{'REMOTE_HOST'} =~ m/$regex/i ) { $ok = 0; last; }
      if( $ENV{'REMOTE_USER'} =~ m/$regex/i ) { $ok = 0; last; }
   }

   return $ok;
}

# ----------------------------------------------------------------------------
# Networking functions...
# ----------------------------------------------------------------------------

# This function sends a query to the spong server.  It takes the results it
# gets back based on the user's query and returns the string back to the 
# code that called this function.  
#
# This query is a slightly different then the text client query function in
# that it translates some template tags into directories on the www server, so
# that links and gifs appear in the correct place.

sub query {
   my( $addr, $query, $hosts, $display, $view, $other ) = @_;
   my( $iaddr, $paddr, $proto, $line, $ip, $ok, $msg );

   if( $addr =~ /^\s*((\d+\.){3}\d+)\s*$/ ) {
      $ip = $addr;
   } else {
      my( @addrs ) = (gethostbyname($addr))[4];
      my( $a, $b, $c, $d ) = unpack( 'C4', $addrs[0] );
      $ip = "$a.$b.$c.$d";
   }

   $iaddr = inet_aton( $ip ) || die "no host: $host\n";
   $paddr = sockaddr_in( $SPONG_QUERY_PORT, $iaddr );
   $proto = getprotobyname( 'tcp' );
   
   # Set an alarm so that if we can't connect "immediately" it times out.

   $SIG{'ALRM'} = sub { die };
   alarm(30);

   eval <<'_EOM_';
   socket( SOCK, PF_INET, SOCK_STREAM, $proto ) || die "socket: $!";
   connect( SOCK, $paddr )                      || die "connect: $!";
   select((select(SOCK), $| = 1)[0]);
   print SOCK "$query [$hosts] $display $view $other\n";
   while( <SOCK> ) { 
      s/!!WWWGIFS!!/$main::WWWGIFS/g;           # Gif directory
      s/!!WWWSPONG!!/$main::WWWSPONG/g;         # Spong program
      s/!!WWWHTML!!/$main::WWWHTML/g;           # Html help files
      $msg .= $_; 
   }
   close( SOCK )                                || die "close: $!";
   $ok = 1;
_EOM_

   alarm(0);
   
   return $msg if $ok;
   return "<font color=red><b>Can't connect to spong server!</b></font>";
}

