#!/usr/bin/perl 
#############################
# my modules
use strict;
use warnings;
use Sys::Syslog;
use POSIX;
use Config::IniFiles;
use Switch;
use Asterisk::Manager;
use DBI;
#############################
# Setting
my $lock_file = '/var/run/ast-reg-switz.pid';
my $runas_user = 'asterisk';
my $monitor_dir = '/var/spool/asterisk/monitor';
my $config_dir = '/etc/asterisk';
#############################
# Globals
my %channels;
my $dbh;
my $astman;
my ($nas_ip,$DB_NAME,$DB_HOST,$DB_USER,$DB_PASS);
my $ast_username = 'test';
my $ast_password="test";
my $ast_port="5038";
my $ast_hostname="127.0.0.1";
#############################
# Check if already running
if( -e $lock_file ) {
	open(PID,$lock_file);
	my $pid=<PID>;
	close PID;
	chomp $pid;
	if( !-e "/proc/$pid" ) {
		print STDERR "Lock file present, but no process with pid=$pid.\n";
		die "Can't delete lock file $lock_file\n" if !unlink $lock_file;
		print STDERR "Lock file has been removed.\n";
	} else {
		die "Lockfile present, another copy is punning pid=$pid\n";
	}
}
my ($name, $passwd, $uid, $gid) = getpwnam($runas_user) or die "$runas_user not in passwd file";;
#############################
# set C locale
POSIX::setlocale(LC_ALL, "C");
#############################
# Become daemon
my $pid;
if( !defined($pid = fork()) ) 
	{
	die "cannot fork: $!";
	} 
	elsif ($pid) 
		{
		# Create lockfile, and finish parent process
		open(PID, "> $lock_file") || die "ast-reg-switz.pl: Unable to create lockfile $lock_file\n";
		print PID "$pid";
		close PID;
		chown $uid, $gid, $lock_file;
		exit;
		} 
		else 
			{
			# daemon
			setpgrp();
			select(STDERR); $| = 1;
			select(STDOUT); $| = 1;
			openlog('ast-reg-switz', 'cons,pid', 'daemon');
			syslog('notice', "Register for Asterisk started");
			}
#############################
# Install signall handler
$SIG{INT} = \&safe_exit;
$SIG{QUIT} = \&safe_exit;
$SIG{TERM} = \&safe_exit;
$SIG{HUP} = \&load_config;
#############################
# Drop privileges
setuid($uid);
$< = $uid;
$> = $uid;
#############################
# connect Acterisk Menager
load_config();
AMI_connect();
#############################
# AMI connect
sub AMI_connect {
$astman = new Asterisk::Manager;
$astman->user($ast_username);
$astman->secret($ast_password);
$astman->host($ast_hostname);
my $cop=0;
my $ast_connected = 1;
while( 1 ) {$cop++;
        if( $astman->connect ) {
                $ast_connected = 1;
                syslog('info', 'Connected to Asterisk!');
                $astman->setcallback('DEFAULT', \&status_callback);
                eval { $astman->eventloop; };
        } else {
                syslog('err', 'Could not connect to Asterisk!') if $ast_connected;
                $ast_connected = 0;
                syslog('info', "SLEEP $cop");
        }
        sleep 1;
}
}
#############################
# Do Event
sub status_callback {
my (%event) = @_;
return  unless scalar(%event);

if ($event{'Event'} eq 'PeerStatus' && defined($event{'Peer'}))
        {
	my $request;
        my ($acc) = $event{'Peer'} =~ /^SIP\/(\d{11})/i;
        my $PStatus = $event{'PeerStatus'};
	my %peerinfo=$astman->sendcommand(Action => 'SIPshowpeer', Peer => $acc);
#	foreach (keys %peerinfo) {
#                syslog('debug', "|$_| = |".$peerinfo{$_} . "|" );
#        	}
	if (defined($peerinfo{'Response'}) && $peerinfo{'Response'} eq 'Success')	
		{
		#syslog('info',"-Public  IP  = |sip:$acc\@$pubcon|");
                #syslog('info',"-Private IP  = |asterisk\@$localip|");
		#syslog('info',"-Expire Date = |$exp|");
		#syslog('info',"-User Agent  = |$ua|");
		#syslog('info',"-Domain      = |$nas_ip|");
		my $request;
		if ($PStatus =~ /^Registered/i && $peerinfo{'Address-IP'} && $peerinfo{'Address-Port'})
			{
			my ($pubcon,$astcon,$ua)=($peerinfo{'Address-IP'}.":".$peerinfo{'Address-Port'},$peerinfo{'Reg-Contact '},$peerinfo{'SIP-Useragent'});
                	my $exp=time();
                	$exp+=$1 if $peerinfo{'RegExpire'} =~ /\s*(\d+) seconds/;
                	$exp=format_date_sql($exp);
                	my $offSet  = 1 + index($astcon, '@');
                	my $localip = substr $astcon, $offSet, index($astcon, ':',$offSet) - $offSet;
              		$request="delete from location where username='$acc' AND i_env=1 AND domain='$nas_ip'";
			location_update($request);
			$request="replace into location (username,contact,expires,q,callid,cseq,flags,user_agent,received,i_env,domain) values ('$acc', 'sip:$acc\@$pubcon', '$exp', -1.00, 'asterisk\@$localip', 1, 1, '$ua', 'NULL', 1, '$nas_ip')";
			syslog('info', "$acc : $PStatus");
			}
		elsif ($PStatus =~ /^Unregistered/i && !$peerinfo{'Address-Port'})
			{
			$request="delete from location where username='$acc' AND i_env=1 AND domain='$nas_ip'";
			syslog('info', "$acc : $PStatus");
			}
		location_update($request) if ($request);
		}
	}

if ($event{'Event'}  =~ /Shutdown/i )
	{
	syslog('info', "Asterisk Shutdown");
	safe_exit();
	}
if ($event{'Event'} =~ /Reload/i )
	{
	syslog('info', "Asterisk Reload");
	load_config();	
	}

}
#############################
# PortaSIP db implementation:
# save("location");
sub location_update {
	my $request=$_[0];
	return 0 unless defined $request;
        $dbh = DBI->connect_cached("DBI:mysql:$DB_NAME;host=$DB_HOST", $DB_USER, $DB_PASS);
        syslog('crit', "REG Could not connect to database $DB_NAME on $DB_HOST") if !defined $dbh;
	if (defined $dbh) {
		syslog('crit', "REG: $request");
		my $sth = $dbh->prepare($request);
		$sth->execute();
	}
}
#############################
# Format date for GMT sample '09:16:05 GMT Sat Dec 11 2004'
sub format_date_gmt {
	my ($date) = @_;
	return strftime "%H:%M:%S GMT %a %b %e %Y", gmtime($date);
}
#############################
# Format date for SQL sample '2004-12-11 09:16:05'
sub format_date_sql {
	my ($date) = @_;
	return strftime("%Y-%m-%d %H:%M:%S", localtime($date));
}
#############################
# Signal Handlers
sub safe_exit {
	my($sig) = @_;
	syslog('crit', "Caught a SIG$sig - shutting down");

	$astman->disconnect if $astman;     
	unlink $lock_file or syslog('crit', "Unable to create lockfile $lock_file\n");
	closelog();     
	exit;
}
#############################
# Load config from extensions.conf
sub load_config {
        #my $conf=Config::IniFiles->new(-file => "$config_dir/manager.conf");
        #syslog('crit', "AMI: Config file error!") if !defined $conf;
        #$ast_password = $conf->val('general','secret');
        #$ast_hostname = $conf->val('general','bindaddr');
        #$ast_port = $conf->val('general','port');
        #syslog('notice', "AMI: $config_dir/manager.conf loaded");
        my $conf=Config::IniFiles->new(-file => "$config_dir/extensions.conf");
        syslog('crit', "Config file error!") if !defined $conf;
        $nas_ip = $conf->val('globals','NAS_IP_Address');
        syslog('notice', "AMI: $config_dir/extensions.conf loaded");
        $conf=Config::IniFiles->new(-file => "$config_dir/billing.conf");
        syslog('crit', "Config file error!") if !defined $conf;
        $DB_NAME = $conf->val('general','DB_NAME');
        $DB_HOST = $conf->val('general','DB_HOST');
        $DB_USER = $conf->val('general','DB_USER');
        $DB_PASS = $conf->val('general','DB_PASS');
	syslog('notice', "AMI: $config_dir/billing.conf loaded");
}

