diff --git a/lib/NGCP/Panel/Controller/API/CallForwards.pm b/lib/NGCP/Panel/Controller/API/CallForwards.pm new file mode 100644 index 0000000000..e55a6be0a3 --- /dev/null +++ b/lib/NGCP/Panel/Controller/API/CallForwards.pm @@ -0,0 +1,138 @@ +package NGCP::Panel::Controller::API::CallForwards; +use Sipwise::Base; +use namespace::sweep; +use boolean qw(true); +use Data::HAL qw(); +use Data::HAL::Link qw(); +use HTTP::Headers qw(); +use HTTP::Status qw(:constants); +use MooseX::ClassAttribute qw(class_has); +use NGCP::Panel::Utils::DateTime; +use Path::Tiny qw(path); +use Safe::Isa qw($_isa); +BEGIN { extends 'Catalyst::Controller::ActionRole'; } +require Catalyst::ActionRole::ACL; +require Catalyst::ActionRole::CheckTrailingSlash; +require Catalyst::ActionRole::HTTPMethods; +require Catalyst::ActionRole::RequireSSL; + +class_has 'api_description' => ( + is => 'ro', + isa => 'Str', + default => + 'Specifies basic callforwards of a subscriber, where a number of destinations and times can be specified for each type (cfu, cfb, cft cfna).', +); + +class_has 'query_params' => ( + is => 'ro', + isa => 'ArrayRef', + default => sub {[ #TODO + ]}, +); + +with 'NGCP::Panel::Role::API::CallForwards'; + +class_has('resource_name', is => 'ro', default => 'callforwards'); +class_has('dispatch_path', is => 'ro', default => '/api/callforwards/'); +class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-callforwards'); + +__PACKAGE__->config( + action => { + map { $_ => { + ACLDetachTo => '/api/root/invalid_user', + AllowedRole => 'admin', + Args => 0, + Does => [qw(ACL CheckTrailingSlash RequireSSL)], + Method => $_, + Path => __PACKAGE__->dispatch_path, + } } @{ __PACKAGE__->allowed_methods }, + }, + action_roles => [qw(HTTPMethods)], +); + +sub auto :Private { + my ($self, $c) = @_; + + $self->set_body($c); + $self->log_request($c); + return 1; +} + +sub GET :Allow { + my ($self, $c) = @_; + my $page = $c->request->params->{page} // 1; + my $rows = $c->request->params->{rows} // 10; + { + my $cfs = $self->item_rs($c, "callforwards"); + my $total_count = int($cfs->count); + $cfs = $cfs->search(undef, { + page => $page, + rows => $rows, + }); + my (@embedded, @links); + for my $cf ($cfs->search({}, {order_by => ['subscriber_id', 'type']})->all) { + push @embedded, $self->hal_from_item($c, $cf, "callforwards"); + push @links, Data::HAL::Link->new( + relation => 'ngcp:'.$self->resource_name, + href => sprintf('%s%s', $self->dispatch_path, $cf->subscriber_id), + ); + } + push @links, + Data::HAL::Link->new( + relation => 'curies', + href => 'http://purl.org/sipwise/ngcp-api/#rel-{rel}', + name => 'ngcp', + templated => true, + ), + Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'), + Data::HAL::Link->new(relation => 'self', href => sprintf('%s?page=%s&rows=%s', $self->dispatch_path, $page, $rows)); + if(($total_count / $rows) > $page ) { + push @links, Data::HAL::Link->new(relation => 'next', href => sprintf('%s?page=%d&rows=%d', $self->dispatch_path, $page + 1, $rows)); + } + if($page > 1) { + push @links, Data::HAL::Link->new(relation => 'prev', href => sprintf('%s?page=%d&rows=%d', $self->dispatch_path, $page - 1, $rows)); + } + + my $hal = Data::HAL->new( + embedded => [@embedded], + links => [@links], + ); + $hal->resource({ + total_count => $total_count, + }); + my $response = HTTP::Response->new(HTTP_OK, undef, + HTTP::Headers->new($hal->http_headers(skip_links => 1)), $hal->as_json); + $c->response->headers($response->headers); + $c->response->body($response->content); + return; + } + return; +} + +sub HEAD :Allow { + my ($self, $c) = @_; + $c->forward(qw(GET)); + $c->response->body(q()); + return; +} + +sub OPTIONS :Allow { + my ($self, $c) = @_; + my $allowed_methods = $self->allowed_methods; + $c->response->headers(HTTP::Headers->new( + Allow => $allowed_methods->join(', '), + Accept_Post => 'application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-'.$self->resource_name, + )); + $c->response->content_type('application/json'); + $c->response->body(JSON::to_json({ methods => $allowed_methods })."\n"); + return; +} + +sub end : Private { + my ($self, $c) = @_; + + $self->log_response($c); + return 1; +} + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Controller/API/CallForwardsItem.pm b/lib/NGCP/Panel/Controller/API/CallForwardsItem.pm new file mode 100644 index 0000000000..e2b02565de --- /dev/null +++ b/lib/NGCP/Panel/Controller/API/CallForwardsItem.pm @@ -0,0 +1,95 @@ +package NGCP::Panel::Controller::API::CallForwardsItem; +use Sipwise::Base; +use namespace::sweep; +use boolean qw(true); +use Data::HAL qw(); +use Data::HAL::Link qw(); +use HTTP::Headers qw(); +use HTTP::Status qw(:constants); +use MooseX::ClassAttribute qw(class_has); +use NGCP::Panel::Utils::ValidateJSON qw(); +use NGCP::Panel::Utils::DateTime; +use Path::Tiny qw(path); +use Safe::Isa qw($_isa); +BEGIN { extends 'Catalyst::Controller::ActionRole'; } +require Catalyst::ActionRole::ACL; +require Catalyst::ActionRole::HTTPMethods; +require Catalyst::ActionRole::RequireSSL; + +with 'NGCP::Panel::Role::API::CallForwards'; + +class_has('resource_name', is => 'ro', default => 'callforwards'); +class_has('dispatch_path', is => 'ro', default => '/api/callforwards/'); +class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-callforwards'); + +__PACKAGE__->config( + action => { + map { $_ => { + ACLDetachTo => '/api/root/invalid_user', + AllowedRole => 'admin', + Args => 1, + Does => [qw(ACL RequireSSL)], + Method => $_, + Path => __PACKAGE__->dispatch_path, + } } @{ __PACKAGE__->allowed_methods }, + }, + action_roles => [qw(HTTPMethods)], +); + +sub auto :Private { + my ($self, $c) = @_; + + $self->set_body($c); + $self->log_request($c); + return 1; +} + +sub GET :Allow { + my ($self, $c, $id) = @_; + { + last unless $self->valid_id($c, $id); + my $cfm = $self->item_by_id($c, $id, "callforwards"); + last unless $self->resource_exists($c, cfm => $cfm); + + my $hal = $self->hal_from_item($c, $cfm, "callforwards"); + + my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new( + (map { # XXX Data::HAL must be able to generate links with multiple relations + s|rel="(http://purl.org/sipwise/ngcp-api/#rel-resellers)"|rel="item $1"|r + =~ s/rel=self/rel="item self"/r; + } $hal->http_headers), + ), $hal->as_json); + $c->response->headers($response->headers); + $c->response->body($response->content); + return; + } + return; +} + +sub HEAD :Allow { + my ($self, $c, $id) = @_; + $c->forward(qw(GET)); + $c->response->body(q()); + return; +} + +sub OPTIONS :Allow { + my ($self, $c, $id) = @_; + my $allowed_methods = $self->allowed_methods; + $c->response->headers(HTTP::Headers->new( + Allow => $allowed_methods->join(', '), + Accept_Patch => 'application/json-patch+json', + )); + $c->response->content_type('application/json'); + $c->response->body(JSON::to_json({ methods => $allowed_methods })."\n"); + return; +} + +sub end : Private { + my ($self, $c) = @_; + + $self->log_response($c); + return 1; +} + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index 6025d30930..d96767d37d 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -723,27 +723,10 @@ sub preferences_callforward :Chained('base') :PathPart('preferences/callforward' } } my $d = $destination ? $destination->destination : ""; - my $duri = undef; + my $duri; my $t = $destination ? ($destination->timeout || 300) : 300; - if($d =~ /\@voicebox\.local$/) { - $d = 'voicebox'; - } elsif($d =~ /\@fax2mail\.local$/) { - $d = 'fax2mail'; - } elsif($d =~ /\@conference\.local$/) { - $d = 'conference'; - } elsif($d =~ /^sip:callingcard\@app\.local$/) { - $d = 'callingcard'; - } elsif($d =~ /^sip:callthrough\@app\.local$/) { - $d = 'callthrough'; - } elsif($d =~ /^sip:localuser\@.+\.local$/) { - $d = 'localuser'; - } elsif($d =~ /^sip:auto-attendant\@app\.local$/) { - $d = 'autoattendant'; - } elsif($d =~ /^sip:office-hours\@app\.local$/) { - $d = 'officehours'; - } else { - $duri = $d; - $d = 'uri'; + ($d, $duri) = NGCP::Panel::Utils::Subscriber::destination_to_field($d); + if ($d eq "uri") { $c->stash->{cf_tmp_params} = { uri => { destination => $duri, @@ -824,32 +807,16 @@ sub preferences_callforward :Chained('base') :PathPart('preferences/callforward' my $dest = $cf_form->field('destination'); my $d = $dest->field('destination')->value; my $t = 300; - if($d eq "voicebox") { - $d = "sip:vmu$numberstr\@voicebox.local"; - } elsif($d eq "fax2mail") { - $d = "sip:$numberstr\@fax2mail.local"; - } elsif($d eq "conference") { - $d = "sip:conf=$numberstr\@conference.local"; - } elsif($d eq "callingcard") { - $d = "sip:callingcard\@app.local"; - } elsif($d eq "callthrough") { - $d = "sip:callthrough\@app.local"; - } elsif($d eq "localuser") { - $d = "sip:localuser\@app.local"; - } elsif($d eq "autoattendant") { - $d = "sip:auto-attendant\@app.local"; - } elsif($d eq "officehours") { - $d = "sip:office-hours\@app.local"; - } elsif($d eq "uri") { - $d = $dest->field('uri')->field('destination')->value; - if($d !~ /\@/) { - $d .= '@'.$c->stash->{subscriber}->domain->domain; - } - if($d !~ /^sip:/) { - $d = 'sip:' . $d; - } + if ($d eq "uri") { $t = $dest->field('uri')->field('timeout')->value; + # TODO: check for valid timeout here } + $d = NGCP::Panel::Utils::Subscriber::field_to_destination( + number => $numberstr, + domain => $c->stash->{subscriber}->domain->domain, + destination => $d, + uri => $dest->field('uri')->field('destination')->value, + ); $dest_set->voip_cf_destinations->create({ destination => $d, @@ -1148,34 +1115,16 @@ sub preferences_callforward_destinationset_create :Chained('base') :PathPart('pr foreach my $dest(@fields) { my $d = $dest->field('destination')->value; my $t = 300; - if($d eq "voicebox") { - $d = "sip:vmu$numberstr\@voicebox.local"; - } elsif($d eq "fax2mail") { - $d = "sip:$numberstr\@fax2mail.local"; - } elsif($d eq "conference") { - $d = "sip:conf=$numberstr\@conference.local"; - } elsif($d eq "callingcard") { - $d = "sip:callingcard\@app.local"; - } elsif($d eq "callthrough") { - $d = "sip:callthrough\@app.local"; - } elsif($d eq "localuser") { - $d = "sip:localuser\@app.local"; - } elsif($d eq "autoattendant") { - $d = "sip:auto-attendant\@app.local"; - } elsif($d eq "officehours") { - $d = "sip:office-hours\@app.local"; - } elsif($d eq "uri") { - $d = $dest->field('uri')->field('destination')->value; - # TODO: check for valid dest here - if($d !~ /\@/) { - $d .= '@'.$c->stash->{subscriber}->domain->domain; - } - if($d !~ /^sip:/) { - $d = 'sip:' . $d; - } + if ($d eq "uri") { $t = $dest->field('uri')->field('timeout')->value; # TODO: check for valid timeout here } + $d = NGCP::Panel::Utils::Subscriber::field_to_destination( + number => $numberstr, + domain => $c->stash->{subscriber}->domain->domain, + destination => $d, + uri => $dest->field('uri')->field('destination')->value, + ); $set->voip_cf_destinations->create({ destination => $d, @@ -1253,31 +1202,11 @@ sub preferences_callforward_destinationset_edit :Chained('preferences_callforwar my @destinations; for my $dest($set->voip_cf_destinations->all) { my $d = $dest->destination; - my $duri = undef; + my $duri; my $t = $dest->timeout; - if($d =~ /\@voicebox\.local$/) { - $d = 'voicebox'; - } elsif($d =~ /\@fax2mail\.local$/) { - $d = 'fax2mail'; - } elsif($d =~ /\@conference\.local$/) { - $d = 'conference'; - } elsif($d =~ /\@fax2mail\.local$/) { - $d = 'fax2mail'; - } elsif($d =~ /^sip:callingcard\@app\.local$/) { - $d = 'callingcard'; - } elsif($d =~ /^sip:callthrough\@app\.local$/) { - $d = 'callthrough'; - } elsif($d =~ /^sip:localuser\@.+\.local$/) { - $d = 'localuser'; - } elsif($d =~ /^sip:auto-attendant\@app\.local$/) { - $d = 'autoattendant'; - } elsif($d =~ /^sip:office-hours\@app\.local$/) { - $d = 'officehours'; - } else { - $duri = $d; - $d = 'uri'; - } - push @destinations, { + ($d, $duri) = NGCP::Panel::Utils::Subscriber::destination_to_field($d); + + push @destinations, { destination => $d, uri => {timeout => $t, destination => $duri}, priority => $dest->priority, @@ -1335,34 +1264,16 @@ sub preferences_callforward_destinationset_edit :Chained('preferences_callforwar foreach my $dest($form->field('destination')->fields) { my $d = $dest->field('destination')->value; my $t = 300; - if($d eq "voicebox") { - $d = "sip:vmu$numberstr\@voicebox.local"; - } elsif($d eq "fax2mail") { - $d = "sip:$numberstr\@fax2mail.local"; - } elsif($d eq "conference") { - $d = "sip:conf=$numberstr\@conference.local"; - } elsif($d eq "callingcard") { - $d = "sip:callingcard\@app.local"; - } elsif($d eq "callthrough") { - $d = "sip:callthrough\@app.local"; - } elsif($d eq "localuser") { - $d = "sip:localuser\@app.local"; - } elsif($d eq "autoattendant") { - $d = "sip:auto-attendant\@app.local"; - } elsif($d eq "officehours") { - $d = "sip:office-hours\@app.local"; - } elsif($d eq "uri") { - $d = $dest->field('uri')->field('destination')->value; - # TODO: check for valid dest here - if($d !~ /\@/) { - $d .= '@'.$c->stash->{subscriber}->domain->domain; - } - if($d !~ /^sip:/) { - $d = 'sip:' . $d; - } + if ($d eq "uri") { $t = $dest->field('uri')->field('timeout')->value; # TODO: check for valid timeout here } + $d = NGCP::Panel::Utils::Subscriber::field_to_destination( + number => $numberstr, + domain => $c->stash->{subscriber}->domain->domain, + destination => $d, + uri => $dest->field('uri')->field('destination')->value, + ); $set->voip_cf_destinations->create({ destination => $d, diff --git a/lib/NGCP/Panel/Form/CFSimpleAPI.pm b/lib/NGCP/Panel/Form/CFSimpleAPI.pm new file mode 100644 index 0000000000..04d70ff950 --- /dev/null +++ b/lib/NGCP/Panel/Form/CFSimpleAPI.pm @@ -0,0 +1,148 @@ +package NGCP::Panel::Form::CFSimpleAPI; +use HTML::FormHandler::Moose; +use HTML::FormHandler::Widget::Block::Bootstrap; +use Moose::Util::TypeConstraints; +extends 'HTML::FormHandler'; + +has '+widget_wrapper' => (default => 'Bootstrap'); +sub build_render_list {[qw/fields actions/]} +sub build_form_element_class {[qw(form-horizontal)]} + +has_field 'id' => ( + type => 'Hidden', + noupdate => 1, +); + +has_field 'cfu' => ( + type => 'Compound', + do_wrapper => 1, + do_label => 0, + required => 1, + element_attr => { + rel => ['tooltip'], + title => ['Call Forward Unconditional'] + }, +); + +has_field 'cfb' => ( + type => 'Compound', + do_wrapper => 1, + do_label => 0, + required => 1, + element_attr => { + rel => ['tooltip'], + title => ['Call Forward Busy'] + }, +); + +has_field 'cft' => ( + type => 'Compound', + do_wrapper => 1, + do_label => 0, + required => 1, + element_attr => { + rel => ['tooltip'], + title => ['Call Forward Timeout'] + }, +); + +has_field 'cfna' => ( + type => 'Compound', + do_wrapper => 1, + do_label => 0, + required => 1, + element_attr => { + rel => ['tooltip'], + title => ['Call Forward Unavailable'] + }, +); + +has_field 'cfu.destinations' => ( + type => 'Repeatable', + do_wrapper => 1, + do_label => 0, +); + +has_field 'cfu.destinations.destination' => ( + type => 'Text', +); + +has_field 'cfu.destinations.timeout' => ( + type => 'Text', +); + +has_field 'cfu.times' => ( + type => 'Repeatable', + do_wrapper => 1, + do_label => 0, +); + +has_field 'cfb.destinations' => ( + type => 'Repeatable', + do_wrapper => 1, + do_label => 0, +); + +has_field 'cfb.destinations.destination' => ( + type => 'Text', +); + +has_field 'cfb.destinations.timeout' => ( + type => 'Text', +); + +has_field 'cfb.times' => ( + type => 'Repeatable', + do_wrapper => 1, + do_label => 0, +); + +has_field 'cft.destinations' => ( + type => 'Repeatable', + do_wrapper => 1, + do_label => 0, +); + +has_field 'cft.destinations.destination' => ( + type => 'Text', +); + +has_field 'cft.destinations.timeout' => ( + type => 'Text', +); + +has_field 'cft.times' => ( + type => 'Repeatable', + do_wrapper => 1, + do_label => 0, +); + +has_field 'cfna.destinations' => ( + type => 'Repeatable', + do_wrapper => 1, + do_label => 0, +); + +has_field 'cfna.destinations.destination' => ( + type => 'Text', +); + +has_field 'cfna.destinations.timeout' => ( + type => 'Text', +); + +has_field 'cfna.times' => ( + type => 'Repeatable', + do_wrapper => 1, + do_label => 0, +); + +has_block 'fields' => ( + tag => 'div', + class => [qw(modal-body)], + render_list => [qw(cfu cfb cft cfna)], +); + +1; + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Role/API/CallForwards.pm b/lib/NGCP/Panel/Role/API/CallForwards.pm new file mode 100644 index 0000000000..e615c867a1 --- /dev/null +++ b/lib/NGCP/Panel/Role/API/CallForwards.pm @@ -0,0 +1,144 @@ +package NGCP::Panel::Role::API::CallForwards; +use Moose::Role; +use Sipwise::Base; +with 'NGCP::Panel::Role::API' => { + -alias =>{ item_rs => '_item_rs', }, + -excludes => [ 'item_rs' ], +}; + +use boolean qw(true); +use TryCatch; +use Data::HAL qw(); +use Data::HAL::Link qw(); +use HTTP::Status qw(:constants); +use JSON::Types; +use NGCP::Panel::Form::CFSimpleAPI; +use NGCP::Panel::Utils::Subscriber; + +sub get_form { + my ($self, $c, $type) = @_; + + return NGCP::Panel::Form::CFSimpleAPI->new(ctx => $c); +} + +sub hal_from_item { + my ($self, $c, $item, $type) = @_; + my $form; + my $rwr_form = $self->get_form($c, "rules"); + + my %resource = (subscriber_id => $item->subscriber_id); + + my $hal = Data::HAL->new( + links => [ + Data::HAL::Link->new( + relation => 'curies', + href => 'http://purl.org/sipwise/ngcp-api/#rel-{rel}', + name => 'ngcp', + templated => true, + ), + Data::HAL::Link->new(relation => 'collection', href => sprintf("%s", $self->dispatch_path)), + Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'), + Data::HAL::Link->new(relation => 'self', href => sprintf("%s%s", $self->dispatch_path, $item->subscriber_id)), + Data::HAL::Link->new(relation => "ngcp:$type", href => sprintf("/api/%s/%s", $type, $item->subscriber_id)), + Data::HAL::Link->new(relation => 'ngcp:subscribers', href => sprintf("/api/subscribers/%d", $item->subscriber->voip_subscriber->id)), + ], + relation => 'ngcp:'.$self->resource_name, + ); + + for my $cf_type (qw/cfu cfb cft cfna/) { + my $mapping = $c->model('DB')->resultset('voip_cf_mappings')->search({ + subscriber_id => $item->subscriber_id, + type => $cf_type, + })->first; + if ($mapping) { + $resource{$cf_type} = $self->_contents_from_cfm($c, $mapping); + } else { + $resource{$cf_type} = {}; + } + } + + $form //= $self->get_form($c); + return unless $self->validate_form( + c => $c, + form => $form, + resource => \%resource, + run => 0, + ); + + $hal->resource(\%resource); + return $hal; +} + +sub item_rs { + my ($self, $c, $type) = @_; + my $item_rs; + + if($type eq "callforwards") { + if($c->user->roles eq "admin") { + $item_rs = $c->model('DB')->resultset('voip_cf_mappings') + ->search_rs(undef,{ + columns => ['subscriber_id'], + group_by => ['subscriber_id']}); + } else { + return; + } + } else { + die "You should not reach this"; + } + return $item_rs; +} + +sub item_by_id { + my ($self, $c, $id, $type) = @_; + + my $item_rs = $self->item_rs($c, $type); + return $item_rs->search_rs({subscriber_id => $id})->first; +} + +sub update_item { + my ($self, $c, $item, $old_resource, $resource, $form) = @_; + + delete $resource->{id}; + + if ($resource->{rewriterules}) { + $item->voip_rewrite_rules->delete; + my $i = 30; + for my $rule (@{ $resource->{rewriterules} }) { + $item->voip_rewrite_rules->create({ + %{ $rule }, + priority => $i++, + }); + } + } + + return unless $self->validate_form( + c => $c, + form => $form, + resource => $resource, + ); + #TODO: priority not accessible here + $item->update($resource); + + return $item; +} + +sub _contents_from_cfm { + my ($self, $c, $cfm_item) = @_; + my (@times, @destinations); + my $timeset_item = $cfm_item->time_set; + my $dset_item = $cfm_item->destination_set; + for my $time ($timeset_item ? $timeset_item->voip_cf_periods->all : () ) { + push @times, {$time->get_inflated_columns}; + } + for my $dest ($dset_item ? $dset_item->voip_cf_destinations->all : () ) { + my ($d, $duri) = NGCP::Panel::Utils::Subscriber::destination_to_field($dest->destination); + $d = $duri if $d eq "uri"; + push @destinations, {$dest->get_inflated_columns, + destination => $d, + }; + } + return {times => \@times, destinations => \@destinations}; +} + +1; +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Utils/Subscriber.pm b/lib/NGCP/Panel/Utils/Subscriber.pm index c55d649070..7807aea1dc 100644 --- a/lib/NGCP/Panel/Utils/Subscriber.pm +++ b/lib/NGCP/Panel/Utils/Subscriber.pm @@ -630,6 +630,71 @@ sub terminate { }); } +sub field_to_destination { + my %params = @_; + + my $number = $params{number}; + my $domain = $params{domain}; + my $d = $params{destination}; + my $uri = $params{uri}; + + if($d eq "voicebox") { + $d = "sip:vmu$number\@voicebox.local"; + } elsif($d eq "fax2mail") { + $d = "sip:$number\@fax2mail.local"; + } elsif($d eq "conference") { + $d = "sip:conf=$number\@conference.local"; + } elsif($d eq "callingcard") { + $d = "sip:callingcard\@app.local"; + } elsif($d eq "callthrough") { + $d = "sip:callthrough\@app.local"; + } elsif($d eq "localuser") { + $d = "sip:localuser\@app.local"; + } elsif($d eq "autoattendant") { + $d = "sip:auto-attendant\@app.local"; + } elsif($d eq "officehours") { + $d = "sip:office-hours\@app.local"; + } elsif($d eq "uri") { + $d = $uri; + } + # TODO: check for valid dest here + if($d !~ /\@/) { + $d .= '@'.$domain; + } + if($d !~ /^sip:/) { + $d = 'sip:' . $d; + } + return $d; +} + +sub destination_to_field { + my ($d) = @_; + + $d //= ""; + my $duri = undef; + if($d =~ /\@voicebox\.local$/) { + $d = 'voicebox'; + } elsif($d =~ /\@fax2mail\.local$/) { + $d = 'fax2mail'; + } elsif($d =~ /\@conference\.local$/) { + $d = 'conference'; + } elsif($d =~ /^sip:callingcard\@app\.local$/) { + $d = 'callingcard'; + } elsif($d =~ /^sip:callthrough\@app\.local$/) { + $d = 'callthrough'; + } elsif($d =~ /^sip:localuser\@.+\.local$/) { + $d = 'localuser'; + } elsif($d =~ /^sip:auto-attendant\@app\.local$/) { + $d = 'autoattendant'; + } elsif($d =~ /^sip:office-hours\@app\.local$/) { + $d = 'officehours'; + } else { + $duri = $d; + $d = 'uri'; + } + return ($d, $duri); +} + 1; =head1 NAME diff --git a/t/api-root.t b/t/api-root.t index 645a490b52..5aad7ba69f 100644 --- a/t/api-root.t +++ b/t/api-root.t @@ -62,6 +62,7 @@ $ua->ssl_opts( customers => 1, domainpreferences => 1, subscribers => 1, + callforwards => 1, }; foreach my $link(@links) { my $rex = qr/^<\/api\/[a-z]+\/>; rel=\"collection http:\/\/purl\.org\/sipwise\/ngcp-api\/#rel-([a-z]+s)\"$/;