#!/usr/bin/perl
#
# UserSide Asterisk Manager PlugIn
# http://wiki.userside.eu/Us_asterisk - READ INFORMATION ABOUT THIS
our $zrevision = "18";
our $zver = "usm_asterisk v1.0." . $zrevision;
# =============================================================================
use strict;
use warnings;
use EV;
use Asterisk::AMI;
use Time::Local;
use LWP::UserAgent;
use HTTP::Request::Common qw{ POST };
use FindBin;

our $zpath = $FindBin::RealBin;
require($zpath . "/usm_asterisk.conf");

our $zastertype;
our $zusapikey;
our $ps_logpath;
our @zasterfromcontexts;
my %contexts = map { $_ => 1 } @zasterfromcontexts;

if (!defined($zastertype)) {
  $zastertype = 1;
}

chdir '/'                                               or die "Can't chdir to /: $!";
open STDIN, '/dev/null'                                 or die "Can't read /dev/null: $!";

if (!our $ps_consolelog) {
  open STDOUT, '/dev/null'                              or die "Can't write stdout: $!";
  open STDERR, '>>' . $ps_logpath . '/usm_asterisk.err' or die "Can't dup usm_asterisk.err: $!";
}

open (PID, '>'.$zpath.'/usm_asterisk.pid'); print PID "$$\n"; close (PID);

loger("===========================", '');
loger("Starting Asterisk module...", '');

my $astman = Asterisk::AMI->new(PeerAddr   => our $zasterhost,
                                PeerPort   => our $zasterport,
                                Username   => our $zasterlogin,
                                Secret     => our $zastersecret,
                                Events     => 'on',
                                Timeout    => 3,
                                Keepalive  => 60,
                                on_error   => sub { loger("*** Disconnected from Asterisk server.", 'err'); exit; },
                                on_timeout => sub { loger("*** Asterisk server connection timeout.", 'err'); exit; },
                                Handlers   => { default => \&usersidehandler }
                                );

unless ($astman) {
    loger("*** Unnable connect to Asterisk server.", 'err');
    exit;
}

loger($zver . " is started.", '');
send_heartbeat();

#========================================================
sub usersidehandler {
    my ($ami, $event) = @_;

    if ($zastertype == 5) {             ## Personal mode CALLBACK

        # CALLS-ADD
        if (($event->{'Event'} eq "Newstate") && $event->{'ChannelState'} == 4) {

            loger("Incoming call from: ".$event->{'CallerIDNum'}." Uniqueid:".$event->{'Uniqueid'}, '');

            if ($event->{'CallerIDNum'} eq '<unknown>' || $event->{'CallerIDNum'} eq '') {
                loger("Received an empty subscriber number. Don't send to the API.", '');
                return;
            }

            my $num  = $event->{'CallerIDNum'};
            my $name = $event->{'CallerIDName'};
            if ($num eq $name) {
                $name = '';
            }

            my $data = {
                'api'      => $zusapikey,
                'cat'      => 'calls',
                'subcat'   => 'add',
                'phoneno'  => $num,
                'caller_id_name' => $name,
                'date'     => now(),
                'aster_id' => $event->{'Uniqueid'}
            };

            curlPOST($data);
        }

        # CALLS-OPERCALL
        if ($event->{'Event'} eq "Newstate" && $event->{'ChannelState'} == 5) {

            my $channel = $event->{'Channel'};
            $channel =~ /\D*\/(\d*)[-@]\d*/;    ## Get Channel ID Number from Channel line
            $channel = $1;

            loger("Call to operator: ".$channel." From:".$event->{'ConnectedLineNum'}." Uniqueid:".$event->{'Uniqueid'}, '');

            if ($event->{'ConnectedLineNum'} eq '<unknown>' || $event->{'ConnectedLineNum'} eq '') {
                loger("Received an empty subscriber number. Don't send to the API.", '');
                return;
            }

            my $num  = $event->{'ConnectedLineNum'};
            my $name = $event->{'ConnectedLineName'};
            if ($num eq $name) {
                $name = '';
            }

            my $data = {
                'api'       => $zusapikey,
                'cat'       => 'calls',
                'subcat'    => 'opercall',
                'phoneno'   => $num,
                'caller_id_name' => $name,
                'date'      => now(),
                'ipphoneno' => $channel,
                'aster_id'  => $event->{'Uniqueid'}
            };

            curlPOST($data);
        }

        # CALLBACK
        if ($event->{'Event'} eq "Newstate" && $event->{'ChannelState'} == 6) {

            loger("Callback to: ".$event->{'CallerIDNum'}." Uniqueid:".$event->{'Uniqueid'}, '');

            my $num  = $event->{'CallerIDNum'};
            my $name = $event->{'CallerIDName'};
            if ($num eq $name) {
                $name = '';
            }

            my $data = {
                'api'       => $zusapikey,
                'cat'       => 'calls',
                'subcat'    => 'cback',
                'phoneno'   => $num,
                'caller_id_name' => $name,
                'date'      => now(),
                'aster_id'  => $event->{'Uniqueid'}
            };

            curlPOST($data);
        }

        # CALLS-ANSWER
        if ($event->{'Event'} eq "Bridge" && $event->{'Bridgestate'} eq "Link") {

            loger("Switching bgidge: Uniqueid1:".$event->{'Uniqueid1'}." Uniqueid2:".$event->{'Uniqueid2'}, '');

            my $data = {
              'api'       => $zusapikey,
              'cat'       => 'calls',
              'subcat'    => 'answer',
              'date'      => now(),
              'aster_id'  => $event->{'Uniqueid1'},
              'aster_id2' => $event->{'Uniqueid2'}
            };

            curlPOST($data);
        }

    } elsif ($zastertype == 4) {         ## Personal mode

        # CALLS-ADD
        if (($event->{'Event'} eq "Newstate") && $event->{'ChannelState'} == 4) {

            loger("Incoming call from: ".$event->{'CallerIDNum'}." Uniqueid:".$event->{'Uniqueid'}, '');

            if ($event->{'CallerIDNum'} eq '<unknown>' || $event->{'CallerIDNum'} eq '') {
                loger("Received an empty subscriber number. Don't send to the API.", '');
                return;
            }

            my $num  = $event->{'CallerIDNum'};
            my $name = $event->{'CallerIDName'};
            if ($num eq $name) {
                $name = '';
            }

            my $data = {
                'api'      => $zusapikey,
                'cat'      => 'calls',
                'subcat'   => 'add',
                'phoneno'  => $num,
                'caller_id_name' => $name,
                'date'     => now(),
                'aster_id' => $event->{'Uniqueid'}
            };

            curlPOST($data);
        }

        # CALLS-OPERCALL
        if ($event->{'Event'} eq "Newstate" && $event->{'ChannelState'} == 5) {

            my $channel = $event->{'Channel'};
            $channel =~ /\D*\/(\d*)[-@]\d*/;
            $channel = $1;

            loger("Call to operator: ".$channel." From:".$event->{'ConnectedLineNum'}." Uniqueid:".$event->{'Uniqueid'}, '');

            if ($event->{'ConnectedLineNum'} eq '<unknown>' || $event->{'ConnectedLineNum'} eq '') {
                loger("Received an empty subscriber number. Don't send to the API.", '');
                return;
            }

            my $num  = $event->{'ConnectedLineNum'};
            my $name = $event->{'ConnectedLineName'};
            if ($num eq $name) {
                $name = undef;
            }

            my $data = {
                'api'       => $zusapikey,
                'cat'       => 'calls',
                'subcat'    => 'opercall',
                'phoneno'   => $num,
                'caller_id_name' => $name,
                'date'      => now(),
                'ipphoneno' => $channel,
                'aster_id'  => $event->{'Uniqueid'}
            };

            curlPOST($data);
        }

        # CALLS-ANSWER
        if ($event->{'Event'} eq "Bridge" && $event->{'Bridgestate'} eq "Link") {

            loger("Switching bgidge: Uniqueid1:".$event->{'Uniqueid1'}." Uniqueid2:".$event->{'Uniqueid2'}, '');

            my $data = {
              'api'       => $zusapikey,
              'cat'       => 'calls',
              'subcat'    => 'answer',
              'date'      => now(),
              'aster_id'  => $event->{'Uniqueid1'},
              'aster_id2' => $event->{'Uniqueid2'}
            };

            curlPOST($data);
        }

    } elsif ($zastertype == 3) {    ## Asterisk 13

        ## INCOMEING
        if (($event->{'Event'} eq "Newchannel" && $event->{'ChannelState'} eq "0" && $contexts{$event->{'Context'}}) ||
            ($event->{'Event'} eq "NewCallerid" && $event->{'ChannelState'} eq "4")) {

            loger("Incoming call from ".$event->{'CallerIDNum'}." UniqueID:".$event->{'Uniqueid'}, '');

            if ($event->{'CallerIDNum'} eq '<unknown>' || $event->{'CallerIDNum'} eq '') {
                loger("Received an empty subscriber number. Don't send to the API.", '');
                return;
            }

            my $num  = $event->{'CallerIDNum'};
            my $name = $event->{'CallerIDName'};
            if ($num eq $name || $name eq "<unknown>") {
                $name = '';
            }

            my $data = {
                'api'      => $zusapikey,
                'cat'      => 'calls',
                'subcat'   => 'add',
                'phoneno'   => $num,
                'caller_id_name' => $name,
                'date'     => now(),
                'aster_id' => $event->{'Uniqueid'}
            };

            curlPOST($data);
        }

        # DIALBEGIN
#        if ($event->{'Event'} eq "DialBegin" && $event->{'ChannelState'} eq "4" && $event->{'DestChannelState'} eq "0") {
        if ($event->{'Event'} eq "DialBegin" && $event->{'DestChannelState'} eq "0") {

            my $dialstring = $event->{'DialString'};

            if($dialstring =~ /\//) {               # if dialstring has a slash
                $dialstring =~ /[A-Z]*\/?([^@]*)/;  # getting channel name = SUBSYSTEM/channel@domain = realy right regexp
                $dialstring = $1;
            }

            loger("Call to operator ".$dialstring." UniqueID:".$event->{'Uniqueid'}, '');

            my $num  = $event->{'CallerIDNum'};
            my $name = $event->{'CallerIDName'};
            if ($num eq $name) {
                $name = '';
            }

            my $data = {
                'api'       => $zusapikey,
                'cat'       => 'calls',
                'subcat'    => 'opercall',
                'phoneno'   => $num,
                'caller_id_name' => $name,
                'date'      => now(),
                'ipphoneno' => $dialstring,
                'aster_id'  => $event->{'Uniqueid'}
            };

            curlPOST($data);
        }

        # BRIDGEENTER
        if ($event->{'Event'} eq "BridgeEnter" && $event->{'ChannelState'} eq "6" && $event->{'BridgeNumChannels'} eq "1") {

            my $uniqued = $event->{'Uniqueid'};
            my $linkedid = $event->{'Linkedid'};
            # if Linkedid not defined, Linkedid = Uniqued
            if ($linkedid eq "") {
                $linkedid = $uniqued;
            }

            my $connected = $event->{'ConnectedLineName'};
            if ($connected eq "" || $connected eq "<unknown>") {
                $connected = $event->{'ConnectedLineNum'};
            }

            loger("Switching bgidge: Uniqueid:".$uniqued." Linkedid:".$linkedid, '');

            my $data = {
              'api'       => $zusapikey,
              'cat'       => 'calls',
              'subcat'    => 'answer',
              'date'      => now(),
              'aster_id'  => $uniqued,
              'aster_id2' => $linkedid,
              'connected_line' => $connected
            };

            curlPOST($data);
        }

    } elsif ($zastertype == 2) {    ## Asterisk 10

        # CALLS-ADD and CALLS-OPERCALL
        if ($event->{'Event'} eq "Dial" && $event->{'SubEvent'} eq "Begin") {

            loger("Incoming call from ".$event->{'CallerIDNum'}." UniqueID:".$event->{'UniqueID'}, '');

            if ($event->{'CallerIDNum'} eq '<unknown>' || $event->{'CallerIDNum'} eq '') {
                loger("Received an empty subscriber number. Don't send to the API.", '');
                return;
            }

            my $num  = $event->{'CallerIDNum'};
            my $name = $event->{'CallerIDName'};
            if ($num eq $name) {
                $name = '';
            }

            my $data = {
                'api'      => $zusapikey,
                'cat'      => 'calls',
                'subcat'   => 'add',
                'phoneno'   => $num,
                'caller_id_name' => $name,
                'date'     => now(),
                'aster_id' => $event->{'UniqueID'}
            };

            curlPOST($data);

            loger("Call to operator ".$event->{'Dialstring'}." UniqueID:".$event->{'DestUniqueID'}, '');

            $num  = $event->{'CallerIDNum'};
            $name = $event->{'CallerIDName'};
            if ($num eq $name) {
                $name = '';
            }

            $data = {
                'api'       => $zusapikey,
                'cat'       => 'calls',
                'subcat'    => 'opercall',
                'phoneno'   => $num,
                'caller_id_name' => $name,
                'date'      => now(),
                'ipphoneno' => $event->{'Dialstring'},
                'aster_id'  => $event->{'DestUniqueID'}
            };

            curlPOST($data);
        }

        # CALLS-ANSWER
        if ($event->{'Event'} eq "Bridge" && $event->{'Bridgestate'} eq "Link") {

            loger("Switching bgidge: Uniqueid1:".$event->{'Uniqueid1'}." Uniqueid2:".$event->{'Uniqueid2'}, '');

            my $data = {
              'api'       => $zusapikey,
              'cat'       => 'calls',
              'subcat'    => 'answer',
              'date'      => now(),
              'aster_id'  => $event->{'Uniqueid1'},
              'aster_id2' => $event->{'Uniqueid2'}
            };

            curlPOST($data);
        }

    } else {    ## Default Asterisk 11
        # CALLS-ADD
        if (($event->{'Event'} eq "Newchannel" || $event->{'Event'} eq "Newstate") && $event->{'ChannelState'} == 4) {

            loger("Incoming call from: ".$event->{'CallerIDNum'}." Uniqueid:".$event->{'Uniqueid'}, '');

            if ($event->{'CallerIDNum'} eq '<unknown>' || $event->{'CallerIDNum'} eq '') {
                loger("Received an empty subscriber number. Don't send to the API.", '');
                return;
            }

            my $num  = $event->{'CallerIDNum'};
            my $name = $event->{'CallerIDName'};
            if ($num eq $name) {
                $name = '';
            }

            my $data = {
                'api'      => $zusapikey,
                'cat'      => 'calls',
                'subcat'   => 'add',
                'phoneno'   => $num,
                'caller_id_name' => $name,
                'date'     => now(),
                'aster_id' => $event->{'Uniqueid'}
            };

            curlPOST($data);
        }

        # CALLS-OPERCALL
        if ($event->{'Event'} eq "Newstate" && $event->{'ChannelState'} == 5) {

            my $ipphoneno = $event->{'CallerIDNum'};
            if ($ipphoneno eq '') {
                $ipphoneno = $event->{'Channel'};
                $ipphoneno =~ /\D*\/(\d*)[-@]\d*/;
                $ipphoneno = $1;
            }

            loger("Call to operator: ".$ipphoneno." From:".$event->{'ConnectedLineNum'}." Uniqueid:".$event->{'Uniqueid'}, '');

            if ($event->{'ConnectedLineNum'} eq '<unknown>' || $event->{'ConnectedLineNum'} eq '') {
                loger("Received an empty subscriber number. Don't send to the API.", '');
                return;
            }

            my $num  = $event->{'ConnectedLineNum'};
            my $name = $event->{'ConnectedLineName'};
            if ($num eq $name) {
                $name = '';
            }

            my $data = {
                'api'       => $zusapikey,
                'cat'       => 'calls',
                'subcat'    => 'opercall',
                'phoneno'   => $num,
                'caller_id_name' => $name,
                'date'      => now(),
                'ipphoneno' => $ipphoneno,
                'aster_id'  => $event->{'Uniqueid'}
            };

            curlPOST($data);
        }

        # CALLS-ANSWER
        if ($event->{'Event'} eq "Bridge" && $event->{'Bridgestate'} eq "Link") {

            loger("Switching bgidge: Uniqueid1:".$event->{'Uniqueid1'}." Uniqueid2:".$event->{'Uniqueid2'}, '');

            my $data = {
              'api'       => $zusapikey,
              'cat'       => 'calls',
              'subcat'    => 'answer',
              'date'      => now(),
              'aster_id'  => $event->{'Uniqueid1'},
              'aster_id2' => $event->{'Uniqueid2'}
            };

            curlPOST($data);
        }
    }
}

#========================================================
sub now {
  my ($lsec,$lmin,$lhour,$lmday,$lmon,$lyear) = localtime(time);
  my $datetime = sprintf("%04d",($lyear+1900))."-".sprintf("%02d",($lmon+1))."-".sprintf("%02d",$lmday)." ".sprintf("%02d",$lhour).":".sprintf("%02d",$lmin).":".sprintf("%02d",$lsec);
  return $datetime;
}

#========================================================
sub curlPOST {
  my $data     = $_[0];
  $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
  my $ua       = LWP::UserAgent->new();
  my $response = $ua->post(our $zusapiurl, $data);
  my $content  = $response->decoded_content();
  if (our $ps_apianswlog) {
    loger ("FROM API: ".$content, "ans", '');
  }
}

#========================================================
sub send_heartbeat {
  my $data = {
    'api'     => $zusapikey,
    'cat'     => 'module',
    'subcat'  => 'heartbeat',
    'module'  => 'usm_asterisk',
    'version' => $zver,
    'logpath' => $ps_logpath,
    'path'    => $zpath
  };
  curlPOST($data);
}

#========================================================
sub loger {
  my $text = $_[0];
  my $type = $_[1];
  if ($type eq "err") {
    print STDERR now() . " [$$] : " . $text . "\n";       # Вывод на stderr
  }
  if (our $ps_consolelog) {
    if ($type eq "ans") {
      $text = "==> " . $text;
    } else {
      $text = "<== " . $text;
    }
    print now() . "|" . $$ . "|" . $zrevision . "|" . $text . "\n";  # Вывод на stdout
  }
  open (LOG, '>>' . $ps_logpath . '/usm_asterisk.log');
  print LOG now() . " [$$] : " . $text . "\n";            # Вывод в логфайл
  close LOG;
}

EV::loop;
