#!/usr/bin/perl

#Additional routines for dealing with html
#Probably belong in html.pl but want to keep separate as html.pl
#not part of Report Writer
#payerle Feb 2001
#$Id:$

use strict;

sub encode_url_arg ($)
# Encodes an argument to be passed to html_parser so that doesn't
# contain whitespace or commas.  BAsically uuencoded then have characters
# translated to something Base64-like.  Adheres to absolutely no standards.
#Output is essentially same as MIME::Base64::encode_base64 but has the 
#uuencode initial string length character and does not have a trailing CR.
{	my $str =shift;
	my $uustr= pack "u*", $str;
	chomp $uustr;
	$uustr =~ tr# -\`#A-Za-z0-9+_=]#;
	return $uustr;
}

sub decode_url_arg ($)
#Undoes encode_url_arg.  Otherwise adheres to absolutely no standards.
{	my $uustr = shift;;
	$uustr="$uustr\n";
	$uustr =~ tr#A-Za-z0-9+_=]# -\`#;
	my $str= unpack "u*", $uustr;
	return $str;
}

# Creastes a refresh page pointing to $newurl, with timeout $timeout (defaults
# to 0) and text $text
sub refresh_html($$;$$)
{	my $client=shift;
	my $newurl = shift;
	my $timeout = shift or 0;
	my $text = shift or "<p>Processing command<br>Please wait.";

	print $client <<EOF;
<HTML>

<HEAD>
	<META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1">
	<META NAME="GENERATOR" Content="Saint">
	<META NAME="Description" CONTENT="Refresh for cgi-bin stuff">
	<META HTTP-EQUIV="Refresh" CONTENT="$timeout; URL=$newurl">
	<TITLE>SAINT: Processing command</TITLE>
</HEAD>

<BODY BGCOLOR="white">

<P></P>
<p>
$text

<p>
This page should automatically refresh in $timeout seconds.<br>
If your browser doesn't support refreshes, or for some other reason it
is not automatically bringing you to the correct page, just click on the
link below:<br>
<a href="$newurl">Follow this link</a>

</BODY>
</HTML>
EOF
}

sub _print_line_excludable($$$$$$$)
#Prints a HTML line for host/severity record with visibility dependendant
#upon whether that severity fact was excluded.
#Args are:
#	$client: FILEGLOB to print to
#	$color: color of dot (all caps)
#	$text_not_ignored: text to print if not excluded
#	$text_ignored: text to print if excluded and $exlusions::display_ignored
#	$fact: fact (the severity "fact" that is being printed)
#	$myurl: URL of calling page
#	$show_ignored: whether to show ignored stuff
# If record not excluded, will print the colored dot, $text_not_ignored,
# and a link enabling user to exclude it.
# If record is excluded, and $display_ignored is false, just returns.
# If record is excluded, and $display_ignored is true, prints a colored ghost
# instead of the dot, $text_ignored, and a link to un-exclude it.
#
# We assume the host list is in a <dd> </dd> HTML environment
{	my $client=shift;
	my $color = shift;
	my $text_not_ignored= shift;
	my $text_ignored = shift;
	my $fact=shift;
	my $myurl=shift;
	my $show_ignored = shift;

	$color = uc $color;
	my $lccolor = lc $color;
	my $excluded = is_excluded($fact);
	my ($dot,$text,$exclude_script, $exclude_text);
	my $encoded_fact=encode_url_arg($fact);

	if ( $excluded )
	{	#Excluded record, should be displayed at all?
		if (! $show_ignored ) { return; }
		#Yes
		$dot=
"<IMG SRC=\"$::HTML_SERVER/images/ghost-$lccolor.png\" ALT=\"$color (Ignored)\">";
		$text= $text_ignored;
		$exclude_script="unexclude_it";
		$exclude_text="Don't ignore";
	} else
	{	# Not excluded
		$dot=
"<IMG SRC=\"$::HTML_SERVER/dots/${lccolor}dot.gif\" ALT=\"$color\">";
		$text= $text_not_ignored;
		$exclude_script="exclude_it";
		$exclude_text="Ignore";
	}
	print $client <<EOF;
<dt>$dot $text &nbsp; 
<a href="$::HTML_SERVER/reporting/$exclude_script.pl,$encoded_fact,$myurl,">
$exclude_text</a>
EOF

}

sub _print_host_line_excludable($$$$$$$$)
#Prints a HTML line for a possibly excluded severity.  Assumes the line
#will contain the hostname, and will print a link to 
#saint_info_host for that host, with hostname italicized if ignored, followed
#by the text in misc.  See also _print_line_excludable
#Args are:
#	$client: FILEGLOB to print to
#	$host: hostname of host to print
#	$color: color of dot (all caps)
#	$misc: misc text (Usually a link to tutorial on vulnerability or 
#		stats on the host)
#	$fact: fact (the severity "fact" that is being printed)
#	$myurl: URL of calling page
#	$show_ignored: whether to print ignored stuff
#
# We assume the host list is in a <dd> </dd> HTML environment
{	my $client=shift;
	my $host=shift;
	my $color = shift;
	my $top20_ref = shift;
	my $misc=shift;
	my $fact=shift;
	my $myurl=shift;
	my $show_ignored=shift;

	if ( ! $::system )
	{	$::system="saint";
		warn("system not defined, setting to saint\n");
	}
	my $system = $::system;

	my $text_not_exc=<<EOF;
$top20_ref <a href="${system}_info_host.pl,$host,">$host:</a>
$misc
EOF
	my $text_exc=<<EOF;
$top20_ref <a href="${system}_info_host.pl,$host,"><it>$host:</it></a>
$misc
EOF

	_print_line_excludable($client,$color,
		$text_not_exc,$text_exc,$fact,$myurl,$show_ignored);
}

sub print_host_vulnerability_by_danger_line($$$$$$$$)
#Prints results for given host from saint_results_danger.pl
#Args are:
#	FILEGLOB to print to
#	hostname of host to print
#	$color: color of vulnerability
#	vulnerability text (Usually a link to tutorial on vulnerability)
#	fact (the severity "fact" that is being printed)
#	URL of calling page
#	$show_ignored: whether to print ignored stuff
#
#See _print_host_line_excludable
{	my $client=shift;
	my $host=shift;
	my $color=shift;
	my $top20_ref=shift;
	my $vuln=shift;
	my $fact=shift;
	my $myurl=shift;
	my $show_ignored = shift;

	_print_host_line_excludable($client, $host, $color,
		$top20_ref, $vuln, $fact, $myurl, $show_ignored);
}

sub get_host_vulnerability_count_string($;$)
#Returns a vulnerability count string for the named host suitable for inclusion
#in HTML report
#ARgs: $host: host to get stats for
#	$Exclusions::display_ignored: whether to include ignored results 
#REturns something like Red: 1 Brown: 2 or
# Red: 1(1) Yellow: 0(2) Brown: 2, where numbers in parentheses represent
# ignored vulnerabilities
{	my $host=shift;
	my $disp_ignored=shift || undef;

	my $string="";
	my ($tmpstr, $color, $cnt, $igcnt);

	&recalc_host_severity_count;
	my %sev_counts = ( 'Red', $::severity_red_host_count{$host},
			   'Yellow', $::severity_yellow_host_count{$host},
			   'Brown',  $::severity_brown_host_count{$host},
			   'Green',  $::severity_green_host_count{$host},
			);
	my %ig_counts = ( 'Red', 
			$Exclusions::severity_ignored_red_host_count{$host},
		   'Yellow', 
			$Exclusions::severity_ignored_yellow_host_count{$host},
		   'Brown',  
			$Exclusions::severity_ignored_brown_host_count{$host},
		   'Green',  
			$Exclusions::severity_ignored_green_host_count{$host},
			);

	foreach $color ( 'Red', 'Yellow', 'Brown', 'Green' )
	{	$cnt=$sev_counts{$color};
		$igcnt=$ig_counts{$color};
		if ( $cnt == 0 && $igcnt == 0 ) { next; }
		if ( $cnt == 0 && ! $disp_ignored ) { next; }
		$tmpstr = "$color: $cnt";
		if ( $igcnt != 0 && $disp_ignored )
		{	$tmpstr.="[$igcnt]";
		}

		$string.="$tmpstr ";
	}

	if (! $string ) { $string="No severity records."; }

	return "($string)";
}

sub print_vulnerability_by_host_line($$$$$$)
#Prints a vulnerability for a specific host (hostname printed previously)
#Args are:
#	$client: FILEGLOB to print to
#	$vuln: vulnerability
#	$color: color of dot/ghost to print
#	$fact: fact (the severity "fact" that is being printed)
#	$myurl: URL of calling page
#	$show_ignored: whether to print excluded stuff
#
{	my $client=shift;
	my $vuln=shift;
	my $color=shift;
	my $fact=shift;
	my $myurl=shift;
	my $show_ignored=shift;

	_print_line_excludable($client,$color, $vuln,
		$vuln, $fact, $myurl, $show_ignored);
}

sub print_host_by_vulnerability_line($$$$$$)
#Prints results for given host from saint_results_danger.pl
#Args are:
#	$client: FILEGLOB to print to
#	$host: hostname of host to print
#	$color: color of dot/ghost to print
#	$fact: fact (the severity "fact" that is being printed)
#	$myurl: URL of calling page
#	$show_ignored: whether to print excluded stuff
#
#Prints dot or ghost icon of appropriate color, the hostname (italicized
# if the vulnerability is ignored), and stats on the host (with ignored
#info if display ignored)
#See _print_host_line_excludable
{	my $client=shift;
	my $host=shift;
	my $color=shift;
	my $fact=shift;
	my $myurl=shift;
	my $show_ignored=shift;

	my $text = get_host_vulnerability_count_string($host,
		$show_ignored);
	my $top20_ref = "";

	_print_host_line_excludable($client, $host, $color,
		$top20_ref, $text, $fact, $myurl, $show_ignored);
}

sub get_host_dot_color($;$)
#Returns the appropriate dot color for a host line that doesn't refer to a 
#specific vulnerability.  E.g, return color of most severe vulnerability of
#the host.  If $sh_ignored set, includes excluded vulnerablities in the
#list.  Returns the upper case color (RED, YELLOW, BROWN, GREEN, or BLACK;
#black means no records found on host) in that order, if not showing ignored.
#Ignored RED,YELLOW, or BROWN stuff appears between uningored BROWN and GREEN
#in that order, and will result in something like "RED (Ignored)" returned
{	my $host = shift;
	my $disp_ignored=shift || undef;

	my @order = ( 'RED', $::severity_red_host_count{$host},
			   'YELLOW', $::severity_yellow_host_count{$host},
			   'BROWN',  $::severity_brown_host_count{$host},
			);
	if ( $disp_ignored )
	{ 	push @order, 
		(    'RED (Ignored)',$Exclusions::severity_ignored_red_host_count{$host},
		     'YELLOW (Ignored)',
				$Exclusions::severity_ignored_yellow_host_count{$host},
		     'BROWN (Ignored)',
				$Exclusions::severity_ignored_brown_host_count{$host}
		);
	}
	push @order,
		(	'GREEN',  $::severity_green_host_count{$host}
		);
	if ( $disp_ignored )
	{	push @order,
		(	'GREEN (Ignored)',  
			$Exclusions::severity_ignored_green_host_count{$host}
		);
	}

	#And finally, a catch-all
	push @order,
		(	'BLACK',  1 );

	my $color = shift @order;
	my $val = shift @order;
	while ( ! $val ) { $color=shift @order; $val = shift @order; }

	return $color;
}

sub get_severity_dot_color($)
#Returns the proper color for  a given severity code (the 2-4 letter code)
{	my $sevcode=shift;
	if ( $sevcode=~/g/ ) { return "GREEN"; }
	elsif ( $sevcode=~/z/ ) { return "BROWN"; }
	elsif ( $sevcode=~/y/ ) { return "YELLOW"; }
	else { return "RED"; }
}

sub print_host_line($$;$)
#Prints a line for a host, with appropriate colored dot and a count of
#all vulnerabilities
#If second arg is given and true, will include excluded stuff in calcs
{	my $client=shift;
	my $host = shift;
	my $disp_ignored=shift || undef;

	my $col=get_host_dot_color($host,$disp_ignored);
	my $vulncount=get_host_vulnerability_count_string($host, $disp_ignored);

	if ( ! $::system )
	{	$::system="saint";
		warn("system not defined, setting to saint\n");
	}
	my $system = $::system;

	my $color=$col;
	#Remove "(Ignored)" if exists
	$color=~s/\s.*$//;
	$color = uc $color;
	my $lccol = lc $color;
	my $dot;
	if ( $col=~/Ignored/ )
	{	#A ghost, not a dot
		$dot="images/ghost-$lccol.png";
	} else
	{	$dot="dots/${lccol}dot.gif";
	}

	print $client <<EOF;
<dt><IMG SRC=$::HTML_ROOT/$dot ALT="$color">
<a href="${system}_info_host.pl,$host,"> $host</a>
$vulncount
EOF
}

sub generate_html_header($$)
{	my $client=shift;
	my $title=shift;
	
	if ( ! $::system )
	{	$::system="saint";
		warn("system not defined, setting to saint\n");
	}
	return &generate_sara_html_header($client,$title) if ($::system eq "sara");
	return &generate_saint_html_header($client,$title) if ($::system eq "saint");
	return &generate_satan_html_header($client,$title) if ($::system eq "satan");
	die "Illegal value for \$::system = $::system";
}

sub generate_saint_html_header($$)
#Generates the standard SAINT page HTML header stuff.
#PAges should just call generate_saint_html_header(\*CLIENT,"Title");
#Then make their own, customized content, followed by generate_saint_html_footer
#The customized content will automatically appear in right hand side of table
#(e.g. should not have leading <td>, trailing </td> stuff)
{	my $client=shift;
	my $title=shift;

print $client <<EOF;
<HTML>
<HEAD>
<TITLE>$title</TITLE>
<LINK REV="made" HREF="mailto:saint\@saintcorporation.com">
</HEAD>
<BODY BGCOLOR="white" vlink="#000000" link="#04289D">

<P></P>

<P>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0">
	<TR>
		<TD COLSPAN="2"><IMG SRC="$::HTML_ROOT/images/data_analysis_banner.gif" WIDTH="621" HEIGHT="106" ALIGN="BOTTOM" ALT="Data Analysis" BORDER="0"></TD>
	</TR>
	<TR>
	  <TD WIDTH="86" VALIGN="TOP">
	    <TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0">
		<TR>
		    <TD WIDTH="86"><A HREF="http://www.saintcorporation.com"><IMG SRC="$::HTML_ROOT/images/home.gif" WIDTH="86" HEIGHT="55" ALIGN="BOTTOM" ALT="SAINT Corporation"
			BORDER="0"></A></TD>
		</TR>
		<TR>
		    <TD WIDTH="86"><A HREF="$::HTML_ROOT/saint.html"><IMG SRC="$::HTML_ROOT/images/home_button.gif" WIDTH="86" HEIGHT="46"
			ALIGN="BOTTOM" ALT="SAINT Home" BORDER="0" STYLE="Background-Image : none"></A></TD>
		</TR>
		<TR>
		    <TD WIDTH="86"><A HREF="$::HTML_SERVER/data/saint_data_form.pl"><IMG SRC="$::HTML_ROOT/images/data_mgmt_button.gif" WIDTH="86"
			HEIGHT="46" ALIGN="BOTTOM" ALT="Data Management" BORDER="0"></A></TD>
		</TR>
		<TR>
		    <TD WIDTH="86"><A HREF="$::HTML_SERVER/running/saint_run_form.pl"><IMG SRC="$::HTML_ROOT/images/target_selection_button.gif"
			WIDTH="86" HEIGHT="46" ALIGN="BOTTOM" ALT="Target Selection" BORDER="0"></A></TD>
		</TR>
		<TR>
		    <TD WIDTH="86"><A HREF="$::HTML_SERVER/reporting/analysis.pl"><IMG SRC="$::HTML_ROOT/images/data_analysis_button.gif" WIDTH="86"
			HEIGHT="46" ALIGN="BOTTOM" ALT="Data Analysis" BORDER="0"></A></TD>
		</TR>
		<TR>
		    <TD WIDTH="86"><A HREF="$::HTML_SERVER/admin/saint_cf_form.pl"><IMG SRC="$::HTML_ROOT/images/config_mgmt_button.gif" WIDTH="86"
			HEIGHT="46" ALIGN="BOTTOM" ALT="Configuration Management" BORDER="0"></A></TD>
		</TR>
		<TR>
		    <TD WIDTH="86"><A HREF="$::HTML_ROOT/saint_documentation.html"><IMG SRC="$::HTML_ROOT/images/documentation_button.gif"
			WIDTH="86" HEIGHT="46" ALIGN="BOTTOM" ALT="SAINT Documentation" BORDER="0"></A></TD>
		</TR>
		<TR>
		    <TD WIDTH="86" HEIGHT="46"><A HREF="$::HTML_ROOT/docs/FAQ.html#trouble"><IMG SRC="$::HTML_ROOT/images/troubleshooting_button.gif" WIDTH="86"
			HEIGHT="46" ALIGN="BOTTOM" ALT="Troubleshooting" BORDER="0"></A></TD>
		</TR>
		<TR>
		    <TD WIDTH="86" HEIGHT="65"><IMG SRC="$::HTML_ROOT/images/bottom_border.gif" WIDTH="86" HEIGHT="75" ALIGN="BOTTOM" ALT="--------" BORDER="0"></TD>
		</TR>
	    </TABLE>
	  </TD>
	  <TD VALIGN="TOP">
		<DL>
			<P>
		</DL>
<!-- End of stuff from generate_saint_html_header routine -->
EOF
} #End generate_saint_html_header

sub generate_html_footer($)
{	my $client=shift;
	
	if ( ! $::system )
	{	$::system="saint";
		warn("system not defined, setting to saint\n");
	}
	return &generate_sara_html_footer($client) if ($::system eq "sara");
	return &generate_saint_html_footer($client) if ($::system eq "saint");
	return &generate_satan_html_footer($client) if ($::system eq "satan");
	die "Illegal value for \$::system = $::system";
}

sub generate_saint_html_footer($)
#Generates the standard SAINT page HTML footer stuff.
#For use with generate_saint_html_header
{	my $client=shift;
	print $client <<EOF2;
<!-- The rest of this file was from generate_saint_html_footer routine -->
</TD></TR></TABLE>
</BODY>
</HTML>
EOF2
}



sub print_show_hide_html_message ($$;$$)
#Returns a string suitable for including in HTML that will link to a
#function to toggle current state of exclusions visibility in report
#Takes name of current URL as argument
{	my $client=shift;
	my $myurl=shift;
	my $leadtag=shift || "<br>";
	my $endtag=shift || "<br>";
	my $string;
	if ( $Exclusions::display_ignored )
	{	$string=<<EOF
<a href=\"$::HTML_SERVER/reporting/hide_ignored.pl,$myurl\">
	Hide excluded records</a>
EOF
	} else
	{	$string=<<EOF
<a href=\"$::HTML_SERVER/reporting/show_ignored.pl,$myurl\">
	Show excluded records</a>
EOF
	}
	print $client "$leadtag$string$endtag";
}


#Turn off strict cause most of SATAN/SARA/SAINT won't like it
no strict;
		
1;
