summaryrefslogtreecommitdiff
path: root/misc/freeswitch
diff options
context:
space:
mode:
Diffstat (limited to 'misc/freeswitch')
-rw-r--r--misc/freeswitch/conf/freeswitch.xml174
-rw-r--r--misc/freeswitch/scripts/common/array.lua13
-rw-r--r--misc/freeswitch/scripts/common/call_forwarding.lua35
-rw-r--r--misc/freeswitch/scripts/common/configuration_table.lua59
-rw-r--r--misc/freeswitch/scripts/common/gateway.lua7
-rw-r--r--misc/freeswitch/scripts/common/intruder.lua2
-rw-r--r--misc/freeswitch/scripts/common/log.lua2
-rw-r--r--misc/freeswitch/scripts/common/pager.lua75
-rw-r--r--misc/freeswitch/scripts/common/perimeter.lua38
-rw-r--r--misc/freeswitch/scripts/configuration.lua103
-rw-r--r--misc/freeswitch/scripts/dialplan/callback.lua2
-rw-r--r--misc/freeswitch/scripts/dialplan/dialplan.lua33
-rw-r--r--misc/freeswitch/scripts/dialplan/functions.lua82
-rw-r--r--misc/freeswitch/scripts/dialplan/hunt_group.lua1
-rw-r--r--misc/freeswitch/scripts/dialplan/ivr.lua83
-rw-r--r--misc/freeswitch/scripts/dialplan/phone_book.lua29
-rw-r--r--misc/freeswitch/scripts/dialplan/router.lua116
-rw-r--r--misc/freeswitch/scripts/dialplan/session.lua11
-rw-r--r--misc/freeswitch/scripts/dialplan/sip_call.lua75
-rw-r--r--misc/freeswitch/scripts/dialplan/voicemail.lua506
-rw-r--r--misc/freeswitch/scripts/dialplan_default.lua9
-rw-r--r--misc/freeswitch/scripts/event/presence_update.lua9
-rw-r--r--misc/freeswitch/scripts/test_route.lua2
23 files changed, 1217 insertions, 249 deletions
diff --git a/misc/freeswitch/conf/freeswitch.xml b/misc/freeswitch/conf/freeswitch.xml
index 3405bc9..0a6538e 100644
--- a/misc/freeswitch/conf/freeswitch.xml
+++ b/misc/freeswitch/conf/freeswitch.xml
@@ -74,6 +74,70 @@
</match>
</input>
</macro>
+ <macro name="voicemail_message_play">
+ <input pattern="^(\d+):(\d+):(.+)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-received.wav"/>
+ <action function="say" data="$2" method="pronounced" type="short_date_time"/>
+ <action function="play-file" data="$3"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_no_messages">
+ <input pattern="^(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-no_more_messages.wav"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_message_menu_new">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-play_previous_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-delete_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-save_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-play_next_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_message_menu_saved">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-play_previous_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-delete_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-play_next_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
<macro name="voicemail_menu">
<input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
<match>
@@ -96,27 +160,19 @@
</input>
</macro>
<macro name="voicemail_config_menu">
- <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
<match>
<action function="play-file" data="voicemail/vm-to_record_greeting.wav"/>
<action function="play-file" data="voicemail/vm-press.wav"/>
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
<action function="execute" data="sleep(100)"/>
- <action function="play-file" data="voicemail/vm-choose_greeting.wav"/>
- <action function="play-file" data="voicemail/vm-press.wav"/>
- <action function="say" data="$2" method="pronounced" type="name_spelled"/>
- <action function="execute" data="sleep(100)"/>
- <action function="play-file" data="voicemail/vm-record_name2.wav"/>
- <action function="play-file" data="voicemail/vm-press.wav"/>
- <action function="say" data="$3" method="pronounced" type="name_spelled"/>
- <action function="execute" data="sleep(100)"/>
<action function="play-file" data="voicemail/vm-change_password.wav"/>
<action function="play-file" data="voicemail/vm-press.wav"/>
- <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
<action function="execute" data="sleep(100)"/>
<action function="play-file" data="voicemail/vm-main_menu.wav"/>
<action function="play-file" data="voicemail/vm-press.wav"/>
- <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
</match>
</input>
</macro>
@@ -250,6 +306,14 @@
<input pattern="^(.*)$">
<match>
<action function="play-file" data="voicemail/vm-record_message.wav"/>
+ <action function="play-file" data="tone_stream://%(1000,0,500)"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_message_too_short">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-too-small.wav"/>
</match>
</input>
</macro>
@@ -600,6 +664,70 @@
</match>
</input>
</macro>
+ <macro name="voicemail_message_play">
+ <input pattern="^(\d+):(\d+):(.+)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-message_number.wav"/>
+ <action function="say" data="$1" method="pronounced" type="items"/>
+ <action function="play-file" data="voicemail/vm-received.wav"/>
+ <action function="say" data="$2" method="pronounced" type="short_date_time"/>
+ <action function="play-file" data="$3"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_no_messages">
+ <input pattern="^(.*)$" break_on_match="true">
+ <match>
+ <action function="play-file" data="voicemail/vm-no_more_messages.wav"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_message_menu_new">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-play_previous_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-delete_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-save_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-play_next_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$5" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
+ <macro name="voicemail_message_menu_saved">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <match>
+ <action function="play-file" data="voicemail/vm-play_previous_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$1" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-delete_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-play_next_message.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
+ <action function="execute" data="sleep(100)"/>
+ <action function="play-file" data="voicemail/vm-main_menu.wav"/>
+ <action function="play-file" data="voicemail/vm-press.wav"/>
+ <action function="say" data="$4" method="pronounced" type="name_phonetic"/>
+ </match>
+ </input>
+ </macro>
<macro name="voicemail_menu">
<input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
<match>
@@ -622,27 +750,19 @@
</input>
</macro>
<macro name="voicemail_config_menu">
- <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+ <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
<match>
<action function="play-file" data="voicemail/vm-to_record_greeting.wav"/>
<action function="play-file" data="voicemail/vm-press.wav"/>
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
<action function="execute" data="sleep(100)"/>
- <action function="play-file" data="voicemail/vm-choose_greeting.wav"/>
- <action function="play-file" data="voicemail/vm-press.wav"/>
- <action function="say" data="$2" method="pronounced" type="name_spelled"/>
- <action function="execute" data="sleep(100)"/>
- <action function="play-file" data="voicemail/vm-record_name2.wav"/>
- <action function="play-file" data="voicemail/vm-press.wav"/>
- <action function="say" data="$3" method="pronounced" type="name_spelled"/>
- <action function="execute" data="sleep(100)"/>
<action function="play-file" data="voicemail/vm-change_password.wav"/>
<action function="play-file" data="voicemail/vm-press.wav"/>
- <action function="say" data="$4" method="pronounced" type="name_spelled"/>
+ <action function="say" data="$2" method="pronounced" type="name_spelled"/>
<action function="execute" data="sleep(100)"/>
<action function="play-file" data="voicemail/vm-main_menu.wav"/>
<action function="play-file" data="voicemail/vm-press.wav"/>
- <action function="say" data="$5" method="pronounced" type="name_spelled"/>
+ <action function="say" data="$3" method="pronounced" type="name_spelled"/>
</match>
</input>
</macro>
@@ -779,6 +899,13 @@
</match>
</input>
</macro>
+ <macro name="voicemail_message_too_short">
+ <input pattern="^(.*)$">
+ <match>
+ <action function="play-file" data="voicemail/vm-too-small.wav"/>
+ </match>
+ </input>
+ </macro>
<macro name="voicemail_greeting_selected">
<input pattern="^(\d+)$">
<match>
@@ -1234,6 +1361,9 @@
<load module="mod_spandsp"/>
<load module="mod_snmp"/>
<load module="mod_dingaling"/>
+ <!-- Uncomment the following line after running
+ 'gs-addon install fs-g729' -->
+ <!--<load module="mod_com_g729"/>-->
</modules>
</configuration>
<configuration name="lua.conf" description="LUA Configuration">
diff --git a/misc/freeswitch/scripts/common/array.lua b/misc/freeswitch/scripts/common/array.lua
index b1b7a71..c3cabec 100644
--- a/misc/freeswitch/scripts/common/array.lua
+++ b/misc/freeswitch/scripts/common/array.lua
@@ -4,6 +4,8 @@
module(...,package.seeall)
+MAX_JSON_DEPTH = 100;
+
function try(array, arguments)
if type(arguments) ~= 'string' or type(array) ~= 'table' then
return nil;
@@ -79,12 +81,19 @@ function keys_to_s(array, separator, prefix, suffix)
end
-- convert to JSON
-function to_json(array)
+function to_json(array, max_depth)
+ max_depth = tonumber(max_depth) or MAX_JSON_DEPTH;
+ max_depth = max_depth - 1;
+
+ if max_depth <= 0 then
+ return 'null';
+ end
+
require 'common.str';
local buffer = '{';
for key, value in pairs(array) do
if type(value) == 'table' then
- buffer = buffer .. '"' .. key .. '":' .. to_json(value) .. ',';
+ buffer = buffer .. '"' .. key .. '":' .. to_json(value, max_depth) .. ',';
else
buffer = buffer .. '"' .. key .. '":' .. common.str.to_json(value) .. ',';
end
diff --git a/misc/freeswitch/scripts/common/call_forwarding.lua b/misc/freeswitch/scripts/common/call_forwarding.lua
index 3429dc9..9556de3 100644
--- a/misc/freeswitch/scripts/common/call_forwarding.lua
+++ b/misc/freeswitch/scripts/common/call_forwarding.lua
@@ -17,6 +17,7 @@ function CallForwarding.new(self, arg, object)
self.record = arg.record;
self.domain = arg.domain;
self.parent = arg.parent;
+ self.caller = arg.caller;
return object;
end
@@ -70,12 +71,38 @@ function CallForwarding.list_by_owner(self, call_forwardable_id, call_forwardabl
else
local sources = common.str.strip_to_a(forwarding_entry.source, ',')
for source_index=1, #sources do
- for caller_id_index=1, #caller_ids do
- if caller_ids[caller_id_index]:match(sources[source_index]) then
- entry_match = true;
- self.log:debug('CALL_FORWARDING - source match: ', sources[source_index], ' ~ ', caller_ids[caller_id_index] );
+ local variable_name, pattern = common.str.partition(sources[source_index], '!=')
+ local invert = variable_name ~= nil;
+ if not variable_name then
+ variable_name, pattern = common.str.partition(sources[source_index], '=')
+ end
+ if variable_name and self.caller then
+ local search_string = tostring(common.array.try(self.caller, variable_name));
+ local success, result = pcall(string.find, pattern, '^%d+-%d+$');
+
+ if success and result and tonumber(search_string) then
+ local min, max = common.str.partition(pattern, '-')
+ entry_match = tonumber(search_string) >= tonumber(min) and tonumber(search_string) <= tonumber(max);
+ else
+ local success, result = pcall(string.find, search_string, pattern);
+ entry_match = common.str.to_b(result);
+ end
+ if invert then
+ entry_match = not entry_match;
+ end
+
+ if entry_match == false then
break;
end
+ self.log:debug('CALL_FORWARDING ', forwarding_entry.service, ' - element match: ', entry_match, ', variable: ', variable_name, ' = ', pattern, ' ~ ', search_string);
+ else
+ for caller_id_index=1, #caller_ids do
+ if caller_ids[caller_id_index]:match(sources[source_index]) then
+ entry_match = true;
+ self.log:debug('CALL_FORWARDING ', forwarding_entry.service, ' - source match: ', sources[source_index], ' ~ ', caller_ids[caller_id_index] );
+ break;
+ end
+ end
end
end
end
diff --git a/misc/freeswitch/scripts/common/configuration_table.lua b/misc/freeswitch/scripts/common/configuration_table.lua
index 85bc014..1b8d8b7 100644
--- a/misc/freeswitch/scripts/common/configuration_table.lua
+++ b/misc/freeswitch/scripts/common/configuration_table.lua
@@ -4,12 +4,43 @@
module(...,package.seeall)
+
+function cast(variable_type, value, default)
+ require 'common.str';
+
+ if variable_type == 'boolean' then
+ return common.str.to_b(value);
+ elseif variable_type == 'integer' then
+ if default and not tonumber(value) then
+ return default;
+ end
+ return common.str.to_i(value);
+ elseif variable_type == 'float' then
+ if default and not tonumber(value) then
+ return default;
+ end
+ return common.str.to_n(value);
+ elseif variable_type == 'string' then
+ if default and not value then
+ return default;
+ end
+ return common.str.to_s(value);
+ elseif variable_type == 'array' then
+ if default and not value then
+ return default;
+ end
+ return common.str.to_a(value, ',');
+ end
+end
+
-- retrieve configuration from database
-function get(database, entity, section)
+function get(database, entity, section, defaults)
if not database or not entity then
return {};
end
+ defaults = defaults or {};
+
require 'common.str'
local sql_query = 'SELECT * FROM `gs_parameters` WHERE `entity` = "' .. entity .. '"';
@@ -17,7 +48,7 @@ function get(database, entity, section)
sql_query = sql_query .. ' AND `section` = "' .. section .. '"';
end
- local root = {}
+ local root = defaults[1] or {}
local parameter_class = '';
database:query(sql_query, function(parameters)
@@ -26,21 +57,27 @@ function get(database, entity, section)
local p_name = common.str.strip(parameters.name);
if not root[p_section] then
- root[p_section] = {};
+ root[p_section] = defaults[p_section] or {};
end
- if p_class_type == 'boolean' then
- root[p_section][p_name] = common.str.to_b(parameters.value);
- elseif p_class_type == 'integer' then
- root[p_section][p_name] = common.str.to_i(parameters.value);
- else
- root[p_section][p_name] = tostring(parameters.value);
- end
+ root[p_section][p_name] = cast(p_class_type, parameters.value);
end)
if section then
- return root[section];
+ return root[section] or defaults[section];
end
return root;
end
+
+
+function settings(database, table_name, key, value, defaults)
+ local sql_query = 'SELECT * FROM ' .. database:escape(table_name, '`') .. ' WHERE ' .. database:escape(key, '`') .. ' = ' .. database:escape(value, '"');
+
+ local settings_entries = defaults or {};
+ database:query(sql_query, function(record)
+ settings_entries[record.name] = cast(record.class_type:lower(), record.value, settings_entries[record.name]);
+ end);
+
+ return settings_entries;
+end
diff --git a/misc/freeswitch/scripts/common/gateway.lua b/misc/freeswitch/scripts/common/gateway.lua
index ac38326..09e8c4b 100644
--- a/misc/freeswitch/scripts/common/gateway.lua
+++ b/misc/freeswitch/scripts/common/gateway.lua
@@ -34,6 +34,10 @@ end
function Gateway.find_by_id(self, id)
+ if not tonumber(id) then
+ return nil;
+ end
+
local sql_query = 'SELECT `a`.*, `c`.`sip_host` AS `domain`, `c`.`contact` AS `contact_full`, `c`.`network_ip`, `c`.`network_port` \
FROM `gateways` `a` \
LEFT JOIN `gateway_settings` `b` ON `a`.`id` = `b`.`gateway_id` AND `b`.`name` = "inbound_username" \
@@ -57,6 +61,9 @@ function Gateway.find_by_id(self, id)
if gateway then
gateway.settings = self:config_table_get('gateway_settings', gateway.id);
+ if common.str.blank(gateway.domain) then
+ gateway.domain = gateway.settings.domain;
+ end
end
return gateway;
diff --git a/misc/freeswitch/scripts/common/intruder.lua b/misc/freeswitch/scripts/common/intruder.lua
index 7d12155..f5e7a41 100644
--- a/misc/freeswitch/scripts/common/intruder.lua
+++ b/misc/freeswitch/scripts/common/intruder.lua
@@ -35,7 +35,7 @@ function Intruder.update_blacklist(self, event)
contacts_per_second_max = event.contacts_per_second_max,
user_agent = event.user_agent,
to_user = event.to_user,
- comment = 'Permimeter',
+ comment = 'Perimeter',
created_at = {'NOW()', raw = true },
updated_at = {'NOW()', raw = true },
};
diff --git a/misc/freeswitch/scripts/common/log.lua b/misc/freeswitch/scripts/common/log.lua
index b9893ac..7201d2a 100644
--- a/misc/freeswitch/scripts/common/log.lua
+++ b/misc/freeswitch/scripts/common/log.lua
@@ -38,7 +38,7 @@ function Log.message(self, log_level, message_arguments )
if type(index) == 'number' then
if type(value) == 'table' then
require 'common.array';
- message = message .. common.array.to_json(value);
+ message = message .. common.array.to_json(value, 3);
else
message = message .. tostring(value);
end
diff --git a/misc/freeswitch/scripts/common/pager.lua b/misc/freeswitch/scripts/common/pager.lua
new file mode 100644
index 0000000..d9440f4
--- /dev/null
+++ b/misc/freeswitch/scripts/common/pager.lua
@@ -0,0 +1,75 @@
+-- Gemeinschaft 5 module: pager class
+-- (c) AMOOMA GmbH 2013
+--
+
+module(...,package.seeall)
+
+Pager = {}
+
+function Pager.new(self, arg)
+ arg = arg or {}
+ pager = arg.pager or {}
+ setmetatable(pager, self);
+ self.__index = self;
+ self.class = 'pager';
+ self.log = arg.log;
+ self.database = arg.database;
+ self.caller = arg.caller;
+
+ return pager;
+end
+
+
+function Pager.find_by_id(self, id)
+ local sql_query = 'SELECT * FROM `pager_groups` WHERE `id`= '.. tonumber(id) .. ' LIMIT 1';
+ local pager = nil;
+
+ self.database:query(sql_query, function(entry)
+ pager = Pager:new(self);
+ pager.record = entry;
+ pager.id = tonumber(entry.id);
+ pager.uuid = entry.uuid;
+ pager.identifier = 'pager' .. entry.id;
+ end)
+
+ return pager;
+end
+
+
+function Pager.enter(self)
+ local flags = 'mute';
+
+ if not self.caller.account or self.caller.account.class ~= 'sipaccount' then
+ return false;
+ end
+
+ if tonumber(self.record.sip_account_id) == tonumber(self.caller.account.id) then
+ flags = 'moderator|endconf';
+ end
+
+ self:callback();
+
+ local result = self.caller:execute('conference', self.identifier .. "@profile_" .. self.identifier .. "++flags{" .. flags .. "}");
+ self.caller:hangup('NORMAL_CLEARING');
+ self:callback();
+end
+
+
+function Pager.callback(self)
+ local destination = {
+ pager_group_id = self.id,
+ state = 'terminated',
+ }
+
+ if self.caller.account and self.caller.account.class == 'sipaccount' then
+ destination.sip_account_id = self.caller.account.id;
+ end
+
+ if self.caller and self.caller:ready() then
+ destination.state = 'active';
+ end
+
+ local command = 'http_request.lua ' .. self.caller.uuid .. ' ' .. common.array.expand_variables(self.record.callback_url, destination, self.caller);
+ require 'common.fapi';
+ return common.fapi.FApi:new():execute('luarun', command);
+end
diff --git a/misc/freeswitch/scripts/common/perimeter.lua b/misc/freeswitch/scripts/common/perimeter.lua
index d3b601c..fcef97c 100644
--- a/misc/freeswitch/scripts/common/perimeter.lua
+++ b/misc/freeswitch/scripts/common/perimeter.lua
@@ -47,6 +47,7 @@ function Perimeter.setup(self, event)
self.ban_tries = 1;
self.checks = { register = {}, call = {} };
self.bad_headers = { register = {}, call = {} };
+ self.serial = freeswitch.getGlobalVariable('switch_serial');
if config and config.general then
for key, value in pairs(config.general) do
@@ -56,8 +57,14 @@ function Perimeter.setup(self, event)
self.checks.register = config.checks_register or {};
self.checks.call = config.checks_call or {};
- self.bad_headers.register = config.bad_headers_register;
- self.bad_headers.call = config.bad_headers_call;
+
+ for header, patterns in pairs(config.bad_headers_register) do
+ self.bad_headers.register[header] = common.str.strip_to_a(patterns, ',');
+ end
+
+ for header, patterns in pairs(config.bad_headers_call) do
+ self.bad_headers.call[header] = common.str.strip_to_a(patterns, ',');
+ end
self.log:info('[perimeter] PERIMETER - setup perimeter defense');
end
@@ -93,6 +100,7 @@ function Perimeter.record_update(self, event)
event.record.span_start = event.span_start or event.record.span_start;
event.record.span_contact_count = (event.span_contact_count or event.record.span_contact_count) + 1;
event.record.users = event.users or event.record.users;
+ event.record.updated = event.updated or event.record.updated;
end
@@ -144,6 +152,7 @@ function Perimeter.check(self, event)
end
self:execute_ban(event);
event.ban_time = os.time();
+ event.banned = true;
end
event.record.banned = event.record.banned + 1;
@@ -205,12 +214,14 @@ end
function Perimeter.check_bad_headers(self, event)
local points = nil;
- for name, pattern in pairs(self.bad_headers[event.action]) do
- pattern = common.array.expand_variables(pattern, event);
- local success, result = pcall(string.find, event[name], pattern);
- if success and result then
- self.log:debug('[', event.key, '/', event.sequence, '] PERIMETER_BAD_HEADERS - ', name, '=', event[name], ' ~= ', pattern);
- points = (points or 0) + 1;
+ for name, patterns in pairs(self.bad_headers[event.action]) do
+ for index, pattern in ipairs(patterns) do
+ pattern = common.array.expand_variables(pattern, event);
+ local success, result = pcall(string.find, event[name], pattern);
+ if success and result then
+ self.log:debug('[', event.key, '/', event.sequence, '] PERIMETER_BAD_HEADERS - ', name, '=', event[name], ' ~= ', pattern);
+ points = (points or 0) + 1;
+ end
end
end
@@ -247,6 +258,17 @@ end
function Perimeter.update_intruder(self, event)
require 'common.intruder';
local result = common.intruder.Intruder:new{ log = self.log, database = self.database }:update_blacklist(event);
+
+ if not common.str.blank(self.report_url) and (not event.record.updated or event.banned) then
+ event.serial = common.fapi.FApi:new():execute('md5', self.serial);
+ event.blacklisted = tostring(common.str.to_b(event.banned));
+ local command = 'http_request.lua perimeter ' .. common.array.expand_variables(self.report_url, event);
+ require 'common.fapi'
+ common.fapi.FApi:new():execute('luarun', command);
+ self.log:devel(command);
+ end
+
+ event.updated = common.str.to_i(event.updated) + 1;
end
diff --git a/misc/freeswitch/scripts/configuration.lua b/misc/freeswitch/scripts/configuration.lua
index 88ef452..2486565 100644
--- a/misc/freeswitch/scripts/configuration.lua
+++ b/misc/freeswitch/scripts/configuration.lua
@@ -194,8 +194,8 @@ function conf_conference(database)
local conf_name = params:getHeader('conf_name');
local profile_name = params:getHeader('profile_name');
- if conf_name then
- require 'common.conference'
+ if conf_name:find('^conference%d+') then
+ require 'common.conference';
conference = common.conference.Conference:new{log=log, database=database}:find_by_id(common.str.to_i(conf_name));
if conference then
log:debug('CONFIG_CONFERENCE ', conf_name, ' name: ', conference.record.name, ', profile: ', profile_name);
@@ -211,6 +211,27 @@ function conf_conference(database)
else
log:error('CONFIG_CONFERENCE ', conf_name, ' - conference not found');
end
+ elseif conf_name:find('^pager%d+') then
+ local parameters = {
+ ['moh-sound'] = '',
+ ['caller-controls'] = "speaker",
+ ['moderator-controls'] = "moderator",
+ ['comfort-noise'] = false,
+ }
+
+ require 'common.pager';
+ pager = common.pager.Pager:new{log=log, database=database}:find_by_id(common.str.to_i(conf_name));
+ if pager then
+ log:debug('CONFIG_PAGER ', conf_name, ', profile: ', profile_name);
+ profiles = xml:element{
+ 'profiles',
+ xml:element{
+ 'profile',
+ name = profile_name,
+ xml:from_hash('param', parameters, 'name', 'value'),
+ },
+ };
+ end
else
log:notice('CONFIG_CONFERENCE - no conference name');
end
@@ -289,8 +310,9 @@ function conf_event_socket(database)
require 'configuration.simple_xml'
local xml = configuration.simple_xml.SimpleXml:new();
+ local settings = { password = 'ClueCon', ['listen-ip'] = '127.0.0.1', ['listen-port'] = '8021' };
require 'common.configuration_table';
- local settings = common.configuration_table.get(database, 'event_socket', 'settings');
+ settings = common.configuration_table.get(database, 'event_socket', 'settings', {settings = settings});
XML_STRING = xml:element{
'document',
@@ -412,7 +434,27 @@ function directory_sip_account(database)
local user_xml = nil;
- if not common.str.blank(auth_name) then
+ if tostring(purpose) == 'publish-vm' and not common.str.blank(auth_name) then
+ require 'dialplan.voicemail';
+ local voicemail_account = dialplan.voicemail.Voicemail:new{ log = log, database = database }:find_by_name(auth_name);
+ if voicemail_account then
+ log:debug('DIRECTORY_VOICEMAIL_ACCOUNT - ', voicemail_account.class, '=',voicemail_account.id, '/', voicemail_account.uuid, '|', voicemail_account.name);
+ user_xml = xml:element{
+ 'groups',
+ xml:element{
+ 'group',
+ name = 'default',
+ xml:element{
+ 'users',
+ xml:element{
+ 'user',
+ id = voicemail_account.record.name,
+ },
+ },
+ },
+ };
+ end
+ elseif not common.str.blank(auth_name) then
require 'common.sip_account'
local sip_account = common.sip_account.SipAccount:new{ log = log, database = database}:find_by_auth_name(auth_name);
@@ -435,46 +477,19 @@ function directory_sip_account(database)
gs_account_owner_id = sip_account.record.sip_accountable_id
}
- if tostring(purpose) == 'publish-vm' then
- log:debug('DIRECTORY_SIP_ACCOUNT - purpose: VoiceMail, auth_name: ', sip_account.record.auth_name, ', caller_name: ', sip_account.record.caller_name, ', domain: ', domain);
- user_xml = xml:element{
- 'groups',
- xml:element{
- 'group',
- name = 'default',
- xml:element{
- 'users',
- xml:element{
- 'user',
- id = sip_account.record.auth_name,
- xml:element{
- 'params',
- xml:from_hash('param', user_parameters, 'name', 'value'),
- },
- xml:element{
- 'variables',
- xml:from_hash('variable', user_variables, 'name', 'value'),
- },
- },
- },
- },
- };
- else
- log:debug('DIRECTORY_SIP_ACCOUNT - auth_name: ', sip_account.record.auth_name, ', caller_name: ', sip_account.record.caller_name, ', domain: ', domain);
-
- user_xml = xml:element{
- 'user',
- id = sip_account.record.auth_name,
- xml:element{
- 'params',
- xml:from_hash('param', user_parameters, 'name', 'value'),
- },
- xml:element{
- 'variables',
- xml:from_hash('variable', user_variables, 'name', 'value'),
- },
- };
- end
+ log:debug('DIRECTORY_SIP_ACCOUNT - auth_name: ', sip_account.record.auth_name, ', caller_name: ', sip_account.record.caller_name, ', domain: ', domain);
+ user_xml = xml:element{
+ 'user',
+ id = sip_account.record.auth_name,
+ xml:element{
+ 'params',
+ xml:from_hash('param', user_parameters, 'name', 'value'),
+ },
+ xml:element{
+ 'variables',
+ xml:from_hash('variable', user_variables, 'name', 'value'),
+ },
+ };
else
require 'common.gateway'
local sip_gateway = common.gateway.Gateway:new{ log = log, database = database }:find_by_auth_name(auth_name);
diff --git a/misc/freeswitch/scripts/dialplan/callback.lua b/misc/freeswitch/scripts/dialplan/callback.lua
index f6fd48e..ce1068c 100644
--- a/misc/freeswitch/scripts/dialplan/callback.lua
+++ b/misc/freeswitch/scripts/dialplan/callback.lua
@@ -46,7 +46,7 @@ end
function Callback.callback_unset(self, class, identifier, callback_session)
- local session_record = self.sessions[tostring(callback_session or self.session)];
+ local session_record = self.sessions[common.str.to_i(callback_session or self.session)];
if not session_record then
return false;
end
diff --git a/misc/freeswitch/scripts/dialplan/dialplan.lua b/misc/freeswitch/scripts/dialplan/dialplan.lua
index 5335328..913d7a5 100644
--- a/misc/freeswitch/scripts/dialplan/dialplan.lua
+++ b/misc/freeswitch/scripts/dialplan/dialplan.lua
@@ -248,7 +248,7 @@ function Dialplan.destination_new(self, arg)
destination.account = self:object_find{ class = destination.type, id = destination.id};
if self.caller then
require 'common.call_forwarding';
- local call_forwarding_class = common.call_forwarding.CallForwarding:new{ log = self.log, database = self.database }
+ local call_forwarding_class = common.call_forwarding.CallForwarding:new{ log = self.log, database = self.database, caller = self.caller }
destination.call_forwarding = call_forwarding_class:list_by_owner(destination.id, destination.type, self.caller.caller_phone_numbers);
for service, call_forwarding_entry in pairs(call_forwarding_class:list_by_owner(destination.phone_number.id, destination.phone_number.class, self.caller.caller_phone_numbers)) do
destination.call_forwarding[service] = call_forwarding_entry;
@@ -370,9 +370,6 @@ function Dialplan.dial(self, destination)
end
end
self.caller:set_caller_id(destination.caller_id_number, destination.caller_id_name or self.caller.caller_id_name);
- else
- self.caller:set_caller_id('anonymous', 'Unknown');
- self.caller:set_privacy(true);
end
local destinations = { destination };
@@ -417,8 +414,7 @@ function Dialplan.huntgroup(self, destination)
self:set_caller_picture(self.caller.account.owner.id, self.caller.account.owner.class);
end
else
- self.caller:set_caller_id('anonymous', tostring(hunt_group.record.name));
- self.caller:set_privacy(true);
+ self.caller.anonymous_name = tostring(hunt_group.record.name);
end
self.caller.auth_account = hunt_group;
@@ -448,8 +444,7 @@ function Dialplan.acd(self, destination)
self:set_caller_picture(self.caller.account.owner.id, self.caller.account.owner.class);
end
else
- self.caller:set_caller_id('anonymous', tostring(acd.record.name));
- self.caller:set_privacy(true);
+ self.caller.anonymous_name = tostring(acd.record.name);
end
self.caller.auth_account = acd;
@@ -590,23 +585,23 @@ end
function Dialplan.voicemail(self, destination)
- require 'dialplan.voicemail'
+ require 'dialplan.voicemail';
local voicemail_account = nil;
-
- local sip_account_id
- if not common.str.blank(destination.number) and false then
- voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_number(destination.number);
+ if common.str.to_i(destination.id) > 0 then
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database, domain = self.domain }:find_by_id(destination.id);
elseif self.caller.auth_account and self.caller.auth_account.class == 'sipaccount' then
- voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_sip_account_id(self.caller.auth_account.id);
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database, domain = self.domain }:find_by_sip_account_id(self.caller.auth_account.id);
+ elseif self.caller.forwarding_number then
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database, domain = self.domain }:find_by_number(self.caller.forwarding_number);
end
if not voicemail_account then
- self.log:error('VOICEMAIL - no mailbox');
+ self.log:error('VOICEMAIL - mailbox not found, ');
return { continue = false, code = 404, phrase = 'Mailbox not found' }
end
- voicemail_account:leave(self.caller, self.caller.forwarding_number);
+ voicemail_account:leave(self.caller, destination.number, self.caller.forwarding_number);
if self.caller:to_s("voicemail_message_len") == '' then
self.log:info('VOICEMAIL - no message saved');
@@ -709,7 +704,7 @@ function Dialplan.switch(self, destination)
end
end
return result;
- elseif destination.type == 'voicemail' then
+ elseif destination.type == 'voicemail' or destination.type == 'voicemailaccount' then
return self:voicemail(destination);
elseif destination.type == 'dialplanfunction' then
return self:dialplanfunction(destination);
@@ -817,6 +812,10 @@ function Dialplan.run(self, destination)
self.caller.date = os.date('%y%m%d%w');
self.caller.time = os.date('%H%M%S');
+ if self.config then
+ self.caller:export_variable('sip_cid_type=' .. (self.config.sip_cid_type or 'none'));
+ end
+
if type(self.config.variables) == 'table' then
for key, value in pairs(self.config.variables) do
self.caller:set_variable(key, value);
diff --git a/misc/freeswitch/scripts/dialplan/functions.lua b/misc/freeswitch/scripts/dialplan/functions.lua
index a47d9d3..efd1f05 100644
--- a/misc/freeswitch/scripts/dialplan/functions.lua
+++ b/misc/freeswitch/scripts/dialplan/functions.lua
@@ -102,6 +102,8 @@ function Functions.dialplan_function(self, caller, dialed_number)
result = self:voicemail_message_leave(caller, parameters[3]);
elseif fid == "vmcheck" then
result = self:voicemail_check(caller, parameters[3]);
+ elseif fid == "vmplay" then
+ result = self:voicemail_play(caller, tostring(parameters[3]) .. '-' .. tostring(parameters[4]) .. '-' .. tostring(parameters[5]) .. '-' .. tostring(parameters[6]) .. '-' .. tostring(parameters[7]));
elseif fid == "vmtg" then
result = self:call_forwarding_toggle(caller, nil, parameters[3]);
elseif fid == "acdmtg" then
@@ -114,6 +116,10 @@ function Functions.dialplan_function(self, caller, dialed_number)
result = self:call_parking_inout(caller, parameters[3], parameters[4]);
elseif fid == "cpai" then
result = self:call_parking_inout_index(caller, parameters[3]);
+ elseif fid == "test" then
+ result = self:test(caller, parameters[3]);
+ elseif fid == "pager" then
+ result = self:pager(caller, parameters[3]);
end
return result or { continue = false, code = 505, phrase = 'Error executing function', no_cdr = true };
@@ -756,26 +762,35 @@ function Functions.voicemail_message_leave(self, caller, phone_number)
end
-function Functions.voicemail_check(self, caller, phone_number)
+function Functions.voicemail_check(self, caller, number)
+ require 'dialplan.voicemail';
local voicemail_account = nil;
local voicemail_authorized = false;
-
- require 'dialplan.voicemail'
-
- if phone_number then
- voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_number(phone_number);
- else
- if caller.auth_account_type == 'SipAccount' then
- voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_sip_account_id(caller.auth_account.id);
- voicemail_authorized = true;
- end
+
+ if number then
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_number(number);
+ elseif caller.auth_account and tostring(caller.auth_account.class):lower() == 'sipaccount' then
+ voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_sip_account_id(caller.auth_account.id);
+ voicemail_authorized = true;
end
if not voicemail_account then
+ self.log:notice('FUNCTION_VOICEMAIL_CHECK - mailbox not found');
return { continue = false, code = 404, phrase = 'Mailbox not found', no_cdr = true }
end
- voicemail_account:menu(caller, voicemail_authorized);
+ return voicemail_account:menu_main(caller, voicemail_authorized);
+end
+
+
+function Functions.voicemail_play(self, caller, uuid)
+ require 'dialplan.voicemail';
+
+ local voicemail_account = dialplan.voicemail.Voicemail:new{ log = self.log, database = self.database }:find_by_sip_account_id(caller.auth_account.id);
+
+ if voicemail_account then
+ local message = voicemail_account:message_play(caller, uuid);
+ end
return { continue = false, code = 200, phrase = 'OK', no_cdr = true }
end
@@ -901,3 +916,46 @@ function Functions.call_parking_inout_index(self, caller, stall_index)
return { continue = false, code = 200, phrase = 'OK', no_cdr = true }
end
+
+
+function Functions.test(self, caller, name)
+ if tostring(name) == 'dtmf' then
+ self.log:info('FUNCTION_TEST_DTMF');
+ local digits = '';
+ caller:answer();
+ while caller:ready() do
+ if digits == '' then
+ caller:playback('ivr/ivr-love_those_touch_tones.wav');
+ end
+ digits = caller.session:read(1, 1, '', 5000, '');
+ self.log:info('DTMF: ', digits);
+ caller:send_display('DTMF: ', digits);
+ if digits == '*' then
+ caller:playback('digits/star.wav');
+ elseif digits == '#' then
+ caller:playback('digits/pound.wav');
+ elseif digits ~= '' then
+ caller.session:say(digits, "en", "number", "pronounced");
+ end
+ end
+ end
+
+ return { continue = false, code = 200, phrase = 'OK', no_cdr = true }
+end
+
+
+function Functions.pager(self, caller, pager_group_id)
+ require 'common.pager';
+ local pager = common.pager.Pager:new{ log = self.log, database = self.database, caller = caller }:find_by_id(pager_group_id);
+
+ if not pager then
+ self.log:notice('FUNCTION_PAGER not found - pager_group=', pager_group_id);
+ return { continue = false, code = 404, phrase = 'No such pager group', no_cdr = true }
+ end
+
+ self.log:info('FUNCTION_PAGER pager_group=', pager_group_id);
+ caller:answer();
+ pager:enter();
+
+ return { continue = false, code = 200, phrase = 'OK', no_cdr = true }
+end
diff --git a/misc/freeswitch/scripts/dialplan/hunt_group.lua b/misc/freeswitch/scripts/dialplan/hunt_group.lua
index b1728c3..e87c6b2 100644
--- a/misc/freeswitch/scripts/dialplan/hunt_group.lua
+++ b/misc/freeswitch/scripts/dialplan/hunt_group.lua
@@ -108,7 +108,6 @@ function HuntGroup.run(self, dialplan_object, caller, destination)
table.insert(caller.caller_id_numbers, number);
end
- self.log:debug('HUNTGROUP ', self.record.id, ' - auth: ', caller.auth_account.class, '=', caller.auth_account.id, '/', caller.auth_account.uuid, ', caller: ', caller.account.class, '=', caller.account.id, '/', caller.account.uuid);
self.log:info('HUNTGROUP ', self.record.id, ' - clir: ', caller.clir, ', caller_id_numbers: ', table.concat(caller.caller_id_numbers, ','));
local hunt_group_destination = caller.destination;
diff --git a/misc/freeswitch/scripts/dialplan/ivr.lua b/misc/freeswitch/scripts/dialplan/ivr.lua
index f8b8a2d..6b230dc 100644
--- a/misc/freeswitch/scripts/dialplan/ivr.lua
+++ b/misc/freeswitch/scripts/dialplan/ivr.lua
@@ -21,29 +21,51 @@ function Ivr.new(self, arg)
end
-function Ivr.ivr_phrase(self, phrase, keys, timeout, ivr_repeat)
+function Ivr.ivr_break(self)
+ return self.exit or not self.caller:ready();
+end
+
+
+function Ivr.ivr_phrase(self, phrase, keys, timeout, ivr_repeat, phrase_data)
ivr_repeat = ivr_repeat or 3;
timeout = timeout or 30;
self.digit = '';
self.exit = false;
self.break_keys = {};
+ local query_keys = {};
+
for index=1, #keys do
- self.break_keys[keys[index]] = true;
+ if type(keys[index]) == 'table' then
+ if tostring(keys[index].key) ~= '' then
+ table.insert(query_keys, keys[index].key);
+ end
+ self.break_keys[keys[index].key] = keys[index];
+ else
+ if tostring(keys[index]) ~= '' then
+ table.insert(query_keys, keys[index]);
+ end
+ self.break_keys[keys[index]] = true;
+ end
end
global_callback:callback('dtmf', 'ivr_ivr_phrase', self.ivr_phrase_dtmf, self);
-
+ local continue = true;
for index=0, ivr_repeat do
- self.caller.session:sayPhrase(phrase, table.concat(keys, ':'));
- self.caller:sleep(timeout * 1000);
- if self.exit then
+ continue = self:ivr_break() or self.caller.session:sayPhrase(phrase, phrase_data or table.concat(query_keys, ':'));
+ continue = self:ivr_break() or self.caller:sleep(timeout * 1000);
+
+ if self:ivr_break() then
break;
end
end
global_callback:callback_unset('dtmf', 'ivr_ivr_phrase');
+ if type(self.break_keys[self.digit]) == 'table' then
+ return self.digit, self.break_keys[self.digit];
+ end
+
return self.digit;
end
@@ -57,17 +79,17 @@ function Ivr.ivr_phrase_dtmf(self, dtmf)
end
-function Ivr.read_phrase(self, phrase, phrase_data, max_keys, min_keys, timeout, enter_key)
+function Ivr.read_phrase(self, phrase, phrase_data, max_keys, min_keys, timeout, key_terminator)
self.max_keys = max_keys or 64;
self.min_keys = min_keys or 1;
- self.enter_key = enter_key or '#';
+ self.key_terminator = key_terminator or '#';
self.digits = '';
self.exit = false;
timeout = timeout or 30;
global_callback:callback('dtmf', 'ivr_read_phrase', self.read_phrase_dtmf, self);
- self.caller.session:sayPhrase(phrase, phrase_data or enter_key or '');
- self.caller:sleep(timeout * 1000);
+ local continue = self:ivr_break() or self.caller.session:sayPhrase(phrase, phrase_data or key_terminator or '');
+ continue = self:ivr_break() or self.caller:sleep(timeout * 1000);
global_callback:callback_unset('dtmf', 'ivr_read_phrase');
return self.digits;
@@ -79,7 +101,7 @@ function Ivr.read_phrase_dtmf(self, dtmf)
return nil;
end
- if self.enter_key == dtmf.digit then
+ if self.key_terminator == dtmf.digit then
self.exit = true;
return false;
end
@@ -88,32 +110,63 @@ function Ivr.read_phrase_dtmf(self, dtmf)
end
-function Ivr.check_pin(self, phrase, pin, pin_timeout, pin_repeat, key_enter)
+function Ivr.check_pin(self, phrase_enter, phrase_incorrect, pin, pin_timeout, pin_repeat, key_terminator)
if not pin then
return nil;
end
+ self.exit = false;
pin_timeout = pin_timeout or 30;
pin_repeat = pin_repeat or 3;
- key_enter = key_enter or '#';
+ key_terminator = key_terminator or '#';
local digits = '';
for i = 1, pin_repeat do
if digits == pin then
self.caller:send_display('PIN: OK');
- break
+ break;
elseif digits ~= "" then
self.caller:send_display('PIN: wrong');
+ self.caller.session:sayPhrase(phrase_incorrect, '');
+ elseif self:ivr_break() then
+ break;
end
self.caller:send_display('Enter PIN');
- digits = ivr:read_phrase(phrase, nil, 0, pin:len() + 1, pin_timeout, key_enter);
+ digits = ivr:read_phrase(phrase_enter, nil, 0, pin:len() + 1, pin_timeout, key_terminator);
end
if digits ~= pin then
self.caller:send_display('PIN: wrong');
+ self.caller.session:sayPhrase(phrase_incorrect, '');
return false
end
self.caller:send_display('PIN: OK');
return true;
end
+
+
+function Ivr.record(self, file_name, beep, phrase_record, phrase_too_short, record_length_max, record_length_min, record_repeat, silence_level, silence_lenght_abort)
+ local duration = nil;
+ for index=1, record_repeat do
+ if (duration and duration >= record_length_min) or not self.caller:ready() then
+ break;
+ elseif duration then
+ self.caller:send_display('Recording too short');
+ if phrase_too_short then
+ self.caller.session:sayPhrase(phrase_too_short);
+ end
+ end
+ if phrase_record then
+ self.caller.session:sayPhrase(phrase_record);
+ end
+ if beep then
+ self.caller:playback(beep);
+ end
+ self.caller:send_display('Recording...');
+ local result = self.caller.session:recordFile(file_name, record_length_max, silence_level, silence_lenght_abort);
+ duration = self.caller:to_i('record_seconds');
+ end
+
+ return duration or 0;
+end
diff --git a/misc/freeswitch/scripts/dialplan/phone_book.lua b/misc/freeswitch/scripts/dialplan/phone_book.lua
index 6653789..50972ac 100644
--- a/misc/freeswitch/scripts/dialplan/phone_book.lua
+++ b/misc/freeswitch/scripts/dialplan/phone_book.lua
@@ -19,7 +19,7 @@ function PhoneBook.new(self, arg)
end
-function PhoneBook.find_entry_by_number_user_tenant(self, numbers, user_id, tenant_id)
+function PhoneBook.find_entry_by_number_user_tenant(self, numbers, user_id, tenant_id, name)
user_id = tonumber(user_id) or 0;
tenant_id = tonumber(tenant_id) or 0;
@@ -37,7 +37,7 @@ function PhoneBook.find_entry_by_number_user_tenant(self, numbers, user_id, tena
`c`.`name` AS `phone_book_name`, \
`d`.`bellcore_id` \
FROM `phone_numbers` `a` \
- JOIN `phone_book_entries` `b` ON `a`.`phone_numberable_id` = `b`.`id` AND `a`.`phone_numberable_type` = "PhoneBookENtry" \
+ JOIN `phone_book_entries` `b` ON `a`.`phone_numberable_id` = `b`.`id` AND `a`.`phone_numberable_type` = "PhoneBookEntry" \
JOIN `phone_books` `c` ON `b`.`phone_book_id` = `c`.`id` \
LEFT JOIN `ringtones` `d` ON `a`.`id` = `d`.`ringtoneable_id` AND `d`.`ringtoneable_type` = "PhoneNumber" \
WHERE ((`c`.`phone_bookable_type` = "User" AND `c`.`phone_bookable_id` = ' .. user_id .. ') \
@@ -45,8 +45,13 @@ function PhoneBook.find_entry_by_number_user_tenant(self, numbers, user_id, tena
AND `a`.`number` IN (' .. numbers_sql .. ') \
AND `a`.`state` = "active" \
AND `b`.`state` = "active" \
- AND `c`.`state` = "active" \
- ORDER BY `c`.`phone_bookable_type` DESC LIMIT 1';
+ AND `c`.`state` = "active"';
+
+ if name then
+ sql_query = sql_query ..' AND `a`.`name` = ' .. self.database:escape(name, '"');
+ end
+
+ sql_query = sql_query ..' ORDER BY `c`.`phone_bookable_type` DESC LIMIT 1';
local phone_book_entry = nil;
@@ -61,3 +66,19 @@ function PhoneBook.find_entry_by_number_user_tenant(self, numbers, user_id, tena
return phone_book_entry;
end
+
+
+function PhoneBook.numbers(self, phone_book_entry_id, name, not_name)
+ local sql_query = 'SELECT * FROM `phone_numbers` \
+ WHERE `phone_numberable_id` = ' .. phone_book_entry_id .. ' AND `phone_numberable_type` = "PhoneBookEntry"';
+
+ if name then
+ sql_query = sql_query ..' AND `name` = ' .. self.database:escape(name, '"');
+ end
+
+ if not_name then
+ sql_query = sql_query ..' AND `name` != ' .. self.database:escape(not_name, '"');
+ end
+
+ return self.database:query_return_all(sql_query);
+end
diff --git a/misc/freeswitch/scripts/dialplan/router.lua b/misc/freeswitch/scripts/dialplan/router.lua
index 322c748..20b833b 100644
--- a/misc/freeswitch/scripts/dialplan/router.lua
+++ b/misc/freeswitch/scripts/dialplan/router.lua
@@ -22,6 +22,7 @@ function Router.new(self, arg)
self.variables = arg.variables or {};
self.log_details = arg.log_details;
self.routing_tables = {};
+
return object;
end
@@ -38,15 +39,19 @@ function Router.read_table(self, table_name, force_reload)
JOIN `route_elements` `b` ON `a`.`id` = `b`.`call_route_id`\
WHERE `a`.`routing_table` = "' .. table_name .. '" \
ORDER BY `a`.`position`, `b`.`position`';
-
- local last_id = 0;
+
+ local call_routes = {};
+
self.database:query(sql_query, function(route)
- if last_id ~= tonumber(route.call_route_id) then
- last_id = tonumber(route.call_route_id);
- table.insert(routing_table, {id = route.call_route_id, name = route.name, endpoint_type = route.endpoint_type , endpoint_id = route.endpoint_id, elements = {} });
+ if call_routes[route.call_route_id] then
+ call_route = call_routes[route.call_route_id];
+ else
+ call_route = {id = route.call_route_id, name = route.name, endpoint_type = route.endpoint_type , endpoint_id = route.endpoint_id, elements = {} };
+ call_routes[route.call_route_id] = call_route;
+ table.insert(routing_table, call_route);
end
- table.insert(routing_table[#routing_table].elements, {
+ table.insert(call_route.elements, {
var_in = route.var_in,
var_out = route.var_out,
pattern = route.pattern,
@@ -71,8 +76,9 @@ function Router.element_match(self, pattern, search_string, replacement, route_v
local replace_by = common.array.expand_variables(replacement, route_variables, self.variables)
result = search_string:gsub(pattern, replace_by);
if self.log_details then
- self.log:debug('ELEMENT_MATCH - ', search_string, ' ~= ', pattern, ' => ', replacement, ' => ', replace_by);
+ self.log:debug('ELEMENT_MATCH - ', search_string, ' ~= ', pattern, ' => ', replacement, ' => ', result);
end
+
return true, result;
end
@@ -105,6 +111,37 @@ function Router.element_match_group(self, pattern, groups, replacement, use_key,
end
+function Router.element_run_function(self, variable_name, element, destination)
+ local result = nil;
+ local replacement = nil;
+
+ if self['fun_' .. variable_name] then
+ local arguments = {};
+ for index, argument in ipairs(common.str.to_a(element.replacement, ',')) do
+ table.insert(arguments, common.array.expand_variables(argument, destination, self.variables));
+ end
+ result, replacement = self['fun_' .. variable_name](self, unpack(arguments))
+ if not common.str.blank(element.pattern) then
+ if self.log_details then
+ self.log:debug('ELEMENT_FUNCTION - function: ', variable_name, '(', table.concat(arguments, ', '), ') => ', replacement);
+ end
+ result, replacement = self:element_match(tostring(element.pattern), tostring(replacement), tostring(replacement));
+ end
+ if self.log_details then
+ if result then
+ self.log:debug('ELEMENT_MATCH - function: ', variable_name, '(', table.concat(arguments, ', '), ') => ', replacement);
+ else
+ self.log:debug('ELEMENT_NO_MATCH - function: ', variable_name, '(', table.concat(arguments, ', '), ') => ', tostring(replacement));
+ end
+ end
+ else
+ self.log:error('ELEMENT_FUNCTION - function not found: ', 'fun_' .. variable_name);
+ end
+
+ return result, replacement;
+end
+
+
function Router.route_match(self, route)
local destination = {
gateway = 'gateway' .. route.endpoint_id,
@@ -128,7 +165,7 @@ function Router.route_match(self, route)
end
if element.action ~= 'none' then
- if common.str.blank(element.var_in) or common.str.blank(element.pattern) and element.action == 'set' then
+ if common.str.blank(element.var_in) and element.action == 'set' then
result = true;
replacement = common.array.expand_variables(element.replacement, destination, self.variables);
else
@@ -149,11 +186,16 @@ function Router.route_match(self, route)
elseif command == 'hdr' then
local search_string = self.caller:to_s('sip_h_' .. variable_name);
result, replacement = self:element_match(tostring(element.pattern), search_string, tostring(element.replacement));
+ elseif command == 'fun' then
+ result, replacement = self:element_run_function(variable_name, element, destination);
end
end
if element.action == 'not_match' then
result = not result;
+ if result then
+ replacement = tostring(element.replacement);
+ end
end
if not result then
@@ -214,3 +256,61 @@ function Router.route_run(self, table_name, find_first)
return routes;
end
end
+
+
+function Router.fun_speeddial(self, number, name)
+ local owner_class = common.array.try(self, 'caller.auth_account.owner.class');
+ local owner_id = common.array.try(self, 'caller.auth_account.owner.id')
+
+ local user_id = nil;
+ local tenant_id = nil;
+
+ if tostring(owner_class) == 'user' then
+ user_id = owner_id;
+ tenant_id = common.array.try(self, 'caller.auth_account.owner.record.current_tenant_id');
+ elseif
+ tostring(owner_class) == 'tenant' then
+ tenant_id = owner_id;
+ end
+
+ require 'dialplan.phone_book'
+ local phone_book_class = dialplan.phone_book.PhoneBook:new{ log = self.log, database = self.database }
+ local phone_book_entry = phone_book_class:find_entry_by_number_user_tenant({number}, user_id, tenant_id, 'speeddial');
+
+ self.log:debug('SPEEDDIAL - user=', user_id, ', tenant=', tenant_id, ', entry: "', common.array.try(phone_book_entry, 'phone_book_name'), '" => "', common.array.try(phone_book_entry, 'caller_id_name'), '"');
+
+ if phone_book_entry then
+ local phone_numbers = phone_book_class:numbers(phone_book_entry.id, name, 'speeddial');
+ for index, phone_number in ipairs(phone_numbers) do
+ self.log:info('SPEEDDIAL - ', number, ' => ', phone_number.number)
+ return true, phone_number.number;
+ end
+ end
+end
+
+
+function Router.fun_expression(self, expression_str)
+ if common.str.blank(expression_str) then
+ self.log:error('EXPRESSION - no expression specified');
+ return false;
+ end
+
+ expression_str = expression_str:gsub('[^%d%.%+%(%)%^%%%*%/-<>=!|&]', '');
+ expression_str = expression_str:gsub('&&', ' and ');
+ expression_str = expression_str:gsub('||', ' or ');
+ expression_str = expression_str:gsub('!=', '~=');
+
+ local expression = loadstring("return (" .. expression_str .. ")")
+
+ if not expression then
+ self.log:error('EXPRESSION - invalid expression: ', expression_str);
+ return false;
+ end
+
+ result = expression();
+ if result then
+ return true, result;
+ else
+ return false, result;
+ end
+end
diff --git a/misc/freeswitch/scripts/dialplan/session.lua b/misc/freeswitch/scripts/dialplan/session.lua
index 9c43e74..1d907c5 100644
--- a/misc/freeswitch/scripts/dialplan/session.lua
+++ b/misc/freeswitch/scripts/dialplan/session.lua
@@ -211,17 +211,6 @@ function Session.set_callee_id(self, number, name)
end
end
--- Set caller Privacy header
-function Session.set_privacy(self, privacy)
- if privacy then
- self.session:setVariable('cid_type', 'none');
- self.session:setVariable('sip_h_Privacy', 'id');
- else
- self.session:setVariable('cid_type', 'none');
- self.session:setVariable('sip_h_Privacy', 'none');
- end
-end
-
function Session.set_auth_account(self, auth_account)
if auth_account then
diff --git a/misc/freeswitch/scripts/dialplan/sip_call.lua b/misc/freeswitch/scripts/dialplan/sip_call.lua
index 0cde601..1966a41 100644
--- a/misc/freeswitch/scripts/dialplan/sip_call.lua
+++ b/misc/freeswitch/scripts/dialplan/sip_call.lua
@@ -90,7 +90,7 @@ function SipCall.fork(self, destinations, arg )
for index, destination in ipairs(destinations) do
local origination_variables = { 'gs_fork_index=' .. index }
- self.log:info('FORK ', index, '/', #destinations, ' - ', destination.type, '=', destination.id, '/', destination.uuid, '@', destination.node_id, ', number: ', destination.number, ', caller_id: "', destination.caller_id_name, '" <', destination.caller_id_number, '>');
+ self.log:info('FORK ', index, '/', #destinations, ' - ', destination.type, '=', destination.id, '/', destination.uuid, '@', destination.node_id, ', number: ', destination.number);
if not common.str.to_b(arg.update_callee_display) then
table.insert(origination_variables, 'ignore_display_updates=true');
@@ -112,6 +112,7 @@ function SipCall.fork(self, destinations, arg )
table.insert(origination_variables, 'sip_h_X-GS_auth_account_type=' .. tostring(self.caller.auth_account_type));
table.insert(origination_variables, 'sip_h_X-GS_auth_account_uuid=' .. tostring(self.caller.auth_account_uuid));
table.insert(origination_variables, 'sip_h_X-GS_loop_count=' .. tostring(self.caller.loop_count));
+ table.insert(origination_variables, 'sip_h_X-GS_clir=' .. tostring(self.caller.clir));
table.insert(dial_strings, '[' .. table.concat(origination_variables , ',') .. ']sofia/gateway/' .. node.record.name .. '/' .. destination.number);
end
elseif destination.type == 'sipaccount' then
@@ -124,6 +125,8 @@ function SipCall.fork(self, destinations, arg )
else
local call_waiting = self:call_waiting_busy(sip_account);
if not call_waiting then
+ local caller_id_number = destination.caller_id_number or self.caller.caller_id_number;
+ local caller_id_name = destination.caller_id_name or self.caller.caller_id_name;
destinations[index].numbers = sip_account:phone_numbers();
if not arg.callee_id_name then
@@ -146,6 +149,16 @@ function SipCall.fork(self, destinations, arg )
table.insert(origination_variables, "gs_auth_account_uuid='" .. common.str.to_s(self.caller.auth_account.uuid) .. "'");
end
+ if self.caller.clir then
+ caller_id_number = self.caller.anonymous_number or 'anonymous';
+ caller_id_name = self.caller.anonymous_name or 'Anonymous';
+ table.insert(origination_variables, "origination_caller_id_number='" .. caller_id_number .. "'");
+ table.insert(origination_variables, "origination_caller_id_name='" .. caller_id_name .. "'");
+ table.insert(origination_variables, "sip_h_Privacy='id'");
+ end
+
+ self.log:info('FORK ', index, '/', #destinations, ' - caller_id: "', caller_id_name, '" <', caller_id_number, '>, privacy: ', self.caller.clir);
+
table.insert(dial_strings, '[' .. table.concat(origination_variables , ',') .. ']sofia/' .. sip_account.record.profile_name .. '/' .. sip_account.record.auth_name .. '%' .. sip_account.record.sip_host);
if destination.pickup_groups and #destination.pickup_groups > 0 then
for key=1, #destination.pickup_groups do
@@ -162,12 +175,50 @@ function SipCall.fork(self, destinations, arg )
local gateway = common.gateway.Gateway:new{ log = self.log, database = self.database}:find_by_id(destination.id);
if gateway and gateway.outbound then
- if destination.caller_id_number then
- table.insert(origination_variables, "origination_caller_id_number='" .. destination.caller_id_number .. "'");
- end
- if destination.caller_id_name then
- table.insert(origination_variables, "origination_caller_id_name='" .. destination.caller_id_name .. "'");
+ local asserted_identity = tostring(gateway.settings.asserted_identity);
+ local asserted_identity_clir = tostring(gateway.settings.asserted_identity);
+ local caller_id_number = destination.caller_id_number or self.caller.caller_id_number;
+ local caller_id_name = destination.caller_id_name or self.caller.caller_id_name;
+ local from_uri = common.array.expand_variables(gateway.settings.from or '', destination, self.caller, { gateway = gateway });
+
+ if gateway.settings.asserted_identity then
+ local identity = common.array.expand_variables(gateway.settings.asserted_identity or '', destination, self.caller, { gateway = gateway })
+
+ if self.caller.clir then
+ caller_id_number = self.caller.anonymous_number or 'anonymous';
+ caller_id_name = self.caller.anonymous_name or 'Anonymous';
+ from_uri = common.array.expand_variables(gateway.settings.from_clir or '', destination, self.caller, { gateway = gateway }) or from_uri;
+ identity = common.array.expand_variables(gateway.settings.asserted_identity_clir or '', destination, self.caller, { gateway = gateway }) or identity;
+ table.insert(origination_variables, "origination_caller_id_number='" .. caller_id_number .. "'");
+ table.insert(origination_variables, "origination_caller_id_name='" .. caller_id_name .. "'");
+ table.insert(origination_variables, "sip_h_Privacy='id'");
+ else
+ if destination.caller_id_number then
+ table.insert(origination_variables, "origination_caller_id_number='" .. destination.caller_id_number .. "'");
+ end
+ if destination.caller_id_name then
+ table.insert(origination_variables, "origination_caller_id_name='" .. destination.caller_id_name .. "'");
+ end
+ end
+
+ if from_uri then
+ table.insert(origination_variables, "sip_from_uri='" .. from_uri .. "'");
+ end
+
+ if identity then
+ table.insert(origination_variables, "sip_h_P-Asserted-Identity='" .. identity .. "'");
+ end
+
+ self.log:info('FORK ', index, '/', #destinations, ' - from: ', from_uri, ', identity: ', identity, ', privacy: ', self.caller.clir);
+ else
+ if destination.caller_id_number then
+ table.insert(origination_variables, "origination_caller_id_number='" .. destination.caller_id_number .. "'");
+ end
+ if destination.caller_id_name then
+ table.insert(origination_variables, "origination_caller_id_name='" .. destination.caller_id_name .. "'");
+ end
end
+
if destination.channel_variables then
for key, value in pairs(destination.channel_variables) do
table.insert(origination_variables, tostring(key) .. "='" .. tostring(value) .. "'");
@@ -233,11 +284,19 @@ function SipCall.fork(self, destinations, arg )
end
if arg.detect_dtmf_after_bridge_caller and self.caller.auth_account then
- session:execute('start_dtmf');
+ if not string.match(self.caller:to_s('switch_r_sdp'), '101 telephone%-event') then
+ self.log:notice('FORK A_LEG inband dtmf detection - channel_uuid: ', session:get_uuid());
+ session:execute('start_dtmf');
+ end
end
+
if arg.detect_dtmf_after_bridge_callee and destination.type == 'sipaccount' then
- session_callee:execute('start_dtmf');
+ if not string.match(tostring(session_callee:getVariable('switch_r_sdp')), '101 telephone%-event') then
+ self.log:notice('FORK B_LEG inband dtmf detection - channel_uuid: ', session_callee:get_uuid());
+ session_callee:execute('start_dtmf');
+ end
end
+
if arg.bypass_media_network then
local callee_uuid = session_callee:get_uuid();
diff --git a/misc/freeswitch/scripts/dialplan/voicemail.lua b/misc/freeswitch/scripts/dialplan/voicemail.lua
index caeeb48..3358d2b 100644
--- a/misc/freeswitch/scripts/dialplan/voicemail.lua
+++ b/misc/freeswitch/scripts/dialplan/voicemail.lua
@@ -1,17 +1,41 @@
-- Gemeinschaft 5 module: voicemail class
--- (c) AMOOMA GmbH 2012-2013
+-- (c) AMOOMA GmbH 2013
--
module(...,package.seeall)
Voicemail = {}
-MESSAGE_LENGTH_MIN = 3;
-MESSAGE_LENGTH_MAX = 120;
-SILENCE_LENGTH_ABORT = 5;
-SILENCE_LEVEL = 500;
-BEEP = 'tone_stream://%(1000,0,500)';
-RECORD_FILE_PREFIX = '/var/spool/freeswitch/voicemail_';
+DEFAULT_SETTINGS = {
+ pin_length_max = 10,
+ pin_length_min = 2,
+ pin_timeout = 20,
+ key_new_messages = '1',
+ key_saved_messages = '2',
+ key_config_menu = '0',
+ key_terminator = '#',
+ key_previous = '4',
+ key_next = '6',
+ key_delete = '7',
+ key_save = '2',
+ key_main_menu = '#',
+ record_length_max = 300,
+ record_length_min = 4,
+ records_max = 100,
+ silence_lenght_abort = 3,
+ silence_level = 500,
+ beep = 'tone_stream://%(1000,0,500)',
+ record_file_prefix = 'voicemail_',
+ record_file_suffix = '.wav',
+ record_file_path = '/var/spool/freeswitch/',
+ record_repeat = 3,
+ notify = true,
+ attachment = true,
+ mark_read = true,
+ purge = false,
+ generic_file_path = '/var/opt/gemeinschaft/generic_files/',
+}
+
-- create voicemail object
function Voicemail.new(self, arg)
@@ -23,93 +47,296 @@ function Voicemail.new(self, arg)
self.log = arg.log;
self.database = arg.database;
self.record = arg.record;
+ self.domain = arg.domain;
return object
end
--- find voicemail account by sip account id
-function Voicemail.find_by_sip_account_id(self, id)
- local sql_query = 'SELECT `a`.`id`, `a`.`uuid`, `a`.`auth_name`, `a`.`caller_name`, `b`.`name_path`, `b`.`greeting_path`, `a`.`voicemail_pin`, `b`.`password`, `c`.`host` AS `domain` \
- FROM `sip_accounts` `a` LEFT JOIN `voicemail_prefs` `b` ON `a`.`auth_name` = `b`.`username` \
- JOIN `sip_domains` `c` ON `a`.`sip_domain_id` = `c`.`id` \
- WHERE `a`.`id` = ' .. tonumber(id);
+function Voicemail.find_by_sql(self, sql_query)
local voicemail_account = nil;
self.database:query(sql_query, function(entry)
voicemail_account = Voicemail:new(self);
voicemail_account.record = entry;
voicemail_account.id = tonumber(entry.id);
voicemail_account.uuid = entry.uuid;
- end)
+ voicemail_account.name = entry.name;
+ voicemail_account.settings = self:settings_get(entry.id);
+ end);
return voicemail_account;
end
--- Find Voicemail account by name
-function Voicemail.find_by_name(self, account_name)
- id = tonumber(id) or 0;
- local sql_query = string.format('SELECT * FROM `voicemail_prefs` WHERE `username`= "%s" LIMIT 1', account_name)
- local record = nil
- self.database:query(sql_query, function(voicemail_entry)
- record = voicemail_entry
- end)
+function Voicemail.find_by_name(self, name)
+ local sql_query = 'SELECT * FROM `voicemail_accounts` \
+ WHERE `name` = ' .. self.database:escape(name, '"') .. ' LIMIT 1';
+
+ return self:find_by_sql(sql_query);
+end
+
+
+function Voicemail.find_by_id(self, id)
+ local sql_query = 'SELECT * FROM `voicemail_accounts` \
+ WHERE `id` = ' .. self.database:escape(id, '"') .. ' LIMIT 1';
+
+ return self:find_by_sql(sql_query);
+end
+
+
+function Voicemail.find_by_sip_account_id(self, id)
+ local sql_query = 'SELECT `a`.* FROM `voicemail_accounts` `a` \
+ JOIN `sip_accounts` `b` ON `a`.`id` = `b`.`voicemail_account_id` \
+ WHERE `b`.`id` = ' .. self.database:escape(id, '"') .. ' LIMIT 1';
+
+ return self:find_by_sql(sql_query);
+end
+
+
+function Voicemail.find_by_number(self, number)
+ local sql_query = 'SELECT `a`.* FROM `voicemail_accounts` `a` \
+ JOIN `sip_accounts` `b` ON `a`.`id` = `b`.`voicemail_account_id` \
+ JOIN `phone_numbers` `c` ON `b`.`id` = `c`.`phone_numberable_id` \
+ WHERE `c`.`number` = ' .. self.database:escape(number, '"') .. ' \
+ AND `c`.`phone_numberable_type` = "SipAccount" LIMIT 1';
+
+ return self:find_by_sql(sql_query);
+end
+
+
+function Voicemail.settings_get(self, id)
+ require 'common.configuration_table';
+ local parameters = common.configuration_table.get(self.database, 'voicemail', 'settings', { settings = DEFAULT_SETTINGS });
+
+ return common.configuration_table.settings(self.database, 'voicemail_settings', 'voicemail_account_id', id or self.id, parameters)
+end
+
+
+function Voicemail.find_message_by_uuid(self, uuid)
+ local sql_query = 'SELECT * FROM `voicemail_msgs` WHERE `uuid` = ' .. self.database:escape(uuid, '"') .. ' LIMIT 1';
+ return self.database:query_return_first(sql_query);
+end
+
+
+function Voicemail.messages_get(self, status)
+ local sql_query = 'SELECT * FROM `voicemail_msgs` WHERE `username` = ' .. self.database:escape(self.name, '"');
+
+ status = status or 'all';
+
+ if status == 'read' then
+ sql_query = sql_query .. ' AND `read_epoch` > 0';
+ elseif status == 'unread' then
+ sql_query = sql_query .. ' AND (`read_epoch` = 0 OR `read_epoch` IS NULL)';
+ elseif status == 'new' then
+ sql_query = sql_query .. ' AND `in_folder` != "save" AND `flags` != "save"';
+ elseif status == 'saved' then
+ sql_query = sql_query .. ' AND (`in_folder` = "save" OR `flags` = "save")';
+ end
+
+ return self.database:query_return_all(sql_query);
+end
- if voicemail_account then
- voicemail_account.account_name = account_name;
- if record then
- voicemail_account.name_path = record.name_path;
- voicemail_account.greeting_path = record.greeting_path;
- voicemail_account.password = record.password;
+
+function Voicemail.menu_main(self, caller, authorized)
+ self.caller = caller;
+
+ require 'dialplan.ivr';
+ self.ivr = dialplan.ivr.Ivr:new{ caller = self.caller, log = self.log };
+
+ if not authorized then
+ if common.str.blank(self.settings.pin) then
+ self.log:notice('VOICEMAIL_MAIN_MENU - unaunthorized, no PIN, ', self.class, '=', self.id, '/', self.uuid, '|', self.name);
+ return { continue = false, code = 500, phrase = 'Unauthorized', no_cdr = true }
+ end
+
+ self.caller:answer();
+ self.caller:sleep(1000);
+
+ if not self.ivr:check_pin('voicemail_enter_pass', 'voicemail_fail_auth', self.settings.pin) then
+ self.log:notice('VOICEMAIL_MAIN_MENU - wrong PIN, ', self.class, '=', self.id, '/', self.uuid, '|', self.name);
+ caller.session:sayPhrase('voicemail_goodbye');
+ return { continue = false, code = 500, phrase = 'Unauthorized', no_cdr = true }
end
end
- return voicemail_account
+ local messages_new = self:messages_get('unread');
+ local messages_saved = self:messages_get('read');
+
+ if not caller:answered() then
+ self.caller:answer();
+ self.caller:sleep(1000);
+ end
+
+ if self.settings.voicemail_hello then
+ caller.session:sayPhrase('voicemail_hello');
+ end
+
+ if #messages_new > 0 then
+ caller.session:sayPhrase('voicemail_message_count', #messages_new .. ':new');
+ end
+
+ while true do
+ self.log:info('VOICEMAIL_MAIN_MENU - ', self.class, '=', self.id, '/', self.uuid, '|', self.name, ', messages: ', #messages_new, ':', #messages_saved);
+ self.caller:send_display(#messages_new .. ' new messages');
+
+ local main_menu = {
+ { key = self.settings.key_new_messages, method = self.menu_messages, parameters = { self, 'new', messages_new } },
+ { key = self.settings.key_saved_messages, method = self.menu_messages, parameters = { self, 'saved', messages_saved } },
+ { key = self.settings.key_config_menu, method = self.menu_options, parameters = { self } },
+ { key = self.settings.key_terminator, exit = true },
+ { key = '', exit = true },
+ };
+
+ local digits, key = self.ivr:ivr_phrase('voicemail_menu', main_menu);
+ self.log:debug('VOICEMAIL_MAIN_MENU - digit: ', digits);
+ if key.exit then
+ break;
+ end
+
+ key.method(unpack(key.parameters));
+
+ messages_new = self:messages_get('unread');
+ messages_saved = self:messages_get('read');
+ end
+ self.caller:send_display('Goodbye');
+ caller.session:sayPhrase('voicemail_goodbye');
end
--- Find Voicemail account by number
-function Voicemail.find_by_number(self, phone_number)
- local sip_account = nil;
- require "common.phone_number"
- local destination_number_object = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database }:find_by_number(phone_number);
- if destination_number_object and destination_number_object.record.phone_numberable_type:lower() == "sipaccount" then
- return self:find_by_sip_account_id(destination_number_object.record.phone_numberable_id);
+function Voicemail.menu_messages(self, folder, messages)
+ self.log:info('VOICEMAIL_MESSAGES_MENU - ', folder,' messages: ', #messages);
+
+ local digits = nil;
+ local key = nil;
+
+ local message_menu = {
+ { key = self.settings.key_previous, action = 'previous' },
+ { key = self.settings.key_delete, action = 'delete' },
+ { key = self.settings.key_save, action = 'save' },
+ { key = self.settings.key_next, action = 'next' },
+ { key = self.settings.key_main_menu, exit = true },
+ };
+
+ if folder == 'saved' then
+ message_menu = {
+ { key = self.settings.key_previous, action = 'previous' },
+ { key = self.settings.key_delete, action = 'delete' },
+ { key = self.settings.key_next, action = 'next' },
+ { key = self.settings.key_main_menu, exit = true },
+ };
+ end
+
+ if #messages == 0 then
+ digits, key = self.ivr:ivr_phrase('voicemail_no_messages', message_menu, 0, 0);
+ return;
+ end
+
+ local index = 1;
+ while index <= #messages do
+ local message = messages[index];
+ self.caller:send_display(index .. ': ' .. message.cid_name .. ' ' .. message.cid_number);
+ digits, key = self.ivr:ivr_phrase('voicemail_message_play', message_menu, 0, 0,
+ index .. ':' .. message.created_epoch .. ':' .. message.file_path
+ );
+ if digits == '' then
+ if common.str.to_i(message.read_epoch) == 0 then
+ self:message_mark_read(message);
+ end
+ digits, key = self.ivr:ivr_phrase('voicemail_message_menu_' .. folder, message_menu, 15, 0);
+ end
+
+ if not key or key.exit then
+ break;
+ end
+
+ if key.action == 'previous' then
+ if index > 1 then
+ index = index - 1;
+ end
+ else
+ index = index + 1;
+ end
+
+ if key.action == 'delete' and self:message_delete(message) then
+ self.caller:send_display('Message deleted');
+ digits = self.caller.session:sayPhrase('voicemail_ack', 'deleted');
+ elseif key.action == 'save' and self:message_save(message) then
+ self.caller:send_display('Message saved');
+ digits = self.caller.session:sayPhrase('voicemail_ack', 'saved');
+ end
+ if index > #messages then
+ digits = self.ivr:ivr_phrase('voicemail_no_messages', message_menu, 0, 0);
+ end
end
+end
- return false;
+
+function Voicemail.message_delete(self, message)
+ self.log:debug('VOICEMAIL_MESSAGE_DELETE - message: ', message.uuid);
+ require 'common.fapi';
+ return common.fapi.FApi:new{ log = self.log }:execute('vm_delete', message.username .. '@' .. message.domain .. ' ' .. message.uuid);
+end
+
+function Voicemail.message_mark_read(self, message)
+ self.log:debug('VOICEMAIL_MESSAGE_MARK_READ - message: ', message.uuid);
+ require 'common.fapi';
+ return common.fapi.FApi:new{ log = self.log }:execute('vm_read', message.username .. '@' .. message.domain .. ' read ' .. message.uuid);
end
-function Voicemail.leave(self, caller, phone_number)
- require 'common.str'
+function Voicemail.message_save(self, message)
+ self.log:debug('VOICEMAIL_MESSAGE_SAVE - message: ', message.uuid);
+ require 'common.fapi';
+ return common.fapi.FApi:new{ log = self.log }:execute('vm_fsdb_msg_save', 'default ' .. message.domain .. ' ' .. message.username .. ' ' .. message.uuid);
+end
+
- self.log:info('VOICEMAIL_LEAVE - account=', self.record.id, '/', self.record.uuid, ', auth_name: ', self.record.auth_name, ', caller_name: ', self.record.caller_name);
+function Voicemail.leave(self, caller, greeting, number)
+ self.log:info('VOICEMAIL_LEAVE - voicemail_account=', self.record.id, '/', self.record.uuid, '|', self.record.name, ', forwarding_number: ', number, ', greeting: ', greeting);
- caller:set_callee_id(phone_number, self.record.caller_name);
+ caller:set_callee_id(number, common.array.try(caller, 'auth_account.caller_name') or common.array.try(caller, 'auth_account.name'));
caller:answer();
- caller:send_display(common.str.to_s(self.record.caller_name), common.str.to_s(phone_number));
+ caller:send_display(common.array.try(caller, 'auth_account.caller_name') or common.array.try(caller, 'auth_account.name'));
caller:sleep(1000);
- if not common.str.blank(self.record.greeting_path) then
- caller.session:sayPhrase('voicemail_play_greeting', 'greeting:' .. tostring(self.record.greeting_path));
- elseif not common.str.blank(self.record.name_path) then
- caller.session:sayPhrase('voicemail_play_greeting', 'name:' .. tostring(self.record.name_path));
- elseif not common.str.blank(phone_number) then
- caller.session:sayPhrase('voicemail_play_greeting', (tostring(phone_number):gsub('[%D]', '')));
+ local greeting_file = nil;
+ if not common.str.blank(greeting) then
+ greeting_file = self:greeting_get(greeting, caller.auth_account.owner.class, caller.auth_account.owner.id)
+ end
+
+ if not common.str.blank(greeting_file) then
+ self.log:debug('VOICEMAIL_LEAVE greeting_file: ', greeting_file);
+ caller:playback(greeting_file);
+ elseif not common.str.blank(number) then
+ caller.session:sayPhrase('voicemail_play_greeting', (tostring(number):gsub('[%D]', '')));
end
- local record_file_name = RECORD_FILE_PREFIX .. caller.uuid .. '.wav';
- caller.session:streamFile(BEEP);
+ local record_file_name = self.settings.record_file_path ..self.settings.record_file_prefix .. caller.uuid .. self.settings.record_file_suffix;
self.log:info('VOICEMAIL_LEAVE - recording to file: ', tostring(record_file_name));
- local result = caller.session:recordFile(record_file_name, MESSAGE_LENGTH_MAX, SILENCE_LEVEL, SILENCE_LENGTH_ABORT);
- local duration = caller:to_i('record_seconds');
+
+ local voicemail_record_message = nil;
+ if common.str.blank(greeting_file) then
+ voicemail_record_message = 'voicemail_record_message';
+ end
+
+ require 'dialplan.ivr';
+ local ivr = dialplan.ivr.Ivr:new{ caller = caller, log = self.log };
+ local duration = ivr:record(
+ record_file_name,
+ self.settings.beep,
+ voicemail_record_message,
+ 'voicemail_message_too_short',
+ self.settings.record_length_max,
+ self.settings.record_length_min,
+ self.settings.record_repeat,
+ self.settings.silence_level,
+ self.settings.silence_lenght_abort);
- if duration >= MESSAGE_LENGTH_MIN then
+ if duration >= self.settings.record_length_min then
self.log:info('VOICEMAIL_LEAVE - saving recorded message to voicemail, duration: ', duration);
require 'common.fapi'
common.fapi.FApi:new{ log = self.log, uuid = caller.uuid }:execute('vm_inject',
- self.record.auth_name ..
- '@' .. self.record.domain .. " '" ..
+ self.record.name ..
+ '@' .. self.domain .. " '" ..
record_file_name .. "' '" ..
caller.caller_id_number .. "' '" ..
caller.caller_id_name .. "' '" ..
@@ -121,32 +348,171 @@ function Voicemail.leave(self, caller, phone_number)
caller:set_variable('voicemail_message_len');
end
os.remove(record_file_name);
- return true;
+ caller:send_display('Goodbye');
+ caller.session:sayPhrase('voicemail_goodbye');
end
function Voicemail.trigger_notification(self, caller)
- local command = 'http_request.lua ' .. caller.uuid .. ' http://127.0.0.1/trigger/voicemail?sip_account_id=' .. tostring(self.id);
+ local command = 'http_request.lua ' .. caller.uuid .. ' http://127.0.0.1/trigger/voicemail?voicemail_account_id=' .. tostring(self.id);
- require 'common.fapi'
+ require 'common.fapi';
return common.fapi.FApi:new():execute('luarun', command);
end
-function Voicemail.menu(self, caller, authorized)
- self.log:info('VOICEMAIL_MENU - account: ', self.record.auth_name);
+function Voicemail.message_play(self, caller, uuid)
+ local message = self:find_message_by_uuid(uuid);
- if authorized then
- caller:set_variable('voicemail_authorized', true);
+ if message and message.file_path then
+ if not caller:answered() then
+ caller:answer();
+ caller:sleep(1000);
+ end
+ caller:send_display(message.cid_name .. ' ' .. message.cid_number);
+ caller:playback(message.file_path);
end
- caller:set_callee_id(phone_number, self.record.caller_name);
- caller:answer();
- caller:send_display(common.str.to_s(self.record.caller_name), common.str.to_s(phone_number));
+ return message;
+end
- caller:sleep(1000);
- caller:set_variable('skip_greeting', true);
- caller:set_variable('skip_instructions', true);
-
- caller:execute('voicemail', 'check default ' .. self.record.domain .. ' ' .. self.record.auth_name);
+
+function Voicemail.greeting_get(self, name, owner_type, owner_id, file_types)
+ file_types = file_types or {'audio/x-wav'}
+ local sql_query = 'SELECT * FROM `generic_files` \
+ WHERE `name` = ' .. self.database:escape(name, '"') .. ' \
+ AND `owner_type` = ' .. self.database:escape(owner_type, '"') .. ' \
+ AND `owner_id` = ' .. self.database:escape(owner_id, '"') .. ' \
+ AND `file_type` IN ("' .. table.concat(file_types, '","') .. '") LIMIT 1';
+
+ local greeting = self.database:query_return_first(sql_query);
+
+ if not greeting or common.str.blank(greeting.file) then
+ return nil;
+ end
+
+ return self.settings.generic_file_path .. greeting.id .. '/' .. greeting.file;
end
+
+
+function Voicemail.menu_options(self)
+ self.log:info('VOICEMAIL_OPTIONS_MENU');
+ self.caller:send_display('Voicemail options');
+
+ local menu = {
+ { key = '1', method = self.greeting_record, parameters = { self } },
+ { key = '3', method = self.pin_change, parameters = { self } },
+ { key = self.settings.key_terminator, exit = true },
+ { key = '', exit = true },
+ };
+
+ while true do
+ local digits, key = self.ivr:ivr_phrase('voicemail_config_menu', menu);
+ self.log:debug('VOICEMAIL_MAIN_MENU - digit: ', digits);
+ if key.exit then
+ break;
+ end
+
+ key.method(unpack(key.parameters));
+ end
+end
+
+
+function Voicemail.greeting_record(self)
+ self.log:info('VOICEMAIL_GREETING_RECORD');
+
+ local record_file_name = self.settings.record_file_path ..self.settings.record_file_prefix .. self.caller.uuid .. self.settings.record_file_suffix;
+
+ require 'dialplan.ivr';
+ local ivr = dialplan.ivr.Ivr:new{ caller = self.caller, log = self.log };
+ local duration = ivr:record(
+ record_file_name,
+ self.settings.beep,
+ 'voicemail_record_greeting',
+ 'voicemail_message_too_short',
+ self.settings.record_length_max,
+ 1,
+ self.settings.record_repeat,
+ self.settings.silence_level,
+ self.settings.silence_lenght_abort);
+
+ if duration >= 0 then
+ self.caller:playback(record_file_name);
+ end
+
+ if duration >= 1 then
+ local name = 'greeting_' .. os.time();
+ local file_name = name .. self.settings.record_file_suffix
+
+ local generic_file_record = {
+ name = name,
+ file = file_name,
+ file_type = 'audio/x-wav',
+ category = 'greeting',
+ owner_id = self.record.voicemail_accountable_id,
+ owner_type = self.record.voicemail_accountable_type,
+ updated_at = { 'NOW()', raw = true },
+ created_at = { 'NOW()', raw = true },
+ };
+
+ if self.database:insert_or_update('generic_files', generic_file_record) then
+ local file_id = self.database:last_insert_id();
+
+ local destination_directory = self.settings.generic_file_path .. file_id;
+ self.log:info('VOICEMAIL_GREETING_RECORD recorded greeting - id: ', file_id, ', file: ', destination_directory .. '/' .. file_name, ', duration: ', duration);
+ os.execute('mkdir ' .. destination_directory);
+ local result, error_string = os.rename(record_file_name, destination_directory .. '/' .. file_name);
+ if not result then
+ self.log:error('VOICEMAIL_GREETING_RECORD - ', error_string);
+ end
+ end
+ end
+end
+
+
+function Voicemail.pin_change(self)
+ self.log:info('VOICEMAIL_PIN_CHANGE - lenght: ', self.settings.pin_length_min, '-', self.settings.pin_length_max);
+
+ if not common.str.blank(self.settings.pin) then
+ if not self.ivr:check_pin('voicemail_enter_pass', 'voicemail_fail_auth', self.settings.pin) then
+ self.log:notice('VOICEMAIL_PIN_CHANGE - wrong old PIN, ', self.class, '=', self.id, '/', self.uuid, '|', self.name);
+ return false;
+ end
+ end
+
+ local digits = '';
+ for i = 1, 3 do
+ if digits:len() < self.settings.pin_length_min then
+ self.caller:send_display('PIN too short');
+ elseif digits:len() > self.settings.pin_length_max then
+ self.caller:send_display('PIN too long');
+ else
+ self.caller:send_display('PIN: OK');
+ break
+ end
+ self.caller:send_display('Enter new PIN');
+ digits = self.ivr:read_phrase('voicemail_enter_pass', nil, self.settings.pin_length_max, self.settings.pin_length_min, self.settings.pin_timeout, self.settings.terminator_key);
+ end
+
+ if digits:len() < self.settings.pin_length_min or digits:len() > self.settings.pin_length_max then
+ self.caller:send_display('PIN not changed');
+ return false;
+ end
+
+ local sql_query = 'UPDATE `voicemail_settings` \
+ SET `value` = ' .. self.database:escape(digits, '"') .. ', `class_type` = "String", `updated_at` = NOW() \
+ WHERE `name`="pin" AND `voicemail_account_id` = ' .. self.id;
+ if not self.settings.pin then
+ sql_query = 'INSERT INTO `voicemail_settings` \
+ (`voicemail_account_id`, `name`, `value`, `class_type`, `description`, `created_at`, `updated_at`) \
+ VALUES (' .. self.id .. ', "pin", "' .. digits .. '", "String", "Voicemail PIN", NOW(), NOW())';
+ end
+
+ if self.database:query(sql_query) then
+ self.settings.pin = digits;
+ self.caller:send_display('PIN changed');
+ self.caller.session:sayPhrase('voicemail_change_pass_success');
+ return true;
+ end
+end
+
diff --git a/misc/freeswitch/scripts/dialplan_default.lua b/misc/freeswitch/scripts/dialplan_default.lua
index 2b651c5..32789cb 100644
--- a/misc/freeswitch/scripts/dialplan_default.lua
+++ b/misc/freeswitch/scripts/dialplan_default.lua
@@ -29,6 +29,9 @@ function input_call_back_callee(s, object_type, object_data, arg)
end
end
+function global_callback_handler(...)
+ return global_callback:run({...});
+end
-- initialize logging
require 'common.log'
@@ -90,6 +93,10 @@ else
end
end
+require 'dialplan.callback';
+global_callback = dialplan.callback.Callback:new{ session = session, log = log };
+
+
if start_caller.from_node then
log:debug('AUTHENTICATION_REQUIRED_NODE - node_id: ', start_caller.node_id, ', domain: ', start_dialplan.domain);
start_dialplan:hangup(407, start_dialplan.domain);
@@ -110,6 +117,8 @@ else
start_dialplan:run(start_destination);
end
+start_caller.session:unsetInputCallback();
+
-- release database handle
if database then
database:release();
diff --git a/misc/freeswitch/scripts/event/presence_update.lua b/misc/freeswitch/scripts/event/presence_update.lua
index f9d8ee7..fd85f03 100644
--- a/misc/freeswitch/scripts/event/presence_update.lua
+++ b/misc/freeswitch/scripts/event/presence_update.lua
@@ -95,9 +95,6 @@ function PresenceUpdate.sofia_register(self, event)
local timestamp = event:getHeader('Event-Date-Timestamp');
local sip_account = self:retrieve_sip_account(account, account);
- if sip_account and common.str.to_b(self.config.trigger.sip_account_register) then
- self:trigger_rails(sip_account, 'register', timestamp, account)
- end
self.log:debug('[', account, '] PRESENCE_UPDATE - flushing account cache on register');
self.presence_accounts[account] = nil;
@@ -109,9 +106,6 @@ function PresenceUpdate.sofia_ungerister(self, event)
local timestamp = event:getHeader('Event-Date-Timestamp');
local sip_account = self:retrieve_sip_account(account, account);
- if sip_account and common.str.to_b(self.config.trigger.sip_account_unregister) then
- self:trigger_rails(sip_account, 'unregister', timestamp, account)
- end
self.log:debug('[', account, '] PRESENCE_UPDATE - flushing account cache on unregister');
self.presence_accounts[account] = nil;
@@ -242,9 +236,6 @@ function PresenceUpdate.sip_account(self, inbound, account, domain, status, uuid
uuid = uuid
}:set(status_map[status] or 'terminated', caller_id);
- if common.str.to_b(self.config.trigger.sip_account_presence) then
- self:trigger_rails(sip_account, status_map[status] or 'terminated', timestamp, uuid);
- end
end
diff --git a/misc/freeswitch/scripts/test_route.lua b/misc/freeswitch/scripts/test_route.lua
index 98dfda9..4a879d1 100644
--- a/misc/freeswitch/scripts/test_route.lua
+++ b/misc/freeswitch/scripts/test_route.lua
@@ -41,6 +41,8 @@ local dialplan_object = dialplan.dialplan.Dialplan:new{ log = log, caller = call
dialplan_object:configuration_read();
caller.dialplan = dialplan_object;
caller.local_node_id = dialplan_object.node_id;
+caller.date = os.date('%y%m%d%w');
+caller.time = os.date('%H%M%S');
dialplan_object:retrieve_caller_data();
local destination = arguments.destination or dialplan_object:destination_new{ number = caller.destination_number };