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>';
}

sub FULLPORT {
    my $quick  = shift;    
    my $ip     = $in{site} || return;
    my $p      = $quick ? '' : '-p 1-65535';
    my $cmd    = join(' ',$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>';
}

sub QUICKPORT {
    return &FULLPORT(1);
}

sub RECHECKOS {
  my $ip = $in{site} || return;
  $Config{$ip}{os} = '';
  require "$Libdir/gendisp.pl";
  my %OS;
  %OS = &GetAllOS($ip);
  undef %in;
  foreach (keys %OS) {
    $Config{$ip}{os}   = $in{os}   = $OS{$_};
    $Config{$ip}{icon} = $in{icon} = &GetICON($OS{$_});
    &SaveStoredInfo($_);
  }
  &Generate($ip);
  ($in{a},$in{s},$in{site}) = ('detail','overview',$ip);
  return &OVERVIEW();
} 

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);
      return '<pre>Unexpected exception</pre>';
    }
  }

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

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>; close(S);
    chomp @sdata;
    $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 {
    my $ip = $in{site};
    &SaveStoredInfo($ip);
    undef %in; undef %Config;
    &GetConfigHash();
    ($in{a},$in{s},$in{site}) = ('detail','overview',$ip);
    return &OVERVIEW();
}

sub UPDCHK {
    my $ip = $in{site};
    &SaveStoredInfo($ip);
    foreach $key (keys %in) {
      next unless $key =~ /^port\d+$/;
      $new{$key} = $in{$key} || '* * * * *';
      $new{$key} = '* * * * *' if $new{$key} =~ /allways/i;
    }
    my %site = &GetStoredInfo($ip);
    %site = &AddToStoredInfo(\%site,\%new);
    &SaveInfo2(%site);
    undef %in;
    ($in{a},$in{s},$in{site}) = ('detail','overview',$ip);
    return &OVERVIEW();
}

sub PORTCONFIG {
    unless ($AdminUser) {
      $in{s} = 'overview';
      return &OVERVIEW();
    }

    my $ip = $in{site};
    my %site;
    foreach (keys %{$Config{$ip}}) { $site{$_} = $Config{$ip}{$_}; }

    if ($in{'save'}) {
      #Save results
      my($key,%hr,%dow);
      foreach $key (keys %in) {
        if ($key =~ /^dow(\d+)$/) {
          ($dow{$1} = $in{$key}) =~ s/\000/,/g;
        } elsif ($key =~ /^hour(\d+)$/) {
          ($hr{$1} = $in{$key}) =~ s/\000/,/g;
        }
      }
      foreach (sort {$a <=> $b} keys %dow) {
        $site{'port'.$_} = $dow{$_}.':'.$hr{$_};
      }
      foreach ( qw(status Ports ports portlist IgnoredState Status) ) { delete $site{$_}; }
      &SaveInfo2(%site);
      undef %in;
      ($in{a},$in{s},$in{site}) = ('detail','overview',$ip);
      return &OVERVIEW();
    }

    #Display port change form
    my $html = <<HTML;
        <form method="post" action="$nPulseurl">
        <input type="hidden" name="a" value="detail">
        <input type="hidden" name="site" value="$ip">
        <input type="hidden" name="s" value="portconfig">
        <p align="right" style="$Fsn"><b>Port Configuration</b></p>
        <p style="$Fss">nPulse normally sends an Email alert whenever a port transistions from
        down to up or from up to down. By using this form you can specify when nPulse sends the alerts.
        For example, if this device is a server that disables the FTP port except during
        normal working hours you could set port 21 (FTP) to Days = Mon - Fri, Hours = 8a - 5p</p>
        <p style="$Fxs">Use the shift or control keys to make multiple selections.</p>
        <table border="0" cellspacing="0" cellpadding="2" width="100%">
HTML
    my $c = 0; my $i = -1;
    my($port,$sp,$selected,$day,$hour,$d,$h,@daylist,@hourlist);
    foreach $port (split(/,\s*/,$site{portlist})) {
      $html .= '<tr align="center">' unless $c;
      $html .= <<HTML;
<td><table border="1" cellspacing="0" cellpadding="2" width="100%">
<tr align="center"><td bgcolor="#333399" colspan=2><font style="$Fss"><b>$port<br>$Services{$port}</b></font></td></tr>
<tr align="center"><td bgcolor="#333399"><font style="$Fss"><b>Days</b></font></td>
<td bgcolor="#333388"><font style="$Fss"><b>Hours</b></font></td></tr>
<tr align="center"><td><select size="7" name="dow$port" style="$Fxs" multiple>
HTML
      # Format of site{port.$port} is daylist : hourlist
      # where daylist and hourlist are comma separated lists of numbers starting at -1
      ($d,$h)   = split(/\s*:\s*/,$site{'port'.$port});
      @daylist  = sort {$a <=> $b} split(/\s*,\s*/,$d);
      @hourlist = sort {$a <=> $b} split(/\s*,\s*/,$h);

      $i = -1;
      foreach $day ('All',@Days) {
        if ($#daylist >= 0) {
          if ($daylist[0] == -1) {
            $selected = ($i == -1)?'selected':'';
          } elsif (grep(/^$i$/,@daylist)) {
            $selected = 'selected';
          } else {
            $selected = '';
          }
        } elsif ($i == -1) {
          $selected = 'selected';
        } else {
          $selected = '';
        }
          
        $html .= '<option value="'.$i++.'" '.$selected.'>'.$day.'</option>';
      }

      $html .= '</select></td><td><select size="7" name="hour'.$port.'" style="'.$Fxs.'" multiple>';
      $i = -1;
      foreach $hour ('All',@Hours) {
        if ($#hourlist >= 0) {
          if ($hourlist[0] == -1) {
            $selected = ($i == -1)?'selected':'';
          } elsif (grep(/^$i$/,@hourlist)) {
            $selected = 'selected';
          } else {
            $selected = '';
          }
        } elsif ($i == -1) {
          $selected = 'selected';
        } else {
          $selected = '';
        }

        $html .= '<option value="'.$i++.'" '.$selected.'>'.$hour.'</option>';
      }

      $html .= '</select></td></tr></table></td>';
      if ($c++ >= 3) {
        $c = 0;
        $html .= '</tr>';
      }
    }
    $html .= '</tr>' if $c;
    $html .= '</table><input type="submit" name="save" value="Save" style="'.$Fxs.'"> '.
             '<input type="reset" value="Reset" style="'.$Fxs.'"></form>';
    return $html;
}

sub OVERVIEW {
    my(%site,%ports,$bg,$fg,$tx,$cx);
    my $ip = $in{site};
    foreach (keys %{$Config{$ip}}) { $site{$_} = $Config{$ip}{$_}; }
    my(%scripts) = &GetScripts($ip);

    my $td1 = '<td>';
    $td1 .= '<table border="1" cellspacing="0" cellpadding="2"><tr>'.
            '<td bgcolor="#333399" align="center"><span style="'.$Fss.'"><b>Port</b></span></td>'.
            '<td bgcolor="#333399" align="center"><span style="'.$Fss.'"><b>Type</b></span></td>'.
            '<td bgcolor="#333399" align="center"><span style="'.$Fss.'"><b>Status</b></span></td>'.
            '</tr>' if $site{portlist};

    foreach (split(/,\s*/,$site{ports})) { $ports{$_} = 1; }

    foreach (split(/,\s*/,$site{portlist})) {
      my $ok = &Ok2Check($ip,$_,%site);
      ($bg, $fg, $tx) = ($ok and exists $ports{$_}) ? ($Color_ok, $Text_ok, 'OK') :
                        ($ok ? ($Color_fail, $Text_fail, 'FAIL') :
                               ('#000000','#000000',''));
      
      $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{$_};

      my $cxtitle = 'Alerts sent every day at all hours';
      if ($site{'port'.$_} and ($site{'port'.$_} ne '-1:-1')) {
        $cxtitle = 'Alerts sent ';
        my ($d,$h) = split(/\s*:\s*/,$site{'port'.$_});
        if ($d == -1) {
          $cxtitle .= 'every day';
        } else {
          foreach (sort {$a <=> $b} split(/\s*,\s*/,$d)) {
            $cxtitle .= $Days[$_].',';
          }
          $cxtitle =~ s/,$//;
        }
        $cxtitle .= ' at ';
        if ($h == -1) {
          $cxtitle .= 'all hours';
        } else {
          foreach (sort {$a <=> $b} split(/\s*,\s*/,$h)) {
            $cxtitle .= $Hours[$_].',';
          }
          $cxtitle =~ s/,$//;
        }
      }

      $cx = '<img width="15" height="15" border="0" title="'.$cxtitle.'" src="'.$Htmlprefix.'/images/'.
            ($site{'port'.$_}?(($site{'port'.$_} eq '-1:-1')?'dot':'time'):'dot').
            ($ok?'':'_off').
            '.gif">';

      $td1 .= '<tr><td align="right" nowrap><span style="'.$Fss.'">'.$_.' '.$cx.'</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>';
    }

    $td1 .= $site{portlist}?'</table>':'<span style="'.$Fss.'"><i>No ports currently monitored</i></span>';
    $td1 .= '</td>';

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

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

    return '<table border="0" cellspacing="0" cellpadding="0" width="100%">'.
           '<tr valign="top">'.$td1.'<td>&nbsp;</td>'.$td2.'<td>&nbsp;</td>'.$td3.'</tr></table>';
}

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

    $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.'">' if $AdminUser;

    $output .=  '<p style="'.$Fss.'"><strong>Information</strong><br>'.
                '<table border="0" cellspacing="0" cellpadding="0" bgcolor="#272727">';

    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>';
      } else {
        $v = $site{icon} if $k eq 'Icon';
        $out[$i] .= '<span style="'.$Fxs.';color:white">'.$v.'</span></td></tr>';
      }
    }
    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>') if $AdminUser;

    push(@out,'<tr><td colspan="2"><span style="color:#ffffe7;'.$Fss.'">Extra Notes:</span><br>'.
              '<textarea name="notes" rows=5 cols=30 style="background-color:#ffffe7;border-width:1;'.$Fxs.'">'.
              $site{notes}.'</textarea></td></tr>');

    $output .= join("\n",@out).'</table></p><center>';
    $output .= '<input type="submit" style="'.
               $Fxs.'" value="Update"><input type="reset" style="'.$Fxs.
               '" value="Reset"></center></form>' 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);

    use Time::Local;

    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 @stimevals = localtime($ctime - (($Maxhistorytime-1) * 86400));
    $in{startyear} ||= $stimevals[5]-100;
    $in{startmon}  ||= $stimevals[4];
    $in{startday}  ||= $stimevals[3];
    my $stime = timelocal(1,0,0,$in{startday},$in{startmon},$in{startyear}+100);
    
    my @etimevals = localtime($ctime);
    $in{endyear} ||= $etimevals[5]-100;
    $in{endmon}  ||= $etimevals[4];
    $in{endday}  ||= $etimevals[3];
    $ctime = timelocal(59,59,23,$in{endday},$in{endmon},$in{endyear}+100);

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

    %history = &ReadHistoryFile($ip,$in{startyear}+100,$in{startmon},$in{startday},$in{endyear}+100,$in{endmon},$in{endday});
    &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 ($in{porthistory}) {
      @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) {
    foreach $i (1..$maxdays) {
      @tvals = localtime($stime + (($i-1)*86400));
      $gx = $Months[$tvals[4]].' '.$tvals[3];
      push(@{$data[0]},$gx);
      if (grep(/^$gx$/, @timelist)) {
          $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);
          }
      } 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;
    
    my $monthopts = "";
    my $monthopte = "";
    my $mi = 0;
    foreach (@Months) {
      $monthopts .= '<option value="'.$mi.'"'.(($mi == $in{startmon})?'selected':'').'>'.$_.'</option>';
      $monthopte .= '<option value="'.$mi.'"'.(($mi == $in{endmon})?'selected':'').'>'.$_.'</option>';
      $mi++;
    }
    
    $html .= '<table border="0" cellspacing="0" cellpadding="0" width="100%"><tr valign="top">'.
             '<td nowrap><span style="'.$Fss.'"><form method="post" action="'.$nPulseurl.'">'.
             '<input type="hidden" name="a" value="detail">'.
             '<input type="hidden" name="site" value="'.$ip.'">'.
             '<input type="hidden" name="s" value="history">'.
             '<table border="1" cellspacing="0" cellpadding="0"><tr><td colspan="3" align="center" bgcolor="#333399"><span style="'.$Fss.'"><b>Starting Time</b></span></td></tr>'.
             '<tr align="center"><td nowrap><span style="'.$Fxs.'">DD<br>'.
             '<input type="text" size="2" maxlength="2" name="startday" value="'.$in{startday}.'"></span></td>'.
             '<td><span style="'.$Fxs.'">MMM<br>'.
             '<select name="startmon">'.$monthopts.'</select></span></td>'.
             '<td nowrap><span style="'.$Fxs.'">YY<br>'.
             '<input type="text" size="2" maxlength="2" name="startyear" value="'.&sint($in{startyear},2).'"></span></td></tr>'.
             '<tr><td colspan="3" align="center" bgcolor="#333399"><span style="'.$Fss.'"><b>Ending Time</b></span></td></tr>'.
             '<tr align="center"><td nowrap><span style="'.$Fxs.'">DD<br>'.
             '<input type="text" size="2" maxlength="2" name="endday" value="'.$in{endday}.'"></span></td>'.
             '<td nowrap><span style="'.$Fxs.'">MMM<br>'.
             '<select name="endmon">'.$monthopte.'</select></span></td>'.
             '<td nowrap><span style="'.$Fxs.'">YY<br>'.
             '<input type="text" size="2" maxlength="2" name="endyear" value="'.&sint($in{endyear},2).'"></span></td></tr>'.
             '<tr><td colspan="3" align="center"><input type="submit" value="Refresh" style="'.$Fss.'"></td></tr></table>'.
             '<input type="checkbox" name="showeventlog" value="1" style="'.$Fxs.'" '.
             ($in{showeventlog}?'checked':'').'>Show Event Log<br>'.
             '<input type="checkbox" name="porthistory" value="1" style="'.$Fxs.'" '.
             ($in{porthistory}?'checked':'').'>Show Ports'.
             '</form></span></td><td>&nbsp;</td>'.
             '<td><span style="'.$Fsn.'">'.
             'Uptime OK:'.$uptime.'%, Warn:'.$warntime.'%, Fail:'.$failtime.
             '%</span>'.&genBarGraph($maxdays,\@data,\@xlabels).'</td><td>&nbsp;</td>';

    if ($in{showeventlog} and $MaxEvents and ($AdminUser or $Events4Users)) {
      require "$Libdir/gendisp.pl";
      my @saveips = @AllowedIPs;
      undef @AllowedIPs;
      $AllowedIPs[0] = $ip;
      $html .= '<td>'.&DisplayEventLog(1).'</td>';
      @AllowedIPs = @saveips;
    }
    $html .= '</tr></table>';
    
    if ($in{porthistory}) {
      $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}}) {
        foreach $i (1..$maxdays) {
          @tvals = localtime($stime + (($i-1)*86400));
          $gx = $Months[$tvals[4]].' '.$tvals[3];
          push(@{$data[0]},$gx);
          if (grep(/^$gx$/, @timelist)) {
            $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);
            }
          } 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($maxdays,\@data,\@xlabels).'</td>';
        if (++$c >= $PortHistory) {
          $c = 0;
          $html .= '</tr><tr><td colspan='.$PortHistory.'><big>&nbsp;</big></td></tr>';
        }
      }
      $html .= ($c?'</tr>':'').'</table>';
    }

    return $html;
}

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

    my $xp = $data->[0];
    my $numsets = $#{$data};
    
    return unless $numsets > 0;
    
    my (@yp,$i);
    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="'.$_.'"><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.'>'.$xmax.' Days</td></tr>';
    $out .= '</table></td></tr></table>';
    return $out;
}

sub DisplayDETAIL {
    &DEBUG('starting');
    my($ip, $output) = @_;

    my %text = (
        overview       => 'Device Overview',
        traceroute     => 'Perform Traceroute',
        quickport      => 'Perform Port Scan',
        fullport       => 'Perform Full Port Scan',
        history        => 'Show Historical Info',
        telnet         => "Telnet to $ip",
        ssh            => "SSH to $ip",
        recheckos      => 'Recheck Operating System',
        portconfig     => 'Port Configuration'
    );

    my %site;
    foreach (keys %{$Config{$ip}}) { $site{$_} = $Config{$ip}{$_}; }
    $site{os} =~ s/\|/<br>/g;

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

# System Info

    my $sysinfo = '<span 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}.'</span>';

    my ($c,$ct,$t);
    ($c, $ct, $t) = &colorStatus($site{status});
    my $devstatus = '<span 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').'</span>';

# Navigation

    my($ison,$bgcolor,%buttons);
    foreach (qw(overview traceroute quickport fullport history telnet ssh recheckos portconfig)) {
      $ison = $in{s} eq $_;
      $bgcolor = $ison?'#f0f0ff':'#000000';
      $buttons{$_}   = '<tr><td nowrap id="'.$_.'" style="'.$Fss.';background-color:'.
                       $bgcolor.'">'.
                       '<a onMouseOver="hilite('.($ison?'0':'1').',\''.$_.'\')"'.
                       '   onMouseOut="hilite('.($ison?'0':'2').',\''.$_.'\')"';
      $buttons{$_} .=  ($_ eq "telnet")?
                       '   href="'.$nPulseurl.'?a=telnet&host='.$ip
                       :($_ eq "ssh")?
                       '   href="'.$nPulseurl.'?a=telnet&mode=1&host='.$ip
                       :($_ eq "history")?
                       '   href="'.$nPulseurl.'?a=detail&s=history&showeventlog='.(($MaxEvents>0)?'1':'0').'&site='.$ip
                       :($_ eq "recheckos")?
                       '   href="'.$nPulseurl.'?a=detail&s=recheckos&site='.$ip
                       :
                       '   href="'.$nPulseurl.'?a=detail&site='.$ip.'&s='.$_;
      $buttons{$_} .=  '">'.$text{$_}.'</a></td><td id="a_'.$_.'" align="right" style="'.$Fss.';background-color:'.
                       $bgcolor.'"><img width="14" height="13" src="/images/'.
                       ($ison?'arrow':'dot').'.gif"></td></tr>';
    }
    my $navbar = '<table border="0" cellspacing="0" cellpadding="0">'.
                 '<tr><td colspan="2" align="center" bgcolor="#333399"><font style="'.$Fss.'"><b>Commands</b></font></td></tr>'.
                 $buttons{overview};
    $navbar .= $buttons{recheckos}.$buttons{portconfig} if $AdminUser;
    $navbar .= $buttons{traceroute}.
               $buttons{quickport}.
               $buttons{fullport}.
               $buttons{history}.
               ($HaveTelnet?$buttons{telnet}.$buttons{ssh}:'');

    $navbar .= '<tr><td colspan="2">&nbsp;</td></tr>';

    my %Cmds = &GetUserCmds();
    my @ns = sort keys %Cmds;
    $navbar .= '<tr><td colspan="2" align="center" bgcolor="#333399"><font style="'.$Fss.
               '"><b>Custom Commands</b></font></td></tr>' if $#ns >= 0;

    my $n;
    foreach $n (@ns) { 
      next unless $n;
      next if ($Cmds{$n}{admin} and !$AdminUser);
      $ison = $in{cmd} eq $n;
      $bgcolor = $ison?'#f0f0ff':'#000000';
      $navbar .= '<tr><td nowrap id="'.$n.'" style="'.$Fss.';background-color:'.
                 $bgcolor.'">'.
                 '<a onMouseOver="hilite('.($ison?'0':'1').',\''.$n.'\')"'.
                 '   onMouseOut="hilite('.($ison?'0':'2').',\''.$n.'\')"'.
                 '   href="'.$nPulseurl.'?a=detail&site='.$ip.'&s=usercmds&cmd='.$n.'">'.
                 $n.'</a></td><td id="a_'.$n.'" align="right" style="'.$Fss.';background-color:'.
                 $bgcolor.'"><img width=14 height=13 src="/images/'.
                 ($ison?'arrow':'dot').'.gif"></td></tr>';
    }
    $navbar .= '</table>';

# Print results
    
    print <<HTML;
        <table cellspacing="0" cellpadding="0" width="100%">
        <tr valign="top"><td>$sysinfo</td><td align="right">$devstatus</td></tr></table>
        <table cellspacing="0" cellpadding="0" width="100%">
        <tr><td colspan="2" bgcolor="#f0f0ff" height="1"><img src="/images/dot.gif" height="1"></td>
        <td colspan="2"></td></tr>
        <tr valign="top"><td nowrap>$navbar</td>
        <td bgcolor="#f0f0ff" width="1"><img src="/images/dot.gif" width="1"></td>
        <td width="8"><img src="/images/dot.gif" width="8"></td>
        <td width="100%">$output</tr></table>
HTML
    print &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;
