diff options
Diffstat (limited to 'misc')
-rw-r--r-- | misc/freeswitch/conf/freeswitch.xml | 16 | ||||
-rw-r--r-- | misc/freeswitch/scripts/common/call_forwarding.lua | 55 | ||||
-rw-r--r-- | misc/freeswitch/scripts/common/conference.lua | 6 | ||||
-rw-r--r-- | misc/freeswitch/scripts/common/group.lua | 88 | ||||
-rw-r--r-- | misc/freeswitch/scripts/common/phone_number.lua | 55 | ||||
-rw-r--r-- | misc/freeswitch/scripts/common/str.lua | 34 | ||||
-rw-r--r-- | misc/freeswitch/scripts/configuration.lua | 3 | ||||
-rw-r--r-- | misc/freeswitch/scripts/dialplan/call_parking.lua | 16 | ||||
-rw-r--r-- | misc/freeswitch/scripts/dialplan/dialplan.lua | 136 | ||||
-rw-r--r-- | misc/freeswitch/scripts/dialplan/dtmf.lua | 8 | ||||
-rw-r--r-- | misc/freeswitch/scripts/dialplan/fax.lua | 8 | ||||
-rw-r--r-- | misc/freeswitch/scripts/dialplan/functions.lua | 141 | ||||
-rw-r--r-- | misc/freeswitch/scripts/dialplan/sip_call.lua | 26 | ||||
-rw-r--r-- | misc/freeswitch/scripts/dialplan/user.lua | 17 | ||||
-rw-r--r-- | misc/freeswitch/scripts/dialplan/voicemail.lua | 2 | ||||
-rw-r--r-- | misc/freeswitch/scripts/send_fax.lua | 5 |
16 files changed, 410 insertions, 206 deletions
diff --git a/misc/freeswitch/conf/freeswitch.xml b/misc/freeswitch/conf/freeswitch.xml index 4969b07..f72651f 100644 --- a/misc/freeswitch/conf/freeswitch.xml +++ b/misc/freeswitch/conf/freeswitch.xml @@ -27,6 +27,14 @@ </match> </input> </macro> + <macro name="voicemail_change_pass_success"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-password_has_been_changed.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> <macro name="voicemail_fail_auth"> <input pattern="(.*)"> <match> @@ -536,6 +544,14 @@ </match> </input> </macro> + <macro name="voicemail_change_pass_success"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-password_has_been_changed.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> <macro name="voicemail_fail_auth"> <input pattern="(.*)"> <match> diff --git a/misc/freeswitch/scripts/common/call_forwarding.lua b/misc/freeswitch/scripts/common/call_forwarding.lua index 400fcde..192c694 100644 --- a/misc/freeswitch/scripts/common/call_forwarding.lua +++ b/misc/freeswitch/scripts/common/call_forwarding.lua @@ -37,6 +37,61 @@ function CallForwarding.find_by_id(self, id) return nil end + +function CallForwarding.list_by_owner(self, call_forwardable_id, call_forwardable_type, caller_ids) + require 'common.str'; + + if not tonumber(call_forwardable_id) or common.str.blank(call_forwardable_type) then + return {}; + end + + local sql_query = 'SELECT \ + `a`.`destination` AS `number`, \ + `a`.`destinationable_id` AS `id`, \ + `a`.`destinationable_type` AS `type`, \ + `a`.`call_forwardable_id`, \ + `a`.`call_forwardable_type`, \ + `a`.`timeout`, `a`.`depth`, \ + `a`.`source`, \ + `b`.`value` AS `service` \ + FROM `call_forwards` `a` JOIN `call_forward_cases` `b` ON `a`.`call_forward_case_id` = `b`.`id` \ + WHERE `a`.`call_forwardable_id`= ' .. tonumber(call_forwardable_id) .. ' \ + AND `a`.`call_forwardable_type`= ' .. self.database:escape(call_forwardable_type, '"') .. ' \ + AND `a`.`active` IS TRUE'; + + local call_forwarding_entries = {}; + + self.database:query(sql_query, function(forwarding_entry) + local entry_match = false; + + if common.str.blank(forwarding_entry.source) then + entry_match = true; + 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] ); + break; + end + end + end + end + + if entry_match then + call_forwarding_entries[forwarding_entry.service] = forwarding_entry; + self.log:debug('CALL_FORWARDING - ', call_forwardable_type, '=', call_forwardable_id, + ', service: ', forwarding_entry.service, + ', destination: ',forwarding_entry.type, '=', forwarding_entry.id, + ', number: ', forwarding_entry.number); + end + end) + + return call_forwarding_entries; +end + + function CallForwarding.presence_set(self, presence_state) require 'dialplan.presence' local presence = dialplan.presence.Presence:new(); diff --git a/misc/freeswitch/scripts/common/conference.lua b/misc/freeswitch/scripts/common/conference.lua index f6a4d87..b56cba9 100644 --- a/misc/freeswitch/scripts/common/conference.lua +++ b/misc/freeswitch/scripts/common/conference.lua @@ -159,7 +159,7 @@ function Conference.enter(self, caller, domain) caller:sleep(1000); caller.session:sayPhrase('conference_welcome'); - if pin and pin ~= "" then + if not common.str.blank(pin) then local digits = ""; for i = 1, 3, 1 do if digits == pin then @@ -167,7 +167,7 @@ function Conference.enter(self, caller, domain) elseif digits ~= "" then caller.session:sayPhrase('conference_bad_pin'); end - digits = caller.session:read(PIN_LENGTH_MIN, PIN_LENGTH_MAX, 'conference/conf-enter_conf_pin.wav', PIN_TIMEOUT, '#'); + digits = caller.session:read(PIN_LENGTH_MIN, PIN_LENGTH_MAX, 'conference/conf-pin.wav', PIN_TIMEOUT, '#'); end if digits ~= pin then caller.session:sayPhrase('conference_goodbye'); @@ -191,7 +191,7 @@ function Conference.enter(self, caller, domain) -- Record caller's name if common.str.to_b(self.record.announce_new_member_by_name) or common.str.to_b(self.record.announce_left_member_by_name) then local uid = session:get_uuid(); - name_file = "/tmp/conference_caller_name_" .. uid .. ".wav"; + name_file = "/var/spool/freeswitch/conference_caller_name_" .. uid .. ".wav"; caller.session:sayPhrase('conference_record_name'); session:recordFile(name_file, ANNOUNCEMENT_MAX_LEN, ANNOUNCEMENT_SILENCE_THRESHOLD, ANNOUNCEMENT_SILENCE_LEN); caller.session:streamFile(name_file); diff --git a/misc/freeswitch/scripts/common/group.lua b/misc/freeswitch/scripts/common/group.lua new file mode 100644 index 0000000..c4125bc --- /dev/null +++ b/misc/freeswitch/scripts/common/group.lua @@ -0,0 +1,88 @@ +-- Gemeinschaft 5 module: group class +-- (c) AMOOMA GmbH 2013 +-- + +module(...,package.seeall) + +Group = {} + +MAX_GROUP_MEMBERSHIPS = 256; + +-- create group object +function Group.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'group'; + self.log = arg.log; + self.database = arg.database; + return object; +end + +-- find group by id +function Group.find_by_id(self, id) + local sql_query = 'SELECT * FROM `groups` WHERE `id`= ' .. tonumber(id) .. ' LIMIT 1'; + local group = nil; + + self.database:query(sql_query, function(account_entry) + group = Group:new(self); + group.record = account_entry; + group.id = tonumber(account_entry.id); + group.name = account_entry.name; + end); + + return group; +end + +-- list groups by member permissions +function Group.name_id_by_permission(self, member_id, member_type, permission) + if not tonumber(member_id) then + return {}; + end + + local sql_query = 'SELECT DISTINCT `c`.`id`, `c`.`name` \ + FROM `group_permissions` `a` \ + JOIN `group_memberships` `b` ON `a`.`target_group_id` = `b`.`group_id` \ + JOIN `groups` `c` ON `c`.`id` = `b`.`group_id` \ + WHERE `b`.`item_type` = ' .. self.database:escape(member_type, '"') .. ' \ + AND `b`.`item_id` = ' .. member_id .. ' \ + AND `a`.`permission` = ' .. self.database:escape(permission, '"') .. ' \ + AND `c`.`active` IS TRUE \ + GROUP BY `b`.`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 + +-- list groups by member +function Group.name_id_by_member(self, member_id, member_type) + if not tonumber(member_id) then + return {}; + end + + local sql_query = 'SELECT DISTINCT `c`.`id`, `c`.`name` \ + FROM `group_memberships` `b` \ + JOIN `groups` `c` ON `c`.`id` = `b`.`group_id` \ + WHERE `b`.`item_type` = ' .. self.database:escape(member_type, '"') .. ' \ + AND `b`.`item_id` = ' .. member_id .. ' \ + AND `c`.`active` IS TRUE \ + GROUP BY `b`.`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 diff --git a/misc/freeswitch/scripts/common/phone_number.lua b/misc/freeswitch/scripts/common/phone_number.lua index 6635296..d6ee42a 100644 --- a/misc/freeswitch/scripts/common/phone_number.lua +++ b/misc/freeswitch/scripts/common/phone_number.lua @@ -55,6 +55,8 @@ function PhoneNumber.find_by_number(self, number, phone_numberable_types) self.database:query(sql_query, function(number_entry) phone_number = PhoneNumber:new(self); phone_number.record = number_entry; + phone_number.id = tonumber(number_entry.id); + phone_number.uuid = number_entry.uuid; end) return phone_number; @@ -68,6 +70,8 @@ function PhoneNumber.find_all_by_owner(self, owner_id, owner_type) self.database:query(sql_query, function(number_entry) phone_numbers[tonumber(number_entry.id)] = PhoneNumber:new(self); phone_numbers[tonumber(number_entry.id)].record = number_entry; + phone_numbers[tonumber(number_entry.id)].id = tonumber(number_entry.id); + phone_numbers[tonumber(number_entry.id)].uuid = number_entry.uuid; end) return phone_numbers; @@ -94,57 +98,6 @@ function PhoneNumber.list_by_same_owner(self, number, owner_types) end end --- Retrieve call forwarding -function PhoneNumber.call_forwarding(self, caller_ids) - require 'common.str' - - sources = sources or {}; - table.insert(sources, ''); - - local sql_query = 'SELECT \ - `a`.`destination` AS `number`, \ - `a`.`call_forwardable_id` AS `id`, \ - `a`.`call_forwardable_type` AS `type`, \ - `a`.`timeout`, `a`.`depth`, \ - `a`.`source`, \ - `b`.`value` AS `service` \ - FROM `call_forwards` `a` JOIN `call_forward_cases` `b` ON `a`.`call_forward_case_id` = `b`.`id` \ - WHERE `a`.`phone_number_id`= ' .. tonumber(self.record.id) .. ' \ - AND `a`.`active` IS TRUE'; - - local call_forwarding = {} - - self.database:query(sql_query, function(forwarding_entry) - local entry_match = false; - - if common.str.blank(forwarding_entry.source) then - entry_match = true; - else - local sources = common.str.strip_to_a(forwarding_entry.source, ',') - for index, source in ipairs(sources) do - for index, caller_id in ipairs(caller_ids) do - if caller_id:match(source) then - entry_match = true; - self.log:debug('CALL_FORWARDING_GET - source match: ', source, ' ~ ', caller_id ); - break; - end - end - end - end - - if entry_match then - call_forwarding[forwarding_entry.service] = forwarding_entry; - self.log:debug('CALL_FORWARDING_GET - PhoneNumber=', self.record.id, '/', self.record.uuid, '@', self.record.gs_node_id, - ', number: ', self.record.number, - ', service: ', forwarding_entry.service, - ', destination: ',forwarding_entry.type, '=', forwarding_entry.id, - ', number: ', forwarding_entry.number); - end - end) - - return call_forwarding; -end - function PhoneNumber.call_forwarding_effective(self, service, source) local conditions = {} diff --git a/misc/freeswitch/scripts/common/str.lua b/misc/freeswitch/scripts/common/str.lua index 72ff388..541199f 100644 --- a/misc/freeswitch/scripts/common/str.lua +++ b/misc/freeswitch/scripts/common/str.lua @@ -146,3 +146,37 @@ end function blank(value) return (value == nil or to_s(value) == ''); end + +-- concatenate string to buffer +function append(buffer, value, separator, prefix, suffix) + separator = separator or ''; + prefix = prefix or ''; + suffix = suffix or ''; + if buffer ~= '' then + buffer = buffer .. separator .. prefix .. value .. suffix; + else + buffer = prefix .. value .. suffix; + end + + return buffer; +end + +-- concatenate array values to string +function concat(array, separator, prefix, suffix) + local buffer = ''; + for key, value in pairs(array) do + buffer = append(buffer, value, separator, prefix, suffix); + end + + return buffer; +end + +-- concatenate array keys to string +function concat_keys(array, separator, prefix, suffix) + local buffer = ''; + for key, value in pairs(array) do + buffer = append(buffer, key, separator, prefix, suffix); + end + + return buffer; +end diff --git a/misc/freeswitch/scripts/configuration.lua b/misc/freeswitch/scripts/configuration.lua index 75d0df3..6660c3d 100644 --- a/misc/freeswitch/scripts/configuration.lua +++ b/misc/freeswitch/scripts/configuration.lua @@ -79,6 +79,9 @@ function profile(database, sofia_ini, profile_name, index, domains, node_id) parameters['odbc-dsn'] = 'gemeinschaft:' .. tostring(database.user_name) .. ':' .. tostring(database.password); end + parameters['username'] = parameters['username'] or 'Gemeinschaft'; + parameters['user-agent-string'] = parameters['user-agent-string'] or 'Gemeinschaft'; + -- set local bind address if domains[index] then parameters['sip-ip'] = domains[index]['host']; diff --git a/misc/freeswitch/scripts/dialplan/call_parking.lua b/misc/freeswitch/scripts/dialplan/call_parking.lua index cc2cf4b..e51eb16 100644 --- a/misc/freeswitch/scripts/dialplan/call_parking.lua +++ b/misc/freeswitch/scripts/dialplan/call_parking.lua @@ -39,6 +39,22 @@ function CallParking.find_by_name(self, name) end +function CallParking.find_by_owner(self, owner_id, owner_type) + local sql_query = 'SELECT * FROM `parking_stalls` WHERE `parking_stallable_id` = '.. owner_id .. ' AND `parking_stallable_type` = "' .. owner_type .. '" ORDER BY `name`'; + local parking_stalls = {}; + + self.database:query(sql_query, function(entry) + local parking_stall = CallParking:new(self); + parking_stall.record = entry; + parking_stall.id = tonumber(entry.id); + parking_stall.name = entry.name; + table.insert(parking_stalls, parking_stall) + end) + + return parking_stalls; +end + + function CallParking.list_occupied(self, lot) lot = lot or self.lot; diff --git a/misc/freeswitch/scripts/dialplan/dialplan.lua b/misc/freeswitch/scripts/dialplan/dialplan.lua index b27bb9d..7d9ac58 100644 --- a/misc/freeswitch/scripts/dialplan/dialplan.lua +++ b/misc/freeswitch/scripts/dialplan/dialplan.lua @@ -148,6 +148,9 @@ 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; @@ -158,7 +161,8 @@ function Dialplan.object_find(self, class, identifier, auth_name) end if user then - user.groups = user:list_groups(); + user.user_groups = user:list_groups(); + user.groups = group_class:name_id_by_member(user.id, user.class); end return user; @@ -171,6 +175,10 @@ function Dialplan.object_find(self, class, identifier, auth_name) 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' @@ -184,6 +192,7 @@ function Dialplan.object_find(self, class, identifier, auth_name) 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 @@ -198,6 +207,7 @@ function Dialplan.object_find(self, class, identifier, auth_name) 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; @@ -213,6 +223,7 @@ function Dialplan.object_find(self, class, identifier, auth_name) 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; @@ -226,6 +237,7 @@ function Dialplan.object_find(self, class, identifier, auth_name) 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; @@ -235,7 +247,6 @@ end function Dialplan.retrieve_caller_data(self) require 'common.str' - self.caller.caller_phone_numbers_hash = {} -- TODO: Set auth_account on transfer initiated by calling party @@ -252,9 +263,9 @@ function Dialplan.retrieve_caller_data(self) end if self.caller.auth_account then - self.log:info('CALLER_DATA - auth account: ', self.caller.auth_account.class, '=', self.caller.auth_account.id, '/', self.caller.auth_account.uuid); + self.log:info('CALLER_DATA - auth account: ', self.caller.auth_account.class, '=', self.caller.auth_account.id, '/', self.caller.auth_account.uuid, ', groups: ', table.concat(self.caller.auth_account.groups, ',')); if self.caller.auth_account.owner then - self.log:info('CALLER_DATA - auth owner: ', self.caller.auth_account.owner.class, '=', self.caller.auth_account.owner.id, '/', self.caller.auth_account.owner.uuid); + self.log:info('CALLER_DATA - auth owner: ', self.caller.auth_account.owner.class, '=', self.caller.auth_account.owner.id, '/', self.caller.auth_account.owner.uuid, ', groups: ', table.concat(self.caller.auth_account.owner.groups, ',')); else self.log:error('CALLER_DATA - auth owner not found'); end @@ -273,9 +284,9 @@ function Dialplan.retrieve_caller_data(self) if not common.str.blank(self.caller.account.record.language_code) then self.caller.language = self.caller.account.record.language_code; end - self.log:info('CALLER_DATA - caller account: ', self.caller.account.class, '=', self.caller.account.id, '/', self.caller.account.uuid, ', phone_numbers: ', #self.caller.caller_phone_numbers, ', language: ', self.caller.language); + self.log:info('CALLER_DATA - caller account: ', self.caller.account.class, '=', self.caller.account.id, '/', self.caller.account.uuid, ', phone_numbers: ', #self.caller.caller_phone_numbers, ', language: ', self.caller.language, ', groups: ', table.concat(self.caller.account.groups, ',')); if self.caller.account.owner then - self.log:info('CALLER_DATA - caller owner: ', self.caller.account.owner.class, '=', self.caller.account.owner.id, '/', self.caller.account.owner.uuid); + self.log:info('CALLER_DATA - caller owner: ', self.caller.account.owner.class, '=', self.caller.account.owner.id, '/', self.caller.account.owner.uuid, ', groups: ', table.concat(self.caller.account.owner.groups, ',')); else self.log:error('CALLER_DATA - caller owner not found'); end @@ -319,7 +330,13 @@ function Dialplan.destination_new(self, arg) 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); if self.caller then - destination.call_forwarding = destination.phone_number:call_forwarding(self.caller.caller_phone_numbers); + require 'common.call_forwarding'; + local call_forwarding_class = common.call_forwarding.CallForwarding:new{ log = self.log, database = self.database } + 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; + end + -- destination.call_forwarding = destination.phone_number:call_forwarding(self.caller.caller_phone_numbers); end elseif destination.type == 'unknown' then require 'common.sip_account' @@ -362,64 +379,74 @@ end function Dialplan.dial(self, destination) + local user_id = nil; + local tenant_id = nil; + require 'common.str' destination.caller_id_number = destination.caller_id_number or self.caller.caller_phone_numbers[1]; - if not self.caller.clir then - if destination.node_local and destination.type == 'sipaccount' then - local user_id = nil; - local tenant_id = nil; - - destination.account = self:object_find(destination.type, destination.id); - if destination.account then - if destination.account.class == 'sipaccount' then - destination.callee_id_name = destination.account.record.caller_name; - self.caller:set_callee_id(destination.number, destination.account.record.caller_name); - end + if destination.node_local and destination.type == 'sipaccount' then + destination.pickup_groups = {}; + + destination.account = self:object_find(destination.type, destination.id); + if destination.account then + if destination.account.class == 'sipaccount' then + destination.callee_id_name = destination.account.record.caller_name; + self.caller:set_callee_id(destination.number, destination.account.record.caller_name); + table.insert(destination.pickup_groups, 's' .. destination.account.id ); end + require 'common.group'; + local group_names, group_ids = common.group.Group:new{ log = self.log, database = self.database }:name_id_by_permission(destination.id, destination.type, 'pickup'); + self.log:debug('DESTINATION_GROUPS - pickup_groups: ', table.concat(group_names, ',')); + for index=1, #group_ids do + table.insert(destination.pickup_groups, 'g' .. group_ids[index]); + end + end - if destination.account and destination.account.owner then - if destination.account.owner.class == 'user' then - user_id = destination.account.owner.id; - tenant_id = tonumber(destination.account.owner.record.current_tenant_id); - elseif destination.account.owner.class == 'tenant' then - tenant_id = destination.account.owner.id; - end + if destination.account and destination.account.owner then + 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)); + elseif destination.account.owner.class == 'tenant' then + tenant_id = destination.account.owner.id; end + end + end - if user_id or tenant_id then - require 'common.str' - local phone_book_entry = nil; + 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); - end + 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); + 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'); - end - if phone_book_entry.image then - self:set_caller_picture(phone_book_entry.id, 'phonebookentry', 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 + 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'); + end + if phone_book_entry.image then + self:set_caller_picture(phone_book_entry.id, 'phonebookentry', 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); - elseif self.geo_number_lookup then - require 'dialplan.geo_number' - local geo_number = dialplan.geo_number.GeoNumber:new{ log = self.log, database = self.database }:find(destination.caller_id_number); - if geo_number then - self.log:info('GEO_NUMBER - found: ', geo_number.name, ', ', geo_number.country); - if geo_number.name then - destination.caller_id_name = common.str.to_ascii(geo_number.name) .. ', ' .. common.str.to_ascii(geo_number.country); - else - destination.caller_id_name = common.str.to_ascii(geo_number.country); - end + end + elseif self.caller.account and self.caller.account.owner then + self:set_caller_picture(self.caller.account.owner.id, self.caller.account.owner.class); + elseif self.geo_number_lookup then + require 'dialplan.geo_number' + local geo_number = dialplan.geo_number.GeoNumber:new{ log = self.log, database = self.database }:find(destination.caller_id_number); + if geo_number then + self.log:info('GEO_NUMBER - found: ', geo_number.name, ', ', geo_number.country); + if geo_number.name then + destination.caller_id_name = common.str.to_ascii(geo_number.name) .. ', ' .. common.str.to_ascii(geo_number.country); + else + destination.caller_id_name = common.str.to_ascii(geo_number.country); end end end @@ -997,6 +1024,7 @@ function Dialplan.run(self, destination) destination = self:destination_new(result.call_forwarding); self.caller.destination = destination; + self.caller.destination_number = destination.number; if not result.no_cdr and auth_account then require 'common.call_history' diff --git a/misc/freeswitch/scripts/dialplan/dtmf.lua b/misc/freeswitch/scripts/dialplan/dtmf.lua index 4dbd35f..64f538e 100644 --- a/misc/freeswitch/scripts/dialplan/dtmf.lua +++ b/misc/freeswitch/scripts/dialplan/dtmf.lua @@ -17,12 +17,17 @@ function Dtmf.new(self, arg) self.bleg = arg.bleg self.digit_timeout = arg.digit_timeout or 5; self.router = arg.router; + self.digit_duration_min = arg.digit_duration_min or 500; return object; end function Dtmf.detect(self, caller, sequence, digit, duration, calee) + if not tonumber(duration) or duration < self.digit_duration_min then + return; + end + local timestamp = os.time(); if timestamp - sequence.updated > self.digit_timeout then sequence.digits = digit; @@ -63,6 +68,9 @@ function Dtmf.transfer(self, caller, destination, calee) caller:execute('transfer', destination); fapi:execute('uuid_kill', callee_uuid); else + if caller.account then + fapi:execute('uuid_setvar_multi', callee_uuid .. ' gs_auth_account_type=' .. caller.account.class .. ';gs_auth_account_uuid=' .. caller.account.uuid); + end fapi:execute('uuid_transfer', callee_uuid .. ' ' .. destination); caller.session:hangup(); end diff --git a/misc/freeswitch/scripts/dialplan/fax.lua b/misc/freeswitch/scripts/dialplan/fax.lua index 49a45d9..6dce0a9 100644 --- a/misc/freeswitch/scripts/dialplan/fax.lua +++ b/misc/freeswitch/scripts/dialplan/fax.lua @@ -4,7 +4,7 @@ module(...,package.seeall) -FAX_DOCUMENTS_DIRECTORY = '/tmp/' +FAX_SPOOL_DIRECTORY = '/var/spool/freeswitch/' FAX_PARALLEL_MAX = 8; Fax = {} @@ -18,7 +18,7 @@ function Fax.new(self, arg) self.log = arg.log; self.database = arg.database; self.record = arg.record; - self.fax_directory = arg.fax_directory or FAX_DOCUMENTS_DIRECTORY; + self.fax_spool_directory = arg.fax_spool_directory or FAX_SPOOL_DIRECTORY; return object; end @@ -79,7 +79,7 @@ end -- List waiting fax documents function Fax.queued_for_sending(self, limit) limit = limit or FAX_PARALLEL_MAX; - local sql_query = 'SELECT * FROM `fax_documents` WHERE `state` IN ("queued_for_sending","unsuccessful") AND `retry_counter` > 0 ORDER BY `sent_at` ASC LIMIT ' .. limit; + local sql_query = 'SELECT * FROM `fax_documents` WHERE `state` IN ("queued_for_sending","unsuccessful") AND `retry_counter` > 0 AND `tiff` IS NOT NULL AND `tiff` != "" ORDER BY `sent_at` ASC LIMIT ' .. limit; local fax_documents = {} self.database:query(sql_query, function(fax_entry) fax_entry['destination_numbers'] = Fax:destination_numbers(fax_entry.id) @@ -136,7 +136,7 @@ end -- Receive Fax function Fax.receive(self, caller, file_name) - file_name = file_name or self.fax_directory .. 'fax_in_' .. caller.uuid .. '.tiff'; + file_name = file_name or self.fax_spool_directory .. 'fax_in_' .. caller.uuid .. '.tiff'; caller:set_variable('fax_ident', self.record.station_id) caller:set_variable('fax_verbose', 'false') diff --git a/misc/freeswitch/scripts/dialplan/functions.lua b/misc/freeswitch/scripts/dialplan/functions.lua index acfa336..3706872 100644 --- a/misc/freeswitch/scripts/dialplan/functions.lua +++ b/misc/freeswitch/scripts/dialplan/functions.lua @@ -35,10 +35,8 @@ function Functions.dialplan_function(self, caller, dialed_number) if fid == "ta" then result = self:transfer_all(caller, parameters[3]); - elseif fid == "in" then - result = self:intercept_extensions(caller, parameters[3]); elseif fid == "ia" then - result = self:intercept_any_extension(caller, parameters[3]); + result = self:intercept_any_number(caller, parameters[3]); elseif fid == "anc" then result = self:account_node_change(caller); elseif fid == "li" then @@ -113,9 +111,11 @@ function Functions.dialplan_function(self, caller, dialed_number) result = self:hangup(caller, parameters[3], parameters[4]); elseif fid == "cpa" then result = self:call_parking_inout(caller, parameters[3], parameters[4]); + elseif fid == "cpai" then + result = self:call_parking_inout_index(caller, parameters[3]); end - return result; + return result or { continue = false, code = 505, phrase = 'Error executing function', no_cdr = true }; end -- Transfer all calls to a conference @@ -149,94 +149,27 @@ function Functions.transfer_all(self, caller, destination_number) return destination_number; end --- Intercept Extensions -function Functions.intercept_extensions(self, caller, destination_numbers) - if type(destination_numbers) == "string" then - destination_numbers = "\"" .. destination_numbers .. "\""; - else - destination_numbers = "\"" .. table.concat(destination_numbers, "\",\"") .. "\""; - end - - self.log:debug("Intercept call to number(s): " .. destination_numbers); - - if caller.account_type ~= "SipAccount" then - self.log:error("caller is not a SipAccount"); - return { continue = false, code = 403, phrase = 'Incompatible caller' } - end - local sql_query = 'SELECT * FROM `channels` WHERE `callstate` IN ("EARLY", "ACTIVE") AND `dest` IN (' .. destination_numbers .. ') LIMIT 1'; - - self.database:query(sql_query, function(call_entry) - self.log:debug("intercepting call with uid: " .. call_entry.uuid); - caller:intercept(call_entry.uuid); - end) - - return nil; -end +function Functions.intercept_any_number(self, caller, destination_number) + require 'common.phone_number' + local phone_number = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database }:find_by_number(destination_number); --- intercept call to destination (e.g. sip_account) -function Functions.intercept_destination(self, caller, destination) - self.log:debug("Intercept call to destination " .. destination); - local result = false; - local sql_query = 'SELECT `call_uuid`, `uuid` FROM `channels` WHERE `callstate` = "RINGING" AND `dest` = "' .. destination .. '" LIMIT 1'; + if not phone_number or not phone_number.record then + self.log:notice('FUNCTION_INTERCEPT_ANY_NUMBER - number not found: ', destination_number); + return { continue = false, code = 404, phrase = 'Number not found', no_cdr = true }; + end - caller:set_caller_id(caller.caller_phone_numbers[1] ,caller.caller_id_name); - self.database:query(sql_query, function(call_entry) - if call_entry.call_uuid and tostring(call_entry.call_uuid) then - self.log:debug("intercepting call - uuid: " .. call_entry.call_uuid); - caller:intercept(call_entry.call_uuid); - result = { continue = false, code = 200, call_service = 'pickup' } - require 'common.str' - require 'common.fapi' - local fapi = common.fapi.FApi:new{ log = self.log, uuid = call_entry.call_uuid } - if fapi:channel_exists() then - caller:set_caller_id( - common.str.to_s(fapi:get_variable('effective_caller_id_number')), - common.str.to_s(fapi:get_variable('effective_caller_id_name')) - ); - caller:set_callee_id( - common.str.to_s(fapi:get_variable('effective_callee_id_number')), - common.str.to_s(fapi:get_variable('effective_callee_id_name')) - ); - - caller:set_variable('gs_destination_type', fapi:get_variable('gs_destination_type')); - caller:set_variable('gs_destination_id', fapi:get_variable('gs_destination_id')); - caller:set_variable('gs_destination_uuid', fapi:get_variable('gs_destination_uuid')); - - caller:set_variable('gs_caller_account_type', fapi:get_variable('gs_account_type')); - caller:set_variable('gs_caller_account_id', fapi:get_variable('gs_account_id')); - caller:set_variable('gs_caller_account_uuid', fapi:get_variable('gs_account_uuid')); - - caller:set_variable('gs_auth_account_type', fapi:get_variable('gs_auth_account_type')); - caller:set_variable('gs_auth_account_id', fapi:get_variable('gs_auth_account_id')); - caller:set_variable('gs_auth_account_uuid', fapi:get_variable('gs_auth_account_uuid')); - end - else - self.log:error('FUNCTION - failed to intercept call - no caller uuid for callee uuid: ', call_entry.uuid); - end - 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 }; + end - return result; -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); --- intercept call to owner of destination_number -function Functions.intercept_any_extension(self, caller, destination_number) - require 'common.phone_number' - local phone_number_object = common.phone_number.PhoneNumber:new{ log = self.log, database = self.database }:find_by_number(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); - if not phone_number_object or not phone_number_object.record then - self.log:notice("unallocated number: " .. tostring(destination_number)); - return false; - end - - if phone_number_object.record.phone_numberable_type == 'SipAccount' then - require "common.sip_account" - local sip_account_class = common.sip_account.SipAccount:new{ log = self.log, database = self.database } - local sip_account = sip_account_class:find_by_id(phone_number_object.record.phone_numberable_id) - if sip_account then - return self:intercept_destination(caller, sip_account.record.auth_name); - end - end + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } end @@ -932,3 +865,39 @@ function Functions.call_parking_inout(self, caller, stall_name, lot_name) return { continue = false, code = 200, phrase = 'OK', no_cdr = true } end + + +function Functions.call_parking_inout_index(self, caller, stall_index) + if not tonumber(stall_index) then + self.log:notice('FUNCTION_CALL_PARKING_INOUT_INDEX - malformed index: ', stall_index); + return { continue = false, code = 404, phrase = 'No parkings stall specified', no_cdr = true } + end + + require 'common.str'; + local owner = common.str.try(caller, 'auth_account.owner'); + + if not owner then + self.log:notice('FUNCTION_CALL_PARKING_INOUT_INDEX - stall owner not specified'); + return { continue = false, code = 404, phrase = 'No parkings stalls owner' , no_cdr = true } + end + + require 'dialplan.call_parking'; + local parking_stalls = dialplan.call_parking.CallParking:new{ log = self.log, database = self.database, caller = caller }:find_by_owner(owner.id, owner.class); + + if not parking_stalls or #parking_stalls < 1 then + self.log:notice('FUNCTION_CALL_PARKING_INOUT_INDEX - no parkings stalls found'); + return { continue = false, code = 404, phrase = 'No parkings stalls', no_cdr = true } + end + + local parking_stall = parking_stalls[tonumber(stall_index)]; + + if not parking_stall then + self.log:notice('FUNCTION_CALL_PARKING_INOUT_INDEX - no parkings stall found with index: ', stall_index); + return { continue = false, code = 404, phrase = 'Parking stall not found', no_cdr = true } + end + + self.log:info('FUNCTION_CALL_PARKING_INOUT_INDEX parking/retrieving call - parkingstall=', parking_stall.id, '/', parking_stall.name, ', index: ', stall_index); + parking_stall:park_retrieve(); + + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end diff --git a/misc/freeswitch/scripts/dialplan/sip_call.lua b/misc/freeswitch/scripts/dialplan/sip_call.lua index b56f1b2..5c98792 100644 --- a/misc/freeswitch/scripts/dialplan/sip_call.lua +++ b/misc/freeswitch/scripts/dialplan/sip_call.lua @@ -76,7 +76,8 @@ end function SipCall.fork(self, destinations, arg ) - local dial_strings = {} + local dial_strings = {}; + local pickup_groups = {}; require 'common.sip_account' require 'common.str' @@ -135,6 +136,11 @@ function SipCall.fork(self, destinations, arg ) table.insert(origination_variables, "alert_info='" .. destination.alert_info .. "'"); end 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 + pickup_groups[destination.pickup_groups[key]] = true; + end + end else some_destinations_busy = true; call_result = { code = 486, phrase = 'User busy', disposition = 'USER_BUSY' }; @@ -184,6 +190,10 @@ function SipCall.fork(self, destinations, arg ) self.caller:set_variable('call_timeout', arg.timeout ); self.log:info('FORK DIAL - destinations: ', #dial_strings, ', timeout: ', arg.timeout); + for pickup_group, value in pairs(pickup_groups) do + table.insert(dial_strings, 'pickup/' .. pickup_group); + end + if arg.send_ringing then self.caller:execute('ring_ready'); end @@ -201,10 +211,20 @@ function SipCall.fork(self, destinations, arg ) fork_index = tonumber(session_callee:getVariable('gs_fork_index')) or 0; local destination = destinations[fork_index]; - if arg.detect_dtmf_after_bridge_caller then + if not destination then + destination = { + ['type'] = session_callee:getVariable('gs_account_type'); + id = session_callee:getVariable('gs_account_id'); + uuid = session_callee:getVariable('gs_account_uuid'); + pickup_group_pick = session_callee:getVariable('gs_pickup_group_pick'); + } + self.log:notice('FORK - call picked off by: ', destination.type, '=', destination.id, '/', destination.uuid, ', pickup_group: ', destination.pickup_group_pick); + end + + if arg.detect_dtmf_after_bridge_caller and self.caller.auth_account then session:execute('start_dtmf'); end - if arg.detect_dtmf_after_bridge_callee then + if arg.detect_dtmf_after_bridge_callee and destination.type == 'sipaccount' then session_callee:execute('start_dtmf'); end if arg.bypass_media_network then diff --git a/misc/freeswitch/scripts/dialplan/user.lua b/misc/freeswitch/scripts/dialplan/user.lua index b536600..b6928b4 100644 --- a/misc/freeswitch/scripts/dialplan/user.lua +++ b/misc/freeswitch/scripts/dialplan/user.lua @@ -69,6 +69,23 @@ function User.list_groups(self, id) end +function User.list_group_ids(self, id) + require 'common.str' + id = id or self.id; + local sql_query = 'SELECT `b`.`id` FROM `user_group_memberships` `a` \ + JOIN `user_groups` `b` ON `a`.`user_group_id` = `b`.`id` \ + WHERE `a`.`state` = "active" AND `a`.`user_id`= ' .. tonumber(id) .. ' ORDER BY `b`.`position` LIMIT ' .. MAX_GROUP_MEMBERSHIPS; + + local groups = {}; + + self.database:query(sql_query, function(entry) + table.insert(groups, common.str.downcase(entry.id)); + end); + + return groups; +end + + function User.check_pin(self, pin_to_check) if not self.record then return nil diff --git a/misc/freeswitch/scripts/dialplan/voicemail.lua b/misc/freeswitch/scripts/dialplan/voicemail.lua index 4c96fbe..ae7d0a1 100644 --- a/misc/freeswitch/scripts/dialplan/voicemail.lua +++ b/misc/freeswitch/scripts/dialplan/voicemail.lua @@ -11,7 +11,7 @@ MESSAGE_LENGTH_MAX = 120; SILENCE_LENGTH_ABORT = 5; SILENCE_LEVEL = 500; BEEP = 'tone_stream://%(1000,0,500)'; -RECORD_FILE_PREFIX = '/tmp/voicemail_'; +RECORD_FILE_PREFIX = '/var/spool/freeswitch/voicemail_'; -- create voicemail object function Voicemail.new(self, arg) diff --git a/misc/freeswitch/scripts/send_fax.lua b/misc/freeswitch/scripts/send_fax.lua index 4898cb8..d717f93 100644 --- a/misc/freeswitch/scripts/send_fax.lua +++ b/misc/freeswitch/scripts/send_fax.lua @@ -2,7 +2,6 @@ -- (c) AMOOMA GmbH 2012-2013 -- -local FAX_FILE_PATH = "/opt/GS5/public/uploads/fax_document/tiff/"; local FAX_ANSWERING_TIMEOUT = 20; -- Set logger @@ -142,13 +141,11 @@ end if session and session:answered() then log:info('FAX_SEND - sending fax_document=' .. fax_document.id .. ' (' .. fax_document.tiff .. ')'); - local file_name = FAX_FILE_PATH .. fax_document.id .. "/" .. fax_document.tiff; - session:setVariable('fax_ident', fax_account.record.station_id) session:setVariable('fax_header', fax_account.record.name) session:setVariable('fax_verbose', 'false') local start_time = os.time(); - session:execute('txfax', file_name); + session:execute('txfax', fax_document.tiff); fax_state = { state = nil, |