From 17f435fb2214d7fb9d7fe2d6f41baf00e8f5a7e7 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 18 Nov 2015 15:37:41 +0300 Subject: [PATCH 01/21] Add. Web UI to configure fax_queue. --- app/fax/app_config.php | 79 +++++++++++++++++++++++++++ app/fax/app_defaults.php | 49 +++++++++++++++++ app/fax/fax_edit.php | 42 +++++++++++++- app/fax/fax_emails.php | 1 + app/fax/fax_send.php | 115 ++++++++++++++++++++++++++++++++------- 5 files changed, 264 insertions(+), 22 deletions(-) diff --git a/app/fax/app_config.php b/app/fax/app_config.php index 8567a0023d..dc9b788ffa 100644 --- a/app/fax/app_config.php +++ b/app/fax/app_config.php @@ -277,6 +277,14 @@ $apps[$x]['db'][$y]['fields'][$z]['type'] = "text"; $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ""; $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = "fax_send_greeting"; + $apps[$x]['db'][$y]['fields'][$z]['type'] = "text"; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ""; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = "fax_send_channels"; + $apps[$x]['db'][$y]['fields'][$z]['type'] = "numeric"; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ""; + $z++; //$apps[$x]['db'][$y]['fields'][$z]['name']['text'] = "fax_keep_local"; //$apps[$x]['db'][$y]['fields'][$z]['name']['deprecated'] = "fax_local"; //$apps[$x]['db'][$y]['fields'][$z]['type'] = "text"; @@ -499,4 +507,75 @@ $apps[$x]['db'][$y]['fields'][$z]['type'] = "numeric"; $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ""; + $y = 4; //table array index + $z = 0; //field array index + $apps[$x]['db'][$y]['table'] = 'v_fax_tasks'; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_uuid'; + $apps[$x]['db'][$y]['fields'][$z]['type']['pgsql'] = 'uuid'; + $apps[$x]['db'][$y]['fields'][$z]['type']['sqlite'] = 'text'; + $apps[$x]['db'][$y]['fields'][$z]['type']['mysql'] = 'char(36)'; + $apps[$x]['db'][$y]['fields'][$z]['key']['type'] = 'primary'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'fax_uuid'; + $apps[$x]['db'][$y]['fields'][$z]['type']['pgsql'] = 'uuid'; + $apps[$x]['db'][$y]['fields'][$z]['type']['sqlite'] = 'text'; + $apps[$x]['db'][$y]['fields'][$z]['type']['mysql'] = 'char(36)'; + $apps[$x]['db'][$y]['fields'][$z]['key']['type'] = 'foreign'; + $apps[$x]['db'][$y]['fields'][$z]['key']['reference']['table'] = 'v_fax'; + $apps[$x]['db'][$y]['fields'][$z]['key']['reference']['field'] = 'fax_uuid'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = 'FAX server primary key'; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_next_time'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'timestamp'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_lock_time'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'timestamp'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_fax_file'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'text'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_wav_file'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'text'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_uri'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'text'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_dial_string'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'text'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_dtmf'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'text'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_interrupted'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'text'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_status'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'numeric'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_no_answer_counter'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'numeric'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_no_answer_retry_counter'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'numeric'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_retry_counter'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'numeric'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; + $apps[$x]['db'][$y]['fields'][$z]['name'] = 'task_description'; + $apps[$x]['db'][$y]['fields'][$z]['type'] = 'text'; + $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ''; + $z++; ?> \ No newline at end of file diff --git a/app/fax/app_defaults.php b/app/fax/app_defaults.php index b3ab8cd98c..d688de4388 100644 --- a/app/fax/app_defaults.php +++ b/app/fax/app_defaults.php @@ -60,6 +60,55 @@ if ($domains_processed == 1) { $array[$x]['default_setting_enabled'] = 'true'; $array[$x]['default_setting_description'] = 'Keep the file after sending or receiving the fax.'; $x++; + $array[$x]['default_setting_category'] = 'fax'; + $array[$x]['default_setting_subcategory'] = 'send_mode'; + $array[$x]['default_setting_name'] = 'text'; + $array[$x]['default_setting_value'] = 'queue'; + $array[$x]['default_setting_enabled'] = 'false'; + $array[$x]['default_setting_description'] = ''; + $x++; + $array[$x]['default_setting_category'] = 'fax'; + $array[$x]['default_setting_subcategory'] = 'send_retry_limit'; + $array[$x]['default_setting_name'] = 'numeric'; + $array[$x]['default_setting_value'] = '5'; + $array[$x]['default_setting_enabled'] = 'true'; + $array[$x]['default_setting_description'] = 'Number of attempts to send fax (count only calls with answer)'; + $x++; + $array[$x]['default_setting_category'] = 'fax'; + $array[$x]['default_setting_subcategory'] = 'send_retry_interval'; + $array[$x]['default_setting_name'] = 'numeric'; + $array[$x]['default_setting_value'] = '15'; + $array[$x]['default_setting_enabled'] = 'true'; + $array[$x]['default_setting_description'] = 'Delay before we make next call after answered call'; + $x++; + $array[$x]['default_setting_category'] = 'fax'; + $array[$x]['default_setting_subcategory'] = 'send_no_answer_retry_limit'; + $array[$x]['default_setting_name'] = 'numeric'; + $array[$x]['default_setting_value'] = '3'; + $array[$x]['default_setting_enabled'] = 'true'; + $array[$x]['default_setting_description'] = 'Number of unanswered attempts in sequence'; + $x++; + $array[$x]['default_setting_category'] = 'fax'; + $array[$x]['default_setting_subcategory'] = 'send_no_answer_retry_interval'; + $array[$x]['default_setting_name'] = 'numeric'; + $array[$x]['default_setting_value'] = '30'; + $array[$x]['default_setting_enabled'] = 'true'; + $array[$x]['default_setting_description'] = 'Delay before we make next call after no answered call'; + $x++; + $array[$x]['default_setting_category'] = 'fax'; + $array[$x]['default_setting_subcategory'] = 'send_no_answer_limit'; + $array[$x]['default_setting_name'] = 'numeric'; + $array[$x]['default_setting_value'] = '3'; + $array[$x]['default_setting_enabled'] = 'true'; + $array[$x]['default_setting_description'] = 'Giveup reach the destination after this number of sequences'; + $x++; + $array[$x]['default_setting_category'] = 'fax'; + $array[$x]['default_setting_subcategory'] = 'send_no_answer_interval'; + $array[$x]['default_setting_name'] = 'numeric'; + $array[$x]['default_setting_value'] = '300'; + $array[$x]['default_setting_enabled'] = 'true'; + $array[$x]['default_setting_description'] = 'Delay before next call sequence'; + $x++; //get an array of the default settings $sql = "select * from v_default_settings "; $prep_statement = $db->prepare($sql); diff --git a/app/fax/fax_edit.php b/app/fax/fax_edit.php index 27ffdc8ca4..28d7613324 100644 --- a/app/fax/fax_edit.php +++ b/app/fax/fax_edit.php @@ -135,8 +135,10 @@ else { } else { $forward_prefix = $forward_prefix.$fax_forward_number.'#'; //found } - $fax_local = check_str($_POST["fax_local"]); + $fax_local = check_str($_POST["fax_local"]); //! @todo check in database $fax_description = check_str($_POST["fax_description"]); + $fax_send_greeting = check_str($_POST["fax_send_greeting"]); + $fax_send_channels = check_str($_POST["fax_send_channels"]); } //delete the user from the fax users @@ -274,6 +276,8 @@ if (count($_POST)>0 && strlen($_POST["persistformvar"]) == 0) { if (strlen($fax_forward_number) > 0) { $sql .= "fax_forward_number, "; } + $sql .= "fax_send_greeting,"; + $sql .= "fax_send_channels,"; $sql .= "fax_description "; $sql .= ")"; $sql .= "values "; @@ -305,6 +309,9 @@ if (count($_POST)>0 && strlen($_POST["persistformvar"]) == 0) { if (strlen($fax_forward_number) > 0) { $sql .= "'$fax_forward_number', "; } + $sql .= (strlen($fax_send_greeting)==0?'NULL':"'$fax_send_greeting'") . ","; + $sql .= (strlen($fax_send_channels)==0?'NULL':"'$fax_send_channels'") . ","; + $sql .= "'$fax_description' "; $sql .= ")"; $db->exec(check_sql($sql)); @@ -345,9 +352,16 @@ if (count($_POST)>0 && strlen($_POST["persistformvar"]) == 0) { else { $sql .= "fax_forward_number = null, "; } + $tmp = strlen($fax_send_greeting)==0?'NULL':"'$fax_send_greeting'"; + $sql .= "fax_send_greeting = $tmp,"; + $tmp = strlen($fax_send_channels)==0?'NULL':"'$fax_send_channels'"; + $sql .= "fax_send_channels = $tmp,"; + $sql .= "fax_description = '$fax_description' "; + $sql .= "where domain_uuid = '".$_SESSION['domain_uuid']."' "; $sql .= "and fax_uuid = '$fax_uuid' "; + $db->exec(check_sql($sql)); unset($sql); } @@ -426,9 +440,14 @@ if (count($_POST)>0 && strlen($_POST["persistformvar"]) == 0) { $fax_caller_id_number = $row["fax_caller_id_number"]; $fax_forward_number = $row["fax_forward_number"]; $fax_description = $row["fax_description"]; + $fax_send_greeting = $row["fax_send_greeting"]; + $fax_send_channels = $row["fax_send_channels"]; } unset ($prep_statement); } + else{ + $fax_send_channels = 10; + } //replace the dash with a space $fax_name = str_replace("-", " ", $fax_name); @@ -678,6 +697,27 @@ if (count($_POST)>0 && strlen($_POST["persistformvar"]) == 0) { echo "\n"; echo "\n"; + echo "\n"; + echo "\n"; + echo " ".$text['label-fax_send_greeting']."\n"; + echo "\n"; + echo "\n"; + echo " \n"; + echo "
\n"; + echo " ".$text['description-fax_send_greeting']."\n"; + echo "\n"; + echo "\n"; + + echo "\n"; + echo "\n"; + echo " ".$text['label-fax_send_channels']."\n"; + echo "\n"; + echo "\n"; + echo " \n"; + echo "
\n"; + echo " ".$text['description-fax_send_channels']."\n"; + echo "\n"; + echo "\n"; } echo " \n"; diff --git a/app/fax/fax_emails.php b/app/fax/fax_emails.php index 3c615a8e75..f1762bc8fd 100644 --- a/app/fax/fax_emails.php +++ b/app/fax/fax_emails.php @@ -74,6 +74,7 @@ if (sizeof($result) != 0) { $fax_email_connection_mailbox = $row["fax_email_connection_mailbox"]; $fax_email_outbound_subject_tag = $row["fax_email_outbound_subject_tag"]; $fax_email_outbound_authorized_senders = $row["fax_email_outbound_authorized_senders"]; + $fax_send_greeting = $row["fax_send_greeting"]; //load default settings, then domain settings over top unset($_SESSION); diff --git a/app/fax/fax_send.php b/app/fax/fax_send.php index 315737f933..0e710bcf07 100644 --- a/app/fax/fax_send.php +++ b/app/fax/fax_send.php @@ -84,21 +84,27 @@ if (!$included) { } foreach ($result as &$row) { //set database fields as variables + $fax_uuid = $row["fax_uuid"]; $fax_extension = $row["fax_extension"]; $fax_caller_id_name = $row["fax_caller_id_name"]; $fax_caller_id_number = $row["fax_caller_id_number"]; $fax_accountcode = $row["accountcode"]; + $fax_send_greeting = $row["fax_send_greeting"]; //limit to one row break; } unset ($prep_statement); + $fax_send_mode = $_SESSION['fax']['send_mode']['text']; + if(strlen($fax_send_mode) == 0){ + $fax_send_mode = 'direct'; + } } //set the fax directory $fax_dir = $_SESSION['switch']['storage']['dir'].'/fax'.((count($_SESSION["domains"]) > 1) ? '/'.$_SESSION['domain_name'] : null); } -else { +else{ require_once "resources/classes/EventSocket.php"; } @@ -534,7 +540,7 @@ if(!function_exists('gs_cmd')) { } //preview, if requested - if ($_REQUEST['submit'] == $text['button-preview']) { + if (($_REQUEST['submit'] != '') && ($_REQUEST['submit'] == $text['button-preview'])) { unset($file_type); if (file_exists($dir_fax_temp.'/'.$fax_instance_uuid.'.pdf')) { $file_type = 'pdf'; @@ -599,29 +605,96 @@ if(!function_exists('gs_cmd')) { } //send the fax + $fax_file = $dir_fax_temp."/".$fax_instance_uuid.".tif"; + $common_dial_string = "for_fax=1,"; + $common_dial_string .= "accountcode='" . $fax_accountcode . "',"; + $common_dial_string .= "sip_h_X-accountcode='" . $fax_accountcode . "',"; + $common_dial_string .= "domain_uuid=" . $_SESSION["domain_uuid"] . ","; + $common_dial_string .= "domain_name=" . $_SESSION["domain_name"] . ","; + $common_dial_string .= "mailto_address='" . $mailto_address . "',"; + $common_dial_string .= "mailfrom_address='" . $mailfrom_address . "',"; + $common_dial_string .= "origination_caller_id_name='" . $fax_caller_id_name . "',"; + $common_dial_string .= "origination_caller_id_number='" . $fax_caller_id_number . "',"; + $common_dial_string .= "fax_ident='" . $fax_caller_id_number . "',"; + $common_dial_string .= "fax_header='" . $fax_caller_id_name . "',"; + $common_dial_string .= "fax_file='" . $fax_file . "',"; + foreach ($fax_numbers as $fax_number) { - $fp = event_socket_create($_SESSION['event_socket_ip_address'], $_SESSION['event_socket_port'], $_SESSION['event_socket_password']); - if ($fp) { - //prepare the fax command - $route_array = outbound_route_to_bridge($_SESSION['domain_uuid'], $fax_prefix.$fax_number); - $fax_file = $dir_fax_temp."/".$fax_instance_uuid.".tif"; - if (count($route_array) == 0) { - //send the internal call to the registered extension - $fax_uri = "user/".$fax_number."@".$_SESSION['domain_name']; - $t38 = ""; + $dial_string = $common_dial_string; + + //prepare the fax command + $route_array = outbound_route_to_bridge($_SESSION['domain_uuid'], $fax_prefix . $fax_number); + + if (count($route_array) == 0) { + //send the internal call to the registered extension + $fax_uri = "user/".$fax_number."@".$_SESSION['domain_name']; + $t38 = ""; + } + else { + //send the external call + $fax_uri = $route_array[0]; + $t38 = "fax_enable_t38=true,fax_enable_t38_request=true,"; + } + $tail_dial_string = $fax_uri." &txfax('".$fax_file."')"; + + if ($fax_send_mode != 'queue') { + $dial_string .= $t38; + $dial_string .= "fax_uri=" . $fax_uri . ","; + $dial_string .= "fax_retry_attempts=1" . ","; + $dial_string .= "fax_retry_limit=20" . ","; + $dial_string .= "fax_retry_sleep=180" . ","; + $dial_string .= "fax_verbose=true" . ","; + $dial_string .= "fax_use_ecm=off" . ","; + $dial_string .= "api_hangup_hook='lua fax_retry.lua'"; + $dial_string = "{" . $dial_string . "}" . $tail_dial_string; + + $fp = event_socket_create($_SESSION['event_socket_ip_address'], $_SESSION['event_socket_port'], $_SESSION['event_socket_password']); + if ($fp) { + $cmd = "api originate " . $dial_string; + // echo($cmd . "
\n"); + //send the command to event socket + $response = event_socket_request($fp, $cmd); + $response = str_replace("\n", "", $response); + $uuid = str_replace("+OK ", "", $response); } - else { - //send the external call - $fax_uri = $route_array[0]; - $t38 = "fax_enable_t38=true,fax_enable_t38_request=true,"; - } - $cmd = "api originate {for_fax=1,accountcode='".$fax_accountcode."',sip_h_X-accountcode='".$fax_accountcode."',domain_uuid=".$_SESSION["domain_uuid"].",domain_name=".$_SESSION["domain_name"].",mailto_address='".$mailto_address."',mailfrom_address='".$mailfrom_address."',origination_caller_id_name='".$fax_caller_id_name."',origination_caller_id_number='".$fax_caller_id_number."',fax_ident='".$fax_caller_id_number."',fax_header='".$fax_caller_id_name."',fax_uri=".$fax_uri.",fax_file='".$fax_file."',fax_retry_attempts=1,fax_retry_limit=20,fax_retry_sleep=180,fax_verbose=true,fax_use_ecm=off,".$t38."api_hangup_hook='lua fax_retry.lua'}".$fax_uri." &txfax('".$fax_file."')"; - //send the command to event socket - $response = event_socket_request($fp, $cmd); - $response = str_replace("\n", "", $response); - $uuid = str_replace("+OK ", "", $response); fclose($fp); } + else{ // enqueue + $task_uuid = uuid(); + $dial_string .= "task_uuid='" . $task_uuid . "',"; + $wav_file = ''; //! @todo add custom message + $dtmf = ''; //! @todo add generate dtmf + $description = ''; //! @todo add description + $sql = <<prepare($sql); + $i = 0; + $stmt->bindValue(++$i, $task_uuid); + $stmt->bindValue(++$i, $fax_uuid); + $stmt->bindValue(++$i, $fax_file); + $stmt->bindValue(++$i, $wav_file); + $stmt->bindValue(++$i, $fax_uri); + $stmt->bindValue(++$i, $dial_string); + $stmt->bindValue(++$i, $dtmf); + $stmt->bindValue(++$i, $description); + if ($stmt->execute()) { + $response = 'Enqueued'; + } + else{ + //! @todo log error + $response = 'Fail enqueue'; + } + } } //wait for a few seconds From e769a0eafe6adb3620d1c11b83f9a0eb7868090e Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 18 Nov 2015 16:27:55 +0300 Subject: [PATCH 02/21] Add. Lua part of fax_queue. To poll active task you can run `luarun fax_queue_poll_once.lua` --- resources/install/scripts/fax_queue/exec.lua | 185 ++++++++++ resources/install/scripts/fax_queue/next.lua | 69 ++++ resources/install/scripts/fax_queue/retry.lua | 345 ++++++++++++++++++ resources/install/scripts/fax_queue/tasks.lua | 223 +++++++++++ .../install/scripts/fax_queue_poll_once.lua | 1 + .../scripts/resources/functions/cache.lua | 8 +- .../scripts/resources/functions/database.lua | 237 ++++++++---- .../scripts/resources/functions/esl.lua | 159 ++++++++ .../resources/functions/lazy_settings.lua | 172 +++++++++ .../scripts/resources/functions/log.lua | 11 +- .../scripts/resources/functions/sleep.lua | 15 + 11 files changed, 1340 insertions(+), 85 deletions(-) create mode 100644 resources/install/scripts/fax_queue/exec.lua create mode 100644 resources/install/scripts/fax_queue/next.lua create mode 100644 resources/install/scripts/fax_queue/retry.lua create mode 100644 resources/install/scripts/fax_queue/tasks.lua create mode 100644 resources/install/scripts/fax_queue_poll_once.lua create mode 100644 resources/install/scripts/resources/functions/esl.lua create mode 100644 resources/install/scripts/resources/functions/lazy_settings.lua create mode 100644 resources/install/scripts/resources/functions/sleep.lua diff --git a/resources/install/scripts/fax_queue/exec.lua b/resources/install/scripts/fax_queue/exec.lua new file mode 100644 index 0000000000..2ecf66bcc7 --- /dev/null +++ b/resources/install/scripts/fax_queue/exec.lua @@ -0,0 +1,185 @@ +-- @usage without queue +-- api: originate {fax_file='',wav_file='',fax_dtmf=''}user/108@domain.local &lua(fax_task.lua) +-- @usage with queue task +-- api: originate {task_uuid=''}user/108@domain.local &lua(fax_task.lua) +-- @fax_dtmf +-- 0-9*# - dtmf symbols +-- @200 - dtmf duration in ms +-- p - pause 500 ms +-- P - pause 1000 ms +-- +-- example: pause 5 sec dial 008 pause 2 sec paly greeting +-- PPPPP008@300PP +-- +-- api: originate {fax_file='d:/fax/file1.tiff',wav_file='c:/FreeSWITCH/sounds/music/8000/suite-espanola-op-47-leyenda_x8.wav',fax_dtmf=''}user/101@sip.office.intelcom-tg.ru &lua(fax_task.lua) + +require "resources.functions.config" +local log = require "resources.functions.log".fax_task + +-- If we handle queue task +local task_uuid = session:getVariable('task_uuid') +local task if task_uuid then + local Tasks = require "fax_queue.tasks" + task = Tasks.select_task(task_uuid) + if not task then + log.warningf("Can not found fax task: %q", tostring(task_uuid)) + return + end +end + +if task then + local str = 'Queue task :' + for k, v in pairs(task) do + str = str .. ('\n %q = %q'):format(k, v) + end + log.info(str) +else + log.info('Not queued task') +end + +local function empty(t) return (not t) or (#t == 0) end + +local function not_empty(t) if not empty(t) then return t end end + +local dtmf, wav_file, fax_file + +if task then + dtmf = not_empty(task.dtmf) + wav_file = not_empty(task.wav_file) or not_empty(task.greeting) + fax_file = not_empty(task.fax_file) +else + dtmf = not_empty(session:getVariable('fax_dtmf')) + wav_file = not_empty(session:getVariable('wav_file')) + fax_file = not_empty(session:getVariable('fax_file')) +end + +if not (wav_file or fax_file) then + log.warning("No fax or wav file") + return +end + +local function decode_dtmf(dtmf) + local r, sleep, seq = {} + dtmf:gsub('P', 'pp'):gsub('.', function(ch) + -- print(ch) + if ch == ';' or ch == ',' then + r[#r + 1] = sleep or seq + sleep, seq = nil + elseif ch == 'p' then + r[#r + 1] = seq + sleep = (sleep or 0) + 500 + seq = nil + else + r[#r + 1] = sleep + seq = (seq or '') .. ch + sleep = nil + end + end) + r[#r + 1] = sleep or seq + return r +end + +local function send_fax() + session:execute("txfax", fax_file) +end + +local function start_fax_detect(detect_duration) + if not tone_detect_cb then + function tone_detect_cb(s, type, obj, arg) + if type == "event" then + if obj:getHeader('Event-Name') == 'DETECTED_TONE' then + return "false" + end + end + end + end + + log.notice("Start detecting fax") + + detect_duration = detect_duration or 60000 + + session:setInputCallback("tone_detect_cb") + session:execute("tone_detect", "txfax 2100 r +" .. tostring(detect_duration) .. " set remote_fax_detected=txfax") + session:execute("tone_detect", "rxfax 1100 r +" .. tostring(detect_duration) .. " set remote_fax_detected=rxfax") + session:setVariable("sip_api_on_image", "uuid_break " .. session:getVariable("uuid") .. " all") +end + +local function stop_fax_detect() + session:unsetInputCallback() + session:execute("stop_tone_detect") + session:setVariable("sip_api_on_image", "") +end + +local function fax_deteced() + if session:getVariable('has_t38') == 'true' then + log.noticef('Detected t38') + session:setVariable('remote_fax_detected', 'txfax') + end + + if fax_file and session:getVariable('remote_fax_detected') then + log.noticef("Detected %s", session:getVariable('remote_fax_detected')) + if session:getVariable('remote_fax_detected') == 'txfax' then + send_fax() + else + log.warning('Remote fax try send fax') + end + return true + end +end + +local function check() + if not session:ready() then return false end + if fax_deteced() then return false end + return true +end + +local function task() + session:waitForAnswer(session) + + while not session:answered() do + if not session:ready() then return end + session:sleep(500) + end + + log.notice("11111") + if not (session:ready() and session:answered()) then return end + log.notice("2222") + + if fax_file and wav_file then + start_fax_detect() + end + + if dtmf then + dtmf = decode_dtmf(dtmf) + for _, element in ipairs(dtmf) do + if type(element) == 'number' then + session:streamFile("silence_stream://" .. tostring(element)) + else + session:execute("send_dtmf", element) + end + if not check() then return end + end + end + + if wav_file then + session:streamFile(wav_file) + if not check() then return end + end + + if fax_file then + if wav_file then + stop_fax_detect() + end + send_fax() + end +end + +log.noticef("START TASK") +log.notice("Fax:" .. tostring(fax_file)) +log.notice("Wav:" .. tostring(wav_file)) + +task() + +log.noticef("STOP TASK") +log.notice("Ready: " .. tostring(session:ready())) +log.notice("answered: " .. tostring(session:answered())) diff --git a/resources/install/scripts/fax_queue/next.lua b/resources/install/scripts/fax_queue/next.lua new file mode 100644 index 0000000000..0067d0e9b4 --- /dev/null +++ b/resources/install/scripts/fax_queue/next.lua @@ -0,0 +1,69 @@ +require "resources.functions.config" + +require "resources.functions.sleep" +local log = require "resources.functions.log".next_fax_task +local Tasks = require "fax_queue.tasks" +local Esl = require "resources.functions.esl" + +local FAX_OPTIONS = { + "fax_use_ecm=false,fax_enable_t38=true,fax_enable_t38_request=true,fax_disable_v17=default"; + "fax_use_ecm=true,fax_enable_t38=true,fax_enable_t38_request=true,fax_disable_v17=false"; + "fax_use_ecm=true,fax_enable_t38=false,fax_enable_t38_request=false,fax_disable_v17=false"; + "fax_use_ecm=true,fax_enable_t38=true,fax_enable_t38_request=true,fax_disable_v17=true"; + "fax_use_ecm=false,fax_enable_t38=false,fax_enable_t38_request=false,fax_disable_v17=false"; +} + +local function next_task() + local task, err = Tasks.next_task() + + if not task then + if err then + log.noticef('Can not select next task: %s', tostring(err)) + else + log.notice("No task") + end + return + end + + local esl + local ok, err = pcall(function() + + for k, v in pairs(task) do + print(string.format(" `%s` => `%s`", tostring(k), tostring(v))) + end + + local mode = (task.retry_counter % #FAX_OPTIONS) + 1 + local dial_string = '{' .. + task.dial_string .. "api_hangup_hook='lua fax_queue/retry.lua'," .. + FAX_OPTIONS[mode] .. + '}' .. task.uri + + local originate = 'originate ' .. dial_string .. ' &lua(fax_queue/exec.lua)' + + log.notice(originate) + esl = assert(Esl.new()) + local ok, err = esl:api(originate) + log.notice(ok or err) + end) + + if esl then esl:close() end + + if not ok then + Tasks.release_task(task) + log.noticef("Error execute task: %s", tostring(err)) + end + + return true +end + +local function poll_once() + Tasks.cleanup_tasks() + while next_task() do + sleep(5000) + end + Tasks.release_db() +end + +return { + poll_once = poll_once; +} diff --git a/resources/install/scripts/fax_queue/retry.lua b/resources/install/scripts/fax_queue/retry.lua new file mode 100644 index 0000000000..2adcd45b27 --- /dev/null +++ b/resources/install/scripts/fax_queue/retry.lua @@ -0,0 +1,345 @@ +-- include libraries + require "resources.functions.config"; + require "resources.functions.explode"; + require "resources.functions.split"; + require "resources.functions.count"; + + local log = require "resources.functions.log".fax_retry + local Database = require "resources.functions.database" + local Settings = require "resources.functions.lazy_settings" + local Tasks = require "fax_queue.tasks" + + local task_uuid = env:getHeader('task_uuid') + local task = Tasks.select_task(task_uuid) + if not task then + log.warningf("Can not find fax task: %q", tostring(task_uuid)) + return + end + +-- show all channel variables + if debug["fax_serialize"] then + log.noticef("info:\n%s", env:serialize()) + end + + local dbh = Database.new('system') + +-- Channel/FusionPBX variables + local uuid = env:getHeader("uuid") + local domain_uuid = env:getHeader("domain_uuid") or task.domain_uuid + local domain_name = env:getHeader("domain_name") or task.domain_name + local origination_caller_id_name = env:getHeader("origination_caller_id_name") or '000000000000000' + local origination_caller_id_number = env:getHeader("origination_caller_id_number") or '000000000000000' + local accountcode = env:getHeader("accountcode") + local duration = tonumber(env:getHeader("billmsec")) or 0 + local sip_to_user = env:getHeader("sip_to_user") + local bridge_hangup_cause = env:getHeader("bridge_hangup_cause") + local hangup_cause_q850 = tonumber(env:getHeader("hangup_cause_q850")) + local answered = duration > 0 + +-- fax variables + local fax_success = env:getHeader('fax_success') + local has_t38 = env:getHeader('has_t38') or 'false' + local t38_broken_boolean = env:getHeader('t38_broken_boolean') or '' + local fax_result_code = tonumber(env:getHeader('fax_result_code')) or 2 + local fax_result_text = env:getHeader('fax_result_text') or 'FS_NOT_SET' + local fax_ecm_used = env:getHeader('fax_ecm_used') or '' + local fax_local_station_id = env:getHeader('fax_local_station_id') or '' + local fax_document_transferred_pages = env:getHeader('fax_document_transferred_pages') or nil + local fax_document_total_pages = env:getHeader('fax_document_total_pages') or nil + local fax_image_resolution = env:getHeader('fax_image_resolution') or '' + local fax_image_size = env:getHeader('fax_image_size') or nil + local fax_bad_rows = env:getHeader('fax_bad_rows') or nil + local fax_transfer_rate = env:getHeader('fax_transfer_rate') or nil + local fax_v17_disabled = env:getHeader('fax_v17_disabled') or '' + local fax_ecm_requested = env:getHeader('fax_ecm_requested') or '' + local fax_remote_station_id = env:getHeader('fax_remote_station_id') or '' + + local fax_options = ("fax_use_ecm=%s,fax_enable_t38=%s,fax_enable_t38_request=%s,fax_disable_v17=%s"):format( + env:getHeader('fax_use_ecm') or '', + env:getHeader('fax_enable_t38') or '', + env:getHeader('fax_enable_t38_request') or '', + env:getHeader('fax_disable_v17') or '' + ) + +-- Fax task params + local fax_uri = env:getHeader("fax_uri") or task.uri + local fax_file = env:getHeader("fax_file") or task.fax_file + local wav_file = env:getHeader("wav_file") or task.wav_file + local fax_uuid = task.fax_uuid + +-- Email variables + local email_address = env:getHeader("mailto_address") + local from_address = env:getHeader("mailfrom_address") or email_address + local number_dialed = fax_uri:match("/([^/]-)%s*$") + local email_message_fail = "We are sorry the fax failed to go through. It has been attached. Please check the number "..number_dialed..", and if it was correct you might consider emailing it instead." + local email_message_success = "We are happy to report the fax was sent successfully. It has been attached for your records." + + log.noticef([[<<< CALL RESULT >>> + uuid: = '%s' + answered: = '%s' + fax_file: = '%s' + wav_file: = '%s' + fax_uri: = '%s' + sip_to_user: = '%s' + accountcode: = '%s' + origination_caller_id_name: = '%s' + origination_caller_id_number: = '%s' + mailfrom_address: = '%s' + mailto_address: = '%s' + hangup_cause_q850: = '%s' + fax_options = '%s' +]], + tostring(uuid) , + tostring(answered) , + tostring(fax_file) , + tostring(wav_file) , + tostring(fax_uri) , + tostring(sip_to_user) , + tostring(accountcode) , + tostring(origination_caller_id_name) , + tostring(origination_caller_id_number) , + tostring(mailfrom_address) , + tostring(mailto_address) , + tostring(hangup_cause_q850) , + fax_options +) + + if fax_success then + log.noticef([[<<< FAX RESULT >>> + fax_success = '%s' + has_t38 = '%s' + t38_broken_boolean = '%s' + fax_result_code = '%s' + fax_result_text = '%s' + fax_ecm_used = '%s' + fax_local_station_id = '%s' + fax_document_transferred_pages = '%s' + fax_document_total_pages = '%s' + fax_image_resolution = '%s' + fax_image_size = '%s' + fax_bad_rows = '%s' + fax_transfer_rate = '%s' + fax_v17_disabled = '%s' + fax_ecm_requested = '%s' + fax_remote_station_id = '%s' + '%s' +]], + fax_success , + has_t38 , + t38_broken_boolean , + fax_result_code , + fax_result_text , + fax_ecm_used , + fax_local_station_id , + fax_document_transferred_pages , + fax_document_total_pages , + fax_image_resolution , + fax_image_size , + fax_bad_rows , + fax_transfer_rate , + fax_v17_disabled , + fax_ecm_requested , + fax_remote_station_id , + '---------------------------------' + ) + end + +--get the values from the fax file + if not (fax_uuid and domain_name) then + local array = split(fax_file, "[\\/]+") + domain_name = domain_name or array[#array - 3] + local fax_extension = fax_extension or array[#array - 2] + + if not fax_uuid then + local sql = "SELECT fax_uuid FROM v_fax " + sql = sql .. "WHERE domain_uuid = '" .. domain_uuid .. "' " + sql = sql .. "AND fax_extension = '" .. fax_extension .. "' " + fax_uuid = dbh:first_value(sql); + end + end + +--get the domain_uuid using the domain name required for multi-tenant + if domain_name and not domain_uuid then + local sql = "SELECT domain_uuid FROM v_domains "; + sql = sql .. "WHERE domain_name = '" .. domain_name .. "' " + domain_uuid = dbh:first_value(sql) + end + + assert(domain_name and domain_uuid) + +--settings + local settings = Settings.new(dbh, domain_name, domain_uuid) + local keep_local = settings:get('fax', 'keep_local','boolean') + local storage_type = (keep_local == "false") and "" or settings:get('fax', 'storage_type', 'text') + +--be sure accountcode is not empty + if (accountcode == nil) then + accountcode = domain_name + end + + local function opt(v, default) + if v then return "'" .. v .. "'" end + return default or 'NULL' + end + + local function now_sql() + return (database["type"] == "sqlite") and "'"..os.date("%Y-%m-%d %X").."'" or "now()"; + end + +--add to fax logs + do + local fields = { + "fax_log_uuid"; + "domain_uuid"; + "fax_uuid"; + "fax_success"; + "fax_result_code"; + "fax_result_text"; + "fax_file"; + "fax_ecm_used"; + "fax_local_station_id"; + "fax_document_transferred_pages"; + "fax_document_total_pages"; + "fax_image_resolution"; + "fax_image_size"; + "fax_bad_rows"; + "fax_transfer_rate"; + "fax_retry_attempts"; + "fax_retry_limit"; + "fax_retry_sleep"; + "fax_uri"; + "fax_date"; + "fax_epoch"; + } + + local values = { + "'"..uuid .. "'"; + "'"..domain_uuid .. "'"; + opt(fax_uuid); + opt(fax_success); + opt(fax_result_code); + opt(fax_result_text); + opt(fax_file); + opt(fax_ecm_used); + opt(fax_local_station_id); + opt(fax_document_transferred_pages, "'0'"); + opt(fax_document_total_pages, "'0'"); + opt(fax_image_resolution); + opt(fax_image_size); + opt(fax_bad_rows); + opt(fax_transfer_rate); + opt(fax_retry_attempts); + opt(fax_retry_limit); + opt(fax_retry_sleep); + opt(fax_uri); + now_sql(); + "'"..os.time().."' "; + } + + local sql = "insert into v_fax_logs(" .. table.concat(fields, ",") .. ")" .. + "values(" .. table.concat(values, ",") .. ")" + + if (debug["sql"]) then + freeswitch.consoleLog("notice", "[FAX] retry: "..sql.."\n"); + end + dbh:query(sql); + end + +-- add the fax files + if fax_success == "1" then + + if storage_type == "base64" then + --include the base64 function + require "resources.functions.base64"; + + --base64 encode the file + local f = io.open(fax_file, "rb"); + if not f then + log.waitng("Can not find file %s", fax_file) + storage_type = nil + else + local file_content = f:read("*all"); + f:close() + fax_base64 = base64.encode(file_content) + end + end + + -- build SQL + local sql do + sql = { + "insert into v_fax_files("; + "fax_file_uuid"; ","; + "fax_uuid"; ","; + "fax_mode"; ","; + "fax_destination"; ","; + "fax_file_type"; ","; + "fax_file_path"; ","; + "fax_caller_id_name"; ","; + "fax_caller_id_number"; ","; + "fax_date"; ","; + "fax_epoch"; ","; + "fax_base64"; ","; + "domain_uuid"; " "; + ") values ("; + opt(uuid); ","; + opt(fax_uuid); ","; + "'tx'"; ","; + opt(sip_to_user); ","; + "'tif'"; ","; + opt(fax_file); ","; + opt(origination_caller_id_name); ","; + opt(origination_caller_id_number); ","; + now_sql(); ","; + "'" .. os.time() .. "'"; ","; + opt(fax_base64); ","; + opt(domain_uuid); " "; + ")" + } + + sql = table.concat(sql, "\n"); + if (debug["sql"]) then + freeswitch.consoleLog("notice", "[FAX] SQL: " .. sql .. "\n"); + end + end + + if storage_type == "base64" then + local db_type, db_cnn = split_first(database["system"], "://", true) + local luasql = require ("luasql." .. db_type); + local env = assert (luasql[db_type]()); + local dbh = env:connect(db_cnn); + dbh:execute(sql) + dbh:close() + env:close() + else + result = dbh:query(sql) + end + end + + if fax_success == "1" then + --Success + log.info("RETRY STATS SUCCESS: GATEWAY[%s] VARS[%s]", fax_options, fax_trial); + + if keep_local == "false" then + os.remove(fax_file); + end + + Tasks.remove_task(task) + end + + if fax_success ~= "1" then + if not answered then + log.noticef("no answer: %d", hangup_cause_q850) + else + if not fax_success then + log.noticef("Fax not detected: %s", fax_options) + else + log.noticef("fax fail %s", fax_options) + end + end + + Tasks.wait_task(task, answered, hangup_cause_q850) + if task.status ~= 0 then + Tasks.remove_task(task) + end + end + diff --git a/resources/install/scripts/fax_queue/tasks.lua b/resources/install/scripts/fax_queue/tasks.lua new file mode 100644 index 0000000000..31eeb70009 --- /dev/null +++ b/resources/install/scripts/fax_queue/tasks.lua @@ -0,0 +1,223 @@ +local Database = require "resources.functions.database" +local Settings = require "resources.functions.lazy_settings" + +local db + +local Q850_TIMEOUT = { + [17] = 60; +} + +local select_task_common_sql = [[ +select + t1.task_uuid as uuid, + t1.fax_uuid as fax_uuid, + t3.domain_name, + t3.domain_uuid, + t1.task_status as status, + t1.task_uri as uri, + t1.task_dial_string as dial_string, + t1.task_dtmf as dtmf, + t1.task_fax_file as fax_file, + t1.task_wav_file as wav_file, + t1.task_no_answer_counter as no_answer_counter, + t1.task_no_answer_retry_counter as no_answer_retry_counter, + t1.task_retry_counter as retry_counter, + t2.fax_send_greeting as greeting +from v_fax_tasks t1 + inner join v_fax t2 on t2.fax_uuid = t1.fax_uuid + inner join v_domains t3 on t2.domain_uuid = t3.domain_uuid +where t1.task_interrupted <> 'true' +]] + +local next_task_sql = select_task_common_sql .. [[ +and t1.task_status = 0 and t1.task_next_time < NOW() +and t2.fax_send_channels > (select count(*) from v_fax_tasks as tasks + where tasks.fax_uuid = t1.fax_uuid and + tasks.task_status > 0 and tasks.task_status <= 2 +) +order by t1.task_next_time +]] + +local select_task_sql = select_task_common_sql .. "and t1.task_uuid='%s'" + +local aquire_task_sql = [[ + update v_fax_tasks set task_status = 1, task_lock_time = NOW() + where task_uuid = '%s' and task_status = 0 +]] + +local wait_task_sql = [[ + update v_fax_tasks + set task_status = %s, + task_lock_time = NULL, + task_no_answer_counter = %s, + task_no_answer_retry_counter = %s, + task_retry_counter = %s, + task_next_time = NOW() + interval '%s second' + where task_uuid = '%s' +]] + +local remove_task_task_sql = [[ + delete from v_fax_tasks + where task_uuid = '%s' +]] + +local release_task_sql = [[ + update v_fax_tasks + set task_status = 0, task_lock_time = NULL, + task_next_time = NOW() + interval '%s second' + where task_uuid = '%s' +]] + +local release_stuck_tasks_sql = [[ + update v_fax_tasks + set task_status = 0, task_lock_time = NULL, + task_next_time = NOW() + where task_lock_time < NOW() + interval '3600 second' +]] + +local remove_finished_tasks_sql = [[ + delete from v_fax_tasks where task_status > 3 +]] + +local function get_db() + if not db then + db = assert(Database.new('system')) + end + return db +end + +local function next_task() + local db = get_db() + + while true do + local task, err = db:first_row(next_task_sql) + if not task then return nil, err end + local ok, err = db:query( aquire_task_sql:format(task.uuid) ) + if not ok then return nil, err end + if db:affected_rows() == 1 then + task.no_answer_counter = tonumber(task.no_answer_counter) + task.no_answer_retry_counter = tonumber(task.no_answer_retry_counter) + task.retry_counter = tonumber(task.retry_counter) + return task + end + end +end + +local function select_task(task_uuid) + local db = get_db() + + local task, err = db:first_row(select_task_sql:format(task_uuid)) + if not task then return nil, err end + + task.no_answer_counter = tonumber(task.no_answer_counter) + task.no_answer_retry_counter = tonumber(task.no_answer_retry_counter) + task.retry_counter = tonumber(task.retry_counter) + + return task +end + +local function wait_task(task, answered, q850) + local db = get_db() + + local interval = 30 + + local settings = Settings.new(db, task.domain_name, task.domain_uuid) + task.status = 0 + + if not answered then + interval = Q850_TIMEOUT[q850 or 17] or interval + end + + if not answered then + local fax_send_no_answer_retry_limit = tonumber(settings:get('fax', 'send_no_answer_retry_limit', 'numeric')) or 0 + task.no_answer_retry_counter = task.no_answer_retry_counter + 1 + + if task.no_answer_retry_counter >= fax_send_no_answer_retry_limit then + task.no_answer_retry_counter = 0 + task.no_answer_counter = task.no_answer_counter + 1 + local fax_send_no_answer_limit = tonumber(settings:get('fax', 'send_no_answer_limit', 'numeric')) or 0 + if task.no_answer_counter >= fax_send_no_answer_limit then + task.status = 4 + else + interval = tonumber(settings:get('fax', 'send_no_answer_interval', 'numeric')) or interval + end + else + interval = tonumber(settings:get('fax', 'send_no_answer_retry_interval', 'numeric')) or interval + end + else + task.retry_counter = task.retry_counter + 1 + local fax_send_retry_limit = tonumber(settings:get('fax', 'send_retry_limit', 'numeric')) or 0 + + if task.retry_counter >= fax_send_retry_limit then + task.status = 4 + else + interval = tonumber(settings:get('fax', 'send_retry_interval', 'numeric')) or interval + task.task_seq_call_counter = 0 + end + end + + local sql = wait_task_sql:format( + tostring(task.status), + tostring(task.no_answer_counter), + tostring(task.no_answer_retry_counter), + tostring(task.retry_counter), + tostring(interval), + task.uuid + ) + + print(sql) + + local ok, err = db:query( sql ) + + if not ok then return nil, err end + + return task +end + +local function remove_task(task) + local db = get_db() + + local sql = remove_task_task_sql:format(task.uuid) + local ok, err = db:query( sql ) + if not ok then return nil, err end + return db:affected_rows() +end + +local function release_task(task) + local db = get_db() + + local interval = 30 + + local sql = release_task_sql:format( + tostring(interval), + task.uuid + ) + + local ok, err = db:query( sql ) + + if not ok then return nil, err end + + return task +end + +local function cleanup_tasks() + local db = get_db() + + db:query(release_stuck_tasks_sql) + db:query(remove_finished_tasks_sql) +end + +return { + release_db = function() + if db then + db:release() + db = nil + end + end; + next_task = next_task; + wait_task = wait_task; + select_task = select_task; + remove_task = remove_task; + release_task = release_task; + cleanup_tasks = cleanup_tasks; +} diff --git a/resources/install/scripts/fax_queue_poll_once.lua b/resources/install/scripts/fax_queue_poll_once.lua new file mode 100644 index 0000000000..c42ee62c72 --- /dev/null +++ b/resources/install/scripts/fax_queue_poll_once.lua @@ -0,0 +1 @@ +require "fax_queue.next".poll_once() \ No newline at end of file diff --git a/resources/install/scripts/resources/functions/cache.lua b/resources/install/scripts/resources/functions/cache.lua index 06548af1cd..6d121eb995 100644 --- a/resources/install/scripts/resources/functions/cache.lua +++ b/resources/install/scripts/resources/functions/cache.lua @@ -8,7 +8,13 @@ require "resources.functions.trim"; -local api = api or freeswitch.API(); +local api = api +if (not api) and freeswitch then api = freeswitch.API() else +api = {} +function api:execute() + return '-ERR UNSUPPORTTED' +end +end local function send_event(action, key) local event = freeswitch.Event("MEMCACHE", action); diff --git a/resources/install/scripts/resources/functions/database.lua b/resources/install/scripts/resources/functions/database.lua index f8939126ff..17abe280ba 100644 --- a/resources/install/scripts/resources/functions/database.lua +++ b/resources/install/scripts/resources/functions/database.lua @@ -1,115 +1,190 @@ require 'resources.functions.config' -require 'resources.functions.file_exists' -require 'resources.functions.database_handle' -local unpack = unpack or table.unpack +----------------------------------------------------------- +local OdbcDatabase = {} if not freeswitch then +OdbcDatabase.__index = OdbcDatabase -local Database = {} do +local odbc = require "odbc.dba" -Database.__index = Database +function OdbcDatabase.new(name) + local self = setmetatable({}, OdbcDatabase) -function Database.new(name) - local dbh = assert(name) - if type(name) == 'string' then - if name == 'switch' and file_exists(database_dir.."/core.db") then - dbh = freeswitch.Dbh("sqlite://"..database_dir.."/core.db") - else - dbh = database_handle(name) - end - end - assert(dbh:connected()) + local connection_string = assert(database[name]) - local self = setmetatable({ - _dbh = dbh; - }, Database) + local typ, dsn, user, password = connection_string:match("^(.-)://(.-):(.-):(.-)$") + assert(typ == 'odbc', "unsupported connection string:" .. connection_string) - return self + self._dbh = odbc.Connect(dsn, user, password) + + return self end -function Database:query(sql, fn) - if (fn == nil) then - return self._dbh:query(sql) - else - return self._dbh:query(sql, fn) - end +function OdbcDatabase:query(sql, fn) + self._rows_affected = nil + if fn then + return self._dbh:neach(sql, function(row) + local o = {} + for k, v in pairs(row) do + if v == odbc.NULL then + o[k] = nil + else + o[k] = tostring(v) + end + end + return fn(o) + end) + end + local ok, err = self._dbh:exec(sql) + if not ok then return nil, err end + self._rows_affected = ok + return self._rows_affected +end + +function OdbcDatabase:affected_rows() + return self._rows_affected; +end + +function OdbcDatabase:release() + if self._dbh then + self._dbh:destroy() + self._dbh = nil + end +end + +function OdbcDatabase:connected() + return self._dbh and self._dbh:connected() +end + +end +----------------------------------------------------------- + +----------------------------------------------------------- +local FsDatabase = {} if freeswitch then + +require "resources.functions.file_exists" +require "resources.functions.database_handle" + +FsDatabase.__index = FsDatabase + +function FsDatabase.new(name) + local dbh = assert(name) + if type(name) == 'string' then + if name == 'switch' and file_exists(database_dir.."/core.db") then + dbh = freeswitch.Dbh("sqlite://"..database_dir.."/core.db") + else + dbh = database_handle(name) + end + end + assert(dbh:connected()) + + local self = setmetatable({ + _dbh = dbh; + }, FsDatabase) + + return self +end + +function FsDatabase:query(sql, fn) + if fn then + return self._dbh:query(sql, fn) + end + return self._dbh:query(sql) +end + +function FsDatabase:affected_rows() + if self._dbh then + return self._dbh:affected_rows() + end +end + +function FsDatabase:release() + if self._dbh then + self._dbh:release() + self._dbh = nil + end +end + +function FsDatabase:connected() + return self._dbh and self._dbh:connected() +end + +end +----------------------------------------------------------- + +----------------------------------------------------------- +local Database = {} do +Database.__index = Database +Database.__base = freeswitch and FsDatabase or OdbcDatabase +Database = setmetatable(Database, Database.__base) + +function Database.new(...) + local self = Database.__base.new(...) + setmetatable(self, Database) + return self end function Database:first_row(sql) - local result - local ok, err = self:query(sql, function(row) - result = row - return 1 - end) - if not ok then return nil, err end - return result + local result + local ok, err = self:query(sql, function(row) + result = row + return 1 + end) + if not ok then return nil, err end + return result end function Database:first_value(sql) - local result, err = self:first_row(sql) - if not result then return nil, err end - local k, v = next(result) - return v + local result, err = self:first_row(sql) + if not result then return nil, err end + local k, v = next(result) + return v end function Database:first(sql, ...) - local result, err = self:first_row(sql) - if not result then return nil, err end - local t, n = {}, select('#', ...) - for i = 1, n do - t[i] = result[(select(i, ...))] - end - return unpack(t, 1, n) + local result, err = self:first_row(sql) + if not result then return nil, err end + local t, n = {}, select('#', ...) + for i = 1, n do + t[i] = result[(select(i, ...))] + end + return unpack(t, 1, n) end function Database:fetch_all(sql) - local result = {} - local ok, err = self:query(sql, function(row) - result[#result + 1] = row - end) - if not ok then return nil, err end - return result + local result = {} + local ok, err = self:query(sql, function(row) + result[#result + 1] = row + end) + if (not ok) and err then return nil, err end + return result end -function Database:release(sql) - if self._dbh then - self._dbh:release() - self._dbh = nil - end -end +function Database.__self_test__(...) + local db = Database.new(...) + assert(db:connected()) -function Database:connected(sql) - return self._dbh and self._dbh:connected() -end + assert("1" == db:first_value("select 1 as v union all select 2 as v")) -function Database.__self_test__(name) - local db = Database.new(name or 'system') - assert(db:connected()) + local t = assert(db:first_row("select '1' as v union all select '2' as v")) + assert(t.v == "1") - assert("1" == db:first_value("select 1 as v union all select 2 as v")) + t = assert(db:fetch_all("select '1' as v union all select '2' as v")) + assert(#t == 2) + assert(t[1].v == "1") + assert(t[2].v == "2") - local t = assert(db:first_row("select 1 as v union all select 2 as v")) - assert(t.v == "1") + local a, b = assert(db:first("select '1' as b, '2' as a", 'a', 'b')) + assert(a == "2") + assert(b == "1") - t = assert(db:fetch_all("select 1 as v union all select 2 as v")) - assert(#t == 2) - assert(t[1].v == "1") - assert(t[2].v == "2") + -- assert(nil == db:first_value("some non sql query")) - local a, b = assert(db:first("select 1 as b, 2 as a", 'a', 'b')) - assert(a == "2") - assert(b == "1") - - -- assert(nil == db:first_value("some non sql query")) - - db:release() - assert(not db:connected()) - print(" * databse - OK!") + db:release() + assert(not db:connected()) + print(" * databse - OK!") end end - --- if debug.self_test then --- Database.__self_test__() --- end +----------------------------------------------------------- return Database \ No newline at end of file diff --git a/resources/install/scripts/resources/functions/esl.lua b/resources/install/scripts/resources/functions/esl.lua new file mode 100644 index 0000000000..fcd4898754 --- /dev/null +++ b/resources/install/scripts/resources/functions/esl.lua @@ -0,0 +1,159 @@ +local function class(base) + local t = base and setmetatable({}, base) or {} + t.__index = t + t.__class = t + t.__base = base + + function t.new(...) + local o = setmetatable({}, t) + if o.__init then + if t == ... then -- we call as Class:new() + return o:__init(select(2, ...)) + else -- we call as Class.new() + return o:__init(...) + end + end + return o + end + + return t +end + +local EventSocket = class() do + +if not freeswitch then + +local socket = require "socket" +local ESLParser = require "lluv.esl".ESLParser +local split_status = require "lluv.esl.utils".split_status +local Database = require "resources.functions.database" + +local EOL = '\n' + +local host, port, auth + +function EventSocket:__init() + if not host then + local db = Database.new('system') + local settings, err = db:first_row("select event_socket_ip_address, event_socket_port, event_socket_password from v_settings") + if not settings then return nil, err end + host, port, auth = settings.event_socket_ip_address, settings.event_socket_port, settings.event_socket_password + end + + return self:_connect(host, port, auth) +end + +function EventSocket:_connect(host, port, password) + local err + self._cnn, err = socket.connect(host, port) + if not self._cnn then return nil, err end + + self._cnn:settimeout(1) + + self._parser = ESLParser.new() + local auth + while true do + local event + event, err = self:_recv_event() + if not event then break end + + local ct = event:getHeader('Content-Type') + if ct == 'auth/request' then + self._cnn:send('auth ' .. password .. EOL .. EOL) + elseif ct == 'command/reply' then + local reply = event:getHeader('Reply-Text') + if reply then + local ok, status, msg = split_status(reply) + if ok then auth = true else err = msg end + else + err = 'invalid response' + end + break + end + end + + if not auth then + self._cnn:close() + self._cnn = nil + return nil, err + end + + return self +end + +function EventSocket:_recv_event() + local event, err = self._parser:next_event() + + while event == true do + local str, rst + str, err, rst = self._cnn:receive("*l") + if str then self._parser:append(str):append(EOL) end + if rst then self._parser:append(rst) end + if err and err ~= 'timeout' then + break + end + event = self._parser:next_event() + end + + if (not event) or (event == true) then + return nil, err + end + + return event +end + +function EventSocket:_request(cmd) + if not self._cnn then return nil, 'closed' end + + for str in (cmd .. '\n'):gmatch("(.-)\n") do + self._cnn:send(str .. EOL) + end + self._cnn:send(EOL) + + return self:_recv_event() +end + +function EventSocket:api(cmd) + local event, err = self:_request('api ' .. cmd) + if not event then return nil, err end + local body = event:getBody() + if body then return body end + return event:getReply() +end + +function EventSocket:close() + if self._cnn then + self._cnn:close() + self._cnn = nil + end +end + +end + +if freeswitch then + +local api + +function EventSocket:__init() + self._api = api or freeswitch.API() + api = self._api + return self +end + +function EventSocket:api(cmd) + local result = self._api:executeString(cmd) + if result and result:sub(1, 4) == '-ERR' then + return nil, result:sub(5) + end + return result +end + +function EventSocket:close() + self._api = nil +end + +end + +end + +return EventSocket diff --git a/resources/install/scripts/resources/functions/lazy_settings.lua b/resources/install/scripts/resources/functions/lazy_settings.lua new file mode 100644 index 0000000000..3d3b72b342 --- /dev/null +++ b/resources/install/scripts/resources/functions/lazy_settings.lua @@ -0,0 +1,172 @@ +-- -- Global settings +-- local settings = Settings.new('system') +-- print(settings:get('switch', 'base', 'dir')) +-- +-- Domain settings (to `fax_retry.lua`) +-- local Settings = require "resources.functions.settings" +-- local settings = Settings.new(dbh, domain_name, domain_uuid) +-- storage_type = settings:get('fax', 'storage_type', 'text') or '' +-- storage_path = settings:get('fax', 'storage_path', 'text') or '' +-- storage_path = storage_path +-- :gsub("${domain_name}", domain_name) +-- :gsub("${voicemail_id}", voicemail_id) +-- :gsub("${voicemail_dir}", voicemail_dir) + +local Database = require "resources.functions.database" +local cache = require "resources.functions.cache" +require "resources.functions.split" + +----------------------------------------------------------- +local Settings = {} do +Settings.__index = Settings + +local NONE = '15783958-912c-4893-8866-4ccd1ca73c6e' + +local function append(t, v) + t[#t+1] = v + return t +end + +local function append_setting(array, category, subcategory, name, value) + --add the category array + if not array[category] then + array[category] = {} + end + + --add the subcategory array + if not array[category][subcategory] then + array[category][subcategory] = {} + end + + --set the name and value + if (name == "array") then + if not array[category][subcategory][name] then + array[category][subcategory][name] = {} + end + append(array[category][subcategory][name], value); + elseif value ~= nil then + array[category][subcategory][name] = value; + end +end + +function Settings.new(db, domain_name, domain_uuid) + local self = setmetatable({}, Settings) + self._array = {} + self._db = db + self._domain_name = domain_name + self._domain_uuid = domain_uuid + + return self +end + +function Settings:_cache_key(category, subcategory, name) + return 'setting:' .. (self._domain_name or '') .. ':' .. category .. ':' .. subcategory .. ':' .. name +end + +function Settings:set(category, subcategory, name, value) + append_setting(self._array, category, subcategory, name, value) + return self +end + +function Settings:get(category, subcategory, name) + local a = self._array + local v = a[category] and a[category][subcategory] and a[category][subcategory][name] + if v == NONE then return nil end + if v ~= nil then return v end + + local key = self:_cache_key(category, subcategory, name) + + v = cache.get(key) + if v then + if v ~= NONE and name == 'array' then + v = split(v, '/+/', true) + end + self:set(category, subcategory, name, v) + if v == NONE then return nil end + return v + end + + return self:_load(category, subcategory, name) +end + +function Settings:_load(category, subcategory, name) + local domain_uuid = self._domain_uuid + local db = self._db + if type(self._db) == 'string' then + db = Database.new(self._db) + end + + local found = false + --get the domain settings + if domain_uuid then + sql = "SELECT domain_setting_uuid,domain_setting_category,domain_setting_subcategory,domain_setting_name,domain_setting_value " + sql = sql .. "FROM v_domain_settings "; + sql = sql .. "WHERE domain_uuid = '" .. domain_uuid .. "'"; + sql = sql .. "AND domain_setting_enabled = 'true' "; + sql = sql .. "AND domain_setting_category = '" .. category .."'"; + sql = sql .. "AND domain_setting_subcategory = '" .. subcategory .. "'"; + sql = sql .. "AND domain_setting_name = '" .. name .. "'"; + sql = sql .. "AND domain_setting_value is not null "; + sql = sql .. "ORDER BY domain_setting_category, domain_setting_subcategory ASC "; + + db:query(sql, function(row) + found = true; + self:set( + row.domain_setting_category, + row.domain_setting_subcategory, + row.domain_setting_name, + row.domain_setting_value + ) + end) + end + + if not found then + local sql = "SELECT default_setting_uuid,default_setting_category,default_setting_subcategory,default_setting_name,default_setting_value " + sql = sql .. "FROM v_default_settings "; + sql = sql .. "WHERE default_setting_enabled = 'true' "; + sql = sql .. "AND default_setting_category = '" .. category .."'"; + sql = sql .. "AND default_setting_subcategory = '" .. subcategory .. "'"; + sql = sql .. "AND default_setting_name = '" .. name .. "'"; + sql = sql .. "AND default_setting_value is not null "; + sql = sql .. "ORDER BY default_setting_category, default_setting_subcategory ASC"; + + db:query(sql, function(row) + found = true; + self:set( + row.default_setting_category, + row.default_setting_subcategory, + row.default_setting_name, + row.default_setting_value + ) + end) + end + + if not found then + self:set(category, subcategory, name, NONE) + end + + local a = self._array + local v = a[category] and a[category][subcategory] and a[category][subcategory][name] + + if cache.support() then + local key = self:_cache_key(category, subcategory, name) + local value = v + if v ~= NONE and name == 'array' then + value = table.concat(v, '/+/') + end + local exp = expire and expire["settings"] or 3600 + cache.set(key, value, exp) + end + + if type(self._db) == 'string' then + db:release() + end + + if v == NONE then return nil end + return v +end + +end +----------------------------------------------------------- + +return Settings diff --git a/resources/install/scripts/resources/functions/log.lua b/resources/install/scripts/resources/functions/log.lua index b6cb59652b..b88818447b 100644 --- a/resources/install/scripts/resources/functions/log.lua +++ b/resources/install/scripts/resources/functions/log.lua @@ -3,9 +3,14 @@ -- log.noticef("%s %s", "hello", "world") -- -- log if debug.SQL or debug.xml_handler.SQL then -- log.tracef("SQL", "SQL is %s", sql) - -local function log(name, level, msg) - freeswitch.consoleLog(level, "[" .. name .. "] " .. msg .. "\n") +local log if freeswitch then + log = function (name, level, msg) + freeswitch.consoleLog(level, "[" .. name .. "] " .. msg .. "\n") + end +else + log = function (name, level, msg) + print(os.date("%Y-%m-%d %X") .. '[' .. level:upper() .. '] [' .. name .. '] ' .. msg) + end end local function logf(name, level, ...) diff --git a/resources/install/scripts/resources/functions/sleep.lua b/resources/install/scripts/resources/functions/sleep.lua new file mode 100644 index 0000000000..e7ace57abd --- /dev/null +++ b/resources/install/scripts/resources/functions/sleep.lua @@ -0,0 +1,15 @@ +if freeswitch then + +function sleep(ms) + freeswitch.msleep(ms) +end + +else + +local socket = require "socket" + +function sleep(ms) + socket.sleep(ms/1000) +end + +end From 21e3c628161673a53bdb89d705d652dcff6dab73 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 18 Nov 2015 16:32:00 +0300 Subject: [PATCH 03/21] Rome some comments. --- resources/install/scripts/fax_queue/exec.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/install/scripts/fax_queue/exec.lua b/resources/install/scripts/fax_queue/exec.lua index 2ecf66bcc7..47dcf54e48 100644 --- a/resources/install/scripts/fax_queue/exec.lua +++ b/resources/install/scripts/fax_queue/exec.lua @@ -11,7 +11,6 @@ -- example: pause 5 sec dial 008 pause 2 sec paly greeting -- PPPPP008@300PP -- --- api: originate {fax_file='d:/fax/file1.tiff',wav_file='c:/FreeSWITCH/sounds/music/8000/suite-espanola-op-47-leyenda_x8.wav',fax_dtmf=''}user/101@sip.office.intelcom-tg.ru &lua(fax_task.lua) require "resources.functions.config" local log = require "resources.functions.log".fax_task @@ -61,7 +60,6 @@ end local function decode_dtmf(dtmf) local r, sleep, seq = {} dtmf:gsub('P', 'pp'):gsub('.', function(ch) - -- print(ch) if ch == ';' or ch == ',' then r[#r + 1] = sleep or seq sleep, seq = nil From 1944c01820ea852e7e17ed747523d4de86e6f92d Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 18 Nov 2015 16:41:13 +0300 Subject: [PATCH 04/21] Remove some log. --- resources/install/scripts/fax_queue/exec.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/resources/install/scripts/fax_queue/exec.lua b/resources/install/scripts/fax_queue/exec.lua index 47dcf54e48..a47dc92148 100644 --- a/resources/install/scripts/fax_queue/exec.lua +++ b/resources/install/scripts/fax_queue/exec.lua @@ -1,7 +1,7 @@ -- @usage without queue --- api: originate {fax_file='',wav_file='',fax_dtmf=''}user/108@domain.local &lua(fax_task.lua) +-- api: originate {fax_file='',wav_file='',fax_dtmf=''}user/108@domain.local &lua(fax_queue/exec.lua) -- @usage with queue task --- api: originate {task_uuid=''}user/108@domain.local &lua(fax_task.lua) +-- api: originate {task_uuid=''}user/108@domain.local &lua(fax_queue/exec.lua) -- @fax_dtmf -- 0-9*# - dtmf symbols -- @200 - dtmf duration in ms @@ -139,9 +139,7 @@ local function task() session:sleep(500) end - log.notice("11111") if not (session:ready() and session:answered()) then return end - log.notice("2222") if fax_file and wav_file then start_fax_detect() @@ -180,4 +178,4 @@ task() log.noticef("STOP TASK") log.notice("Ready: " .. tostring(session:ready())) -log.notice("answered: " .. tostring(session:answered())) +log.notice("Answered: " .. tostring(session:answered())) From ed13cbc5ff5772a1ab2d65c7b8f891549c5ed176 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Thu, 19 Nov 2015 12:36:35 +0300 Subject: [PATCH 05/21] Fix. Display description as last item on fax_server page --- app/fax/fax_edit.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/fax/fax_edit.php b/app/fax/fax_edit.php index 28d7613324..e690d06739 100644 --- a/app/fax/fax_edit.php +++ b/app/fax/fax_edit.php @@ -686,17 +686,6 @@ if (count($_POST)>0 && strlen($_POST["persistformvar"]) == 0) { } } - echo "\n"; - echo "\n"; - echo " ".$text['label-description']."\n"; - echo "\n"; - echo "\n"; - echo " \n"; - echo "
\n"; - echo "".$text['description-info']."\n"; - echo "\n"; - echo "\n"; - echo "\n"; echo "\n"; echo " ".$text['label-fax_send_greeting']."\n"; @@ -718,6 +707,17 @@ if (count($_POST)>0 && strlen($_POST["persistformvar"]) == 0) { echo " ".$text['description-fax_send_channels']."\n"; echo "\n"; echo "\n"; + + echo "\n"; + echo "\n"; + echo " ".$text['label-description']."\n"; + echo "\n"; + echo "\n"; + echo " \n"; + echo "
\n"; + echo "".$text['description-info']."\n"; + echo "\n"; + echo "\n"; } echo " \n"; From aaa333eda17a1429f42600953a513c40bac787a7 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Thu, 19 Nov 2015 13:13:47 +0300 Subject: [PATCH 06/21] Fix. Use fax_send_mode option in fax_send.php file. --- app/fax/fax_emails.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/fax/fax_emails.php b/app/fax/fax_emails.php index f1762bc8fd..a403ffac07 100644 --- a/app/fax/fax_emails.php +++ b/app/fax/fax_emails.php @@ -55,6 +55,11 @@ if (sizeof($result) != 0) { $event_socket['password'] = $record['event_socket_password']; unset($sql, $prep_statement, $record); + $fax_send_mode_default = $_SESSION['fax']['send_mode']['text']; + if(strlen($fax_send_mode_default) == 0){ + $fax_send_mode_default = 'direct'; + } + foreach ($result as $row) { //get fax server and account connection details $fax_uuid = $row["fax_uuid"]; @@ -81,6 +86,11 @@ if (sizeof($result) != 0) { $_SESSION = $default_settings; load_domain_settings($domain_uuid); + $fax_send_mode = $_SESSION['fax']['send_mode']['text']; + if(strlen($fax_send_mode) == 0){ + $fax_send_mode = $fax_send_mode_default; + } + //load event socket connection parameters $_SESSION['event_socket_ip_address'] = $event_socket['ip_address']; $_SESSION['event_socket_port'] = $event_socket['port']; From 82d858c2b8338a26d29c9a83e0360db766e38a29 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Thu, 19 Nov 2015 13:19:59 +0300 Subject: [PATCH 07/21] Add. Basic fax_queue_monitor script. To start From cli: `luarun fax_queue_monitor.lua` Autostart with FS: Add `` to `lua.conf.xml` From shell: call from `script_dir` `lua fax_queue_monitor.lua` (require installed Lua and some additional libraries). To stop remove `fax_queue.tmp` file from `script_dir/run` --- .../install/scripts/fax_queue_monitor.lua | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 resources/install/scripts/fax_queue_monitor.lua diff --git a/resources/install/scripts/fax_queue_monitor.lua b/resources/install/scripts/fax_queue_monitor.lua new file mode 100644 index 0000000000..1f4371d2b7 --- /dev/null +++ b/resources/install/scripts/fax_queue_monitor.lua @@ -0,0 +1,42 @@ + local sleep_interval = 60; + +--include config.lua + require "resources.functions.config"; + +--general functions + require "resources.functions.file_exists"; + require "resources.functions.mkdir"; + require "resources.functions.sleep"; + + local log = require "resources.functions.log".fax_queue_monitor + local Next = require "fax_queue.next" + + mkdir(scripts_dir .. "/run"); + +--define the run file + local run_file = scripts_dir .. "/run/fax_queue.tmp"; + +--used to stop the lua service + local file = assert(io.open(run_file, "w")); + file:write("remove this file to stop the script"); + file:close() + + log.notice("Start") + + while true do + local ok, err = pcall(function() + Next.poll_once() + end) + + if not ok then + log.errf("fail poll queue: %s", tostring(err)) + end + + if not file_exists(run_file) then + break; + end + + sleep(sleep_interval * 1000) + end + + log.notice("Stop") From c146f9fc4f43db9d4a761158ae03ad21be26d1d3 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Thu, 19 Nov 2015 14:30:06 +0300 Subject: [PATCH 08/21] Add. Allow specify DTMF in parentheses in phone number. E.g. `123456 (PP789)` --- app/fax/fax_send.php | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/app/fax/fax_send.php b/app/fax/fax_send.php index 0e710bcf07..e7fb27c8ef 100644 --- a/app/fax/fax_send.php +++ b/app/fax/fax_send.php @@ -177,14 +177,6 @@ if(!function_exists('gs_cmd')) { if (($_POST['action'] == "send")) { $fax_numbers = $_POST['fax_numbers']; - if (sizeof($fax_numbers) > 0) { - foreach ($fax_numbers as $index => $fax_number) { - $fax_numbers[$index] = preg_replace("~[^0-9]~", "", $fax_number); - if ($fax_numbers[$index] == '') { unset($fax_numbers[$index]); } - } - sort($fax_numbers); - } - $fax_uuid = check_str($_POST["id"]); $fax_caller_id_name = check_str($_POST['fax_caller_id_name']); $fax_caller_id_number = check_str($_POST['fax_caller_id_number']); @@ -206,6 +198,28 @@ if(!function_exists('gs_cmd')) { $continue = true; } + // cleanup numbers + if (isset($fax_numbers)) { + foreach ($fax_numbers as $index => $fax_number) { + $tmp=array(); + $fax_dtmf = ''; + if(preg_match('/^\s*(.*?)\s*\((.*)\)\s*$/', $fax_number, $tmp)){ + $fax_number = $tmp[1]; + $fax_dtmf = $tmp[2]; + } + $fax_number = preg_replace("~[^0-9]~", "", $fax_number); + $fax_dtmf = preg_replace("~[^0-9Pp*#]~", "", $fax_dtmf); + if ($fax_number != ''){ + if ($fax_dtmf != '') {$fax_number .= " (" . $fax_dtmf . ")";} + $fax_numbers[$index] = $fax_number; + } + else{ + unset($fax_numbers[$index]); + } + } + sort($fax_numbers); + } + if ($continue) { //determine page size switch ($fax_page_size) { @@ -621,6 +635,12 @@ if(!function_exists('gs_cmd')) { foreach ($fax_numbers as $fax_number) { $dial_string = $common_dial_string; + $tmp = array(); + $fax_dtmf = ''; + if(preg_match('/^\s*(.*?)\s*\((.*)\)\s*$/', $fax_number, $tmp)){ + $fax_number = $tmp[1]; + $fax_dtmf = $tmp[2]; + } //prepare the fax command $route_array = outbound_route_to_bridge($_SESSION['domain_uuid'], $fax_prefix . $fax_number); @@ -663,7 +683,6 @@ if(!function_exists('gs_cmd')) { $task_uuid = uuid(); $dial_string .= "task_uuid='" . $task_uuid . "',"; $wav_file = ''; //! @todo add custom message - $dtmf = ''; //! @todo add generate dtmf $description = ''; //! @todo add description $sql = <<bindValue(++$i, $wav_file); $stmt->bindValue(++$i, $fax_uri); $stmt->bindValue(++$i, $dial_string); - $stmt->bindValue(++$i, $dtmf); + $stmt->bindValue(++$i, $fax_dtmf); $stmt->bindValue(++$i, $description); if ($stmt->execute()) { $response = 'Enqueued'; From 2aff85192947f49a25e51f5275ac38a3df7753ce Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Thu, 19 Nov 2015 14:48:41 +0300 Subject: [PATCH 09/21] Fix. fax_emails.php relay on number cleanup in fax_send.php --- app/fax/fax_emails.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/fax/fax_emails.php b/app/fax/fax_emails.php index a403ffac07..69bc60defe 100644 --- a/app/fax/fax_emails.php +++ b/app/fax/fax_emails.php @@ -170,10 +170,6 @@ if (sizeof($result) != 0) { else { $fax_numbers[] = $tmp; } - foreach ($fax_numbers as $index => $fax_number) { - $fax_numbers[$index] = preg_replace("~[^0-9]~", "", $fax_number); - if ($fax_numbers[$index] == '') { unset($fax_numbers[$index]); } - } unset($fax_subject); //clear so not on cover page //get email body (if any) for cover page From b3cc3a88878699b24b0e4e950d5747ebb5eb18f9 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Fri, 20 Nov 2015 10:38:51 +0300 Subject: [PATCH 10/21] Add. Supports MySQL/SQLite to fax queue. --- app/fax/fax_send.php | 11 +++++++- resources/install/scripts/fax_queue/tasks.lua | 28 +++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/app/fax/fax_send.php b/app/fax/fax_send.php index e7fb27c8ef..d7f6d1827d 100644 --- a/app/fax/fax_send.php +++ b/app/fax/fax_send.php @@ -684,6 +684,15 @@ if(!function_exists('gs_cmd')) { $dial_string .= "task_uuid='" . $task_uuid . "',"; $wav_file = ''; //! @todo add custom message $description = ''; //! @todo add description + if ($db_type == "pgsql") { + $date_utc_now_sql = "NOW() at time zone 'utc'"; + } + if ($db_type == "mysql") { + $date_utc_now_sql = "UTC_TIMESTAMP()"; + } + if ($db_type == "sqlite") { + $date_utc_now_sql = "datetime('now')"; + } $sql = << 'true' ]] local next_task_sql = select_task_common_sql .. [[ -and t1.task_status = 0 and t1.task_next_time < NOW() +and t1.task_status = 0 and t1.task_next_time < ]] .. date_utc_now_sql .. [[ and t2.fax_send_channels > (select count(*) from v_fax_tasks as tasks where tasks.fax_uuid = t1.fax_uuid and tasks.task_status > 0 and tasks.task_status <= 2 @@ -41,7 +57,7 @@ order by t1.task_next_time local select_task_sql = select_task_common_sql .. "and t1.task_uuid='%s'" local aquire_task_sql = [[ - update v_fax_tasks set task_status = 1, task_lock_time = NOW() + update v_fax_tasks set task_status = 1, task_lock_time = ]] .. date_utc_now_sql .. [[ where task_uuid = '%s' and task_status = 0 ]] @@ -52,7 +68,7 @@ local wait_task_sql = [[ task_no_answer_counter = %s, task_no_answer_retry_counter = %s, task_retry_counter = %s, - task_next_time = NOW() + interval '%s second' + task_next_time = ]] .. now_add_sec_sql .. [[ where task_uuid = '%s' ]] @@ -64,15 +80,15 @@ local remove_task_task_sql = [[ local release_task_sql = [[ update v_fax_tasks set task_status = 0, task_lock_time = NULL, - task_next_time = NOW() + interval '%s second' + task_next_time = ]] .. now_add_sec_sql .. [[ where task_uuid = '%s' ]] local release_stuck_tasks_sql = [[ update v_fax_tasks set task_status = 0, task_lock_time = NULL, - task_next_time = NOW() - where task_lock_time < NOW() + interval '3600 second' + task_next_time = ]] .. date_utc_now_sql .. [[ + where task_lock_time < ]] .. now_add_sec_sql:format('3600') .. [[ ]] local remove_finished_tasks_sql = [[ From 2f88d1d985396e7e9b2ee8becab62178d36d564e Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Fri, 20 Nov 2015 14:45:06 +0300 Subject: [PATCH 11/21] Fix. Decode email message from quoted-printable/base64. Fix. Allow set custom fonts to support non latin alphabet. --- app/fax/app_defaults.php | 7 ++++ app/fax/fax_emails.php | 15 +++++--- app/fax/fax_send.php | 30 ++++++++++----- app/fax/resources/functions/parse_message.php | 38 +++++++++++++++++++ 4 files changed, 75 insertions(+), 15 deletions(-) create mode 100644 app/fax/resources/functions/parse_message.php diff --git a/app/fax/app_defaults.php b/app/fax/app_defaults.php index d688de4388..9efab50752 100644 --- a/app/fax/app_defaults.php +++ b/app/fax/app_defaults.php @@ -12,6 +12,13 @@ if ($domains_processed == 1) { $array[$x]['default_setting_description'] = 'Path to image/logo file displayed in the header of the cover sheet.'; $x++; $array[$x]['default_setting_category'] = 'fax'; + $array[$x]['default_setting_subcategory'] = 'cover_font'; + $array[$x]['default_setting_name'] = 'text'; + $array[$x]['default_setting_value'] = 'times'; + $array[$x]['default_setting_enabled'] = 'false'; + $array[$x]['default_setting_description'] = 'Font used to generate cover page. Can be full path to .ttf file or font name alredy installed.'; + $x++; + $array[$x]['default_setting_category'] = 'fax'; $array[$x]['default_setting_subcategory'] = 'cover_footer'; $array[$x]['default_setting_name'] = 'text'; $array[$x]['default_setting_value'] = "The information contained in this facsimile is intended for the sole confidential use of the recipient(s) designated above, and may contain confidential and legally privileged information. If you are not the intended recipient, you are hereby notified that the review, disclosure, dissemination, distribution, copying, duplication in any form, and taking of any action in regards to the contents of this document - except with respect to its direct delivery to the intended recipient - is strictly prohibited. Please notify the sender immediately and destroy this cover sheet and all attachments. If stored or viewed electronically, please permanently delete it from your system."; diff --git a/app/fax/fax_emails.php b/app/fax/fax_emails.php index 69bc60defe..269827e493 100644 --- a/app/fax/fax_emails.php +++ b/app/fax/fax_emails.php @@ -29,6 +29,7 @@ include "root.php"; require_once "resources/require.php"; require_once "resources/functions/object_to_array.php"; require_once "resources/functions/parse_attachments.php"; +require_once "resources/functions/parse_message.php"; require_once "resources/classes/text.php"; //get accounts to monitor @@ -59,6 +60,7 @@ if (sizeof($result) != 0) { if(strlen($fax_send_mode_default) == 0){ $fax_send_mode_default = 'direct'; } + $fax_cover_font_default = $_SESSION['fax']['cover_font']['text']; foreach ($result as $row) { //get fax server and account connection details @@ -91,6 +93,11 @@ if (sizeof($result) != 0) { $fax_send_mode = $fax_send_mode_default; } + $fax_cover_font = $_SESSION['fax']['cover_font']['text']; + if(strlen($fax_cover_font) == 0){ + $fax_cover_font = $fax_cover_font_default; + } + //load event socket connection parameters $_SESSION['event_socket_ip_address'] = $event_socket['ip_address']; $_SESSION['event_socket_port'] = $event_socket['port']; @@ -173,15 +180,11 @@ if (sizeof($result) != 0) { unset($fax_subject); //clear so not on cover page //get email body (if any) for cover page - $fax_message = imap_fetchbody($connection, $email_id, '1.1', FT_UID); - $fax_message = strip_tags($fax_message); - $fax_message = trim($fax_message); + $fax_message = parse_message($connection, $email_id, FT_UID); if ($fax_message == '') { - $fax_message = imap_fetchbody($connection, $email_id, '1', FT_UID); $fax_message = strip_tags($fax_message); - $fax_message = trim($fax_message); + $fax_message = str_replace("\r\n\r\n","\r\n", $fax_message); } - $fax_message = str_replace("\r\n\r\n","\r\n", $fax_message); // set fax directory (used for pdf creation - cover and/or attachments) $fax_dir = $_SESSION['switch']['storage']['dir'].'/fax'.(($domain_name != '') ? '/'.$domain_name : null); diff --git a/app/fax/fax_send.php b/app/fax/fax_send.php index d7f6d1827d..a059e7ad40 100644 --- a/app/fax/fax_send.php +++ b/app/fax/fax_send.php @@ -103,6 +103,8 @@ if (!$included) { //set the fax directory $fax_dir = $_SESSION['switch']['storage']['dir'].'/fax'.((count($_SESSION["domains"]) > 1) ? '/'.$_SESSION['domain_name'] : null); + // set fax cover font to generate pdf + $fax_cover_font = $_SESSION['fax']['cover_font']['text']; } else{ require_once "resources/classes/EventSocket.php"; @@ -342,6 +344,19 @@ if(!function_exists('gs_cmd')) { $pdf -> setPrintFooter(false); $pdf -> SetMargins(0, 0, 0, true); + if(strlen($fax_cover_font) > 0){ + if(substr($fax_cover_font, -4) == '.ttf'){ + $pdf_font = TCPDF_FONTS::addTTFfont($fax_cover_font); + } + else{ + $pdf_font = $fax_cover_font; + } + } + + if(!$pdf_font){ + $pdf_font = 'times'; + } + //add blank page $pdf -> AddPage('P', array($page_width, $page_height)); @@ -349,9 +364,6 @@ if(!function_exists('gs_cmd')) { $x = 0; $y = 0; - // output grid - //showgrid($pdf); - //logo $display_logo = false; if (!isset($_SESSION['fax']['cover_logo']['text'])) { @@ -395,23 +407,23 @@ if(!function_exists('gs_cmd')) { //header if ($fax_header != '') { $pdf -> SetLeftMargin(0.5); - $pdf -> SetFont("times", "", 10); + $pdf -> SetFont($pdf_font, "", 10); $pdf -> Write(0.3, $fax_header); } //fax, cover sheet $pdf -> SetTextColor(0,0,0); - $pdf -> SetFont("times", "B", 55); + $pdf -> SetFont($pdf_font, "B", 55); $pdf -> SetXY($x + 4.55, $y + 0.25); $pdf -> Cell($x + 3.50, $y + 0.4, $text['label-fax-fax'], 0, 0, 'R', false, null, 0, false, 'T', 'T'); - $pdf -> SetFont("times", "", 12); + $pdf -> SetFont($pdf_font, "", 12); $pdf -> SetFontSpacing(0.0425); $pdf -> SetXY($x + 4.55, $y + 1.0); $pdf -> Cell($x + 3.50, $y + 0.4, $text['label-fax-cover-sheet'], 0, 0, 'R', false, null, 0, false, 'T', 'T'); $pdf -> SetFontSpacing(0); //field labels - $pdf -> SetFont("times", "B", 12); + $pdf -> SetFont($pdf_font, "B", 12); if ($fax_recipient != '' || sizeof($fax_numbers) > 0) { $pdf -> Text($x + 0.5, $y + 2.0, strtoupper($text['label-fax-recipient']).":"); } @@ -426,7 +438,7 @@ if(!function_exists('gs_cmd')) { } //field values - $pdf -> SetFont("times", "", 12); + $pdf -> SetFont($pdf_font, "", 12); $pdf -> SetXY($x + 2.0, $y + 1.95); if ($fax_recipient != '') { $pdf -> Write(0.3, $fax_recipient); @@ -466,7 +478,7 @@ if(!function_exists('gs_cmd')) { //message $pdf -> Rect($x + 0.5, $y + 3.4, 7.5, 6.25, 'D'); if ($fax_message != '') { - $pdf -> SetFont("times", "", 12); + $pdf -> SetFont($pdf_font, "", 12); $pdf -> SetXY($x + 0.75, $y + 3.65); $pdf -> MultiCell(7, 5.75, $fax_message, 0, 'L', false); } diff --git a/app/fax/resources/functions/parse_message.php b/app/fax/resources/functions/parse_message.php new file mode 100644 index 0000000000..c2945e76b2 --- /dev/null +++ b/app/fax/resources/functions/parse_message.php @@ -0,0 +1,38 @@ +parts) && count($structure->parts)) { + for($i = 0; $i < count($structure->parts); $i++) { + $msg = ''; + $part = $structure->parts[$i]; + if($part->type == TYPETEXT){ + $msg = imap_fetchbody($connection, $message_number, $i+1, $option); + if($part->encoding == ENCBASE64){ + $msg = base64_decode($msg); + } + else if($part->encoding == ENCQUOTEDPRINTABLE){ + $msg = quoted_printable_decode($msg); + } + if($msg && $to_charset){ + $charset = ''; + if(isset($part->parameters) && count($part->parameters)) { + foreach($part->parameters as &$parameter){ + if($parameter->attribute == 'CHARSET') { + $charset = $parameter->value; + break; + } + } + } + if($charset){ + $msg = mb_convert_encoding($msg, $to_charset, $charset); + } + } + + if($msg){ + return $msg; + } + } + } + } +} From 64f91c3ed2c718acf51cdc1093dc14d3f7230173 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Fri, 20 Nov 2015 15:22:09 +0300 Subject: [PATCH 12/21] Fix. Poll several mail at once. Problem causes by fax_send.php which change current dir and second call can not finding libraries. --- app/fax/fax_emails.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/fax/fax_emails.php b/app/fax/fax_emails.php index 269827e493..234367e7b8 100644 --- a/app/fax/fax_emails.php +++ b/app/fax/fax_emails.php @@ -210,8 +210,12 @@ if (sizeof($result) != 0) { } //send fax + $cwd = getcwd(); $included = true; require("fax_send.php"); + if($cwd){ + chdir($cwd); + } //reset variables unset($fax_numbers); From 9f59353061eafce880747de864c8cb09f031cce3 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Mon, 23 Nov 2015 14:57:44 +0300 Subject: [PATCH 13/21] Change. Use function to deal with fax_queue in fax_send.php. --- app/fax/fax_send.php | 123 +++++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 56 deletions(-) diff --git a/app/fax/fax_send.php b/app/fax/fax_send.php index a059e7ad40..b50cbdbd91 100644 --- a/app/fax/fax_send.php +++ b/app/fax/fax_send.php @@ -130,6 +130,68 @@ if(!function_exists('gs_cmd')) { } } +if(!function_exists('fax_enqueue')) { + function fax_enqueue($fax_uuid, $fax_file, $wav_file, $fax_uri, $fax_dtmf, $dial_string){ + global $db, $db_type; + + $task_uuid = uuid(); + $dial_string .= "task_uuid='" . $task_uuid . "',"; + $description = ''; //! @todo add description + if ($db_type == "pgsql") { + $date_utc_now_sql = "NOW() at time zone 'utc'"; + } + if ($db_type == "mysql") { + $date_utc_now_sql = "UTC_TIMESTAMP()"; + } + if ($db_type == "sqlite") { + $date_utc_now_sql = "datetime('now')"; + } + $sql = <<prepare($sql); + $i = 0; + $stmt->bindValue(++$i, $task_uuid); + $stmt->bindValue(++$i, $fax_uuid); + $stmt->bindValue(++$i, $fax_file); + $stmt->bindValue(++$i, $wav_file); + $stmt->bindValue(++$i, $fax_uri); + $stmt->bindValue(++$i, $dial_string); + $stmt->bindValue(++$i, $fax_dtmf); + $stmt->bindValue(++$i, $description); + if ($stmt->execute()) { + $response = 'Enqueued'; + } + else{ + //! @todo log error + $response = 'Fail enqueue'; + var_dump($db->errorInfo()); + } + unset($stmt); + return $response; + } +} + +if(!function_exists('fax_split_dtmf')) { +function fax_split_dtmf(&$fax_number, &$fax_dtmf){ + $tmp = array(); + $fax_dtmf = ''; + if(preg_match('/^\s*(.*?)\s*\((.*)\)\s*$/', $fax_number, $tmp)){ + $fax_number = $tmp[1]; + $fax_dtmf = $tmp[2]; + } +} +} + //get the fax extension if (strlen($fax_extension) > 0) { //set the fax directories. example /usr/local/freeswitch/storage/fax/329/inbox @@ -200,15 +262,10 @@ if(!function_exists('gs_cmd')) { $continue = true; } - // cleanup numbers +// cleanup numbers if (isset($fax_numbers)) { foreach ($fax_numbers as $index => $fax_number) { - $tmp=array(); - $fax_dtmf = ''; - if(preg_match('/^\s*(.*?)\s*\((.*)\)\s*$/', $fax_number, $tmp)){ - $fax_number = $tmp[1]; - $fax_dtmf = $tmp[2]; - } + fax_split_dtmf($fax_number, $fax_dtmf); $fax_number = preg_replace("~[^0-9]~", "", $fax_number); $fax_dtmf = preg_replace("~[^0-9Pp*#]~", "", $fax_dtmf); if ($fax_number != ''){ @@ -647,12 +704,7 @@ if(!function_exists('gs_cmd')) { foreach ($fax_numbers as $fax_number) { $dial_string = $common_dial_string; - $tmp = array(); - $fax_dtmf = ''; - if(preg_match('/^\s*(.*?)\s*\((.*)\)\s*$/', $fax_number, $tmp)){ - $fax_number = $tmp[1]; - $fax_dtmf = $tmp[2]; - } + fax_split_dtmf($fax_number, $fax_dtmf); //prepare the fax command $route_array = outbound_route_to_bridge($_SESSION['domain_uuid'], $fax_prefix . $fax_number); @@ -667,7 +719,6 @@ if(!function_exists('gs_cmd')) { $fax_uri = $route_array[0]; $t38 = "fax_enable_t38=true,fax_enable_t38_request=true,"; } - $tail_dial_string = $fax_uri." &txfax('".$fax_file."')"; if ($fax_send_mode != 'queue') { $dial_string .= $t38; @@ -678,7 +729,7 @@ if(!function_exists('gs_cmd')) { $dial_string .= "fax_verbose=true" . ","; $dial_string .= "fax_use_ecm=off" . ","; $dial_string .= "api_hangup_hook='lua fax_retry.lua'"; - $dial_string = "{" . $dial_string . "}" . $tail_dial_string; + $dial_string = "{" . $dial_string . "}" . $fax_uri." &txfax('".$fax_file."')"; $fp = event_socket_create($_SESSION['event_socket_ip_address'], $_SESSION['event_socket_port'], $_SESSION['event_socket_password']); if ($fp) { @@ -692,48 +743,8 @@ if(!function_exists('gs_cmd')) { fclose($fp); } else{ // enqueue - $task_uuid = uuid(); - $dial_string .= "task_uuid='" . $task_uuid . "',"; $wav_file = ''; //! @todo add custom message - $description = ''; //! @todo add description - if ($db_type == "pgsql") { - $date_utc_now_sql = "NOW() at time zone 'utc'"; - } - if ($db_type == "mysql") { - $date_utc_now_sql = "UTC_TIMESTAMP()"; - } - if ($db_type == "sqlite") { - $date_utc_now_sql = "datetime('now')"; - } - $sql = <<prepare($sql); - $i = 0; - $stmt->bindValue(++$i, $task_uuid); - $stmt->bindValue(++$i, $fax_uuid); - $stmt->bindValue(++$i, $fax_file); - $stmt->bindValue(++$i, $wav_file); - $stmt->bindValue(++$i, $fax_uri); - $stmt->bindValue(++$i, $dial_string); - $stmt->bindValue(++$i, $fax_dtmf); - $stmt->bindValue(++$i, $description); - if ($stmt->execute()) { - $response = 'Enqueued'; - } - else{ - //! @todo log error - $response = 'Fail enqueue'; - } + $response = fax_enqueue($fax_uuid, $fax_file, $wav_file, $fax_uri, $fax_dtmf, $dial_string); } } From d5e63317d67763ae913034f317e84339bfa5e52c Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Mon, 23 Nov 2015 15:12:55 +0300 Subject: [PATCH 14/21] Add. Use fax queue to do fax forward. --- secure/fax_to_email.php | 458 ++++++++++++++++++++++++++++++---------- 1 file changed, 346 insertions(+), 112 deletions(-) diff --git a/secure/fax_to_email.php b/secure/fax_to_email.php index 6972b225b9..2699de074a 100644 --- a/secure/fax_to_email.php +++ b/secure/fax_to_email.php @@ -37,6 +37,253 @@ if (defined('STDIN')) { //echo "$document_root is document_root\n"; } +if (stristr(PHP_OS, 'WIN')) { $IS_WINDOWS = true; } else { $IS_WINDOWS = false; } + +if(!function_exists('exec_in_dir')) { + function exec_in_dir($dir, $cmd, &$ok){ + $args = func_get_args(); + $cwd = getcwd(); + chdir($dir); + $output = array(); + $ret = 0; + $result = exec($cmd, $output, $ret); + if($cwd) + chdir($cwd); + $ok = ($ret == 0); + return join($output, "\n"); + } +} + +if(!function_exists('correct_path')) { + function correct_path($p) { + global $IS_WINDOWS; + if ($IS_WINDOWS) { + return str_replace('/', '\\', $p); + } + return $p; + } +} + +if(!function_exists('path_join')) { + function path_join() { + $args = func_get_args(); + $paths = array(); + foreach ($args as $arg) { + $paths = array_merge($paths, (array)$arg); + } + + $paths = array_map(create_function('$p', 'return trim($p, "/");'), $paths); + $paths = array_filter($paths); + return join('/', $paths); + } +} + +if(!function_exists('tiff2pdf')) { + function tiff2pdf($tiff_file_name){ + //convert the tif to a pdf + //Ubuntu: apt-get install libtiff-tools + + global $IS_WINDOWS; + + if(!file_exists($tiff_file_name)){ + echo "tiff file does not exists"; + return false; // "tiff file does not exists"; + } + + $GS = $IS_WINDOWS ? 'gswin32c' : 'gs'; + $tiff_file = pathinfo($tiff_file_name); + $dir_fax = $tiff_file['dirname']; + $fax_file_name = $tiff_file['filename']; + $pdf_file_name = path_join( $dir_fax, $fax_file_name . '.pdf' ); + + if(file_exists($pdf_file_name)) + return $pdf_file_name; + + $dir_fax_temp = $_SESSION['server']['temp']['dir']; + if(!$dir_fax_temp){ + $dir_fax_temp = path_join(dirname($dir_fax), 'temp'); + } + + if(!file_exists($dir_fax_temp)){ + echo"can not create temporary directory"; + return false; // + } + + $cmd = "tiffinfo " . correct_path($tiff_file_name) . ' | grep "Resolution:"'; + $ok = false; + $resp = exec_in_dir($dir_fax, $cmd, $ok); + if(!$ok){ + echo"can not find fax resoulution"; + return false; // "can not find fax resoulution" + } + + $ppi_w = 0; + $ppi_h = 0; + $tmp = array(); + if(preg_match('/Resolution.*?(\d+).*?(\d+)/', $resp, $tmp)){ + $ppi_w = $tmp[1]; + $ppi_h = $tmp[2]; + } + + $cmd = "tiffinfo " . $tiff_file_name . ' | grep "Image Width:"'; + $resp = exec_in_dir($dir_fax, $cmd, $ok); + if(!$ok){ + echo"can not find fax size"; + return false; // "can not find fax size" + } + + $pix_w = 0; + $pix_h = 0; + $tmp = array(); + if(preg_match('/Width.*?(\d+).*?Length.*?(\d+)/', $resp, $tmp)){ + $pix_w = $tmp[1]; + $pix_h = $tmp[2]; + } + + $page_width = $pix_w / $ppi_w; + $page_height = $pix_h / $ppi_h; + $page_size = 'a4'; + + if (($page_width > 8.4) && ($page_height > 13)) { + $page_width = 8.5; + $page_height = 14; + $page_size = 'legal'; + } + elseif (($page_width > 8.4) && ($page_height < 12)) { + $page_width = 8.5; + $page_height = 11; + $page_size = 'letter'; + } + elseif (($page_width < 8.4) && ($page_height > 11)) { + $page_width = 8.3; + $page_height = 11.7; + $page_size = 'a4'; + } + $page_width = sprintf('%.4f', $page_width); + $page_height = sprintf('%.4f', $page_height); + + $cmd = join(array('tiff2pdf', + '-i -u i', + '-p', $page_size, + '-w', $page_width, + '-l', $page_height, + '-f', + '-o', correct_path(path_join($dir_fax_temp, $fax_file_name . '.pdf')), + correct_path($tiff_file_name), + ), ' '); + + $resp = exec_in_dir($dir_fax, $cmd, $ok); + + if(!file_exists(path_join($dir_fax_temp, $fax_file_name . '.pdf'))){ + echo "can not create temporary pdf: $resp"; + return false; + } + + $cmd = join(array($GS, + '-q -sDEVICE=tiffg3', + '-r' . $ppi_w . 'x' . $ppi_h, + '-g' . $pix_w . 'x' . $pix_h, + '-dNOPAUSE', + '-sOutputFile=' . $fax_file_name . '_temp.tif', + '--', + $fax_file_name . '.pdf', + '-c quit', + ), ' '); + + $resp = exec_in_dir($dir_fax_temp, $cmd, $ok); + + unlink(path_join($dir_fax_temp, $fax_file_name . '.pdf')); + + if(!file_exists(path_join($dir_fax_temp, $fax_file_name . '_temp.tif'))){ + echo "can not temporary tiff: $resp"; + return false; + } + + $cmd = join(array('tiff2pdf', + '-i -u i', + '-p', $page_size, + '-w', $page_width, + '-l', $page_height, + '-f', + '-o', correct_path($pdf_file_name), + correct_path(path_join($dir_fax_temp, $fax_file_name . '_temp.tif')), + ), ' '); + + $resp = exec_in_dir($dir_fax, $cmd, $ok); + + unlink(path_join($dir_fax_temp, $fax_file_name . '_temp.tif')); + + if(!file_exists($pdf_file_name)){ + echo "can not create pdf: $resp"; + return false; + } + + return $pdf_file_name; + } +} + +if(!function_exists('fax_enqueue')) { + function fax_enqueue($fax_uuid, $fax_file, $wav_file, $fax_uri, $fax_dtmf, $dial_string){ + global $db, $db_type; + + $task_uuid = uuid(); + $dial_string .= "task_uuid='" . $task_uuid . "',"; + $description = ''; //! @todo add description + if ($db_type == "pgsql") { + $date_utc_now_sql = "NOW() at time zone 'utc'"; + } + if ($db_type == "mysql") { + $date_utc_now_sql = "UTC_TIMESTAMP()"; + } + if ($db_type == "sqlite") { + $date_utc_now_sql = "datetime('now')"; + } + $sql = <<prepare($sql); + $i = 0; + $stmt->bindValue(++$i, $task_uuid); + $stmt->bindValue(++$i, $fax_uuid); + $stmt->bindValue(++$i, $fax_file); + $stmt->bindValue(++$i, $wav_file); + $stmt->bindValue(++$i, $fax_uri); + $stmt->bindValue(++$i, $dial_string); + $stmt->bindValue(++$i, $fax_dtmf); + $stmt->bindValue(++$i, $description); + if ($stmt->execute()) { + $response = 'Enqueued'; + } + else{ + //! @todo log error + $response = 'Fail enqueue'; + var_dump($db->errorInfo()); + } + unset($stmt); + return $response; + } +} + +if(!function_exists('fax_split_dtmf')) { + function fax_split_dtmf(&$fax_number, &$fax_dtmf){ + $tmp = array(); + $fax_dtmf = ''; + if(preg_match('/^\s*(.*?)\s*\((.*)\)\s*$/', $fax_number, $tmp)){ + $fax_number = $tmp[1]; + $fax_dtmf = $tmp[2]; + } + } +} + //includes if (!defined('STDIN')) { include "root.php"; } require_once "resources/require.php"; @@ -115,9 +362,11 @@ if (defined('STDIN')) { echo "fax_email is ".$fax_email."\n"; //get the fax file name (only) if a full path + $fax_file = realpath($fax_file); $array = explode("/", $fax_file); $fax_file_only = $array[count($array)-1]; $fax_file_name = pathinfo($fax_file_only, PATHINFO_FILENAME); + $dir_fax = pathinfo($fax_file, PATHINFO_DIRNAME); unset($array); //used for debug @@ -153,6 +402,7 @@ if (defined('STDIN')) { foreach ($result as &$row) { //set database fields as variables //$fax_email = $row["fax_email"]; + $fax_uuid = $row["fax_uuid"]; $fax_accountcode = $row["fax_accountcode"]; $fax_pin_number = $row["fax_pin_number"]; $fax_caller_id_name = $row["fax_caller_id_name"]; @@ -164,124 +414,108 @@ if (defined('STDIN')) { unset ($prep_statement); //set the fax directory - $dir_fax = $_SESSION['switch']['storage']['dir'].'/fax/'.$domain_name.'/'.$fax_extension.'/inbox'; - echo "dir_fax is $dir_fax\n"; if (!file_exists($dir_fax)) { - $dir_fax = $_SESSION['switch']['storage']['dir'].'/fax/'.$fax_extension.'/inbox'; - } - -//convert the tif to a pdf - //Ubuntu: apt-get install libtiff-tools - $fax_file_warning = ""; - if (file_exists($dir_fax.'/'.$fax_file_name.".tif")) { - if (!file_exists($dir_fax.'/'.$fax_file_name.".pdf")) { - //define temp directory - $dir_fax_temp = str_replace('/inbox', '/temp', $dir_fax); - if (!is_dir($dir_fax_temp)) { - mkdir($dir_fax_temp,0774,true); - chmod($dir_fax_temp,0774); - } - //enter fax directory - chdir($dir_fax); - //get fax resolution (ppi, W & H) - $resp = exec("tiffinfo ".$fax_file_name.".tif | grep 'Resolution:'"); - $resp_array = explode(' ', trim($resp)); - $ppi_w = (int) $resp_array[1]; - $ppi_h = (int) $resp_array[2]; - unset($resp_array); - $gs_r = $ppi_w.'x'.$ppi_h; //used by ghostscript - //get page dimensions/size (pixels/inches, W & H) - $resp = exec("tiffinfo ".$fax_file_name.".tif | grep 'Image Width:'"); - $resp_array = explode(' ', trim($resp)); - $pix_w = $resp_array[2]; - $pix_h = $resp_array[5]; - unset($resp_array); - $gs_g = $pix_w.'x'.$pix_h; //used by ghostscript - $page_width = $pix_w / $ppi_w; - $page_height = $pix_h / $ppi_h; - if ($page_width > 8.4 && $page_height > 13) { - $page_width = 8.5; - $page_height = 14; - $page_size = 'legal'; - } - else if ($page_width > 8.4 && $page_height < 12) { - $page_width = 8.5; - $page_height = 11; - $page_size = 'letter'; - } - else if ($page_width < 8.4 && $page_height > 11) { - $page_width = 8.3; - $page_height = 11.7; - $page_size = 'a4'; - } - //generate pdf (a work around, as tiff2pdf improperly inverts the colors) - $cmd_tif2pdf = "tiff2pdf -i -u i -p ".$page_size." -w ".$page_width." -l ".$page_height." -f -o ".$dir_fax_temp.'/'.$fax_file_name.".pdf ".$dir_fax.'/'.$fax_file_name.".tif"; - exec($cmd_tif2pdf); - chdir($dir_fax_temp); - $cmd_pdf2tif = "gs -q -sDEVICE=tiffg3 -r".$gs_r." -g".$gs_g." -dNOPAUSE -sOutputFile=".$fax_file_name."_temp.tif -- ".$fax_file_name.".pdf -c quit"; - exec($cmd_pdf2tif); //convert pdf to tif - @unlink($dir_fax_temp.'/'.$fax_file_name.".pdf"); - $cmd_tif2pdf = "tiff2pdf -i -u i -p ".$page_size." -w ".$page_width." -l ".$page_height." -f -o ".$dir_fax.'/'.$fax_file_name.".pdf ".$dir_fax_temp.'/'.$fax_file_name."_temp.tif"; - exec($cmd_tif2pdf); - @unlink($dir_fax_temp.'/'.$fax_file_name."_temp.tif"); + $dir_fax = $_SESSION['switch']['storage']['dir'].'/fax/'.$domain_name.'/'.$fax_extension.'/inbox'; + echo "dir_fax is $dir_fax\n"; + if (!file_exists($dir_fax)) { + $dir_fax = $_SESSION['switch']['storage']['dir'].'/fax/'.$fax_extension.'/inbox'; } } - else { - $fax_file_warning = " Fax image not available on server."; - echo $fax_file_warning."
"; + + $pdf_file = tiff2pdf($fax_file); + if(!$pdf_file){ + $fax_file_warning = ' Fax image not available on server.'; } //forward the fax - if (strpos($fax_file_name,'#') !== false) { - $tmp = explode("#",$fax_file_name); - $fax_forward_number = $tmp[0]; - } + if(file_exists($fax_file)) { + if (strpos($fax_file_name,'#') !== false) { + $tmp = explode("#",$fax_file_name); + $fax_forward_number = $tmp[0]; + } - echo "fax_forward_number is $fax_forward_number\n"; - if (strlen($fax_forward_number) > 0) { - if (file_exists($dir_fax."/".$fax_file_name.".tif")) { - //get the event socket information - $sql = "select * from v_settings "; - $prep_statement = $db->prepare(check_sql($sql)); - $prep_statement->execute(); - $result = $prep_statement->fetchAll(PDO::FETCH_ASSOC); - foreach ($result as &$row) { - $event_socket_ip_address = $row["event_socket_ip_address"]; - $event_socket_port = $row["event_socket_port"]; - $event_socket_password = $row["event_socket_password"]; - break; - } - //create the event socket connection - $fp = event_socket_create($event_socket_ip_address, $event_socket_port, $event_socket_password); - //send the command with event socket - if ($fp) { - //prepare the fax originate command - $route_array = outbound_route_to_bridge($_SESSION['domain_uuid'], $fax_forward_number); - $fax_file = $dir_fax."/".$fax_file_name.".tif"; - if (count($route_array) == 0) { - //send the internal call to the registered extension - $fax_uri = "user/".$fax_forward_number."@".$domain_name; - $t38 = ""; - } - else { - //send the external call - $fax_uri = $route_array[0]; - $t38 = "fax_enable_t38=true,fax_enable_t38_request=true"; - } - $cmd = "api originate {absolute_codec_string='PCMU,PCMA',accountcode='".$fax_accountcode."',sip_h_X-accountcode='".$fax_accountcode."',domain_uuid=".$_SESSION["domain_uuid"].",domain_name=".$_SESSION["domain_name"].",mailto_address='".$mailto_address."',mailfrom_address='".$mailfrom_address."',origination_caller_id_name='".$fax_caller_id_name."',origination_caller_id_number=".$fax_caller_id_number.",fax_uri=".$fax_uri.",fax_file='".$fax_file."',fax_retry_attempts=1,fax_retry_limit=20,fax_retry_sleep=180,fax_verbose=true,fax_use_ecm=off,".$t38.",api_hangup_hook='lua fax_retry.lua'}".$fax_uri." &txfax('".$fax_file."')"; - //send info to the log - echo "fax forward\n"; - echo $cmd."\n"; - //send the command to event socket - $response = event_socket_request($fp, $cmd); - $response = str_replace("\n", "", $response); - //send info to the log - echo "response: ".$response."\n"; - //get the uuid - $uuid = str_replace("+OK ", "", $response); - //close event socket - fclose($fp); - } + echo "fax_forward_number is $fax_forward_number\n"; + if (strlen($fax_forward_number) > 0) { + fax_split_dtmf($fax_forward_number, $fax_dtmf); + + $fax_send_mode = $_SESSION['fax']['send_mode']['text']; + if(strlen($fax_send_mode) == 0){ + $fax_send_mode = 'direct'; + } + + $route_array = outbound_route_to_bridge($_SESSION['domain_uuid'], $fax_forward_number); + if (count($route_array) == 0) { + //send the internal call to the registered extension + $fax_uri = "user/".$fax_forward_number."@".$domain_name; + $t38 = ""; + } + else { + //send the external call + $fax_uri = $route_array[0]; + $t38 = "fax_enable_t38=true,fax_enable_t38_request=true"; + } + + $common_dial_string = "absolute_codec_string='PCMU,PCMA',"; + $common_dial_string .= "accountcode='" . $fax_accountcode . "',"; + $common_dial_string .= "sip_h_X-accountcode='" . $fax_accountcode . "',"; + $common_dial_string .= "domain_uuid=" . $_SESSION["domain_uuid"] . ","; + $common_dial_string .= "domain_name=" . $_SESSION["domain_name"] . ","; + $common_dial_string .= "mailto_address='" . $mailto_address . "',"; + $common_dial_string .= "mailfrom_address='" . $mailfrom_address . "',"; + $common_dial_string .= "origination_caller_id_name='" . $fax_caller_id_name . "',"; + $common_dial_string .= "origination_caller_id_number='" . $fax_caller_id_number . "',"; + $common_dial_string .= "fax_ident='" . $fax_caller_id_number . "',"; + $common_dial_string .= "fax_header='" . $fax_caller_id_name . "',"; + $common_dial_string .= "fax_file='" . $fax_file . "',"; + + if ($fax_send_mode != 'queue') { + $dial_string .= $t38; + $dial_string .= "fax_uri=" . $fax_uri . ","; + $dial_string .= "fax_retry_attempts=1" . ","; + $dial_string .= "fax_retry_limit=20" . ","; + $dial_string .= "fax_retry_sleep=180" . ","; + $dial_string .= "fax_verbose=true" . ","; + $dial_string .= "fax_use_ecm=off" . ","; + $dial_string .= "api_hangup_hook='lua fax_retry.lua'"; + $dial_string = "{" . $dial_string . "}" . $fax_uri." &txfax('".$fax_file."')"; + + //get the event socket information + $sql = "select * from v_settings "; + $prep_statement = $db->prepare(check_sql($sql)); + $prep_statement->execute(); + $result = $prep_statement->fetchAll(PDO::FETCH_ASSOC); + foreach ($result as &$row) { + $event_socket_ip_address = $row["event_socket_ip_address"]; + $event_socket_port = $row["event_socket_port"]; + $event_socket_password = $row["event_socket_password"]; + break; + } + + //create the event socket connection + $fp = event_socket_create($event_socket_ip_address, $event_socket_port, $event_socket_password); + + //send the command with event socket + if ($fp) { + //prepare the fax originate command + $cmd = "api originate " . $dial_string; + //send info to the log + echo "fax forward\n"; + echo $cmd."\n"; + //send the command to event socket + $response = event_socket_request($fp, $cmd); + $response = str_replace("\n", "", $response); + //send info to the log + echo "response: ".$response."\n"; + //get the uuid + $uuid = str_replace("+OK ", "", $response); + //close event socket + fclose($fp); + } + } + else{ + $wav_file = ''; + $response = fax_enqueue($fax_uuid, $fax_file, $wav_file, $fax_uri, $fax_dtmf, $dial_string); + } } } @@ -413,4 +647,4 @@ if (defined('STDIN')) { fwrite($fp, $content); fclose($fp); -?> +?> \ No newline at end of file From bbe62a294162bdb13755bd81afb42175c0afc6cd Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Mon, 23 Nov 2015 15:51:05 +0300 Subject: [PATCH 15/21] Fix. Use fax_dir only if we can not find fax file. --- secure/fax_to_email.php | 50 +++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/secure/fax_to_email.php b/secure/fax_to_email.php index 2699de074a..2ca26ee3c1 100644 --- a/secure/fax_to_email.php +++ b/secure/fax_to_email.php @@ -358,21 +358,12 @@ if(!function_exists('fax_split_dtmf')) { } $mailto_address = $fax_email; - echo "mailto_adress is ".$mailto_address."\n"; - echo "fax_email is ".$fax_email."\n"; //get the fax file name (only) if a full path - $fax_file = realpath($fax_file); - $array = explode("/", $fax_file); - $fax_file_only = $array[count($array)-1]; - $fax_file_name = pathinfo($fax_file_only, PATHINFO_FILENAME); - $dir_fax = pathinfo($fax_file, PATHINFO_DIRNAME); - unset($array); - -//used for debug - echo "fax_email $fax_email\n"; - echo "fax_extension $fax_extension\n"; - echo "fax_name $fax_file_only\n"; + $fax_path = pathinfo($fax_file); + $fax_file_only = $fax_path['basename']; + $fax_file_name = $fax_path['filename']; + $dir_fax = $fax_path['dirname']; //get the domain_uuid from the database $sql = "select * from v_domains "; @@ -414,18 +405,33 @@ if(!function_exists('fax_split_dtmf')) { unset ($prep_statement); //set the fax directory - if (!file_exists($dir_fax)) { + if (!file_exists($dir_fax) || !file_exists(path_join($dir_fax, $fax_file_only))) { $dir_fax = $_SESSION['switch']['storage']['dir'].'/fax/'.$domain_name.'/'.$fax_extension.'/inbox'; - echo "dir_fax is $dir_fax\n"; - if (!file_exists($dir_fax)) { + if (!file_exists($dir_fax) || !file_exists(path_join($dir_fax, $fax_file_only))) { $dir_fax = $_SESSION['switch']['storage']['dir'].'/fax/'.$fax_extension.'/inbox'; } } + $fax_file = path_join($dir_fax, $fax_file_only); + +//used for debug + echo "mailto_adress is $mailto_address\n"; + echo "fax_email is $fax_email\n"; + echo "fax_extension is $fax_extension\n"; + echo "fax_name is $fax_file_only\n"; + echo "dir_fax is $dir_fax\n"; + echo "full_path is $fax_file\n"; + $pdf_file = tiff2pdf($fax_file); if(!$pdf_file){ $fax_file_warning = ' Fax image not available on server.'; } + else{ + $fax_file_warning = ''; + } + +//used for debug + echo "pdf file is $pdf_file\n"; //forward the fax if(file_exists($fax_file)) { @@ -520,7 +526,7 @@ if(!function_exists('fax_split_dtmf')) { } //send the email - if (strlen($fax_email) > 0 && file_exists($dir_fax."/".$fax_file_name.".tif")) { + if (strlen($fax_email) > 0 && file_exists($fax_file)) { //prepare the message $tmp_subject = (($fax_email_inbound_subject_tag != '') ? "[".$fax_email_inbound_subject_tag."]" : "Fax Received").": ".$fax_file_name; @@ -574,11 +580,11 @@ if(!function_exists('fax_split_dtmf')) { //add the attachments if (strlen($fax_file_name) > 0) { - if (file_exists($dir_fax.'/'.$fax_file_name.".pdf")) { - $mail->AddAttachment($dir_fax.'/'.$fax_file_name.'.pdf'); // pdf attachment + if ($pdf_file && file_exists($pdf_file)) { + $mail->AddAttachment($pdf_file); // pdf attachment } else { - $mail->AddAttachment($dir_fax.'/'.$fax_file_name.'.tif'); // tif attachment + $mail->AddAttachment($fax_file); // tif attachment } //$filename='fax.tif'; $encoding = "base64"; $type = "image/tif"; //$mail->AddStringAttachment(base64_decode($strfax),$filename,$encoding,$type); @@ -607,7 +613,7 @@ if(!function_exists('fax_split_dtmf')) { // failed_fax_emails.sh - this is created when we have a email we need to re-send. At the time it is created, an at job is created to execute it in 3 minutes time, // this allows us to try sending the email again at that time. If the file exists but there is no at job this is because there are no longer any emails queued // as we have successfully sent them all. - if (strlen($fax_email) > 0 && file_exists($dir_fax."/".$fax_file_name.".tif")) { + if (strlen($fax_email) > 0 && file_exists($fax_file)) { if (stristr(PHP_OS, 'WIN')) { //not compatible with windows } @@ -621,7 +627,7 @@ if(!function_exists('fax_split_dtmf')) { } else { // create an instruction log to email messages once the connection to the mail server has been restored $fp = fopen($fax_to_email_queue_dir."/failed_fax_emails.log", "a"); - fwrite($fp, PHP_BINDIR."/php ".$_SERVER["DOCUMENT_ROOT"].PROJECT_PATH."/secure/fax_to_email.php email='".$fax_email."' extension=".$fax_extension." name='".$dir_fax.'/'.$fax_file_only."' messages='".$fax_messages."' domain=".$domain_name." caller_id_name='".$caller_id_name."' caller_id_number=".$caller_id_number." retry=true\n"); + fwrite($fp, PHP_BINDIR."/php ".$_SERVER["DOCUMENT_ROOT"].PROJECT_PATH."/secure/fax_to_email.php email='".$fax_email."' extension=".$fax_extension." name='".$fax_file."' messages='".$fax_messages."' domain=".$domain_name." caller_id_name='".$caller_id_name."' caller_id_number=".$caller_id_number." retry=true\n"); fclose($fp); // create a script to do the delayed mailing $fp = fopen($_SESSION['server']['temp']['dir']."/failed_fax_emails.sh", "w"); From d2f0d65842b569abbdb8f42e2da788bb2fc76c67 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Mon, 23 Nov 2015 16:33:43 +0300 Subject: [PATCH 16/21] Fix. Update language file. --- app/fax/app_languages.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/fax/app_languages.php b/app/fax/app_languages.php index 93ad7aa319..5d47714551 100644 --- a/app/fax/app_languages.php +++ b/app/fax/app_languages.php @@ -1111,6 +1111,10 @@ $text['label-accountcode']['de-at'] = "Account Code"; $text['label-accountcode']['ro'] = "Cod cont"; $text['label-accountcode']['he'] = "קוד חשבון"; +$text['label-fax_send_greeting']['en-us'] = "Greeting"; + +$text['label-fax_send_channels']['en-us'] = "Number of channels"; + $text['header-sent']['en-us'] = "Sent Faxes"; $text['header-sent']['es-cl'] = "Los Faxes Enviados"; $text['header-sent']['pt-pt'] = "Faxes Enviados"; From 2a3b762aa2d49ad57b35245d98b778559aea2cf1 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Tue, 24 Nov 2015 13:54:04 +0300 Subject: [PATCH 17/21] Fix. `path_join` function when first argument is full path. --- secure/fax_to_email.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/secure/fax_to_email.php b/secure/fax_to_email.php index 2ca26ee3c1..6a184b001a 100644 --- a/secure/fax_to_email.php +++ b/secure/fax_to_email.php @@ -72,9 +72,22 @@ if(!function_exists('path_join')) { $paths = array_merge($paths, (array)$arg); } - $paths = array_map(create_function('$p', 'return trim($p, "/");'), $paths); + $prefix = null; + foreach($paths as &$path) { + if($prefix === null && strlen($path) > 0) { + if(substr($path, 0, 1) == '/') $prefix = '/'; + else $prefix = ''; + } + $path = trim( $path, '/' ); + } + + if($prefix === null){ + return ''; + } + $paths = array_filter($paths); - return join('/', $paths); + + return $prefix . join('/', $paths); } } From 6d1e73ca13ff403ee3ec03f426b666b362b15b51 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Tue, 24 Nov 2015 14:26:21 +0300 Subject: [PATCH 18/21] Fix. Add `split` function (required by lazy_settings). --- .../scripts/resources/functions/split.lua | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 resources/install/scripts/resources/functions/split.lua diff --git a/resources/install/scripts/resources/functions/split.lua b/resources/install/scripts/resources/functions/split.lua new file mode 100644 index 0000000000..c46e0cad0b --- /dev/null +++ b/resources/install/scripts/resources/functions/split.lua @@ -0,0 +1,27 @@ +function split(str, sep, plain) + local b, res = 1, {} + while b <= #str do + local e, e2 = string.find(str, sep, b, plain) + if e then + res[#res + 1] = string.sub(str, b, e-1) + b = e2 + 1 + else + res[#res + 1] = string.sub(str, b) + break + end + end + return res +end + +function split_first(str, sep, plain) + local e, e2 = string.find(str, sep, nil, plain) + if e then + return string.sub(str, 1, e - 1), string.sub(str, e2 + 1) + end + return str +end + +local unpack = unpack or table.unpack + +function usplit(...) return unpack(split(...)) end + From e4c51396c80ae25da2e48daae881c034d5e092f5 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Tue, 24 Nov 2015 18:49:21 +0300 Subject: [PATCH 19/21] Add. option to work with broken `affected_rows` FS 1.4 --- resources/install/scripts/fax_queue/tasks.lua | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/resources/install/scripts/fax_queue/tasks.lua b/resources/install/scripts/fax_queue/tasks.lua index 2605d6e237..b3827a9051 100644 --- a/resources/install/scripts/fax_queue/tasks.lua +++ b/resources/install/scripts/fax_queue/tasks.lua @@ -19,6 +19,14 @@ else error("unsupported database type: " .. database.type) end +-- Broken on FS 1.4 with native postgresql +-- Fixed on 1.6.0 +-- Also works with ODBC +local ignore_affected_rows = true +if dbh_affected_rows_broken ~= nil then + ignore_affected_rows = dbh_affected_rows_broken +end + local Q850_TIMEOUT = { [17] = 60; } @@ -110,7 +118,11 @@ local function next_task() if not task then return nil, err end local ok, err = db:query( aquire_task_sql:format(task.uuid) ) if not ok then return nil, err end - if db:affected_rows() == 1 then + local rows = db:affected_rows() + if ignore_affected_rows then + rows = 1 + end + if rows == 1 then task.no_answer_counter = tonumber(task.no_answer_counter) task.no_answer_retry_counter = tonumber(task.no_answer_retry_counter) task.retry_counter = tonumber(task.retry_counter) From e51c890c415796b1619f23b5e5c62263790657ee Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 25 Nov 2015 12:42:56 +0300 Subject: [PATCH 20/21] Fix. Log output in retry.lua --- resources/install/scripts/fax_queue/retry.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/resources/install/scripts/fax_queue/retry.lua b/resources/install/scripts/fax_queue/retry.lua index 2adcd45b27..f4744bce73 100644 --- a/resources/install/scripts/fax_queue/retry.lua +++ b/resources/install/scripts/fax_queue/retry.lua @@ -98,8 +98,8 @@ tostring(accountcode) , tostring(origination_caller_id_name) , tostring(origination_caller_id_number) , - tostring(mailfrom_address) , - tostring(mailto_address) , + tostring(from_address) , + tostring(email_address) , tostring(hangup_cause_q850) , fax_options ) @@ -240,8 +240,9 @@ "values(" .. table.concat(values, ",") .. ")" if (debug["sql"]) then - freeswitch.consoleLog("notice", "[FAX] retry: "..sql.."\n"); + log.noticef("SQL: %s", sql); end + dbh:query(sql); end @@ -298,7 +299,7 @@ sql = table.concat(sql, "\n"); if (debug["sql"]) then - freeswitch.consoleLog("notice", "[FAX] SQL: " .. sql .. "\n"); + log.noticef("SQL: %s", sql); end end @@ -317,7 +318,7 @@ if fax_success == "1" then --Success - log.info("RETRY STATS SUCCESS: GATEWAY[%s] VARS[%s]", fax_options, fax_trial); + log.infof("RETRY STATS SUCCESS: GATEWAY[%s]", fax_options); if keep_local == "false" then os.remove(fax_file); From 3e9222d3ab318d241a5107de345b0c80b49435b0 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 25 Nov 2015 13:21:02 +0300 Subject: [PATCH 21/21] Fix. cleanup old tasks. --- resources/install/scripts/fax_queue/tasks.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/install/scripts/fax_queue/tasks.lua b/resources/install/scripts/fax_queue/tasks.lua index b3827a9051..bb09ffb295 100644 --- a/resources/install/scripts/fax_queue/tasks.lua +++ b/resources/install/scripts/fax_queue/tasks.lua @@ -14,7 +14,7 @@ elseif database.type == 'mysql' then now_add_sec_sql = "DATE_ADD(UTC_TIMESTAMP(), INTERVAL %s SECOND)" elseif database.type == 'sqlite' then date_utc_now_sql = "datetime('now')" - now_add_sec_sql = "datetime('now', '+%s seconds')" + now_add_sec_sql = "datetime('now', '%s seconds')" else error("unsupported database type: " .. database.type) end @@ -96,7 +96,7 @@ local release_stuck_tasks_sql = [[ update v_fax_tasks set task_status = 0, task_lock_time = NULL, task_next_time = ]] .. date_utc_now_sql .. [[ - where task_lock_time < ]] .. now_add_sec_sql:format('3600') .. [[ + where task_lock_time < ]] .. now_add_sec_sql:format('-3600') .. [[ ]] local remove_finished_tasks_sql = [[