package ModPerl::ASTPP;

#
# ASTPP - Open Source Voip Billing
#
# Copyright (C) 2008, Aleph Communications
#
# Darren Wiebe (darren@aleph-com.net)
#
# This program is Free Software and is distributed under the
# terms of the GNU General Public License version 2.
############################################################
#
# Usage-example:
#

use Apache2::Const qw(:common);
use Apache2::Request;
use Apache::DBI;
use DBI;
use CGI;
use CGI qw/:standard Vars/;
use ASTPP;
use XML::Simple;
use Data::Dumper;
use URI::Escape;
use Time::HiRes qw( gettimeofday tv_interval );

use POSIX;
use POSIX qw(strftime);

use strict;
$Apache::DBI::DEBUG = 2;

use vars
  qw( $cdr_db $ASTPP @output $config $freeswitch_db $astpp_db $verbosity );
use Locale::gettext_pp qw(:locale_h);
my $starttime = [gettimeofday];
require "/usr/local/astpp/astpp-common.pl";
$ENV{LANGUAGE} = "en";    # de, es, br - whatever
print STDERR "Interface language is set to: " . $ENV{LANGUAGE} . "\n";
bindtextdomain( "astpp", "/usr/local/share/locale" );
textdomain("astpp");
$verbosity = 2;
@output    = ("STDERR");
$ASTPP     = ASTPP->new;
$ASTPP->set_verbosity(4);    #Tell ASTPP debugging how verbose we want to be.

sub initialize() {
    $config = &load_config();
    $astpp_db = &connect_db( $config, @output );
    $ASTPP->set_astpp_db($astpp_db);
    $config = &load_config_db( $astpp_db, $config ) if $astpp_db;
    $freeswitch_db = &connect_freeswitch_db( $config, @output );
    $ASTPP->set_freeswitch_db($freeswitch_db);
    $cdr_db = &cdr_connect_db( $config, @output );
    $config->{cdr_table} = $config->{freeswitch_cdr_table};
}

################# Programs start here #######################################
&initialize;

sub handler { 
	my $r = shift;
my ( $ipinfo, $params,$xml, $maxlength, $maxmins, $callstatus,$astppdid,$didinfo );
my $starttime = [gettimeofday];
my	$q = new CGI;


foreach my $param ( $q->param()  ) {
    $params->{$param} = $q->param($param);
    $ASTPP->debug( debug => "$param $params->{$param}" );
}

$xml = header( -type => 'text/plain' );

$ASTPP->debug(
    debug => "Destination = $params->{'Caller-Destination-Number'}" );

if ( $params->{section} eq "dialplan" ) {
    # Check to see if this is a DID.  If it is we handle it differently.
    #
    $didinfo = &get_did($astpp_db, $params->{'Caller-Destination-Number'});
    if ($didinfo->{number}) {
	$astppdid = "ASTPP-DID";
        $ASTPP->debug( debug => "This is a call for a DID: ");
	$params->{variable_accountcode} = $didinfo->{account};
    }


    if ( !$params->{variable_accountcode} ) {

      # First we strip off X digits to see if this account is prepending numbers
      # as authentications
        $ASTPP->debug( debug => "Checking CC Number: "
              . $params->{'Caller-Destination-Number'} );
        my $cc = substr( $params->{'Caller-Destination-Number'},
            0, $config->{cardlength} );
        my $sql =
          $astpp_db->prepare("SELECT number FROM accounts WHERE cc = $cc");
        $sql->execute;
        my $record = $sql->fetchrow_hashref;
        $sql->finish;
        $params->{variable_accountcode} = $record->{cardnum}
          if ( $record->{cardnum} );
    }
    if ( !$params->{variable_accountcode} ) {
        $ASTPP->debug(
            debug => "Checking IP Address:" . $params->{'Hunt-Network-Addr'} );
        $ipinfo = $ASTPP->ip_address_authenticate(
            ip_address  => $params->{'Hunt-Network-Addr'},
            destination => $params->{'Caller-Destination-Number'}
        );
        if ($ipinfo->{account} ne "") {
            $params->{variable_accountcode} = $ipinfo->{account};
            $params->{'Caller-Destination-Number'} =~ s/$ipinfo->{prefix}//g;
        }
    }
print STDERR "IP ACCOUNT: $ipinfo->{account} \n";

    $xml = $ASTPP->fs_dialplan_xml_header(
        xml                => $xml,
        destination_number => $params->{'Caller-Destination-Number'},
	DID		   => $didinfo->{number},
	IP		   => 123	#$ipinfo->{account}
    );

    $ASTPP->debug( debug =>
"$params->{variable_accountcode}, $params->{'Caller-Destination-Number'}"
    );

    my $carddata =
      &get_account( $astpp_db, $params->{variable_accountcode} )
      ;    # Fetch all the account info from the db.

    if ( !$carddata->{number} )
    {      # Check to see if the account exists.  If not then exit.
        $ASTPP->debug( debug => "CALLSTATUS 2" );
        $ASTPP->debug( debug => "CANNOT RETRIEVE CARD" );
        $xml .=
          "<action application=\"reject\" data=\"CANNOT RETRIEVE ACCOUNT\"/>\n";
        $xml = $ASTPP->fs_dialplan_xml_footer( xml => $xml );
#        print $xml;
        $ASTPP->debug( debug => "Returning nothing so dialplan can continue." );
	$r->print("");

my $generation_time = tv_interval($starttime);
print STDERR "TOOK: " . $generation_time ;

        exit(1);
#        return OK;
    }

    if ( $carddata->{dialed_modify} ) {
        my @regexs = split( m/,/m, $carddata->{dialed_modify} );
        foreach my $regex (@regexs) {
            $regex =~ s/"//g;    #Strip off quotation marks
            my ( $grab, $replace ) = split( m!/!i, $regex )
              ; # This will split the variable into a "grab" and "replace" as needed
            $ASTPP->debug( debug => "Grab: $grab" );
            $ASTPP->debug( debug => "Replacement: $replace" );
            $ASTPP->debug( debug =>
                  "Phone Before: $params->{'Caller-Destination-Number'}" );
            $$params->{'Caller-Destination-Number'} =~ s/$grab/$replace/is;
            $ASTPP->debug( debug =>
                  "Phone After: $params->{'Caller-Destination-Number'}" );
        }
    }

    $ASTPP->debug( debug => "FINDING LIMIT FOR: " . $carddata->{number} );
    ( $callstatus, $maxlength ) =
      &max_length( $astpp_db, $config, $carddata,
        $params->{'Caller-Destination-Number'} );

    my $routeinfo = &get_route(
        $astpp_db, $config,
        $params->{'Caller-Destination-Number'},
        $carddata->{pricelist}, $carddata,$astppdid
    );

    $ASTPP->debug( debug => "Cost: " . $routeinfo->{cost} );
    $ASTPP->debug( debug => "Pricelist: " . $routeinfo->{pricelist} );
    my $minimumcharge = $routeinfo->{cost};
    my @reseller_list;
    $ASTPP->debug( debug => "CALLSTATUS: $callstatus MAX_LENGTH: $maxlength" );

    if (!$routeinfo->{cost} && !$routeinfo->{pricelist}) {
        $ASTPP->debug( debug => "COULD NOT FIND ROUTE.  EXITING SO DIALPLAN CAN TAKE OVER" );
my $generation_time = tv_interval($starttime);
print STDERR "TOOK: " . $generation_time ;
	$r->print($xml);
        exit(1);
#	return OK;
    }
    while ( $carddata->{reseller} && $maxlength > 1 && $callstatus == 1 ) {
        $ASTPP->debug( debug => "FINDING LIMIT FOR: $carddata->{reseller}" );
        $carddata = &get_account( $astpp_db, $carddata->{reseller} );
        push @reseller_list, $carddata->{number};
        $ASTPP->debug( debug =>
"ADDING $carddata->{number} to the list of resellers for this account"
        );
        my ( $resellercallstatus, $resellermaxlength ) =
          &max_length( $astpp_db, $config, $carddata,
            $params->{'Caller-Destination-Number'} );
        my $routeinfo = &get_route(
            $astpp_db, $config,
            $params->{'Caller-Destination-Number'},
            $carddata->{pricelist}, $carddata
        );
        if ( $resellercallstatus != 1 ) {
            $carddata->{reseller} = "";
            $callstatus = $resellercallstatus;
        }
        elsif ( $resellermaxlength < $maxlength ) {
            $maxlength = $resellermaxlength;
        }
        $ASTPP->debug( debug =>
"Reseller cost = $routeinfo->{cost} and minimum charge is $minimumcharge"
        );
        if ( $resellermaxlength < 1 || $routeinfo->{cost} > $minimumcharge ) {
            $ASTPP->debug( debug =>
                  "Reseller call is priced too cheap!  Call being barred!" );
            $xml .=
"<action application=\"reject\" data=\"Reseller call is priced too cheap!  Call being barred!\"/>\n";
            $xml = $ASTPP->fs_dialplan_xml_footer( xml => $xml );
            $r->print( $xml);
my $generation_time = tv_interval($starttime);
print STDERR "TOOK: " . $generation_time ;
        exit(1);
        }
        $ASTPP->debug( debug => "RESELLER Max Length: $resellermaxlength" );
        $ASTPP->debug( debug => "RESELLER Call Status: $resellercallstatus" );
    }

    if ( $config->{debug} == 1 ) {
        $ASTPP->debug( debug => "PRINTING LIST OF RESELLERS FOR THIS ACCOUNT" );
        foreach my $reseller (@reseller_list) {
            $ASTPP->debugb( debug => "RESELLER: $reseller" );
        }
    }

    $ASTPP->debug("Max Call Length: $maxlength minutes");
    $ASTPP->debug("Call Status: $callstatus");

    if ( $maxlength <= 1 ) {
        $ASTPP->debug( debug => "NOT ENOUGH CREDIT" );
        $xml .= "<action application=\"reject\" data=\"NOT ENOUGH CREDIT\"/>\n";
        $xml = $ASTPP->fs_dialplan_xml_footer( xml => $xml );
        $r->print( $xml);
my $generation_time = tv_interval($starttime);
print STDERR "TOOK: " . $generation_time ;
        exit(1);
    } elsif ($config->{call_max_length} && $maxlength < $config->{call_max_length} / 1000){
	$maxlength = $config->{call_max_length} / 1000;
    }

    $xml = $ASTPP->fs_dialplan_xml_timelimit(
        xml        => $xml,
        max_length => $maxlength,
	accountcode => $carddata->{number}
    );

# Set the timelimit as well as other variables which are needed in the dialplan.
    my $timelimit =
      "L(" . sprintf( "%.0f", $maxlength * 60 * 1000 ) . ":60000:30000)";

    $ASTPP->debug( debug => "Looking for outbound Route" );
    $routeinfo = &get_route(
        $astpp_db, $config,
        $params->{'Caller-Destination-Number'},
        $carddata->{pricelist}, $carddata, $astppdid
    );

    if ($didinfo->{number} ) {
	$ASTPP->debug( debug => "THIS IS A DID CALL: $xml");
        my ($returned_data) = $ASTPP->fs_dialplan_xml_did(
		did		   => $params->{'Caller-Destination-Number'}
       );
	$xml .= $returned_data;
    } else {
    # Get the list of routes for the phone number.
    my @outboundroutes =
      &get_outbound_routes( $astpp_db, $params->{'Caller-Destination-Number'},
        $carddata, $routeinfo, @reseller_list );
    foreach my $route (@outboundroutes) {
        $ASTPP->debug( debug =>
              "$route->{trunk}: cost $route->{cost}\t $route->{pattern}" );
        if ( $route->{cost} > $routeinfo->{cost} ) {
            $ASTPP->debug( debug =>
"$route->{trunk}: $route->{cost} > $routeinfo->{cost}, skipping"
            );
        }
        else {
            my ($returned_data,$junk) = $ASTPP->fs_dialplan_xml_bridge(
                destination_number => $params->{'Caller-Destination-Number'},
                route_prepend      => $route->{prepend},
                trunk_name         => $route->{trunk},
                route_id	   => $route->{id}
            );
	    $xml .= $returned_data;
        }
    }
    }
    $xml = $ASTPP->fs_dialplan_xml_footer( xml => $xml );
    $ASTPP->debug( debug => $xml );
    print $xml;
    exit(1);
}
if ( $params->{section} eq "directory" ) {

    #hostname darren-laptop
    #section directory
    #tag_name domain
    #key_name name
    #key_value 192.168.2.119
    #action sip_auth
    #sip_profile internal
    #sip_user_agent Zoiper rev.1118
    #sip_auth_username 1000
    #sip_auth_realm 192.168.2.119
    #sip_auth_nonce 83005e62-7e13-11dd-9eb1-25560b0691a8
    #sip_auth_uri sip:192.168.2.119;transport=UDP
    #sip_auth_qop auth
    #sip_auth_cnonce a79169d2656f292a
    #sip_auth_nc 00000001
    #sip_auth_response 4475154556879ec2017978f1347192a6
    #sip_auth_method REGISTER
    #key id
    #user 1000
    #domain 192.168.2.119
    #ip 192.168.2.119

    if ($params->{'user'}) {
       $xml = $ASTPP->fs_directory_xml_header( xml => $xml );
       $xml = $ASTPP->fs_directory_xml(
           xml    => $xml,
           ip     => $params->{'ip'},
           user   => $params->{'user'},
           domain => $params->{'domain'}
       );
       $xml = $ASTPP->fs_directory_xml_footer( xml => $xml );
    }
    $ASTPP->debug( debug => $xml );
    $r->print ($xml);
    exit(1);
}
if ( $params->{cdr} ) {

$r->print(header( -type => 'text/plain' ));
    
# create object
my $xml = new XML::Simple;

# read XML file
my $data = $xml->XMLin($params->{cdr});

# print output
#print STDERR Dumper($data);


my $tmp = "INSERT INTO " . $config->{freeswitch_cdr_table} . "(accountcode,src,dst,dcontext,clid,channel,dstchannel,lastapp,"
	. "lastdata,calldate,answerdate,enddate,duration,billsec,disposition,amaflags,uniqueid,userfield,read_codec,"
	. "write_codec,cost,vendor,provider,trunk,outbound_route,progressmsec,answermsec,progress_mediamsec) VALUES ("
	. "'"
	. $data->{variables}->{accountcode}
	. "'"
	. ","
	. "'"
	. $data->{callflow}->{caller_profile}->{username}
	. "'"
	. ","
#	. $cdr_db->quote($data->{callflow}->{caller_profile}->{originatee}->{originatee_caller_profile}->{destination_number})
	. "'"
	. $data->{callflow}->{caller_profile}->{destination_number}
	. "'"
	. ","
#	. $cdr_db->quote($data->{callflow}->{caller_profile}->{originatee}->{originatee_caller_profile}->{context})
	. "'"
	. $data->{callflow}->{caller_profile}->{context}
	. "'"
	. ","
	. "'"
	. uri_unescape($data->{variables}->{caller_id})
	. "'"
#	. "\"" . $cdr_db->quote($data->{callflow}->{caller_profile}->{originatee}->{originatee_caller_profile}->{caller_id_name}) . "\""
#	. "<" . $cdr_db->quote($data->{callflow}->{caller_profile}->{originatee}->{originatee_caller_profile}->{caller_id_number}) . ">"
#	. "\"" . $data->{callflow}->{caller_profile}->{caller_id_name} . "\""
#	. "<" . $data->{callflow}->{caller_profile}->{caller_id_number} . ">"
	. ","
	. "'"
	. uri_unescape($data->{variables}->{channel_name})
	. "'"
	. ","
	. "''"
	. ","
	. "'" . $data->{variables}->{last_app} . "'"
	. ","
	. "'" . uri_unescape($data->{variables}->{last_arg}) . "'"
	. ","
	. "'"
	. uri_unescape($data->{variables}->{start_stamp})
	. "'"
	. ","
	. "'" . uri_unescape($data->{variables}->{answer_stamp}) . "'"
	. ","
	. "'"
	. uri_unescape($data->{variables}->{end_stamp})
	. "'"
	. ","
	. "'"
	. $data->{variables}->{duration}
	. "'"
	. ","
	. "'"
	. $data->{variables}->{billsec}
	. "'"
	. ","
	. "'"
	. $data->{variables}->{hangup_cause}
	. "'"
	. ","
	. "''"
	. ","
	. "'"
	. $data->{callflow}->{caller_profile}->{uuid}
	. "'"
	. ","
	. "''"
	. ","
	. "'" . $data->{variables}->{read_code} . "'"
	. ","
	. "'" . $data->{variables}->{write_code} . "'"
	. ",'none','none'"
	. ","
	. "'" . $data->{variables}->{provider} . "'"
	. ","
	. "'" . $data->{variables}->{trunk} . "'"
	. ","
	. "'" . $data->{variables}->{outbound_route} . "'"
	. ","
	. "'" . $data->{variables}->{progressmsec} . "'"
	. ","
	. "'" . $data->{variables}->{answermsec} . "'"
	. ","
	. "'" . $data->{variables}->{progress_mediamsec} . "'"
	. ")";

print STDERR "\n" . $tmp . "\n";
$cdr_db->do($tmp);
$r->print( "Wrote CDR");
my (@chargelist);
push @chargelist, $data->{callflow}->{caller_profile}->{uuid};
&processlist( $astpp_db, $cdr_db, $config, \@chargelist );
print STDERR "VENDOR CHARGES: " . $config->{trackvendorcharges} . "\n";
if ($config->{trackvendorcharges} == 1 && $data->{variables}->{accountcode}) {
	&vendor_process_rating_fs( $astpp_db, $cdr_db, $config, "none",  $data->{callflow}->{caller_profile}->{uuid},"");
}

&process_callingcard_cdr if $data->{variables}->{callingcard};    
	
sub process_callingcard_cdr() {
	my ( $cardinfo, $brandinfo, $numberinfo, $pricelistinfo,$cc );
	my $destination = $data->{callflow}->{caller_profile}->{destination_number};
	$destination =~ s/@.*//g;
        my $cardnumber = $data->{variables}->{callingcard};
        $cardinfo = &get_callingcard( $astpp_db, $cardnumber, $config );
        if ( !$cardinfo ) {
                $cardinfo = &get_account_cc( $astpp_db, $cardnumber );
                $cc = 1 if $cardinfo;
        }
	$brandinfo = &get_cc_brand( $astpp_db, $cardinfo->{brand} ) if $cc == 0;
	if ($brandinfo->{reseller}) {
	        $config     = &load_config_db_reseller($astpp_db,$config,$brandinfo->{reseller});
	}
	$config     = &load_config_db_brand($astpp_db,$config,$cardinfo->{brand});
	$pricelistinfo = &get_pricelist( $astpp_db, $brandinfo->{pricelist} )
	  if $cc == 0;
	$pricelistinfo = &get_pricelist( $astpp_db, $cardinfo->{pricelist} )
	  if $cc == 1;

                    print STDERR "THIS IS A CALLINGCARD CALL! \n";
                    print STDERR "CARD: $cardinfo->{cardnumber} \n";
                    print STDERR "CARD: $cardnumber \n";
                    $numberinfo = &get_route(
                        $astpp_db, $config,
                        $destination,
                        $brandinfo->{pricelist}, $cardinfo
                    );
                    if ( $data->{variables}->{billsec} > 0 )
                    {
                        $ASTPP->debug(
                            debug     => "CALL ANSWERED",
                            verbosity => $verbosity
                        );
                        my $increment;
                        if ( $numberinfo->{inc} > 0 ) {
                            $increment = $numberinfo->{inc};
                        }
                        else {
                            $increment = $pricelistinfo->{inc};
                        }
                        $ASTPP->debug(
                            debug =>
"$numberinfo->{connectcost}, $numberinfo->{cost}, $data->{variables}->{billsec}, $increment, $numberinfo->{includedseconds}",
                            verbosity => $verbosity
                        );
                        my $charge = &calc_call_cost(
                            $numberinfo->{connectcost},
                            $numberinfo->{cost},
                            $data->{variables}->{billsec},
                            $increment,
                            $numberinfo->{includedseconds}
                        );
                        $ASTPP->debug(
                            debug     => "Cost $charge ",
                            verbosity => $verbosity
                        );
                        if ( $cardinfo->{minute_fee_pennies} > 0 ) {
                            $charge =
                              ( ( $cardinfo->{minute_fee_pennies} * 100 ) +
                                  $charge )
                              if $cardinfo->{timeused} +
                                   $data->{variables}->{billsec} =>
                                  $cardinfo->{minute_fee_minutes};
                        }
                        if ( $cardinfo->{min_length_pennies} > 0
                            && ( $cardinfo->{min_length_minutes} * 60 ) >
                             $data->{variables}->{billsec} )
                        {
                            $charge =
                              ( ( $cardinfo->{min_length_pennies} * 100 ) +
                                  $charge );
                        }
			print STDERR "CARDNUMBER: " . $cardinfo->{cardnumber};
                        &write_callingcard_cdr(
                            $astpp_db,
                            $config,
                            $cardinfo,
                            uri_unescape($data->{variables}->{caller_id}),
                             $destination,
                            uri_unescape($data->{variables}->{hangup_cause}),
                            uri_unescape($data->{variables}->{start_stamp}),
                            $charge,
                             $data->{variables}->{billsec}
                        );
                        &callingcard_set_in_use($astpp_db,$cardinfo,0);
                        &callingcard_update_balance($astpp_db,$cardinfo,$charge);
            }
                }

    
}

my $generation_time = tv_interval($starttime);
print STDERR "TOOK: " . $generation_time ;
}
my $generation_time = tv_interval($starttime);
print STDERR "TOOK: " . $generation_time ;
1;
