#!/usr/bin/perl
####################################################
# npulse.cgi
# Network Monitoring Program
#
# Author: Steve Horsburgh  (npulse@horsburgh.com)
# Version: 0.31 beta
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
# 
####################################################
$|=1;
require 5.004;
require "./preferences.pl";
require "$Libdir/util.pl";
#------------------------------------------------------------------#
#-- Main Program --------------------------------------------------#
#------------------------------------------------------------------#

$DoneHeader = 0;
#$cmdline = $ENV{'HTTP_HOST'}?0:1;
$cmdline = ($#ARGV >= 0)?1:0;

&DEBUG("Starting npulse.cgi");

if ($cmdline) {
  &DEBUG("Command line mode");
  foreach (@ARGV) {
    $in{$1} = $2 if m/\s*([^=\s]+)\s*=\s*(.*)$/;
  }
} else {
  &DEBUG("CGI mode");
  require $Cgilib;
  print &PrintHeader();
  &ReadParse();
}

use locale;
foreach ('site','a','s') {
  $in{$_} =~ s/[^\w\.\d-]//g if defined $in{$_};   # Trap bad/imposter characters
}

&DEBUG("in{a} = ",$in{'a'});
$in{'a'} = "display" unless $in{'a'};

%allowedactions = (
                    display => "$Libdir/gendisp.pl",
                    pref    => "$Libdir/prefs.pl",
                    setup   => "$Libdir/editor.pl",
                    recheck => "$Libdir/gendisp.pl",
                    detail  => "$Libdir/detail.pl",
                    create  => "$Libdir/gendisp.pl",
                    auto    => "$Libdir/prefs.pl",
                    status  => ""
                  );

$allowedsubactions = 'single traceroute quickport fullport history lookup discover save updateinfo compact basic help copyright on off ';

delete $in{'s'} unless ($in{'s'} && (index(lc($allowedsubactions),lc($in{'s'})) >= 0));

if (exists $allowedactions{lc($in{'a'})}) {
  require $allowedactions{lc($in{'a'})} if $allowedactions{lc($in{'a'})};
  &{uc($in{'a'})};
}

&DEBUG("Exiting");
exit;

#------------------------------------------------------------------#
#-- Main Routines -------------------------------------------------#
#------------------------------------------------------------------#

sub PREF {
    my($type) = $in{'s'} || "edit";
    &DEBUG("type = $type");
    print &PREFS(lc($type));
    &DEBUG("Exiting");
    exit;
}

sub SETUP {
    &DEBUG("in{s} = ",$in{'s'});
    &{uc($in{'s'})} if $in{'s'} =~ /discover|lookup/i;
    &EDITOR();  #Does not return
}

sub RECHECK {
    my($ip,$portmask) = @_;
    &DEBUG("DEMO = $DEMO");
    exit if $DEMO;

    if ($ip or $portmask) {
      my $cmd = $Autorecheck." auto".$$.".pid -1 '".$ip."' ".$portmask;
      &DEBUG("Issuing cmd: $cmd");
      system($cmd);

    } else {
      &StartAutoCheck($Autorecheck." auto".$$.".pid -1");

    }
    &CREATE();
    &DEBUG("Exiting");
    exit;
}

sub DETAIL {
    &DEBUG("in{s} = ",$in{'s'});
    &ReadServices();
    my($result) = &{uc($in{'s'})} if $in{'s'} =~ /single|traceroute|quickport|fullport|history|updateinfo/i;
    &DisplayDETAIL($in{'site'}, $result) unless $in{'s'} =~ /single/i;

    &DEBUG("Exiting");
    exit;
}

sub CREATE {
    local(*F);
    my(%s, $sip, $host, $status, $line, $ip, $name, @p, $ipmask, $portmask, $portlist, @cfglist);

# $Infofile: (key => val, ... format)

    &DEBUG("starting");
    if (-f $Infofile) {
      open(F, "<$Infofile") || &Error("Infofile error");
      while (<F>) {
        %s = ( eval ($_) );
        $sip = &sip($s{'ip'});
        foreach $k (keys %s) {
          $mySite{$sip}{$k} = $s{$k};
        }
      }
      close(F);
    }

# $Configfile: (ipmask portmask)
    @cfglist = &ReadConfigfile();
    foreach (@cfglist) {
      ($ipmask, $portmask) = split / /;
      $portlist = join(',',&GeneratePortList($portmask));
      foreach (&GenerateSiteList($ipmask)) {
        $mySite{&sip($_)}{'portlist'} = $portlist;
      }
    }

# $Currentfile:
# Host: 192.168.10.1 ()\tStatus: Down
# or
# Host: 192.168.10.1 (pwawpz.localdomain)\tPorts: 21/open/tcp//ftp///, ....

    &DEBUG("Looking for $Currentfile");
    open(F, "<$Currentfile") || return;
    &DEBUG("Found $Currentfile");
    while (<F>) {
      chomp;
      ($host, $status) = split /\t/;
      $line = $_;
      ($ip, $name) = $line =~ /^Host: ([\d\.]+) \((.*?)\).*$/;
      $sip = &sip($ip);
      $mySite{$sip}{'line'}   = $line;
      $mySite{$sip}{'ip'}     = $ip;
      $mySite{$sip}{'name'}   = $name;
      if ($status =~ /^Status/) {
        $mySite{$sip}{'status'} = ($status =~ /Up/) ? $Stat_ok : $Stat_fail;
        $mySite{$sip}{'ports'}  = "";
      } else {
        $status =~ s/^Ports: //;
        foreach (split(/,\s*/, $status)) {
          @p = split /\//;
          $mySite{$sip}{'ports'} .= $p[0].',';
        }
        chop $mySite{$sip}{'ports'};
        $mySite{$sip}{'status'} = ($mySite{$sip}{'ports'} eq $mySite{$sip}{'portlist'}) ? $Stat_ok : $Stat_warn;
      }
    }
    close(F);

    foreach (@cfglist) {
      ($ipmask, $portmask) = split / /;
      &Generate(&GenerateSiteList($ipmask));
    }
    &DEBUG("Writing HTML files");
    &WriteHTMLfiles();

    if (lc($in{'s'}) eq "single") {
      &DETAIL();
    } else {
      my($type) = $in{'disp'};
      print (($type eq "compact") ? &DisplayHTML($Compactresultfile) : &DisplayHTML($Basicresultfile));
    }
    
    return;
}

sub AUTO {
    &Error("Auto Check mode is not available in DEMO mode.") if $DEMO;
    my($enabled);

    &DEBUG("in{s} = ",$in{'s'});
    if ($in{'s'} =~ /on/i) {
      $enabled = "enabled";
      $Autointerval = abs($Autointerval);
    } elsif ($in{'s'} =~ /off/i) {
      $Autointerval = -abs($Autointerval);
      $enabled = "disabled";
    }

    foreach (keys %in) { delete $in{$_}; }
    $in{'a'} = "pref";
    $in{'Autointerval'} = $Autointerval;
    &PREFS("save");
    &DEBUG("Auto Check has been ",$enabled);
    $in{'s'} = "basic";
    &DISPLAY("Auto Check has been ".$enabled);
}

sub DISPLAY {
    my $extra = shift;
    if ($in{'s'}) {
      require "$Libdir/gendisp.pl";
      print &DisplayHTML((($in{'s'} =~ /help/i)      ? $Helpfile :
                          ($in{'s'} =~ /copyright/i) ? $Copyrightfile :
                          ($in{'s'} =~ /compact/i)   ? $Compactresultfile : 
                          $Basicresultfile),$extra);
      
    } else {
      print &Header($Myname,$in{'s'},$extra,0);
      print <<EOF;
<p> Welcome to the nPULSE web-based network monitoring program for Linux and Unix. 
If you have already set up nPULSE, then jump right into the <a href="$Compactdisplay_url">Compact</a> or 
<a href="$Basicdisplay_url">Basic</a> monitoring page. 
For help, check out the <a href="$Help_url">Help Page</a>.</p>
<p>Otherwise you should manually <a href="npulse.cgi?a=setup&s=edit">Setup</a> 
nPULSE and click on the 'Recheck' button for a first check of your network. 
(See the README file for more information.)</p>
<hr><p><small>This software is a "proof of principle" prototype. The concept is to use
freely available software (under the GPL agreement) together with
"glue" software to solve real-world problems. nPULSE accomplishes
this by using the following freeware products:</small></p>
<blockquote><table>
<tr><td><small>PERL</small></td><td><small>Perl 5.004 or greater</small></td></tr>
<tr><td><small>NMAP</small></td><td><small>Network exploration tool V2.51 or greater</small></td></tr>
<tr><td><small>TRACEROUTE&nbsp;</small></td><td><small>Packet tracking tool</small></td></tr>
<tr><td><small>MINISERV</small></td><td><small>Web Server (written entirely in Perl)</small></td></tr>
<tr><td><small>HTML</small></td><td><small>nPULSE is Web-enabled and Web-driven</small></td></tr>
<tr><td><small>SH</small></td><td><small>Install scripts are in 'sh' shell script</small></td></tr>
</table></blockquote>
<p><small>The glue that holds the whole thing together is a set of custom Perl routines
and a single CGI program.</small></p>
EOF
    print &Footer();
    }
    &DEBUG("Exiting");
    exit;
}

sub STATUS {
  my $servstat = &ServerCheckStatus();
  my $autostat = &AutoCheckStatus();
  
  my($ipmask,$portmask,$numhosts,$numports,@ports);
  $numhosts = $numports = 0;
  foreach (&ReadConfigfile()) {
    ($ipmask, $portmask) = split / /;
    foreach (&GenerateSiteList($ipmask)) {
      $numhosts++;
      @ports = &GeneratePortList($portmask);
      $numports += $#ports+1;
    }
  }

  if ($servstat) {
    my($auto,$nauto,$autocheck);
    if ($autostat) { $auto = "Stop Process";  $nauto = "off"; }
    else           { $auto = "Start Process"; $nauto = "on"; }

    print &Header($Myname,"","nPULSE Status",2),
          '<blockquote><span style="',$Fsn,'">',scalar(localtime(time)),'<p>',
          'The Web Server is <font color="#',
          ($servstat?'00cc00">Running':'cc0000">Stopped'),'</font><p>',
          'The Automatic Network Monitor is <font color="#',
          ($autostat?'00cc00">'.((-f $Vardir.'/autorecheck.lock')?
          'Currently scanning the network':'Sleeping'):'cc0000">Stopped'),'</font> [',
          '<a href="',$nPULSEurl,'?a=auto&s=',$nauto,'">',$auto,'</a>]<p>',
          'nPULSE is monitoring ',$numhosts,' hosts and a total of ',$numports,' ports<p>',
          '</span></blockquote>'.&Footer();
  } else {
    print scalar(localtime(time)),"\n",
          'The Web Server is ',($servstat?'Running':'Stopped'),"\n",
          'The Automatic Network Monitor is ',
          ($autostat?((-f $Vardir.'/autorecheck.lock')?
          'Currently scanning the network':'Sleeping'):'Stopped'),"\n",
          'nPULSE is monitoring ',int($numhosts),' hosts and a total of ',int($numports),' ports>',"\n";
  }
  exit;
}

