From a8d5033953c0d65bab361a7c8962fe91067b6a6c Mon Sep 17 00:00:00 2001 From: Chris Black Date: Thu, 19 May 2016 11:09:49 -0700 Subject: [PATCH] Flowroute SMS (#1603) * Fix Yealink provisioning for contacts/groups Fix the Yealink provision templates to allow for directory_extensions, groups and users. * Fix group_uuid and user_uuid messup * Yealink directory and provisioning changes Includes the changes from PR 1582 with some fixes to the SQL. Also merged all the directory_* options into ONE directory.xml Will need to call the it with any of these: http://mydomain/app/provision/?file=directory.xml&contacts=groups http://mydomain/app/provision/?file=directory.xml&contacts=users http://mydomain/app/provision/?file=directory.xml&contacts=extensions http://mydomain/app/provision/?file=directory.xml&contacts=all * Flow route SMS --- app/sms/app_config.php | 86 ++++++++ app/sms/app_defaults.php | 29 +++ app/sms/app_languages.php | 107 ++++++++++ app/sms/app_menu.php | 24 +++ app/sms/root.php | 90 +++++++++ app/sms/sms.php | 137 +++++++++++++ app/sms/sms_api.php | 121 +++++++++++ resources/install/scripts/app/sms/index.lua | 189 ++++++++++++++++++ resources/templates/conf/chatplan/default.xml | 25 ++- 9 files changed, 799 insertions(+), 9 deletions(-) create mode 100644 app/sms/app_config.php create mode 100644 app/sms/app_defaults.php create mode 100644 app/sms/app_languages.php create mode 100644 app/sms/app_menu.php create mode 100644 app/sms/root.php create mode 100644 app/sms/sms.php create mode 100644 app/sms/sms_api.php create mode 100644 resources/install/scripts/app/sms/index.lua diff --git a/app/sms/app_config.php b/app/sms/app_config.php new file mode 100644 index 0000000000..0e3d2117a8 --- /dev/null +++ b/app/sms/app_config.php @@ -0,0 +1,86 @@ + diff --git a/app/sms/app_defaults.php b/app/sms/app_defaults.php new file mode 100644 index 0000000000..380be27d4a --- /dev/null +++ b/app/sms/app_defaults.php @@ -0,0 +1,29 @@ + + Portions created by the Initial Developer are Copyright (C) 2008-2012 + the Initial Developer. All Rights Reserved. + + Contributor(s): + Mark J Crane +*/ + +//if the extensions dir doesn't exist then create it + +?> \ No newline at end of file diff --git a/app/sms/app_languages.php b/app/sms/app_languages.php new file mode 100644 index 0000000000..b2c537fbd2 --- /dev/null +++ b/app/sms/app_languages.php @@ -0,0 +1,107 @@ + \ No newline at end of file diff --git a/app/sms/app_menu.php b/app/sms/app_menu.php new file mode 100644 index 0000000000..3af01191d8 --- /dev/null +++ b/app/sms/app_menu.php @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/app/sms/root.php b/app/sms/root.php new file mode 100644 index 0000000000..6fdf32f37b --- /dev/null +++ b/app/sms/root.php @@ -0,0 +1,90 @@ + + Portions created by the Initial Developer are Copyright (C) 2008-2012 + the Initial Developer. All Rights Reserved. + + Contributor(s): + Mark J Crane +*/ + +// make sure the PATH_SEPARATOR is defined + umask(2); + if (!defined("PATH_SEPARATOR")) { + if (strpos($_ENV["OS"], "Win") !== false) { + define("PATH_SEPARATOR", ";"); + } else { + define("PATH_SEPARATOR", ":"); + } + } + + if (!isset($output_format)) $output_format = (PHP_SAPI == 'cli') ? 'text' : 'html'; + + // make sure the document_root is set + $_SERVER["SCRIPT_FILENAME"] = str_replace("\\", '/', $_SERVER["SCRIPT_FILENAME"]); + if(PHP_SAPI == 'cli'){ + chdir(pathinfo(realpath($_SERVER["PHP_SELF"]), PATHINFO_DIRNAME)); + $script_full_path = str_replace("\\", '/', getcwd() . '/' . $_SERVER["SCRIPT_FILENAME"]); + $dirs = explode('/', pathinfo($script_full_path, PATHINFO_DIRNAME)); + if (file_exists('/project_root.php')) { + $path = '/'; + } else { + $i = 1; + $path = ''; + while ($i < count($dirs)) { + $path .= '/' . $dirs[$i]; + if (file_exists($path. '/project_root.php')) { + break; + } + $i++; + } + } + $_SERVER["DOCUMENT_ROOT"] = $path; + }else{ + $_SERVER["DOCUMENT_ROOT"] = str_replace($_SERVER["PHP_SELF"], "", $_SERVER["SCRIPT_FILENAME"]); + } + $_SERVER["DOCUMENT_ROOT"] = realpath($_SERVER["DOCUMENT_ROOT"]); +// try to detect if a project path is being used + if (!defined('PROJECT_PATH')) { + if (is_dir($_SERVER["DOCUMENT_ROOT"]. '/fusionpbx')) { + define('PROJECT_PATH', '/fusionpbx'); + } elseif (file_exists($_SERVER["DOCUMENT_ROOT"]. '/project_root.php')) { + define('PROJECT_PATH', ''); + } else { + $dirs = explode('/', str_replace('\\', '/', pathinfo($_SERVER["PHP_SELF"], PATHINFO_DIRNAME))); + $i = 1; + $path = $_SERVER["DOCUMENT_ROOT"]; + while ($i < count($dirs)) { + $path .= '/' . $dirs[$i]; + if (file_exists($path. '/project_root.php')) { + break; + } + $i++; + } + if(!file_exists($path. '/project_root.php')){ + die("Failed to locate the Project Root by searching for project_root.php please contact support for assistance"); + } + $project_path = str_replace($_SERVER["DOCUMENT_ROOT"], "", $path); + define('PROJECT_PATH', $project_path); + } + $_SERVER["PROJECT_ROOT"] = realpath($_SERVER["DOCUMENT_ROOT"] . PROJECT_PATH); + set_include_path(get_include_path() . PATH_SEPARATOR . $_SERVER["PROJECT_ROOT"]); + } + +?> \ No newline at end of file diff --git a/app/sms/sms.php b/app/sms/sms.php new file mode 100644 index 0000000000..a753a12b46 --- /dev/null +++ b/app/sms/sms.php @@ -0,0 +1,137 @@ + + James Rose + +*/ + +include "root.php"; +require_once "resources/require.php"; +require_once "resources/check_auth.php"; +if (permission_exists('sms_view')) { + //access granted +} +else { + echo "access denied"; + exit; +} + +//add multi-lingual support + $language = new text; + $text = $language->get(); + +//include the header + require_once "resources/header.php"; + require_once "resources/paging.php"; + + $rows_per_page = ($_SESSION['domain']['paging']['numeric'] != '') ? $_SESSION['domain']['paging']['numeric'] : 50; + + $sql = "select domain_name, extension, sms_message_uuid,start_stamp,from_numer,to_number,message,direction from v_sms_messages, v_domains, v_extensions where v_sms_messages.domain_uuid = v_domains.domain_uuid and v_sms_messages.extension_uuid = v_extensions.extension_uuid and v_domains.domain_uuid = '" . $domain_uuid . "' order by start_stamp"; + error_log("SQL: " . print_r($sql,true)); + $prep_statement = $db->prepare(check_sql($sql)); + $prep_statement->execute(); + $result = $prep_statement->fetchAll(PDO::FETCH_ASSOC); + $result_count = count($result); + unset ($prep_statement, $sql); + + $c = 0; + $row_style["0"] = "row_style0"; + $row_style["1"] = "row_style1"; + +//mod paging parameters for inclusion in column sort heading links + $param = substr($param, 1); //remove leading '&' + $param = substr($param, 0, strrpos($param, '&order_by=')); //remove trailing order by + +//show the results + $col_count = 6; + echo "
\n"; + echo "\n"; + echo "\n"; + echo "\n"; + if ($_REQUEST['showall'] && permission_exists('xml_cdr_all')) { + echo th_order_by('domain_name', $text['label-domain'], $order_by, $order, null, null, $param); + $col_count++; + } + echo th_order_by('extension', $text['label-extension'], $order_by, $order, null, null, $param); + echo th_order_by('start_stamp', $text['label-start'], $order_by, $order, null, "style='text-align: center;'", $param); + echo th_order_by('caller_id_number', $text['label-source'], $order_by, $order, null, null, $param); + echo th_order_by('destination_number', $text['label-destination'], $order_by, $order, null, null, $param); + echo th_order_by('message', $text['label-message'], $order_by, $order, null, null, $param); + echo "\n"; + + if ($result_count > 0) { + echo "\n"; + + //determine if theme images exist + $theme_image_path = $_SERVER["DOCUMENT_ROOT"]."/themes/".$_SESSION['domain']['template']['name']."/images/"; + $theme_cdr_images_exist = ( + file_exists($theme_image_path."icon_cdr_inbound_answered.png") && + file_exists($theme_image_path."icon_cdr_inbound_voicemail.png") && + file_exists($theme_image_path."icon_cdr_inbound_cancelled.png") && + file_exists($theme_image_path."icon_cdr_inbound_failed.png") && + file_exists($theme_image_path."icon_cdr_outbound_answered.png") && + file_exists($theme_image_path."icon_cdr_outbound_cancelled.png") && + file_exists($theme_image_path."icon_cdr_outbound_failed.png") && + file_exists($theme_image_path."icon_cdr_local_answered.png") && + file_exists($theme_image_path."icon_cdr_local_voicemail.png") && + file_exists($theme_image_path."icon_cdr_local_cancelled.png") && + file_exists($theme_image_path."icon_cdr_local_failed.png") + ) ? true : false; + + foreach($result as $index => $row) { + + $tmp_start_epoch = ($_SESSION['domain']['time_format']['text'] == '12h') ? date("j M Y g:i:sa", $row['start_stamp']) : date("j M Y H:i:s", $row['start_epoch']); + + //determine call result and appropriate icon + echo "\n"; + //domain name + if ($_REQUEST['showall'] && permission_exists('xml_cdr_all')) { + echo " \n"; + } + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo " \n"; + echo "\n"; + $c = ($c) ? 0 : 1; + } // end foreach + unset($sql, $result, $row_count); + } // end if +//show the footer + require_once "resources/footer.php"; +?> \ No newline at end of file diff --git a/app/sms/sms_api.php b/app/sms/sms_api.php new file mode 100644 index 0000000000..2d0d8c07e4 --- /dev/null +++ b/app/sms/sms_api.php @@ -0,0 +1,121 @@ + + James Rose + +*/ +include "root.php"; + +//luarun /var/www/fusionpbx/app/sms/sms.lua TO FROM 'BODY' + +$debug = false; + +require_once "resources/require.php"; + +if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $data = json_decode(file_get_contents("php://input")); + + if ($debug) { + error_log('DATA: ' . print_r($data, true)); + } + //create the even socket connection and send the event socket command + $fp = event_socket_create($_SESSION['event_socket_ip_address'], $_SESSION['event_socket_port'], $_SESSION['event_socket_password']); + if (!$fp) { + //error message + echo "
Connection to Event Socket failed.
"; + } + else { + + $to = preg_replace('/(^[1])/','', $data->to); + if ($debug) { + error_log("TO: " . print_r($to,true)); + } + + $sql = "select domain_name, "; + $sql .= "dialplan_detail_data, "; + $sql .= "v_domains.domain_uuid as domain_uuid "; + $sql .= "from v_destinations, "; + $sql .= "v_dialplan_details, "; + $sql .= "v_domains "; + $sql .= "where v_destinations.dialplan_uuid = v_dialplan_details.dialplan_uuid "; + $sql .= "and v_destinations.domain_uuid = v_domains.domain_uuid"; + $sql .= " and destination_number like '" . $to . "' and dialplan_detail_type = 'transfer'"; + $prep_statement = $db->prepare(check_sql($sql)); + $prep_statement->execute(); + $result = $prep_statement->fetchAll(PDO::FETCH_NAMED); + foreach ($result as &$row) { + $domain_name = $row["domain_name"]; + preg_match('/(\d{2,7})/',$row["dialplan_detail_data"],$match); + $domain_uuid = $row["domain_uuid"]; + break; //limit to 1 row + } + unset ($prep_statement); + + if ($debug) { + error_log("MATCH: " . print_r($match,true)); + error_log("DOMAIN_NAME: " . print_r($domain_name,true)); + } + + $sql = "select destination_number "; + $sql .= "from v_ring_groups, v_ring_group_destinations "; + $sql .= "where v_ring_groups.ring_group_uuid = v_ring_group_destinations.ring_group_uuid "; + $sql .= "and ring_group_extension = '" . $match[0] . "' "; + $sql .= "and v_ring_groups.domain_uuid = '" . $domain_uuid . "'"; + $prep_statement = $db->prepare(check_sql($sql)); + $prep_statement->execute(); + $result = $prep_statement->fetchAll(PDO::FETCH_NAMED); + if (count($result)) { + foreach ($result as &$row) { + $switch_cmd = "api luarun app.lua sms inbound "; + $switch_cmd .= $row['destination_number'] . "@" . $domain_name; + $switch_cmd .= " " . $data->from . " '" . $data->body . "'"; + if ($debug) { + error_log(print_r($switch_cmd,true)); + } + $result2 = trim(event_socket_request($fp, $switch_cmd)); + } + } else { + $switch_cmd = "api luarun app.lua sms inbound " . $match[0] . "@" . $domain_name . " " . $data->from . " '" . $data->body . "'"; + if ($debug) { + error_log(print_r($switch_cmd,true)); + } + $result2 = trim(event_socket_request($fp, $switch_cmd)); + } + if ($debug) { + error_log("RESULT: " . print_r($result2,true)); + } + + unset ($prep_statement); + + } + +} +die(); + +?> \ No newline at end of file diff --git a/resources/install/scripts/app/sms/index.lua b/resources/install/scripts/app/sms/index.lua new file mode 100644 index 0000000000..04f0dd2610 --- /dev/null +++ b/resources/install/scripts/app/sms/index.lua @@ -0,0 +1,189 @@ +-- sms.lua +-- Part of FusionPBX +-- Copyright (C) 2010 Mark J Crane +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- 1. Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- 2. Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +-- AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +-- AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +-- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. + +--connect to the database + require "resources.functions.database_handle"; + dbh = database_handle('system'); + +--debug + debug["info"] = false; + debug["sql"] = false; + +--set the api + api = freeswitch.API(); + +--define uuid function + local random = math.random; + local function uuid() + local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; + return string.gsub(template, '[xy]', function (c) + local v = (c == 'x') and random(0, 0xf) or random(8, 0xb); + return string.format('%x', v); + end) + end + +--get the argv values + script_name = argv[0]; + direction = argv[2]; + + if (debug["info"]) then + freeswitch.consoleLog("notice", "[sms] DIRECTION: " .. direction .. "\n"); + freeswitch.consoleLog("info", "chat console\n"); + end + + if direction == "inbound" then + to = argv[3]; + from = argv[4]; + body = argv[5]; + domain_name = string.match(to,'%@+(.+)'); + extension = string.match(to,'%d+'); + + if (debug["info"]) then + freeswitch.consoleLog("notice", "[sms] TO: " .. to .. "\n"); + freeswitch.consoleLog("notice", "[sms] Extension: " .. extension .. "\n"); + freeswitch.consoleLog("notice", "[sms] FROM: " .. from .. "\n"); + freeswitch.consoleLog("notice", "[sms] BODY: " .. body .. "\n"); + freeswitch.consoleLog("notice", "[sms] DOMAIN_NAME: " .. domain_name .. "\n"); + end + + local event = freeswitch.Event("CUSTOM", "SMS::SEND_MESSAGE"); + event:addHeader("proto", "sip"); + event:addHeader("dest_proto", "sip"); + event:addHeader("from", "sip:" .. from); + event:addHeader("from_full", "sip:" .. from); + event:addHeader("sip_profile","internal"); + event:addHeader("to", to); + event:addHeader("subject", "sip:" .. to); + event:addHeader("type", "text/html"); + event:addHeader("hint", "the hint"); + event:addHeader("replying", "true"); + event:addBody(body); + + if (debug["info"]) then + freeswitch.consoleLog("info", event:serialize()); + end + event:fire(); + to = extension; + elseif direction == "outbound" then + to = message:getHeader("to_user"); + domain_name = message:getHeader("from_host"); + from = message:getHeader("from_user"); + body = message:getBody(); + + if (debug["info"]) then + freeswitch.consoleLog("info", message:serialize()); + freeswitch.consoleLog("notice", "[sms] TO: " .. to .. "\n"); + freeswitch.consoleLog("notice", "[sms] FROM: " .. from .. "\n"); + freeswitch.consoleLog("notice", "[sms] BODY: " .. body .. "\n"); + freeswitch.consoleLog("notice", "[sms] DOMAIN_NAME: " .. domain_name .. "\n"); + end + + if (domain_uuid == nil) then + --get the domain_uuid using the domain name required for multi-tenant + if (domain_name ~= nil) then + sql = "SELECT domain_uuid FROM v_domains "; + sql = sql .. "WHERE domain_name = '" .. domain_name .. "' "; + if (debug["sql"]) then + freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n"); + end + status = dbh:query(sql, function(rows) + domain_uuid = rows["domain_uuid"]; + end); + end + end + freeswitch.consoleLog("notice", "[sms] DOMAIN_UUID: " .. domain_uuid .. "\n"); + if (outbound_caller_id_number == nil) then + --get the outbound_caller_id_number using the domain_uuid and the extension number + if (domain_uuid ~= nil) then + sql = "SELECT outbound_caller_id_number, extension_uuid FROM v_extensions "; + sql = sql .. "WHERE domain_uuid = '" .. domain_uuid .. "' and extension = '" .. from .."' "; + if (debug["sql"]) then + freeswitch.consoleLog("notice", "[sms] SQL: " .. sql .. "\n"); + end + status = dbh:query(sql, function(rows) + outbound_caller_id_number = rows["outbound_caller_id_number"]; + extension_uuid = rows["extension_uuid"]; + end); + end + end + + sql = "SELECT default_setting_value FROM v_default_settings "; + sql = sql .. "where default_setting_category = 'sms' and default_setting_subcategory = 'flowroute_access_key'"; + if (debug["sql"]) then + freeswitch.consoleLog("notice", "[sms] SQL: " .. sql .. "\n"); + end + status = dbh:query(sql, function(rows) + flowroute_access_key = rows["default_setting_value"]; + end); + + sql = "SELECT default_setting_value FROM v_default_settings "; + sql = sql .. "where default_setting_category = 'sms' and default_setting_subcategory = 'flowroute_secret_key'"; + if (debug["sql"]) then + freeswitch.consoleLog("notice", "[sms] SQL: " .. sql .. "\n"); + end + status = dbh:query(sql, function(rows) + flowroute_secret_key = rows["default_setting_value"]; + end); + + cmd = "curl -u ".. flowroute_access_key ..":" .. flowroute_secret_key .. " -H \"Content-Type: application/json\" -X POST -d '{\"to\":\"" .. to .. "\",\"from\":\"" .. outbound_caller_id_number .."\",\"body\":\"" .. body .. "\"}' https://api.flowroute.com/v2/messages" + os.execute(cmd) + + end + +--write message to DB + if (domain_uuid == nil) then + --get the domain_uuid using the domain name required for multi-tenant + if (domain_name ~= nil) then + sql = "SELECT domain_uuid FROM v_domains "; + sql = sql .. "WHERE domain_name = '" .. domain_name .. "' "; + if (debug["sql"]) then + freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n"); + end + status = dbh:query(sql, function(rows) + domain_uuid = rows["domain_uuid"]; + end); + end + end + if (extension_uuid == nil) then + --get the extension_uuid using the domain_uuid and the extension number + if (domain_uuid ~= nil) then + sql = "SELECT extension_uuid FROM v_extensions "; + sql = sql .. "WHERE domain_uuid = '" .. domain_uuid .. "' and extension = '" .. extension .."' "; + if (debug["sql"]) then + freeswitch.consoleLog("notice", "[sms] SQL: " .. sql .. "\n"); + end + status = dbh:query(sql, function(rows) + extension_uuid = rows["extension_uuid"]; + end); + end + end + sql = "insert into v_sms_messages"; + sql = sql .. "(sms_message_uuid,extension_uuid,domain_uuid,start_stamp,from_numer,to_number,message,direction,response)"; + sql = sql .. " values ('" .. uuid() .. "','" .. extension_uuid .. "','" .. domain_uuid .."',now(),'" .. from .. "','" .. to .. "','" .. body .. "','" .. direction .. "','')"; + if (debug["sql"]) then + freeswitch.consoleLog("notice", "[sms] "..sql.."\n"); + end + dbh:query(sql); \ No newline at end of file diff --git a/resources/templates/conf/chatplan/default.xml b/resources/templates/conf/chatplan/default.xml index 62f3f020ea..e4676e9c75 100644 --- a/resources/templates/conf/chatplan/default.xml +++ b/resources/templates/conf/chatplan/default.xml @@ -1,11 +1,18 @@ - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file
 
\n"; + if ($theme_cdr_images_exist) { + $call_result = 'answered'; + echo "\n"; + } + else { echo " "; } + echo ""; + echo $row['domain_name'].' '; + echo " ".$row['extension']." ".$row['start_stamp']." ".$row['from_numer']." ".$row['to_number']." ".$row['message']."