Move the includes/install to resources/install

This commit is contained in:
Mark Crane
2013-06-09 02:42:36 +00:00
parent 0407ee236d
commit 896fdb37b2
203 changed files with 0 additions and 0 deletions
@@ -0,0 +1,630 @@
-- conference.lua
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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 variables
flags = "";
max_tries = 3;
digit_timeout = 5000;
--debug
debug["sql"] = true;
--connect to the database
dofile(scripts_dir.."/resources/functions/database_handle.lua");
dbh = database_handle('system');
--prepare the api object
api = freeswitch.API();
--general functions
dofile(scripts_dir.."/resources/functions/base64.lua");
dofile(scripts_dir.."/resources/functions/trim.lua");
dofile(scripts_dir.."/resources/functions/file_exists.lua");
dofile(scripts_dir.."/resources/functions/explode.lua");
dofile(scripts_dir.."/resources/functions/format_seconds.lua");
dofile(scripts_dir.."/resources/functions/mkdir.lua");
--get the session variables
uuid = session:getVariable("uuid");
--answer the call
session:answer();
--define a function to send email
function send_email(email, attachment, default_language, default_dialect)
--require the email address to send the email
if (string.len(email) > 2) then
--format the message length and date
--message_length_formatted = format_seconds(message_length);
--if (debug["info"]) then
-- freeswitch.consoleLog("notice", "[conference_center] message length: " .. message_length .. "\n");
--end
local conference_date_end = os.date("%A, %d %b %Y %I:%M %p");
--os.time();
--prepare the files
file_subject = scripts_dir.."/app/conference_center/resources/templates/"..default_language.."/"..default_dialect.."email_subject.tpl";
file_body = scripts_dir.."/app/conference_center/resources/templates/"..default_language.."/"..default_dialect.."/email_body.tpl";
if (not file_exists(file_subject)) then
file_subject = scripts_dir.."/app/conference_center/resources/templates/en/us/email_subject.tpl";
file_body = scripts_dir.."/app/conference_center/resources/templates/en/us/email_body.tpl";
end
--get the moderator_pin
sql = [[SELECT moderator_pin FROM v_meetings
WHERE meeting_uuid = ']] .. meeting_uuid ..[[']];
freeswitch.consoleLog("notice", "[voicemail] sql: " .. sql .. "\n");
status = dbh:query(sql, function(row)
moderator_pin = string.lower(row["moderator_pin"]);
end);
--prepare the subject
local f = io.open(file_subject, "r");
local subject = f:read("*all");
f:close();
subject = subject:gsub("${moderator_pin}", moderator_pin);
subject = subject:gsub("${conference_date_end}", conference_date_end);
--subject = subject:gsub("${conference_duration}", message_length_formatted);
subject = subject:gsub("${domain_name}", domain_name);
subject = trim(subject);
subject = '=?utf-8?B?'..base64.enc(subject)..'?=';
--prepare the body
local f = io.open(file_body, "r");
local body = f:read("*all");
f:close();
body = body:gsub("${moderator_pin}", moderator_pin);
body = body:gsub("${conference_date_end}", conference_date_end);
body = body:gsub("${conference_uuid}", conference_session_uuid);
--body = body:gsub("${conference_duration}", message_length_formatted);
body = body:gsub("${domain_name}", domain_name);
body = body:gsub(" ", "&nbsp;");
body = body:gsub("%s+", "");
body = body:gsub("&nbsp;", " ");
body = body:gsub("\n", "");
body = body:gsub("\n", "");
body = body:gsub("'", "&#39;");
body = body:gsub([["]], "&#34;");
body = trim(body);
--send the email
if (string.len(attachment) > 4) then
cmd = "luarun email.lua "..email.." "..email.." '"..subject.."' '"..body.."' '"..attachment.."'";
else
cmd = "luarun email.lua "..email.." "..email.." '"..subject.."' '"..body.."'";
end
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] cmd: " .. cmd .. "\n");
end
result = api:executeString(cmd);
end
end
--define the session hangup
function session_hangup_hook()
--get the session variables
conference_session_detail_uuid = api:executeString("create_uuid");
--conference_name = session:getVariable("conference_name");
conference_session_uuid = session:getVariable("conference_uuid");
--conference_recording = session:getVariable("conference_recording");
conference_moderator = session:getVariable("conference_moderator");
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
--recording = session:getVariable("recording");
--set the end epoch
end_epoch = os.time();
--connect to the database
dofile(scripts_dir.."/resources/functions/database_handle.lua");
dbh = database_handle('system');
--get the conference sessions
if (conference_session_uuid) then
sql = [[SELECT count(*) as num_rows
FROM v_conference_sessions
WHERE conference_session_uuid = ']] .. conference_session_uuid ..[[']];
status = dbh:query(sql, function(row)
num_rows = string.lower(row["num_rows"]);
end);
freeswitch.consoleLog("notice", "[conference] SQL: " .. sql .. " Rows:"..num_rows.."\n");
if (tonumber(num_rows) == 0) then
local sql = {}
table.insert(sql, "INSERT INTO v_conference_sessions ");
table.insert(sql, "(");
table.insert(sql, "conference_session_uuid, ");
table.insert(sql, "domain_uuid, ");
table.insert(sql, "meeting_uuid, ");
--if (conference_recording) then
-- table.insert(sql, "recording, ");
--end
--if (wait_mod) then
-- table.insert(sql, "wait_mod, ");
--end
--table.insert(sql, "start_epoch, ");
table.insert(sql, "profile ");
table.insert(sql, ") ");
table.insert(sql, "VALUES ");
table.insert(sql, "( ");
table.insert(sql, "'".. conference_session_uuid .."', ");
table.insert(sql, "'".. domain_uuid .."', ");
table.insert(sql, "'".. meeting_uuid .."', ");
--if (conference_recording) then
-- table.insert(sql, "'".. conference_recording .."', ");
--end
--if (wait_mod) then
-- table.insert(sql, "'".. wait_mod .."', ");
--end
--table.insert(sql, "'".. start_epoch .."', ");
table.insert(sql, "'".. profile .."' ");
table.insert(sql, ") ");
SQL_STRING = table.concat(sql, "\n");
dbh:query(SQL_STRING);
freeswitch.consoleLog("notice", "[conference] SQL: " .. SQL_STRING .. "\n");
end
end
--add the conference sessions details
if (conference_session_uuid) then
local sql = {}
table.insert(sql, "INSERT INTO v_conference_session_details ");
table.insert(sql, "(");
table.insert(sql, "conference_session_detail_uuid, ");
table.insert(sql, "domain_uuid, ");
table.insert(sql, "conference_session_uuid, ");
table.insert(sql, "meeting_uuid, ");
table.insert(sql, "username, ");
table.insert(sql, "caller_id_name, ");
table.insert(sql, "caller_id_number, ");
table.insert(sql, "network_addr, ");
table.insert(sql, "uuid, ");
if (conference_moderator) then
table.insert(sql, "moderator, ");
end
table.insert(sql, "start_epoch, ");
table.insert(sql, "end_epoch ");
table.insert(sql, ") ");
table.insert(sql, "VALUES ");
table.insert(sql, "( ");
table.insert(sql, "'".. conference_session_detail_uuid .."', ");
table.insert(sql, "'".. domain_uuid .."', ");
table.insert(sql, "'".. conference_session_uuid .."', ");
table.insert(sql, "'".. meeting_uuid .."', ");
table.insert(sql, "'".. username .."', ");
table.insert(sql, "'".. caller_id_name .."', ");
table.insert(sql, "'".. caller_id_number .."', ");
table.insert(sql, "'".. network_addr .."', ");
table.insert(sql, "'".. uuid .."', ");
if (conference_moderator) then
table.insert(sql, "'".. conference_moderator .."', ");
end
table.insert(sql, "'".. start_epoch .."', ");
table.insert(sql, "'".. end_epoch .."' ");
table.insert(sql, ") ");
SQL_STRING = table.concat(sql, "\n");
dbh:query(SQL_STRING);
end
--if the conference is empty
if (conference_session_uuid) then
cmd = "conference "..meeting_uuid.."-"..domain_name.." xml_list";
result = trim(api:executeString(cmd));
if (string.sub(result, -9) == "not found") then
--get the conference start_epoch
sql = [[SELECT start_epoch
FROM v_conference_session_details
WHERE conference_session_uuid = ']] .. conference_session_uuid ..[['
ORDER BY start_epoch ASC
LIMIT 1]];
status = dbh:query(sql, function(row)
start_epoch = string.lower(row["start_epoch"]);
end);
freeswitch.consoleLog("notice", "[conference] <conference_start_epoch> sql: " .. sql .. "\n");
--set the conference_recording
conference_recording = recordings_dir.."/archive/"..os.date("%Y", start_epoch).."/"..os.date("%b", start_epoch).."/"..os.date("%d", start_epoch) .."/"..conference_session_uuid;
--conference has ended set the end_epoch
local sql = {}
table.insert(sql, "update v_conference_sessions set ");
table.insert(sql, "recording = '".. conference_recording .."', ");
table.insert(sql, "start_epoch = '".. start_epoch .."', ");
table.insert(sql, "end_epoch = '".. end_epoch .."' ");
table.insert(sql, "where conference_session_uuid = '"..conference_session_uuid.."' ");
SQL_STRING = table.concat(sql, "\n");
freeswitch.consoleLog("notice", "[conference] SQL: " .. SQL_STRING .. "\n");
dbh:query(SQL_STRING);
--convert the wav to an mp3
if (record == "true") then
--cmd = "sox "..conference_recording..".wav -r 16000 -c 1 "..conference_recording..".mp3";
cmd = "/usr/bin/lame -b 32 --resample 8 -a "..conference_recording..".wav "..conference_recording..".mp3";
freeswitch.consoleLog("notice", "[conference] cmd: " .. cmd .. "\n");
os.execute(cmd);
--if (file_exists(conference_recording..".mp3")) then
-- cmd = "rm "..conference_recording..".wav";
-- os.execute(cmd);
--end
end
--send the email addresses
sql = [[SELECT c.contact_email FROM v_users as u, v_meeting_users as m, v_contacts as c
WHERE m.domain_uuid = ']] .. domain_uuid ..[['
AND u.user_uuid = m.user_uuid
AND m.meeting_uuid = ']] .. meeting_uuid ..[['
and u.contact_uuid = c.contact_uuid]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference] <email> SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
if (row["contact_email"] ~= nil) then
contact_email = string.lower(row["contact_email"]);
if (string.len(contact_email) > 3) then
freeswitch.consoleLog("notice", "[conference] contact_email: " .. contact_email .. "\n");
if (record == "true") then
if (file_exists(conference_recording..".wav")) then
send_email(contact_email, "", default_language, default_dialect);
end
end
end
end
end);
end
end
--close the database connection
if (conference_session_uuid) then
dbh:release();
end
end
--make sure the session is ready
if (session:ready()) then
--answer the call
session:answer();
--set the hangup hook function
session:setHangupHook("session_hangup_hook");
--get session variables
sounds_dir = session:getVariable("sounds_dir");
hold_music = session:getVariable("hold_music");
domain_name = session:getVariable("domain_name");
pin_number = session:getVariable("pin_number");
--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", "[conference] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(rows)
domain_uuid = string.lower(rows["domain_uuid"]);
end);
end
--add the domain to the recording directory
if (domain_count > 1) then
recordings_dir = recordings_dir.."/"..domain_name;
end
--set the sounds path for the language, dialect and voice
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
default_voice = session:getVariable("default_voice");
if (not default_language) then default_language = 'en'; end
if (not default_dialect) then default_dialect = 'us'; end
if (not default_voice) then default_voice = 'callie'; end
--sounds
enter_sound = "tone_stream://v=-20;%(100,1000,100);v=-20;%(90,60,440);%(90,60,620)";
exit_sound = "tone_stream://v=-20;%(90,60,620);/%(90,60,440)";
--get the variables
username = session:getVariable("username");
caller_id_name = session:getVariable("caller_id_name");
caller_id_number = session:getVariable("caller_id_number");
callee_id_name = session:getVariable("callee_id_name");
callee_id_number = session:getVariable("callee_id_number");
dialplan = session:getVariable("dialplan");
network_addr = session:getVariable("network_addr");
uuid = session:getVariable("uuid");
--context = session:getVariable("context");
chan_name = session:getVariable("chan_name");
--define the function get_pin_number
function get_pin_number(domain_uuid)
--if the pin number is provided then require it
if (not pin_number) then
min_digits = 3;
max_digits = 12;
max_tries = 1;
digit_timeout = 5000;
pin_number = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-pin.wav", "", "\\d+");
end
if (pin_number ~= "") then
sql = [[SELECT * FROM v_conference_rooms as r, v_meetings as m
WHERE r.domain_uuid = ']] .. domain_uuid ..[['
AND r.meeting_uuid = m.meeting_uuid
AND m.domain_uuid = ']] .. domain_uuid ..[['
AND (m.moderator_pin = ']] .. pin_number ..[[' or m.participant_pin = ']] .. pin_number ..[[')
AND r.enabled = 'true' ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
conference_room_uuid = string.lower(row["conference_room_uuid"]);
end);
end
if (conference_room_uuid == nil) then
return nil;
else
return pin_number;
end
end
--get the pin
pin_number = session:getVariable("pin_number");
if (not pin_number) then
pin_number = nil;
pin_number = get_pin_number(domain_uuid);
end
if (pin_number == nil) then
pin_number = get_pin_number(domain_uuid);
end
if (pin_number == nil) then
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-bad-pin.wav");
pin_number = get_pin_number(domain_uuid);
end
if (pin_number == nil) then
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-bad-pin.wav");
pin_number = get_pin_number(domain_uuid);
end
if (pin_number ~= nil) then
sql = [[SELECT * FROM v_conference_rooms as r, v_meetings as m
WHERE r.domain_uuid = ']] .. domain_uuid ..[['
AND r.meeting_uuid = m.meeting_uuid
AND m.domain_uuid = ']] .. domain_uuid ..[['
AND (m.moderator_pin = ']] .. pin_number ..[[' or m.participant_pin = ']] .. pin_number ..[[')
AND r.enabled = 'true' ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[conference] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
conference_room_uuid = string.lower(row["conference_room_uuid"]);
conference_center_uuid = string.lower(row["conference_center_uuid"]);
meeting_uuid = string.lower(row["meeting_uuid"]);
record = string.lower(row["record"]);
profile = string.lower(row["profile"]);
max_members = row["max_members"];
wait_mod = row["wait_mod"];
moderator_pin = row["moderator_pin"];
participant_pin = row["participant_pin"];
announce = row["announce"];
mute = row["mute"];
sounds = row["sounds"];
created = row["created"];
created_by = row["created_by"];
enabled = row["enabled"];
description = row["description"];
return pin_number;
end);
freeswitch.consoleLog("INFO","conference_room_uuid: " .. conference_room_uuid .. "\n");
end
--set the member type
if (pin_number == moderator_pin) then
member_type = "moderator";
end
if (pin_number == participant_pin) then
member_type = "participant";
end
--close the database connection
dbh:release();
--set the meeting uuid
if (meeting_uuid) then
session:setVariable("meeting_uuid", meeting_uuid);
end
if (conference_center_uuid == nil) then
--invalid pin number
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-bad-pin.wav");
session:hangup("NORMAL_CLEARING");
else
--check if the conference exists
cmd = "conference "..meeting_uuid.."-"..domain_name.." xml_list";
result = trim(api:executeString(cmd));
if (string.sub(result, -9) == "not found") then
conference_exists = false;
else
conference_exists = true;
end
--check if the conference is locked
if (string.find(result, [[locked="true"]]) == nil) then
conference_locked = false;
else
conference_locked = true;
end
--set a conference parameter
if (max_members ~= nil) then
if (tonumber(max_members) > 0) then
--max members must be 2 or more
session:execute("set","conference_max_members="..max_members);
if (conference_exists) then
cmd = "conference "..meeting_uuid.."-"..domain_name.." get count";
count = trim(api:executeString(cmd));
if (count ~= nil) then
if (tonumber(count) >= tonumber(max_members)) then
session:execute("playback", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-locked.wav");
session:hangup("CALL_REJECTED");
end
end
end
end
end
--announce the caller
if (conference_locked) then
announce = "false";
end
if (announce == "true") then
--prompt for the name of the caller
session:execute("playback", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-say_name.wav");
session:execute("playback", "tone_stream://v=-7;%%(500,0,500.0)");
--record the response
max_len_seconds = 5;
silence_threshold = "500";
silence_secs = "3";
session:recordFile("/tmp/conference-"..uuid..".wav", max_len_seconds, silence_threshold, silence_secs);
end
--play a message that the conference is being a recorded
--if (record == "true") then
--session:execute("playback", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-recording_started.wav");
--end
--wait for moderator
if (wait_mod == "true") then
if (conference_exists) then
--continue
else
if (member_type == "participant") then
profile = "wait_mod";
end
end
end
--set the exit sound
if (sounds == "true") then
session:execute("set","conference_exit_sound="..exit_sound);
end
--set flags and moderator controls
if (wait_mod == "true") then
if (member_type == "participant") then
flags = flags .. "wait-mod";
end
end
if (mute == "true") then
if (member_type == "participant") then
flags = flags .. "|mute";
end
end
if (member_type == "moderator") then
--set as the moderator
flags = flags .. "|moderator";
--when the moderator leaves end the conference
--flags = flags .. "|endconf";
--set the moderator controls
session:execute("set","conference_controls=moderator");
end
--get the conference xml_list
cmd = "conference "..meeting_uuid.."-"..domain_name.." xml_list";
freeswitch.consoleLog("INFO","" .. cmd .. "\n");
result = trim(api:executeString(cmd));
--get the content to the <conference> tag
result = string.match(result,[[<conference (.-)>]],1);
--get the uuid out of the xml tag contents
if (result ~= nil) then
conference_session_uuid = string.match(result,[[uuid="(.-)"]],1);
end
--log entry
if (conference_session_uuid ~= nil) then
freeswitch.consoleLog("INFO","conference_session_uuid: " .. conference_session_uuid .. "\n");
end
--set the start epoch
start_epoch = os.time();
--set the recording variable
if (conference_session_uuid ~= nil) then
if (record == "true") then
recordings_dir = recordings_dir.."/archive/"..os.date("%Y", start_epoch).."/"..os.date("%b", start_epoch).."/"..os.date("%d", start_epoch);
mkdir(recordings_dir);
recording = recordings_dir.."/"..conference_session_uuid;
session:execute("set","recording="..recording);
end
end
--record the conference
if (record == "true") then
--play a message that the conference is being a recorded
session:execute("playback", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-recording_started.wav");
--play a message that the conference is being a recorded
--cmd = "conference "..meeting_uuid.."-"..domain_name.." play "..sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/ivr/ivr-recording_started.wav";
--freeswitch.consoleLog("notice", "[conference] ".. cmd .."\n");
--response = api:executeString(cmd);
--record the conference when it exists
if (conference_exists) then
--send a command to record the conference
if (not file_exists(recording..".wav")) then
cmd = "conference "..meeting_uuid.."-"..domain_name.." record "..recording..".wav";
freeswitch.consoleLog("notice", "[conference] cmd: " .. cmd .. "\n");
response = api:executeString(cmd);
end
end
end
--announce the caller
if (announce == "true") then
--announce the caller - play the recording
cmd = "conference "..meeting_uuid.."-"..domain_name.." play /tmp/conference-"..uuid..".wav";
freeswitch.consoleLog("notice", "[conference] ".. cmd .."\n");
response = api:executeString(cmd);
--play has entered the conference
cmd = "conference "..meeting_uuid.."-"..domain_name.." play "..sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/conference/conf-has_joined.wav";
freeswitch.consoleLog("notice", "[conference] ".. cmd .."\n");
response = api:executeString(cmd);
else
if (not conference_locked) then
if (sounds == "true") then
cmd = "conference "..meeting_uuid.."-"..domain_name.." play "..enter_sound;
response = api:executeString(cmd);
end
end
end
--close the database connection
dbh:release();
--send the call to the conference
profile = "default";
cmd = meeting_uuid.."-"..domain_name.."@"..profile.."+flags{".. flags .."}";
session:execute("conference", cmd);
end
end
@@ -0,0 +1,7 @@
<font face="arial">
<p>The conference moderator pin is ${moderator_pin} and it ended on ${conference_date_end}.</p>
<p>You can download your conference recording by clicking <a href='http://${domain_name}/app/conference_centers/conference_session_details.php?uuid=${conference_uuid}'>here</a>.</p>
<hr noshade="noshade" size="1"/>
Moderator PIN: ${moderator_pin}
Ended: ${conference_date_end}<br/>
</font>
@@ -0,0 +1 @@
New recording available for conference ${moderator_pin} ${conference_date_end}.
@@ -0,0 +1,328 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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 default values
min_digits = 1;
max_digits = 8;
max_tries = 3;
max_timeouts = 3;
digit_timeout = 3000;
--direct dial
direct_dial = {}
direct_dial["enabled"] = "true";
direct_dial["max_digits"] = 4;
--debug
debug["info"] = false;
debug["sql"] = false;
--get the argv values
script_name = argv[0];
voicemail_action = argv[1];
--starting values
dtmf_digits = '';
timeouts = 0;
password_tries = 0;
--connect to the database
dofile(scripts_dir.."/resources/functions/database_handle.lua");
dbh = database_handle('system');
--set the api
api = freeswitch.API();
--if the session exists
if (session ~= nil) then
--answer the session
if (session:ready()) then
session:answer();
end
--unset bind meta app
session:execute("unbind_meta_app", "");
--set the callback function
if (session:ready()) then
session:setVariable("playback_terminators", "#");
session:setInputCallback("on_dtmf", "");
end
--get session variables
context = session:getVariable("context");
sounds_dir = session:getVariable("sounds_dir");
domain_name = session:getVariable("domain_name");
uuid = session:getVariable("uuid");
voicemail_id = session:getVariable("voicemail_id");
voicemail_action = session:getVariable("voicemail_action");
base_dir = session:getVariable("base_dir");
destination_number = session:getVariable("destination_number");
caller_id_name = session:getVariable("caller_id_name");
caller_id_number = session:getVariable("caller_id_number");
skip_instructions = session:getVariable("skip_instructions");
skip_greeting = session:getVariable("skip_greeting");
vm_message_ext = session:getVariable("vm_message_ext");
if (not vm_message_ext) then vm_message_ext = 'wav'; end
--set the sounds path for the language, dialect and voice
default_language = session:getVariable("default_language");
default_dialect = session:getVariable("default_dialect");
default_voice = session:getVariable("default_voice");
if (not default_language) then default_language = 'en'; end
if (not default_dialect) then default_dialect = 'us'; end
if (not default_voice) then default_voice = 'callie'; end
--get the domain_uuid
domain_uuid = session:getVariable("domain_uuid");
if (domain_count > 1) then
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", "[xml_handler] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(rows)
domain_uuid = rows["domain_uuid"];
end);
end
end
end
if (domain_uuid ~= nil) then
domain_uuid = string.lower(domain_uuid);
end
--set the voicemail_dir
voicemail_dir = base_dir.."/storage/voicemail/default/"..domain_name;
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] voicemail_dir: " .. voicemail_dir .. "\n");
end
--get the voicemail settings
if (voicemail_id ~= nil) then
if (session:ready()) then
--get the information from the database
sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_id = ']] .. voicemail_id ..[['
AND voicemail_enabled = 'true' ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
voicemail_uuid = string.lower(row["voicemail_uuid"]);
voicemail_password = row["voicemail_password"];
greeting_id = row["greeting_id"];
voicemail_mail_to = row["voicemail_mail_to"];
voicemail_attach_file = row["voicemail_attach_file"];
voicemail_local_after_email = row["voicemail_local_after_email"];
end);
--set default values
if (voicemail_local_after_email == nil) then
voicemail_local_after_email = "true";
end
if (voicemail_attach_file == nil) then
voicemail_attach_file = "true";
end
end
end
end
--general functions
dofile(scripts_dir.."/resources/functions/base64.lua");
dofile(scripts_dir.."/resources/functions/trim.lua");
dofile(scripts_dir.."/resources/functions/file_exists.lua");
dofile(scripts_dir.."/resources/functions/explode.lua");
dofile(scripts_dir.."/resources/functions/format_seconds.lua");
dofile(scripts_dir.."/resources/functions/mkdir.lua");
--voicemail functions
dofile(scripts_dir.."/app/voicemail/resources/functions/on_dtmf.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/get_voicemail_id.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/check_password.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/change_password.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/macro.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/play_greeting.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/record_message.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/record_menu.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/forward_to_extension.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/main_menu.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/listen_to_recording.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/message_waiting.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/send_email.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/delete_recording.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/message_saved.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/return_call.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/menu_messages.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/advanced.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/record_greeting.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/choose_greeting.lua");
dofile(scripts_dir.."/app/voicemail/resources/functions/record_name.lua");
--send a message waiting event
if (voicemail_action == "mwi") then
--get the mailbox info
account = argv[2];
array = explode("@", account);
voicemail_id = array[1];
domain_name = array[2];
--send information the console
debug["info"] = "true";
--get voicemail message details
sql = [[SELECT * FROM v_domains WHERE domain_name = ']] .. domain_name ..[[']]
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
domain_uuid = string.lower(row["domain_uuid"]);
end);
--get the message count and send the mwi event
message_waiting(voicemail_id, domain_uuid);
end
--check messages
if (voicemail_action == "check") then
if (session:ready()) then
--check the voicemail password
check_password(voicemail_id, password_tries);
--send to the main menu
timeouts = 0;
main_menu();
end
end
--leave a message
if (voicemail_action == "save") then
--valid voicemail
if (voicemail_uuid ~= nil) then
--save the recording
timeouts = 0;
play_greeting();
record_message();
--save the message to the voicemail messages
if (message_length > 2) then
local sql = {}
table.insert(sql, "INSERT INTO v_voicemail_messages ");
table.insert(sql, "(");
table.insert(sql, "voicemail_message_uuid, ");
table.insert(sql, "domain_uuid, ");
table.insert(sql, "voicemail_uuid, ");
table.insert(sql, "created_epoch, ");
table.insert(sql, "caller_id_name, ");
table.insert(sql, "caller_id_number, ");
table.insert(sql, "message_length ");
--table.insert(sql, "message_status, ");
--table.insert(sql, "message_priority, ");
table.insert(sql, ") ");
table.insert(sql, "VALUES ");
table.insert(sql, "( ");
table.insert(sql, "'".. uuid .."', ");
table.insert(sql, "'".. domain_uuid .."', ");
table.insert(sql, "'".. voicemail_uuid .."', ");
table.insert(sql, "'".. start_epoch .."', ");
table.insert(sql, "'".. caller_id_name .."', ");
table.insert(sql, "'".. caller_id_number .."', ");
table.insert(sql, "'".. message_length .."' ");
--table.insert(sql, "'".. message_status .."', ");
--table.insert(sql, "'".. message_priority .."' ");
table.insert(sql, ") ");
sql = table.concat(sql, "\n");
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
dbh:query(sql);
end
--set the message waiting event
if (message_length > 2) then
local event = freeswitch.Event("message_waiting");
event:addHeader("MWI-Messages-Waiting", "yes");
event:addHeader("MWI-Message-Account", "sip:"..voicemail_id.."@"..domain_name);
event:fire();
end
--send the email with the voicemail recording attached
if (message_length > 2) then
send_email(voicemail_id, uuid);
end
else
--voicemail not enabled or does not exist
referred_by = session:getVariable("sip_h_Referred-By");
if (referred_by) then
referred_by = referred_by:match('[%d]+');
session:transfer(referred_by, "XML", context);
end
end
end
--close the database connection
dbh:release();
--notes
--record the video
--records audio only
--result = session:execute("set", "enable_file_write_buffering=false");
--mkdir(voicemail_dir.."/"..voicemail_id);
--session:recordFile("/tmp/recording.fsv", 200, 200, 200);
--records audio and video
--result = session:execute("record_fsv", "file.fsv");
--freeswitch.consoleLog("notice", "[voicemail] SQL: " .. result .. "\n");
--play the video recording
--plays the video
--result = session:execute("play_fsv", "/tmp/recording.fsv");
--plays the file but without the video
--dtmf = session:playAndGetDigits(min_digits, max_digits, max_tries, digit_timeout, "#", "/tmp/recording.fsv", "", "\\d+");
--freeswitch.consoleLog("notice", "[voicemail] SQL: " .. result .. "\n");
--callback (works with DTMF)
--http://wiki.freeswitch.org/wiki/Mod_fsv
--mkdir(voicemail_dir.."/"..voicemail_id);
--session:recordFile(file_name, max_len_secs, silence_threshold, silence_secs)
--session:sayPhrase(macro_name [,macro_data] [,language]);
--session:sayPhrase("voicemail_menu", "1:2:3:#", default_language);
--session:streamFile("directory/dir-to_select_entry.wav"); --works with setInputCallback
--session:streamFile("tone_stream://L=1;%(1000, 0, 640)");
--session:say("12345", default_language, "number", "pronounced");
--speak
--session:set_tts_parms("flite", "kal");
--session:speak("Please say the name of the person you're trying to contact");
--callback (execute and executeString does not work with DTMF)
--session:execute(api_string);
--session:executeString("playback "..mySound);
--uuid_video_refresh
--uuid_video_refresh,<uuid>,Send video refresh.,mod_commands
--may be used to clear video buffer before using record_fsv
@@ -0,0 +1,90 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--define a function for the advanced menu
function advanced ()
--clear the dtmf
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--To record a greeting press 1
if (session:ready()) then
dtmf_digits = macro(session, "to_record_greeting", 1, 100, '');
end
--To choose greeting press 2
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "choose_greeting", 1, 100, '');
end
end
--To record your name 3
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "to_record_name", 1, 100, '');
end
end
--To change your password press 6
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "change_password", 1, 100, '');
end
end
--For the main menu press 0
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "main_menu", 1, 5000, '');
end
end
--process the dtmf
if (session:ready()) then
if (dtmf_digits == "1") then
--To record a greeting press 1
timeouts = 0;
record_greeting();
elseif (dtmf_digits == "2") then
--To choose greeting press 2
timeouts = 0;
choose_greeting();
elseif (dtmf_digits == "3") then
--To record your name 3
record_name();
elseif (dtmf_digits == "6") then
--To change your password press 6
change_password(voicemail_id);
elseif (dtmf_digits == "0") then
--For the main menu press 0
timeouts = 0;
main_menu();
else
timeouts = timeouts + 1;
if (timeouts <= max_timeouts) then
advanced();
else
macro(session, "goodbye", 1, 1000, '');
session:hangup();
end
end
end
end
@@ -0,0 +1,51 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--check the voicemail password
function change_password(voicemail_id)
if (session:ready()) then
--flush dtmf digits from the input buffer
session:flushDigits();
--please enter your password followed by pound
dtmf_digits = '';
password = macro(session, "password_new", 20, 5000, '');
--update the voicemail password
sql = [[UPDATE v_voicemails
set voicemail_password = ']] .. password ..[['
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_id = ']] .. voicemail_id ..[['
AND voicemail_enabled = 'true' ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
dbh:query(sql);
--has been changed to
dtmf_digits = '';
macro(session, "password_changed", 20, 3000, password);
--advanced menu
timeouts = 0;
advanced();
end
end
@@ -0,0 +1,85 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--check the voicemail password
function check_password(voicemail_id, password_tries)
if (session:ready()) then
--flush dtmf digits from the input buffer
session:flushDigits();
--please enter your id followed by pound
if (voicemail_id) then
--do nothing
else
timeouts = 0;
voicemail_id = get_voicemail_id();
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] voicemail id: " .. voicemail_id .. "\n");
end
end
--get the voicemail settings from the database
if (voicemail_id) then
if (session:ready()) then
sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_id = ']] .. voicemail_id ..[['
AND voicemail_enabled = 'true' ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
voicemail_uuid = string.lower(row["voicemail_uuid"]);
voicemail_password = row["voicemail_password"];
greeting_id = row["greeting_id"];
voicemail_mail_to = row["voicemail_mail_to"];
voicemail_attach_file = row["voicemail_attach_file"];
voicemail_local_after_email = row["voicemail_local_after_email"];
end);
end
end
--please enter your password followed by pound
dtmf_digits = '';
password = macro(session, "voicemail_password", 20, 5000, '');
--freeswitch.consoleLog("notice", "[voicemail] password: " .. password .. "\n");
--compare the password from the database with the password provided by the user
if (voicemail_password ~= password) then
--incorrect password
dtmf_digits = '';
macro(session, "password_not_valid", 1, 1000, '');
if (session:ready()) then
password_tries = password_tries + 1;
if (password_tries < max_tries) then
check_password(voicemail_id, password_tries);
else
macro(session, "goodbye", 1, 1000, '');
session:hangup();
end
end
end
end
end
--dofile(scripts_dir.."/app/voicemail/resources/functions/check_password.lua");
@@ -0,0 +1,110 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--define a function to choose the greeting
function choose_greeting()
--flush dtmf digits from the input buffer
session:flushDigits();
--select the greeting
if (session:ready()) then
dtmf_digits = '';
greeting_id = macro(session, "choose_greeting_choose", 1, 5000, '');
end
--check to see if the greeting file exists
if (greeting_id ~= "0") then
if (not file_exists(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav")) then
--invalid greeting_id file does not exist
greeting_id = "invalid";
end
end
--validate the greeting_id
if (greeting_id == "0"
or greeting_id == "1"
or greeting_id == "2"
or greeting_id == "3"
or greeting_id == "4"
or greeting_id == "5"
or greeting_id == "6"
or greeting_id == "7"
or greeting_id == "8"
or greeting_id == "9") then
--valid greeting_id update the database
if (session:ready()) then
if (greeting_id == "0") then
sql = [[UPDATE v_voicemails SET greeting_id = null ]];
else
sql = [[UPDATE v_voicemails SET greeting_id = ']]..greeting_id..[[' ]];
end
sql = sql ..[[WHERE domain_uuid = ']] .. domain_uuid ..[[' ]]
sql = sql ..[[AND voicemail_uuid = ']] .. voicemail_uuid ..[[' ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
dbh:query(sql);
end
--play the greeting
if (session:ready()) then
if (file_exists(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav")) then
session:streamFile(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav");
end
end
--greeting selected
if (session:ready()) then
dtmf_digits = '';
macro(session, "greeting_selected", 1, 100, greeting_id);
end
--advanced menu
if (session:ready()) then
timeouts = 0;
advanced();
end
else
--invalid greeting_id
if (session:ready()) then
dtmf_digits = '';
greeting_id = macro(session, "choose_greeting_fail", 1, 100, '');
end
--send back to choose the greeting
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
choose_greeting();
else
timeouts = 0;
advanced();
end
end
end
end
@@ -0,0 +1,61 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--delete the message
function delete_recording(voicemail_id, uuid)
--message deleted
if (session ~= nil) then
if (session:ready()) then
dtmf_digits = '';
macro(session, "message_deleted", 1, 100, '');
end
end
--get the voicemail_uuid
sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_id = ']] .. voicemail_id ..[[']];
status = dbh:query(sql, function(row)
db_voicemail_uuid = row["voicemail_uuid"];
end);
--flush dtmf digits from the input buffer
session:flushDigits();
--delete the file
os.remove(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
--delete from the database
sql = [[DELETE FROM v_voicemail_messages
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_uuid = ']] .. db_voicemail_uuid ..[['
AND voicemail_message_uuid = ']] .. uuid ..[[']];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
dbh:query(sql);
--log to console
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail][deleted] message: " .. uuid .. "\n");
end
--clear the variable
db_voicemail_uuid = '';
end
@@ -0,0 +1,133 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--define a function to forward a message to an extension
function forward_to_extension(voicemail_id, uuid)
--flush dtmf digits from the input buffer
session:flushDigits();
--save the voicemail message
message_saved(voicemail_id, uuid);
--request the forward_voicemail_id
if (session:ready()) then
dtmf_digits = '';
forward_voicemail_id = macro(session, "forward_enter_extension", 20, 5000, '');
if (session:ready()) then
if (string.len(forward_voicemail_id) == 0) then
dtmf_digits = '';
forward_voicemail_id = macro(session, "forward_enter_extension", 20, 5000, '');
end
end
end
if (session:ready()) then
if (string.len(forward_voicemail_id) == 0) then
dtmf_digits = '';
forward_voicemail_id = macro(session, "forward_enter_extension", 20, 5000, '');
end
end
--get voicemail message details
if (session:ready()) then
sql = [[SELECT * FROM v_voicemail_messages
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_uuid = ']] .. voicemail_uuid ..[['
AND voicemail_message_uuid = ']] .. uuid ..[[']]
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
--get the values from the database
created_epoch = row["created_epoch"];
caller_id_name = row["caller_id_name"];
caller_id_number = row["caller_id_number"];
message_length = row["message_length"];
message_status = row["message_status"];
message_priority = row["message_priority"];
end);
end
--get the voicemail settings
sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_id = ']] .. forward_voicemail_id ..[['
AND voicemail_enabled = 'true' ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
forward_voicemail_uuid = string.lower(row["voicemail_uuid"]);
forward_voicemail_mail_to = row["voicemail_mail_to"];
forward_voicemail_attach_file = row["voicemail_attach_file"];
forward_voicemail_local_after_email = row["voicemail_local_after_email"];
end);
--save the message to the voicemail messages
local sql = {}
table.insert(sql, "INSERT INTO v_voicemail_messages ");
table.insert(sql, "(");
table.insert(sql, "voicemail_message_uuid, ");
table.insert(sql, "domain_uuid, ");
table.insert(sql, "voicemail_uuid, ");
table.insert(sql, "created_epoch, ");
table.insert(sql, "caller_id_name, ");
table.insert(sql, "caller_id_number, ");
table.insert(sql, "message_length ");
--table.insert(sql, "message_status, ");
--table.insert(sql, "message_priority, ");
table.insert(sql, ") ");
table.insert(sql, "VALUES ");
table.insert(sql, "( ");
table.insert(sql, "'".. uuid .."', ");
table.insert(sql, "'".. domain_uuid .."', ");
table.insert(sql, "'".. forward_voicemail_uuid .."', ");
table.insert(sql, "'".. created_epoch .."', ");
table.insert(sql, "'".. caller_id_name .."', ");
table.insert(sql, "'".. caller_id_number .."', ");
table.insert(sql, "'".. message_length .."' ");
--table.insert(sql, "'".. message_status .."', ");
--table.insert(sql, "'".. message_priority .."' ");
table.insert(sql, ") ");
sql = table.concat(sql, "\n");
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
dbh:query(sql);
--set the message waiting event
local event = freeswitch.Event("message_waiting");
event:addHeader("MWI-Messages-Waiting", "yes");
event:addHeader("MWI-Message-Account", "sip:"..forward_voicemail_id.."@"..domain_name);
event:fire();
--if local after email is true then copy the recording file
mkdir(voicemail_dir.."/"..forward_voicemail_id);
os.execute("cp "..voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext.." "..voicemail_dir.."/"..forward_voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
--send the email with the voicemail recording attached
send_email(forward_voicemail_id, uuid);
end
@@ -0,0 +1,43 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--get the voicemail id
function get_voicemail_id()
session:flushDigits();
dtmf_digits = '';
voicemail_id = macro(session, "voicemail_id", 20, 5000, '');
if (string.len(voicemail_id) == 0) then
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
voicemail_id = get_voicemail_id();
else
macro(session, "goodbye", 1, 1000, '');
session:hangup();
end
end
end
return voicemail_id;
end
@@ -0,0 +1,137 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--define function to listen to the recording
function listen_to_recording (message_number, uuid, created_epoch, caller_id_name, caller_id_number)
--set default values
dtmf_digits = '';
max_digits = 1;
--flush dtmf digits from the input buffer
session:flushDigits();
--set the display
if (session:ready()) then
reply = api:executeString("uuid_display "..session:get_uuid().." "..caller_id_number);
end
--say the message number
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "message_number", 1, 100, '');
end
end
--say the number
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
session:say(message_number, default_language, "NUMBER", "pronounced");
end
end
--say the message date
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
session:say(created_epoch, default_language, "CURRENT_DATE_TIME", "pronounced");
end
end
--play the message
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
session:streamFile(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
session:streamFile("silence_stream://1000");
end
end
--to listen to the recording press 1
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "listen_to_recording", 1, 100, '');
end
end
--to save the recording press 2
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "save_recording", 1, 100, '');
end
end
--to return the call now press 5
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "return_call", 1, 100, '');
end
end
--to delete the recording press 7
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "delete_recording", 1, 100, '');
end
end
--to forward this message press 8
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "to_forward_message", 1, 100, '');
end
end
--to forward this recording to your email press 9
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "forward_to_email", 1, 3000, '');
end
end
--wait for more digits
--if (session:ready()) then
-- if (string.len(dtmf_digits) == 0) then
-- dtmf_digits = session:getDigits(max_digits, "#", 1, 3000);
-- end
--end
--process the dtmf
if (session:ready()) then
if (dtmf_digits == "1") then
listen_to_recording(message_number, uuid, created_epoch, caller_id_name, caller_id_number);
elseif (dtmf_digits == "2") then
message_saved(voicemail_id, uuid);
macro(session, "message_saved", 1, 100, '');
elseif (dtmf_digits == "5") then
message_saved(voicemail_id, uuid);
return_call(caller_id_number);
elseif (dtmf_digits == "7") then
delete_recording(voicemail_id, uuid);
message_waiting(voicemail_id, domain_uuid);
elseif (dtmf_digits == "8") then
forward_to_extension(voicemail_id, uuid);
dtmf_digits = '';
macro(session, "message_saved", 1, 100, '');
elseif (dtmf_digits == "9") then
send_email(voicemail_id, uuid);
dtmf_digits = '';
macro(session, "emailed", 1, 100, '');
elseif (dtmf_digits == "*") then
timeouts = 0;
main_menu();
elseif (dtmf_digits == "0") then
message_saved(voicemail_id, uuid);
session:transfer("0", "XML", context);
else
message_saved(voicemail_id, uuid);
macro(session, "message_saved", 1, 100, '');
end
end
end
@@ -0,0 +1,319 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--define the macro function
function macro(session, name, max_digits, max_timeout, param)
if (session:ready()) then
--create an empty table
actions = {}
--Please enter your id followed by
if (name == "voicemail_id") then
table.insert(actions, {app="streamFile",data="voicemail/vm-enter_id.wav"});
table.insert(actions, {app="streamFile",data="digits/pound.wav"});
end
--Please enter your id followed by
if (name == "voicemail_password") then
table.insert(actions, {app="streamFile",data="voicemail/vm-enter_pass.wav"});
table.insert(actions, {app="streamFile",data="digits/pound.wav"});
end
--the person at extension 101 is not available record your message at the tone press any key or stop talking to end the recording
if (name == "person_not_available_record_message") then
table.insert(actions, {app="streamFile",data="voicemail/vm-person.wav"});
--pronounce the voicemail_id
table.insert(actions, {app="say.number.iterated",data=voicemail_id});
table.insert(actions, {app="streamFile",data="voicemail/vm-not_available.wav"});
end
--record your message at the tone press any key or stop talking to end the recording
if (name == "record_message") then
table.insert(actions, {app="streamFile",data="voicemail/vm-record_message.wav"});
end
--beep
if (name == "record_beep") then
table.insert(actions, {app="tone_stream",data="L=1;%(1000, 0, 640)"});
end
--to listen to the recording press 1
if (name == "to_listen_to_recording") then
table.insert(actions, {app="streamFile",data="voicemail/vm-listen_to_recording.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/1.wav"});
end
--to save the recording press 2
if (name == "to_save_recording") then
table.insert(actions, {app="streamFile",data="voicemail/vm-save_recording.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/2.wav"});
end
--to rerecord press 3
if (name == "to_rerecord") then
table.insert(actions, {app="streamFile",data="voicemail/vm-rerecord.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/3.wav"});
end
--You have zero new messages
if (name == "new_messages") then
table.insert(actions, {app="streamFile",data="voicemail/vm-you_have.wav"});
table.insert(actions, {app="say.number.pronounced",data=param});
table.insert(actions, {app="streamFile",data="voicemail/vm-new.wav"});
if (param == "1") then
table.insert(actions, {app="streamFile",data="voicemail/vm-message.wav"});
else
table.insert(actions, {app="streamFile",data="voicemail/vm-messages.wav"});
end
end
--You have zero saved messages
if (name == "saved_messages") then
table.insert(actions, {app="streamFile",data="voicemail/vm-you_have.wav"});
table.insert(actions, {app="say.number.pronounced",data=param});
table.insert(actions, {app="streamFile",data="voicemail/vm-saved.wav"});
if (param == "1") then
table.insert(actions, {app="streamFile",data="voicemail/vm-message.wav"});
else
table.insert(actions, {app="streamFile",data="voicemail/vm-messages.wav"});
end
end
--To listen to new messages press 1
if (name == "listen_to_new_messages") then
table.insert(actions, {app="streamFile",data="voicemail/vm-listen_new.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/1.wav"});
end
--To listen to saved messages press 2
if (name == "listen_to_saved_messages") then
table.insert(actions, {app="streamFile",data="voicemail/vm-listen_saved.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/2.wav"});
end
--For advanced options press 5
if (name == "advanced") then
table.insert(actions, {app="streamFile",data="voicemail/vm-advanced.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/5.wav"});
end
--Advanced Options Menu
--To record a greeting press 1
if (name == "to_record_greeting") then
table.insert(actions, {app="streamFile",data="voicemail/vm-to_record_greeting.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/1.wav"});
end
--Choose a greeting between 1 and 9
if (name == "choose_greeting_choose") then
table.insert(actions, {app="streamFile",data="voicemail/vm-choose_greeting_choose.wav"});
end
--Greeting invalid value
if (name == "choose_greeting_fail") then
table.insert(actions, {app="streamFile",data="voicemail/vm-choose_greeting_fail.wav"});
end
--Record your greeting at the tone press any key or stop talking to end the recording
if (name == "record_greeting") then
table.insert(actions, {app="streamFile",data="voicemail/vm-record_greeting.wav"});
table.insert(actions, {app="tone_stream",data="L=1;%(1000, 0, 640)"});
end
--To choose greeting press 2
if (name == "choose_greeting") then
table.insert(actions, {app="streamFile",data="voicemail/vm-choose_greeting.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/2.wav"});
end
--Greeting 1 selected
if (name == "greeting_selected") then
table.insert(actions, {app="streamFile",data="voicemail/vm-greeting.wav"});
table.insert(actions, {app="streamFile",data="digits/"..param..".wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-selected.wav"});
end
--To record your name 3
if (name == "to_record_name") then
table.insert(actions, {app="streamFile",data="voicemail/vm-record_name2.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/3.wav"});
end
--At the tone please record your name press any key or stop talking to end the recording
if (name == "record_name") then
table.insert(actions, {app="streamFile",data="voicemail/vm-record_name1.wav"});
table.insert(actions, {app="tone_stream",data="L=1;%(1000, 0, 640)"});
end
--To change your password press 6
if (name == "change_password") then
table.insert(actions, {app="streamFile",data="voicemail/vm-change_password.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/6.wav"});
end
--For the main menu press 0
if (name == "main_menu") then
table.insert(actions, {app="streamFile",data="voicemail/vm-main_menu.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/0.wav"});
end
--To exit press *
if (name == "to_exit_press") then
table.insert(actions, {app="streamFile",data="voicemail/vm-to_exit.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/star.wav"});
end
--Additional Macros
--Please enter your new password then press the # key #
if (name == "password_new") then
table.insert(actions, {app="streamFile",data="voicemail/vm-enter_new_pin.wav"});
end
--Has been changed to
if (name == "password_changed") then
table.insert(actions, {app="streamFile",data="voicemail/vm-has_been_changed_to.wav"});
table.insert(actions, {app="say.number.iterated",data=param});
end
--Login Incorrect
--if (name == "password_not_valid") then
-- table.insert(actions, {app="streamFile",data="voicemail/vm-password_not_valid.wav"});
--end
--Login Incorrect
if (name == "password_not_valid") then
table.insert(actions, {app="streamFile",data="voicemail/vm-fail_auth.wav"});
end
--Too many failed attempts
if (name == "too_many_failed_attempts") then
table.insert(actions, {app="streamFile",data="voicemail/vm-abort.wav"});
end
--Message number
if (name == "message_number") then
table.insert(actions, {app="streamFile",data="voicemail/vm-message_number.wav"});
end
--To listen to the recording press 1
if (name == "listen_to_recording") then
table.insert(actions, {app="streamFile",data="voicemail/vm-listen_to_recording.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/1.wav"});
end
--To save the recording press 2
if (name == "save_recording") then
table.insert(actions, {app="streamFile",data="voicemail/vm-save_recording.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/2.wav"});
end
--To delete the recording press 7
if (name == "delete_recording") then
table.insert(actions, {app="streamFile",data="voicemail/vm-delete_recording.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/7.wav"});
end
--Message deleted
if (name == "message_deleted") then
table.insert(actions, {app="streamFile",data="voicemail/vm-message.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-deleted.wav"});
end
--To return the call now press 5
if (name == "return_call") then
table.insert(actions, {app="streamFile",data="voicemail/vm-return_call.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/5.wav"});
end
--To forward this message press 8
if (name == "to_forward_message") then
table.insert(actions, {app="streamFile",data="voicemail/vm-to_forward.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/8.wav"});
end
--Please enter the extension to forward this message to followed by #
if (name == "forward_enter_extension") then
table.insert(actions, {app="streamFile",data="voicemail/vm-forward_enter_ext.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-followed_by_pound.wav"});
end
--To forward this recording to your email press 9
if (name == "forward_to_email") then
table.insert(actions, {app="streamFile",data="voicemail/vm-forward_to_email.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-press.wav"});
table.insert(actions, {app="streamFile",data="digits/9.wav"});
end
--Emailed
if (name == "emailed") then
table.insert(actions, {app="streamFile",data="voicemail/vm-emailed.wav"});
end
--Please enter the extension to send this message to followed by #
--if (name == "send_message_to_extension") then
-- table.insert(actions, {app="streamFile",data="voicemail/vm-zzz.wav"});
--end
--Message saved
if (name == "message_saved") then
table.insert(actions, {app="streamFile",data="voicemail/vm-message.wav"});
table.insert(actions, {app="streamFile",data="voicemail/vm-saved.wav"});
end
--Your recording is below the minimal acceptable length, please try again.
if (name == "too_small") then
table.insert(actions, {app="streamFile",data="voicemail/vm-too-small.wav"});
end
--Goodbye
if (name == "goodbye") then
table.insert(actions, {app="streamFile",data="voicemail/vm-goodbye.wav"});
end
--if actions table exists then process it
if (actions) then
--set default values
tries = 1;
timeout = 100;
--loop through the action and data
for key, row in pairs(actions) do
--freeswitch.consoleLog("notice", "[voicemail] app: " .. row.app .. " data: " .. row.data .. "\n");
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
if (row.app == "streamFile") then
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/"..row.data);
elseif (row.app == "playback") then
session:streamFile(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/"..row.data);
elseif (row.app == "tone_stream") then
session:streamFile("tone_stream://"..row.data);
elseif (row.app == "silence_stream") then
session:streamFile("silence_stream://100"..row.data);
elseif (row.app == "playAndGetDigits") then
--playAndGetDigits <min> <max> <tries> <timeout> <terminators> <file> <invalid_file> <var_name> <regexp> <digit_timeout>
if (not file_exists(sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/"..row.data)) then
dtmf_digits = session:playAndGetDigits(min_digits, max_digits, tries, timeout, "#", sounds_dir.."/"..default_language.."/"..default_dialect.."/"..default_voice.."/"..row.data, "", "\\d+", max_timeout);
else
dtmf_digits = session:playAndGetDigits(min_digits, max_digits, tries, timeout, "#", row.data, "", "\\d+", max_timeout);
end
elseif (row.app == "say.number.pronounced") then
session:say(row.data, default_language, "number", "pronounced");
elseif (row.app == "say.number.iterated") then
session:say(row.data, default_language, "number", "iterated");
end
session:streamFile("silence_stream://100");
end --if
end --session:ready
end --for
--get the remaining digits
if (session:ready()) then
if (string.len(dtmf_digits) < max_digits) then
dtmf_digits = dtmf_digits .. session:getDigits(max_digits, "#", max_timeout);
end
end
--return dtmf the digits
return dtmf_digits;
else
--no dtmf digits to return
return '';
end
end
end
@@ -0,0 +1,115 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--define function main menu
function main_menu ()
if (voicemail_uuid) then
--clear the value
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--new voicemail count
if (session:ready()) then
sql = [[SELECT count(*) as new_messages FROM v_voicemail_messages
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_uuid = ']] .. voicemail_uuid ..[['
AND (message_status is null or message_status = '') ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
new_messages = row["new_messages"];
end);
dtmf_digits = macro(session, "new_messages", 1, 100, new_messages);
end
--saved voicemail count
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
sql = [[SELECT count(*) as saved_messages FROM v_voicemail_messages
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_uuid = ']] .. voicemail_uuid ..[['
AND message_status = 'saved' ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
saved_messages = row["saved_messages"];
end);
dtmf_digits = macro(session, "saved_messages", 1, 100, saved_messages);
end
end
--to listen to new message
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "listen_to_new_messages", 1, 100, '');
end
end
--to listen to saved message
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "listen_to_saved_messages", 1, 100, '');
end
end
--for advanced options
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "advanced", 1, 100, '');
end
end
--to exit press #
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "to_exit_press", 1, 3000, '');
end
end
--process the dtmf
if (session:ready()) then
if (dtmf_digits == "1") then
menu_messages("new");
elseif (dtmf_digits == "2") then
menu_messages("saved");
elseif (dtmf_digits == "5") then
timeouts = 0;
advanced();
elseif (dtmf_digits == "0") then
main_menu();
elseif (dtmf_digits == "*") then
dtmf_digits = '';
macro(session, "goodbye", 1, 100, '');
session:hangup();
else
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
main_menu();
else
macro(session, "goodbye", 1, 1000, '');
session:hangup();
end
end
end
end
end
end
@@ -0,0 +1,111 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--define function for messages menu
function menu_messages (message_status)
--set default values
max_timeout = 2000;
min_digits = 1;
max_digits = 1;
tries = 1;
timeout = 2000;
--clear the dtmf
dtmf_digits = '';
--flush dtmf digits from the input buffer
--session:flushDigits();
--set the message number
message_number = 0;
--message_status new,saved
if (session:ready()) then
if (voicemail_id ~= nil) then
sql = [[SELECT * FROM v_voicemail_messages
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_uuid = ']] .. voicemail_uuid ..[[']]
if (message_status == "new") then
sql = sql .. [[AND (message_status is null or message_status = '') ]];
elseif (message_status == "saved") then
sql = sql .. [[AND message_status = 'saved' ]];
end
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
--get the values from the database
--row["voicemail_message_uuid"];
--row["created_epoch"];
--row["caller_id_name"];
--row["caller_id_number"];
--row["message_length"];
--row["message_status"];
--row["message_priority"];
--increment the message count
message_number = message_number + 1;
--listen to the message
if (session:ready()) then
if (debug["info"]) then
freeswitch.consoleLog("notice", message_number.." "..string.lower(row["voicemail_message_uuid"]).." "..row["created_epoch"]);
end
listen_to_recording(message_number, string.lower(row["voicemail_message_uuid"]), row["created_epoch"], row["caller_id_name"], row["caller_id_number"]);
end
end);
end
end
--voicemail count if zero new messages set the mwi to no
if (session:ready()) then
if (voicemail_id ~= nil) then
sql = [[SELECT count(*) as new_messages FROM v_voicemail_messages
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_uuid = ']] .. voicemail_uuid ..[['
AND (message_status is null or message_status = '') ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
--send the message waiting event
local event = freeswitch.Event("message_waiting");
if (row["new_messages"] == "0") then
event:addHeader("MWI-Messages-Waiting", "no");
else
event:addHeader("MWI-Messages-Waiting", "yes");
end
event:addHeader("MWI-Message-Account", "sip:"..voicemail_id.."@"..domain_name);
event:fire();
end);
end
end
--set the display
if (session:ready()) then
reply = api:executeString("uuid_display "..session:get_uuid().." "..destination_number);
end
--send back to the main menu
if (session:ready()) then
timeouts = 0;
main_menu();
end
end
@@ -0,0 +1,56 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--save the message
function message_saved(voicemail_id, uuid)
--clear the dtmf
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--get the voicemail_uuid
sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_id = ']] .. voicemail_id ..[[']];
status = dbh:query(sql, function(row)
db_voicemail_uuid = row["voicemail_uuid"];
end);
--delete from the database
sql = [[UPDATE v_voicemail_messages SET message_status = 'saved'
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_uuid = ']] .. db_voicemail_uuid ..[['
AND voicemail_message_uuid = ']] .. uuid ..[[']];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
dbh:query(sql);
--log to console
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail][saved] id: " .. voicemail_id .. " message: "..uuid.."\n");
end
--check the message waiting status
message_waiting(voicemail_id, domain_uuid);
--clear the variable
db_voicemail_uuid = '';
end
@@ -0,0 +1,52 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--voicemail count if zero new messages set the mwi to no
function message_waiting(voicemail_id, domain_uuid)
sql = [[SELECT count(*) as message_count FROM v_voicemail_messages as m, v_voicemails as v
WHERE v.domain_uuid = ']] .. domain_uuid ..[['
AND v.voicemail_uuid = m.voicemail_uuid
AND v.voicemail_id = ']] .. voicemail_id ..[['
AND (m.message_status is null or m.message_status = '') ]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
--send the message waiting event
local event = freeswitch.Event("message_waiting");
if (row["message_count"] == "0") then
--freeswitch.consoleLog("notice", "[voicemail] mailbox: "..voicemail_id.."@"..domain_name.." messages: " .. row["message_count"] .. " no messages\n");
event:addHeader("MWI-Messages-Waiting", "no");
else
event:addHeader("MWI-Messages-Waiting", "yes");
end
event:addHeader("MWI-Message-Account", "sip:"..voicemail_id.."@"..domain_name);
event:fire();
--log to console
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] mailbox: "..voicemail_id.."@"..domain_name.." messages: " .. row["message_count"] .. " \n");
end
end);
end
@@ -0,0 +1,46 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--define on_dtmf call back function
function on_dtmf(s, type, obj, arg)
if (type == "dtmf") then
freeswitch.console_log("info", "[voicemail] dtmf digit: " .. obj['digit'] .. ", duration: " .. obj['duration'] .. "\n");
if (obj['digit'] == "#") then
return 0;
else
dtmf_digits = dtmf_digits .. obj['digit'];
if (debug["info"]) then
freeswitch.console_log("info", "[voicemail] dtmf digits: " .. dtmf_digits .. ", length: ".. string.len(dtmf_digits) .." max_digits: " .. max_digits .. "\n");
end
if (string.len(dtmf_digits) >= max_digits) then
if (debug["info"]) then
freeswitch.console_log("info", "[voicemail] max_digits reached\n");
end
return 0;
end
end
end
return 0;
end
@@ -0,0 +1,44 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--play the greeting
function play_greeting()
--voicemail prompt
if (skip_greeting == "true") then
--skip the greeting
else
if (session:ready()) then
dtmf_digits = '';
if (string.len(greeting_id) > 0) then
--play the custom greeting
session:streamFile(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav");
session:streamFile("silence_stream://200");
else
--play the default greeting
dtmf_digits = macro(session, "person_not_available_record_message", 1, 200);
end
end
end
end
@@ -0,0 +1,96 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--define a function to record the greeting
function record_greeting()
--flush dtmf digits from the input buffer
session:flushDigits();
--Choose a greeting between 1 and 9
if (session:ready()) then
dtmf_digits = '';
greeting_id = macro(session, "choose_greeting_choose", 1, 5000, '');
freeswitch.consoleLog("notice", "[voicemail] greeting_id: " .. greeting_id .. "\n");
end
--validate the greeting_id
if (greeting_id == "1"
or greeting_id == "2"
or greeting_id == "3"
or greeting_id == "4"
or greeting_id == "5"
or greeting_id == "6"
or greeting_id == "7"
or greeting_id == "8"
or greeting_id == "9") then
--record your greeting at the tone press any key or stop talking to end the recording
if (session:ready()) then
dtmf_digits = '';
macro(session, "record_greeting", 1, 100, '');
end
--record the greeting
if (session:ready()) then
max_len_seconds = 30;
silence_threshold = 30;
silence_seconds = 5;
mkdir(voicemail_dir.."/"..voicemail_id);
-- syntax is session:recordFile(file_name, max_len_secs, silence_threshold, silence_secs)
result = session:recordFile(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav", max_len_seconds, silence_threshold, silence_seconds);
--session:execute("record", voicemail_dir.."/"..uuid.." 180 200");
end
--play the greeting
--if (session:ready()) then
-- if (file_exists(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav")) then
-- session:streamFile(voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav");
-- end
--end
--option to play, save, and re-record the greeting
if (session:ready()) then
timeouts = 0;
record_menu("greeting", voicemail_dir.."/"..voicemail_id.."/greeting_"..greeting_id..".wav");
end
else
--invalid greeting_id
if (session:ready()) then
dtmf_digits = '';
macro(session, "choose_greeting_fail", 1, 100, '');
end
--send back to choose the greeting
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
record_greeting();
else
timeouts = 0;
advanced();
end
end
end
end
@@ -0,0 +1,116 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--record message menu
function record_menu(type, file)
if (session:ready()) then
--clear the dtmf digits variable
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--to listen to the recording press 1
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "to_listen_to_recording", 1, 100, '');
end
end
--to save the recording press 2
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "to_save_recording", 1, 100, '');
end
end
--to re-record press 3
if (session:ready()) then
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "to_rerecord", 1, 3000, '');
end
end
--process the dtmf
if (session:ready()) then
if (dtmf_digits == "1") then
--listen to the recording
session:streamFile(file);
--session:streamFile(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
--record menu 1 listen to the recording, 2 save the recording, 3 re-record
record_menu(type, file);
elseif (dtmf_digits == "2") then
--save the message
dtmf_digits = '';
macro(session, "message_saved", 1, 100, '');
if (type == "message") then
--goodbye
macro(session, "goodbye", 1, 100, '');
--hangup the call
session:hangup();
end
if (type == "greeting") then
advanced();
end
if (type == "name") then
advanced();
end
elseif (dtmf_digits == "3") then
--re-record the message
timeouts = 0;
dtmf_digits = '';
if (type == "message") then
record_message();
end
if (type == "greeting") then
record_greeting();
end
if (type == "name") then
record_name();
end
elseif (dtmf_digits == "*") then
--hangup
if (session:ready()) then
dtmf_digits = '';
macro(session, "goodbye", 1, 100, '');
session:hangup();
end
else
if (session:ready()) then
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
record_menu(type, file);
else
if (type == "message") then
macro(session, "goodbye", 1, 1000, '');
session:hangup();
end
if (type == "greeting") then
advanced();
end
if (type == "name") then
advanced();
end
end
end
end
end
end
end
@@ -0,0 +1,122 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--save the recording
function record_message()
--record your message at the tone press any key or stop talking to end the recording
if (skip_greeting == "true") then
--skip the greeting
else
if (string.len(dtmf_digits) == 0) then
dtmf_digits = macro(session, "record_message", 1, 100);
end
end
--direct dial
if (dtmf_digits) then
if (string.len(dtmf_digits) > 0) then
if (session:ready()) then
if (direct_dial["enabled"] == "true") then
if (string.len(dtmf_digits) < max_digits) then
dtmf_digits = dtmf_digits .. session:getDigits(direct_dial["max_digits"], "#", 5000);
end
end
end
if (session:ready()) then
freeswitch.consoleLog("notice", "[voicemail] dtmf_digits: " .. string.sub(dtmf_digits, 0, 1) .. "\n");
if (dtmf_digits == "*") then
--check the voicemail password
check_password(voicemail_id, password_tries);
--send to the main menu
timeouts = 0;
main_menu();
elseif (string.sub(dtmf_digits, 0, 1) == "*") then
--do not allow dialing numbers prefixed with *
session:hangup();
else
session:transfer(dtmf_digits, "XML", context);
end
end
end
end
--play the beep
dtmf_digits = '';
result = macro(session, "record_beep", 1, 100);
--start epoch
start_epoch = os.time();
--save the recording
-- syntax is session:recordFile(file_name, max_len_secs, silence_threshold, silence_secs)
max_len_seconds = 300;
silence_threshold = 30;
silence_seconds = 5;
mkdir(voicemail_dir.."/"..voicemail_id);
result = session:recordFile(voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext, max_len_seconds, silence_threshold, silence_seconds);
--session:execute("record", voicemail_dir.."/"..uuid.." 180 200");
--stop epoch
stop_epoch = os.time();
--calculate the message length
message_length = stop_epoch - start_epoch;
message_length_formatted = format_seconds(message_length);
--if the recording is below the minimal length then re-record the message
if (message_length > 2) then
--continue
else
if (session:ready()) then
--your recording is below the minimal acceptable length, please try again
dtmf_digits = '';
macro(session, "too_small", 1, 100);
--record your message at the tone
timeouts = timeouts + 1;
if (timeouts < max_timeouts) then
record_message();
else
timeouts = 0;
record_menu("message", voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
end
end
end
--instructions press 1 to listen to the recording, press 2 to save the recording, press 3 to re-record
if (session:ready()) then
if (skip_instructions == "true") then
--save the message
dtmf_digits = '';
macro(session, "message_saved", 1, 100, '');
macro(session, "goodbye", 1, 100, '');
--hangup the call
session:hangup();
else
timeouts = 0;
record_menu("message", voicemail_dir.."/"..voicemail_id.."/msg_"..uuid.."."..vm_message_ext);
end
end
end
@@ -0,0 +1,55 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--define a function to record the name
function record_name()
if (session:ready()) then
--flush dtmf digits from the input buffer
session:flushDigits();
--play the name record
dtmf_digits = '';
macro(session, "record_name", 1, 100, '');
--save the recording
-- syntax is session:recordFile(file_name, max_len_secs, silence_threshold, silence_secs)
max_len_seconds = 30;
silence_threshold = 30;
silence_seconds = 5;
mkdir(voicemail_dir.."/"..voicemail_id);
result = session:recordFile(voicemail_dir.."/"..voicemail_id.."/recorded_name.wav", max_len_seconds, silence_threshold, silence_seconds);
--session:execute("record", voicemail_dir.."/"..uuid.." 180 200");
--play the name
--session:streamFile(voicemail_dir.."/"..voicemail_id.."/recorded_name.wav");
--option to play, save, and re-record the name
if (session:ready()) then
timeouts = 0;
record_menu("name", voicemail_dir.."/"..voicemail_id.."/recorded_name.wav");
end
end
end
@@ -0,0 +1,36 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--define a function to return the call
function return_call(destination)
if (session:ready()) then
--clear the dtmf
dtmf_digits = '';
--flush dtmf digits from the input buffer
session:flushDigits();
--transfer the call
session:transfer(destination, "XML", context);
end
end
@@ -0,0 +1,156 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--define a function to send email
function send_email(id, uuid)
--get voicemail message details
sql = [[SELECT * FROM v_voicemails
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_id = ']] .. id ..[[']]
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
db_voicemail_uuid = string.lower(row["voicemail_uuid"]);
--voicemail_password = row["voicemail_password"];
--greeting_id = row["greeting_id"];
voicemail_mail_to = row["voicemail_mail_to"];
voicemail_attach_file = row["voicemail_attach_file"];
voicemail_local_after_email = row["voicemail_local_after_email"];
end);
--set default values
if (voicemail_local_after_email == nil) then
voicemail_local_after_email = "true";
end
if (voicemail_attach_file == nil) then
voicemail_attach_file = "true";
end
--require the email address to send the email
if (string.len(voicemail_mail_to) > 2) then
--get voicemail message details
sql = [[SELECT * FROM v_voicemail_messages
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_message_uuid = ']] .. uuid ..[[']]
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
--get the values from the database
--uuid = row["voicemail_message_uuid"];
created_epoch = row["created_epoch"];
caller_id_name = row["caller_id_name"];
caller_id_number = row["caller_id_number"];
message_length = row["message_length"];
--message_status = row["message_status"];
--message_priority = row["message_priority"];
end);
--format the message length and date
message_length_formatted = format_seconds(message_length);
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] message length: " .. message_length .. "\n");
end
local message_date = os.date("%A, %d %b %Y %I:%M %p", created_epoch)
--prepare the files
file_subject = scripts_dir.."/app/voicemail/resources/templates/"..default_language.."/"..default_dialect.."email_subject.tpl";
file_body = scripts_dir.."/app/voicemail/resources/templates/"..default_language.."/"..default_dialect.."/email_body.tpl";
if (not file_exists(file_subject)) then
file_subject = scripts_dir.."/app/voicemail/resources/templates/en/us/email_subject.tpl";
file_body = scripts_dir.."/app/voicemail/resources/templates/en/us/email_body.tpl";
end
--prepare the subject
local f = io.open(file_subject, "r");
local subject = f:read("*all");
f:close();
subject = subject:gsub("${caller_id_name}", caller_id_name);
subject = subject:gsub("${caller_id_number}", caller_id_number);
subject = subject:gsub("${message_date}", message_date);
subject = subject:gsub("${message_duration}", message_length_formatted);
subject = subject:gsub("${account}", id);
subject = subject:gsub("${domain_name}", domain_name);
subject = trim(subject);
subject = '=?utf-8?B?'..base64.enc(subject)..'?=';
--prepare the body
local f = io.open(file_body, "r");
body = f:read("*all");
f:close();
body = body:gsub("${caller_id_name}", caller_id_name);
body = body:gsub("${caller_id_number}", caller_id_number);
body = body:gsub("${message_date}", message_date);
body = body:gsub("${message_duration}", message_length_formatted);
body = body:gsub("${account}", id);
body = body:gsub("${domain_name}", domain_name);
body = body:gsub(" ", "&nbsp;");
body = body:gsub("%s+", "");
body = body:gsub("&nbsp;", " ");
body = body:gsub("\n", "");
body = body:gsub("\n", "");
body = body:gsub("'", "&#39;");
body = body:gsub([["]], "&#34;");
body = trim(body);
--send the email
if (voicemail_attach_file == "true") then
if (voicemail_local_after_email == "false") then
delete = "true";
else
delete = "false";
end
file = voicemail_dir.."/"..id.."/msg_"..uuid.."."..vm_message_ext;
cmd = "luarun email.lua "..voicemail_mail_to.." "..voicemail_mail_to.." '"..subject.."' '"..body.."' '"..file.."' "..delete;
else
cmd = "luarun email.lua "..voicemail_mail_to.." "..voicemail_mail_to.." '"..subject.."' '"..body.."'";
end
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] cmd: " .. cmd .. "\n");
end
result = api:executeString(cmd);
end
--whether to keep the voicemail message and details local after email
if (voicemail_mail_to) then
if (voicemail_local_after_email == "false" and string.len(voicemail_mail_to) > 0) then
--delete the voicemail message details
sql = [[DELETE FROM v_voicemail_messages
WHERE domain_uuid = ']] .. domain_uuid ..[['
AND voicemail_uuid = ']] .. db_voicemail_uuid ..[['
AND voicemail_message_uuid = ']] .. uuid ..[[']]
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql);
--set message waiting indicator
message_waiting(id, domain_uuid);
--clear the variable
db_voicemail_uuid = '';
end
end
end
@@ -0,0 +1,100 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--include the lua script
scripts_dir = string.sub(debug.getinfo(1).source,2,string.len(debug.getinfo(1).source)-(string.len(argv[0])+1));
dofile(scripts_dir.."/resources/functions/config.lua");
dofile(config());
--define general settings
tmp_file = "/usr/local/freeswitch/log/mwi.tmp";
sleep = 300;
--debug
debug["sql"] = false;
debug["info"] = false;
--connect to the database
dofile(scripts_dir.."/resources/functions/database_handle.lua");
dbh = database_handle('system');
--used to stop the lua service
local file = assert(io.open(tmp_file, "w"));
file:write("remove this file to stop the script");
--add the trim function
function trim(s)
return s:gsub("^%s+", ""):gsub("%s+$", "")
end
--check if a file exists
function file_exists(name)
local f=io.open(name,"r")
if f~=nil then io.close(f) return true else return false end
end
--create the api object
api = freeswitch.API();
--run lua as a service
while true do
--exit the loop when the file does not exist
if (not file_exists(tmp_file)) then
freeswitch.consoleLog("NOTICE", tmp_file.." not found\n");
break;
end
--Send MWI events for voicemail boxes with messages
sql = [[SELECT v.voicemail_id, v.voicemail_uuid, v.domain_uuid, d.domain_name, COUNT(*) AS message_count
FROM v_voicemail_messages as m, v_voicemails as v, v_domains as d
WHERE v.voicemail_uuid = m.voicemail_uuid
AND v.domain_uuid = d.domain_uuid
GROUP BY v.voicemail_id, v.voicemail_uuid, v.domain_uuid, d.domain_name;]];
if (debug["sql"]) then
freeswitch.consoleLog("notice", "[voicemail] SQL: " .. sql .. "\n");
end
status = dbh:query(sql, function(row)
--send the message waiting event
local event = freeswitch.Event("message_waiting");
if (row["message_count"] == "0") then
event:addHeader("MWI-Messages-Waiting", "no");
else
event:addHeader("MWI-Messages-Waiting", "yes");
end
event:addHeader("MWI-Message-Account", "sip:"..row["voicemail_id"].."@"..row["domain_name"]);
event:fire();
--log to console
if (debug["info"]) then
freeswitch.consoleLog("notice", "[voicemail] mailbox: "..row["voicemail_id"].."@"..row["domain_name"].." messages: " .. row["message_count"] .. " \n");
end
end);
--slow the loop down
os.execute("sleep "..sleep);
--testing exit immediately
--break;
end
@@ -0,0 +1,7 @@
<font face="arial">
<b>Message From "${caller_id_name}" <a href="tel:${caller_id_number}">${caller_id_number}</a></b><br/>
<hr noshade="noshade" size="1"/>
Created: ${message_date}<br/>
Duration: ${message_duration}<br/>
Account: ${account}@${domain_name}<br/>
</font>
@@ -0,0 +1 @@
Voicemail from ${caller_id_name} <${caller_id_number}> ${message_duration}
@@ -0,0 +1,7 @@
<font face="arial">
<b>Message From "${caller_id_name}" <a href="tel:${caller_id_number}">${caller_id_number}</a></b><br/>
<hr noshade="noshade" size="1"/>
Created: ${message_date}<br/>
Duration: ${message_duration}<br/>
Account: ${account}@${domain_name}<br/>
</font>
@@ -0,0 +1 @@
Voicemail from ${caller_id_name} <${caller_id_number}> ${message_duration}
@@ -0,0 +1,138 @@
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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;
--general functions
dofile(scripts_dir.."/resources/functions/trim.lua");
dofile(scripts_dir.."/resources/functions/file_exists.lua");
dofile(scripts_dir.."/resources/functions/explode.lua");
--connect to the database
dofile(scripts_dir.."/resources/functions/database_handle.lua");
dbh = database_handle('system');
--exits the script if we didn't connect properly
assert(dbh:connected());
--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 the params in the console
if (debug["params"]) then
if (params:serialize() ~= nil) then
freeswitch.consoleLog("notice", "[xml_handler] Params:\n" .. params:serialize() .. "\n");
end
end
--show the xml request in 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
--get the params and set them as variables
domain_name = params:getHeader("sip_from_host");
if (domain_uuid == nil) then
domain_uuid = params:getHeader("domain_uuid");
end
domain_name = params:getHeader("domain");
if (domain_name == nil) then
domain_name = params:getHeader("domain_name");
end
if (domain_name == nil) then
domain_name = params:getHeader("variable_domain_name");
end
purpose = params:getHeader("purpose");
profile = params:getHeader("profile");
key = params:getHeader("key");
user = params:getHeader("user");
user_context = params:getHeader("variable_user_context");
call_context = params:getHeader("Caller-Context");
destination_number = params:getHeader("Caller-Destination-Number");
caller_id_number = params:getHeader("Caller-Caller-ID-Number");
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
--process the sections
if (XML_REQUEST["section"] == "configuration") then
configuration = scripts_dir.."/app/xml_handler/resources/scripts/configuration/"..XML_REQUEST["key_value"]..".lua";
if (debug["xml_request"]) then
freeswitch.consoleLog("notice", "[xml_handler] " .. configuration .. "\n");
end
if (file_exists(configuration)) then
dofile(configuration);
end
end
if (XML_REQUEST["section"] == "directory") then
dofile(scripts_dir.."/app/xml_handler/resources/scripts/directory/directory.lua");
end
if (XML_REQUEST["section"] == "dialplan") then
dofile(scripts_dir.."/app/xml_handler/resources/scripts/dialplan/dialplan.lua");
end
--close the database connection
dbh:release();
@@ -0,0 +1,103 @@
-- xml_handler.lua
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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 the xml array
local xml = {}
table.insert(xml, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="configuration">]]);
table.insert(xml, [[ <configuration name="conference.conf" description="Audio Conference">]]);
table.insert(xml, [[ <caller-controls>]]);
table.insert(xml, [[ <group name="default">]]);
table.insert(xml, [[ <control action="mute" digits=""/>]]);
table.insert(xml, [[ <control action="deaf mute" digits=""/>]]);
table.insert(xml, [[ <control action="energy up" digits="9"/>]]);
table.insert(xml, [[ <control action="energy equ" digits="8"/>]]);
table.insert(xml, [[ <control action="energy dn" digits="7"/>]]);
table.insert(xml, [[ <control action="vol talk up" digits="3"/>]]);
table.insert(xml, [[ <control action="vol talk zero" digits="2"/>]]);
table.insert(xml, [[ <control action="vol talk dn" digits="1"/>]]);
table.insert(xml, [[ <control action="vol listen up" digits="6"/>]]);
table.insert(xml, [[ <control action="vol listen zero" digits="5"/>]]);
table.insert(xml, [[ <control action="vol listen dn" digits="4"/>]]);
table.insert(xml, [[ <control action="hangup" digits=""/>]]);
table.insert(xml, [[ </group>]]);
table.insert(xml, [[ <group name="moderator">]]);
table.insert(xml, [[ <control action="mute" digits="#"/>]]);
table.insert(xml, [[ <control action="deaf mute" digits=""/>]]);
table.insert(xml, [[ <control action="energy up" digits="9"/>]]);
table.insert(xml, [[ <control action="energy equ" digits="8"/>]]);
table.insert(xml, [[ <control action="energy dn" digits="7"/>]]);
table.insert(xml, [[ <control action="vol talk up" digits="3"/>]]);
table.insert(xml, [[ <control action="vol talk zero" digits="2"/>]]);
table.insert(xml, [[ <control action="vol talk dn" digits="1"/>]]);
table.insert(xml, [[ <control action="vol listen up" digits="6"/>]]);
table.insert(xml, [[ <control action="vol listen zero" digits="5"/>]]);
table.insert(xml, [[ <control action="vol listen dn" digits="4"/>]]);
table.insert(xml, [[ <control action="hangup" digits=""/>]]);
table.insert(xml, [[ </group>]]);
table.insert(xml, [[ </caller-controls>]]);
table.insert(xml, "");
table.insert(xml, [[ <profile name="default">]]);
table.insert(xml, [[ <param name="cdr-log-dir" value="auto"/>]]);
table.insert(xml, [[ <param name="conference-flags" value="wait-mod" />]]);
table.insert(xml, [[ <param name="domain" value="$${domain}"/>]]);
table.insert(xml, [[ <param name="rate" value="16000"/>]]);
table.insert(xml, [[ <param name="interval" value="20"/>]]);
table.insert(xml, [[ <param name="energy-level" value="15"/>]]);
table.insert(xml, [[ <param name="auto-gain-level" value="50"/>]]);
table.insert(xml, [[ <param name="caller-controls" value="default"/>]]);
table.insert(xml, [[ <param name="moderator-controls" value="default"/>]]);
table.insert(xml, [[ <param name="muted-sound" value="conference/conf-muted.wav"/>]]);
table.insert(xml, [[ <param name="unmuted-sound" value="conference/conf-unmuted.wav"/>]]);
table.insert(xml, [[ <param name="alone-sound" value="conference/conf-alone.wav"/>]]);
table.insert(xml, [[ <param name="moh-sound" value="$${hold_music}"/>]]);
table.insert(xml, [[ <param name="enter-sound" value="tone_stream://%(200,0,500,600,700)"/>]]);
table.insert(xml, [[ <param name="exit-sound" value="tone_stream://%(500,0,300,200,100,50,25)"/>]]);
table.insert(xml, [[ <param name="kicked-sound" value="conference/conf-kicked.wav"/>]]);
table.insert(xml, [[ <param name="locked-sound" value="conference/conf-locked.wav"/>]]);
table.insert(xml, [[ <param name="is-locked-sound" value="conference/conf-is-locked.wav"/>]]);
table.insert(xml, [[ <param name="is-unlocked-sound" value="conference/conf-is-unlocked.wav"/>]]);
table.insert(xml, [[ <param name="pin-sound" value="conference/conf-pin.wav"/>]]);
table.insert(xml, [[ <param name="bad-pin-sound" value="conference/conf-bad-pin.wav"/>]]);
table.insert(xml, [[ <param name="caller-id-name" value="$${outbound_caller_name}"/>]]);
table.insert(xml, [[ <param name="caller-id-number" value="$${outbound_caller_id}"/>]]);
table.insert(xml, [[ <param name="comfort-noise" value="true"/>]]);
table.insert(xml, [[ <param name="auto-record" value="/tmp/test.wav"/>]]);
table.insert(xml, [[ </profile>]]);
--set the xml array and then concatenate the array to a string
table.insert(xml, [[ </configuration>]]);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
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
@@ -0,0 +1,265 @@
-- xml_handler.lua
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--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
--set the cache
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, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="configuration">]]);
table.insert(xml, [[ <configuration name="sofia.conf" description="sofia Endpoint">]]);
table.insert(xml, [[ <global_settings>]]);
table.insert(xml, [[ <param name="log-level" value="0"/>]]);
--table.insert(xml, [[ <param name="auto-restart" value="false"/>]]);
table.insert(xml, [[ <param name="debug-presence" value="0"/>]]);
--table.insert(xml, [[ <param name="capture-server" value="udp:homer.domain.com:5060"/>]]);
table.insert(xml, [[ </global_settings>]]);
table.insert(xml, [[ <profiles>]]);
--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, [[ </settings>]]);
table.insert(xml, [[ </profile>]]);
end
table.insert(xml, [[ <profile name="]]..sip_profile_name..[[">]]);
table.insert(xml, [[ <aliases>]]);
table.insert(xml, [[ </aliases>]]);
table.insert(xml, [[ <gateways>]]);
--table.insert(xml, [[ <X-PRE-PROCESS cmd="include" data="]]..sip_profile_name..[[/*.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, [[ <gateway name="]] .. field.domain_name .."-".. gateway .. [[">]]);
else
table.insert(xml, [[ <gateway name="]] .. gateway .. [[">]]);
end
if (string.len(field.username) > 0) then
table.insert(xml, [[ <param name="username" value="]] .. field.username .. [["/>]]);
else
table.insert(xml, [[ <param name="username" value="register:false"/>]]);
end
if (string.len(field.distinct_to) > 0) then
table.insert(xml, [[ <param name="distinct-to" value="]] .. field.distinct_to .. [["/>]]);
end
if (string.len(field.auth_username) > 0) then
table.insert(xml, [[ <param name="auth-username" value="]] .. field.auth_username .. [["/>]]);
end
if (string.len(field.password) > 0) then
table.insert(xml, [[ <param name="password" value="]] .. field.password .. [["/>]]);
else
table.insert(xml, [[ <param name="password" value="register:false"/>]]);
end
if (string.len(field.realm) > 0) then
table.insert(xml, [[ <param name="realm" value="]] .. field.realm .. [["/>]]);
end
if (string.len(field.from_user) > 0) then
table.insert(xml, [[ <param name="from-user" value="]] .. field.from_user .. [["/>]]);
end
if (string.len(field.from_domain) > 0) then
table.insert(xml, [[ <param name="from-domain" value="]] .. field.from_domain .. [["/>]]);
end
if (string.len(field.proxy) > 0) then
table.insert(xml, [[ <param name="proxy" value="]] .. field.proxy .. [["/>]]);
end
if (string.len(field.register_proxy) > 0) then
table.insert(xml, [[ <param name="register-proxy" value="]] .. field.register_proxy .. [["/>]]);
end
if (string.len(field.outbound_proxy) > 0) then
table.insert(xml, [[ <param name="outbound-proxy" value="]] .. field.outbound_proxy .. [["/>]]);
end
if (string.len(field.expire_seconds) > 0) then
table.insert(xml, [[ <param name="expire-seconds" value="]] .. field.expire_seconds .. [["/>]]);
end
if (string.len(field.register) > 0) then
table.insert(xml, [[ <param name="register" value="]] .. field.register .. [["/>]]);
end
if (field.register_transport) then
if (field.register_transport == "udp") then
table.insert(xml, [[ <param name="register-transport" value="udp"/>]]);
elseif (field.register_transport == "tcp") then
table.insert(xml, [[ <param name="register-transport" value="tcp"/>]]);
elseif (field.register_transport == "tls") then
table.insert(xml, [[ <param name="register-transport" value="tls"/>]]);
table.insert(xml, [[ <param name="contact-params" value="transport=tls"/>]]);
else
table.insert(xml, [[ <param name="register-transport" value="udp"/>]]);
end
end
if (string.len(field.retry_seconds) > 0) then
table.insert(xml, [[ <param name="retry-seconds" value="]] .. field.retry_seconds .. [["/>]]);
end
if (string.len(field.extension) > 0) then
table.insert(xml, [[ <param name="extension" value="]] .. field.extension .. [["/>]]);
end
if (string.len(field.ping) > 0) then
table.insert(xml, [[ <param name="ping" value="]] .. field.ping .. [["/>]]);
end
if (string.len(field.context) > 0) then
table.insert(xml, [[ <param name="context" value="]] .. field.context .. [["/>]]);
end
if (string.len(field.caller_id_in_from) > 0) then
table.insert(xml, [[ <param name="caller-id-in-from" value="]] .. field.caller_id_in_from .. [["/>]]);
end
if (string.len(field.supress_cng) > 0) then
table.insert(xml, [[ <param name="supress-cng" value="]] .. field.supress_cng .. [["/>]]);
end
if (string.len(field.sip_cid_type) > 0) then
table.insert(xml, [[ <param name="sip_cid_type" value="]] .. field.sip_cid_type .. [["/>]]);
end
if (string.len(field.extension_in_contact) > 0) then
table.insert(xml, [[ <param name="extension-in-contact" value="]] .. field.extension_in_contact .. [["/>]]);
end
table.insert(xml, [[ </gateway>]]);
end)
table.insert(xml, [[ </gateways>]]);
table.insert(xml, [[ <domains>]]);
table.insert(xml, [[ <domain name="all" alias="false" parse="true"/>]]);
table.insert(xml, [[ </domains>]]);
table.insert(xml, [[ <settings>]]);
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, [[ <param name="]]..sip_profile_setting_name..[[" value="]]..sip_profile_setting_value..[["/>]]);
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, [[ </settings>]]);
table.insert(xml, [[ </profile>]]);
profile_tag_status = "close";
end
table.insert(xml, [[ </profiles>]]);
table.insert(xml, [[ </configuration>]]);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
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("'", "&#39;").."' "..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 &#39 back to a single quote
XML_STRING = XML_STRING:gsub("&#39;", "'");
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] configuration:sofia.conf source: memcache\n");
end
end --if XML_STRING
@@ -0,0 +1,264 @@
-- xml_handler.lua
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--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
--set the cache
if (XML_STRING == "-ERR NOT FOUND") then
--set the xml array and then concatenate the array to a string
local xml = {}
table.insert(xml, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="dialplan" description="">]]);
table.insert(xml, [[ <context name="]] .. call_context .. [[">]]);
--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, [[ </condition>]]);
table.insert(xml, [[ </extension>]]);
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>]]);
condition_tag_status = "closed";
end
end
end
--open the tags
if (dialplan_tag_status == "closed") then
table.insert(xml, [[ <extension name="]] .. dialplan_name .. [[" continue="]] .. dialplan_continue .. [[" uuid="]] .. dialplan_uuid .. [[">]]);
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>]]);
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 field="]] .. dialplan_detail_type .. [[" expression="]] .. dialplan_detail_data .. [["]] .. condition_break;
elseif (condition_type == "time") then
if (condition_attribute) then
condition_attribute = condition_attribute .. dialplan_detail_type .. [[="]] .. dialplan_detail_data .. [[" ]];
else
condition_attribute = dialplan_detail_type .. [[="]] .. dialplan_detail_data .. [[" ]];
end
condition_expression = "";
condition = ""; --prevents a duplicate time condition
else
condition = [[ <condition field="]] .. dialplan_detail_type .. [[" expression="]] .. dialplan_detail_data .. [["]] .. condition_break;
end
condition_tag_status = "open";
end
if (dialplan_detail_tag == "action" or dialplan_detail_tag == "anti-action") then
if (previous_dialplan_detail_tag == "condition") then
--add the condition ending
if (condition_type == "time") then
condition = [[ <condition ]] .. condition_attribute .. condition_break;
condition_attribute = ""; --prevents the condition attribute from being used on every condition
else
if (previous_dialplan_detail_type) then
condition = [[ <condition field="]] .. previous_dialplan_detail_type .. [[" expression="]] .. previous_dialplan_detail_data .. [["]] .. condition_break;
end
end
table.insert(xml, condition .. [[>]]);
condition = ""; --prevents duplicate time conditions
end
end
if (dialplan_detail_tag == "action") then
table.insert(xml, [[ <action application="]] .. dialplan_detail_type .. [[" data="]] .. dialplan_detail_data .. [["]] .. detail_inline .. [[/>]]);
end
if (dialplan_detail_tag == "anti-action") then
table.insert(xml, [[ <anti-action application="]] .. dialplan_detail_type .. [[" data="]] .. dialplan_detail_data .. [["]] .. detail_inline .. [[/>]]);
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, [[ </condition>]]);
table.insert(xml, [[ </extension>]]);
end
--set the xml array and then concatenate the array to a string
table.insert(xml, [[ </context>]]);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
XML_STRING = table.concat(xml, "\n");
--set the cache
tmp = XML_STRING:gsub("\\", "\\\\");
result = trim(api:execute("memcache", "set dialplan:" .. call_context .. " '"..tmp:gsub("'", "&#39;").."' "..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 &#39 back to a single quote
XML_STRING = XML_STRING:gsub("&#39;", "'");
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] dialplan:"..call_context.." source: memcache\n");
end
end
@@ -0,0 +1,124 @@
-- xml_handler.lua
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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.
--get the cache
if (trim(api:execute("module_exists", "mod_memcache")) == "true") then
XML_STRING = trim(api:execute("memcache", "get directory:groups:"..domain_name));
else
XML_STRING = "-ERR NOT FOUND";
end
--set the cache
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, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="directory">]]);
table.insert(xml, [[ <domain name="]] .. domain_name .. [[">]]);
table.insert(xml, [[ <groups>]]);
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, [[ <group name="]]..call_group..[[">]]);
table.insert(xml, [[ <users>]]);
extension_array = explode(",", extension_list);
for index,tmp_extension in pairs(extension_array) do
table.insert(xml, [[ <user id="]]..tmp_extension..[[" type="pointer"/>]]);
end
table.insert(xml, [[ </users>]]);
table.insert(xml, [[ </group>]]);
end
previous_call_group = call_group;
end
end
table.insert(xml, [[ </groups>]]);
table.insert(xml, [[ </domain>]]);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
XML_STRING = table.concat(xml, "\n");
--set the cache
result = trim(api:execute("memcache", "set directory:groups:"..domain_name.." '"..XML_STRING:gsub("'", "&#39;").."' "..expire["directory"]));
--send to the console
if (debug["cache"]) then
freeswitch.consoleLog("notice", "[xml_handler] directory:groups:"..domain_name.." source: database\n");
end
else
--replace the &#39 back to a single quote
XML_STRING = XML_STRING:gsub("&#39;", "'");
--send to the console
if (debug["cache"]) then
if (XML_STRING) then
freeswitch.consoleLog("notice", "[xml_handler] directory:groups:"..domain_name.." source: memcache\n");
end
end
end
--send the xml to the console
if (debug["xml_string"]) then
freeswitch.consoleLog("notice", "[xml_handler] directory:groups:"..domain_name.." XML_STRING: \n" .. XML_STRING .. "\n");
end
@@ -0,0 +1,8 @@
--params
--Event-Calling-Line-Number: 102
--Event-Sequence: 4173
--action: message-count
--key: id
--user: *98
--domain: example.com
@@ -0,0 +1,379 @@
-- xml_handler.lua
-- Part of FusionPBX
-- Copyright (C) 2013 Mark J Crane <markjcrane@fusionpbx.com>
-- 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 the default
continue = true;
--get the action
action = params:getHeader("action");
purpose = params:getHeader("purpose");
--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 (purpose == "gateways") then
if (params:getHeader("profile") == "internal") then
--process when the sip profile is rescanned or sofia is reloaded
local xml = {}
table.insert(xml, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="directory">]]);
sql = "SELECT * FROM v_domains ";
dbh:query(sql, function(row)
table.insert(xml, [[ <domain name="]]..row.domain_name..[[" />]]);
end);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
XML_STRING = table.concat(xml, "\n");
end
elseif (action == "message-count") then
dofile(scripts_dir.."/app/xml_handler/resources/scripts/directory/action/message-count.lua");
elseif (action == "group_call") then
dofile(scripts_dir.."/app/xml_handler/resources/scripts/directory/action/group_call.lua");
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
if (domain_name) then
XML_STRING = trim(api:execute("memcache", "get directory:" .. user .. "@" .. domain_name));
end
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
--prevent processing for invalid domains
if (domain_uuid == nil) 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;
call_timeout = row.call_timeout;
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 .. ",leg_timeout=" .. call_timeout .. ",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;
--call_timeout = row.call_timeout;
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, [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>]]);
table.insert(xml, [[<document type="freeswitch/xml">]]);
table.insert(xml, [[ <section name="directory">]]);
table.insert(xml, [[ <domain name="]] .. domain_name .. [[" alias="true">]]);
table.insert(xml, [[ <groups>]]);
table.insert(xml, [[ <group name="default">]]);
table.insert(xml, [[ <users>]]);
if (number_alias) then
if (cidr) then
table.insert(xml, [[ <user id="]] .. extension .. [["]] .. cidr .. number_alias .. [[ type=>]]);
else
table.insert(xml, [[ <user id="]] .. extension .. [["]] .. number_alias .. [[>]]);
end
else
if (cidr) then
table.insert(xml, [[ <user id="]] .. extension .. [["]] .. cidr .. [[>]]);
else
table.insert(xml, [[ <user id="]] .. extension .. [[">]]);
end
end
table.insert(xml, [[ <params>]]);
table.insert(xml, [[ <param name="password" value="]] .. password .. [["/>]]);
table.insert(xml, [[ <param name="vm-enabled" value="]] .. vm_enabled .. [["/>]]);
if (string.len(vm_mailto) > 0) then
table.insert(xml, [[ <param name="vm-password" value="]] .. vm_password .. [["/>]]);
table.insert(xml, [[ <param name="vm-email-all-messages" value="]] .. vm_enabled ..[["/>]]);
table.insert(xml, [[ <param name="vm-attach-file" value="]] .. vm_attach_file .. [["/>]]);
table.insert(xml, [[ <param name="vm-keep-local-after-email" value="]] .. vm_keep_local_after_email .. [["/>]]);
table.insert(xml, [[ <param name="vm-mailto" value="]] .. vm_mailto .. [["/>]]);
end
if (string.len(mwi_account) > 0) then
table.insert(xml, [[ <param name="MWI-Account" value="]] .. mwi_account .. [["/>]]);
end
if (string.len(auth_acl) > 0) then
table.insert(xml, [[ <param name="auth-acl" value="]] .. auth_acl .. [["/>]]);
end
table.insert(xml, [[ <param name="dial-string" value="]] .. dial_string .. [["/>]]);
table.insert(xml, [[ </params>]]);
table.insert(xml, [[ <variables>]]);
table.insert(xml, [[ <variable name="domain_uuid" value="]] .. domain_uuid .. [["/>]]);
table.insert(xml, [[ <variable name="domain_name" value="]] .. domain_name .. [["/>]]);
table.insert(xml, [[ <variable name="extension_uuid" value="]] .. extension_uuid .. [["/>]]);
--table.insert(xml, [[ <variable name="call_timeout" value="]] .. call_timeout .. [["/>]]);
table.insert(xml, [[ <variable name="caller_id_name" value="]] .. sip_from_user .. [["/>]]);
table.insert(xml, [[ <variable name="caller_id_number" value="]] .. sip_from_user .. [["/>]]);
if (string.len(call_group) > 0) then
table.insert(xml, [[ <variable name="call_group" value="]] .. call_group .. [["/>]]);
end
if (string.len(hold_music) > 0) then
table.insert(xml, [[ <variable name="hold_music" value="]] .. hold_music .. [["/>]]);
end
if (string.len(toll_allow) > 0) then
table.insert(xml, [[ <variable name="toll_allow" value="]] .. toll_allow .. [["/>]]);
end
if (string.len(accountcode) > 0) then
table.insert(xml, [[ <variable name="accountcode" value="]] .. accountcode .. [["/>]]);
end
table.insert(xml, [[ <variable name="user_context" value="]] .. user_context .. [["/>]]);
if (string.len(effective_caller_id_name) > 0) then
table.insert(xml, [[ <variable name="effective_caller_id_name" value="]] .. effective_caller_id_name.. [["/>]]);
end
if (string.len(effective_caller_id_number) > 0) then
table.insert(xml, [[ <variable name="effective_caller_id_number" value="]] .. effective_caller_id_number.. [["/>]]);
end
if (string.len(outbound_caller_id_name) > 0) then
table.insert(xml, [[ <variable name="outbound_caller_id_name" value="]] .. outbound_caller_id_name .. [["/>]]);
end
if (string.len(outbound_caller_id_number) > 0) then
table.insert(xml, [[ <variable name="outbound_caller_id_number" value="]] .. outbound_caller_id_number .. [["/>]]);
end
if (string.len(emergency_caller_id_number) > 0) then
table.insert(xml, [[ <variable name="emergency_caller_id_number" value="]] .. emergency_caller_id_number .. [["/>]]);
end
if (string.len(directory_full_name) > 0) then
table.insert(xml, [[ <variable name="directory_full_name" value="]] .. directory_full_name .. [["/>]]);
end
if (string.len(directory_visible) > 0) then
table.insert(xml, [[ <variable name="directory-visible" value="]] .. directory_visible .. [["/>]]);
end
if (string.len(directory_exten_visible) > 0) then
table.insert(xml, [[ <variable name="directory-exten-visible" value="]] .. directory_exten_visible .. [["/>]]);
end
if (string.len(limit_max) > 0) then
table.insert(xml, [[ <variable name="limit_max" value="]] .. limit_max .. [["/>]]);
else
table.insert(xml, [[ <variable name="limit_max" value="5"/>]]);
end
if (string.len(limit_destination) > 0) then
table.insert(xml, [[ <variable name="limit_destination" value="]] .. limit_destination .. [["/>]]);
end
if (string.len(sip_force_contact) > 0) then
table.insert(xml, [[ <variable name="sip_force_contact" value="]] .. sip_force_contact .. [["/>]]);
end
if (string.len(sip_force_expires) > 0) then
table.insert(xml, [[ <variable name="sip-force-expires" value="]] .. sip_force_expires .. [["/>]]);
end
if (string.len(nibble_account) > 0) then
table.insert(xml, [[ <variable name="nibble_account" value="]] .. nibble_account .. [["/>]]);
end
if (sip_bypass_media == "bypass-media") then
table.insert(xml, [[ <variable name="bypass_media" value="true"/>]]);
end
if (sip_bypass_media == "bypass-media-after-bridge") then
table.insert(xml, [[ <variable name="bypass_media_after_bridge" value="true"/>]]);
end
if (sip_bypass_media == "proxy-media") then
table.insert(xml, [[ <variable name="proxy_media" value="true"/>]]);
end
table.insert(xml, [[ <variable name="record_stereo" value="true"/>]]);
table.insert(xml, [[ <variable name="transfer_fallback_extension" value="operator"/>]]);
table.insert(xml, [[ <variable name="export_vars" value="domain_name"/>]]);
table.insert(xml, [[ </variables>]]);
table.insert(xml, [[ </user>]]);
table.insert(xml, [[ </users>]]);
table.insert(xml, [[ </group>]]);
table.insert(xml, [[ </groups>]]);
table.insert(xml, [[ </domain>]]);
table.insert(xml, [[ </section>]]);
table.insert(xml, [[</document>]]);
XML_STRING = table.concat(xml, "\n");
--set the cache
if (user and domain_name) then
result = trim(api:execute("memcache", "set directory:" .. user .. "@" .. domain_name .. " '"..XML_STRING:gsub("'", "&#39;").."' "..expire["directory"]));
end
--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 &#39 back to a single quote
if (XML_STRING) then
XML_STRING = XML_STRING:gsub("&#39;", "'");
end
--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 = [[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="freeswitch/xml">
<section name="result">
<result status="not found" />
</section>
</document>]];
--set the cache
if (user and domain_name) then
result = trim(api:execute("memcache", "set directory:" .. user .. "@" .. domain_name .. " '"..XML_STRING:gsub("'", "&#39;").."' "..expire["directory"]));
end
end
--send the xml to the console
if (debug["xml_string"]) then
freeswitch.consoleLog("notice", "[xml_handler] XML_STRING: \n" .. XML_STRING .. "\n");
end