diff --git a/app/call_flows/call_flow_edit.php b/app/call_flows/call_flow_edit.php index 808c705a41..a43f3d2aff 100644 --- a/app/call_flows/call_flow_edit.php +++ b/app/call_flows/call_flow_edit.php @@ -230,7 +230,7 @@ if (count($_POST) > 0 && strlen($_POST["persistformvar"]) == 0) { $dialplan->dialplan_uuid = $dialplan_uuid; $dialplan->dialplan_detail_tag = 'condition'; //condition, action, antiaction $dialplan->dialplan_detail_type = 'destination_number'; - $dialplan->dialplan_detail_data = '^'.str_replace('*', '\*', $call_flow_feature_code).'$'; + $dialplan->dialplan_detail_data = '^'.str_replace('+', '\+', str_replace('*', '\*', $call_flow_feature_code)).'$'; $dialplan->dialplan_detail_break = 'on-true'; //$dialplan->dialplan_detail_inline = ''; $dialplan->dialplan_detail_group = '1'; @@ -287,7 +287,7 @@ if (count($_POST) > 0 && strlen($_POST["persistformvar"]) == 0) { $dialplan->dialplan_uuid = $dialplan_uuid; $dialplan->dialplan_detail_tag = 'condition'; //condition, action, antiaction $dialplan->dialplan_detail_type = 'destination_number'; - $dialplan->dialplan_detail_data = '^'.str_replace('*', '\*', $call_flow_extension).'$'; + $dialplan->dialplan_detail_data = '^'.str_replace('+', '\+', str_replace('*', '\*', $call_flow_extension)).'$'; //$dialplan->dialplan_detail_break = ''; //$dialplan->dialplan_detail_inline = ''; $dialplan->dialplan_detail_group = '2'; diff --git a/resources/install/scripts/call_flow_subscribe.lua b/resources/install/scripts/call_flow_subscribe.lua new file mode 100644 index 0000000000..8de2b2a61d --- /dev/null +++ b/resources/install/scripts/call_flow_subscribe.lua @@ -0,0 +1,111 @@ +require "resources.functions.config" +require "resources.functions.split" + +local log = require "resources.functions.log".call_flow_subscribe +local file = require "resources.functions.file" +local presence_in = require "resources.functions.presence_in" +local Database = require "resources.functions.database" + +local ievents = function(events, ...) + if type(events) == 'string' then + events = freeswitch.EventConsumer(events) + end + + local block, timeout = ... + if timeout and (timeout == 0) then block, timeout = 0, 0 end + timeout = timeout or 0 + + return function() + local event = events:pop(block, timeout) + return not event, event + end +end + +local find_call_flow_sql = [[select t1.call_flow_uuid, t1.call_flow_status +from v_call_flows t1 inner join v_domains t2 on t1.domain_uuid = t2.domain_uuid +where t2.domain_name = '%s' and t1.call_flow_feature_code = '%s' +]] + +local function find_call_flow(user) + local ext, domain_name = split_first(user, '@', true) + if not domain_name then return end + local dbh = Database.new('system') + if not dbh then return end + local sql = string.format(find_call_flow_sql, dbh:escape(domain_name), dbh:escape(ext)) + local row = dbh:first_row(sql) + dbh:release() + if not row then return end + return row.call_flow_uuid, row.call_flow_status +end + +local IntervalTimer = {} do +IntervalTimer.__index = IntervalTimer + +function IntervalTimer.new(interval) + local o = setmetatable({}, IntervalTimer) + o._interval = interval + return o +end + +function IntervalTimer:rest() + local d = self._interval - os.difftime(os.time(), self._begin) + if d < 0 then d = 0 end + return d +end + +function IntervalTimer:start() + self._begin = os.time() + return self +end + +function IntervalTimer:stop() + self._begin = nil + return self +end + +end + +local pid_file = scripts_dir .. "/run/call_flow_subscribe.tmp" + +local pid = tostring(os.clock()) + +file.write(pid_file, pid) + +log.notice("start call_flow_subscribe"); + +local timer = IntervalTimer.new(60):start() + +for timeout, event in ievents("PRESENCE_PROBE", 1, timer:rest() * 1000) do + if timeout or timer:rest() == 0 then + local stored = file.read(pid_file) + if stored then + if stored ~= pid then break end + else + if not file.exists(pid_file) then break end + end + timer:start() + end + + if event then + -- log.notice("event:" .. event:serialize("xml")); + if event:getHeader('proto') == 'flow' and + event:getHeader('Event-Calling-Function') == 'sofia_presence_handle_sip_i_subscribe' + then + local from, to = event:getHeader('from'), event:getHeader('to') + local expires = tonumber(event:getHeader('expires')) + if expires and expires > 0 then + local call_flow_uuid, call_flow_status = find_call_flow(to) + if call_flow_uuid then + log.debugf("Find call flow: %s", to) + presence_in.turn_lamp(call_flow_status == "false", to, call_flow_uuid); + else + log.warningf("Can not find call flow: %s", to) + end + else + log.noticef("%s UNSUBSCRIBE from %s", from, to) + end + end + end +end + +log.notice("stop call_flow_subscribe") diff --git a/resources/install/scripts/resources/functions/database.lua b/resources/install/scripts/resources/functions/database.lua index 34989f62aa..35c4e1c87f 100644 --- a/resources/install/scripts/resources/functions/database.lua +++ b/resources/install/scripts/resources/functions/database.lua @@ -77,6 +77,14 @@ local function new_database(backend, backend_name) return result end + function Database:escape(str) + return (string.gsub(str, "'", "''")) + end + + function Database:quote(str) + return "'" .. self:escape(str) .. "'" + end + function Database.__self_test__(...) log.info('self_test Database - ' .. Database._backend_name) local db = Database.new(...) @@ -151,6 +159,16 @@ local function new_database(backend, backend_name) local a = assert(db:first_value("select NULL as a")) assert(a == "") + -- escape + local values = {"hello';select 'world", "hello'"} + for _, value in ipairs(values) do + local a = assert(db:first_value( + string.format("select '%s' as a", db:escape(value)) + )) + assert(a == value) + end + + -- close db:release() assert(not db:connected()) diff --git a/resources/install/scripts/resources/functions/presence_in.lua b/resources/install/scripts/resources/functions/presence_in.lua index b8369f9b17..988850119b 100644 --- a/resources/install/scripts/resources/functions/presence_in.lua +++ b/resources/install/scripts/resources/functions/presence_in.lua @@ -1,6 +1,17 @@ +require "resources.functions.split" + local function turn_lamp(on, user, uuid) + local userid, domain, proto = split_first(user, "@", true) + proto, userid = split_first(userid, "+", true) + if userid then + user = userid .. "@" .. domain + else + proto = "sip" + end + + local event = freeswitch.Event("PRESENCE_IN"); - event:addHeader("proto", "sip"); + event:addHeader("proto", proto); event:addHeader("event_type", "presence"); event:addHeader("alt_event_type", "dialog"); event:addHeader("Presence-Call-Direction", "outbound");