summaryrefslogtreecommitdiff
path: root/misc/freeswitch/scripts/dialplan/sip_call.lua
diff options
context:
space:
mode:
Diffstat (limited to 'misc/freeswitch/scripts/dialplan/sip_call.lua')
-rw-r--r--misc/freeswitch/scripts/dialplan/sip_call.lua266
1 files changed, 266 insertions, 0 deletions
diff --git a/misc/freeswitch/scripts/dialplan/sip_call.lua b/misc/freeswitch/scripts/dialplan/sip_call.lua
new file mode 100644
index 0000000..57f92c6
--- /dev/null
+++ b/misc/freeswitch/scripts/dialplan/sip_call.lua
@@ -0,0 +1,266 @@
+-- Gemeinschaft 5 module: sip call class
+-- (c) AMOOMA GmbH 2012
+--
+
+module(...,package.seeall);
+
+SipCall = {}
+
+-- Create SipCall object
+function SipCall.new(self, arg)
+ arg = arg or {}
+ object = arg.object or {}
+ setmetatable(object, self);
+ self.__index = self;
+ self.log = arg.log;
+ self.session = arg.session;
+ self.record = arg.record;
+ self.database = arg.database;
+ self.domain = arg.domain;
+ self.caller = arg.caller;
+ self.on_answer = arg.on_answer;
+ self.calling_object = arg.calling_object;
+ return object;
+end
+
+
+function SipCall.wait_answer(self, caller_session, callee_session, timeout, start_time)
+ if caller_session:ready() and callee_session:ready() then
+ callee_session:waitForAnswer(caller_session);
+ end
+
+ while true do
+ if not caller_session:ready() then
+ return 'ORIGINATOR_CANCEL';
+ elseif not callee_session:ready() then
+ return 'UNSPECIFIED';
+ elseif (os.time() - start_time) > timeout then
+ return 'NO_ANSWER';
+ elseif callee_session:answered() then
+ return 'SUCCESS';
+ end
+
+ self.caller:sleep(500);
+ end
+end
+
+
+function SipCall.wait_hangup(self, caller_session, callee_session)
+ local hangup_on = {
+ CS_HANGUP = true,
+ CS_DESTROY = true,
+ }
+
+ while true do
+ local state_caller = caller_session:getState();
+ local state_callee = callee_session:getState();
+ if hangup_on[state_caller] or hangup_on[state_callee] then
+ break;
+ end
+ caller_session:sleep(500);
+ end
+end
+
+
+function SipCall.call_waiting_busy(self, sip_account)
+ require 'common.str'
+ if common.str.to_b(sip_account.record.call_waiting) then
+ self.log:info('CALL_WAITING - status: enabled');
+ return false;
+ else
+ local state = sip_account:call_state();
+ self.log:info('CALL_WAITING - status: disabled, sip_account state: ', state);
+ return state;
+ end
+end
+
+
+function SipCall.fork(self, destinations, arg )
+ local dial_strings = {}
+
+ require 'common.sip_account'
+ local sip_account_class = common.sip_account.SipAccount:new{ log = self.log, database = self.database };
+
+ local call_result = { code = 404, phrase = 'No destination' };
+ local some_destinations_busy = false;
+
+ for index, destination in ipairs(destinations) do
+ local origination_variables = { 'gs_fork_index=' .. index }
+
+ self.log:info('FORK ', index, '/', #destinations, ' - ', destination.type, '=', destination.id, '/', destination.gateway or destination.uuid, '@', destination.node_id, ', number: ', destination.number);
+ if not destination.node_local or destination.type == 'node' then
+ require 'common.node'
+ local node = nil;
+ if tonumber(destination.gateway) then
+ node = common.node.Node:new{ log = self.log, database = self.database }:find_by_id(tonumber(destination.gateway));
+ else
+ node = common.node.Node:new{ log = self.log, database = self.database }:find_by_id(destination.node_id);
+ end
+ if node then
+ table.insert(origination_variables, 'sip_h_X-GS_node_id=' .. self.caller.local_node_id);
+ table.insert(dial_strings, '[' .. table.concat(origination_variables , ',') .. ']sofia/gateway/' .. node.record.name .. '/' .. destination.number);
+ end
+ elseif destination.type == 'sipaccount' then
+ local callee_id_params = '';
+ local sip_account = sip_account_class:find_by_id(destination.id);
+ local call_waiting = self:call_waiting_busy(sip_account);
+ if not call_waiting then
+ destinations[index].numbers = sip_account:phone_numbers();
+
+ if not arg.callee_id_name then
+ table.insert(origination_variables, "effective_callee_id_name='" .. sip_account.record.caller_name .. "'");
+ end
+ if not arg.callee_id_number then
+ table.insert(origination_variables, "effective_callee_id_number='" .. destination.number .. "'");
+ end
+ if destination.alert_info then
+ table.insert(origination_variables, "alert_info='" .. destination.alert_info .. "'");
+ end
+ table.insert(dial_strings, '[' .. table.concat(origination_variables , ',') .. ']user/' .. sip_account.record.auth_name);
+ else
+ some_destinations_busy = true;
+ call_result = { code = 486, phrase = 'User busy', disposition = 'USER_BUSY' };
+ end
+ elseif destination.type == 'gateway' then
+ if destination.caller_id_number then
+ table.insert(origination_variables, "origination_caller_id_number='" .. destination.caller_id_number .. "'");
+ end
+ if destination.caller_id_name then
+ table.insert(origination_variables, "origination_caller_id_name='" .. destination.caller_id_name .. "'");
+ end
+ table.insert(dial_strings, '[' .. table.concat(origination_variables , ',') .. ']sofia/gateway/' .. destination.gateway .. '/' .. destination.number);
+ elseif destination.type == 'dial' then
+ if destination.caller_id_number then
+ table.insert(origination_variables, "origination_caller_id_number='" .. destination.caller_id_number .. "'");
+ end
+ if destination.caller_id_name then
+ table.insert(origination_variables, "origination_caller_id_name='" .. destination.caller_id_name .. "'");
+ end
+ table.insert(dial_strings, '[' .. table.concat(origination_variables , ',') .. ']' .. destination.number);
+ else
+ self.log:info('FORK ', index, '/', #destinations, ' - unhandled destination type: ', destination.type, ', number: ', destination.number);
+ end
+ end
+
+ if #dial_strings == 0 then
+ self.log:notice('FORK - no active destinations - result: ', call_result.code, ' ', call_result.phrase);
+ return call_result;
+ end
+
+ self.caller:set_callee_id(arg.callee_id_number, arg.callee_id_name);
+ self.caller:set_header('X-GS_account_uuid', self.caller.account_uuid);
+ self.caller:set_header('X-GS_account_type', self.caller.account_type);
+ self.caller:set_header('X-GS_auth_account_type', self.caller.auth_account_type);
+ self.caller:set_header('X-GS_auth_account_uuid', self.caller.auth_account_uuid);
+ self.caller:set_header('X-GS_loop_count', self.caller.loop_count);
+
+ self.caller:set_variable('call_timeout', arg.timeout );
+ self.log:info('FORK DIAL - destinations: ', #dial_strings, ', timeout: ', arg.timeout);
+
+ if arg.send_ringing then
+ self.caller:execute('ring_ready');
+ end
+
+ local start_time = os.time();
+ local session_callee = freeswitch.Session('{local_var_clobber=true}' .. table.concat(dial_strings, ','), self.caller.session);
+ self.log:debug('FORK SESSION_INIT - dial_time: ', os.time() - start_time);
+ local answer_result = self:wait_answer(self.caller.session, session_callee, arg.timeout, start_time);
+ local fork_index = nil;
+ self.log:info('FORK ANSWER - status: ', answer_result, ', dial_time: ', os.time() - start_time);
+ if answer_result == 'SUCCESS' then
+ session_callee:setAutoHangup(false);
+ fork_index = tonumber(session_callee:getVariable('gs_fork_index')) or 0;
+ local destination = destinations[fork_index];
+
+ if arg.bypass_media_network then
+ local callee_uuid = session_callee:get_uuid();
+
+ if callee_uuid and self.caller.uuid and freeswitch then
+ require 'common.ipcalc'
+ local callee_network_str = self.caller:to_s('bleg_network_addr');
+ local caller_network_str = self.caller:to_s('network_addr');
+ local callee_network_addr = common.ipcalc.ipv4_to_i(callee_network_str);
+ local caller_network_addr = common.ipcalc.ipv4_to_i(caller_network_str);
+ local network, netmask = common.ipcalc.ipv4_to_network_netmask(arg.bypass_media_network);
+ if network and netmask and callee_network_addr and caller_network_addr
+ and common.ipcalc.ipv4_in_network(callee_network_addr, network, netmask)
+ and common.ipcalc.ipv4_in_network(caller_network_addr, network, netmask) then
+ self.log:info('FORK ', fork_index, ' BYPASS_MEDIA - caller_ip: ', caller_network_str,
+ ', callee_ip: ', callee_network_str,
+ ', subnet: ', arg.bypass_media_network,
+ ', uuid: ', self.caller.uuid, ', bleg_uuid: ', callee_uuid);
+ freeswitch.API():execute('uuid_media', 'off ' .. self.caller.uuid);
+ freeswitch.API():execute('uuid_media', 'off ' .. callee_uuid);
+ end
+ end
+ end
+
+ if self.on_answer then
+ self.on_answer(self.calling_object, destination);
+ end
+
+ self.caller:set_variable('gs_destination_type', destination.type);
+ self.caller:set_variable('gs_destination_id', destination.id);
+ self.caller:set_variable('gs_destination_uuid', destination.uuid);
+
+ self.log:info('FORK ', fork_index,
+ ' BRIDGE - destination: ', destination.type, '=', destination.id, '/', destination.uuid,'@', destination.node_id,
+ ', number: ', destination.number,
+ ', dial_time: ', os.time() - start_time);
+ freeswitch.bridge(self.caller.session, session_callee);
+ self:wait_hangup(self.caller.session, session_callee);
+ end
+
+ -- if session_callee:ready() then
+ -- self.log:info('FORK - hangup destination channel');
+ -- session_callee:hangup('ORIGINATOR_CANCEL');
+ -- end
+
+ call_result = {};
+ call_result.disposition = session_callee:hangupCause();
+ call_result.fork_index = fork_index;
+
+ if some_destinations_busy and call_result.disposition == 'USER_NOT_REGISTERED' then
+ call_result.phrase = 'User busy';
+ call_result.code = 486;
+ call_result.disposition = 'USER_BUSY';
+ elseif call_result.disposition == 'USER_NOT_REGISTERED' then
+ call_result.phrase = 'User offline';
+ call_result.code = 480;
+ elseif call_result.disposition == 'NO_ANSWER' then
+ call_result.phrase = 'No answer';
+ call_result.code = 408;
+ elseif call_result.disposition == 'NORMAL_TEMPORARY_FAILURE' then
+ call_result.phrase = 'User offline';
+ call_result.code = 480;
+ else
+ call_result.cause = self.caller:to_s('last_bridge_hangup_cause');
+ call_result.code = self.caller:to_i('last_bridge_proto_specific_hangup_cause');
+ call_result.phrase = self.caller:to_s('sip_hangup_phrase');
+ end
+
+ self.log:info('FORK EXIT - disposition: ', call_result.disposition,
+ ', cause: ', call_result.cause,
+ ', code: ', call_result.code,
+ ', phrase: ', call_result.phrase,
+ ', dial_time: ', os.time() - start_time);
+
+ return call_result;
+end
+
+-- Return call forwarding settngs
+function SipCall.conditional_call_forwarding(self, cause, call_forwarding)
+ local condition_map = {USER_NOT_REGISTERED="offline", NO_ANSWER="noanswer", USER_BUSY="busy"}
+ local condition = condition_map[cause]
+ if call_forwarding and condition and call_forwarding[condition] then
+ log:debug('call forwarding on ' .. condition .. ' - destination: ' .. call_forwarding[condition].destination .. ', type: ' .. call_forwarding[condition].call_forwardable_type);
+ return call_forwarding[condition]
+ end
+end
+
+function SipCall.set_callee_variables(self, sip_account)
+ self.session:setVariable("gs_callee_account_id", sip_account.id);
+ self.session:setVariable("gs_callee_account_type", "SipAccount");
+ self.session:setVariable("gs_callee_account_owner_type", sip_account.sip_accountable_type);
+ self.session:setVariable("gs_callee_account_owner_id", sip_account.sip_accountable_id);
+end