#!/usr/bin/perl
##########################################################################
#                                                                        #
# Perl agi Script for Authenticate caller via RADIUS or DBA Database     #
#                                                                        #
# Switzernet(c)2011                                                      #
#                                                                        #
##########################################################################
# my modules
use Crypt::CBC;
use Asterisk::AGI;
use Authen::Radius;
use Sys::Syslog;
use Switch;
use List::Util;
use Config::IniFiles;
use Date::Format;
use Time::HiRes qw/gettimeofday/;
use DBI;
use Data::Dumper;

use constant ROUTING => "SIP"; 
#Disable debug
use constant DEBUG => 0;

use constant {H323_CONTINUE        => 0,
              H323_INVALID_ACC     => 1,
              H323_MAX_SIMUL_CALLS => 3,
              H323_ACC_EXPIRED     => 5,
              H323_CREDIT_LIMIT    => 6,
              H323_ACC_BLOCKED     => 7,
              H323_TECH_PROB       => 8,
              H323_DEST_BLOCKED    => 9,
              H323_FREE_CALL       => 13
             };

use constant {CONTACT_PREFIX => 1,
              CONTACT_IP     => 0};


use constant CONFIG_DIR => "/etc/astrad/config";

##########################################################################
# Globals
my $radius_auth = 0; #0 for RADIUS authentication/1 for MySQL authentication

my $credit_time = -1;
my $hangup      = 1;
my $numroutes   = 0;
my $skip_route  = 0;
my $iexpires    = 0;
my $return_code;
my @routes;
my @Expire;
my @Join_Expire;
my %expires;
my $auth_cli;

my $dial_info = '';
my $ii;
my $xtot;
my $dnid;
my $pbcld;
my $nroutes;
my $sip_username;
my $activeprivacy  = 0;
my $activeprivacycode;
my $activefollowme = 0;
my $typefollowme   = 0;
my $fromaccount    = 0;
my $radiusstatus;
my $language;
my $calllimit;
my @routeslist;
my @XSUM;

my $toE164;

my $max_duration = 7200;
my $date_format  = "%Y-%m-%d %T";

my ($setuptime1,$setupms1) = gettimeofday();
my ($setuptime2,$setupms2);
my ($setuptime3,$setupms3);


##########################################################################
# Database

my $dbh;
my $LOCAL_HOST;
my $DB_NAME1;
my $DB_NAME2;
my $DB_HOST;
my $DB_USER;
my $DB_PASS;

##########################################################################
#                                FUNCTION                                #
##########################################################################

# Load config
sub load_config {
    my $conf=Config::IniFiles->new(-file => CONFIG_DIR ."/switzer.conf");
    return 0 if !defined $conf ;
    $LOCAL_HOST  = $conf->val('GLOBAL','NAS_IP');
    $DB_NAME1 = $conf->val('DB3','DB_NAME1');
    $DB_NAME2 = $conf->val('DB3','DB_NAME2');
    $DB_HOST  = $conf->val('DB3','DB_HOST');
    $DB_USER  = $conf->val('DB3','DB_USER');
    $DB_PASS  = $conf->val('DB3','DB_PASS');
    return 1;
}

sub unescape {
    shift() if ref($_[0]);
    my $todecode = shift;
    return undef unless defined($todecode);
    #$todecode =~ tr/+/ /;       # pluses become spaces
    $todecode =~ s/%([0-9a-fA-F]{2})/pack("c",hex($1))/ge;
    return $todecode;
}

sub set_asterisk_variables {
  $max_duration=$credit_time if ($credit_time > 0 && $credit_time < $max_duration && $credit_time ne '');
  $AGI->set_variable('II', $ii);
  $AGI->set_variable('XTOT', $xtot);
  $AGI->set_variable('DNID', $dnid) if $dnid ne '';
  $AGI->set_variable('PBCLD',$pbcld) if $pbcld ne '';
  $AGI->set_variable('NumRoutes',     $nroutes);
  $AGI->set_variable('SIP_Username',  $sip_username) if $sip_username ne '';
  $AGI->set_variable('ActivePrivacy', $activeprivacy) if $activeprivacy ne '';
  $AGI->set_variable('RADIUS_Status', $radiusstatus);
  $AGI->set_variable('ActiveFolloMe', $activefollowme) if $activefollowme ne '';
  $AGI->set_variable('h323-return-code', $return_code);
  $AGI->set_variable('Dial_Info', $dial_info) if $dial_info ne '';
  $AGI->set_variable('CALL_LIMIT', "L(".$max_duration."000:120000:40000)");
  $AGI->set_variable('LIMIT_PLAYAUDIO_CALLER', 'yes');
  if ($language eq "en" || $language eq "de" || $language eq "fr" || $language eq "it" || $language eq "es" || $language eq "pt") {
          $AGI->set_variable('CHANNEL(language)',$language);
          $AGI->verbose("SV SET LANGUAGE = ".$language,5) if (DEBUG);
  } else {
          $AGI->verbose("SV DESIRED LANGUAGE= ".$language,5) if (DEBUG && $language ne '');
  }
  if (DEBUG) {
    $AGI->verbose("SV II           = ".$ii,5);
    $AGI->verbose("SV DNID         = ".$dnid,5);
    $AGI->verbose("SV XTOT         = ".$xtot,5);
    $AGI->verbose("SV PBCLD        = ".$pbcld,5);
    $AGI->verbose("SV Dial_Info    = ".$dial_info,5);
    $AGI->verbose("SV NumRoutes    = ".$nroutes,5);
    $AGI->verbose("SV SIP_Username = ".$sip_username,5);
    $AGI->verbose("SV ActivePrivacy= ".$activeprivacy,5);
    $AGI->verbose("SV ActiveFolloMe= ".$activefollowme,5);
    $AGI->verbose("SV RADIUS_Status= ".$radiusstatus,5);
    $AGI->verbose("SV CALL_LIMIT   = L(".$max_duration."000:120000:40000)",5);
    $AGI->verbose("SV h323-return-code=".$return_code,5);
    $AGI->verbose("SV LIMIT_PLAYAUDIO_CALLER=yes",5);
  }
  syslog('info', "Ast-Aut: II   = ".$ii);
  syslog('info', "Ast-Aut: XTOT = ".$xtot);
  syslog('info', "Ast-Aut: AGI dial info = ".$dial_info);
  syslog('info', "Ast-Aut: return_code = ".$return_code);
  syslog('info', "Ast-Aut: PortaBilling_Auth_Reseller_CLD=".$pbcld) if $pbcld ne '';
  $AGI->hangup() if $return_code != 0 && $return_code != 13 && $hangup;
}

###################################################################################
#
# Authenticate the client using db2 instead of Radius 
# Also return client data.
#
# params : - username of client
#
# return : - h323-return-code :
#            o H323_TECH_PROB if database is unavailable
#            o H323_INVALID_ACC if acc doesn't exist in db
#            o H323_ACC_BLOCKED if acc or customer is blocked
#            o H323_ACC_EXPIRED if acc is expired
#            o H323_MAX_SIMUL_CALLS if call limitation reached
#            o H323_CONTINUE else
#
#          - credit state : 
#            o 1 if client has credits
#            o 0 else
#
sub alternative_acc_auth {
        
    my $cli = @_[0];
	
    if (not defined $dbh){
	# Not available
        syslog('err', "Ast-Aut: Could not connect to database $DB_NAME1 on $DB_HOST");
        return (H323_TECH_PROB);
    }


    ###################################################################################
    #
    # Check the caller to know if he is allowed to call or if he is blocked
    #

    my $request="SELECT c.balance < c.credit_limit AS 'has_credits', "
                ."    (c.blocked = 'Y' OR a.blocked = 'Y') AS 'is_blocked', "
                ."    (a.expiration_date < CURDATE()) or false AS 'is_expired', "
                ."    a.iso_639_1 as language, "
                ."    c.cld_translation_rule as transrule, "
                ."    a.follow_me_enabled, "
                ."    a.i_customer, i_account "; 
    if (DEBUG) {
    # if in debug mode request more details
    $request   .=",   c.balance AS 'balance', c.credit_limit AS 'credit_limit',"
                ."    c.blocked AS 'cust_blocked', a.blocked AS 'acc_blocked',"
                ."    a.expiration_date AS 'expires', CURDATE() AS 'cur_date'";
    }
    $request   .=" FROM Accounts a "
                ." INNER JOIN Customers c "
                ."    ON (a.i_customer = c.i_customer) "
                ." WHERE a.id='$cli';";
    my $sth     = $dbh->prepare($request);
    $sth->execute();
        
    my @data1;
    return (H323_INVALID_ACC) unless(@data1 = $sth->fetchrow());

    $language       = $data1[3];
    $toE164         = $data1[4];
    $activefollowme = 1 if ($data1[5] eq 'Y');
    my $customerid  = $data1[6];
    my $accountid   = $data1[7];

    if ($fromaccount == 1) {
      #Call from a customers account
      # Checks if the customer has CID hiding configured or simultaneous call limitation
      # TODO with this query it is not possible to see if the user chose Customers default on hide CLI
      # see how to differentiate options use default and no from hide CLI in Accounts
      my $request2 = "SELECT sav.i_sattribute, sav.value "
                    ."FROM Service_Attribute_Values sav "
                    ."WHERE (sav.i_foreign = '".$customerid."' AND (sav.i_sattribute = 6 OR sav.i_sattribute = 1)) "
                    ."OR (sav.i_foreign = '".$accountid."' AND sav.i_sattribute = 5);";
      my $sth      = $dbh->prepare($request2);
      $sth->execute();
      while (my $data2 = $sth->fetchrow_hashref()) {
        if (defined($data2->{value})) {
          switch ($data2->{i_sattribute}) {
            # Sets simultanous call limit
            case 1 {
              $calllimit = $data2->{value} if ($data2->{value} > 0);
            }
            # Sets hide cli code
            case 5 {
              $data2->{value}  =~ m/hide=(.*)/;
              $activeprivacycode = $1 if ($1 ne '');
            }
            case 6 {
              $data2->{value}  =~ m/hide=(.*)/;
              $activeprivacycode = $1 if ($1 ne '' && !defined($activeprivacycode));
            }
          }
        }
      }

      if ($calllimit > 0) {
        my $request3 = "SELECT count(*) AS total "
                      ."FROM Active_Calls where CLI='".$cli."';";
        my $sth      = $dbh->prepare($request3);
      }
      if (my $data2 = $sth->fetchrow_hashref() && $data2->{total} >= $calllimit) {
        return (H323_MAX_SIMUL_CALLS,undef,@data1);      
      }
    }     
    #Call from a node to a customers account
    #Follow me
    #TODO this should be in the route function
    if ($activefollowme) {
      my $request4 = "SELECT mode, sequence, timeout "
                    ."FROM Follow_Me where i_account='".$accountid."';";
      my $sth      = $dbh->prepare($request4);
      if (my $data2 = $sth->fetchrow_hashref()) {
        #Modes:    Never, Always, Unavail
        #Sequence: Order, Random, Simultaneous
        if ($data2->{mode} eq 'Never') {
          $activefollowme = 0;
        } else { 
          $activefollowme = 2 if ($data2->{mode} eq 'Always');
          $typefollowme   = 1 if ($data2->{sequence} eq 'Random');
          $typefollowme   = 2 if ($data2->{sequence} eq 'Simultaneous');
        }
      }
    }

    # Account or customer is blocked
    return (H323_ACC_BLOCKED,undef,@data1) if ($data1[1]);

    # Account is expired
    return (H323_ACC_EXPIRED,undef,@data1) if ($data1[2]);

    return (H323_CONTINUE,$data1[0],@data1);
}


###################################################################################
#
# Authenticate a node using db2 instead of Radius
#
# params : - ip of node
#
# return : - h323-return-code :
#            o H323_TECH_PROB   if database is unavailable
#            o H323_INVALID_ACC if acc doesn't exist in db
#            o H323_ACC_BLOCKED if acc or customer is blocked
#            o H323_ACC_EXPIRED if acc is expired
#            o H323_CONTINUE else
#
#          - credit state :
#            o 1 if client has credits
#            o 0 else
#
sub alternative_ip_auth {

    my $node = @_[0];

    if (not defined $dbh){
        # Not available
        syslog('err', "Ast-Aut: Could not connect to database $DB_NAME1 on $DB_HOST");
        return (H323_TECH_PROB);
    }

    ###################################################################################
    #
    # Check if the node is allowed to call
    #
    #TODO use local table sipdevices where context fromhost instead of using DBA
    my $request="SELECT ip,name
                   FROM Nodes
                 WHERE ip = '$node'
                   AND i_env=1
                   AND i_node_type=11
                   AND name not like '%[Deleted]%'";
    my $sth = $dbh->prepare($request);
    $sth->execute();

    return H323_INVALID_ACC unless (my $result1 = $sth->fetchrow_hashref());

    return H323_CONTINUE;
}
###################################################################################
#
# Define the route to destinations
#
# params : - destination
#
#
# return : - h323 return code
#          - formated destination 
#          - a list of ip to contact for routing
#
sub define_routing_params {


    # return values
    my ($return_code, $dest, @contacts);

    my $dest = @_[0];

    ############################################################################
    # Do the regexp rules depending on number called
    # It is important to match a format like +cc num
    # If it begins by the national prefix (0) replace it by 41
    my $prefix_international = '00';
    my $prefix_domestic      = '0';
    my $country_code         = '41';

    if ($toE164 ne '') {
      #User as defined rules
      if($toE164 =~ m/#.* dp.([\d]+) .*/) {
        $prefix_domestic      = $1;
      } else {
        $prefix_domestic      = '';
      }
      if($toE164 =~ m/#.* ip.([\d]+) .*/) {
        $prefix_international = $1 if ($1 ne '');
      }
      if($toE164 =~ m/#.* cc.([\d]+) .*/) {
        $country_code = $1 if ($1 ne '');
      }
    } else {
      $country_code = '' if ($dest =~ m/^$country_code/);
    }
    $AGI->verbose("OriginalDest=$dest",5) if (DEBUG);

    if (defined($activeprivacycode)) {
      # User has a privacy code enabled
      $activeprivacycode =~ s/([*+]{1})/\\$1/m;
      $AGI->verbose("Activeprivacycode=$activeprivacycode",6) if (DEBUG);
      if ($dest =~ m/^$activeprivacycode/) {
        $activeprivacy = 1;
        $dest =~ s/^$activeprivacycode//;
      }      
    }

    # remove 00 or + and the local prefix and replace it by the country code
    if ($dest =~ m/^($prefix_international|\+)/) {
      $dest =~ s/^($prefix_international|\+)//;
    } else {
      $dest =~ s/^$prefix_domestic//;
      $dest =~ s/^/$country_code/;
    }

    $AGI->verbose("E164Dest=$dest",5) if (DEBUG);
 

    ############################################################################
    #
    # Check the dest to know if it is a free call or not
    #

    my $request="SELECT id FROM Accounts WHERE id='$dest';";
    $sth = $dbh->prepare($request);
    $sth->execute();
    my $acc_data = $sth->fetchrow_hashref();
    my $is_outgoing = not defined $acc_data->{id};
    my $join_expire = 1;
    my $expire = 300;

    $XSUM[$join_expire] = 0;

    if ($is_outgoing) {
      my $forcerates = 1; #Forces fixed routing 
      if ($forcerates == 0) {
        #Gets list of vendors from database with outgoing format
        $request="SELECT c.remote_ip,c.cld_translation_rule ";
        $request.=",c.description,c.outgoing_cld_translation_rule,c.capacity,c.i_tariff,r.price_1,r.preference " if (DEBUG);
        $request.="FROM `porta-billing`.Connections c, ("
                   ."SELECT * FROM ("
                     ."SELECT i_tariff, price_1, preference, effective_from "
                     ."FROM Rates "
                     ."WHERE i_dest=("
                       ."SELECT i_dest "
                       ."FROM  Destinations des "
                       ."WHERE '".$dest."' LIKE CONCAT(des.destination, '%') AND i_env = 1 "
                       ."ORDER BY LENGTH(des.destination) desc limit 1"
                     .") AND effective_from < NOW() "
                   .") AS tout "
                 ."GROUP BY tout.i_tariff) r "
                 ."WHERE c.i_env=1 AND c.remote_ip REGEXP '^([0-9]{1,3}[.]){3}[0-9]{1,3}\$' AND c.sip='Y' AND c.i_tariff = r.i_tariff "
                 ."ORDER BY r.preference DESC, r.price_1 ASC;";
        $sth = $dbh->prepare($request);
        $sth->execute();
        my $append='';
        while(my $acc_data2 = $sth->fetchrow_hashref()) {
          if (not defined $acc_data2->{outgoing_cld_translation_rule}) {
            $acc_data2->{outgoing_cld_translation_rule} = '';
            $append='';
          } else {
            $acc_data2->{outgoing_cld_translation_rule} =~ /^s\/[^\/]*\/(.*)\$1\/;/;
            $append = $1;
            $append =~ s/\\//m;
          }
          
          push(@routeslist, [$join_expire."_".$XSUM[$join_expire], ROUTING."/".$append.$dest."@".$acc_data2->{remote_ip}, $expire, $append.$sip_username]);
          $XSUM[$join_expire]++;
        }
      } else {
        push(@routeslist, ["1_0", ROUTING."/+".$dest.'@212.249.15.9', $expire, "+".$sip_username]);   # Verizon 1 +dest
        push(@routeslist, ["1_1", ROUTING."/+".$dest.'@212.249.15.4', $expire, "+".$sip_username]);   # Verizon 2 +dest
        push(@routeslist, ["1_2", ROUTING."/00".$dest.'@212.249.15.5', $expire, "00".$sip_username]); # Verizon SS7 00dest
        push(@routeslist, ["1_3", ROUTING."/00".$dest.'@217.168.45.4', $expire, "00".$sip_username]); # Colt SS7 00dest
        $XSUM[$join_expire]=4;
      }
      $return_code = H323_CONTINUE;
    } else {
        # Find and define the right SIP server (only phones registered in Astrad servers found)
        #TODO Enable Followme in DBA 
        my $request="SELECT domain FROM `$DB_NAME2`.location2 "
                   ."WHERE username='$dest' ORDER BY reception DESC LIMIT 1;";
        my $sth = $dbh->prepare($request);
        $sth->execute();
        my $location = $sth->fetchrow_hashref();
        # dest format unchanged
        push(@routeslist, [$join_expire."_".$XSUM[$join_expire], ROUTING."/".$dest."@".$location->{domain}, $expire, $sip_username]);
        $XSUM[$join_expire]++;
        $return_code = H323_FREE_CALL;
    }
    $dnid = $dest;
    return $return_code;
}

sub set_routing_data {
    for my $i ( 0 .. $#routeslist ) {
        if ($routeslist[$i][1] && $routeslist[$i][2] && $routeslist[$i][3]) {
            $nroutes++;
            $AGI->set_variable("XROUTE_".$routeslist[$i][0], $routeslist[$i][1]);
            $AGI->set_variable("XEXP_".$routeslist[$i][0], $routeslist[$i][2]);
            $AGI->set_variable("XCLI_".$routeslist[$i][0], $routeslist[$i][3]);
            if (DEBUG) {
                $AGI->verbose("SV XROUTE_".$routeslist[$i][0]." = ".$routeslist[$i][1],5);
                $AGI->verbose("SV XEXP_".$routeslist[$i][0]."   = ".$routeslist[$i][2],5);
                $AGI->verbose("SV XCLI_".$routeslist[$i][0]."   = ".$routeslist[$i][3],5);
            }
            $dial_info .= '&' if $dial_info ne '';
            $dial_info .= $routeslist[$i][1];
            syslog('info', "Ast-Aut: XROUTE_".$routeslist[$i][0]."=".$routeslist[$i][1]." | XEXP_".$routeslist[$i][0]."=".$routeslist[$i][2]." | XCLI_".$routeslist[$i][0]."=".$routeslist[$i][3]) ;
        }
    }
    my $k = 0;
    $ii=0;
    foreach my $vl (@XSUM) {
        if ($vl) {
            syslog('info', "Ast-Aut: XSUM_$k = $vl");
            $AGI->set_variable("XSUM_$k", $vl);
            $AGI->verbose("SV XSUM_".$k." = ".$vl,5) if (DEBUG);
            $ii=$k if (!$ii);
        }
        $k++;
    }
    $k = $k - 1 ;
    $xtot=$k;
}

sub set_routing_data2 {
        $radiusstatus = 'OK';
}


##########################################################################
#                                 main()                                 #
##########################################################################
$AGI = new Asterisk::AGI;
my %input = $AGI->ReadParse();
openlog('ast-auth', 'cons,pid', 'daemon');
syslog('notice', "Ast-Aut: Script Asterisk Authentication Started");
$AGI->verbose("AGI START",0);


my $username;

my %params;
my(@pairs) = split(/[&;]/,$ARGV[0]);
my($param,$value);
foreach (@pairs) {
    ($param,$value) = split('=',$_,2);
    $param = unescape($param);
    $value = unescape($value);
    $params{$param}=$value;
}
##########################################################################
# Sending auth. radius packet to portaone server
$hangup = 0 if defined $params{'IfFailed'} && $params{'IfFailed'} =~ /DoNotHangup/i;
my ($a,$b,$c,$d) = ($1,$2,$3,$4) if defined $params{'H323_ID'} && $params{'H323_ID'} =~ /([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)/;
my $h323_id = sprintf("%08X %08X %08X %08X",$a,$b,$c,$d);
$AGI->verbose("H323 hex: $h323_id");

$max_duration = $AGI->get_variable('max_duration');
my $r = new Authen::Radius(Host => $AGI->get_variable('RADIUS_Server'), Secret => $AGI->get_variable('RADIUS_Secret'));

if( !defined $r ) {
        $AGI->verbose('RADIUS server "'.$AGI->get_variable('RADIUS_Server').'" ERROR', 0);
        $AGI->hangup() if $hangup;
        syslog('err', "Ast-Aut: Error in Portaone Radius connection");
        closelog;
        exit;
}

Authen::Radius->load_dictionary;

$input{'callerid'} = $1 if defined $input{'callerid'}  && $input{'callerid'} =~ /<(\d*)>/;
$input{'dnid'} = $1 if defined $input{'dnid'}  && $input{'dnid'} =~ /<(\d*)>/;


$r->add_attributes (
    { Name => 'NAS-IP-Address', Value => $AGI->get_variable('NAS_IP_Address') },
    { Name => 'NAS-Port-Name', Value => $input{'channel'} },
    { Name => 'Calling-Station-Id', Value => $input{'callerid'} },
    { Name => 'Called-Station-Id', Value => $input{'dnid'} },
    { Name => 'Cisco-AVPair', Value => "call-id=$input{'uniqueid'}" },
    #{ Name => 'Cisco-AVPair', Value => "call-id=$callid" },
    { Name => 'Cisco-AVPair', Value => "h323-conf-id=$h323_id" }
);

if( defined $params{'AuthorizeBy'} && $params{'AuthorizeBy'} =~ /sip/i ) {
    my $sip_auth_header = $AGI->get_variable('SIP_Authorization');
    if( !defined $sip_auth_header ) {
        $AGI->verbose("ERROR Authorization=SIP requested but no SIP Authorization header provided.", 0);
        $AGI->set_variable('RADIUS_Status', 'ConfigurationError');
        $AGI->hangup() if $hangup;
        syslog('err', "Ast-Aut: ERROR Authorization=SIP requested but no SIP Authorization header provided.");
        closelog;
        exit;
    }
    $AGI->verbose("sip_auth_header=$sip_auth_header",5);
    $sip_auth_header =~ s/\s*Digest\s*//;
    $AGI->verbose("sip_auth_header=$sip_auth_header",5);
    my %sip_auth;
    my(@pairs) = split(/,\s?/,$sip_auth_header);
    my($param,$value);
    foreach (@pairs) {
        ($param,$value) = split('=',$_,2);
        $param = unescape($param);
        $value = unescape($value);
        $value =~ s/^"//;
        $value =~ s/"$//;
        $sip_auth{$param}=$value;
    }

    $username = $sip_auth{'username'};
    
    $r->add_attributes ( { Name => 'User-Name', Value => $username } );
    $r->add_attributes ( { Name => 'Digest-Attributes', Value => 'User-Name = "'.$username.'"' } );
    $r->add_attributes ( { Name => 'Digest-Attributes', Value => 'Realm = "'.$sip_auth{'realm'}.'"' } );
    $r->add_attributes ( { Name => 'Digest-Attributes', Value => 'Nonce = "'.$sip_auth{'nonce'}.'"' } );
    $r->add_attributes ( { Name => 'Digest-Attributes', Value => 'URI = "'.$sip_auth{'uri'}.'"' } );
    $r->add_attributes ( { Name => 'Digest-Attributes', Value => 'Method = "INVITE"' } ); 
    $r->add_attributes ( { Name => 'Digest-Attributes', Value => 'Algorithm = "'.$sip_auth{'algorithm'}.'"' } );
    $r->add_attributes ( { Name => 'Digest-Response', Value => $sip_auth{'response'} } );
    $sip_username = $username;
} else {
    #TODO looks wrong, check
    $username = $input{'accountcode'};
    $r->add_attributes ( { Name => 'User-Name', Value => $input{'accountcode'} } );
    $r->add_attributes ( { Name => 'Password', Value => $params{'Password'} } ) if defined $params{'Password'};
}

$r->add_attributes ( { Name => 'h323-remote-address', Value => "$params{H323_RADDR}" } ) if defined $params{'H323_RADDR'};
$r->add_attributes ( { Name => 'h323-ivr-out', Value => "PortaBilling_Routing:$params{Routing}" } ) if defined $params{'Routing'};
if ($radius_auth == 0) {
  $r->send_packet (ACCESS_REQUEST) and $type = $r->recv_packet;
}
if( !defined $type || $radius_auth == 1) {
    
    $error = $r->get_error;
    ($setuptime2,$setupms2) = gettimeofday();

    ###################################################################################
    #
    # Master is unavailable. Use db2 to authentificate and route the call
    #
    syslog('info', "Ast-Aut: Master not available => Alternative Authentification for $h323_id");
    closelog;
    $AGI->verbose("Master unavailable. Trying DBA",0);

    openlog('ast-auth-alt', 'cons,pid', 'daemon');
    syslog('info', "Ast-Aut-Alt : Call $h323_id");

    my @debug_data;

    load_config();
    $dbh = DBI->connect("DBI:mysql:$DB_NAME1;host=$DB_HOST", $DB_USER, $DB_PASS, {PrintError=>0});
    #TODO connect locally (needs required data locally)
    #TODO if connection fails try another DBA sequentially (only needed if there is no data locally)
    if (not defined $dbh){
      # DBA2 Not available
      syslog('err', "Ast-Aut: Could not connect to database $DB_NAME1 on $DB_HOST");
      $return_code = H323_TECH_PROB;
    } else {
      $AGI->verbose("Ast-Aut: Connected to database $DB_NAME1 on $DB_HOST",5) if (DEBUG);
      # Check in which context we are
      $AGI->verbose("sip_auth_header=Auth by ".$params{'CustomerType'},6);
      switch ($params{'CustomerType'}) {
        #####################################
        #
        # SIP Authentication
        #
        case("SipAuth") {
            $fromaccount=1;
            ($return_code,my $cli_has_credits, @debug_data) = alternative_acc_auth($username);
            $return_code = define_routing_params($input{'dnid'}) if ($return_code == H323_CONTINUE);
            $pbcld=$dnid;
            # check  if customer has credits in case of non-free call
            $return_code = H323_CREDIT_LIMIT if ($return_code == H323_CONTINUE and not $cli_has_credits);
        }
        #####################################
        #
        # Node Authentication
        #
        case("PeerAuth") {
            $return_code = alternative_ip_auth($username);
            if ($return_code == H323_CONTINUE) {
                $dnid = $input{'dnid'};
                $pbcld=$dnid;
                my $callerid = $AGI->get_variable('CALLERID(num)');
                push(@routeslist, ["1_0", ROUTING."/".$dnid, '300', $callerid]);
                $XSUM[1]=1;
                $join_expire = 1;
            }
        }
        #####################################
        #
        # IP Authentication
        #
        case("IpAuth"){
            ($return_code,my $void,@debug_data) = alternative_acc_auth($username);
            $return_code = define_routing_params($input{'dnid'}) if ($return_code == H323_CONTINUE);
        }
      }
      $dbh->disconnect();
    }
    $radiusstatus = 'OK' if ($return_code == H323_CONTINUE or $return_code == H323_FREE_CALL);

    ($setuptime3,$setupms3) = gettimeofday();

    my $debug_str;
    if (defined @debug_data) {
      $debug_str = "";
      foreach $d (@debug_data){
        $debug_str.="," unless $debug_str eq "";
        $debug_str.=$d;
      }
    } else {
      # create empty fields
      $debug_str = ",,,,,";
    }

    # Log Call ID, CLI, CLD, setup time, connection time, error reason, return code
    syslog('notice', ",$h323_id,$input{'callerid'},$username,$input{'dnid'},"
                   .time2str($date_format, $setuptime1).".$setupms1,"
                   .time2str($date_format, $setuptime2).".$setupms2,"
                   .time2str($date_format, $setuptime3).".$setupms3,$error,$return_code,"
                   .$params{H323_RADDR}.",".$params{'CustomerType'}.","
                   . $debug_str);
    set_routing_data();	
    set_asterisk_variables ();
    closelog;
    exit;

}

##########################################################################
# Treatment reply auth. radius packet from portaone server
my ($ActiveFolloMe,$TypeFollowMe);
my $Pred_expire=0;
my $ActivePrivacy=0;
for $a ($r->get_attributes) {
#    $AGI->set_variable($a->{'Name'}, $a->{'Value'});
    $AGI->verbose("SV ".$a->{'Name'}."=".$a->{'Value'},6) if (DEBUG);
    $return_code = $a->{'Value'} if $a->{'Name'} eq 'h323-return-code';
    $credit_time = List::Util::min($max_duration,$a->{'Value'}) if $a->{'Name'} eq 'h323-credit-time';
    if( $a->{'Name'} eq 'Cisco-AVPair' && $a->{'Value'} =~ /^h323-ivr-in=PortaBilling_Routing:/ ) {
        $AGI->verbose("  -- AGI full route: $a->{'Value'}", 5) if (DEBUG);
        #TODO this regexp is very important, double-check
        $a->{'Value'} =~ /^h323-ivr-in=PortaBilling_Routing:\s*([^@]*)@([^;]*);?(.*)$/;
        my ($dest, $contact) = ($1, $2);
        if ($a->{'Value'} =~ /^h323-ivr-in=PortaBilling_Routing:.*;cli=;/){
           $ActivePrivacy=1;
        }
        my %route_info;
        my(@pairs) = split(/[&;]/,$3);
        my($param,$value);
        foreach (@pairs) {
            ($param,$value) = split('=',$_,2);
            $param = unescape($param);
            $value = unescape($value);
            $route_info{$param}=$value;
        }
        my $cipher = Crypt::CBC->new({ 'key'            => $AGI->get_variable('RADIUS_Secret'),
                                       'cipher'         => 'DES',
                                       'iv'             => '$KJh#(}q',
                                       'regenerate_key' => 1, # default  true
                                       'padding'        => 'oneandzeroes',
                                       'prepend_iv'     => 0
                                    });
        my ($authuser, $secret) = split(/\0/, $cipher->decrypt(pack("H*",$route_info{'auth'})) );


        $ActiveFolloMe=1 if ($route_info{'access-code'} eq 'FOLLOWME');
        switch ($route_info{'g-hunt'}) {
            case 'all'  { $TypeFollowMe='Simultaneous';}
            case 'seq'  { $TypeFollowMe='Sequential' ;}
        }
        if ($route_info{'expires'}) {
            $skip_route++;
            $Pred_expire=$route_info{'expires'};
        }
        if($dest ne '' and $contact ne '' and ($Pred_expire or $ActivePrivacy)) {
            if (!$Pred_expire and $ActivePrivacy){
               $Pred_expire=30;
               $skip_route++;
            }
            @routes[$numroutes]=$params{'Routing'}."/".$dest;
            @routes[$numroutes].=":$secret" if defined $route_info{'auth'} && defined $secret;
            @routes[$numroutes].=":$authuser" if defined $route_info{'auth'} && defined $authuser;
            @routes[$numroutes].='@'.$contact;

            if (defined $route_info{'cli'}) { @clis[$numroutes]=$route_info{'cli'};}
            elsif($ActivePrivacy) {@clis[$numroutes]=$username;}
            @Join_Expire[$numroutes]=$skip_route;
            @Expire[$numroutes]=$Pred_expire;
            $numroutes++;
        } else {
            $AGI->verbose("  -- AGI route skipped",5) if (DEBUG);
        }
    }
    if( $a->{'Name'} eq 'Cisco-AVPair' && $a->{'Value'} =~ /^h323-ivr-in=PortaBilling_CompleteNumber:/ ) {
        $routing_info = $a->{'Value'};
        $routing_info =~ s/^h323-ivr-in=PortaBilling_CompleteNumber://;
        $dnid         = $routing_info
    }
    if( $a->{'Name'} eq 'Cisco-AVPair' && $a->{'Value'} =~ /^h323-ivr-in=PortaBilling_CLI:/ ) {
        $routing_info = $a->{'Value'};
        $routing_info =~ s/^h323-ivr-in=PortaBilling_CLI://;
        $auth_cli=$routing_info;
    }
    if( $a->{'Name'} eq 'Cisco-AVPair' && $a->{'Value'} =~ /^h323-ivr-in=PortaBilling_Auth_Reseller_CLD:([0-9]+)/ ) {
        $pbcld=$1;
    }
    #Selects language
    if( $a->{'Name'} eq 'h323-preferred-lang') {
        $language=$a->{'Value'};
    }
}

#Not used yet:
$TypeFollowMe='List' if ($ActiveFolloMe && !$TypeFollowMe);
syslog('info', "Ast-Aut: ActiveFolloMe=$ActiveFolloMe -> $TypeFollowMe");

for ($i=0;$i<$numroutes;$i++) {
    if (@Join_Expire[$i]) {
        my $cli=$auth_cli;
        $cli=@clis[$i] if defined @clis[$i];
	if (@routes[$i] && @Expire[$i] && $cli) {
            @XSUM[@Join_Expire[$i]]=0 if (!defined @XSUM[@Join_Expire[$i]]);
            push(@routeslist, [@Join_Expire[$i]."_".@XSUM[@Join_Expire[$i]], @routes[$i], @Expire[$i], $cli]);
            @XSUM[@Join_Expire[$i]]++;
	}
    }    
}


$radiusstatus   = 'OK';
$activefollowme = $ActiveFolloMe;
$activeprivacy  = $ActivePrivacy;

set_routing_data();
set_asterisk_variables();

closelog;
exit;
##########################################################################
#                                  END                                   #
##########################################################################
