summaryrefslogtreecommitdiff
path: root/misc/freeswitch/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'misc/freeswitch/scripts')
-rw-r--r--misc/freeswitch/scripts/common/call_forwarding.lua185
-rw-r--r--misc/freeswitch/scripts/common/database.lua2
-rw-r--r--misc/freeswitch/scripts/common/group.lua84
-rw-r--r--misc/freeswitch/scripts/common/log.lua16
-rw-r--r--misc/freeswitch/scripts/common/object.lua106
-rw-r--r--misc/freeswitch/scripts/common/sip_account.lua36
-rw-r--r--misc/freeswitch/scripts/dialplan/dialplan.lua166
-rw-r--r--misc/freeswitch/scripts/dialplan/functions.lua94
-rw-r--r--misc/freeswitch/scripts/dialplan/router.lua1
-rw-r--r--misc/freeswitch/scripts/dialplan_default.lua2
10 files changed, 503 insertions, 189 deletions
diff --git a/misc/freeswitch/scripts/common/call_forwarding.lua b/misc/freeswitch/scripts/common/call_forwarding.lua
index 192c694..3429dc9 100644
--- a/misc/freeswitch/scripts/common/call_forwarding.lua
+++ b/misc/freeswitch/scripts/common/call_forwarding.lua
@@ -16,6 +16,7 @@ function CallForwarding.new(self, arg, object)
self.database = arg.database;
self.record = arg.record;
self.domain = arg.domain;
+ self.parent = arg.parent;
return object;
end
@@ -92,11 +93,191 @@ function CallForwarding.list_by_owner(self, call_forwardable_id, call_forwardabl
end
-function CallForwarding.presence_set(self, presence_state)
+function CallForwarding.presence_set(self, presence_state, id)
+ id = id or self.record.id;
+
+ if not id or not presence_state then
+ return;
+ end
+
require 'dialplan.presence'
local presence = dialplan.presence.Presence:new();
- presence:init{log = self.log, accounts = { 'f-cftg-' .. tostring(self.record.id) }, domain = self.domain, uuid = 'call_forwarding_' .. tostring(self.record.id)};
+ presence:init{log = self.log, accounts = { 'f-cftg-' .. id }, domain = self.domain, uuid = 'call_forwarding_' .. id};
return presence:set(presence_state);
end
+
+
+function CallForwarding.service_id_by_name(self, service_name)
+ local service_id = nil;
+ sql_query = 'SELECT `id` FROM `call_forward_cases` WHERE `value` = ' .. self.database:escape(service_name, '"');
+ self.database:query(sql_query, function(record)
+ service_id = tonumber(record.id);
+ end);
+
+ return service_id;
+end
+
+
+function CallForwarding.camelize_type(self, account_type)
+ ACCOUNT_TYPES = {
+ sipaccount = 'SipAccount',
+ conference = 'Conference',
+ faxaccount = 'FaxAccount',
+ callthrough = 'Callthrough',
+ huntgroup = 'HuntGroup',
+ automaticcalldistributor = 'AutomaticCallDistributor',
+ }
+
+ return ACCOUNT_TYPES[account_type] or account_type;
+end
+
+function CallForwarding.call_forwarding_on(self, service, destination, destination_type, timeout, source)
+ require 'common.str'
+
+ if source then
+ sql_query = 'SELECT `id`, `destination`, `destinationable_type`, `destinationable_id`, `call_forward_case_id`, `position`, `timeout` FROM `call_forwards` \
+ WHERE `call_forwardable_id` = ' .. self.parent.id .. ' \
+ AND `call_forwardable_type` = "' .. self.parent.class .. '" \
+ AND `call_forward_case_id` IN (SELECT `id` FROM `call_forward_cases` WHERE `value` = "' .. service .. '") \
+ AND `source` = "' .. source .. '" ORDER BY `active` DESC LIMIT 1';
+ else
+ sql_query = 'SELECT `id`, `destination`, `destinationable_type`, `destinationable_id`, `call_forward_case_id`, `position`, `timeout` FROM `call_forwards` \
+ WHERE `call_forwardable_id` = ' .. self.parent.id .. ' \
+ AND `call_forwardable_type` = "' .. self.parent.class .. '" \
+ AND `call_forward_case_id` IN (SELECT `id` FROM `call_forward_cases` WHERE `value` = "' .. service .. '") \
+ AND (`source` = "" OR `source` IS NULL) ORDER BY `active` DESC LIMIT 1';
+ end
+
+ destination_type = destination_type or 'PhoneNumber';
+ local destination_id = nil;
+ destination = destination or '';
+ local service_id = nil;
+ local entry_id = nil;
+
+ self.database:query(sql_query, function(record)
+ entry_id = tonumber(record.id);
+ service_id = record.call_forward_case_id;
+ timeout = tonumber(timeout) or tonumber(record.timeout);
+ if common.str.blank(destination) then
+ if not common.str.blank(record.destinationable_type) then
+ destination_type = common.str.downcase(record.destinationable_type);
+ end
+ if not common.str.blank(record.destination) then
+ destination = record.destination;
+ end
+ destination_id = tonumber(record.destinationable_id);
+ end
+ end)
+
+ if service == 'noanswer' then
+ timeout = tonumber(timeout) or '30';
+ else
+ timeout = nil;
+ end
+
+ if destination == '' and not estination_id and destination_type:lower() ~= 'voicemail' then
+ self.log:notice('CALL_FORWARDING_ON ', service, ' - for: ', self.parent.class, '=', self.parent.id, '/', self.parent.uuid,' - destination not specified: ', destination_type, '=', destination_id);
+ return false;
+ end
+
+ if not tonumber(service_id) then
+ service_id = self:service_id_by_name(service);
+ end
+
+ local call_forwarding_record = {
+ id = entry_id,
+ active = true,
+ uuid = { 'UUID()', raw = true },
+ updated_at = { 'NOW()', raw = true },
+ created_at = { 'NOW()', raw = true },
+ call_forwardable_id = self.parent.id,
+ call_forwardable_type = self:camelize_type(self.parent.class),
+ call_forward_case_id = service_id,
+ destination = destination,
+ destinationable_type = self:camelize_type(destination_type),
+ destinationable_id = destination_id,
+ timeout = timeout,
+ position = 1,
+ };
+
+ local result = self.database:insert_or_update('call_forwards', call_forwarding_record, { created_at = false, position = false });
+
+ if not result then
+ self.log:notice('CALL_FORWARDING_ON ', service, ' - could not be activated for: ', self.parent.class, '=', self.parent.id, '/', self.parent.uuid,' - destination: ', destination_type, '=', destination_id, '|', destination);
+ return false;
+ end
+
+ entry_id = entry_id or self.database:last_insert_id();
+
+ self.log:info('CALL_FORWARDING_ON ', service, ' - callforwarding=', entry_id, ', for: ', self.parent.class, '=', self.parent.id, '/', self.parent.uuid, ', destination: ', destination_type, '=', destination_id, '|', destination, ', timeout: ', timeout);
+
+ if tonumber(entry_id) then
+ if destination_type:lower() == 'voicemail' then
+ self:presence_set('early', entry_id);
+ else
+ self:presence_set('confirmed', entry_id);
+ end
+ end
+
+ return result;
+end
+
+
+function CallForwarding.call_forwarding_off(self, service, source, delete)
+ local conditions = {}
+ table.insert(conditions, '`call_forwardable_id` = ' .. self.parent.id);
+ table.insert(conditions, '`call_forwardable_type` = "' .. self.parent.class .. '"');
+
+ if source then
+ table.insert(conditions, '`source` = "' .. source);
+ else
+ table.insert(conditions, '(`source` = "" OR `source` IS NULL)');
+ end
+
+ if service then
+ table.insert(conditions, '`call_forward_case_id` IN (SELECT `id` FROM `call_forward_cases` WHERE `value` = "' .. service .. '")');
+ end
+
+ local call_forwarding_ids = {}
+ local sql_query = 'SELECT `id` FROM `call_forwards` WHERE ' .. table.concat(conditions, ' AND ');
+ self.database:query(sql_query, function(record)
+ table.insert(call_forwarding_ids, record.id);
+ end)
+
+ -- set call forwarding entry inactive
+ local sql_query = 'UPDATE `call_forwards` SET `active` = FALSE, `updated_at` = NOW() WHERE ' .. table.concat(conditions, ' AND ');
+ local call_forwards = {};
+
+ -- or delete call forwarding entry
+ if delete then
+ sql_query = 'SELECT * FROM `call_forwards` WHERE ' .. table.concat(conditions, ' AND ');
+ self.database:query(sql_query, function(forwarding_entry)
+ table.insert(call_forwards, forwarding_entry)
+ end)
+ sql_query = 'DELETE FROM `call_forwards` WHERE ' .. table.concat(conditions, ' AND ');
+ end
+
+ if not self.database:query(sql_query) then
+ self.log:notice('CALL_FORWARDING_OFF ', (service or 'any'), ' - could not be deactivated for: ', self.parent.class, '=', self.parent.id, '/', self.parent.uuid);
+ return false;
+ end
+
+ if delete then
+ require 'common.sync_log'
+ local sync_log_class = common.sync_log.SyncLog:new{ log = self.log, database = self.database, homebase_ip_address = '' }
+
+ for index, call_forward in ipairs(call_forwards) do
+ sync_log_class:insert('CallForward', call_forward, 'destroy', nil);
+ end
+ end
+
+ for index, entry_id in ipairs(call_forwarding_ids) do
+ if tonumber(entry_id) then
+ self:presence_set('terminated', entry_id);
+ end
+ end
+
+ return true;
+end
diff --git a/misc/freeswitch/scripts/common/database.lua b/misc/freeswitch/scripts/common/database.lua
index 345f69d..8aed1ac 100644
--- a/misc/freeswitch/scripts/common/database.lua
+++ b/misc/freeswitch/scripts/common/database.lua
@@ -72,7 +72,7 @@ function Database.last_insert_id(self)
end
-function Database.insert_or_update(self, db_table, record, use_on_update)
+function Database.insert_or_update(self, db_table, record, ignore_on_update)
ignore_on_update = ignore_on_update or self.ignore_on_update;
local record_sql_create = {};
local record_sql_update = {};
diff --git a/misc/freeswitch/scripts/common/group.lua b/misc/freeswitch/scripts/common/group.lua
index c4125bc..b9cae61 100644
--- a/misc/freeswitch/scripts/common/group.lua
+++ b/misc/freeswitch/scripts/common/group.lua
@@ -86,3 +86,87 @@ function Group.name_id_by_member(self, member_id, member_type)
return group_names, group_ids;
end
+
+
+function Group.permission_targets(self, group_ids, permission)
+ if not group_ids or #group_ids == 0 or not permission then
+ return {};
+ end
+
+ local sql_query = 'SELECT DISTINCT `b`.`id`, `b`.`name` \
+ FROM `group_permissions` `a` \
+ JOIN `groups` `b` ON `b`.`id` = `a`.`target_group_id` \
+ WHERE `a`.`permission` = ' .. self.database:escape(permission, '"') .. ' \
+ AND `a`.`group_id` IN (' .. table.concat(group_ids, ',') .. ') \
+ AND `b`.`active` IS TRUE \
+ GROUP BY `a`.`target_group_id` LIMIT ' .. MAX_GROUP_MEMBERSHIPS;
+
+ local group_names = {};
+ local group_ids = {};
+
+ self.database:query(sql_query, function(account_entry)
+ table.insert(group_names, account_entry.name);
+ table.insert(group_ids, tonumber(account_entry.id));
+ end);
+
+ return group_names, group_ids;
+end
+
+
+function Group.is_target(self, group_id, permission)
+ if not group_id or not permission then
+ return nil;
+ end
+
+ local sql_query = 'SELECT `b`.`name` \
+ FROM `group_permissions` `a` \
+ JOIN `groups` `b` ON `b`.`id` = `a`.`target_group_id` \
+ WHERE `a`.`permission` = ' .. self.database:escape(permission, '"') .. ' \
+ AND `a`.`group_id` = ' .. tonumber(group_id) .. ' \
+ AND `b`.`active` IS TRUE \
+ LIMIT 1';
+
+ return self.database:query_return_value(sql_query);
+end
+
+
+function Group.union(self, ...)
+ local groups = {};
+ local group_sets = {...};
+ for set_index=1, #group_sets do
+ if type(group_sets[set_index]) == 'table' then
+ local group_ids = group_sets[set_index];
+ for index=1, #group_ids do
+ groups[tonumber(group_ids[index])] = true;
+ end
+ end
+ end
+
+ local group_ids = {};
+ for group_id, status in pairs(groups) do
+ table.insert(group_ids, group_id);
+ end
+
+ return group_ids;
+end
+
+
+function Group.intersection(self, set_one, set_two)
+ if not set_one or not set_two then
+ return {};
+ end
+
+ local basic_set = {};
+ for index=1, #set_one do
+ basic_set[set_one[index]] = true;
+ end
+
+ local final_set = {};
+ for index=1, #set_two do
+ if basic_set[set_two[index]] then
+ table.insert(final_set, set_two[index]);
+ end
+ end
+
+ return final_set;
+end
diff --git a/misc/freeswitch/scripts/common/log.lua b/misc/freeswitch/scripts/common/log.lua
index 5aff2b8..b7c8d09 100644
--- a/misc/freeswitch/scripts/common/log.lua
+++ b/misc/freeswitch/scripts/common/log.lua
@@ -37,33 +37,33 @@ function Log.message(self, log_level, message_arguments )
end
function Log.console(self, ...)
- self:message(self.level_console, arg);
+ self:message(self.level_console, {...});
end
function Log.alert(self, ...)
- self:message(self.level_alert, arg);
+ self:message(self.level_alert, {...});
end
function Log.critical(self, ...)
- self:message(self.level_critical, arg);
+ self:message(self.level_critical, {...});
end
function Log.error(self, ...)
- self:message(self.level_error, arg);
+ self:message(self.level_error, {...});
end
function Log.warning(self, ...)
- self:message(self.level_warning, arg);
+ self:message(self.level_warning, {...});
end
function Log.notice(self, ...)
- self:message(self.level_notice, arg);
+ self:message(self.level_notice, {...});
end
function Log.info(self, ...)
- self:message(self.level_info, arg);
+ self:message(self.level_info, {...});
end
function Log.debug(self, ...)
- self:message(self.level_debug, arg);
+ self:message(self.level_debug, {...});
end
diff --git a/misc/freeswitch/scripts/common/object.lua b/misc/freeswitch/scripts/common/object.lua
new file mode 100644
index 0000000..8c195e5
--- /dev/null
+++ b/misc/freeswitch/scripts/common/object.lua
@@ -0,0 +1,106 @@
+-- Gemeinschaft 5 module: object class
+-- (c) AMOOMA GmbH 2013
+--
+
+module(...,package.seeall)
+
+Object = {}
+
+-- create object object ;)
+function Object.new(self, arg)
+ arg = arg or {}
+ object = arg.object or {}
+ setmetatable(object, self);
+ self.__index = self;
+ self.class = 'object';
+ self.log = arg.log;
+ self.database = arg.database;
+ return object;
+end
+
+-- find object
+function Object.find(self, attributes)
+ if not attributes.class then
+ return nil;
+ end
+
+ local object = nil;
+
+ require 'common.str';
+ local class = common.str.downcase(attributes.class);
+
+ if class == 'user' then
+ require 'dialplan.user';
+ if tonumber(attributes.id) then
+ object = dialplan.user.User:new{ log = self.log, database = self.database }:find_by_id(attributes.id);
+ elseif not common.str.blank(attributes.uuid) then
+ object = dialplan.user.User:new{ log = self.log, database = self.database }:find_by_uuid(attributes.uuid);
+ end
+
+ if object then
+ object.user_groups = object:list_groups();
+ end
+ elseif class == 'tenant' then
+ require 'dialplan.tenant';
+ if tonumber(attributes.id) then
+ object = dialplan.tenant.Tenant:new{ log = self.log, database = self.database }:find_by_id(attributes.id);
+ elseif not common.str.blank(attributes.uuid) then
+ object = dialplan.tenant.Tenant:new{ log = self.log, database = self.database }:find_by_uuid(attributes.uuid);
+ end
+ elseif class == 'sipaccount' then
+ require 'common.sip_account';
+ if not common.str.blank(attributes.auth_name) then
+ object = common.sip_account.SipAccount:new{ log = self.log, database = self.database }:find_by_auth_name(attributes.auth_name, attributes.domain);
+ elseif tonumber(attributes.id) then
+ object = common.sip_account.SipAccount:new{ log = self.log, database = self.database }:find_by_id(attributes.id);
+ elseif not common.str.blank(attributes.uuid) then
+ object = common.sip_account.SipAccount:new{ log = self.log, database = self.database }:find_by_uuid(attributes.uuid);
+ end
+
+ if object then
+ object.owner = self:find{class = object.record.sip_accountable_type, id = tonumber(object.record.sip_accountable_id)};
+ end
+ elseif class == 'huntgroup' then
+ require 'dialplan.hunt_group';
+
+ if tonumber(attributes.id) then
+ object = dialplan.hunt_group.HuntGroup:new{ log = self.log, database = self.database }:find_by_id(attributes.id);
+ elseif not common.str.blank(attributes.uuid) then
+ object = dialplan.hunt_group.HuntGroup:new{ log = self.log, database = self.database }:find_by_uuid(attributes.uuid);
+ end
+
+ if object then
+ object.owner = self:find{class = 'tenant', id = tonumber(object.record.tenant_id)};
+ end
+ elseif class == 'automaticcalldistributor' then
+ require 'dialplan.acd';
+
+ if tonumber(attributes.id) then
+ object = dialplan.acd.AutomaticCallDistributor:new{ log = self.log, database = self.database, domain = self.domain }:find_by_id(attributes.id);
+ elseif not common.str.blank(attributes.uuid) then
+ object = dialplan.acd.AutomaticCallDistributor:new{ log = self.log, database = self.database, domain = self.domain }:find_by_uuid(attributes.uuid);
+ end
+
+ if object then
+ object.owner = self:find{class = object.record.automatic_call_distributorable_type, id = tonumber(object.record.automatic_call_distributorable_id)};
+ end
+ elseif class == 'faxaccount' then
+ require 'dialplan.fax';
+ if tonumber(attributes.id) then
+ object = dialplan.fax.Fax:new{ log = self.log, database = self.database }:find_by_id(attributes.id);
+ elseif not common.str.blank(attributes.uuid) then
+ object = dialplan.fax.Fax:new{ log = self.log, database = self.database }:find_by_uuid(attributes.uuid);
+ end
+
+ if object then
+ object.owner = self:find{class = object.record.fax_accountable_type, id = tonumber(object.record.fax_accountable_id)};
+ end
+ end
+
+ if object then
+ require 'common.group';
+ object.groups, object.group_ids = common.group.Group:new{ log = self.log, database = self.database }:name_id_by_member(object.id, object.class);
+ end
+
+ return object;
+end
diff --git a/misc/freeswitch/scripts/common/sip_account.lua b/misc/freeswitch/scripts/common/sip_account.lua
index 5b1ea56..6cc7d25 100644
--- a/misc/freeswitch/scripts/common/sip_account.lua
+++ b/misc/freeswitch/scripts/common/sip_account.lua
@@ -16,6 +16,7 @@ function SipAccount.new(self, arg)
self.log = arg.log;
self.database = arg.database;
self.record = arg.record;
+ self.domain = arg.domain;
return object;
end
@@ -128,15 +129,32 @@ function SipAccount.send_text(self, text)
end
-function SipAccount.call_state(self)
- local state = nil
- local sql_query = "SELECT `callstate` FROM `channels` \
- WHERE `name` LIKE (\"\%" .. self.record.auth_name .. "@%\") \
- OR `name` LIKE (\"\%" .. self.record.auth_name .. "@%\") LIMIT 1";
+function SipAccount.call_state(self)
+ local sql_query = 'SELECT `callstate` FROM `detailed_calls` \
+ WHERE `presence_id` LIKE "' .. self.record.auth_name .. '@%" \
+ OR `b_presence_id` LIKE "' .. self.record.auth_name .. '@%" \
+ LIMIT 1';
- self.database:query(sql_query, function(channel_entry)
- state = channel_entry.callstate;
- end)
+ return self.database:query_return_value(sql_query);
+end
+
+
+function SipAccount.call_forwarding_on(self, service, destination, destination_type, timeout, source)
+
+ if not self.call_forwarding then
+ require 'common.call_forwarding';
+ self.call_forwarding = common.call_forwarding.CallForwarding:new{ log = self.log, database = self.database, parent = self, domain = self.domain };
+ end
+
+ return self.call_forwarding:call_forwarding_on(service, destination, destination_type, timeout, source)
+end
+
+
+function SipAccount.call_forwarding_off(self, service, source, delete)
+ if not self.call_forwarding then
+ require 'common.call_forwarding';
+ self.call_forwarding = common.call_forwarding.CallForwarding:new{ log = self.log, database = self.database, parent = self, domain = self.domain };
+ end
- return state;
+ return self.call_forwarding:call_forwarding_off(service, source, delete)
end
diff --git a/misc/freeswitch/scripts/dialplan/dialplan.lua b/misc/freeswitch/scripts/dialplan/dialplan.lua
index 4425f8b..ffad4da 100644
--- a/misc/freeswitch/scripts/dialplan/dialplan.lua
+++ b/misc/freeswitch/scripts/dialplan/dialplan.lua
@@ -144,119 +144,24 @@ function Dialplan.auth_gateway(self)
end
-function Dialplan.object_find(self, class, identifier, auth_name)
- require 'common.str'
- class = common.str.downcase(class);
-
- require 'common.group';
- local group_class = common.group.Group:new{ log = self.log, database = self.database };
-
- if class == 'user' then
- require 'dialplan.user'
- local user = nil;
- if type(identifier) == 'number' then
- user = dialplan.user.User:new{ log = self.log, database = self.database }:find_by_id(identifier);
- else
- user = dialplan.user.User:new{ log = self.log, database = self.database }:find_by_uuid(identifier);
- end
-
- if user then
- user.user_groups = user:list_groups();
- user.groups = group_class:name_id_by_member(user.id, user.class);
- end
-
- return user;
- elseif class == 'tenant' then
- require 'dialplan.tenant'
- local tenant = nil;
- if type(identifier) == 'number' then
- tenant = dialplan.tenant.Tenant:new{ log = self.log, database = self.database }:find_by_id(identifier);
- else
- tenant = dialplan.tenant.Tenant:new{ log = self.log, database = self.database }:find_by_uuid(identifier);
- end
-
- if tenant then
- tenant.groups = group_class:name_id_by_member(tenant.id, tenant.class);
- end
-
- return tenant;
- elseif class == 'sipaccount' then
- require 'common.sip_account'
- local sip_account = nil;
- if auth_name then
- sip_account = common.sip_account.SipAccount:new{ log = self.log, database = self.database }:find_by_auth_name(auth_name, identifier);
- elseif type(identifier) == 'number' then
- sip_account = common.sip_account.SipAccount:new{ log = self.log, database = self.database }:find_by_id(identifier);
- else
- sip_account = common.sip_account.SipAccount:new{ log = self.log, database = self.database }:find_by_uuid(identifier);
- end
- if sip_account then
- sip_account.owner = self:object_find(sip_account.record.sip_accountable_type, tonumber(sip_account.record.sip_accountable_id));
- sip_account.groups = group_class:name_id_by_member(sip_account.id, sip_account.class);
- end
- return sip_account;
- elseif class == 'huntgroup' then
- require 'dialplan.hunt_group'
-
- local hunt_group = nil;
- if type(identifier) == 'number' then
- hunt_group = dialplan.hunt_group.HuntGroup:new{ log = self.log, database = self.database }:find_by_id(identifier);
- else
- hunt_group = dialplan.hunt_group.HuntGroup:new{ log = self.log, database = self.database }:find_by_uuid(identifier);
- end
-
- if hunt_group then
- hunt_group.owner = self:object_find('tenant', tonumber(hunt_group.record.tenant_id));
- hunt_group.groups = group_class:name_id_by_member(hunt_group.id, hunt_group.class);
- end
-
- return hunt_group;
- elseif class == 'automaticcalldistributor' then
- require 'dialplan.acd'
-
- local acd = nil;
- if type(identifier) == 'number' then
- acd = dialplan.acd.AutomaticCallDistributor:new{ log = self.log, database = self.database, domain = self.domain }:find_by_id(identifier);
- else
- acd = dialplan.acd.AutomaticCallDistributor:new{ log = self.log, database = self.database, domain = self.domain }:find_by_uuid(identifier);
- end
-
- if acd then
- acd.owner = self:object_find(acd.record.automatic_call_distributorable_type, tonumber(acd.record.automatic_call_distributorable_id));
- acd.groups = group_class:name_id_by_member(acd.id, acd.class);
- end
-
- return acd;
- elseif class == 'faxaccount' then
- require 'dialplan.fax'
- local fax_account = nil;
- if type(identifier) == 'number' then
- fax_account = dialplan.fax.Fax:new{ log = self.log, database = self.database }:find_by_id(identifier);
- else
- fax_account = dialplan.fax.Fax:new{ log = self.log, database = self.database }:find_by_uuid(identifier);
- end
- if fax_account then
- fax_account.owner = self:object_find(fax_account.record.fax_accountable_type, tonumber(fax_account.record.fax_accountable_id));
- fax_account.groups = group_class:name_id_by_member(fax_account.id, fax_account.class);
- end
-
- return fax_account;
- end
+function Dialplan.object_find(self, arguments)
+ require 'common.object';
+ return common.object.Object:new{ log = self.log, database = self.database}:find(arguments);
end
function Dialplan.retrieve_caller_data(self)
require 'common.str'
- self.caller.caller_phone_numbers_hash = {}
+ self.caller.caller_phone_numbers_hash = {};
-- TODO: Set auth_account on transfer initiated by calling party
if not common.str.blank(self.caller.dialed_sip_user) then
- self.caller.auth_account = self:object_find('sipaccount', self.caller.dialed_domain, self.caller.dialed_sip_user);
+ self.caller.auth_account = self:object_find{class = 'sipaccount', domain = self.caller.dialed_domain, auth_account = self.caller.dialed_sip_user};
if self.caller.set_auth_account then
self.caller:set_auth_account(self.caller.auth_account);
end
elseif not common.str.blank(self.caller.auth_account_type) and not common.str.blank(self.caller.auth_account_uuid) then
- self.caller.auth_account = self:object_find(self.caller.auth_account_type, self.caller.auth_account_uuid);
+ self.caller.auth_account = self:object_find{class = self.caller.auth_account_type, uuid = self.caller.auth_account_uuid};
if self.caller.set_auth_account then
self.caller:set_auth_account(self.caller.auth_account);
end
@@ -274,7 +179,7 @@ function Dialplan.retrieve_caller_data(self)
end
if not common.str.blank(self.caller.account_type) and not common.str.blank(self.caller.account_uuid) then
- self.caller.account = self:object_find(self.caller.account_type, self.caller.account_uuid);
+ self.caller.account = self:object_find{class = self.caller.account_type, uuid = self.caller.account_uuid};
if self.caller.account then
require 'common.phone_number'
self.caller.caller_phone_numbers = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database }:list_by_owner(self.caller.account.id, self.caller.account.class);
@@ -329,6 +234,7 @@ function Dialplan.destination_new(self, arg)
destination.id = common.str.to_i(destination.phone_number.record.phone_numberable_id);
destination.uuid = common.str.to_s(destination.phone_number.record.phone_numberable_uuid);
destination.node_id = common.str.to_i(destination.phone_number.record.gs_node_id);
+ 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 }
@@ -388,7 +294,7 @@ function Dialplan.dial(self, destination)
if destination.node_local and destination.type == 'sipaccount' then
destination.pickup_groups = {};
- destination.account = self:object_find(destination.type, destination.id);
+ destination.account = self:object_find{class = destination.type, id = destination.id};
if destination.account then
if destination.account.class == 'sipaccount' then
destination.callee_id_name = destination.account.record.caller_name;
@@ -407,7 +313,7 @@ function Dialplan.dial(self, destination)
if destination.account.owner.class == 'user' then
user_id = destination.account.owner.id;
tenant_id = tonumber(destination.account.owner.record.current_tenant_id);
- local user = self:object_find(destination.account.owner.class, tonumber(user_id));
+ local user = self:object_find{class = destination.account.owner.class, id = tonumber(user_id)};
elseif destination.account.owner.class == 'tenant' then
tenant_id = destination.account.owner.id;
end
@@ -417,22 +323,21 @@ function Dialplan.dial(self, destination)
if not self.caller.clir then
if user_id or tenant_id then
require 'common.str'
- local phone_book_entry = nil;
if self.phonebook_number_lookup then
require 'dialplan.phone_book'
- phone_book_entry = dialplan.phone_book.PhoneBook:new{ log = self.log, database = self.database }:find_entry_by_number_user_tenant(self.caller.caller_phone_numbers, user_id, tenant_id);
+ self.caller.phone_book_entry = dialplan.phone_book.PhoneBook:new{ log = self.log, database = self.database }:find_entry_by_number_user_tenant(self.caller.caller_phone_numbers, user_id, tenant_id);
end
- if phone_book_entry then
- self.log:info('PHONE_BOOK_ENTRY - phone_book=', phone_book_entry.phone_book_id, ' (', phone_book_entry.phone_book_name, '), caller_id_name: ', phone_book_entry.caller_id_name, ', ringtone: ', phone_book_entry.bellcore_id);
- destination.caller_id_name = common.str.to_ascii(phone_book_entry.caller_id_name);
- if tonumber(phone_book_entry.bellcore_id) then
- self.log:debug('RINGTONE - phonebookentry, index: ', phone_book_entry.bellcore_id);
- self.caller:export_variable('alert_info', 'http://amooma.de;info=Ringer' .. phone_book_entry.bellcore_id .. ';x-line-id=0');
+ if self.caller.phone_book_entry then
+ self.log:info('PHONE_BOOK_ENTRY - phone_book=', self.caller.phone_book_entry.phone_book_id, ' (', self.caller.phone_book_entry.phone_book_name, '), caller_id_name: ', self.caller.phone_book_entry.caller_id_name, ', ringtone: ', self.caller.phone_book_entry.bellcore_id);
+ destination.caller_id_name = common.str.to_ascii(self.caller.phone_book_entry.caller_id_name);
+ if tonumber(self.caller.phone_book_entry.bellcore_id) then
+ self.log:debug('RINGTONE - phonebookentry=', self.caller.phone_book_entry.id, ', ringtone: ', self.caller.phone_book_entry.bellcore_id);
+ self.caller:export_variable('alert_info', 'http://amooma.de;info=Ringer' .. self.caller.phone_book_entry.bellcore_id .. ';x-line-id=0');
end
- if phone_book_entry.image then
- self:set_caller_picture(phone_book_entry.id, 'phonebookentry', phone_book_entry.image);
+ if self.caller.phone_book_entry.image then
+ self:set_caller_picture(self.caller.phone_book_entry.id, 'phonebookentry', self.caller.phone_book_entry.image);
elseif self.caller.account and self.caller.account.owner then
self:set_caller_picture(self.caller.account.owner.id, self.caller.account.owner.class);
end
@@ -483,7 +388,7 @@ end
function Dialplan.huntgroup(self, destination)
- local hunt_group = self:object_find('huntgroup', tonumber(destination.id));
+ local hunt_group = self:object_find{class = 'huntgroup', id = tonumber(destination.id)};
if not hunt_group then
self.log:error('DIALPLAN_HUNTGROUP - huntgroup not found');
@@ -514,7 +419,7 @@ end
function Dialplan.acd(self, destination)
- local acd = self:object_find('automaticcalldistributor', tonumber(destination.id));
+ local acd = self:object_find{class = 'automaticcalldistributor', id = tonumber(destination.id)};
if not acd then
self.log:error('DIALPLAN_ACD - acd not found');
@@ -623,7 +528,7 @@ function Dialplan.callthrough(self, destination)
end
if type(authorization) == 'table' and tonumber(authorization.sip_account_id) and tonumber(authorization.sip_account_id) > 0 then
- local auth_account = self:object_find('sipaccount', tonumber(authorization.sip_account_id));
+ local auth_account = self:object_find{class = 'sipaccount', id = tonumber(authorization.sip_account_id)};
self.caller.forwarding_number = destination.number;
self.caller.forwarding_service = 'callthrough';
self.caller:set_variable('gs_forwarding_service', self.caller.forwarding_service);
@@ -699,7 +604,7 @@ end
function Dialplan.dialplanfunction(self, destination)
require 'dialplan.functions'
- return dialplan.functions.Functions:new{ log = self.log, database = self.database, domain = self.domain }:dialplan_function(self.caller, destination.number);
+ return dialplan.functions.Functions:new{ log = self.log, database = self.database, domain = self.domain, parent = self }:dialplan_function(self.caller, destination.number);
end
@@ -744,10 +649,17 @@ function Dialplan.switch(self, destination)
self.caller:export_variable('alert_info', self.default_ringtone);
if destination.phone_number then
- local ringtone = destination.phone_number:ringtone();
- if ringtone and ringtone.bellcore_id then
- self.log:debug('RINGTONE - ', ringtone.ringtoneable_type .. ', index: ' .. ringtone.bellcore_id);
- self.caller:export_variable('alert_info', 'http://amooma.de;info=Ringer' .. tonumber(ringtone.bellcore_id) .. ';x-line-id=0');
+ destination.ringtone = destination.phone_number:ringtone();
+ end
+
+ if not destination.ringtone and destination.account and destination.account.ringtone then
+ destination.ringtone = destination.account:ringtone();
+ end
+
+ if destination.ringtone then
+ if destination.ringtone.bellcore_id then
+ self.log:debug('DESTINATION_RINGTONE - ', destination.ringtone.ringtoneable_type .. '=', destination.ringtone.ringtoneable_id, ', ringtone: ' .. destination.ringtone.bellcore_id);
+ self.caller:export_variable('alert_info', 'http://amooma.de;info=Ringer' .. tonumber(destination.ringtone.bellcore_id) .. ';x-line-id=0');
end
end
@@ -821,10 +733,10 @@ function Dialplan.switch(self, destination)
if user_id or tenant_id then
require 'dialplan.phone_book'
- local phone_book_entry = dialplan.phone_book.PhoneBook:new{ log = self.log, database = self.database }:find_entry_by_number_user_tenant({ destination.number }, user_id, tenant_id);
- if phone_book_entry then
- self.log:info('PHONE_BOOK_ENTRY - phone_book=', phone_book_entry.phone_book_id, ' (', phone_book_entry.phone_book_name, '), callee_id_name: ', common.str.to_ascii(phone_book_entry.caller_id_name));
- destination.callee_id_name = common.str.to_ascii(phone_book_entry.caller_id_name);
+ self.caller.callee_phone_book_entry = dialplan.phone_book.PhoneBook:new{ log = self.log, database = self.database }:find_entry_by_number_user_tenant({ destination.number }, user_id, tenant_id);
+ if self.caller.callee_phone_book_entry then
+ self.log:info('PHONE_BOOK_ENTRY - phone_book=', self.caller.callee_phone_book_entry.phone_book_id, ' (', self.caller.callee_phone_book_entry.phone_book_name, '), callee_id_name: ', common.str.to_ascii(self.caller.callee_phone_book_entry.caller_id_name));
+ destination.callee_id_name = common.str.to_ascii(self.caller.callee_phone_book_entry.caller_id_name);
end
end
end
@@ -1005,7 +917,7 @@ function Dialplan.run(self, destination)
', destination: ', result.call_forwarding.type, '=', result.call_forwarding.id,
', number: ', result.call_forwarding.number);
- local auth_account = self:object_find(destination.type, destination.id);
+ local auth_account = self:object_find{class = destination.type, id = destination.id};
self.caller.forwarding_number = destination.number;
self.caller.forwarding_service = result.call_forwarding.service;
self.caller:set_variable('gs_forwarding_service', self.caller.forwarding_service);
diff --git a/misc/freeswitch/scripts/dialplan/functions.lua b/misc/freeswitch/scripts/dialplan/functions.lua
index 3706872..780e3be 100644
--- a/misc/freeswitch/scripts/dialplan/functions.lua
+++ b/misc/freeswitch/scripts/dialplan/functions.lua
@@ -37,6 +37,8 @@ function Functions.dialplan_function(self, caller, dialed_number)
result = self:transfer_all(caller, parameters[3]);
elseif fid == "ia" then
result = self:intercept_any_number(caller, parameters[3]);
+ elseif fid == "ig" then
+ result = self:group_pickup(caller, parameters[3]);
elseif fid == "anc" then
result = self:account_node_change(caller);
elseif fid == "li" then
@@ -159,12 +161,27 @@ function Functions.intercept_any_number(self, caller, destination_number)
return { continue = false, code = 404, phrase = 'Number not found', no_cdr = true };
end
- if not phone_number.record.phone_numberable_type:lower() == 'sipaccount' or not tonumber(phone_number.record.phone_numberable_id) then
- self.log:notice('FUNCTION_INTERCEPT_ANY_NUMBER - destination: ', phone_number.record.phone_numberable_type:lower(), '=', phone_number.record.phone_numberable_id, ', number: ', destination_number);
- return { continue = false, code = 505, phrase = 'Incompatible destination', no_cdr = true };
+ require 'common.object';
+ local phone_numberable = common.object.Object:new{ log = self.log, database = self.database}:find{class = phone_number.record.phone_numberable_type, id = phone_number.record.phone_numberable_id};
+
+ if not phone_numberable then
+ self.log:notice('FUNCTION_INTERCEPT_ANY_NUMBER - numberable not found: ', dphone_number.record.phone_numberable_type, '=', phone_number.record.phone_numberable_id);
+ return { continue = false, code = 404, phrase = 'Destination not found', no_cdr = true };
+ end
+
+ require 'common.str';
+ require 'common.group';
+ local group_class = common.group.Group:new{ log = self.log, database = self.database };
+ local group_ids = group_class:union(common.str.try(caller, 'auth_account.group_ids'), common.str.try(caller, 'auth_account.owner.group_ids'));
+ local target_groups, target_group_ids = group_class:permission_targets(group_ids, 'pickup');
+ local destination_group_ids = group_class:union(common.str.try(phone_numberable, 'group_ids'), common.str.try(phone_numberable, 'owner.group_ids'));
+
+ if #group_class:intersection(destination_group_ids, target_group_ids) == 0 then
+ self.log:notice('FUNCTION_INTERCEPT_ANY_NUMBER - Groups not found or insufficient permissions');
+ return { continue = false, code = 402, phrase = '"Insufficient permissions', no_cdr = true };
end
- self.log:info('FUNCTION_INTERCEPT_ANY_NUMBER intercepting call - to: ', phone_number.record.phone_numberable_type:lower(), '=', phone_number.record.phone_numberable_id, ', number: ', destination_number);
+ self.log:info('FUNCTION_INTERCEPT_ANY_NUMBER intercepting call - to: ', phone_numberable.class, '=',phone_numberable.id, '|', destination_number);
caller:set_variable('gs_pickup_group_pick', 's' .. phone_number.record.phone_numberable_id);
caller:execute('pickup', 's' .. phone_number.record.phone_numberable_id);
@@ -173,6 +190,31 @@ function Functions.intercept_any_number(self, caller, destination_number)
end
+function Functions.group_pickup(self, caller, group_id)
+ if not tonumber(group_id) then
+ return { continue = false, code = 505, phrase = 'Incompatible destination', no_cdr = true };
+ end
+
+ require 'common.str';
+ require 'common.group';
+ local group_class = common.group.Group:new{ log = self.log, database = self.database };
+ local group_ids = group_class:union(common.str.try(caller, 'auth_account.group_ids'), common.str.try(caller, 'auth_account.owner.group_ids'));
+ local target_group = group_class:is_target(group_id, 'pickup');
+
+ if not target_group then
+ self.log:notice('FUNCTION_GROUP_PICKUP - group=', group_id, ' not found or insufficient permissions');
+ return { continue = false, code = 402, phrase = '"Insufficient permissions', no_cdr = true };
+ end
+
+ self.log:notice('FUNCTION_GROUP_PICKUP - group=', group_id, '|', target_group);
+
+ caller:set_variable('gs_pickup_group_pick', 'g' .. group_id);
+ caller:execute('pickup', 'g' .. group_id);
+
+ return { continue = false, code = 200, phrase = 'OK', no_cdr = true }
+end
+
+
function Functions.account_node_change(self, caller)
self.log:info('NODE_CHANGE - caller: ', caller.account_type, '/', caller.account_uuid, ', caller_id: ', caller.caller_id_number);
@@ -618,32 +660,17 @@ function Functions.clip_off(self, caller)
end
function Functions.call_forwarding_off(self, caller, call_forwarding_service, delete)
- local defaults = {log = self.log, database = self.database, domain = caller.domain}
-
-- Find caller's SipAccount
local caller_sip_account = self:ensure_caller_sip_account(caller);
if not caller_sip_account then
return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true }
end
- require 'common.phone_number'
- local phone_number_class = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database, domain = caller.domain };
- local phone_numbers = phone_number_class:list_by_owner(caller_sip_account.record.id, 'SipAccount');
+ caller_sip_account.domain = caller_sip_account.domain or caller.domain;
- local success = false;
- for index, phone_number in pairs(phone_numbers) do
- phone_number_object = phone_number_class:find_by_number(phone_number);
- if phone_number_object then
- if phone_number_object:call_forwarding_off(call_forwarding_service, nil, delete) then
- success = true;
- end
- end
- end
-
- if not success then
- self.log:notice("call forwarding could not be deactivated");
+ if not caller_sip_account:call_forwarding_off(call_forwarding_service, nil, delete) then
+ self.log:notice('FUNCTION_CALL_FORWARDING_OFF - call forwarding could not be deactivated');
return { continue = false, code = 500, phrase = 'Call Forwarding could not be deactivated', no_cdr = true }
-
end
caller:answer();
@@ -654,10 +681,8 @@ end
function Functions.call_forwarding_on(self, caller, call_forwarding_service, destination, destination_type, timeout)
- local defaults = {log = self.log, database = self.database, domain = caller.domain}
-
if not call_forwarding_service then
- self.log:notice('no call forwarding service specified');
+ self.log:notice('FUNCTION_CALL_FORWARDING_ON - no call forwarding service specified');
end
-- Find caller's SipAccount
@@ -666,24 +691,11 @@ function Functions.call_forwarding_on(self, caller, call_forwarding_service, des
return { continue = false, code = 403, phrase = 'Incompatible caller', no_cdr = true }
end
- require "common.phone_number"
- local phone_number_class = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database, domain = caller.domain };
- local phone_numbers = phone_number_class:list_by_owner(caller_sip_account.record.id, 'SipAccount');
+ caller_sip_account.domain = caller_sip_account.domain or caller.domain;
- local success = false;
- for index, phone_number in pairs(phone_numbers) do
- phone_number_object = phone_number_class:find_by_number(phone_number);
- if phone_number_object then
- if phone_number_object:call_forwarding_on(call_forwarding_service, destination, timeout) then
- success = true;
- end
- end
- end
-
- if not success then
- self.log:notice("call forwarding could not be activated");
+ if not caller_sip_account:call_forwarding_on(call_forwarding_service, destination, destination_type, timeout) then
+ self.log:notice('FUNCTION_CALL_FORWARDING_ON - call forwarding could not be activated');
return { continue = false, code = 500, phrase = 'Call Forwarding could not be activated', no_cdr = true }
-
end
caller:answer();
diff --git a/misc/freeswitch/scripts/dialplan/router.lua b/misc/freeswitch/scripts/dialplan/router.lua
index bda80a7..8473c2b 100644
--- a/misc/freeswitch/scripts/dialplan/router.lua
+++ b/misc/freeswitch/scripts/dialplan/router.lua
@@ -8,6 +8,7 @@ Router = {}
-- create route object
function Router.new(self, arg)
+ require 'common.str';
arg = arg or {}
object = arg.object or {}
setmetatable(object, self);
diff --git a/misc/freeswitch/scripts/dialplan_default.lua b/misc/freeswitch/scripts/dialplan_default.lua
index 8fb9057..2b651c5 100644
--- a/misc/freeswitch/scripts/dialplan_default.lua
+++ b/misc/freeswitch/scripts/dialplan_default.lua
@@ -8,7 +8,7 @@ function hangup_hook_caller(s, status, arg)
if tostring(status) == 'transfer' then
if start_caller and start_caller.destination then
log:info('CALL_TRANSFERRED - destination was: ', start_caller.destination.type, '=', start_caller.destination.id,', number: ' .. tostring(start_caller.destination.number) .. ', to: ' .. start_caller:to_s('sip_refer_to'));
- start_caller.auth_account = start_caller.dialplan:object_find(start_caller.destination.type, start_caller.destination.id);
+ start_caller.auth_account = start_caller.dialplan:object_find{class = start_caller.destination.type, id = start_caller.destination.id};
start_caller.forwarding_number = start_caller.destination.number;
start_caller.forwarding_service = 'transfer';
end