#!/usr/bin/perl

require 5.004;
use locale;
use POSIX qw (locale_h);
use CGI qw (:standard);
use DBI;
use POSIX qw(strftime);
unshift(@INC, '.');
use ipac_cfg;
#use strict;
#use vars qw(%cfg);

#&main;

#sub main {
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";

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

my $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 = 0;			# ! makeunixtime needs this!
$tzoffset = int( ($now-makeunixtime(strftime "%Y%m%d%H%M%S", localtime))/60)*60;
# get time zone name
my $tzname = strftime "%Z", localtime; chop $tzname;
my $starttime = 0 + tzoffset;
my $endtime = $now;

my $timestamps = "";
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 $interval = $q->param('inter');
my $exact = 0;
my $exacts = 0;
my $regex ="";
###@C_QU = (1, 1024, 1048576, 1073741824, 1099511627776 );
@C_QU = (1, 1000, 1_000_000, 1_000_000_000, 1_000_000_000_000 );
@C_QUC= ('','K',  'M',     'G',        'T');

setlocale(LC_CTYPE, "");

$user = $ENV{REMOTE_USER};

if (($ssl_is_a_must eq "1" && $ENV{HTTPS} ne "on") || $user eq "") {
    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;
}

if ($q->param('showpic')) {
	&print_pic($q->param('showpic'), $q->param('frame'));
}

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) = split(/ /, set_time_frame($options), 2);
if ($options =~ /graph/i) {
	$show_graph = "1";
	$st_align = "left";
}
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;
}
$regex = $q->param('regex');
## print summary
{
	my $fr = $q->param('list');
	if ($exacts == 1) {
		open (DATA, "$ipacsum -x -t\"$fr\"|")
				|| die "$me: can't run $ipacsum\n";
	} else {
		open (DATA, "$ipacsum -t\"$fr\"|")
				|| 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 "<tr><td align=center>Summary for $fr</td><td>&nbsp;</td></tr>";
	}
	my $t1;
	my $t2;
	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 != 0) {
				    $bytez{$t1} = $t2;
				    $last_printed{$t1} = $starttime;
				    if ($show_summary) {
					    print "<tr><td>$t1</td><td align=right>$t2</td></tr>";
				    }
			    }
		}
	}
	close DATA;
	print "</table><P>";
}

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>)
{
	/^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;
my @rules_sorted = sort {$rulenames{$a}<=>$rulenames{$b}} keys %rulenames;

## print last record if it wasnt
$header_printed = 0;

foreach(@rules_sorted) {
	if ($last_printed{$_} < $timestamp) {
		my $ttt=localtime($timestamp);
		$last_printed{$_} = $timestamp;
		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>";
			$acc_bytes{$rule} = 0;
			$acc_pkts{$rule} = 0;
		}
		if (($skip_empty_lines && ($acc_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>";
			}
			$pre_print = customized_number($acc_bytes{$_});
			print "<TR><TD> $_ </TD><TD align=right>
				 $acc_pkts{$_} </TD><TD align=right> $pre_print </TD></TR>";
			$acc_bytes{$_} = 0;
			$acc_pkts{$_} = 0;
		}
	}
}
		
print "</TABLE>\n\n";

if ($show_graph eq "1") {
	print "<TABLE align=right>";
	my $iam = $q->url();
	my $fr = $q->param('list');
	foreach(@rules_sorted) {
		my $tmp=$_;
		s/ /_/g;
		if (defined $bytez{$tmp}) {
    			print "<TR><TD><IMG src=$iam?showpic=$_&frame=$fr>
					</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 $timest=shift;
	my $ttt=localtime($timest);
	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 = $1;
		$pkts = $2;
		$rule = $3;
		$acc_bytes{$rule} += $bytes;
		$acc_pkts{$rule} += $pkts;
		if (($filter_to_ssl_user eq "0" || 
			    ($user eq $admin_login) || ($rule =~ /$user/)) &&
				    ($rule =~ /$regex/)) {
			if (!defined($rulenames{$rule})) {
				$rulenames{$rule}=$rulenumber++;
			}

			if (($timest - $last_printed{$rule}) >= $interval) {
					$last_printed{$rule} = $timest;
				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>";
					$acc_bytes{$rule} = 0;
					$acc_pkts{$rule} = 0;
				}
				if (($skip_empty_lines && ($acc_bytes{$rule} 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>";
					}
					$pre_print = customized_number($acc_bytes{$rule});
					print "<TR><TD> $rule </TD><TD align=right>
						 $acc_pkts{$rule} </TD><TD align=right> $pre_print </TD></TR>";
##					$last_printed{$rule} = $timest;
					$acc_bytes{$rule} = 0;
					$acc_pkts{$rule} = 0;
				}
	    		}
		}
	}
	# read another emtpy line (data format consistency)
	$_ = <$file>;
	die "$me: bad data from fetchipac (expected emtpy line): $_\n"
		if ($_ !~ /^$/);
}

sub set_time_frame {
	my($opt_t) = shift;
	my $starttime="";
	my $endtime="";
	my $i="";
	my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
	my @mofg = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

	if ($opt_t =~ /^this\s*hour/i) {
		$opt_t = "the hour 0 hours ago";
	} elsif ($opt_t =~ /^last\s*hour/i) {
		$opt_t = "the hour 1 hour ago";
	} elsif ($opt_t =~ /^today/i) {
		$opt_t = "the day 0 days ago";
	} elsif ($opt_t =~ /^yesterday/i) {
		$opt_t = "the day 1 day ago";
	} elsif ($opt_t =~ /^the\s*day\s*before\s*yesterday/i) {
		$opt_t = "the day 2 days ago";
	} elsif ($opt_t =~ /^this\s*week/i) {
		$opt_t = "the week 0 weeks ago";
	} elsif ($opt_t =~ /^last\s*week/i) {
		$opt_t = "the week 1 week ago";
	} elsif ($opt_t =~ /^the\s*week\s*before\s*last\s*week/i) {
		$opt_t = "the week 2 weeks ago";
	} elsif ($opt_t =~ /^this\s*month/i) {
		$opt_t = "the month 0 months ago";
	} elsif ($opt_t =~ /^last\s*month/i) {
		$opt_t = "the month 1 month ago";
	} elsif ($opt_t =~ /^this\s*year/i) {
		$opt_t = "the year 0 years ago";
	} elsif ($opt_t =~ /^last\s*year/i) {
		$opt_t = "the year 1 year ago";
	} if ($opt_t =~ /^the\s*hour\s*(\d+)\s*hours?\s*ago/i) {
		$i=$1;
		my($thishour)=makeunixtime(sprintf("%04d%02d%02d%02d0000", 
			1900+$year, $mon+1, $mday, $hour));
		$starttime=$thishour - 60*60 * $i;
		$endtime = $thishour - 60*60 * ($i-1);
	} elsif ($opt_t =~ /^the\s*day\s*(\d+)\s*days?\s*ago/i) {
		$i=$1;
		my($thismorning)=makeunixtime(sprintf("%04d%02d%02d000000", 
			1900+$year, $mon+1, $mday));
		$starttime=$thismorning - 60*60*24 * $i;
		$endtime=$thismorning - 60*60*24 * ($i-1);
	} elsif ($opt_t =~ /^the\s*week\s*(\d+)\s*weeks?\s*ago/i) {
		$i=$1;
		$mday = $mday-($wday >0 ? $wday-1 : 6);
		if ($mday < 1) {
			$mon--;
			if ($mon < 0) {
				$mon += 12;
				$year--;
			}
			$mday += $mofg[$mon];
		}
		my($monday)=makeunixtime(sprintf("%04d%02d%02d000000", 
			1900+$year, $mon+1, $mday));
		$starttime=$monday - 60*60*24*7 * $i;
		$endtime=$monday- 60*60*24*7 * ($i-1);
	} elsif ($opt_t =~ /^the\s*month\s*(\d+)\s*months?\s*ago/i) {
		$mon = $mon - $1;
		while ($mon < 0) {
			$year--;
			$mon += 12;
		}

		$starttime=makeunixtime(sprintf("%04d%02d01000000", 
			1900+$year, $mon+1));
		$endtime=$starttime + 60*60*24*$mofg[$mon];
		$endtime += 60*60*24 if ((1900+$year)%4 ==0 && $mon==1);
	} elsif ($opt_t =~ /^the\s*year\s*(\d+)\s*years?\s*ago/i) {
		$i=$1;

		$starttime=makeunixtime(sprintf("%04d0101000000", 
			1900+$year-$i));
		$endtime=makeunixtime(sprintf("%04d0101000000", 
			1900+$year-$i+1));
        } elsif (($opt_t =~ /^ever/i) || ($opt_t eq "")) {
		$starttime=0;
		$endtime=time();
	} else {
		die "$me: Unknown time frame: \"$opt_t\"\n";
	}
	return sprintf "%s %s", $starttime, $endtime;
}

sub makeunixtime {
	my($y, $m, $d, $h, $i, $e);
	my $s = shift;
	my @moff = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 );

	$h=0; $i=0; $e=0;
	if ($s =~ /^(\d\d\d\d)(\d\d)(\d\d)/) {
		($y, $m, $d) = ($1, $2, $3);
		if ($s =~ /^\d\d\d\d\d\d\d\d-?(\d\d)/) {
			$h=$1;
			if ($s =~ /^\d\d\d\d\d\d\d\d-?\d\d(\d\d)/) {
				$i=$1;
				if($s =~ /^\d\d\d\d\d\d\d\d-?\d\d\d\d(\d\d)/){
					$e=$1;
				}
			}
		}
	} else {
		return 0;
	}
	$y-=1970;
	$s = (($y)*365) + int(($y+2)/4);
	$s+=$moff[$m-1];
	$s+=$d-1;
	$s-- if (($y+2)%4 == 0 && $m < 3);
	$s*86400 + $h*3600 + $i*60 + $e + $tzoffset;
}

sub print_pic {
	my $pic=shift;
	my $frame=shift;
	$pic=~s/\_/ /g;
	if (defined($pic) && ($ENV{'HTTP_REFERER'} eq url())) {
		open (DATA, "$ipacsum -f\"$pic\" ".
			    "-t \"$frame\" --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 %labels = ('today'=>'today', 'yesterday'=>'yesterday', 
	    'thishour'=>'this hour', 'lasthour'=>'last hour', 
	    'thisweek'=>'this week', 'lastweek'=>'last week', 
	    'thismonth'=>'this month', 'lastmonth'=>'last month', 
	    'thisyear'=>'this year', 'lastyear'=>'last year');
	my %timelabels = ('50'=>'1 min', '290'=>'5 min', '585'=>'10 min',
			    '880'=>'15 min', '1780'=>'30 min', '3580'=>'1 hour',
			    '10780'=>'3 hours', '21580'=>'6 hours', 
			    '86380'=>'1 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');
	print $q->startform,
	    $form_head,
	    "<P><TABLE CELLPADDING=2 CELLSPACING=1 BORDER=0>",
	    "<TR><TD>$sel</TD><TD>",
	    $q->popup_menu('list', 
		['today','yesterday','thishour','lasthour',
		'thisweek','lastweek','thismonth','lastmonth',
		'thisyear','lastyear'],
		'today',\%labels), "</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>Interval:</td><td>",
	    $q->popup_menu('inter', ['50', '290', '585', '880', '1780',
			    '3580', '10780', '21580', '86380'],
			    '880', \%timelabels), "</td><td>",
	    $q->checkbox_group('options',
		['showempty','skiptimestamps'],
		['showempty','skiptimestamps'],
		'true', \%opt_labels), "</td><td>",
	    " &nbsp;Regex: ",
	    $q->textfield('regex'), "</td></tr></table><p>", 
	    $q->submit(-value=>$submit_btn),
	    $q->endform;
	if ($q->param) {
		return sprintf("%s %s %s %s", $q->param('list'), 
					    join(" ", $q->param('options')),
					    join(" ", $q->param('opts')),
					    join(" ", $q->param('opts1')));
	}
}

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);
        }
        $n;
}
