diff --git a/lib/NGCP/Panel/Controller/API/VoicemailGreetings.pm b/lib/NGCP/Panel/Controller/API/VoicemailGreetings.pm index 3617e613ad..9399fccf7b 100644 --- a/lib/NGCP/Panel/Controller/API/VoicemailGreetings.pm +++ b/lib/NGCP/Panel/Controller/API/VoicemailGreetings.pm @@ -81,7 +81,7 @@ sub create_item { my ($self, $c, $resource, $form, $process_extras) = @_; my $dir = NGCP::Panel::Utils::Subscriber::get_subscriber_voicemail_directory( c => $c, subscriber => $c->stash->{checked}->{subscriber}, dir => $resource->{dir} ); my $item = $c->stash->{checked}->{voicemail_subscriber}->voicemail_spools->create({ - 'recording' => $resource->{greetingfile}->slurp, + 'recording' => ${$process_extras->{binary_ref}}, 'dir' => $dir, 'origtime' => time(),#just to make inflate possible. Really we don't need this value 'mailboxcontext' => 'default', diff --git a/lib/NGCP/Panel/Controller/API/VoicemailGreetingsItem.pm b/lib/NGCP/Panel/Controller/API/VoicemailGreetingsItem.pm index 1e9aed32b1..30c0982f02 100644 --- a/lib/NGCP/Panel/Controller/API/VoicemailGreetingsItem.pm +++ b/lib/NGCP/Panel/Controller/API/VoicemailGreetingsItem.pm @@ -29,10 +29,12 @@ sub update_item_model{ my($self, $c, $item, $old_resource, $resource, $form, $process_extras) = @_; my $dir = NGCP::Panel::Utils::Subscriber::get_subscriber_voicemail_directory(c => $c, subscriber => $c->stash->{checked}->{subscriber}, dir => $resource->{dir} ); + $item->update({ - 'recording' => $resource->{greetingfile}->slurp, - 'dir' => $dir, - 'origtime' => time(),#just to make inflate possible. Really we don't need this value + 'recording' => ${$process_extras->{binary_ref}}, + 'dir' => $dir, + 'mailboxuser' => $c->stash->{checked}->{subscriber}->uuid, + 'origtime' => time(),#just to make inflate possible. Really we don't need this value }); #we need to return subscriber id, so item can be used for further update #We can't just add field to the item object, so we need to reselect it diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index f48ebcf63e..e9262c7916 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -8,7 +8,6 @@ use JSON qw(decode_json encode_json); use URI::Escape qw(uri_unescape); use Data::Dumper; use MIME::Base64 qw(encode_base64url decode_base64url); -use File::Type; use NGCP::Panel::Utils::Navigation; use NGCP::Panel::Utils::Contract; @@ -3043,17 +3042,28 @@ sub edit_voicebox :Chained('base') :PathPart('preferences/voicebox/edit') :Args( return; } }elsif($posted){ - my $ft = File::Type->new(); my $greetingfile = delete $form->values->{'greetingfile'}; - my $mime_type = $ft->mime_type($greetingfile); - if(('edit' eq $action) &&('audio/x-wav' ne $mime_type && 'application/octet-stream' ne $mime_type)){ - die('Wrong mime-type '.$mime_type.' for the voicemail greeting. Must be a audio file in the "wav" format'); + my $greeting_converted_ref; + try { + NGCP::Panel::Utils::Subscriber::convert_voicemailgreeting( + c => $c, + upload => $greetingfile, + filepath => $greetingfile->tempname, + converted_data_ref => \$greeting_converted_ref ); + } catch($e) { + NGCP::Panel::Utils::Message::error( + c => $c, + log => $e, + desc => $c->loc($e), + ); + NGCP::Panel::Utils::Navigation::back_or($c, + $c->uri_for_action('/subscriber/preferences', [$c->req->captures->[0]]), 1); return; } if($form->validated) { if('edit' eq $action){ $vm_user->voicemail_spools->update_or_create({ - 'recording' => $greetingfile->slurp, + 'recording' => $$greeting_converted_ref, 'dir' => $dir, 'origtime' => time(),#just to make inflate possible. Really we don't need this value 'mailboxcontext' => 'default', @@ -3097,7 +3107,7 @@ sub edit_voicebox :Chained('base') :PathPart('preferences/voicebox/edit') :Args( NGCP::Panel::Utils::Message::error( c => $c, error => $e, - desc => $c->loc('Failed to update voicemail setting'), + desc => $c->loc('Failed to update voicemail setting.'), ); NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for_action('/subscriber/preferences', [$c->req->captures->[0]])); diff --git a/lib/NGCP/Panel/Role/API/VoicemailGreetings.pm b/lib/NGCP/Panel/Role/API/VoicemailGreetings.pm index f0a71d6d12..0df6f88235 100644 --- a/lib/NGCP/Panel/Role/API/VoicemailGreetings.pm +++ b/lib/NGCP/Panel/Role/API/VoicemailGreetings.pm @@ -62,8 +62,31 @@ sub get_form { return (NGCP::Panel::Form::Voicemail::GreetingAPI->new, ['subscriber_id'] ); } +sub process_hal_resource{ + my $self = shift; + my ($c, $item, $resource, $form) = @_; + $resource->{dir} = NGCP::Panel::Utils::Subscriber::get_subscriber_voicemail_type(c => $c, dir => $resource->{dir} ); + return $resource; +} + +sub process_form_resource{ + my($self, $c, $item, $old_resource, $resource, $form, $process_extras) = @_; + try{ + NGCP::Panel::Utils::Subscriber::convert_voicemailgreeting( + c => $c, + upload => $resource->{greetingfile}, + converted_data_ref => \$process_extras->{binary_ref}, + ); + } catch($e) { + $self->error($c, HTTP_UNPROCESSABLE_ENTITY, $e); + return; + } + return 1; +} + sub check_resource{ - my($self, $c, $item, $old_resource, $resource, $form) = @_; + my($self, $c, $item, $old_resource, $resource, $form, $process_extras) = @_; + #TODO: Move subscriber checking to some checking collections my $subscriber_rs = $c->model('DB')->resultset('voip_subscribers')->search({ @@ -108,7 +131,7 @@ sub check_resource{ } sub check_duplicate{ - my($self, $c, $item, $old_resource, $resource, $form) = @_; + my($self, $c, $item, $old_resource, $resource, $form, $process_extras) = @_; my $rs = $self->item_rs($c); @@ -132,11 +155,6 @@ sub check_duplicate{ # return $res; #} -sub process_hal_resource{ - my $self = shift; - my ($c, $item, $resource, $form) = @_; - $resource->{dir} = NGCP::Panel::Utils::Subscriber::get_subscriber_voicemail_type(c => $c, dir => $resource->{dir} ); - return $resource; -} + 1; # vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Utils/Subscriber.pm b/lib/NGCP/Panel/Utils/Subscriber.pm index faf0ee2e64..847a6667e2 100644 --- a/lib/NGCP/Panel/Utils/Subscriber.pm +++ b/lib/NGCP/Panel/Utils/Subscriber.pm @@ -15,6 +15,7 @@ use NGCP::Panel::Utils::Events; use UUID qw/generate unparse/; use JSON qw/decode_json encode_json/; use IPC::System::Simple qw/capturex/; +use File::Slurp qw/read_file/; my %LOCK = ( 0, 'none', @@ -1493,6 +1494,7 @@ sub update_voicemail_number { return; } + sub vmnotify{ my (%params) = @_; @@ -1518,6 +1520,7 @@ sub vmnotify{ $c->log->debug("cmd=".join(" ", @cmd)."; output=$output;"); return; } + sub mark_voicemail_read{ my (%params) = @_; @@ -1528,6 +1531,7 @@ sub mark_voicemail_read{ $voicemail->update({ dir => $dir }); return; } + sub get_subscriber_voicemail_directory{ my (%params) = @_; @@ -1536,6 +1540,7 @@ sub get_subscriber_voicemail_directory{ my $dir = $params{dir}; return "/var/spool/asterisk/voicemail/default/".$subscriber->uuid."/$dir"; } + sub get_subscriber_voicemail_type{ my (%params) = @_; @@ -1544,6 +1549,46 @@ sub get_subscriber_voicemail_type{ $dir =~s/.*?\/([^\/]+)$/$1/gis; return $dir; } + +sub convert_voicemailgreeting{ + my (%params) = @_; + + my $c = $params{c}; + my $upload = $params{upload}; + my $converted_data_ref = $params{converted_data_ref}; + if(!$upload->size){ + die('Uploaded greeting file is empty.'); + } + $c->log->debug("type=".$upload->type."; size=".$upload->size."; filename=".$upload->filename.";"); + + my $filepath = $upload->tempname; + my $filepath_converted = $filepath; + $filepath_converted =~s/\.([^\.]+)$/\.converted.$1/; + + #my $ft = File::Type->new(); + #my $greetingfile = delete $form->values->{'greetingfile'}; + #my $mime_type = $ft->mime_type($greetingfile); + #if(('edit' eq $action) &&('audio/x-wav' ne $mime_type && 'application/octet-stream' ne $mime_type)){ + # die('Wrong mime-type '.$mime_type.' for the voicemail greeting. Must be a audio file in the "wav" format'); + # return; + #} + my @cmd = ( $filepath, '-e', 'gsm', '-b', '16', '-r', '8000', '-c', '1', $filepath_converted); + my $output = ''; + $c->log->debug("cmd=".join(" ", 'sox', @cmd)); + eval { + $output = capturex('sox', @cmd); + }; + $c->log->debug("cmd=".join(" ", 'sox', @cmd)."; output=$output; \$\@=".($@?$@:'').';'); + if($output || $@){ + die('Wrong file format for the voicemail greeting. Must be a audio file in the "wav" format with "gsm" encoding.'); + } + my $data = read_file($filepath_converted, {binmode => ':raw'},); + if(!length($data)){ + die('Empty greeting file after conversion to GSM encoding.'); + } + ${$params{converted_data_ref}} = \$data; +} + sub number_as_string{ my ($number_row, %params) = @_; return 'HASH' eq ref $number_row @@ -1610,6 +1655,7 @@ sub create_cf_destination{ }); } } + sub get_subscriber_pbx_status{ my($c, $subscriber) = @_; if($c->config->{features}->{cloudpbx}) { diff --git a/t/api-rest/api-voicemailgreetings.t b/t/api-rest/api-voicemailgreetings.t index cd474d98ac..0428cb2f6c 100644 --- a/t/api-rest/api-voicemailgreetings.t +++ b/t/api-rest/api-voicemailgreetings.t @@ -7,7 +7,9 @@ use JSON; use Test::More; use Data::Dumper; use File::Basename; -use File::Slurp qw/read_file/; +use File::Slurp qw/read_file write_file/; +use File::Temp; +use NGCP::Panel::Utils::Generic qw(:all); #init test_machine my $fake_data = Test::FakeData->new; @@ -36,26 +38,74 @@ $test_machine->methods->{item}->{allowed} = {map {$_ => 1} qw(GET HEAD OPT #$test_machine->form_data_item( sub {$_[0]->{json}->{dir} = $dir;} ); $test_machine->form_data_item(); +my ($res,$content,$req); my $greeting = $test_machine->check_create_correct( 1 )->[0]; +ok(is_int($greeting->{content}->{id}), "Check id presence after creation."); +ok(is_int($greeting->{content}->{subscriber_id}), "Check subscriber_id presence after creation."); +is($greeting->{content}->{subscriber_id}, $test_machine->DATA_ITEM->{json}->{subscriber_id}, "Check subscriber_id after creation."); +ok($greeting->{content}->{dir} =~/^(?:unavail|busy)$/, "Check dir after creation."); +is($greeting->{content}->{dir}, $test_machine->DATA_ITEM->{json}->{dir}, "Check dir after creation #2."); -my $req = $test_machine->get_request_get($greeting->{location}); -$req->header('Accept' => 'audio/x-wav'); -my ($res) = $test_machine->request_get(undef, $req); -$test_machine->http_code_msg(200, "check download ", $res); -is($res->filename, "voicemail_".$greeting->{content}->{dir}."_".$greeting->{content}->{subscriber_id}.".wav","Check downloaded file name."); +$res = $test_machine->request_get($greeting->{location}, undef, { + 'Accept' => 'audio/x-wav', +}); +$test_machine->http_code_msg(200, "check download voicemail greeting", $res); + +my $expected_downloaded_name = "voicemail_".$greeting->{content}->{dir}."_".$greeting->{content}->{subscriber_id}.".wav"; +is($res->filename, $expected_downloaded_name ,"Check downloaded file name: $expected_downloaded_name ."); +ok(length($res->content)>0,"Check length of the downloaded file > 0 :".length($res->content)); + +my $uploaded_path = $test_machine->DATA_ITEM_STORE->{greetingfile}->[0]; -my $sent_file_content_ref = read_file($test_machine->DATA_ITEM_STORE->{greetingfile}->[0], binmode => ':raw'); +my $dir = File::Temp->newdir(undef, CLEANUP => 0); +my $tempdir = $dir->dirname; +my $downloaded_path = $tempdir.'/'.$expected_downloaded_name; +write_file( $downloaded_path , {binmode => ':raw'}, $res->content); +my $soxi_output = `soxi -e $downloaded_path`; +$soxi_output=~s/\n//g; -is(length($res->content), length($sent_file_content_ref),"Check length of the downloaded file"); +if(ok($soxi_output =~/GSM/, "Check that we converted wav to GSM encoding:".$soxi_output)){ + my $soxi_output_original = `soxi -e $uploaded_path`; + $soxi_output_original=~s/\n//g; + if(ok($soxi_output_original ne $soxi_output, "Check that we used non GSM encoded wav from the start")){ + diag("Time to test uploading of the wav file with GSM encoding"); + $test_machine->DATA_ITEM->{json}->{dir} = 'busy'; + ($res,$content,$req) = $test_machine->request_put( [ + 'json' => JSON::to_json($test_machine->DATA_ITEM->{json}), + greetingfile => [ $downloaded_path ], + ] ); + $test_machine->http_code_msg(200, "check download voicemail greeting after put", $res, $content); + ok(is_int($content->{id}),"Check id presence after editing."); + ok(is_int($content->{subscriber_id}),"Check subscriber_id presence after editing."); + is($content->{dir}, 'busy', "Check dir after editing."); + is($content->{subscriber_id}, $greeting->{content}->{subscriber_id}, "Check subscriber_id after editing."); -$test_machine->check_get2put( { - 'data_cb' => sub { - $_[0] = { - 'json' => JSON::to_json($_[0]), - 'greetingfile' => $test_machine->DATA_ITEM_STORE->{greetingfile} - }; + $uploaded_path = $downloaded_path; + $test_machine->http_code_msg(200, "check update voicemail greeting with put. We used GSM file $downloaded_path.", $res); + my $uploaded_content = read_file($downloaded_path, binmode => ':raw'); + my $res_download = $test_machine->request_get($greeting->{location}, undef, { + 'Accept' => 'audio/x-wav', + }); + $test_machine->http_code_msg(200, "check download voicemail greeting after put", $res); + is(length($res_download->content), length($uploaded_content),"Check length of the downloaded file: ".length($uploaded_content)."<=>".length($res_download->content)); } -} ); +} +{ + diag("Check empty file:"); + #btw - other vriant of tha put data - closer to stored. will be changed by Collection::encode_content + my ($res_put_empty,$content_put_empty) = $test_machine->request_put( { + %{$test_machine->DATA_ITEM_STORE}, + greetingfile => [ dirname($0).'/resources/empty.wav' ], + } ); + $test_machine->http_code_msg(422, "check response code on put empty file", $res_put_empty, $content_put_empty); + diag("Check dirty file:"); + #btw - other vriant of tha put data - closer to stored. will be changed by Collection::encode_content + ($res_put_empty,$content_put_empty) = $test_machine->request_put( { + %{$test_machine->DATA_ITEM_STORE}, + greetingfile => [ dirname($0).'/resources/empty.wav' ], + } ); + $test_machine->http_code_msg(422, "check response code on put empty file", $res_put_empty, $content_put_empty); +} $test_machine->check_bundle(); $test_machine->check_item_delete($greeting->{location}); diff --git a/t/api-rest/resources/empty.wav b/t/api-rest/resources/empty.wav new file mode 100644 index 0000000000..e69de29bb2 diff --git a/t/lib/Test/Collection.pm b/t/lib/Test/Collection.pm index 8a28c63c84..899dda50a6 100644 --- a/t/lib/Test/Collection.pm +++ b/t/lib/Test/Collection.pm @@ -486,6 +486,15 @@ sub encode_content{ if($content){ if( $json_types{$type} && (('HASH' eq ref $content) ||('ARRAY' eq ref $content)) ){ return JSON::to_json($content); + }elsif('multipart/form-data' eq $type + && 'HASH' eq ref $content + && $content->{json} + && (('HASH' eq ref $content->{json}) || ( 'ARRAY' eq ref $content->{json} ) ) + ){ + $content->{json} = JSON::to_json($content->{json}); + return [ + %{$content}, + ]; } } #print "2. content=$content;\n\n"; @@ -524,9 +533,13 @@ sub request_process{ return ($res,$rescontent,$req); } sub get_request_get{ - my($self, $uri) = @_; + my($self, $uri, $headers) = @_; + $headers ||= {}; $uri = $self->normalize_uri($uri); my $req = HTTP::Request->new('GET', $uri); + foreach my $key (keys %$headers){ + $req->header($key => $headers->{$key}); + } return $req ; } sub get_request_put{ @@ -635,9 +648,9 @@ sub request_delete{ } sub request_get{ - my($self,$uri,$req) = @_; + my($self,$uri,$req,$headers) = @_; $uri = $self->normalize_uri($uri); - $req //= $self->get_request_get($uri); + $req //= $self->get_request_get($uri,$headers); my $res = $self->request($req); my $content = $self->get_response_content($res); return wantarray ? ($res, $content, $req) : $res;