sub TRACEROUTE {
  my $ip = $in{'site'} || return;
  my $output = "";

  if ($DEMO) {
    local($/); undef $/;
    open(F, "<$TracerouteDemo");
    $output = <F>;
    close(F);

  } else {
    my $cmd = $Traceroutecmd . " " . $ip . " 2>&1";
    $output = `$cmd`;
  }

  return "<small><pre>".$output."</pre></small>\n";
}

sub FULLPORT {
    my $quick  = shift;    
    my $ip     = $in{'site'} || return;
    my $p      = $quick ?"" : " -p 1-65535";
    my $cmd    = $Nmapcmd." ".$nmap_general_options." ".$nmap_extra_options." -O -I ".$NmapScan.$p." ".$ip." 2>/dev/null";
    my $output = "";

    if ($DEMO) {
      local(*F);
      local($/); undef $/;
      if ($quick) { open(F, "<$QuickPortDemo"); }
      else {        open(F, "<$FullPortDemo");  }
      $output = <F>;
      close(F);

    } else {
      $output = `$cmd`;
    }

    return "<small><pre>".$output."</pre></small>\n";
}

sub QUICKPORT {
    return &FULLPORT(1);
}

sub RECHECKOS {
  my $ip = $in{'site'} || return;
  my $sip = &sip($ip);
  $mySite{$sip} = &ReadCurrentFile($ip);
  $mySite{$sip}{'os'} = "";
  require "$Libdir/gendisp.pl";
  my %OS;
  %OS = &GetAllOS($ip);
  foreach (keys %OS) {
    $sip = &sip($_);
    $mySite{$sip}{'os'}   = $in{'os'}   = $OS{$_};
    $mySite{$sip}{'icon'} = $in{'icon'} = &GetICON($OS{$_});
    &SaveStoredInfo($_);
  }
  &Generate($ip);
} 

sub USERCMDS {
  my $ip     = $in{'site'} || return;
  my $cname  = $in{'cmd'}  || return;
  my $output = "";

  return "User Commands are disabled in DEMO mode." if $DEMO;

  my %Cmds = &GetUserCmds();
  return "Unknown user command: $cname" if (!$Cmds{$cname}{cmd} or (!$AdminUser and $Cmds{$cname}{admin}));

  my $cmd = &Decode($Cmds{$cname}{cmd});
  $cmd =~ s/\$IP\$/$ip/gs;
  $cmd .= " 2>&1";

  $SIG{ALRM} = sub { die "timeout" };
  eval {
    alarm($CmdTimer);
    $output = `$cmd`;
    alarm(0);
  };

  if ($@) {
    if ($@ =~ /timeout/) {
        return "<pre>Command timed out after $CmdTimer seconds.</pre>";
    } else {
      alarm(0);
      die "Unexpected exception";
    }
  }

  $cmd =~ s/2>\&1$//;
  return "<small>Result of ".$cmd."<pre>".$output."</pre></small>\n";
}

sub RUNSCRIPT {
  my $ip    = $in{'site'} || return "Invalid ip address";
  my $sname = $in{'script'} || return "Invalid script name";
  require "$Libdir/sapi.pl";

  if ($sname and -f "$Scriptdir/$sname") {
    open(S, "<$Scriptdir/$sname") or return "Unable to open script: $sname";
    @sdata = <S>;
    chomp @sdata;
    close(S);
    $port   = shift @sdata; $port  =~ s/^port:\s*//;
    $hosts  = shift @sdata; $hosts =~ s/^hosts:\s*//; $hosts =~ s/\t/\n/gs;
    $script = join("\n",@sdata);
  } else {
    return "Unable to find script: $sname";
  }
  ($ret,$msg) = &sapi($ip,$port,$script,$ScriptTimeout);
  return "Results from the $sname script:<br>Script ".($ret?"Succeeded":"Failed")."!<p>".$msg;
}

sub HISTORY {
    return &DisplayHistory($in{'site'});
}

sub UPDATEINFO {
    &SaveStoredInfo($in{'site'});
    return;
}

sub DisplayStoredInfo {
    my(%site) = @_;
    my($ip) = $site{'ip'};
    return unless $DisplayCustInfo;
    my($i, $s, $k, $v, $selected, $img, @imgs);
    my @out = ();

    my $output = "<form method=\"post\" action=\"".$nPulseurl."\">".
        "<input type=hidden name=a value=\"detail\">".
        "<input type=hidden name=s value=\"updateinfo\">".
        "<input type=hidden name=site value=\"".$ip."\">".
        "<input type=hidden name=returnurl value=\"".$in{'returnurl'}."\">\n" if $AdminUser;
    $output .= "<p style=\"".$Fss."\"><strong>Information</strong><br>".
        "<table border=0 cellspacing=0 cellpadding=0 bgcolor=\"#272727\">\n";

    if (opendir(DIR,"$Htmldir/images/icons")) {
      @imgs = grep(/\.gif$/,readdir(DIR));
      closedir(DIR);
    }

    foreach $s (sort keys %site) {
      next unless $s =~ /_\d/;
      ($k, $i) = $s =~ /^(.*)_(\d+)$/;
      if ($s =~ /^Icon/) {
        $v = $site{'icon'};
        $s = "<select name=icon style=\"".$Fxs."\">";
        foreach $img (sort @imgs) {
          $img =~ s/\.gif$//;
          $selected = ($img eq $v) ? " selected" : "";
          $s .= '<option value="'.$img.'"'.$selected.'>'.$img.'</option>';
        }
        $s .= "</select>";
      } else {
        $v = $site{$s};
      }
      $out[$i] = "<tr><td align=\"right\"><span style=\"".$Fss."\"><nobr>".$k.
                    ":</nobr></span></td><td>";
      if ($AdminUser) {
        $out[$i] .= "<input type=text name=\"".$s.
                    "\" size=20 style=\"border-width:0 0 1 0;background-color:transparent;".
                    $Fxs.";color:white\" value=\"".$v."\">" unless $k eq 'Icon';
        $out[$i] .= $s if $k eq 'Icon';
        $out[$i] .= "</td></tr>\n";
      } else {
        $v = $site{'icon'} if $k eq 'Icon';
        $out[$i] .= "<span style=\"".$Fxs.";color:white\">".$v."</span></td></tr>\n";
      }
    }
    push(@out,"<tr><td align=\"right\"><span style=\"".$Fss."\"><nobr>OS".
                    ":</nobr></span></td><td><input type=text name=\"os".
                    "\" size=20 style=\"border-width:0 0 1 0;background-color:transparent;".
                    $Fxs.";color:white\" value=\"".$site{'os'}."\"></td></tr>\n") if $AdminUser;

    $output .= join("\n",@out)."</table></p>\n<center>";
    $output .= "<input type=submit style=\"".
               $Fxs."\" value=\"Update\"><input type=reset style=\"".$Fxs.
               "\" value=\"Reset\"></center>\n</form>\n" if $AdminUser;
    return $output;
}


sub DisplayHistory {
    &DEBUG("Starting");
    my $ip = shift;
	my $filename = &ip2file($ip) . ".hist";

    my($time, $gx, $c, $ct, $t, $d, $div, $dtime);
    my(%gdata, %history);
    my @data = ([],[],[],[]);
    my(@portlist);

    return "History flag is turned off." unless $Keephistory;

    my $warn   = 0;
    my $ok     = 0;
    my $fail   = 0;
    my $ctime  = time;
    my $output = "";
    my $html   = "";

    my %site = &ReadCurrentFile($ip);

    my($start_year,$start_mon,$start_day);
    my @stimevals = localtime($ctime - (($Maxhistorytime-1) * 86400));
    $start_year = $in{'startyear'} || $stimevals[5];
    $start_mon  = $in{'startmon'}  || $stimevals[4];
    $start_day  = $in{'startday'}  || $stimevals[3];
    my $stime = timelocal(1,0,0,$start_day,$start_mon,$start_year);
    
    my($end_year,$end_mon,$end_day);
    my @etimevals = localtime($ctime);
    $end_year = $in{'endyear'} || $etimevals[5];
    $end_mon  = $in{'endmon'}  || $etimevals[4];
    $end_day  = $in{'endday'}  || $etimevals[3];
    use Time::Local;
    $ctime = timelocal(59,59,23,$end_day,$end_mon,$end_year);

    my @xlabels;
    foreach $i (1..$Maxhistorytime) {
      @tvals = localtime($stime + (($i-1)*86400));
      push(@xlabels,$Months[$tvals[4]]);
      push(@xlabels,$tvals[3]);
    }

    %history = &ReadHistoryFile($ip,$start_year,$start_mon,$start_day,$end_year,$end_mon,$end_day);
    &Error("No recent historical data found in $filename.") unless $history{'keys'};
    @timelist = split(/,/,$history{'keys'});

    foreach $gx (@timelist) {
      $gdata{$gx}{$Stat_fail} = $history{'system'}{$gx}{$Stat_fail};
      $gdata{$gx}{$Stat_warn} = $history{'system'}{$gx}{$Stat_warn};
      $gdata{$gx}{$Stat_ok}   = $history{'system'}{$gx}{$Stat_ok};
    }
         
    if ($PortHistory > 0) {
      @portlist = split(/\s*,\s*/,$history{'portlist'});
      foreach $gx (@timelist) {
        foreach $port (@portlist) {
          $sdata{$port}{$gx}{$Stat_fail} = $history{'portstatus'}{$gx}{$port}{$Stat_fail};
          $sdata{$port}{$gx}{$Stat_ok}   = $history{'portstatus'}{$gx}{$port}{$Stat_ok};
        }
      }
    }

    foreach $gx (@timelist) {
      push(@{$data[0]},$gx);
      $div   = $gdata{$gx}{$Stat_ok} + $gdata{$gx}{$Stat_warn} + $gdata{$gx}{$Stat_fail};
      $ok   += $gdata{$gx}{$Stat_ok};
      $warn += $gdata{$gx}{$Stat_warn};
      $fail += $gdata{$gx}{$Stat_fail};
      if ($div) {
        push(@{$data[1]},($gdata{$gx}{$Stat_ok}/$div)*100);
        push(@{$data[2]},($gdata{$gx}{$Stat_fail}/$div)*100);
        push(@{$data[3]},($gdata{$gx}{$Stat_warn}/$div)*100);
      } else {
        push(@{$data[1]},0);
        push(@{$data[2]},0);
        push(@{$data[3]},0);
      }
    }


    my $num = $ok + $warn + $fail;
    my $warntime = int(($warn/$num)*1000)/10;
    my $failtime = int((($num-$ok-$warn)/$num)*1000)/10;
    my $uptime   = 100 - $warntime - $failtime;
    
    $html .= '<table border=0 cellspacing=5 cellpadding=0 width="100%"><tr valign="top"><td><span style="'.$Fsn.'">'.
             'Uptime OK:'.$uptime.'%, Warn:'.$warntime.'%, Fail:'.$failtime.
             '%</span><br>'.&genBarGraph($Maxhistorytime,\@data,\@xlabels);
    
    if ($PortHistory > 0) {
      $html .= '<table border=0 cellspacing=5 cellpadding=0><tr valign="top"><td align="center">';
      my $c = 0;
      foreach $port (@portlist) {
        $ok = $fail = 0;
        $html .= '<tr valign="top">' unless $c;
        @data = ([],[],[]);
        foreach $gx (sort {$b <=> $a} keys %{$sdata{$port}}) {
          push(@{$data[0]},$gx);
          $div = $sdata{$port}{$gx}{$Stat_ok} + $sdata{$port}{$gx}{$Stat_fail};
          $ok   += $sdata{$port}{$gx}{$Stat_ok};
          $fail += $sdata{$port}{$gx}{$Stat_fail};
          if ($div) {
            push(@{$data[1]},($sdata{$port}{$gx}{$Stat_ok}/$div)*100);
            push(@{$data[2]},($sdata{$port}{$gx}{$Stat_fail}/$div)*100);
          } else {
            push(@{$data[1]},0);
            push(@{$data[2]},0);
          }
        }
        $failtime = int(($fail/($ok+$fail))*1000)/10;
        $uptime   = 100 - $failtime;
    
        $html .= '<td align="center"><span style="'.$Fsn.'">'.$port.'('.$Services{$port}.')</span><br>'.
                 '<span style="'.$Fss.'">Uptime OK:'.$uptime.'%, Fail:'.$failtime.'%</span><br>'.
                 &genBarGraph($Maxhistorytime,\@data,\@xlabels).'</td>';
        if (++$c >= $PortHistory) {
          $c = 0;
          $html .= '</tr><tr><td colspan='.$PortHistory.'><big>&nbsp;</big></td></tr>';
        }
      }
      $html .= ($c?'</tr>':'').'</table>';
    }

    $html .= '</td></tr></table>';

    return $html;
}

sub genBarGraph {
    use strict;
    my ($xmax,$data,$xlabels) = @_;
    my (@yp);
    my (@colors) = qw(gbar rbar ybar);
    my ($j,$i,$maxh,$bar);
    my @xlb = @$xlabels;

    my $xp = $data->[0];
    my $numsets = $#{$data};
    
    return unless $numsets > 0;
    
    foreach $i (1..$numsets) {
      $yp[$i-1] = $data->[$i];
    }

    my $out = '<table border=1 cellspacing=0 cellpadding=2><tr><td>'.
              '<table border=0 cellspacing=0 cellpadding=0><tr valign="bottom">'.
              '<td><img src=images/ytick.gif width=40 height=102></td>';
    my $dat = '';
    my $xaxis = '<tr valign="top" align="center"><td></td>';
    my($m,$d);
    foreach $i (0..($xmax-1)) {
      $m = shift @xlb;
      $d = shift @xlb;
      $xaxis .= '<td><img src="images/xtick.gif" width=19 height=3><br><img src="images/months/month'.
                lc($m).'.gif" width=13 height=22><br>'.
                '<img src="images/dot.gif" height=3 width=3><br>'.
                '<img src="images/months/'.int($d % 10).'.gif" width=9 height=7><br>'.
                '<img src="images/months/'.int($d/10).'.gif" width=9 height=7></td>';
      $bar = '';
      if ($i <= $#{$xp}) {
        my %yvals;
        foreach $j (0..$#yp) {
          $yvals{int($yp[$j]->[$i])} = $colors[$j];
        }

        $maxh = 0;
        foreach (sort {$a <=> $b} keys %yvals) {
#          $bar = '<img src="images/'.$yvals{$_}.'.gif" width=19 height='.($_-$maxh).'><br>'.$bar if $_ > $maxh;
          $bar = '<img src="images/'.$yvals{$_}.'.gif" width=19 height='.$_.'><br>'.$bar if $_ > 0;
          $maxh += $_;
        }

        $bar = '<img src=images/bbar.gif width=19 height='.(100-$maxh).'><br>'.$bar if $maxh < 100;
        $dat .= '<td>'.$bar.'</td>';
      } else {
        $dat = '<td><img src=images/bbar.gif width=19 height=100><br></td>'.$dat;
      }
    }
    $out .= $dat.'</tr>'.$xaxis.'</tr><tr align="center"><td colspan='.$xmax.'>Past '.$xmax.' Days</td></tr>';
    $out .= '</table></td></tr></table>';
    return $out;
}

sub DisplayDETAIL {
    &DEBUG("Starting");
    my($ip, $output) = @_;
    my($c, $ct, $t, $td1, $td2, $td3, %buttons, $bg, $fg, $tx);
    my %ports = ();

    my %text = (
        hidetraceroute => "Hide Traceroute",
        traceroute     => "Perform Traceroute",
        hidequickport  => "Hide Port Scan",
        quickport      => "Perform Port Scan",
        hidefullport   => "Hide Full Port Scan",
        fullport       => "Perform Full Port Scan",
        history        => "Show Historical Info",
        hidehistory    => "Hide Historical Info",
        telnet         => "Telnet to $ip",
        hidetelnet     => "Hide Telnet window",
        ssh            => "SSH to $ip",
        hidessh        => "Hide SSH window"
    );

    my(%site) = &ReadCurrentFile($ip);
    $site{'os'} =~ s/\|/<br>/g;
    my(%scripts) = &GetScripts($ip);

    print &Header($Myname,"Detail for ".$site{'ip'},0);

    $td1 = "<td style=\"$Fsn\"><img width=32 height=32 align=\"left\" border=0 src=\"/images/icons/".
           $site{'icon'}.".gif\"> ".($site{'name'}?$site{'name'}:$site{'sip'})."<br>".
           ($site{'alias'}?"(".$site{'alias'}.")<br>":"").$site{'os'}."<br>";
    $td1 .= "<table border=1><tr>".
           "<td align=\"center\"><span style=\"$Fsn\">Port</span></td>".
           "<td align=\"center\"><span style=\"$Fsn\">Type</span></td>".
           "<td align=\"center\"><span style=\"$Fsn\">Status</span></td>".
           "</tr>\n" if $site{'portlist'};

    foreach (split(/,\s*/,$site{'ports'})) { $ports{$_} = 1; }
    foreach (split(/,\s*/,$site{'portlist'})) {
      ($bg, $fg, $tx) = exists $ports{$_} ? ($Color_ok, $Text_ok, "OK") : ($Color_fail, $Text_fail, "FAIL");
      
      $tx .= ' <a href="'.$nPulseurl.'?a=detail&s=runscript&site='.$ip.'&script='.$scripts{$_}.
             '"><img src="/images/script.gif" width=11 height=13 border=0></a>'
             if $scripts{$_};

      $td1 .= "<tr><td align=\"right\"><span style=\"$Fss\">".$_."</span></td>".
              "<td><span style=\"$Fss\">".($Services{$_}?$Services{$_}:"unknown")."</span></td>".
              "<td style=\"background-color:$bg\"><span style=\"$Fss;color:$fg;background-color:$bg\">".$tx."</span></td></tr>\n";
    }

    ($c, $ct, $t) = &colorStatus($site{'status'});
    $td2 = "<td><div style=\"".$Fss.";color:".$ct.";background-color:".$c."\">".
           (($site{'status'} == $Stat_fail)?"Device is not responding":($site{'status'} == $Stat_warn)?"Device responding with errors":"Device responding ok")."</div>";

    my $eventlog = "";
    if ($MaxEvents and ($AdminUser or $Events4Users)) {
      require "$Libdir/gendisp.pl";
      my @saveips = @AllowedIPs;
      undef @AllowedIPs;
      $AllowedIPs[0] = $ip;
      $eventlog = '<p>'.&DisplayEventLog(1).'</p>';
      @AllowedIPs = @saveips;
    }

    $td3 = "<td><span style=\"background-color:#272727;border:1 solid white;\">".
           &DisplayStoredInfo(%site)."</span>$eventlog</td>\n" if $DisplayCustInfo;

    foreach (qw(traceroute quickport fullport history)) {
      $buttons{$_} = "<a onMouseOver=\"s".$_.".src='images/s".$_."_on.gif'\"".
                     "   onMouseOut=\"s".$_.".src='images/s".$_.".gif'\"".
                     "   href=\"".$nPulseurl."?a=detail&site=".$ip."&returnurl=".$in{'returnurl'};
      $buttons{$_} .= (($in{'s'} =~ /$_/i) ? "" : "&s=".$_);
      $buttons{$_} .= "\"><img width=23 height=22 border=0 src=\"images/s".$_.".gif\" name=s".$_.">";
      $buttons{$_} .= ($in{'s'} =~ /$_/i) ? $text{'hide'.$_} : $text{$_};
      $buttons{$_} .= "</a>";
    }

    $buttons{telnet} = "<a onMouseOver=\"stelnet.src='images/stelnet_on.gif'\"".
                       "   onMouseOut=\"stelnet.src='images/stelnet.gif'\"".
                       '   href="'.$nPulseurl.'?a=telnet&host='.$ip.
                       '"><img width=23 height=22 border=0 src="images/stelnet.gif" name=stelnet>'.
                       (($in{'s'} =~ /telnet/i) ? $text{hidetelnet} : $text{telnet})."</a>";
    $buttons{ssh} = "<a onMouseOver=\"sssh.src='images/sssh_on.gif'\"".
                       "   onMouseOut=\"sssh.src='images/sssh.gif'\"".
                       '   href="'.$nPulseurl.'?a=telnet&mode=1&host='.$ip.
                       '"><img width=23 height=22 border=0 src="images/sssh.gif" name=sssh>'.
                       (($in{'s'} =~ /ssh/i) ? $text{hidessh} : $text{ssh})."</a>";
    $buttons{os}  = "<a onMouseOver=\"sos.src='images/sos_on.gif'\"".
                       "   onMouseOut=\"sos.src='images/sos.gif'\"".
                       '   href="'.$nPulseurl.'?a=detail&site='.$ip.'&s=recheckos">'.
                       '<img width=23 height=22 border=0 src="images/sos.gif" name=sos>'.
                       'Recheck Operating System</a>';

    $td1 .= "</table>\n" if $site{'portlist'};
    $td1 .= '<span style="'.$Fss.'"><i>No ports currently monitored</i></span>' unless $site{'portlist'};
    $td1 .= "</td>\n";
    $td2 .= "<br><span style=\"$Fss\">";
    $td2 .= $buttons{os}        ."<br>\n" if $AdminUser;
    $td2 .= $buttons{traceroute}."<br>\n".
            $buttons{quickport} ."<br>\n".
            $buttons{fullport}  ."<br>\n".
            $buttons{history}   ."<br>\n".
            ($HaveTelnet?$buttons{telnet}.'<br>'.$buttons{ssh}:'');

    my %Cmds = &GetUserCmds();
    my @ns = sort keys %Cmds;
    $td2 .= '<br><form method=post action="'.$nPulseurl.'">'.
            '<input type=hidden name=a value="detail">'.
            '<input type=hidden name=s value="usercmds">'.
            '<input type=hidden name=site value="'.$ip.'">'.
            '<select name=cmd style="'.$Fss.'">'.
            '<option selected value="">Custom Commands</option>';
    my $n;
    foreach $n (@ns) { 
      next unless $n;
      next if ($Cmds{$n}{admin} and !$AdminUser);
      $td2 .= '<option value="'.$n.'">'.$n.'</option>';
    }
    $td2 .= '</select><input type=submit value="Go" onClick="if (cmd.value == '."''".') { alert('."'Click the Settings Button to Add or Delete custom commands.'".');return false }" style="font-size:6pt;color:blue;background-color:black;border:0"></form>';

    $td2 .= "<p><font size=1>(Results at bottom of page)</font>" if $output;
    $td2 .= "</span></td>\n";
    print "<table cellspacing=4 width=\"100%\"><tr valign=\"top\">".$td1.$td2.$td3.
          "</tr></table>\n".$output.&Footer();
    &DEBUG("Exiting");
    exit;
}

sub GetScripts {
    my $ip = shift || return;
    $ip =~ s/\./\\./gs;

    my $port   = "";
    my $hosts  = "";
    my $script = "";
    my $cmd    = 'cd '.$Scriptdir.'; grep -l "^hosts:.*'.$ip.'" * 2>/dev/null';
    
    my (@files, %Scripts, $file);

    @files = `$cmd`;
    foreach $file (@files) {
      open(S, "<$Scriptdir/$file") || next;
      chomp (@sdata = <S>);
      close(S);
      $port   = shift @sdata; $port  =~ s/^port:\s*//;
      $Scripts{$port} = $file;
    }
    delete $Scripts{""};
    return %Scripts;
}

1;
