#!/usr/bin/perl -w
# Accounting controll deamon
# ~AsmodeuS~ (2006.02.03)
# Work only with Dv module



use vars  qw(%RAD %conf @MODULES $db $DATE $GZIP $MYSQLDUMP %ADMIN_REPORT);
#use strict;
use FindBin '$Bin';
require $Bin . '/config.pl';
unshift(@INC, $Bin . '/../', $Bin . "/../Abills/$conf{dbtype}");

require "Abills/defs.conf";
require "Abills/nas.pl";
require Abills::Base;
Abills::Base->import();
my $begin_time = check_time();

my $debug=0;
my $ARGV = parse_arguments(\@ARGV);

if (defined($ARGV->{debug})) {
  print "Debug mode on\n";
  $debug=1;
}
elsif(defined($ARGV->{help})){
  print "Help:
  checkppp - check Active ppp session and disconnect unknown ip
  debug    - debug mode
\n";
  exit;
}

require Abills::SQL;
Abills::SQL->import();
require Users;
Users->import();
require Admins;
Admins->import();

my $sql = Abills::SQL->connect($conf{dbtype}, $conf{dbhost}, $conf{dbname}, $conf{dbuser}, $conf{dbpasswd});
my $db = $sql->{db};
my $admin = Admins->new($db, \%conf);
$admin->info($conf{SYSTEM_ADMIN_ID}, { IP => '127.0.0.1' });

my $default_alive_interval = 120;
my $exppp_hanguplimit = 3500000000;
my $error_alive_count = $conf{ERROR_ALIVE_COUNT} || 5;

if ($admin->{errno}) {
	print  $admin->{errno};
	exit 0;
}

%LIST_PARAMS = ();
require Dv_Sessions;
my $sessions = Dv_Sessions->new($db, $admin, \%conf);
require Nas;
my $nas = Nas->new($db, \%conf);
my %stats = ();


if (defined($ARGV->{checkppp})) {
  check_ppp();
 }
else {
  check_lines();
}

 if ($begin_time > 0 && $debug == 1)  {
   Time::HiRes->import(qw(gettimeofday));
   my $end_time = gettimeofday();
   my $gen_time = $end_time - $begin_time;
   printf(" GT: %2.5f", $gen_time);
  }


#***********************************************************
# check_lines
#***********************************************************
sub check_lines {
	require Billing;
	Billing->import();

	$LIST_PARAMS{ROWS}=2;
	$sessions->online( { %LIST_PARAMS } );	
	
	my $online = $sessions->{nas_sorted};
  $LIST_PARAMS{DISABLE} = 0;
  my $nas_list = $nas->list({ %LIST_PARAMS });
  my %NAS  = ();

  foreach my $nas_row (@$nas_list) {
    
    my $l = $online->{$nas_row->[0]};
    #    print "NAS IP: $nas_row->[3]\n NAS_TYPE: $nas_row->[4]\n STATUS: $nas_row->[6]\n Alive: $nas_row->[7]/\n ";    
    $NAS{NAS_IP}    = $nas_row->[3];
    $NAS{NAS_TYPE}  = $nas_row->[4];
    $NAS{NAS_ALIVE} = $nas_row->[7] || 0;
    $NAS{NAS_MNG_IP_PORT}  = $nas_row->[8];
    $NAS{NAS_MNG_USER}     = $nas_row->[9]; 
    $NAS{NAS_MNG_PASSWORD} = $nas_row->[10]; 

    foreach my $line (@$l) {
	    my $acct_input_octets       = $line->[5];
	    my $acct_output_octets      = $line->[6];
	    my $acct_input_octets_ext   = $line->[7];
	    my $acct_output_octets_ext  = $line->[8];
	    my $nas_port_id             = $line->[2];
	    my $acct_session_id         = $line->[11];
	    
      if ($acct_input_octets_ext > $exppp_hanguplimit || $acct_output_octets_ext > $exppp_hanguplimit) {
        my $ret = hangup(\%NAS, "$nas_port_id", "$line->[0]", { ACCT_SESSION_ID   => "$acct_session_id",
        	                                                      FRAMED_IP_ADDRESS => "$line->[3]"
        	                                                      } );
        print "Hangup '$line->[0]' $acct_input_octets, $acct_output_octets, $acct_input_octets_ext, $acct_output_octets_ext, Session-ID: $acct_session_id\n"; 
       }     


      #Get stats from nas servers which don't support rad Alive
      my $external_stats = get_stats(\%NAS, $line->[2]);

      if ( $NAS{NAS_ALIVE} > 0 
           && ( $line->[20] > $NAS{NAS_ALIVE} * $error_alive_count )) {
         print "Error: Lost Alive / Login: $line->[0], Last Alive: $line->[20], Session-ID: $line->[11]\n"; # if ( $debug == 1 );
         $sessions->zap($nas_row->[0], $line->[2], $line->[11]);
       } 
      elsif (defined($external_stats->{error})) {
      	print "Error:  Login: $line->[0], NAS-IP: $NAS{NAS_IP}, Port: $line->[2], Session-ID: $line->[11]\n" if ($debug == 1);
      	$sessions->online_update({ USER_NAME       => $line->[0], 
      		                         ACCT_SESSION_ID => $line->[11], 
      		                         STATUS          => 5 
      		                        });
       }
      elsif (defined($external_stats->{in})) {
        $sessions->online_update({ USER_NAME => $line->[0], ACCT_SESSION_ID => $line->[11], %$external_stats });
       }
      
      # periodic check deposit stats
      if (defined($conf{periodic_check}) && $conf{periodic_check} eq 'yes') {

        my %RAD = (USER_NAME        => "$line->[0]",
                  SESSION_START     => time - time2sec($line->[4]),
                  ACCT_SESSION_TIME => time2sec($line->[4]),
                  INBYTE         => $line->[5],
                  OUTBYTE        => $line->[6],
                  INBYTE2        => $line->[7],
                  OUTBYTE2       => $line->[8]);

        my $Billing = Billing->new($db, \%conf);	
        my $self;

        ($self->{UID}, 
         $self->{SUM}, 
         $self->{BILL_ID}, 
         $self->{TARIF_PLAN}, 
         $self->{TIME_TARIF}, 
         $self->{TRAF_TARIF}) = $Billing->session_sum("$RAD{USER_NAME}", 
                                                 $RAD{SESSION_START}, 
                                                 $RAD{ACCT_SESSION_TIME}, 
                                                 \%RAD);

        next if ($self->{UID} < 1);
        
        print "> Login: $RAD{USER_NAME} START: $RAD{SESSION_START} DURATION: $RAD{ACCT_SESSION_TIME}\n".
         "UID: $self->{UID} SUM: $self->{SUM} BILL_ID: $self->{BILL_ID} TARIF_PLAN: $self->{TARIF_PLAN} TRAF_TARIF: $self->{TRAF_TARIF}\n" if ($debug == 1);

        if ( ($Billing->{PAYMENT_TYPE}==0) && ($line->[14] + $line->[15] < $self->{SUM}) ) {
           my $ret = hangup(\%NAS, "$nas_port_id", "$line->[0]", { ACCT_SESSION_IS   => $acct_session_id,
           	                                                       FRAMED_IP_ADDRESS => "$line->[3]" });
           print "Hanguped Login: $RAD{USER_NAME} DEPOSIT: $line->[14] CREDIT: $line->[15] SESSION_SUM: $self->{SUM}\n"; # if ($debug == 1);
          }
       }


     }
   }


}



#**********************************************************
# check Active ppp session and disconnect unknown ip
#**********************************************************
sub check_ppp {
  my $ip_pid_hash = get_if_info();
  get_db_info($ip_pid_hash);


while(my($ip_address, $pid)=each %$ip_pid_hash) {
  print "Killed IP: $ip_address PID: $pid\n";
  my $res = `/bin/kill -1 $pid`;
  $pid=~/(\d+)/;
  get_pid_info($1);
}

}

#**********************************************************
#Get ipaddreses of active sessions
#**********************************************************
sub get_db_info {
 my ($ip_pid_hash) = @_;
 my $list = $sessions->online( { %LIST_PARAMS } );	
 foreach my $line (@$list) {
   delete $ip_pid_hash->{"$line->[9]"};
  }
}


#**********************************************************
#Get pid info
#**********************************************************
sub get_pid_info {
 my ($pid, $attr) = @_;
 my $log_file='/var/log/ppp.log';

 print "\nShow log file info:\n";
 open(FILE, "<$log_file") || die "Can't open log file '$log_file' $! " ;
   while(<FILE>) {
     if (/\[$pid\]/) {
       print $_;
      }
    }
 close(FILE);
}

#**********************************************************
# Get tun interface info
#**********************************************************
sub get_if_info {
 my $res = '';
 my $count = 0;
 my $ifconfig = '';

 my %ip_pid_hash = ();

 open (IFC, "$IFCONFIG -u |") || die "Can't open file '$IFCONFIG' $!";
  while (my $l = <IFC>) {
     $ifconfig .= $l;
    }
 close(IFC);

 while($ifconfig =~ /tun(.+\n.+\n.+PID.+)/gi) {
    my @ifs = split(/\n/, $1);
    my $tunnum = 0;

    foreach my $line (@ifs) {
      if ($line =~ /:/) {
         ($tunnum, undef)=split(/:/, $line);
        }
      elsif ($line =~ m/\d+.\d+.\d+.\d+ --> \d+.\d+.\d+.\d+/g) {
         (undef, undef, undef, $addr, undef) = split(/ +/, $line);
        }
      elsif ($line =~ /PID/gi) {
         (undef, $pid) = split(/PID/, $line);
         chomp($pid);
       }
     }
    print "$tunnum $addr $pid\n" if ($debug == 1);
    $ip_pid_hash{$addr}=$pid;
  }

  return \%ip_pid_hash;
}


__END__

