#!/usr/bin/perl

eval 'exec /usr/bin/perl  -S $0 ${1+"$@"}'
    if 0; # not running under some shell
#
# $Id: ndiff2html,v 1.2 2000/12/16 08:59:33 levine Exp $
#
# Copyright (c) 2000  James D. Levine (jdl@vinecorp.com)
#
#
#   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.
#
####################################################################



use strict;
use PortScan::ScannedHost;

use Getopt::Long;

my $embed = 0;			# if !0 output embeddable html fragment; 
                                # otherwise output a full html page

my $help = 0;

sub usage
{
    print <<DONE;

 ndiff -b <baseline> -o <observed>  -fmt machine  |   ndiff2html  [-e|-embed]

   HINT: let ndiff run ndiff2html for you -- try "ndiff -fmt html"  or "ndiff -fmt htmle"

DONE
    ;
    exit 1;
}

GetOptions(
	   "e|embed!"  => \$embed,
	   "h|help!"   => \$help,
	   );


&usage() if $help || scalar( @ARGV );

my %new;
my %missing;
my %changed;

$\ = "\n";

LINE:

while( <> )
{
    /^new:\s*(\S*):\s*(.*)/ &&
	do
	{ 
	    $new{$1} = [split / /, $2];
	    next LINE;
	};

    /^missing:\s*(\S*):\s*(.*)/ &&
	do
	{ 
	    $missing{$1} = [split / /, $2];
	    next LINE;
	};

    /^changed:\s*(\S*):\s*(.*)/ &&
	do
	{ 
	    $changed{$1} = [split / /, $2];
	    next LINE;
	};

}

print "<HTML><HEAD></HEAD><BODY>" if ( ! $embed );

print "<CENTER>";
print "<A NAME=\"top\"></A>";

print "<A HREF=\"#new\">New Hosts</A><BR>";
print "<A HREF=\"#missing\">Missing Hosts</A><BR>";
print "<A HREF=\"#changed\">Changed Hosts</A><BR>";

print "<P><HR>";

print "<A NAME=\"new\"></A>";

{
    print "<FONT SIZE=\"+2\">New Hosts</FONT><BR>";
    print "<I>[ previously un-observed hosts found in the more recent scan<BR>-- all interesting ports shown ]</I><BR><P>";

    if ( scalar( keys %new ) )
    {
	print "<TABLE BORDER=\"1\">";

#	while ( my ($host, $ports) = each %new )
	foreach my $host ( PortScan::ScannedHost::sorted_ip_scalar_list( keys %new ) )
	{
	    my $ports = $new{$host};
	    host_to_tr( $host, $ports );
	}

	print "</TABLE>";
    }
    else
    {
	print "none";
    }
}

print "<P><A HREF=\"#top\">back to top</A><BR>";
print "<P><HR>";
print "<A NAME=\"missing\"></A>";

{
    print "<BR><BR><FONT SIZE=\"+2\">Missing Hosts</FONT><BR>";
    print "<I>[ previously observed hosts missing from the more recent scan<BR>-- no original ports shown ]</I><BR><P>";

    if ( scalar( keys %missing ) )
    {
	print "<TABLE BORDER=\"1\">";

#	while ( my ($host, $ports) = each %missing )
	foreach my $host ( PortScan::ScannedHost::sorted_ip_scalar_list( keys %missing ) )
	{
	    my $ports = $missing{$host};
	    host_to_tr( $host, $ports );
	}
	print "</TABLE>";
    }
    else
    {
	print "none";
    }

}

print "<P><A HREF=\"#top\">back to top</A><BR>";
print "<P><HR>";
print "<A NAME=\"changed\"></A>";

{
    print "<BR><BR><FONT SIZE=\"+2\">Changed Hosts</FONT><BR>";
    print "<I>[ hosts which have changed since the original scan<BR>-- interesting changed ports shown ]</I><BR><P>";

    if ( scalar( keys %changed ) )
    {
	print "<TABLE BORDER=\"1\">";
    
#	while ( my ($host, $ports) = each %changed )
	foreach my $host ( PortScan::ScannedHost::sorted_ip_scalar_list( keys %changed ) )
	{
	    my $ports = $changed{$host};
	    changed_host_to_tr( $host, $ports );
	}

	print "</TABLE>";
    }
    else
    {
	print "none";
    }
}


print "<P><A HREF=\"#top\">back to top</A><BR>";
print "<HR><P><I><FONT SIZE=\"-1\">Generated by ndiff2html " .  `date` . "</FONT></I>"
    if !$embed;

print "</CENTER>";

print "</BODY></HTML>" if ( ! $embed );

sub host_to_tr
{
    my ($host, $ports) = @_;

    print "<TR><TH ALIGN=\"CENTER\" COLSPAN=\"5\">$host</TH><TR>";
    print "<TR><TD>Port</TD><TD>Protocol</TD><TD>Service Name</TD><TD>State</TD></TR>"
	if ( scalar(@$ports) );

    foreach my $port (sort compare_port @$ports)
    {
	my ($port, $proto, $name, $state, $state_sm) = split( /\//, $port );
	printf
	    "<TR><TD>%s &nbsp;</TD><TD>%s &nbsp;</TD><TD>%s &nbsp;</TD><TD>%s &nbsp;</TD></TR>",
	    $port, $proto, $name, $state;
    }


}

sub changed_host_to_tr
{
    my ($host, $ports) = @_;

    print "<TH ALIGN=\"CENTER\" COLSPAN=\"5\">$host</TH>";
    print "<TR><TD>Port</TD><TD>Protocol</TD><TD>Service Name</TD><TD>Original State</TD><TD>Observed State</TD></TR>";

    foreach my $port ( sort compare_port @$ports)
    {
	chomp $port;
	my ($port, $proto, $name, $base_state, $obs_state) = split( /\//, $port );
	printf
	    "<TR><TD>%s &nbsp;</TD><TD>%s &nbsp;</TD><TD>%s &nbsp;</TD><TD>%s &nbsp;</TD><TD>%s &nbsp;</TD></TR>",
	    $port, $proto, $name, $base_state, $obs_state;
    }


}


sub compare_port
{
    my ($aport, $aproto) = split( /\//, $a );
    my ($bport, $bproto) = split( /\//, $b );

    if ( $aproto eq $bproto )
    {
	return $aport <=> $bport;
    }

    return -1 if $aproto eq "tcp";
    return 1;

}


=head1 NAME

ndiff2html - produce html from ndiff machine-format output

=head1 SYNOPSIS

 ndiff -b <baseline> -o <observed>  -fmt machine  |   ndiff2html  [-e|-embed]


=head1 DESCRIPTION

ndiff2html is a filter which makes an html table from ndiff machine-format output,
generated as shown above.  Input is taken from STDIN, and output is sent to STDOUT.
Usually ndiff2html is run directly from within ndiff.  See L<ndiff> for more
information.

=head1 OPTIONS

=over 4

=item -e

=item -embed

Specifies the html output should be an html fragment rather than a full html document.


=head1 AUTHOR

James Levine <jdl@vinecorp.com>


=cut



