543 lines
21 KiB
543 lines
21 KiB
package NGCP::Panel::Controller::CallRouting;
|
|
use NGCP::Panel::Utils::Generic qw(:all);
|
|
use Sipwise::Base;
|
|
|
|
use parent 'Catalyst::Controller';
|
|
|
|
use NGCP::Panel::Form;
|
|
|
|
use NGCP::Panel::Utils::Navigation;
|
|
use NGCP::Panel::Utils::Subscriber;
|
|
use NGCP::Panel::Utils::Peering;
|
|
|
|
use Sys::Hostname;
|
|
|
|
sub auto :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) {
|
|
my ($self, $c) = @_;
|
|
$c->log->debug(__PACKAGE__ . '::auto');
|
|
NGCP::Panel::Utils::Navigation::check_redirect_chain(c => $c);
|
|
return 1;
|
|
}
|
|
|
|
sub root :PathPart('/') :CaptureArgs(0) {
|
|
my ( $self, $c ) = @_;
|
|
}
|
|
|
|
sub callroutingverify :Chained('/') :PathPart('callroutingverify') :Args(0) {
|
|
my ( $self, $c ) = @_;
|
|
|
|
my $form = NGCP::Panel::Form::get("NGCP::Panel::Form::CallRouting::Verify", $c);
|
|
my $params = merge({}, $c->session->{created_objects});
|
|
my $posted = ($c->req->method eq 'POST');
|
|
my $data = $c->req->params;
|
|
my @log;
|
|
|
|
$form->process(
|
|
posted => $posted,
|
|
params => $c->request->params,
|
|
item => $params,
|
|
);
|
|
|
|
unless ($posted && $form->validated) {
|
|
$c->stash(
|
|
template => 'callrouting/verify.tt',
|
|
form => $form,
|
|
);
|
|
return;
|
|
}
|
|
|
|
# TODO: relocate the logic to a common module that can be centralised and
|
|
# reused by other components
|
|
|
|
# caller/callee general parsing
|
|
# remove leading/trailing spaces
|
|
# save the domain part
|
|
foreach my $type (qw(caller callee)) {
|
|
$data->{$type} =~ s/(^\s+|\s+$)//g;
|
|
$data->{$type} =~ s/^sip://;
|
|
if ($data->{$type} =~ s/\@(.+)$//) {
|
|
$data->{$type.'_domain'} = $1;
|
|
}
|
|
}
|
|
|
|
# caller lookup
|
|
if ($data->{caller_subscriber_id}) {
|
|
my $rs = $c->model('DB')->resultset('voip_subscribers')->search({
|
|
id => $data->{caller_subscriber_id},
|
|
});
|
|
unless ($rs->first) {
|
|
push @log, sprintf "no caller subscriber found with id %d",
|
|
$data->{caller_subscriber_id};
|
|
goto RESULT;
|
|
}
|
|
$data->{caller_subscriber} = $rs->first;
|
|
} elsif ($data->{caller_peer_id}) {
|
|
my $rs = $c->model('DB')->resultset('voip_peer_groups')->search({
|
|
id => $data->{caller_peer_id},
|
|
});
|
|
unless ($rs->first) {
|
|
push @log, sprintf "no caller peer found with id %d",
|
|
$data->{caller_peer_id};
|
|
goto RESULT;
|
|
}
|
|
$data->{caller_peer} = $rs->first;
|
|
|
|
unless ($data->{caller_peer}->voip_peer_hosts->first) {
|
|
push @log, sprintf "caller peer with id %d does not contain any peer hosts",
|
|
$rs->id;
|
|
}
|
|
$data->{caller_peer_host} = $data->{caller_peer}->voip_peer_hosts->first;
|
|
} else {
|
|
my $caller_uri = $data->{caller_domain}
|
|
? 'sip:'.$data->{caller}.'@'.$data->{caller_domain}
|
|
: $data->{caller};
|
|
push @log, sprintf "no caller subscriber/peer was specified, using subscriber lookup based on caller %s",
|
|
$caller_uri;
|
|
$data->{caller_subscriber} =
|
|
NGCP::Panel::Utils::Subscriber::lookup(
|
|
c => $c,
|
|
lookup => $caller_uri
|
|
);
|
|
}
|
|
if ($data->{caller_subscriber}) {
|
|
$data->{caller_subscriber_id} = $data->{caller_subscriber}->id;
|
|
my $caller_uri = sprintf 'sip:%s@%s',
|
|
$data->{caller_subscriber}->username,
|
|
$data->{caller_subscriber}->domain->domain;
|
|
$data->{caller_domain} = $data->{caller_subscriber}->domain->domain;
|
|
push @log, sprintf "found caller subscriber '%s' with id %d",
|
|
$caller_uri, $data->{caller_subscriber_id};
|
|
} else {
|
|
push @log, sprintf "no caller subscriber found.";
|
|
goto RESULT;
|
|
|
|
}
|
|
|
|
# caller sum up
|
|
push @log, sprintf "call from %s", $data->{caller};
|
|
$log[-1] .= $data->{caller_subscriber}
|
|
? sprintf " using subscriber '%s\@%s' id %s",
|
|
$data->{caller_subscriber}->username,
|
|
$data->{caller_subscriber}->domain->domain,
|
|
$data->{caller_subscriber}->id
|
|
: sprintf " using peer '%s' id %s",
|
|
$data->{caller_peer}->name,
|
|
$data->{caller_peer}->id;
|
|
if ($data->{caller_peer_host}) {
|
|
$log[-1] .= sprintf " and peer host %s (ip: %s) with id %d",
|
|
$data->{caller_peer_host}->name,
|
|
$data->{caller_peer_host}->ip,
|
|
$data->{caller_peer_host}->id;
|
|
}
|
|
|
|
# caller inbound rewrite rules lookup
|
|
if ($data->{caller_rewrite_id}) {
|
|
my $rs = $c->model('DB')->resultset('voip_rewrite_rule_sets')->search({
|
|
id => $data->{caller_rewrite_id},
|
|
});
|
|
if ($rs->first) {
|
|
push @log, sprintf "using caller rewrite rule set '%s' with id %d",
|
|
$rs->first->name, $rs->first->id;
|
|
$data->{caller_rewrite} = $rs->first;
|
|
}
|
|
} else {
|
|
my ($lookup_rws, $lookup_rws_type, $rws_rs);
|
|
if ($data->{'caller_subscriber_id'}) {
|
|
my $rs = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(
|
|
c => $c, attribute => "rewrite_caller_in_dpid",
|
|
prov_subscriber =>
|
|
$data->{caller_subscriber}->provisioning_voip_subscriber,
|
|
);
|
|
if ($rs->first) {
|
|
$rws_rs = $c->model('DB')->resultset('voip_rewrite_rule_sets')->search({
|
|
caller_in_dpid => $rs->first->value
|
|
});
|
|
}
|
|
if ($rws_rs && $rws_rs->first) {
|
|
$lookup_rws = $rws_rs->first;
|
|
$lookup_rws_type = 'subscriber';
|
|
} else {
|
|
$rs = NGCP::Panel::Utils::Preferences::get_dom_preference_rs(
|
|
c => $c, attribute => "rewrite_caller_in_dpid",
|
|
prov_domain =>
|
|
$data->{caller_subscriber}->provisioning_voip_subscriber->domain,
|
|
);
|
|
if ($rs->first) {
|
|
$rws_rs = $c->model('DB')->resultset('voip_rewrite_rule_sets')->search({
|
|
caller_in_dpid => $rs->first->value
|
|
});
|
|
}
|
|
if ($rws_rs && $rws_rs->first) {
|
|
$lookup_rws = $rws_rs->first;
|
|
$lookup_rws_type = 'domain';
|
|
} else {
|
|
push @log, sprintf "no caller subscriber/domain rewrite rule sets were found";
|
|
}
|
|
}
|
|
} elsif ($data->{caller_peer_id}) {
|
|
my $rs = NGCP::Panel::Utils::Preferences::get_peer_preference_rs(
|
|
c => $c, attribute => "rewrite_caller_in_dpid",
|
|
peer_host => $data->{caller_peer_host},
|
|
);
|
|
if ($rs->first) {
|
|
$rws_rs = $c->model('DB')->resultset('voip_rewrite_rule_sets')->search({
|
|
caller_in_dpid => $rs->first->value
|
|
});
|
|
}
|
|
if ($rws_rs && $rws_rs->first) {
|
|
$lookup_rws = $rws_rs->first;
|
|
$lookup_rws_type = 'peer';
|
|
} else {
|
|
push @log, sprintf "no caller peer rewrite rule sets with were found";
|
|
}
|
|
}
|
|
|
|
if ($lookup_rws) {
|
|
push @log, sprintf "using caller %s inbound rewrite rule set '%s' with id %d",
|
|
$lookup_rws_type, $lookup_rws->name, $lookup_rws->id;
|
|
$data->{caller_rewrite} = $lookup_rws;
|
|
}
|
|
}
|
|
|
|
# apply inbound rewrite rules
|
|
foreach my $type (qw(caller callee)) {
|
|
$data->{$type.'_in'} = $data->{$type};
|
|
next unless $data->{caller_rewrite};
|
|
my $new;
|
|
if ($data->{caller_subscriber_id}) {
|
|
$new =
|
|
NGCP::Panel::Utils::Subscriber::apply_rewrite(
|
|
c => $c, subscriber => $data->{caller_subscriber},
|
|
direction => $type.'_in',
|
|
number => $data->{$type},
|
|
rws_id => $data->{caller_rewrite}->id,
|
|
);
|
|
} elsif ($data->{caller_peer_id}) {
|
|
$new =
|
|
NGCP::Panel::Utils::Peering::apply_rewrite(
|
|
c => $c, peer_host => $data->{caller_peer_host},
|
|
direction => $type.'_in',
|
|
number => $data->{$type},
|
|
rws_id => $data->{caller_rewrite}->id,
|
|
);
|
|
}
|
|
if ($new && $new ne $data->{$type}) {
|
|
push @log, sprintf "%s %s is rewritten based on the inbound rules into %s",
|
|
$type, $data->{$type}, $new;
|
|
}
|
|
$data->{$type.'_in'} = $new || $data->{$type};
|
|
}
|
|
|
|
# subscriber allowed_cli checks
|
|
if ($data->{caller_subscriber}) {
|
|
my %usr_prefs;
|
|
foreach my $pref (qw(allowed_clis allowed_clis_reject_policy cli user_cli)) {
|
|
my $rs = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(
|
|
c => $c, attribute => $pref,
|
|
prov_subscriber =>
|
|
$data->{caller_subscriber}->provisioning_voip_subscriber,
|
|
);
|
|
if ($rs->first) {
|
|
@{$usr_prefs{$pref}} = map { $_->value } $rs->all;
|
|
} else {
|
|
$rs = NGCP::Panel::Utils::Preferences::get_dom_preference_rs(
|
|
c => $c, attribute => $pref,
|
|
prov_domain =>
|
|
$data->{caller_subscriber}->provisioning_voip_subscriber->domain,
|
|
);
|
|
if ($rs->first) {
|
|
@{$usr_prefs{$pref}} = map { $_->value } $rs->all;
|
|
}
|
|
}
|
|
}
|
|
my $match = map { my $val = s/\*/.*/gr;
|
|
$val =~ s/\?/.?/g;
|
|
$data->{caller_in} =~ /^$val$/;
|
|
} @{$usr_prefs{allowed_clis}};
|
|
if ($match) {
|
|
push @log, sprintf
|
|
"caller %s is accepted as it matches subscriber's 'allowed_clis'",
|
|
$data->{caller_in};
|
|
} else {
|
|
push @log, sprintf
|
|
"caller %s is rejected as it does not match subscriber's 'allowed_clis'",
|
|
$data->{caller_in};
|
|
if (defined $usr_prefs{allowed_clis_reject_policy}) {
|
|
SWITCH: for ($usr_prefs{allowed_clis_reject_policy}[0]) {
|
|
/^override_by_clir$/ && do {
|
|
push @log,
|
|
"'allowed_cli' reject policy is 'override_by_clir', anonymising caller";
|
|
$data->{caller_in} = 'anonymous';
|
|
last SWITCH;
|
|
};
|
|
/^override_by_usernpn$/ && do {
|
|
push @log,
|
|
"'allowed_cli' reject policy is 'override_by_usernpn'";
|
|
foreach my $cli (qw(user_cli cli)) {
|
|
if (defined $usr_prefs{$cli}) {
|
|
$data->{caller_in} = $usr_prefs{$cli}[0];
|
|
$log[-1] .= sprintf ", taken from '$cli' %s",
|
|
$usr_prefs{$cli}[0];
|
|
last;
|
|
}
|
|
}
|
|
last SWITCH;
|
|
};
|
|
/^reject$/ && do {
|
|
push @log,
|
|
"'allowed_cli' reject policy is 'reject', terminating the call";
|
|
goto RESULT;
|
|
last SWITCH;
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# callee lookup
|
|
if ($data->{callee_peer_id}) {
|
|
my $rs = $c->model('DB')->resultset('voip_peer_groups')->search({
|
|
id => $data->{callee_peer_id},
|
|
});
|
|
unless ($rs->first) {
|
|
push @log, sprintf "no callee peer found with id %d",
|
|
$data->{callee_peer_id};
|
|
goto RESULT;
|
|
}
|
|
$data->{callee_peer} = $rs->first;
|
|
|
|
unless ($data->{callee_peer}->voip_peer_hosts->first) {
|
|
push @log, sprintf "callee peer with id %d does not contain any peer hosts",
|
|
$rs->id;
|
|
}
|
|
$data->{callee_peer_host} = $data->{callee_peer}->voip_peer_hosts->first;
|
|
} else {
|
|
push @log, sprintf "callee subscriber lookup based on %s",
|
|
$data->{callee_in};
|
|
$data->{callee_subscriber} =
|
|
NGCP::Panel::Utils::Subscriber::lookup(
|
|
c => $c,
|
|
lookup => $data->{callee_in},
|
|
);
|
|
if ($data->{callee_subscriber}) {
|
|
$data->{callee_subscriber_id} = $data->{callee_subscriber}->id;
|
|
my $sub = sprintf '%s@%s',
|
|
$data->{callee_subscriber}->username,
|
|
$data->{callee_subscriber}->domain->domain;
|
|
push @log, sprintf "found callee subscriber '%s' with id %d",
|
|
$sub, $data->{callee_subscriber_id};
|
|
} else {
|
|
foreach my $type (qw(caller callee)) {
|
|
$data->{$type.'_uri'} = 'sip:'.$data->{$type.'_in'};
|
|
if ($data->{$type.'_domain'}) {
|
|
$data->{$type.'_uri'} .= '@' . $data->{$type.'_domain'};
|
|
} elsif ($type eq 'callee' && $data->{caller_domain}) {
|
|
$data->{$type.'_uri'} .= '@' . $data->{caller_domain};
|
|
}
|
|
}
|
|
push @log,
|
|
sprintf "no callee subscriber found, performing peer lookup with caller uri %s and callee uri %s and callee %s",
|
|
@{$data}{qw(caller_uri callee_uri callee_in)};
|
|
$data->{callee_peers} =
|
|
NGCP::Panel::Utils::Peering::lookup(
|
|
c => $c,
|
|
prefix => $data->{callee_in},
|
|
caller => $data->{caller_uri},
|
|
callee => $data->{callee_uri},
|
|
);
|
|
unless ($data->{callee_peers} && scalar @{$data->{callee_peers}}) {
|
|
push @log, sprintf "no callee peers found";
|
|
goto RESULT;
|
|
}
|
|
# as we cannot check peer reply codes here, use first peer for now
|
|
$data->{callee_peer} = $data->{callee_peers}->[0];
|
|
$data->{callee_peer_id} = $data->{callee_peer}->id;
|
|
|
|
push @log, sprintf "matched peer '%s' with id %d",
|
|
$data->{callee_peer}->name, $data->{callee_peer}->id;
|
|
|
|
unless ($data->{callee_peer}->voip_peer_hosts->first) {
|
|
push @log, sprintf "callee peer with id %d does not contain any peer hosts",
|
|
$data->{callee_peer}->id;
|
|
}
|
|
$data->{callee_peer_host} = $data->{callee_peer}->voip_peer_hosts->first;
|
|
}
|
|
}
|
|
|
|
# callee sum up
|
|
push @log, sprintf "call to %s", $data->{callee};
|
|
if ($data->{callee_subscriber}) {
|
|
$log[-1] .= $data->{callee_subscriber}
|
|
? sprintf ", subscriber '%s\@%s' with id %d",
|
|
$data->{callee_subscriber}->username,
|
|
$data->{callee_subscriber}->domain->domain,
|
|
$data->{callee_subscriber}->id
|
|
: sprintf ", peer %s id %s",
|
|
$data->{callee_peer}->name,
|
|
$data->{callee_peer}->id;
|
|
} elsif ($data->{callee_peer_host}) {
|
|
$log[-1] .= sprintf " and peer host '%s' (ip: %s) with id %d",
|
|
$data->{callee_peer_host}->name,
|
|
$data->{callee_peer_host}->ip,
|
|
$data->{callee_peer_host}->id;
|
|
} else {
|
|
push @log, "this call does not have any termination point";
|
|
}
|
|
|
|
# callee outbound rewrite rules lookup
|
|
if ($data->{callee_rewrite_id}) {
|
|
my $rs = $c->model('DB')->resultset('voip_rewrite_rule_sets')->search({
|
|
id => $data->{callee_rewrite_id},
|
|
});
|
|
if ($rs->first) {
|
|
push @log, sprintf "using callee rewrite rule set '%s' with id %d",
|
|
$rs->first->name, $rs->first->id;
|
|
$data->{callee_rewrite} = $rs->first;
|
|
}
|
|
} else {
|
|
my ($lookup_rws, $lookup_rws_type, $rws_rs);
|
|
if ($data->{'callee_subscriber_id'}) {
|
|
my $rs = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(
|
|
c => $c, attribute => "rewrite_callee_out_dpid",
|
|
prov_subscriber =>
|
|
$data->{callee_subscriber}->provisioning_voip_subscriber,
|
|
);
|
|
if ($rs->first) {
|
|
$rws_rs = $c->model('DB')->resultset('voip_rewrite_rule_sets')->search({
|
|
callee_out_dpid => $rs->first->value
|
|
});
|
|
}
|
|
if ($rws_rs && $rws_rs->first) {
|
|
$lookup_rws = $rws_rs->first;
|
|
$lookup_rws_type = 'subscriber';
|
|
} else {
|
|
$rs = NGCP::Panel::Utils::Preferences::get_dom_preference_rs(
|
|
c => $c, attribute => "rewrite_callee_out_dpid",
|
|
prov_domain =>
|
|
$data->{callee_subscriber}->provisioning_voip_subscriber->domain,
|
|
);
|
|
if ($rs->first) {
|
|
$rws_rs = $c->model('DB')->resultset('voip_rewrite_rule_sets')->search({
|
|
callee_out_dpid => $rs->first->value
|
|
});
|
|
}
|
|
if ($rws_rs && $rws_rs->first) {
|
|
$lookup_rws = $rws_rs->first;
|
|
$lookup_rws_type = 'domain';
|
|
} else {
|
|
push @log, sprintf "no callee subscriber/domain rewrite rule sets were found";
|
|
}
|
|
}
|
|
} elsif ($data->{callee_peer_id}) {
|
|
my $rs = NGCP::Panel::Utils::Preferences::get_peer_preference_rs(
|
|
c => $c, attribute => "rewrite_callee_out_dpid",
|
|
peer_host => $data->{callee_peer_host},
|
|
);
|
|
if ($rs->first) {
|
|
$rws_rs = $c->model('DB')->resultset('voip_rewrite_rule_sets')->search({
|
|
callee_out_dpid => $rs->first->value
|
|
});
|
|
}
|
|
if ($rws_rs && $rws_rs->first) {
|
|
$lookup_rws = $rws_rs->first;
|
|
$lookup_rws_type = 'peer';
|
|
} else {
|
|
push @log, sprintf "no callee peer rewrite rule sets with were found";
|
|
}
|
|
}
|
|
|
|
if ($lookup_rws) {
|
|
push @log, sprintf "using callee %s outbound rewrite rule set '%s' with id %d",
|
|
$lookup_rws_type, $lookup_rws->name, $lookup_rws->id;
|
|
$data->{callee_rewrite} = $lookup_rws;
|
|
}
|
|
}
|
|
|
|
# apply outbound rewrite rules
|
|
foreach my $type (qw(caller callee)) {
|
|
$data->{$type.'_out'} = $data->{$type.'_in'};
|
|
next unless $data->{callee_rewrite};
|
|
my $new;
|
|
if ($data->{callee_subscriber_id}) {
|
|
$new =
|
|
NGCP::Panel::Utils::Subscriber::apply_rewrite(
|
|
c => $c, subscriber => $data->{callee_subscriber},
|
|
direction => $type.'_out',
|
|
number => $data->{$type.'_in'},
|
|
rws_id => $data->{callee_rewrite}->id,
|
|
);
|
|
} elsif ($data->{callee_peer_id}) {
|
|
$new =
|
|
NGCP::Panel::Utils::Peering::apply_rewrite(
|
|
c => $c, peer_host => $data->{callee_peer_host},
|
|
direction => $type.'_out',
|
|
number => $data->{$type.'_in'},
|
|
rws_id => $data->{callee_rewrite}->id,
|
|
);
|
|
}
|
|
if ($new && $new ne $data->{$type.'_in'}) {
|
|
push @log, sprintf "%s %s is rewritten based on the outbound rules into %s",
|
|
$type, $data->{$type.'_in'}, $new;
|
|
}
|
|
$data->{$type.'_out'} = $new || $data->{$type.'_in'};
|
|
}
|
|
|
|
|
|
RESULT:
|
|
foreach my $type (qw(caller callee)) {
|
|
$data->{$type.'_type'} =
|
|
$data->{$type.'_subscriber'}
|
|
? 'subscriber'
|
|
: $data->{$type.'_peer'} ? 'peer' : 'unknown';
|
|
#foreach my $dir (qw(in out)) {
|
|
#$data->{$type.'_'.$dir} ||= $data->{$type.'_type'} ne 'unknown'
|
|
# ? $data->{$type}
|
|
# : '';
|
|
# fill in a value even if caller/callee is not identified
|
|
#}
|
|
}
|
|
|
|
$c->stash(
|
|
template => 'callrouting/result.tt',
|
|
close_target => '/callroutingverify',
|
|
caller => $data->{caller},
|
|
callee => $data->{callee},
|
|
caller_in => $data->{caller_in},
|
|
callee_in => $data->{callee_in},
|
|
caller_out => $data->{caller_out},
|
|
callee_out => $data->{callee_out},
|
|
caller_type => $data->{caller_type},
|
|
callee_type => $data->{callee_type},
|
|
log => \@log,
|
|
form => undef,
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
1;
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
NGCP::Panel::Controller::CallRouting
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
A helper to manipulate the call routing data
|
|
|
|
=head1 AUTHOR
|
|
|
|
Kirill Solomko <ksolomko@sipwise.com>
|
|
|
|
=head1 LICENSE
|
|
|
|
This library is free software. You can redistribute it and/or modify
|
|
it under the same terms as Perl itself.
|
|
|
|
=cut
|
|
|
|
# vim: set tabstop=4 expandtab:
|