diff options
Diffstat (limited to 'misc/freeswitch')
-rw-r--r-- | misc/freeswitch/scripts/common/call_forwarding.lua | 185 | ||||
-rw-r--r-- | misc/freeswitch/scripts/common/database.lua | 2 | ||||
-rw-r--r-- | misc/freeswitch/scripts/common/group.lua | 84 | ||||
-rw-r--r-- | misc/freeswitch/scripts/common/log.lua | 16 | ||||
-rw-r--r-- | misc/freeswitch/scripts/common/object.lua | 106 | ||||
-rw-r--r-- | misc/freeswitch/scripts/common/sip_account.lua | 36 | ||||
-rw-r--r-- | misc/freeswitch/scripts/dialplan/dialplan.lua | 166 | ||||
-rw-r--r-- | misc/freeswitch/scripts/dialplan/functions.lua | 94 | ||||
-rw-r--r-- | misc/freeswitch/scripts/dialplan/router.lua | 1 | ||||
-rw-r--r-- | misc/freeswitch/scripts/dialplan_default.lua | 2 |
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 |