From 5ca51f930aec60a3fa9fed11531285be85e9e8aa Mon Sep 17 00:00:00 2001 From: Andreas Granig Date: Mon, 16 Jan 2017 14:00:34 +0100 Subject: [PATCH] TT#2371 Implement 3pcc for sms If pcc is enabled for a subscriber, don't forward sms immediately, rather than mark for forwarding and let the API forward it on incoming request. Change-Id: I75104266a1c1fccc7165af9ba65b31f085d7081f --- .../Panel/Controller/API/PartyCallControls.pm | 73 ++++++++++++++++- lib/NGCP/Panel/Controller/InternalSms.pm | 82 ++++++++++++++++--- lib/NGCP/Panel/Utils/PartyCallControl.pm | 44 ++++++++++ lib/NGCP/Panel/Utils/Sems.pm | 2 +- 4 files changed, 185 insertions(+), 16 deletions(-) create mode 100644 lib/NGCP/Panel/Utils/PartyCallControl.pm diff --git a/lib/NGCP/Panel/Controller/API/PartyCallControls.pm b/lib/NGCP/Panel/Controller/API/PartyCallControls.pm index 9bdd83085a..6b91432a3b 100644 --- a/lib/NGCP/Panel/Controller/API/PartyCallControls.pm +++ b/lib/NGCP/Panel/Controller/API/PartyCallControls.pm @@ -14,6 +14,7 @@ use Path::Tiny qw(path); use Safe::Isa qw($_isa); use NGCP::Panel::Utils::Sems; +use NGCP::Panel::Utils::SMS; require Catalyst::ActionRole::ACL; require Catalyst::ActionRole::CheckTrailingSlash; @@ -25,7 +26,7 @@ sub allowed_methods{ } sub api_description { - return 'Allows to place calls via the API.'; + return 'Allows to control queued calls and sms via the API.'; }; sub query_params { @@ -42,7 +43,7 @@ sub dispatch_path{ return '/api/partycallcontrols/'; } sub relation{ - return 'http://purl.org/sipwise/ngcp-api/#rel-callcontrols'; + return 'http://purl.org/sipwise/ngcp-api/#rel-partycallcontrols'; } __PACKAGE__->config( @@ -104,9 +105,73 @@ sub POST :Allow { "Failed to handle a party call control request."); last; } + } elsif ($resource->{type} eq "sms") { + my $error_msg; + my $callid = $resource->{callid}; + my $status = $resource->{status}; + my $token = $resource->{token}; + my $sms; + try { + if($c->user->roles eq "reseller") { + my $sms_rs = $c->model('DB')->resultset('sms_journal')->search({ + 'me.id' => $callid, + 'pcc_status' => 'pending', + 'contact.reseller_id' => $c->user->reseller_id, + },{ + join => { 'provisioning_voip_subscriber' => { 'voip_subscriber' => { 'contract' => 'contact' } } } + }); + $sms = $sms_rs->first; + } else { + $sms = $c->model('DB')->resultset('sms_journal')->search({ + 'me.id' => $callid, + 'pcc_token' => $token, + 'pcc_status' => 'pending', + })->first; + } + } catch($e) { + $c->log->error("failed to handle a party call control request: $e"); + $self->error($c, HTTP_INTERNAL_SERVER_ERROR, + "Failed to handle a party call control request."); + last; + } + unless($sms) { + $c->log->error("failed to find sms with id $callid and token $token"); + $self->error($c, HTTP_UNPROCESSABLE_ENTITY, + "Failed to find sms with callid $callid and given token"); + last; + } + if($status eq "ACCEPT") { + $c->log->info("status for pcc sms of $callid is $status, forward sms"); + try { + NGCP::Panel::Utils::SMS::send_sms( + c => $c, + caller => $sms->caller, + callee => $sms->callee, + text => $sms->text, + coding => $sms->coding, + err_code => sub {$error_msg = shift;}, + ); + $sms->update({ pcc_status => "complete" }); + } catch($e) { + $c->log->error("failed to handle a party call control request: $e"); + $self->error($c, HTTP_INTERNAL_SERVER_ERROR, + "Failed to handle a party call control request."); + last; + } + } else { + $c->log->info("status for pcc sms of $callid is $status, don't forward sms"); + try { + $sms->update({ pcc_status => "complete" }); + } catch($e) { + $c->log->error("failed to handle a party call control request: $e"); + $self->error($c, HTTP_INTERNAL_SERVER_ERROR, + "Failed to handle a party call control request."); + last; + } + } } else { - $self->error($c, HTTP_INTERNAL_SERVER_ERROR, - "Failed to handle a party call control request of unknown type."); + $self->error($c, HTTP_UNPROCESSABLE_ENTITY, + "Invalid party call control type, must be 'pcc' or 'sms'."); last; } diff --git a/lib/NGCP/Panel/Controller/InternalSms.pm b/lib/NGCP/Panel/Controller/InternalSms.pm index a20168145f..959b5accb4 100644 --- a/lib/NGCP/Panel/Controller/InternalSms.pm +++ b/lib/NGCP/Panel/Controller/InternalSms.pm @@ -6,6 +6,11 @@ use parent 'Catalyst::Controller'; use Time::Period; use File::FnMatch qw(:fnmatch); use Encode qw/decode/; +use UUID; + +use NGCP::Panel::Utils::Preferences; +use NGCP::Panel::Utils::PartyCallControl; +use NGCP::Panel::Utils::SMS; #sub auto :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) { sub auto { @@ -67,12 +72,38 @@ sub receive :Chained('list') :PathPart('receive') :Args(0) { die "no_subscriber_found"; } + my $pcc_url = $c->config->{pcc}->{url}; + my $pcc_timeout = $c->config->{pcc}->{timeout}; + my $pcc_enabled = 0; + my ($pcc_uuid, $pcc_token); + UUID::generate($pcc_uuid); + UUID::unparse($pcc_uuid, $pcc_token); + my $fwd_pref_rs = NGCP::Panel::Utils::Preferences::get_usr_preference_rs( + c => $c, attribute => 'party_call_control', + prov_subscriber => $prov_dbalias->subscriber, + ); + if($fwd_pref_rs && $fwd_pref_rs->first && $fwd_pref_rs->first->value) { + $pcc_enabled = 1; + } else { + $fwd_pref_rs = NGCP::Panel::Utils::Preferences::get_dom_preference_rs( + c => $c, attribute => 'party_call_control', + prov_domain => $prov_dbalias->domain, + ); + if($fwd_pref_rs && $fwd_pref_rs->first && $fwd_pref_rs->first->value) { + $pcc_enabled = 1; + } + } + $c->log->info("pcc is set to $pcc_enabled for prov subscriber id " . $prov_dbalias->subscriber_id); + my $created_item = $c->model('DB')->resultset('sms_journal')->create({ subscriber_id => $prov_dbalias->subscriber_id, direction => "in", caller => $from, callee => $to, text => $text, + pcc_status => "none", + pcc_token => $pcc_token, + coding => $coding, }); # check for cfs @@ -154,27 +185,56 @@ sub receive :Chained('list') :PathPart('receive') :Args(0) { $dst =~ s/^sip:(.+)\@.+$/$1/; $c->log->info(">>>> forward sms to $dst"); - # feed back into kannel - my $error_msg; - NGCP::Panel::Utils::SMS::send_sms( - c => $c, - caller => $to, # use the original to as new from - callee => $dst, - text => $text, - coding => $coding, - err_code => sub {$error_msg = shift;}, - ); + my $pcc_status = $pcc_enabled ? "pending" : "none"; my $fwd_item = $c->model('DB')->resultset('sms_journal')->create({ subscriber_id => $prov_dbalias->subscriber_id, direction => "forward", caller => $to, callee => $dst, text => $text, + pcc_status => $pcc_status, + pcc_token => $pcc_token, + coding => $coding, }); + + if($pcc_enabled && $pcc_url) { + try { + my $ret = NGCP::Panel::Utils::PartyCallControl::dispatch( + c => $c, + url => $pcc_url, + timeout => $pcc_timeout, + id => $fwd_item->id, + from => $from, + to => $to, + type => "sms", + text => $text, + token => $pcc_token, + ); + unless($ret) { + $c->log->error("failed to dispatch pcc request"); + $fwd_item->update({ pcc_status => "failed" }); + } + } catch($e) { + $c->log->error("failed to dispatch pcc request: $e"); + $fwd_item->update({ pcc_status => "failed" }); + } + } else { + # no 3rd party call control, feed back into kannel + my $error_msg; + NGCP::Panel::Utils::SMS::send_sms( + c => $c, + caller => $to, # use the original to as new from + callee => $dst, + text => $text, + coding => $coding, + err_code => sub {$error_msg = shift;}, + ); + } + } }); } catch($e) { - $c->log->error("Failed to store received SMS message."); + $c->log->error("Failed to handle received SMS message."); $c->log->debug($e); } diff --git a/lib/NGCP/Panel/Utils/PartyCallControl.pm b/lib/NGCP/Panel/Utils/PartyCallControl.pm new file mode 100644 index 0000000000..6aaf8b6b4e --- /dev/null +++ b/lib/NGCP/Panel/Utils/PartyCallControl.pm @@ -0,0 +1,44 @@ +package NGCP::Panel::Utils::PartyCallControl; + +use Sipwise::Base; +use LWP::UserAgent; +use URI; +use JSON; + +sub dispatch { + my (%args) = @_; + my $c = $args{c}; + my $url = $args{url}; + my $timeout = $args{timeout}; + my $id = $args{id}; + my $type = $args{type}; + my $from = $args{from}; + my $to = $args{to}; + my $text = $args{text}; + my $token = $args{token}; + + # TODO: dispatch asynchronously! + my $ua = LWP::UserAgent->new( + #ssl_opts => { verify_hostname => 0, SSL_verify_mode => 0 }, + timeout => $timeout, + ); + $c->log->info("sending pcc request for $type with id $id to $url"); + my $req = HTTP::Request->new(POST => $url); + $req->header('Content-Type' => 'application/json'); + $req->content(to_json({ + actualMsisdn => $to, + callingMsisdn => $from, + callid => $id, + type => $type, + token => $token, + $type eq "sms" ? (text => $text) : (), + })); + my $res = $ua->request($req); + $c->log->info("sending pcc request " . ($res->is_success ? "succeeded" : "failed")); + if ($res->is_success) { + return 1; + } + return; +} + +1; diff --git a/lib/NGCP/Panel/Utils/Sems.pm b/lib/NGCP/Panel/Utils/Sems.pm index 1841d15889..3271b9c23b 100644 --- a/lib/NGCP/Panel/Utils/Sems.pm +++ b/lib/NGCP/Panel/Utils/Sems.pm @@ -322,7 +322,7 @@ sub party_call_control { postDSMEvent - pcc_$call_id + pcc_$callid