TT#2369 API for sending SMS and journal

Change-Id: Ia1e09771bc14cd9e8f240de5b86f17563525a565
changes/28/9228/4
Gerhard Jungwirth 9 years ago committed by Andreas Granig
parent 786d934459
commit de239d60eb

2
debian/control vendored

@ -86,10 +86,12 @@ Depends: gettext,
libtemplate-plugin-json-escape-perl,
libtest-mockobject-perl,
libtext-csv-xs-perl,
libtext-glob-perl,
libtext-table-perl,
libtime-warp-perl,
libtypes-path-tiny-perl,
liburi-encode-perl,
liburi-perl,
libuuid-perl,
libxml-mini-perl,
libxml-xpath-perl,

@ -0,0 +1,51 @@
package NGCP::Panel::Controller::API::SMS;
use Sipwise::Base;
use parent qw/NGCP::Panel::Role::Entities NGCP::Panel::Role::API::SMS/;
use HTTP::Status qw(:constants);
__PACKAGE__->set_config();
sub allowed_methods{
return [qw/GET POST OPTIONS HEAD/];
}
sub api_description {
return 'Shows a journal of sent and received messages. New messages can be sent by issuing a POST request to the api collection.';
}
# sub query_params {
# return [
# ];
# }
sub create_item {
my ($self, $c, $resource, $form, $process_extras) = @_;
my $error_msg = "";
NGCP::Panel::Utils::SMS::send_sms(
c => $c,
caller => $resource->{caller},
callee => $resource->{callee},
text => $resource->{text},
err_code => sub {$error_msg = shift;},
);
my $rs = $self->item_rs($c);
my $item = $rs->create({
subscriber_id => $resource->{subscriber_id},
direction => 'out',
caller => $resource->{caller},
callee => $resource->{callee},
text => $resource->{text},
$error_msg ? (status => $error_msg) : (),
});
return $item;
}
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,19 @@
package NGCP::Panel::Controller::API::SMSItem;
use Sipwise::Base;
use parent qw/NGCP::Panel::Role::EntitiesItem NGCP::Panel::Role::API::SMS/;
use HTTP::Status qw(:constants);
__PACKAGE__->set_config();
sub allowed_methods{
return [qw/GET OPTIONS HEAD/];
}
use parent qw/Catalyst::Controller NGCP::Panel::Role::API::CFSourceSets/;
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,114 @@
package NGCP::Panel::Form::SMSAPI;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler';
use HTML::FormHandler::Widget::Block::Bootstrap;
use NGCP::Panel::Field::BillingZone;
has '+widget_wrapper' => ( default => 'Bootstrap' );
has_field 'submitid' => ( type => 'Hidden' );
sub build_render_list {[qw/submitid fields actions/]}
sub build_form_element_class { [qw/form-horizontal/] }
has_field 'id' => (
type => 'Hidden',
element_attr => {
rel => ['tooltip'],
title => ['The internal id in the sms journal'],
},
);
has_field 'subscriber_id' => (
type => 'PosInteger',
required => 1,
element_attr => {
rel => ['tooltip'],
title => ['The subscriber id this journal entry belongs to'],
},
);
has_field 'direction' => (
type => 'Select',
options => [
{ value => 'in', label => 'inbound' },
{ value => 'out', label => 'outbound' },
],
default => 'out', # FYI, default is not considered with API
required => 0, # should be "1" actually, see above
element_attr => {
rel => ['tooltip'],
title => ['Whether the logged message is sent or received'],
},
);
has_field 'caller' => (
type => 'Text',
required => 0,
maxlength => 255,
element_attr => {
rel => ['tooltip'],
title => ['Our CLI in case of sent messages. Must be valid according to the preferences allowed_clis, user_cli, cli'],
},
);
has_field 'callee' => (
type => 'Text',
required => 1,
maxlength => 255,
element_attr => {
rel => ['tooltip'],
title => ['A valid CLI in the E164 format'],
},
);
has_field 'text' => (
type => 'Text',
required => 1,
element_attr => {
rel => ['tooltip'],
title => ['The content of the message'],
},
);
has_field 'status' => (
type => 'Text', # Readonly
required => 0,
maxlength => 255,
element_attr => {
rel => ['tooltip'],
title => ['Whether the message has been sent successfully'],
},
);
has_field 'reason' => (
type => 'Text', # Readonly
required => 0,
maxlength => 255,
element_attr => {
rel => ['tooltip'],
title => ['An error message in case of a failed transmission'],
},
);
has_field 'save' => (
type => 'Submit',
value => 'Save',
element_class => [qw/btn btn-primary/],
label => '',
);
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/id subscriber_id direction caller callee text status reason/],
);
has_block 'actions' => (
tag => 'div',
class => [qw/modal-footer/],
render_list => [qw/save/],
);
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,92 @@
package NGCP::Panel::Role::API::SMS;
use NGCP::Panel::Utils::Generic qw(:all);
use Sipwise::Base;
use parent 'NGCP::Panel::Role::API';
use Data::HAL::Link qw();
use HTTP::Status qw(:constants);
use NGCP::Panel::Utils::SMS;
use NGCP::Panel::Form::SMSAPI;
sub item_name {
return 'sms';
}
sub resource_name{
return 'sms';
}
sub get_form {
my ($self, $c) = @_;
return (NGCP::Panel::Form::SMSAPI->new, ['subscriber_id']);
}
sub hal_links {
my($self, $c, $item, $resource, $form) = @_;
my $b_subs_id = $item->provisioning_voip_subscriber->voip_subscriber->id;
return [
Data::HAL::Link->new(relation => "ngcp:subscribers", href => sprintf("/api/subscribers/%d", $b_subs_id)),
];
}
sub _item_rs {
my ($self, $c) = @_;
my $item_rs;
if($c->user->roles eq "admin") {
$item_rs = $c->model('DB')->resultset('sms_journal');
} elsif ($c->user->roles eq "reseller") {
my $reseller_id = $c->user->reseller_id;
$item_rs = $c->model('DB')->resultset('sms_journal')
->search_rs({
'reseller_id' => $reseller_id,
} , {
join => { provisioning_voip_subscriber => {'subscriber' => {'contract' => 'contact'} } },
});
} elsif ($c->user->roles eq 'subscriber' || $c->user->roles eq 'subscriberadmin') {
my $subscriber_uuid = $c->user->uuid;
$item_rs = $c->model('DB')->resultset('sms_journal')
->search_rs({
'provisioning_voip_subscriber.uuid' => $subscriber_uuid,
} , {
join => 'provisioning_voip_subscriber',
});
}
return $item_rs;
}
sub check_resource{
my($self, $c, $item, $old_resource, $resource, $form) = @_;
unless(defined $resource->{subscriber_id}) { # TODO: might check in form
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Missing mandatory field 'subscriber_id'");
return;
}
my $b_subscriber = $c->model('DB')->resultset('voip_subscribers')->find({
id => $resource->{subscriber_id},
});
unless($b_subscriber) {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'subscriber_id'.");
return;
}
my $subscriber = $b_subscriber->provisioning_voip_subscriber;
unless($subscriber) {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid subscriber.");
return;
}
$resource->{subscriber_id} = $subscriber->id;
return unless NGCP::Panel::Utils::SMS::check_numbers($c, $resource, $subscriber, sub {
my ($err) = @_;
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, $err);
});
return 1;
}
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,139 @@
package NGCP::Panel::Utils::SMS;
use Sipwise::Base;
use LWP::UserAgent;
use URI;
sub send_sms {
my (%args) = @_;
my $c = $args{c};
my $caller = $args{caller};
my $callee = $args{callee};
my $text = $args{text};
my $err_code = $args{err_code};
if (!defined $err_code || ref $err_code ne 'CODE') {
$err_code = sub { return; };
}
my $schema = $c->config->{sms}{schema};
my $host = $c->config->{sms}{host};
my $port = $c->config->{sms}{port};
my $path = $c->config->{sms}{path};
my $user = $c->config->{sms}{user};
my $pass = $c->config->{sms}{pass};
my $fullpath = "$schema://$host:$port$path";
my $ua = LWP::UserAgent->new(
#ssl_opts => { verify_hostname => 0, SSL_verify_mode => 0 },
timeout => 5,
);
my $uri = URI->new($fullpath);
$uri->query_form(
charset => "utf8",
coding => "2",
user => "$user",
pass => "$pass",
text => $text,
to => $callee,
from => $caller,
);
my $res = $ua->get($uri);
if ($res->is_success) {
return 1;
} else {
&{$err_code}("Error with send_sms: " . $res->status_line);
return;
}
}
# false if error, true if ok
# TODOs: normalization?
sub check_numbers {
my ($c, $resource, $prov_subscriber, $err_code) = @_;
if (!defined $err_code || ref $err_code ne 'CODE') {
$err_code = sub { return; };
}
my $pref_rs_allowed_clis = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(
c => $c, attribute => "allowed_clis",
prov_subscriber => $prov_subscriber,
);
my @allowed_clis = $pref_rs_allowed_clis->get_column('value')->all;
my $pref_rs_user_cli = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(
c => $c, attribute => "user_cli",
prov_subscriber => $prov_subscriber,
);
my $user_cli = defined $pref_rs_user_cli->first ? $pref_rs_user_cli->first->value : undef;
my $pref_rs_cli = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(
c => $c, attribute => "cli",
prov_subscriber => $prov_subscriber,
);
my $cli = defined $pref_rs_cli->first ? $pref_rs_cli->first->value : undef;
if ($resource->{caller}) {
my $anumber_ok = 0;
for my $number (@allowed_clis, $user_cli, $cli) {
next unless $number;
if ( _glob_matches($number, $resource->{caller}) ) {
$anumber_ok = 1;
}
}
unless ($anumber_ok) {
return unless &{$err_code}("Invalid 'caller'", 'caller');
}
} else {
if ($user_cli) {
$resource->{caller} = $user_cli;
} elsif ($cli) {
$resource->{caller} = $cli;
} else {
return unless &{$err_code}("Could not set value for 'caller'", 'caller');
}
}
# done setting/checking anumber
# checking bnumber
for my $adm ('adm_', '') {
my $pref_rs_block_out_list = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(
c => $c, attribute => $adm."block_out_list",
prov_subscriber => $prov_subscriber,
);
my @block_out_list = $pref_rs_block_out_list->all;
my $pref_rs_block_out_mode = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(
c => $c, attribute => $adm."block_out_mode",
prov_subscriber => $prov_subscriber,
);
my $block_out_mode = defined $pref_rs_block_out_mode->first ? $pref_rs_block_out_mode->first->value : undef;
if ($block_out_mode) { # whitelist
my $bnumber_ok = 0;
for my $number (@block_out_list) {
if (_glob_matches($number->value, $resource->{callee})) {
$bnumber_ok = 1;
}
}
unless ($bnumber_ok) {
return unless &{$err_code}("Callee Number is not on whitelist for outgoing calls (${adm}block_out_list)", 'callee');
}
} else { # blacklist
for my $number (@block_out_list) {
if (_glob_matches($number->value, $resource->{callee})) {
return unless &{$err_code}("Callee Number is on blocklist for outgoing calls (${adm}block_out_list)", 'callee');
}
}
}
}
return 1;
}
sub _glob_matches {
my ($glob, $string) = @_;
use Text::Glob;
return !!Text::Glob::match_glob($glob, $string);
}
1;

@ -386,3 +386,12 @@ log4perl.appender.Default.layout.ConversionPattern=%d{ISO8601} [%p] [%F +%L] %m{
<external_documentation>
</external_documentation>
<sms>
schema http
host 127.0.0.1
port 999
path /cgi-bin/sendsms
user unset
pass unset
</sms>

@ -121,6 +121,7 @@ $ua->credentials($netloc, "api_admin_http", $user, $pass);
rtcapps => 1,
rtcnetworks => 1,
rtcsessions => 1,
sms => 1,
soundfilerecordings => 1,
soundfiles => 1,
soundhandles => 1,

Loading…
Cancel
Save