#!/usr/bin/perl -w

require 5.004;
use locale;
use POSIX qw (locale_h);
use CGI qw (:standard);
use DBI;
use POSIX qw(strftime);
use Time::Local;
unshift(@INC, '.');
use ipac_cfg;
use strict;

my $fetchipac = $ipac_cfg::fetchipac;
my $fetchipac_options = "";
my $ipacsum = $ipac_cfg::ipacsum;
my $title = "Traffic statistics";
my $form_head = "<H1> Show traffic statistics </H1>";
my $cash_left = "<h2>  : ";
my $cash_left2 = "</h2>";
my $submit_btn = "Show me!";
my $sel = "Select period: ";
my $ssl_is_a_must = "1";
my $filter_to_ssl_user = "1";
my $charset = "koi8-r";
my $admin_login = "admin";
my $show_cash_left = "1";
my $allow_regex = "1";
my $rulenumber = 0;


######  There are no user adjustable parameters below this line #############
#############################################################################

my $user = $ENV{REMOTE_USER};
my $now = time();
my $me = $0;
$me =~ s|^.*/([^/]+)$|$1|;
# calculate time zone offset in seconds - use difference of output of date
# command and time function, round it
my $tzoffset = time()-timegm(localtime());
# get time zone name
my $starttime = 0 + $tzoffset;
my $endtime = $now;

my $q = new CGI;
my $machine_name = "";
my $options = "0";
my $show_graph = "0";
my $skip_empty_lines = 0;
my $skip_empty_times = 0;
my $show_summary = 0;
my $st_align = "center";
my $exact = 0;
my $exacts = 0;
my $regex ="";
my %bytez=();
my %last_printed=();
my @timestamps;
my %rulenames=();


setlocale(LC_CTYPE, "");

sub commify {
        local $_ = shift;
	my $letter = $_;
	
	$letter =~ s/\d//g;
	$_++; $_--;
        1 while s/^([-+]?\d+)(\d{3})/$1,$2/;
        return $_ . $letter;

}

sub nice_number {
        my($n)=shift;

        if ($n >  10_000_000_000) {        # 9999 MByte
                $n = sprintf("%dG", $n/1_000_000_000);
        } elsif ($n > 9999 * 1000) {
                $n = sprintf("%dM", $n/1_000_000);

        } elsif ($n > 9999) {
                $n = sprintf("%dK", $n/1000);
        }
        $n;
}

sub customized_number {
        my $n = shift;

        if ($exact != 1) {
                $n = &nice_number($n);
        }
        return commify($n);
}

if (($ssl_is_a_must eq "1" && $ENV{HTTPS} ne "on") || !(defined($user) || !length($user))) {
    print $q->header(-type=>"text/html; charset=$charset", 
					    -status=>'403 Forbidden'),
	    $q->start_html('Forbidden'),
	    $q->h1('You dont have permission to access this script'),
	    $q->end_html;
    exit;
}

$regex = $q->param('regex');
my $ipac_filt = "-l " . $regex if ($regex || $regex);

if ($q->param('showpic')) {
	print_pic($q->param('showpic'), $q->param('frame_s'), $q->param('frame_e'));
}

print	$q->header(-type=>"text/html; charset=$charset"),
	$q->start_html($title);

if ($show_cash_left eq "1") {
	my $dbh = DBI->connect("dbi:Pg:dbname=$ipac_cfg::pg_dbname",
	    	                $ipac_cfg::pg_login, $ipac_cfg::pg_pwd,
                        	{ RaiseError => 1, AutoCommit => 0 });

	my $sth = $dbh->prepare("select cash from cash, customers ".
			    "where cash.cust_id=customers.cust_id and ".
			    "customers.login = ?");
	$sth->execute($user) or die $sth->errstr;
	my @row = $sth->fetchrow_array();
	printf "<table align=center><tr><td>$cash_left %.2f $cash_left2</td></tr></table>", $row[0];
	$sth->finish();
	$dbh->disconnect() or warn $dbh->errstr;
}

$options = print_get_form();
if ($options eq "0") {
	print $q->end_html;
	exit;
}

($starttime, $endtime, $options) = split(/ /, $options, 3);
if ($options =~ /graph/i) {
	$show_graph = "1";
	$st_align = "left";
}
my $ipacsum_s = strftime "%Y%m%d%H%M%S", localtime($starttime);
my $ipacsum_e = strftime "%Y%m%d%H%M%S", localtime($endtime);

if ($options =~ /showempty/i) {
	$skip_empty_lines = 1;
}
if ($options =~ /skiptimestamps/i) {
	$skip_empty_times = 1;
}
if ($options =~ /summary/i) {
	$show_summary = 1;
}
if ($options =~ /exact/i) {
	$exact = 1;
}
if ($options =~ /sumexa/i) {
	$exacts = 1;
}
## print summary
{
	if ($exacts == 1) {
		open (DATA, "$ipacsum $ipac_filt -x -s\"$ipacsum_s\" -e\"$ipacsum_e\"|")
				|| die "$me: can't run $ipacsum\n";
	} else {
		open (DATA, "$ipacsum $ipac_filt -s\"$ipacsum_s\" -e\"$ipacsum_e\"|")
				|| die "$me: can't run $ipacsum\n";
	}
	print "<TABLE align=center width=50% border=1>";
	$_=<DATA>;
	$_=<DATA>;
	$_=<DATA>; # skip first three lines
	if ($show_summary) {
		print "<caption>Summary</caption>";
	}
	my $t1;
	my $t2;
	my $t3;
	my $rulenumber=0;
	while(<DATA>)
	{
		($t1, $t2) = split(/\:/, $_, 2);
		$t1 =~ s/^\**\s*//g;
		$t1 =~ s/\s*$//g;
                if (($filter_to_ssl_user eq "0" || $user eq $admin_login
				|| ($t1 =~ /$user/)) && ($t1 =~ /$regex/)) {
			    if ($skip_empty_lines == 0 || $t2 ne 0) {
				    $bytez{$t1} = $t2;
				    $last_printed{$t1} = $starttime;
				    $t3 = commify($t2);
				    if ($show_summary) {
					    print "<tr><td>$t1</td><td align=right>$t3</td></tr>";
					    $rulenames{$t1}=$rulenumber++;
				    }
			    }
		}
	}
	close DATA;
	print "</table><P>";
}


my $header_printed = 0;
my %acc_bytes;
my %acc_pkts;
my $tre;
my @rules_sorted = sort keys %rulenames;

if (defined($q->param('detailed')) && $q->param('detailed') eq 'on') {

	print "<TABLE align=$st_align CELLPADDING=2 CELLSPACING=1 BORDER=1>";

	open(DATA, "$fetchipac $fetchipac_options -t$starttime,$endtime -m|") 
		|| die "can't run $fetchipac\n";

	my $count=<DATA>;
	while(<DATA>)
	{
		if (/^(.)\s(\d+)$/) {
			my $ts = $2;
			if ($1 eq "*") {
				push(@timestamps, $ts);
			}
		}	
	}
	close DATA;

	pipe(PREAD,PWRITE);
	my $pid = fork();
	die "$me: can't fork: $!\n" if (!defined($pid));
	if ($pid == 0) {
		close PREAD;
		close STDOUT;
		open(STDOUT, ">&PWRITE") 
			|| die "$me: cant redirect stdout to pipe\n";
		open(FETCHIPAC, "|$fetchipac -r -m") 
			|| die "$me: can't exec $fetchipac\n";
	        print(FETCHIPAC $timestamps[0],"-",$timestamps[-1],"\n");
		close(FETCHIPAC);
		close(PWRITE);
		exit;
	}
	close PWRITE;

	while(<PREAD>)
	{
		my $timestamp;
		/^ADD\s*$/i or die "$me: bad line from fetchipac: $_\n";
		# second line of fetchipac output: timestamp no_of_records
		$_ = <PREAD> || last;
		/^(\d+)\s(\d+)$/ or die "$me: bad line from fetchipac: $_\n";
		$timestamp = $1;
		# read each record
		read_data_record("PREAD", $timestamp);

	}
	close PREAD;

	print "</TABLE>\n\n";
}


if ($show_graph eq "1") {
	print "<TABLE align=right>";
	my $iam = $q->url();
	foreach(@rules_sorted) {
		my $tmp=$_;
		s/ /_/g;
		if (defined $bytez{$tmp}) {
    			print "<TR><TD><IMG src=$iam?showpic=$_&frame_s=$ipacsum_s&frame_e=$ipacsum_e>
					</TD></TR>";
		}
	}
	print "</TABLE>\n\n";
}

print $q->end_html;
######################### end of main proc #############################

sub read_data_record {
	my($file, $irec);
	my($pkts, $bytes, $rule);
	my(@result);

	$file=shift;
	my $ttt=localtime(shift);
	my $header_printed = 0;
	$_ = <$file>;
	chop;
	/^\(\s*(.*)$/ or die "$me: bad line from fetchipac
					(expecting machine name): $_\n";
	$machine_name = $1; 	# remember final machine name
	while(<$file>) {
		last if (/^\)$/);	# terminating line ')'
		/^(\d+)\s(\d+)\s\|(.*)\|$/
			or die "$me: bad line from fetchipac 
					    (expecting rule item): $_\n";
		$bytes = customized_number($1);
		$pkts = customized_number($2);
		$rule = $3;
		if (($filter_to_ssl_user eq "0" || 
			    ($user eq $admin_login) || ($rule =~ /$user/)) && ($rule =~ /$regex/)) {
                        if (!defined($rulenames{$rule})) {
                                $rulenames{$rule}=$rulenumber++;
                        }
			if (!$skip_empty_times && !$header_printed) {
				$header_printed = 1;
				print "<TR><TD><STRONG> $ttt </STRONG></TD>
					<TD align=right> packets </TD>
					<TD align=right> bytes </TD></TR>";
			}
			if (($skip_empty_lines && ($bytes ne "0"))
    					|| !$skip_empty_lines) {
				if (!$header_printed) { #print date, etc
					$header_printed = 1;
					print "<TR><TD><STRONG> $ttt </STRONG>
						</TD><TD align=right> packets </TD>
						<TD align=right> bytes </TD></TR>";
				}
				print "<TR><TD> $rule </TD><TD align=right>
					 $pkts </TD><TD align=right> $bytes </TD></TR>";
			}
    		}
	}
	# read another emtpy line (data format consistency)
	$_ = <$file>;
	die "$me: bad data from fetchipac (expected emtpy line): $_\n"
		if ($_ !~ /^$/);
}

sub print_pic {
	my $pic=shift;
	my $frame_s=shift;
	my $frame_e=shift;
	$pic=~s/\_/ /g;
	if (defined($pic) && ($ENV{'HTTP_REFERER'} eq url())) {
		open (DATA, "$ipacsum -l \'$pic\' ".
			    "-s \"$frame_s\" -e \"$frame_e\" --png-average-curve 20 --png --png-asis --png-to-stdout|")
		    || die "$me: can't run $ipacsum\n";
		binmode DATA;
		binmode STDOUT;
		print <DATA>;
		close DATA;
	} else {
		print header, start_html('Error'),
		    h1({-align=>"center"}, 'This script is not intended for ',
		    'such a use. Sorry for any inconveniecies.'), end_html;
	}
	exit;
}	

sub print_get_form {
        my $q = new CGI;
	my ($c_day,$c_mon,$c_year);
	($c_day,$c_mon,$c_year) = split(/ /,strftime("%d %m %Y", localtime));
	$c_day++;
        my %opt_labels = ('graph'=>' build graphs', 'summary'=>' show summary',
	    'showempty'=>' skip lines with 0s only', 'exact'=>' exact numbers',
	    'sumexa'=>' exact numbers in summary',
	    'skiptimestamps'=>' skip timestamp with no data');
        my %timelabels = ('60'=>'1 min', '300'=>'5 min', '600'=>'10 min',
                    '900'=>'15 min', '1800'=>'30 min', '3600'=>'1 hour',
                    '10800'=>'3 hours', '21600'=>'6 hours', '86400'=>'1 day');

	print $q->startform,
	    "<TABLE CELLPADDING=1 CELLSPACING=1 BORDER=0>",
	    "<tr><td>",
	    $q->checkbox('detailed', '', 'on', 'Show details'),
	    "</td><td>",
	    $q->checkbox_group('opts', ['graph', 'summary'], 
			'summary', 'true', \%opt_labels), 
	    "</td><td>",
	    $q->checkbox_group('opts1', ['exact', 'sumexa'],
			'exact', 'true', \%opt_labels),
	    "</td>",
	    "</tr><tr><td>",
	    $q->checkbox_group('options',
		['showempty','skiptimestamps'],
		['showempty','skiptimestamps'],
		'true', \%opt_labels), 
	    "</td><td colspan=2>&nbsp;Regex: ",
	    $q->textfield('regex'), 
	    "</td></tr>", 
	    "<tr><td>Show from:&nbsp;",
	    $q->popup_menu('from_d', ['1', '2', '3', '4', '5', '6', '7', '8', '9',
			'10', '11', '12', '13', '14', '15', '16', '17', '18', '19',
			'20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
			'30', '31']),
	    $q->popup_menu('from_m', ['1', '2', '3', '4', '5', '6', '7', '8', '9',
				'10', '11', '12'], $c_mon),
	    $q->popup_menu('from_y', ['2001', '2002', '2003'], $c_year),
	    "<td colspan=2>&nbsp;to&nbsp;",
	    $q->popup_menu('to_d', ['1', '2', '3', '4', '5', '6', '7', '8', '9',
			'10', '11', '12', '13', '14', '15', '16', '17', '18', '19',
			'20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
			'30', '31'], $c_day),
	    $q->popup_menu('to_m', ['1', '2', '3', '4', '5', '6', '7', '8', '9',
				'10', '11', '12'], $c_mon),
	    $q->popup_menu('to_y', ['2001', '2002', '2003'], $c_year),
##<td>&nbsp;</td>
	    "</td></tr><tr><td colspan=1 align=right>",
	    $q->popup_menu('from_hour', ['0', '1', '2', '3', '4', '5', '6', '7',
			    '8', '9', '10', '11', '12', '13', '14', '15', '16',
			    '17', '18', '19', '20', '21', '22', '23']),
	    "&nbsp;:&nbsp;",
	    $q->popup_menu('from_mins', ['0', '10', '20', '30', '40', '50']),
	    "</td><td colspan=2>&nbsp;--&nbsp;",
	    $q->popup_menu('to_hour', ['0', '1', '2', '3', '4', '5', '6', '7',
			    '8', '9', '10', '11', '12', '13', '14', '15', '16',
			    '17', '18', '19', '20', '21', '22', '23']),
	    "&nbsp;:&nbsp;",
	    $q->popup_menu('to_mins', ['0', '10', '20', '30', '40', '50']),
	    "</td></tr></table>",
	    $q->submit(-value=>$submit_btn),
	    $q->endform;
	if ($q->param) {
		my $from_m=$q->param('from_m');
		my $to_m=$q->param('to_m');
		$from_m--;
		$to_m--;
		my $starttime=timelocal(0, $q->param('from_mins'), 
					    $q->param('from_hour'), 
					    $q->param('from_d'), 
					    $from_m, 
					    $q->param('from_y'));
		my $endtime=timelocal(0, $q->param('to_mins'), 
					    $q->param('to_hour'), 
					    $q->param('to_d'), 
					    $to_m, 
					    $q->param('to_y'));
		return sprintf("%s %s %s %s %s %s", $starttime, $endtime,
					    join(" ", $q->param('options')),
					    join(" ", $q->param('opts')),
					    join(" ", $q->param('opts1')),
					    $q->param('detailed')
					    );
	}
	return "0";
}
