package NGCP::Panel::Utils::Fax; use warnings; use strict; use English; use File::Temp qw/tempfile/; use File::Slurp; use TryCatch; use IPC::System::Simple qw/capture/; use Data::Dumper; use Net::Ping; sub send_fax { my (%args) = @_; my $c = $args{c}; #moved here due to CE, as it doesn't carry NGCP::fax eval { require NGCP::Fax; }; if ($@) { if ($@ =~ m#Can't locate NGCP/Fax.pm#) { $c->log->debug("Fax features are not supported in the Community Edition"); return; } else { die $@; } } my $subscriber = $args{subscriber}; my $prov_subscriber = $subscriber->provisioning_voip_subscriber; my %sendfax_args = (); if ($c->config->{faxserver}{hosts}) { my @hosts = split(/,\s*/, $c->config->{faxserver}{hosts}); my $port = $c->config->{faxserver}{port}; $c->log->debug("faxserver port $port hosts: " . join(',',@hosts)); my $ping = new Net::Ping('tcp', 1); $ping->port_number($port); do { my $host = $hosts[rand @hosts]; $c->log->debug("pinging $host:$port"); if ($ping->ping($host)) { $sendfax_args{host} = $host; $sendfax_args{port} = $port; } @hosts = grep { $_ ne $host } @hosts; } while (!$sendfax_args{host} && @hosts); die "No alive proxy hosts to queue the send fax to" unless $sendfax_args{host} && $sendfax_args{port}; } my $sender = 'webfax'; my $number; if($subscriber->primary_number) { $number = $subscriber->primary_number->cc . ($subscriber->primary_number->ac // ''). $subscriber->primary_number->sn; } else { $number = $sender; } $sendfax_args{caller} = $number; $sendfax_args{callee} = $args{destination}; if($args{quality}) {#low|medium|extended $sendfax_args{quality} = $args{quality}; } if($args{pageheader}) { $sendfax_args{header} = $args{pageheader}; } $sendfax_args{files} = []; if($args{upload}){ push @{$sendfax_args{files}}, eval { $args{upload}->tempname }; $c->log->debug('error to retrieve tempfile of upload: ' . @_) if @_; } if($args{data}){ $sendfax_args{input} = [\$args{data}]; } my $client = new NGCP::Fax; use Data::Dumper; $c->log->debug('invoke send_fax with args: ' . Dumper(\%sendfax_args)); my $res = $client->send_fax(\%sendfax_args); $c->log->debug("webfax: res=$res;"); } sub get_fax { my (%args) = @_; my $c = $args{c}; # mandatory is $item or $filename # $item - get a stored fax from the db # $filename - get a stored fax from the spool #moved here due to CE, as it doesn't carry NGCP::fax eval { require NGCP::Fax; }; if ($@) { if ($@ =~ m#Can't locate NGCP/Fax.pm#) { $c->log->debug("Fax features are not supported in the Community Edition"); return; } else { die $@; } } my $filepath; my $content; my $ext = 'tif'; my ($filename, $format, $item) = @{args}{qw(filename format item)}; return unless $filename || $item; return unless $item && $item->voip_fax_data->data; my $tmp_fh; if ($filename) { my $spool = $c->config->{faxserver}{spool_dir} || return; foreach my $dir (qw(completed failed)) { my $check_path = sprintf "%s/%s/%s", $spool, $dir, $filename; if (-e $check_path) { $filepath = $check_path; last; } } return unless $filepath; } else { if ($format) { unless ( ($tmp_fh, $filepath) = File::Temp::tempfile( DIR => $c->config->{faxserver}{spool_dir}."/tmp") ) { $c->log->error("Cannot create temp file: $ERRNO"); return; } binmode $tmp_fh; close $tmp_fh; print $tmp_fh $item->voip_fax_data->data; } else { return ($item->voip_fax_data->data, $ext); } } if ($format) { my $client = new NGCP::Fax; my $fh = $client->convert_file({}, $filepath, $format); my $rs_old = $RS; local $RS = undef; $content = <$fh>; local $RS = $rs_old; close $fh; $ext = $client->formats->{$format}->{extension}; } else { eval { $content = read_file($filepath, binmode => ':raw'); }; return if $@; } return ($content, $ext); } sub process_fax_journal_item { my ($c, $result, $subscriber) = @_; my $resource = { caller => $result->caller, callee => $result->callee }; my $dir = $result->direction; my $prov_sub = $subscriber->provisioning_voip_subscriber; my $src_sub = $result->caller_subscriber // undef; #undef, if not local, or if caller is username #try finding it: unless ($src_sub) { my $prov_src_sub = $c->model('DB')->resultset('provisioning_voip_subscribers')->search({ 'username' => $result->caller, })->first(); if ($prov_src_sub) { $src_sub = $prov_src_sub->voip_subscriber; } } my $dst_sub = $result->callee_subscriber // undef; #undef, if not local, or if callee is username #try finding it: unless ($dst_sub) { my $prov_dst_sub = $c->model('DB')->resultset('provisioning_voip_subscribers')->search({ 'username' => $result->callee, })->first(); if ($prov_dst_sub) { $dst_sub = $prov_dst_sub->voip_subscriber; } } # either src or dst must be local. my $prov_src_sub = $src_sub ? $src_sub->provisioning_voip_subscriber : undef; #: $prov_sub; my $prov_dst_sub = $dst_sub ? $dst_sub->provisioning_voip_subscriber : undef; #: $prov_sub; my $src_rewrite = 1; my $dst_rewrite = 1; my $label = 'fax_journal id ' . $result->id . ' (sid = ' . $result->sid . ') ' . $dir; $c->log->debug($label . ' number normalization: fax_journal sub username = ' . $prov_sub->username . ', caller = ' . $resource->{caller} . ', callee = ' . $resource->{callee} . ', prov_src_sub username = ' . ($prov_src_sub ? $prov_src_sub->username : '') . ', prov_dst_sub username = ' . ($prov_dst_sub ? $prov_dst_sub->username : '')); if ($dir eq 'out') { # outgoing fax_journal record: fax_journal item subscriber is the callER if ($src_sub and not $dst_sub) { $prov_dst_sub = $c->model('DB')->resultset('provisioning_voip_subscribers')->search({ 'account_id' => $src_sub->contract_id, 'pbx_extension' => $resource->{callee}, })->first(); if ($prov_dst_sub) { $dst_sub = $prov_dst_sub->voip_subscriber; $c->log->debug($label . ' no destination, but found extension ' . $resource->{callee}); } else { #try harder: my $callee = $resource->{callee}; if (my $rt_callee = NGCP::Panel::Utils::Subscriber::apply_rewrite( c => $c, number => $callee, subscriber => $src_sub // $subscriber, direction => 'callee_in' )) { $callee = $rt_callee; #e164 $c->log->debug($label . ' no destination, normalized ' . $resource->{callee} . ' to ' . $callee); my $prov_dst_alias = $c->model('DB')->resultset('voip_dbaliases')->search({ 'username' => $callee, })->first(); if ($prov_dst_alias) { $c->log->debug($label . ' no destination, alias found'); $prov_dst_sub = $prov_dst_alias->subscriber; if ($prov_dst_sub) { $dst_sub = $prov_dst_sub->voip_subscriber; } } else { $c->log->debug($label . ' no destination, no alias found'); } } else { $c->log->debug($label . ' no destination, rewrite not matched'); } } } # caller field: if ($prov_sub->pbx_extension) { # always set the caller to the extension, if available (pbx) $resource->{caller} = $prov_sub->pbx_extension; $src_rewrite = 0; $c->log->debug($label . ' CALLER number normalization: subscriber has pbx_extension' . ', applying ' . $resource->{caller}); } elsif ($prov_sub->is_pbx_pilot) { # if no extension, it can be the pbx pilot. if ($resource->{caller} eq $prov_sub->username) { # use its primary number, if the caller field shows a username. $resource->{caller} = _get_alias_or_primary_number($c,$subscriber); $src_rewrite = 0; $c->log->debug($label . ' CALLER number normalization: subscriber is pbx pilot' . ' and caller shows the username, applying primary number ' . $resource->{caller}); } else { $c->log->debug($label . ' CALLER number normalization: subscriber is pbx pilot' . ' but caller does not show the username, no override'); } } else { # otherwise its some other local subscriber. if ($resource->{caller} eq $prov_sub->username) { # use its primary number, if the caller field shows a username. $resource->{caller} = _get_alias_or_primary_number($c,$subscriber); $src_rewrite = 0; $c->log->debug($label . ' CALLER number normalization: subscriber is other local susbcriber' . ' and caller shows the username, applying primary number ' . $resource->{caller}); } else { $c->log->debug($label . ' CALLER number normalization: subscriber is other local susbcriber' . ' but caller does not show the username, no override'); } } # callee field: if ($src_sub && $dst_sub && $src_sub->contract_id == $dst_sub->contract_id) { # for pbx, src and dst are local and belong to same contract if ($prov_dst_sub->pbx_extension) { # always set the callee to the extension, if available $resource->{callee} = $prov_dst_sub->pbx_extension; $dst_rewrite = 0; $c->log->debug($label . ' CALLEE number normalization: intra customer and destination has pbx_extension' . ', applying ' . $resource->{callee}); } elsif ($prov_dst_sub->is_pbx_pilot) { # if no extension, it can be the pbx pilot. if ($resource->{callee} eq $prov_dst_sub->username) { # use its primary number, if the callee field shows a username. $resource->{callee} = _get_alias_or_primary_number($c,$dst_sub); $dst_rewrite = 0; $c->log->debug($label . ' CALLEE number normalization: intra customer and destination is pbx pilot' . ' and callee shows the username, applying primary number ' . $resource->{callee}); } else { $c->log->debug($label . ' CALLEE number normalization: intra customer and destination is pbx pilot' . ' but callee does not show the username, no override'); } } else { # otherwise its a non-pbx intra customer fax. if ($resource->{callee} eq $prov_dst_sub->username) { # use its primary number, if the callee field shows a username. $resource->{callee} = _get_alias_or_primary_number($c,$dst_sub); $dst_rewrite = 0; $c->log->debug($label . ' CALLEE number normalization: non-pbx intra customer destination' . ' and callee shows the username, applying primary number ' . $resource->{callee}); } else { $c->log->debug($label . ' CALLEE number normalization: non-pbx intra customer destination' . ' but callee does not show the username, no override'); } } } else { # fax to other subscriber, maybe not local. if ($dst_sub && $prov_dst_sub && $resource->{callee} eq $prov_dst_sub->username) { # use its primary number, if the callee field shows a username. $resource->{callee} = _get_alias_or_primary_number($c,$dst_sub); $dst_rewrite = 0; $c->log->debug($label . ' CALLEE number normalization: destination is other local susbcriber ' . ' and callee shows the username, applying primary number ' . $resource->{callee}); } else { $c->log->debug($label . ' CALLEE number normalization: destination is other susbcriber ' . ' and callee does not show the username, no override'); } } if ($dst_rewrite) { if (my $rt_callee = NGCP::Panel::Utils::Subscriber::apply_rewrite( c => $c, number => $resource->{callee}, subscriber => $src_sub // $subscriber, direction => 'callee_in' )) { $resource->{callee} = $rt_callee; $c->log->debug($label . ' number normalization: callee rewrite applied - ' . $resource->{callee}); #now we should have strict e164. if ($rt_callee = NGCP::Panel::Utils::Subscriber::apply_rewrite( c => $c, number => $resource->{callee}, subscriber => $src_sub // $subscriber, #avp_caller_subscriber => $dst_sub, direction => 'caller_out' )) { $resource->{callee} = $rt_callee; $c->log->debug($label . ' 2nd number normalization: caller rewrite applied to callee - ' . $resource->{callee}); } else { $c->log->debug($label . ' 2nd number normalization: caller rewrite ignored to callee'); } } else { $c->log->debug($label . ' number normalization: callee rewrite ignored'); } } else { $c->log->debug($label . ' number normalization: callee rewrite skipped'); } } else { # incoming fax_journal record: fax_journal item subscriber is the callEE # callee field: if ($prov_sub->pbx_extension) { # always set the callee to the extension, if available (pbx) $resource->{callee} = $prov_sub->pbx_extension; $dst_rewrite = 0; $c->log->debug($label . ' CALLEE number normalization: subscriber has pbx_extension' . ', applying ' . $resource->{callee}); } elsif ($prov_sub->is_pbx_pilot) { # if no extension, it can be the pbx pilot. if ($resource->{callee} eq $prov_sub->username) { # use its primary number, if the callee field shows a username. $resource->{callee} = _get_alias_or_primary_number($c,$subscriber); $dst_rewrite = 0; $c->log->debug($label . ' CALLEE number normalization: subscriber is pbx pilot' . ' and callee shows the username, applying primary number ' . $resource->{callee}); } else { $c->log->debug($label . ' CALLEE number normalization: subscriber is pbx pilot' . ' but callee does not show the username, no override'); } } else { # otherwise its some other local subscriber. if ($resource->{callee} eq $prov_sub->username) { # use its primary number, if the caller field shows a username. $resource->{callee} = _get_alias_or_primary_number($c,$subscriber); $dst_rewrite = 0; $c->log->debug($label . ' CALLEE number normalization: subscriber is other local susbcriber' . ' and callee shows the username, applying primary number ' . $resource->{callee}); } else { $c->log->debug($label . ' CALLEE number normalization: subscriber is other local susbcriber' . ' but callee does not show the username, no override'); } } # caller field: if ($src_sub && $dst_sub && $src_sub->contract_id == $dst_sub->contract_id) { # for pbx, src and dst are local and belong to same contract if ($prov_src_sub->pbx_extension) { # always set the caller to the extension, if available $resource->{caller} = $prov_src_sub->pbx_extension; $src_rewrite = 0; $c->log->debug($label . ' CALLER number normalization: intra customer and source has pbx_extension' . ', applying ' . $resource->{caller}); } elsif ($prov_src_sub->is_pbx_pilot) { # if no extension, it can be the pbx pilot. if ($resource->{caller} eq $prov_src_sub->username) { # use its primary number, if the caller field shows a username. $resource->{caller} = _get_alias_or_primary_number($c,$src_sub); $src_rewrite = 0; $c->log->debug($label . ' CALLER number normalization: intra customer and source is pbx pilot' . ' and caller shows the username, applying primary number ' . $resource->{caller}); } else { $c->log->debug($label . ' CALLER number normalization: intra customer and source is pbx pilot' . ' but caller does not show the username, no override'); } #if ($src_rewrite) { _apply_in_caller_rewrite($c,$resource,$dst_sub,$dst_sub,$label); #} } else { # otherwise its a non-pbx intra customer fax. if ($resource->{caller} eq $prov_src_sub->username) { # use its primary number, if the caller field shows a username. $resource->{caller} = _get_alias_or_primary_number($c,$src_sub); $src_rewrite = 0; $c->log->debug($label . ' CALLER number normalization: non-pbx intra customer source' . ' and caller shows the username, applying primary number ' . $resource->{caller}); } else { $c->log->debug($label . ' CALLER number normalization: non-pbx intra customer source' . ' but caller does not show the username, no override'); } #if ($src_rewrite) { _apply_in_caller_rewrite($c,$resource,$dst_sub,$dst_sub,$label); #} } } else { # fax from other subscriber, maybe not local. if ($src_sub && $prov_src_sub && $resource->{caller} eq $prov_src_sub->username) { # use its primary number, if the caller field shows a username. $resource->{caller} = _get_alias_or_primary_number($c,$src_sub); $src_rewrite = 0; $c->log->debug($label . ' CALLER number normalization: source is other local susbcriber ' . ' and caller shows the username, applying primary number ' . $resource->{caller}); } else { $c->log->debug($label . ' CALLER number normalization: source is other susbcriber ' . ' and caller does not show the username, no override'); } if ($dst_sub) { _apply_in_caller_rewrite($c,$resource,$dst_sub,$dst_sub,$label); } } } return $resource; } sub _apply_in_caller_rewrite { my ($c,$resource,$src_sub,$dst_sub,$label) = @_; if (my $rt_caller = NGCP::Panel::Utils::Subscriber::apply_rewrite( c => $c, number => $resource->{caller}, subscriber => $src_sub, avp_callee_subscriber => $dst_sub, direction => 'caller_out' )) { $resource->{caller} = $rt_caller; $c->log->debug($label . ' number normalization: caller rewrite applied - ' . $resource->{caller}); return 1; } else { $c->log->debug($label . ' number normalization: caller rewrite ignored'); return 0; } } sub _get_alias_or_primary_number { my ($c,$subscriber) = @_; my $prov_subs = $subscriber->provisioning_voip_subscriber; if ($prov_subs) { my $pref = $c->model('DB')->resultset('voip_usr_preferences')->search({ 'attribute.attribute' => 'gpp0', 'subscriber_id' => $prov_subs->id, },{ 'join' => 'attribute', })->first(); if ($pref and $pref->value) { return $pref->value; } } my $primary_number = $subscriber->primary_number; return $primary_number->cc . ($primary_number->ac // '') . $primary_number->sn; } 1; # vim: set tabstop=4 expandtab: