-- xml_handler.lua -- Part of FusionPBX -- Copyright (C) 2013 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. --set defaults expire = {} expire["directory"] = "3600"; expire["dialplan"] = "300"; expire["sofia.conf"] = "3600"; --set the debug options debug["params"] = false; debug["sql"] = false; debug["xml_request"] = false; debug["xml_string"] = false; debug["cache"] = false; --include the lua script scripts_dir = string.sub(debug.getinfo(1).source,2,string.len(debug.getinfo(1).source)-(string.len(argv[0])+1)); include = assert(loadfile(scripts_dir .. "/resources/config.lua")); include(); --connect to the database --ODBC - data source name if (dsn_name) then dbh = freeswitch.Dbh(dsn_name,dsn_username,dsn_password); end --FreeSWITCH core db handler if (db_type == "sqlite") then dbh = freeswitch.Dbh("core:"..db_path.."/"..db_name); end --add the trim function function trim(s) if (s == nil) then s = ''; end return s:gsub("^%s+", ""):gsub("%s+$", "") end --add the explode function function explode ( seperator, str ) local pos, arr = 0, {} for st, sp in function() return string.find( str, seperator, pos, true ) end do -- for each divider found table.insert( arr, string.sub( str, pos, st-1 ) ) -- attach chars left of current divider pos = sp + 1 -- jump past current divider end table.insert( arr, string.sub( str, pos ) ) -- attach chars right of last divider return arr end --if the params class and methods do not exist then add them to prevent errors if (not params) then params = {} function params:getHeader(name) self.name = name end function params:serialize(name) self.name = name end end --show param debug info if (debug["params"]) then freeswitch.consoleLog("notice", "[xml_handler] Params:\n" .. params:serialize() .. "\n"); end --get the params and set them as variables local domain_uuid = params:getHeader("variable_domain_uuid"); if (domain_uuid == nil) then local domain_uuid = params:getHeader("domain_uuid"); end local domain_name = params:getHeader("domain"); if (domain_name == nil) then local domain_name = params:getHeader("domain_name"); end if (domain_name == nil) then local domain_name = params:getHeader("variable_domain_name"); end local purpose = params:getHeader("purpose"); local profile = params:getHeader("profile"); local key = params:getHeader("key"); local user = params:getHeader("user"); local user_context = params:getHeader("variable_user_context"); local call_context = params:getHeader("Caller-Context"); local destination_number = params:getHeader("Caller-Destination-Number"); local caller_id_number = params:getHeader("Caller-Caller-ID-Number"); local hunt_context = params:getHeader("Hunt-Context"); if (hunt_context ~= nil) then call_context = hunt_context; end --prepare the api object api = freeswitch.API(); --get the domain_uuid if (domain_uuid == nil) then --get the domain_uuid 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", "[xml_handler] SQL: " .. sql .. "\n"); end status = dbh:query(sql, function(rows) domain_uuid = rows["domain_uuid"]; end); end end --handle the configuration if (XML_REQUEST["section"] == "configuration") then --sofia.conf - profiles and gateways if (XML_REQUEST["key_value"] == "sofia.conf") then --get the cache if (trim(api:execute("module_exists", "mod_memcache")) == "true") then XML_STRING = trim(api:execute("memcache", "get configuration:sofia.conf")); else XML_STRING = "-ERR NOT FOUND"; end if (XML_STRING == "-ERR NOT FOUND") then --get the variables vars = trim(api:execute("global_getvar", "")); --start the xml array local xml = {} table.insert(xml, [[]]); table.insert(xml, [[]]); table.insert(xml, [[
]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); --table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); --table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); --set defaults previous_sip_profile_name = ""; profile_tag_status = "closed"; --run the query sql = "select p.sip_profile_name, p.sip_profile_description, s.sip_profile_setting_name, s.sip_profile_setting_value "; sql = sql .. "from v_sip_profiles as p, v_sip_profile_settings as s "; sql = sql .. "where p.sip_profile_uuid = s.sip_profile_uuid "; sql = sql .. "and s.sip_profile_setting_enabled = 'true' "; sql = sql .. "order by p.sip_profile_name asc "; if (debug["sql"]) then freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n"); end x = 0; dbh:query(sql, function(row) --set as variables sip_profile_name = row.sip_profile_name; --sip_profile_description = row.sip_profile_description; sip_profile_setting_name = row.sip_profile_setting_name; sip_profile_setting_value = row.sip_profile_setting_value; --open xml tag if (sip_profile_name ~= previous_sip_profile_name) then if (x > 1) then table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); end table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); --table.insert(xml, [[ ]]); --get the gateways if (domain_count > 1) then sql = "select * from v_gateways as g, v_domains as d "; sql = sql .. "where g.profile = '"..sip_profile_name.."' "; sql = sql .. "and g.enabled = 'true' "; sql = sql .. "and g.domain_uuid = d.domain_uuid "; else sql = "select * from v_gateways "; sql = sql .. "where profile = '"..sip_profile_name.."' and enabled = 'true' "; end if (debug["sql"]) then freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n"); end x = 0; dbh:query(sql, function(field) --set as variables gateway = field.gateway; gateway = gateway:gsub(" ", "_"); if (domain_count > 1) then table.insert(xml, [[ ]]); else table.insert(xml, [[ ]]); end if (string.len(field.username) > 0) then table.insert(xml, [[ ]]); else table.insert(xml, [[ ]]); end if (string.len(field.distinct_to) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.auth_username) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.password) > 0) then table.insert(xml, [[ ]]); else table.insert(xml, [[ ]]); end if (string.len(field.realm) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.from_user) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.from_domain) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.proxy) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.register_proxy) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.outbound_proxy) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.expire_seconds) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.register) > 0) then table.insert(xml, [[ ]]); end if (field.register_transport) then if (field.register_transport == "udp") then table.insert(xml, [[ ]]); elseif (field.register_transport == "tcp") then table.insert(xml, [[ ]]); elseif (field.register_transport == "tls") then table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); else table.insert(xml, [[ ]]); end end if (string.len(field.retry_seconds) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.extension) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.ping) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.context) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.caller_id_in_from) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.supress_cng) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.sip_cid_type) > 0) then table.insert(xml, [[ ]]); end if (string.len(field.extension_in_contact) > 0) then table.insert(xml, [[ ]]); end table.insert(xml, [[ ]]); end) table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); profile_tag_status = "open"; end --loop through the var array for line in (vars.."\n"):gmatch"(.-)\n" do if (line) then pos = string.find(line, "=", 0, true); --name = string.sub( line, 0, pos-1); --value = string.sub( line, pos+1); sip_profile_setting_value = sip_profile_setting_value:gsub("%$%${"..string.sub( line, 0, pos-1).."}", string.sub( line, pos+1)); end end --remove $ and replace with "" --if (sip_profile_setting_value) then -- sip_profile_setting_value = sip_profile_setting_value:gsub("%$", ""); --end --set the parameters if (sip_profile_setting_name) then table.insert(xml, [[ ]]); end --set the previous value previous_sip_profile_name = sip_profile_name; --increment the value of x x = x + 1; end) --close the extension tag if it was left open if (profile_tag_status == "open") then table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); profile_tag_status = "close"; end table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[
]]); table.insert(xml, [[
]]); XML_STRING = table.concat(xml, "\n"); if (debug["xml_string"]) then freeswitch.consoleLog("notice", "[xml_handler] XML_STRING: " .. XML_STRING .. "\n"); end --set the cache result = trim(api:execute("memcache", "set configuration:sofia.conf '"..XML_STRING:gsub("'", "'").."' "..expire["sofia.conf"])); --send the xml to the console if (debug["xml_string"]) then local file = assert(io.open("/tmp/sofia.conf.xml", "w")); file:write(XML_STRING); file:close(); end --send to the console if (debug["cache"]) then freeswitch.consoleLog("notice", "[xml_handler] configuration:sofia.conf source: database\n"); end else --replace the ' back to a single quote XML_STRING = XML_STRING:gsub("'", "'"); --send to the console if (debug["cache"]) then freeswitch.consoleLog("notice", "[xml_handler] configuration:sofia.conf source: memcache\n"); end end --if XML_STRING end --sofia.conf --conference.conf - conference controls, and conference profiles if (XML_REQUEST["key_value"] == "conference.conf") then --start the xml array local xml = {} table.insert(xml, [[]]); table.insert(xml, [[]]); table.insert(xml, [[
]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, ""); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); --set the xml array and then concatenate the array to a string table.insert(xml, [[ ]]); table.insert(xml, [[
]]); table.insert(xml, [[
]]); XML_STRING = table.concat(xml, "\n"); --send the xml to the console if (debug["xml_string"]) then local file = assert(io.open("/tmp/conference.conf.xml", "w")); file:write(XML_STRING); file:close(); end end --conference.conf end --section configuration --handle the directory if (XML_REQUEST["section"] == "directory") then --set the default continue = true; --get the action action = params:getHeader("action"); --sip_auth - registration --group_call - call group has been called --user_call - user has been called --additional information --event_calling_function = params:getHeader("Event-Calling-Function"); --determine the correction action to perform if (action == "message-count") then --Event-Calling-Line-Number: 102 --Event-Sequence: 4173 --action: message-count --key: id --user: *98 --domain: example.com elseif (action == "group_call") then --handles action --group_call --attempt to use the cache if (trim(api:execute("module_exists", "mod_memcache")) == "true") then XML_STRING = trim(api:execute("memcache", "get directory:groups")); else XML_STRING = "-ERR NOT FOUND"; end if (XML_STRING == "-ERR NOT FOUND") then --build the call group array sql = [[ select * from v_extensions where domain_uuid = ']]..domain_uuid..[[' order by call_group asc ]]; if (debug["sql"]) then freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n"); end call_group_array = {}; status = dbh:query(sql, function(row) call_group = row['call_group']; --call_group = str_replace(";", ",", call_group); tmp_array = explode(",", call_group); for key,value in pairs(tmp_array) do value = trim(value); --freeswitch.consoleLog("notice", "[directory] Key: " .. key .. " Value: " .. value .. " " ..row['extension'] .."\n"); if (string.len(value) == 0) then --do nothing else if (call_group_array[value] == nil) then call_group_array[value] = row['extension']; else call_group_array[value] = call_group_array[value]..','..row['extension']; end end end end); --for key,value in pairs(call_group_array) do -- freeswitch.consoleLog("notice", "[directory] Key: " .. key .. " Value: " .. value .. "\n"); --end --build the xml array local xml = {} table.insert(xml, [[]]); table.insert(xml, [[]]); table.insert(xml, [[
]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); previous_call_group = ""; for key, value in pairs(call_group_array) do call_group = trim(key); extension_list = trim(value); if (string.len(call_group) > 0) then freeswitch.consoleLog("notice", "[directory] call_group: " .. call_group .. "\n"); freeswitch.consoleLog("notice", "[directory] extension_list: " .. extension_list .. "\n"); if (previous_call_group ~= call_group) then table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); extension_array = explode(",", extension_list); for index,tmp_extension in pairs(extension_array) do table.insert(xml, [[ ]]); end table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); end previous_call_group = call_group; end end table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[
]]); table.insert(xml, [[
]]); XML_STRING = table.concat(xml, "\n"); --set the cache result = trim(api:execute("memcache", "set directory:groups '"..XML_STRING:gsub("'", "'").."' "..expire["directory"])); --send to the console if (debug["cache"]) then freeswitch.consoleLog("notice", "[xml_handler] directory:groups source: database\n"); end else --replace the ' back to a single quote XML_STRING = XML_STRING:gsub("'", "'"); --send to the console if (debug["cache"]) then if (XML_STRING) then freeswitch.consoleLog("notice", "[xml_handler] directory:groups source: memcache\n"); end end end --send the xml to the console if (debug["xml_string"]) then freeswitch.consoleLog("notice", "[directory] Groups XML_STRING: \n" .. XML_STRING .. "\n"); end else --handle action --all other directory actions: sip_auth, user_call --except for the action: group_call --get the cache if (trim(api:execute("module_exists", "mod_memcache")) == "true") then if (user == nil) then user = ""; end XML_STRING = trim(api:execute("memcache", "get directory:" .. user .. "@" .. domain_name)); if (XML_STRING == "-ERR NOT FOUND") then continue = true; else continue = false; end else XML_STRING = ""; continue = true; end --prevent processing for invalid user if (user == "*97") then continue = false; end --get the extension from the database if (continue) then sql = "SELECT * FROM v_extensions WHERE domain_uuid = '" .. domain_uuid .. "' and extension = '" .. user .. "' and enabled = 'true' "; if (debug["sql"]) then freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n"); end dbh:query(sql, function(row) --general domain_uuid = row.domain_uuid; extension_uuid = row.extension_uuid; extension = row.extension; cidr = ""; if (string.len(row.cidr) > 0) then cidr = [[ cidr="]] .. row.cidr .. [["]]; end number_alias = ""; if (string.len(row.number_alias) > 0) then number_alias = [[ number-alias="]] .. row.number_alias .. [["]]; end --params password = row.password; vm_enabled = "true"; if (string.len(row.vm_enabled) > 0) then vm_enabled = row.vm_enabled; end vm_password = row.vm_password; vm_attach_file = "true"; if (string.len(row.vm_attach_file) > 0) then vm_attach_file = row.vm_attach_file; end vm_keep_local_after_email = "true"; if (string.len(row.vm_keep_local_after_email) > 0) then vm_keep_local_after_email = row.vm_keep_local_after_email; end if (string.len(row.vm_mailto) > 0) then vm_mailto = row.vm_mailto; else vm_mailto = ""; end mwi_account = row.mwi_account; auth_acl = row.auth_acl; --variables sip_from_user = row.extension; call_group = row.call_group; hold_music = row.hold_music; toll_allow = row.toll_allow; accountcode = row.accountcode; user_context = row.user_context; effective_caller_id_name = row.effective_caller_id_name; effective_caller_id_number = row.effective_caller_id_number; outbound_caller_id_name = row.outbound_caller_id_name; outbound_caller_id_number = row.outbound_caller_id_number; emergency_caller_id_number = row.emergency_caller_id_number; directory_full_name = row.directory_full_name; directory_visible = row.directory_visible; directory_exten_visible = row.directory_exten_visible; limit_max = row.limit_max; limit_destination = row.limit_destination; sip_force_contact = row.sip_force_contact; sip_force_expires = row.sip_force_expires; nibble_account = row.nibble_account; sip_bypass_media = row.sip_bypass_media; --set the dial_string if (string.len(row.dial_string) > 0) then dial_string = row.dial_string; else dial_string = "{sip_invite_domain=" .. domain_name .. ",presence_id=" .. user .. "@" .. domain_name .. "}${sofia_contact(" .. user .. "@" .. domain_name .. ")}"; end end); end --if the extension does not exist set continue to false; if (extension_uuid == nil) then continue = false; end --outbound hot desking - get the extension variables if (continue) then sql = "SELECT * FROM v_extensions WHERE dial_domain = '" .. domain_name .. "' and dial_user = '" .. user .. "' and enabled = 'true' "; if (debug["sql"]) then freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n"); end dbh:query(sql, function(row) --get the values from the database extension_uuid = row.extension_uuid; domain_uuid = row.domain_uuid; sip_from_user = row.extension; call_group = row.call_group; hold_music = row.hold_music; toll_allow = row.toll_allow; accountcode = row.accountcode; user_context = row.user_context; effective_caller_id_name = row.effective_caller_id_name; effective_caller_id_number = row.effective_caller_id_number; outbound_caller_id_name = row.outbound_caller_id_name; outbound_caller_id_number = row.outbound_caller_id_number; emergency_caller_id_number = row.emergency_caller_id_number; directory_full_name = row.directory_full_name; directory_visible = row.directory_visible; directory_exten_visible = row.directory_exten_visible; limit_max = row.limit_max; limit_destination = row.limit_destination; sip_force_contact = row.sip_force_contact; sip_force_expires = row.sip_force_expires; nibble_account = row.nibble_account; sip_bypass_media = row.sip_bypass_media; end); end --set the xml array and then concatenate the array to a string if (continue and password) then --build the xml local xml = {} table.insert(xml, [[]]); table.insert(xml, [[]]); table.insert(xml, [[
]]); table.insert(xml, [[ ]]); if (number_alias) then if (cidr) then table.insert(xml, [[ ]]); else table.insert(xml, [[ ]]); end else if (cidr) then table.insert(xml, [[ ]]); else table.insert(xml, [[ ]]); end end table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); if (string.len(vm_mailto) > 0) then table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); end if (string.len(mwi_account) > 0) then table.insert(xml, [[ ]]); end if (string.len(auth_acl) > 0) then table.insert(xml, [[ ]]); end table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); if (string.len(call_group) > 0) then table.insert(xml, [[ ]]); end if (string.len(hold_music) > 0) then table.insert(xml, [[ ]]); end if (string.len(toll_allow) > 0) then table.insert(xml, [[ ]]); end if (string.len(accountcode) > 0) then table.insert(xml, [[ ]]); end table.insert(xml, [[ ]]); if (string.len(effective_caller_id_name) > 0) then table.insert(xml, [[ ]]); end if (string.len(effective_caller_id_number) > 0) then table.insert(xml, [[ ]]); end if (string.len(outbound_caller_id_name) > 0) then table.insert(xml, [[ ]]); end if (string.len(outbound_caller_id_number) > 0) then table.insert(xml, [[ ]]); end if (string.len(emergency_caller_id_number) > 0) then table.insert(xml, [[ ]]); end if (string.len(directory_full_name) > 0) then table.insert(xml, [[ ]]); end if (string.len(directory_visible) > 0) then table.insert(xml, [[ ]]); end if (string.len(directory_exten_visible) > 0) then table.insert(xml, [[ ]]); end if (string.len(limit_max) > 0) then table.insert(xml, [[ ]]); else table.insert(xml, [[ ]]); end if (string.len(limit_destination) > 0) then table.insert(xml, [[ ]]); end if (string.len(sip_force_contact) > 0) then table.insert(xml, [[ ]]); end if (string.len(sip_force_expires) > 0) then table.insert(xml, [[ ]]); end if (string.len(nibble_account) > 0) then table.insert(xml, [[ ]]); end if (sip_bypass_media == "bypass-media") then table.insert(xml, [[ ]]); end if (sip_bypass_media == "bypass-media-after-bridge") then table.insert(xml, [[ ]]); end if (sip_bypass_media == "proxy-media") then table.insert(xml, [[ ]]); end table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); table.insert(xml, [[
]]); table.insert(xml, [[
]]); XML_STRING = table.concat(xml, "\n"); --set the cache result = trim(api:execute("memcache", "set directory:" .. user .. "@" .. domain_name .. " '"..XML_STRING:gsub("'", "'").."' "..expire["directory"])); --send the xml to the console if (debug["xml_string"]) then local file = assert(io.open("/tmp/" .. user .. "@" .. domain_name .. ".xml", "w")); file:write(XML_STRING); file:close(); end --send to the console if (debug["cache"]) then freeswitch.consoleLog("notice", "[xml_handler] directory:" .. user .. "@" .. domain_name .. " source: database\n"); end else --replace the ' back to a single quote XML_STRING = XML_STRING:gsub("'", "'"); --send to the console if (debug["cache"]) then if (XML_STRING) then freeswitch.consoleLog("notice", "[xml_handler] directory:" .. user .. "@" .. domain_name .. " source: memcache \n"); end end end end --if action --if the extension does not exist send "not found" if (trim(XML_STRING) == "-ERR NOT FOUND" or XML_STRING == nil) then --send not found XML_STRING = [[
]]; --set the cache result = trim(api:execute("memcache", "set directory:" .. user .. "@" .. domain_name .. " '"..XML_STRING:gsub("'", "'").."' "..expire["directory"])); end --send the xml to the console if (debug["xml_string"]) then freeswitch.consoleLog("notice", "[xml_handler] XML_STRING: \n" .. XML_STRING .. "\n"); end end --handle the dialplan if (XML_REQUEST["section"] == "dialplan") then if (debug["params"]) then freeswitch.consoleLog("notice", "[xml_handler] Params:\n" .. params:serialize() .. "\n"); end --get the cache if (trim(api:execute("module_exists", "mod_memcache")) == "true") then XML_STRING = trim(api:execute("memcache", "get dialplan:" .. call_context)); else XML_STRING = "-ERR NOT FOUND"; end if (XML_STRING == "-ERR NOT FOUND") then --set the xml array and then concatenate the array to a string local xml = {} table.insert(xml, [[]]); table.insert(xml, [[]]); table.insert(xml, [[
]]); table.insert(xml, [[ ]]); --set defaults previous_dialplan_uuid = ""; previous_dialplan_detail_group = ""; previous_dialplan_detail_tag = ""; previous_dialplan_detail_type = ""; previous_dialplan_detail_data = ""; dialplan_tag_status = "closed"; condition_tag_status = "closed"; --get the dialplan and related details sql = "select * from v_dialplans as d, v_dialplan_details as s "; sql = sql .. "where d.dialplan_context = '" .. call_context .. "' "; sql = sql .. "and d.dialplan_enabled = 'true' "; sql = sql .. "and d.dialplan_uuid = s.dialplan_uuid "; --if (call_context ~= "public") then -- sql = sql .. "and d.domain_uuid = '" .. domain_uuid .. "' "; --end sql = sql .. "order by "; sql = sql .. "d.dialplan_order asc, "; sql = sql .. "d.dialplan_name asc, "; sql = sql .. "d.dialplan_uuid asc, "; sql = sql .. "s.dialplan_detail_group asc, "; sql = sql .. "CASE s.dialplan_detail_tag "; sql = sql .. "WHEN 'condition' THEN 1 "; sql = sql .. "WHEN 'action' THEN 2 "; sql = sql .. "WHEN 'anti-action' THEN 3 "; sql = sql .. "ELSE 100 END, "; sql = sql .. "s.dialplan_detail_order asc "; if (debug["sql"]) then freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n"); end x = 0; dbh:query(sql, function(row) --get the dialplan --domain_uuid = row.domain_uuid; dialplan_uuid = row.dialplan_uuid; --app_uuid = row.app_uuid; --dialplan_context = row.dialplan_context; dialplan_name = row.dialplan_name; --dialplan_number = row.dialplan_number; dialplan_continue = row.dialplan_continue; --dialplan_order = row.dialplan_order; --dialplan_enabled = row.dialplan_enabled; --dialplan_description = row.dialplan_description; --get the dialplan details --dialplan_detail_uuid = row.dialplan_detail_uuid; dialplan_detail_tag = row.dialplan_detail_tag; dialplan_detail_type = row.dialplan_detail_type; dialplan_detail_data = row.dialplan_detail_data; dialplan_detail_break = row.dialplan_detail_break; dialplan_detail_inline = row.dialplan_detail_inline; dialplan_detail_group = row.dialplan_detail_group; --dialplan_detail_order = row.dialplan_detail_order; --remove $$ and replace with $ dialplan_detail_data = dialplan_detail_data:gsub("%$%$", "$"); --get the dialplan detail inline detail_inline = ""; if (dialplan_detail_inline) then if (string.len(dialplan_detail_inline) > 0) then detail_inline = [[ inline="]] .. dialplan_detail_inline .. [["]]; end end --close the tags if (condition_tag_status ~= "closed") then if (previous_dialplan_uuid ~= dialplan_uuid) then table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); dialplan_tag_status = "closed"; condition_tag_status = "closed"; else if (previous_dialplan_detail_group ~= dialplan_detail_group and previous_dialplan_detail_tag == "condition") then table.insert(xml, [[ ]]); condition_tag_status = "closed"; end end end --open the tags if (dialplan_tag_status == "closed") then table.insert(xml, [[ ]]); dialplan_tag_status = "open"; end if (dialplan_detail_tag == "condition") then --determine the type of condition if (dialplan_detail_type == "hour") then condition_type = 'time'; elseif (dialplan_detail_type == "minute") then condition_type = 'time'; elseif (dialplan_detail_type == "minute-of-day") then condition_type = 'time'; elseif (dialplan_detail_type == "mday") then condition_type = 'time'; elseif (dialplan_detail_type == "mweek") then condition_type = 'time'; elseif (dialplan_detail_type == "mon") then condition_type = 'time'; elseif (dialplan_detail_type == "yday") then condition_type = 'time'; elseif (dialplan_detail_type == "year") then condition_type = 'time'; elseif (dialplan_detail_type == "wday") then condition_type = 'time'; elseif (dialplan_detail_type == "week") then condition_type = 'time'; else condition_type = 'default'; end --get the condition break attribute condition_break = ""; if (dialplan_detail_break) then if (string.len(dialplan_detail_break) > 0) then condition_break = [[ break="]] .. dialplan_detail_break .. [["]]; end end if (condition_tag_status == "open") then if (previous_dialplan_detail_tag == "condition") then --add the condition self closing tag if (condition) then if (string.len(condition) > 0) then table.insert(xml, condition .. [[/>]]); end end end if (previous_dialplan_detail_tag == "action" or previous_dialplan_detail_tag == "anti-action") then table.insert(xml, [[ ]]); condition_tag_status = "closed"; condition_type = ""; condition_attribute = ""; condition_expression = ""; end end --condition tag but leave off the ending if (condition_type == "default") then condition = [[ ]]); condition = ""; --prevents duplicate time conditions end end if (dialplan_detail_tag == "action") then table.insert(xml, [[ ]]); end if (dialplan_detail_tag == "anti-action") then table.insert(xml, [[ ]]); end --save the previous values previous_dialplan_uuid = dialplan_uuid; previous_dialplan_detail_group = dialplan_detail_group; previous_dialplan_detail_tag = dialplan_detail_tag; previous_dialplan_detail_type = dialplan_detail_type; previous_dialplan_detail_data = dialplan_detail_data; --increment the x x = x + 1; end); --close the extension tag if it was left open if (dialplan_tag_status == "open") then table.insert(xml, [[ ]]); table.insert(xml, [[ ]]); end --set the xml array and then concatenate the array to a string table.insert(xml, [[ ]]); table.insert(xml, [[
]]); table.insert(xml, [[
]]); XML_STRING = table.concat(xml, "\n"); --set the cache tmp = XML_STRING:gsub("\\", "\\\\"); result = trim(api:execute("memcache", "set dialplan:" .. call_context .. " '"..tmp:gsub("'", "'").."' "..expire["dialplan"])); --send the xml to the console if (debug["xml_string"]) then local file = assert(io.open("/tmp/dialplan-" .. call_context .. ".xml", "w")); file:write(XML_STRING); file:close(); end --send to the console if (debug["cache"]) then freeswitch.consoleLog("notice", "[xml_handler] dialplan:"..call_context.." source: database\n"); end else --replace the ' back to a single quote XML_STRING = XML_STRING:gsub("'", "'"); --send to the console if (debug["cache"]) then freeswitch.consoleLog("notice", "[xml_handler] dialplan:"..call_context.." source: memcache\n"); end end end --send debug info to the console if (debug["xml_request"]) then freeswitch.consoleLog("notice", "[xml_handler] Section: " .. XML_REQUEST["section"] .. "\n"); freeswitch.consoleLog("notice", "[xml_handler] Tag Name: " .. XML_REQUEST["tag_name"] .. "\n"); freeswitch.consoleLog("notice", "[xml_handler] Key Name: " .. XML_REQUEST["key_name"] .. "\n"); freeswitch.consoleLog("notice", "[xml_handler] Key Value: " .. XML_REQUEST["key_value"] .. "\n"); end --close the database connection --dbh:release();