diff options
Diffstat (limited to 'misc')
25 files changed, 1276 insertions, 203 deletions
diff --git a/misc/freeswitch/conf/freeswitch.xml b/misc/freeswitch/conf/freeswitch.xml index fd6ab67..a5fe873 100644 --- a/misc/freeswitch/conf/freeswitch.xml +++ b/misc/freeswitch/conf/freeswitch.xml @@ -466,6 +466,470 @@ </macros> </phrases> </language> + <language name="de" say-module="de" sound-prefix="/opt/freeswitch/sounds/de/de/callie"> + <phrases> + <macros> + <macro name="voicemail_hello"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-hello.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_enter_id"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-enter_id.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_enter_pass"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-enter_pass.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_fail_auth"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-fail_auth.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_goodbye"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-goodbye.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_abort"> + <input pattern="(.*)"> + <match> + <action function="play-file" data="voicemail/vm-abort.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_message_count"> + <input pattern="^(1):(.*)$" break_on_match="true"> + <match> + <action function="play-file" data="voicemail/vm-you_have.wav"/> + <action function="say" data="$1" method="pronounced" type="items"/> + <action function="play-file" data="voicemail/vm-$2.wav"/> + <action function="play-file" data="voicemail/vm-message.wav"/> + </match> + </input> + <input pattern="^(\d+):(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-you_have.wav"/> + <action function="say" data="$1" method="pronounced" type="items"/> + <action function="play-file" data="voicemail/vm-$2.wav"/> + <action function="play-file" data="voicemail/vm-messages.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_menu"> + <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-listen_new.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-listen_saved.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-advanced.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$3" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-to_exit.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$4" method="pronounced" type="name_phonetic"/> + </match> + </input> + </macro> + <macro name="voicemail_config_menu"> + <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-to_record_greeting.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-choose_greeting.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-record_name2.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$3" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-change_password.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$4" method="pronounced" type="name_spelled"/> + <action function="execute" data="sleep(100)"/> + <action function="play-file" data="voicemail/vm-main_menu.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$5" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_record_name"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-record_name1.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_record_file_check"> + <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-save_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$3" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-rerecord.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_record_urgent_check"> + <input pattern="^([0-9#*]):([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-mark-urgent.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-continue.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_forward_prepend"> + <input pattern="^([0-9#*]):([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-forward_add_intro.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-send_message_now.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_forward_message_enter_extension"> + <input pattern="^([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-forward_enter_ext.wav"/> + <action function="play-file" data="voicemail/vm-followed_by.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_invalid_extension"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-that_was_an_invalid_ext.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_listen_file_check"> + <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-save_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-delete_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$3" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-forward_to_email.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$4" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-return_call.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$5" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-to_forward.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$6" method="pronounced" type="name_spelled"/> + </match> + </input> + <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$"> + <match> + <action function="play-file" data="voicemail/vm-listen_to_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-save_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-delete_recording.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$3" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-return_call.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$5" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-to_forward.wav"/> + <action function="play-file" data="voicemail/vm-press.wav"/> + <action function="say" data="$6" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_choose_greeting"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-choose_greeting_choose.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_choose_greeting_fail"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-choose_greeting_fail.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_record_greeting"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-record_greeting.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_record_message"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-record_message.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_greeting_selected"> + <input pattern="^(\d+)$"> + <match> + <action function="play-file" data="voicemail/vm-greeting.wav"/> + <action function="say" data="$1" method="pronounced" type="items"/> + <action function="play-file" data="voicemail/vm-selected.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_play_greeting"> + <input pattern="^(\d+)$"> + <match> + <action function="play-file" data="voicemail/vm-person.wav"/> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + <action function="play-file" data="voicemail/vm-not_available.wav"/> + </match> + </input> + <input pattern="^name:(.+)$"> + <match> + <action function="play-file" data="$1"/> + <action function="play-file" data="voicemail/vm-not_available.wav"/> + </match> + </input> + <input pattern="^greeting:(.+)$"> + <match> + <action function="play-file" data="$1"/> + </match> + </input> + </macro> + <macro name="voicemail_say_number"> + <input pattern="^(\d+)$"> + <match> + <action function="say" data="$1" method="pronounced" type="items"/> + </match> + </input> + </macro> + <macro name="voicemail_say_message_number"> + <input pattern="^([a-z]+):(\d+)$"> + <match> + <action function="play-file" data="voicemail/vm-$1.wav"/> + <action function="play-file" data="voicemail/vm-message_number.wav"/> + <action function="say" data="$2" method="pronounced" type="items"/> + </match> + </input> + </macro> + <macro name="voicemail_say_phone_number"> + <input pattern="^(.*)$"> + <match> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_say_name"> + <input pattern="^(.*)$"> + <match> + <action function="say" data="$1" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="voicemail_ack"> + <input pattern="^(too-small)$"> + <match> + <action function="play-file" data="voicemail/vm-too-small.wav"/> + </match> + </input> + <input pattern="^(deleted)$"> + <match> + <action function="play-file" data="voicemail/vm-message.wav"/> + <action function="play-file" data="voicemail/vm-$1.wav"/> + </match> + </input> + <input pattern="^(saved)$"> + <match> + <action function="play-file" data="voicemail/vm-message.wav"/> + <action function="play-file" data="voicemail/vm-saved2.wav"/> + </match> + </input> + <input pattern="^(emailed)$"> + <match> + <action function="play-file" data="voicemail/vm-message.wav"/> + <action function="play-file" data="voicemail/vm-$1.wav"/> + </match> + </input> + <input pattern="^(marked-urgent)$"> + <match> + <action function="play-file" data="voicemail/vm-message.wav"/> + <action function="play-file" data="voicemail/vm-$1.wav"/> + </match> + </input> + </macro> + <macro name="voicemail_say_date"> + <input pattern="^(.*)$"> + <match> + <action function="say" data="$1" method="pronounced" type="current_date_time"/> + </match> + </input> + </macro> + <macro name="voicemail_disk_quota_exceeded"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="voicemail/vm-mailbox_full.wav"/> + </match> + </input> + </macro> + <macro name="valet_announce_ext"> + <input pattern="^([^\:]+):(.*)$"> + <match> + <action function="say" data="$2" method="pronounced" type="name_spelled"/> + </match> + </input> + </macro> + <macro name="valet_lot_full"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/> + </match> + </input> + </macro> + <macro name="valet_lot_empty"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="tone_stream://%(275,10,600);%(275,100,300)"/> + </match> + </input> + </macro> + <macro name="logged_in"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_now_logged_in.wav"/> + </match> + </input> + </macro> + <macro name="logged_out"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_now_logged_out.wav"/> + </match> + </input> + </macro> + <macro name="acd_announce_position_enter"> + <input pattern="^([0-9]+)$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_number.wav"/> + <action function="say" data="$1" method="pronounced" type="number"/> + <action function="play-file" data="ivr/ivr-in_line.wav"/> + </match> + </input> + </macro> + <macro name="acd_announce_position_change"> + <input pattern="^1$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_number.wav"/> + <action function="say" data="1" method="pronounced" type="number"/> + <action function="play-file" data="ivr/ivr-in_line.wav"/> + <action function="break"/> + </match> + </input> + <input pattern="^([0-9]+)$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_number.wav"/> + <action function="say" data="$1" method="pronounced" type="number"/> + <action function="play-file" data="ivr/ivr-in_line.wav"/> + <action function="play-file" data="ivr/ivr-thank_you_for_holding.wav"/> + </match> + </input> + </macro> + <macro name="acd_announce_position_periodic"> + <input pattern="^1$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_number.wav"/> + <action function="say" data="1" method="pronounced" type="number"/> + <action function="play-file" data="ivr/ivr-in_line.wav"/> + <action function="break"/> + </match> + </input> + <input pattern="^([0-9]+)$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_number.wav"/> + <action function="say" data="$1" method="pronounced" type="number"/> + <action function="play-file" data="ivr/ivr-in_line.wav"/> + <action function="play-file" data="ivr/ivr-thank_you_for_holding.wav"/> + </match> + </input> + </macro> + <macro name="acd_announce_call_agents"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="$1"/> + </match> + </input> + </macro> + <macro name="acd_greeting"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="$1"/> + </match> + </input> + </macro> + <macro name="acd_goodbye"> + <input pattern="^(.*)$"> + <match> + <action function="play-file" data="$1"/> + </match> + </input> + </macro> + <macro name="acd_agent_status"> + <input pattern="^active$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_now_logged_in.wav"/> + </match> + </input> + <input pattern="^inactive$"> + <match> + <action function="play-file" data="ivr/ivr-you_are_now_logged_out.wav"/> + </match> + </input> + </macro> + </macros> + </phrases> + </language> </section> <section name="configuration" description="Gemeinschaft5 FreeSwitch configuration"> <configuration name="acl.conf" description="Network Lists"> @@ -632,6 +1096,7 @@ <load module="mod_local_stream"/> <load module="mod_tone_stream"/> <load module="mod_say_en"/> + <load module="mod_say_de"/> <load module="mod_spandsp"/> <load module="mod_snmp"/> <load module="mod_dingaling"/> @@ -639,6 +1104,7 @@ </configuration> <configuration name="lua.conf" description="LUA Configuration"> <settings> + <param name="module-directory" value="/usr/lib/i386-linux-gnu/lua/5.1/?.so;"/> <param name="script-directory" value="/usr/share/freeswitch/scripts/?.lua;/usr/share/lua/5.1/?.lua;"/> <param name="xml-handler-script" value="configuration.lua"/> <param name="xml-handler-bindings" value="directory|configuration"/> diff --git a/misc/freeswitch/scripts/common/database.lua b/misc/freeswitch/scripts/common/database.lua index 1f39135..345f69d 100644 --- a/misc/freeswitch/scripts/common/database.lua +++ b/misc/freeswitch/scripts/common/database.lua @@ -16,6 +16,7 @@ function Database.new(self, arg) self.class = 'database'; self.log = arg.log; self.conn = nil; + self.ignore_on_update = arg.ignore_on_update or {}; return object; end @@ -71,6 +72,45 @@ function Database.last_insert_id(self) end +function Database.insert_or_update(self, db_table, record, use_on_update) + ignore_on_update = ignore_on_update or self.ignore_on_update; + local record_sql_create = {}; + local record_sql_update = {}; + + for key, value in pairs(record) do + if ignore_on_update[key] ~= false then + table.insert(record_sql_update, self:key_value(key, value)); + end + table.insert(record_sql_create, self:key_value(key, value)); + end + + local sql_query = 'INSERT INTO `' .. db_table .. '` SET ' .. table.concat(record_sql_create, ', ') .. ' ON DUPLICATE KEY UPDATE ' .. table.concat(record_sql_update, ', '); + + return self:query(sql_query); +end + + +function Database.key_value(self, key, value) + return self:escape(key, '`') .. ' = ' .. self:escape(value, '"'); +end + + +function Database.escape(self, value, str_quotes) + str_quotes = str_quotes or ''; + if type(value) == 'boolean' then + return tostring(value):upper(); + elseif type(value) == 'number' then + return tostring(value); + elseif type(value) == 'string' then + return str_quotes .. value:gsub('"', '\\"'):gsub("'", "\\'") .. str_quotes; + elseif type(value) == 'table' and value.raw then + return tostring(value[1]); + else + return 'NULL'; + end +end + + function Database.release(self) if self.conn then self.conn:release(); diff --git a/misc/freeswitch/scripts/common/fapi.lua b/misc/freeswitch/scripts/common/fapi.lua index 5b96633..b749a69 100644 --- a/misc/freeswitch/scripts/common/fapi.lua +++ b/misc/freeswitch/scripts/common/fapi.lua @@ -32,8 +32,10 @@ function FApi.return_result(self, result, positive, negative, unspecified) return negative; elseif result:match('^+OK') then return positive; - else + elseif type(unspecified) ~= 'nil' then return unspecified; + else + return result; end end @@ -75,6 +77,7 @@ function FApi.create_uuid(self, uuid) end function FApi.execute(self, function_name, function_parameters) + function_parameters = function_parameters or ''; local result = self.fs_api:execute(function_name, function_parameters); return self:return_result(result, true); end diff --git a/misc/freeswitch/scripts/common/intruder.lua b/misc/freeswitch/scripts/common/intruder.lua new file mode 100644 index 0000000..083ec37 --- /dev/null +++ b/misc/freeswitch/scripts/common/intruder.lua @@ -0,0 +1,51 @@ +-- Gemeinschaft 5 module: intruder class +-- (c) AMOOMA GmbH 2013 +-- + +module(...,package.seeall) + + +Intruder = {} + + +function Intruder.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.class = 'intruder' + self.database = arg.database; + + return object; +end + + +function Intruder.update_blacklist(self, event) + local intruder_record = { + list_type = 'blacklist', + key = event.key, + points = event.points, + bans = event.record.banned, + contact_ip = event.received_ip, + contact_port = event.received_port, + contact_count = event.record.contact_count + 1, + contact_last = { 'FROM_UNIXTIME(' .. tostring(math.floor(event.timestamp/1000000)) .. ')', raw = true }, + contacts_per_second = event.contacts_per_second, + contacts_per_second_max = event.contacts_per_second_max, + user_agent = event.user_agent, + to_user = event.to_user, + comment = 'Permimeter', + created_at = {'NOW()', raw = true }, + updated_at = {'NOW()', raw = true }, + }; + + if tonumber(event.ban_time) then + intruder_record.ban_last = { 'FROM_UNIXTIME(' .. event.ban_time .. ')', raw = true }; + end + if tonumber(event.ban_end) then + intruder_record.ban_end = { 'FROM_UNIXTIME(' .. event.ban_end .. ')', raw = true }; + end + + self.database:insert_or_update('intruders', intruder_record, { created_at = false, comment = false }); +end diff --git a/misc/freeswitch/scripts/common/perimeter.lua b/misc/freeswitch/scripts/common/perimeter.lua new file mode 100644 index 0000000..0815d33 --- /dev/null +++ b/misc/freeswitch/scripts/common/perimeter.lua @@ -0,0 +1,241 @@ +-- Gemeinschaft 5 module: perimeter class +-- (c) AMOOMA GmbH 2013 +-- + +module(...,package.seeall) + + +Perimeter = {} + + +function Perimeter.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.class = 'perimeter' + self.database = arg.database; + self.domain = arg.domain; + self.sources = {}; + + self.checks_available = { + check_frequency = self.check_frequency, + check_username_scan = self.check_username_scan, + check_bad_headers = self.check_bad_headers, + }; + + return object; +end + + +function Perimeter.setup(self, event) + require 'common.configuration_table'; + local config = common.configuration_table.get(self.database, 'perimeter'); + + self.contact_count_threshold = 10; + self.contact_span_threshold = 2; + self.name_changes_threshold = 2; + self.blacklist_file = '/var/opt/gemeinschaft/firewall/blacklist'; + self.blacklist_file_comment = '# PERIMETER_BAN - points: {points}, generated: {date}'; + self.blacklist_file_entry = '{received_ip} udp 5060'; + self.ban_command = 'sudo /sbin/service shorewall refresh'; + self.ban_threshold = 20; + self.ban_tries = 1; + self.checks = { register = {}, call = {} }; + self.bad_headers = { register = {}, call = {} }; + + if config and config.general then + for key, value in pairs(config.general) do + self[key] = value; + end + end + + self.checks.register = config.checks_register or {}; + self.checks.call = config.checks_call or {}; + self.bad_headers.register = config.bad_headers_register; + self.bad_headers.call = config.bad_headers_call; + + self.log:info('[perimeter] PERIMETER - setup perimeter defense'); +end + + +function Perimeter.record_load(self, event) + if not self.sources[event.key] then + self.sources[event.key] = { + contact_first = event.timestamp, + contact_last = event.timestamp, + contact_count = 0, + span_contact_count = 0, + span_start = event.timestamp, + points = 0, + banned = 0, + }; + end + + return self.sources[event.key]; +end + + +function Perimeter.format_date(self, value) + local epoch = tonumber(tostring(value/1000000):match('^(%d-)%.')); + return os.date('%Y-%m-%d %X', tonumber(epoch)) .. '.' .. (value-(epoch*1000000)); +end + + +function Perimeter.record_update(self, event) + event.record.contact_last = event.timestamp; + event.record.contact_count = event.record.contact_count + 1; + event.record.points = event.points or event.record.points; + event.record.span_start = event.span_start or event.record.span_start; + event.record.span_contact_count = (event.span_contact_count or event.record.span_contact_count) + 1; + event.record.users = event.users or event.record.users; +end + + +function Perimeter.check(self, event) + if not event or not event.key then + self.log:warning('[perimeter] PERIMETER_CHECK - no event/key'); + return; + end + + event.record = self:record_load(event); + if event.record.banned <= self.ban_tries then + for check_name, check_points in pairs(self.checks[event.action]) do + if self.checks_available[check_name] then + local result = self.checks_available[check_name](self, event); + if tonumber(result) then + event.points = (event.points or event.record.points) + result * check_points; + end + end + end + end + + if tonumber(event.points) and event.points < 0 then + event.points = 0; + end + + if event.points then + self.log:info('[', event.key, '/', event.sequence, '] PERIMETER suspicion rising - points: ', event.points,', ', event.action, '=', event.class, ', from: ', event.from_user, '@', event.from_host, ', to: ', event.to_user, '@', event.to_host, ', user_agent: ', event.user_agent); + end + + if (event.points or event.record.points) > self.ban_threshold and event.record.banned <= self.ban_tries then + if event.record.banned > 0 and event.record.banned == self.ban_tries then + self.log:warning('[', event.key, '/', event.sequence, '] PERIMETER_BAN_FUTILE - points: ', event.points,', event: ', event.class, ', from: ', event.from_user, '@', event.from_host, ', to: ', event.to_user, '@', event.to_host); + else + self.log:notice('[', event.key, '/', event.sequence, '] PERIMETER_BAN - threshold reached: ', event.points,', event: ', event.class, ', from: ', event.from_user, '@', event.from_host, ', to: ', event.to_user, '@', event.to_host); + if event.record.banned == 0 then + self:append_blacklist_file(event); + end + self:execute_ban(event); + event.ban_time = os.time(); + end + + event.record.banned = event.record.banned + 1; + event.span_start = event.timestamp; + event.span_contact_count = 0; + event.points = 0; + end + + if event.points then + self:update_intruder(event); + end + + self:record_update(event); +end + + +function Perimeter.check_frequency(self, event) + if event.record.span_contact_count >= self.contact_count_threshold then + self.log:debug('[', event.key, '/', event.sequence, '] PERIMETER_FREQUENCY_CHECK - contacts: ', event.record.span_contact_count, ' in < ', (event.timestamp - event.record.span_start)/1000000, ' sec, threshold: ', self.contact_count_threshold, ' in ', self.contact_span_threshold, ' sec'); + event.span_contact_count = 0; + event.span_start = event.timestamp; + event.contacts_per_second = event.record.span_contact_count / ((event.timestamp - event.record.span_start)/1000000) + return 1; + elseif (event.timestamp - event.record.span_start) > (self.contact_span_threshold * 1000000) then + event.span_contact_count = 0; + event.span_start = event.timestamp; + end +end + + +function Perimeter.check_username_scan(self, event) + if not event.to_user then + return; + end + + if not event.record.users or tostring(event.auth_result) == 'SUCCESS' or tostring(event.auth_result) == 'RENEWED' then + event.users = { event.to_user }; + return; + end + + if #event.record.users >= self.name_changes_threshold then + self.log:debug('[', event.key, '/', event.sequence, '] PERIMETER_USER_SCAN - user names: ', #event.record.users, ', threshold: ', self.name_changes_threshold); + event.users = {}; + return 1; + else + for index=1, #event.record.users do + if event.record.users[index] == tostring(event.to_user) then + return + end + end + + if not event.users then + event.users = event.record.users or {}; + end + table.insert(event.users, tostring(event.to_user)); + end +end + + +function Perimeter.check_bad_headers(self, event) + local points = nil; + for name, pattern in pairs(self.bad_headers[event.action]) do + pattern = self:expand_variables(pattern, event); + local success, result = pcall(string.find, event[name], pattern); + if success and result then + self.log:debug('[', event.key, '/', event.sequence, '] PERIMETER_BAD_HEADERS - ', name, '=', event[name], ' ~= ', pattern); + points = (points or 0) + 1; + end + end + + return points; +end + + +function Perimeter.append_blacklist_file(self, event) + local blacklist = io.open(self.blacklist_file, 'a'); + if not blacklist then + self.log:error('[', event.key, '/', event.sequence, '] PERIMETER_APPEND_BLACKLIST - could not open file: ', self.blacklist_file); + return false; + end + + event.date = self:format_date(event.timestamp); + + if self.blacklist_file_comment then + blacklist:write(self:expand_variables(self.blacklist_file_comment, event), '\n'); + end + + self.log:debug('[', event.key, '/', event.sequence, '] PERIMETER_APPEND_BLACKLIST - file: ', self.blacklist_file); + blacklist:write(self:expand_variables(self.blacklist_file_entry, event), '\n'); + blacklist:close(); +end + + +function Perimeter.execute_ban(self, event) + local command = self:expand_variables(self.ban_command, event); + self.log:debug('[', event.key, '/', event.sequence, '] PERIMETER_EXECUTE_BAN - command: ', command); + local result = os.execute(command); +end + +function Perimeter.update_intruder(self, event) + require 'common.intruder'; + local result = common.intruder.Intruder:new{ log = self.log, database = self.database }:update_blacklist(event); +end + + +function Perimeter.expand_variables(self, line, variables) + return (line:gsub('{([%a%d%._]+)}', function(captured) + return variables[captured] or ''; + end)) +end diff --git a/misc/freeswitch/scripts/common/sip_account.lua b/misc/freeswitch/scripts/common/sip_account.lua index 8dd432b..d023f20 100644 --- a/misc/freeswitch/scripts/common/sip_account.lua +++ b/misc/freeswitch/scripts/common/sip_account.lua @@ -38,8 +38,12 @@ function SipAccount.find_by_sql(self, where) `a`.`sip_accountable_id`, \ `a`.`hotdeskable`, \ `a`.`gs_node_id`, \ - `b`.`host` \ - FROM `sip_accounts` `a` JOIN `sip_domains` `b` ON `a`.`sip_domain_id` = `b`.`id` \ + `b`.`host`, \ + `c`.`sip_host`, \ + `c`.`profile_name` \ + FROM `sip_accounts` `a` \ + JOIN `sip_domains` `b` ON `a`.`sip_domain_id` = `b`.`id` \ + LEFT JOIN `sip_registrations` `c` ON `a`.`auth_name` = `c`.`sip_user` \ WHERE ' .. where .. ' LIMIT 1'; local sip_account = nil; diff --git a/misc/freeswitch/scripts/common/str.lua b/misc/freeswitch/scripts/common/str.lua index 793c191..72ff388 100644 --- a/misc/freeswitch/scripts/common/str.lua +++ b/misc/freeswitch/scripts/common/str.lua @@ -18,6 +18,23 @@ function try(array, arguments) return result; end + +function set(array, arguments, value) + local nop, arguments_count = arguments:gsub('%.', ''); + local structure = array; + arguments:gsub('([^%.]+)', function(entry) + if arguments_count <= 0 then + structure[entry] = value; + elseif type(structure[entry]) == 'table' then + structure = structure[entry]; + else + structure[entry] = {}; + structure = structure[entry]; + end + arguments_count = arguments_count - 1; + end); +end + -- to number function to_n(value) value = tostring(value):gsub('[^%d%.%+%-]', ''); diff --git a/misc/freeswitch/scripts/configuration.lua b/misc/freeswitch/scripts/configuration.lua index 062cf5d..75d0df3 100644 --- a/misc/freeswitch/scripts/configuration.lua +++ b/misc/freeswitch/scripts/configuration.lua @@ -63,7 +63,12 @@ function profile(database, sofia_ini, profile_name, index, domains, node_id) require 'configuration.simple_xml' local xml = configuration.simple_xml.SimpleXml:new(); - local parameters = sofia_ini['profile:' .. profile_name]; + local profile_template = sofia_ini['profile'] or {}; + local parameters = sofia_ini['profile:' .. profile_name] or {}; + + for key, value in pairs(profile_template) do + parameters[key] = parameters[key] or value; + end if not parameters then log:error('SOFIA_PROFILE ', index,' - name: ', profile_name, ' - no parameters'); @@ -134,9 +139,16 @@ function conf_sofia(database) require 'configuration.sip' local domains = configuration.sip.Sip:new{ log = log, database = database}:domains(); - sofia_profiles_xml = ''; + local sofia_profiles = {}; for profile_name, index in pairs(sofia_ini.profiles) do if tonumber(index) and tonumber(index) > 0 then + sofia_profiles[index] = profile_name; + end + end + + local sofia_profiles_xml = ''; + for index, profile_name in ipairs(sofia_profiles) do + if tonumber(index) and tonumber(index) > 0 then sofia_profiles_xml = sofia_profiles_xml .. profile(database, sofia_ini, profile_name, tonumber(index), domains, local_node_id); end end @@ -403,7 +415,7 @@ function directory_sip_account(database) end else require 'common.sip_account' - local sip_account = common.sip_account.SipAccount:new{ log = log, database = database}:find_by_auth_name(auth_name, domain); + local sip_account = common.sip_account.SipAccount:new{ log = log, database = database}:find_by_auth_name(auth_name); require 'common.configuration_table' local user_parameters = common.configuration_table.get(database, 'sip_accounts', 'parameters'); diff --git a/misc/freeswitch/scripts/dialplan/acd.lua b/misc/freeswitch/scripts/dialplan/acd.lua index f4b298e..5ed8979 100644 --- a/misc/freeswitch/scripts/dialplan/acd.lua +++ b/misc/freeswitch/scripts/dialplan/acd.lua @@ -194,7 +194,7 @@ function AutomaticCallDistributor.agents_available(self, strategy) local accounts = {} self.database:query(sql_query, function(entry) - if not entry.callstate then + if common.str.blank(entry.callstate) then table.insert(accounts, entry); end end); diff --git a/misc/freeswitch/scripts/dialplan/dialplan.lua b/misc/freeswitch/scripts/dialplan/dialplan.lua index ff4adc6..72503e5 100644 --- a/misc/freeswitch/scripts/dialplan/dialplan.lua +++ b/misc/freeswitch/scripts/dialplan/dialplan.lua @@ -350,10 +350,10 @@ function Dialplan.set_caller_picture(self, entry_id, entry_type, image) require 'dialplan.user' local user = dialplan.user.User:new{ log = self.log, database = self.database }:find_by_id(entry_id); if user then - self.caller:set_variable('sip_h_Call-Info', '<' .. self.user_image_url .. '/' .. tonumber(entry_id) .. '/snom_caller_picture_' .. tostring(user.record.image) .. '>;purpose=icon'); + self.caller:export_variable('sip_h_Call-Info', '<' .. self.user_image_url .. '/' .. tonumber(entry_id) .. '/snom_caller_picture_' .. tostring(user.record.image) .. '>;purpose=icon'); end elseif entry_type == 'phonebookentry' and image then - self.caller:set_variable('sip_h_Call-Info', '<' .. self.phone_book_entry_image_url .. '/' .. tonumber(entry_id) .. '/snom_caller_picture_' .. tostring(image) .. '>;purpose=icon'); + self.caller:export_variable('sip_h_Call-Info', '<' .. self.phone_book_entry_image_url .. '/' .. tonumber(entry_id) .. '/snom_caller_picture_' .. tostring(image) .. '>;purpose=icon'); end end @@ -445,6 +445,8 @@ function Dialplan.dial(self, destination) send_ringing = ( self.send_ringing_to_gateways and self.caller.from_gateway ), bypass_media_network = self.config.parameters.bypass_media_network, update_callee_display = self.config.parameters.update_callee_display, + detect_dtmf_after_bridge_caller = self.detect_dtmf_after_bridge_caller, + detect_dtmf_after_bridge_callee = self.detect_dtmf_after_bridge_callee, } ); end @@ -759,7 +761,7 @@ function Dialplan.switch(self, destination) elseif not common.str.blank(destination.number) then local result = { continue = false, code = 404, phrase = 'No route' } - local clip_no_screening = common.str.try(caller, 'account.record.clip_no_screening'); + local clip_no_screening = common.str.try(self.caller, 'account.record.clip_no_screening'); self.caller.caller_id_numbers = {} if not common.str.blank(clip_no_screening) then for index, number in ipairs(common.str.strip_to_a(clip_no_screening, ',')) do @@ -814,11 +816,13 @@ function Dialplan.switch(self, destination) self.caller:set_callee_id(destination.callee_id_number, destination.callee_id_name); for index, route in ipairs(routes) do - if route.endpoint_type == 'hangup' then - return { continue = false, code = route.endpoint, phrase = route.phrase, cause = route.value } + if route.type == 'hangup' then + self.log:notice('SWITCH_HANGUP - code: ', route.code, ', phrase: ', route.phrase, ', cause: ', route.cause); + return { continue = false, code = route.code or '404', phrase = route.phrase, cause = route.cause } end - if route.endpoint_type == 'forward' then - return { continue = true, call_forwarding = { number = route.value, service = 'route', type = 'phonenumber' }} + if route.type == 'forward' then + self.log:notice('SWITCH_CALL_FORWARDING - number: ', route.number); + return { continue = true, call_forwarding = { number = route.number, service = 'route', type = 'phonenumber' }} end for key, value in pairs(route) do diff --git a/misc/freeswitch/scripts/dialplan/dtmf.lua b/misc/freeswitch/scripts/dialplan/dtmf.lua new file mode 100644 index 0000000..4dbd35f --- /dev/null +++ b/misc/freeswitch/scripts/dialplan/dtmf.lua @@ -0,0 +1,69 @@ +-- Gemeinschaft 5 module: dtmf class +-- (c) AMOOMA GmbH 2013 +-- + +module(...,package.seeall) + +Dtmf = {} + +-- create dtmf object +function Dtmf.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'Dtmf'; + self.log = arg.log; + self.bleg = arg.bleg + self.digit_timeout = arg.digit_timeout or 5; + self.router = arg.router; + + return object; +end + + +function Dtmf.detect(self, caller, sequence, digit, duration, calee) + local timestamp = os.time(); + if timestamp - sequence.updated > self.digit_timeout then + sequence.digits = digit; + else + sequence.digits = sequence.digits .. digit; + end + + caller.dtmf_digits = sequence.digits; + + if calee then + self.log:debug('DTMF_RECEIVER callee - digit: [', digit, '][', duration, '], sequence: ', sequence.digits); + else + self.log:debug('DTMF_RECEIVER caller - digit: [', digit, '][', duration, '], sequence: ', sequence.digits); + end + + local route = self.router:route_run('dtmf', true); + sequence.updated = timestamp; + + if not route then + return; + end + + if route.type == 'dialplanfunction' or route.type == 'phonenumber' or route.type == 'unknown' then + self:transfer(caller, route.destination_number, calee) + else + self.log:notice('DTMF_RECEIVER - unhandled destination: ', route.type, '=', route.id); + end +end + + +function Dtmf.transfer(self, caller, destination, calee) + require 'common.fapi' + local fapi = common.fapi.FApi:new{ log = log }; + local callee_uuid = caller:to_s('bridge_to'); + + self.log:notice('DTMF_RECEIVER_TRANSFER - destination: ', destination, ', uuid: ', caller.uuid, ', callee_uuid: ', callee_uuid, ', callee_initiated: ', calee); + if calee then + caller:execute('transfer', destination); + fapi:execute('uuid_kill', callee_uuid); + else + fapi:execute('uuid_transfer', callee_uuid .. ' ' .. destination); + caller.session:hangup(); + end +end diff --git a/misc/freeswitch/scripts/dialplan/functions.lua b/misc/freeswitch/scripts/dialplan/functions.lua index 2ca51c8..4430be1 100644 --- a/misc/freeswitch/scripts/dialplan/functions.lua +++ b/misc/freeswitch/scripts/dialplan/functions.lua @@ -111,6 +111,8 @@ function Functions.dialplan_function(self, caller, dialed_number) result = "+" .. tostring(parameters[3]); elseif fid == "hangup" then result = self:hangup(caller, parameters[3], parameters[4]); + elseif fid == "park" then + result = self:park(caller, parameters[3]); end return result; @@ -264,7 +266,7 @@ function Functions.account_node_change(self, caller) -- resync caller phones for index, phone_caller in ipairs(caller_phones) do - local result = phone_caller:resync{ auth_name = caller_sip_account.auth_name, domain = caller.domain }; + local result = phone_caller:resync{ auth_name = caller_sip_account.record.auth_name, domain = caller_sip_account.record.host }; self.log:info('NODE_CHANGE - resync phone - mac: ', phone_caller.record.mac_address, ', ip_address: ', phone_caller.record.ip_address, ', result: ', result); end @@ -302,6 +304,7 @@ function Functions.user_login(self, caller, number, pin) if not caller_phone then self.log:notice('LOGIN - caller phone not found or not hot-deskable'); + local result = phone_class:resync{ auth_name = caller_sip_account.record.auth_name, domain = caller_sip_account.record.host }; return { continue = false, code = 403, phrase = 'Phone not hot-deskable', no_cdr = true } end @@ -374,13 +377,13 @@ function Functions.user_login(self, caller, number, pin) -- resync destination phones for index, phone_destination in ipairs(destination_phones) do - local result = phone_destination:resync{ auth_name = destination_sip_account.auth_name, domain = caller.domain_local }; + local result = phone_destination:resync{ auth_name = destination_sip_account.record.auth_name, domain = destination_sip_account.record.host }; self.log:info('LOGIN - resync destination phone - mac: ', phone_destination.record.mac_address, ', ip_address: ', phone_destination.record.ip_address, ', result: ', result); end -- resync caller phones for index, phone_caller in ipairs(caller_phones) do - local result = phone_caller:resync{ auth_name = caller_sip_account.auth_name, domain = caller.domain }; + local result = phone_caller:resync{ auth_name = caller_sip_account.record.auth_name, domain = caller_sip_account.record.host }; self.log:info('LOGIN - resync caller phone - mac: ', phone_caller.record.mac_address, ', ip_address: ', phone_caller.record.ip_address, ', result: ', result); end @@ -409,8 +412,9 @@ function Functions.user_logout(self, caller) local caller_phones = phone_class:find_all_hot_deskable_by_account(caller_sip_account.id); - if caller_phones == 0 then + if #caller_phones == 0 then self.log:notice('LOGOUT - caller phones not found or not hot-deskable'); + local result = phone_class:resync{ auth_name = caller_sip_account.record.auth_name, domain = caller_sip_account.record.host }; return { continue = false, code = 403, phrase = 'Phone not hot-deskable', no_cdr = true } end @@ -426,7 +430,7 @@ function Functions.user_logout(self, caller) -- resync caller phones for index, phone_caller in ipairs(caller_phones) do - local result = phone_caller:resync{ auth_name = caller_sip_account.auth_name, domain = caller.domain }; + local result = phone_caller:resync{ auth_name = caller_sip_account.record.auth_name, domain = caller_sip_account.record.host }; self.log:info('LOGIN - resync caller phone - mac: ', phone_caller.record.mac_address, ', ip_address: ', phone_caller.record.ip_address, ', result: ', result); end @@ -909,3 +913,9 @@ function Functions.hangup(self, caller, code, phrase) self.log:info("FUNCTION_HANGUP code: ", code, ', phrase: ', phrase); return { continue = false, code = code, phrase = phrase:gsub('_', ' '), no_cdr = true } end + +function Functions.park(self, caller, lot) + self.log:info("FUNCTION_PARK lot: ", lot); + caller:execute("valet_park", 'valet_lot ' .. lot); + return { continue = false, code = 200, phrase = 'OK', no_cdr = true } +end diff --git a/misc/freeswitch/scripts/dialplan/hunt_group.lua b/misc/freeswitch/scripts/dialplan/hunt_group.lua index 2c73bf8..fa3c05b 100644 --- a/misc/freeswitch/scripts/dialplan/hunt_group.lua +++ b/misc/freeswitch/scripts/dialplan/hunt_group.lua @@ -98,6 +98,18 @@ function HuntGroup.run(self, dialplan_object, caller, destination) self.log:info('HUNTGROUP ', self.record.id, ' - name: ', self.record.name, ', strategy: ', self.record.strategy,', members: ', #hunt_group_members); + local clip_no_screening = common.str.try(caller, 'account.record.clip_no_screening'); + caller.caller_id_numbers = {} + if not common.str.blank(clip_no_screening) then + for index, number in ipairs(common.str.strip_to_a(clip_no_screening, ',')) do + table.insert(caller.caller_id_numbers, number); + end + end + for index, number in ipairs(caller.caller_phone_numbers) do + table.insert(caller.caller_id_numbers, number); + end + self.log:info('CALLER_ID_NUMBERS - clir: ', caller.clir, ', numbers: ', table.concat(caller.caller_id_numbers, ',')); + local save_destination = caller.destination; local destinations = {} @@ -172,6 +184,7 @@ function HuntGroup.run(self, dialplan_object, caller, destination) self.log:info('HUNTGROUP ', self.record.id, ' - all members busy'); run_queue = false; end + caller:sleep(500); end else if forwarding_destination then diff --git a/misc/freeswitch/scripts/dialplan/router.lua b/misc/freeswitch/scripts/dialplan/router.lua index cdcb58b..bda80a7 100644 --- a/misc/freeswitch/scripts/dialplan/router.lua +++ b/misc/freeswitch/scripts/dialplan/router.lua @@ -18,11 +18,16 @@ function Router.new(self, arg) self.routes = arg.routes or {}; self.caller = arg.caller; self.variables = arg.variables or {}; + self.routing_tables = {}; return object; end -function Router.read_table(self, table_name) +function Router.read_table(self, table_name, force_reload) + if not force_reload and self.routing_tables[table_name] then + return self.routing_tables[table_name]; + end + local routing_table = {}; local sql_query = 'SELECT * \ @@ -48,6 +53,8 @@ function Router.read_table(self, table_name) }); end); + self.routing_tables[table_name] = routing_table; + return routing_table; end @@ -143,7 +150,7 @@ function Router.route_match(self, route) if not common.str.blank(element.var_out) then local command, variable_name = common.str.partition(element.var_out, ':'); if not command or not variable_name or command == 'var' then - destination[element.var_out] = replacement; + common.str.set(destination, element.var_out, replacement); elseif command == 'chv' then destination.channel_variables[variable_name] = replacement; elseif command == 'hdr' then @@ -159,6 +166,7 @@ function Router.route_match(self, route) end if route_matches then + destination.number = destination.number or destination.destination_number; return destination; end; diff --git a/misc/freeswitch/scripts/dialplan/sip_call.lua b/misc/freeswitch/scripts/dialplan/sip_call.lua index d1557e9..b56f1b2 100644 --- a/misc/freeswitch/scripts/dialplan/sip_call.lua +++ b/misc/freeswitch/scripts/dialplan/sip_call.lua @@ -95,39 +95,50 @@ function SipCall.fork(self, destinations, arg ) table.insert(origination_variables, 'ignore_display_updates=true'); end - if not destination.node_local then + if not destination.node_local or destination.type == 'node' then require 'common.node' - local node = common.node.Node:new{ log = self.log, database = self.database }:find_by_id(destination.node_id); - 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); + local node = nil; + + if not destination.node_local then + node = common.node.Node:new{ log = self.log, database = self.database }:find_by_id(destination.node_id); + else + node = common.node.Node:new{ log = self.log, database = self.database }:find_by_id(destination.id); end - elseif destination.type == 'node' then - local node = common.node.Node:new{ log = self.log, database = self.database }:find_by_id(destination.id); if node then table.insert(origination_variables, 'sip_h_X-GS_node_id=' .. self.caller.local_node_id); + table.insert(origination_variables, 'sip_h_X-GS_account_uuid=' .. tostring(self.caller.account_uuid)); + table.insert(origination_variables, 'sip_h_X-GS_account_type=' .. tostring(self.caller.account_type)); + table.insert(origination_variables, 'sip_h_X-GS_auth_account_type=' .. tostring(self.caller.auth_account_type)); + table.insert(origination_variables, 'sip_h_X-GS_auth_account_uuid=' .. tostring(self.caller.auth_account_uuid)); + table.insert(origination_variables, 'sip_h_X-GS_loop_count=' .. tostring(self.caller.loop_count)); table.insert(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 sip_account then + self.log:notice('FORK - sip_account not found - sip_account=', destination.id); + elseif common.str.blank(sip_account.record.profile_name) or common.str.blank(sip_account.record.sip_host) then + call_result = { code = 480, phrase = 'User offline', disposition = 'USER_NOT_REGISTERED' }; + else + 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 .. "'"); + 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 , ',') .. ']sofia/' .. sip_account.record.profile_name .. '/' .. sip_account.record.auth_name .. '%' .. sip_account.record.sip_host); + else + some_destinations_busy = true; + call_result = { code = 486, phrase = 'User busy', disposition = 'USER_BUSY' }; 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 require 'common.gateway' @@ -169,11 +180,6 @@ function SipCall.fork(self, destinations, arg ) 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); @@ -195,6 +201,12 @@ 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 + session:execute('start_dtmf'); + end + if arg.detect_dtmf_after_bridge_callee then + session_callee:execute('start_dtmf'); + end if arg.bypass_media_network then local callee_uuid = session_callee:get_uuid(); @@ -226,10 +238,15 @@ function SipCall.fork(self, destinations, arg ) self.caller:set_variable('gs_destination_id', destination.id); self.caller:set_variable('gs_destination_uuid', destination.uuid); + if arg.detect_dtmf_after_bridge_callee then + session_callee:setInputCallback('input_call_back_callee', 'session_callee'); + end + 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 diff --git a/misc/freeswitch/scripts/dialplan_default.lua b/misc/freeswitch/scripts/dialplan_default.lua index 7caff57..8fb9057 100644 --- a/misc/freeswitch/scripts/dialplan_default.lua +++ b/misc/freeswitch/scripts/dialplan_default.lua @@ -8,13 +8,28 @@ 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_dialplan:object_find(start_caller.destination.type, start_caller.destination.id); + start_caller.auth_account = start_caller.dialplan:object_find(start_caller.destination.type, start_caller.destination.id); start_caller.forwarding_number = start_caller.destination.number; start_caller.forwarding_service = 'transfer'; end end end +function input_call_back_caller(s, object_type, object_data, arg) + if object_type == 'dtmf' then + require 'dialplan.dtmf' + local dtmf = dialplan.dtmf.Dtmf:new{ log = log, router = dtmf_router }:detect(start_caller, start_caller.dtmf, object_data.digit, object_data.duration); + end +end + +function input_call_back_callee(s, object_type, object_data, arg) + if object_type == 'dtmf' then + require 'dialplan.dtmf' + local dtmf = dialplan.dtmf.Dtmf:new{ log = log, router = dtmf_router }:detect(start_caller, start_caller.dtmf_callee, object_data.digit, object_data.duration, true); + end +end + + -- initialize logging require 'common.log' log = common.log.Log:new{ prefix = '### [' .. session:get_uuid() .. '] ' }; @@ -36,8 +51,17 @@ require 'dialplan.dialplan' local start_dialplan = dialplan.dialplan.Dialplan:new{ log = log, caller = start_caller, database = database }; start_dialplan:configuration_read(); +start_caller.dialplan = start_dialplan; start_caller.local_node_id = start_dialplan.node_id; start_caller:init_channel_variables(); +start_caller.dtmf = { + updated = os.time(), + digits = ''; +}; +start_caller.dtmf_callee = { + updated = os.time(), + digits = ''; +}; if start_dialplan.config.parameters.dump_variables then start_caller:execute('info', 'notice'); @@ -72,6 +96,17 @@ if start_caller.from_node then else start_destination = { type = 'unknown' } start_caller.session:setHangupHook('hangup_hook_caller', 'destination_number'); + + require 'dialplan.router' + dtmf_router = dialplan.router.Router:new{ log = log, database = database, caller = start_caller, variables = start_caller }; + start_dialplan.dtmf_detection = #dtmf_router:read_table('dtmf') > 0; + + if start_dialplan.dtmf_detection then + start_dialplan.detect_dtmf_after_bridge_caller = true; + start_dialplan.detect_dtmf_after_bridge_callee = true; + start_caller.session:setInputCallback('input_call_back_caller', 'start_dialplan'); + end + start_dialplan:run(start_destination); end diff --git a/misc/freeswitch/scripts/event/dump_variables.lua b/misc/freeswitch/scripts/event/dump_variables.lua new file mode 100644 index 0000000..96eb8f7 --- /dev/null +++ b/misc/freeswitch/scripts/event/dump_variables.lua @@ -0,0 +1,52 @@ +-- Gemeinschaft 5 module: dump_variables event handler class +-- (c) AMOOMA GmbH 2012-2013 +-- + +module(...,package.seeall) + + +function handler_class() + return DumpVariables +end + + +DumpVariables = {} + + +function DumpVariables.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.class = 'DumpVariables' + self.dump_file = arg.file or '/var/log/freeswitch/variables'; + + return object; +end + + +function DumpVariables.event_handlers(self) + return { CHANNEL_CREATE = { [true] = self.channel_data } } +end + + +function DumpVariables.channel_data(self, event) + local sequence = event:getHeader('Event-Sequence'); + local direction = event:getHeader('Call-Direction'); + + if not direction or direction ~= 'inbound' then + return; + end + + local file = io.open(self.dump_file, 'w'); + if not file then + self.log:error('[', event.sequence, '] DUMP_VARIABLES - could not open file for writing: ', self.dump_file); + return; + end + + self.log:debug('[', event.sequence, '] DUMP_VARIABLES - dumping channel data to: ', self.dump_file); + + file:write(event:serialize(), '\n'); + file:close(); +end diff --git a/misc/freeswitch/scripts/event/event.lua b/misc/freeswitch/scripts/event/event.lua index 08d8bfe..652fc48 100644 --- a/misc/freeswitch/scripts/event/event.lua +++ b/misc/freeswitch/scripts/event/event.lua @@ -16,29 +16,40 @@ function EventManager.new(self, arg) self.class = 'eventmanager' self.database = arg.database; self.domain = arg.domain; + self.consumer = arg.consumer; return object; end function EventManager.register(self) - self.consumer = freeswitch.EventConsumer('all'); + if not self.consumer then + self.consumer = freeswitch.EventConsumer('all'); + end return (self.consumer ~= nil); end -function EventManager.load_event_modules(self) - require 'common.configuration_table' - self.config = common.configuration_table.get(self.database, 'events'); +function EventManager.load_event_modules(self, modules) + local event_modules = {}; + + for module_name, index in pairs(modules) do + if tonumber(index) and index > 0 then + event_modules[index] = module_name; + self.log:debug('[event] EVENT_MANAGER - enabled handler module: ', module_name); + else + self.log:debug('[event] EVENT_MANAGER - disabled handler module: ', module_name); + end + end - return self.config.modules; + return event_modules; end function EventManager.load_event_handlers(self, event_modules) event_handlers = {} - - for event_module_name, index in pairs(event_modules) do + for index, event_module_name in ipairs(event_modules) do + package.loaded['event.' .. event_module_name] = nil; event_module = require('event.' .. event_module_name); if event_module then self.log:info('[event] EVENT_MANAGER - loading handler module: ', event_module_name); @@ -71,8 +82,12 @@ end function EventManager.run(self) + require 'common.configuration_table' + self.config = common.configuration_table.get(self.database, 'events'); + + local event_modules = self:load_event_modules(self.config.modules); + self.log:info('[event] EVENT_MANAGER_START - handler modules: ', #event_modules); - local event_modules = self:load_event_modules(); local event_handlers = self:load_event_handlers(event_modules); if not event_handlers then @@ -104,4 +119,6 @@ function EventManager.run(self) end end end + + self.log:info('[event] EVENT_MANAGER_EXIT - action: ', freeswitch.getGlobalVariable('gs_event_manager')); end diff --git a/misc/freeswitch/scripts/event/perimeter.lua b/misc/freeswitch/scripts/event/perimeter.lua deleted file mode 100644 index 5bbb032..0000000 --- a/misc/freeswitch/scripts/event/perimeter.lua +++ /dev/null @@ -1,107 +0,0 @@ --- Gemeinschaft 5 module: cdr event handler class --- (c) AMOOMA GmbH 2012-2013 --- - -module(...,package.seeall) - - -function handler_class() - return Perimeter -end - - - -Perimeter = {} - -MALICIOUS_CONTACT_COUNT = 20; -MALICIOUS_CONTACT_TIME_SPAN = 2; -BAN_FUTILE = 2; - -function Perimeter.new(self, arg) - arg = arg or {} - object = arg.object or {} - setmetatable(object, self); - self.__index = self; - self.log = arg.log; - self.class = 'cdrsave' - self.database = arg.database; - self.domain = arg.domain; - - self.ip_address_table = {} - self:init(); - - return object; -end - - -function Perimeter.event_handlers(self) - return { CUSTOM = { ['sofia::pre_register'] = self.sofia_pre_register } } -end - - -function Perimeter.init(self) - require 'common.configuration_table'; - local config = common.configuration_table.get(self.database, 'perimeter'); - if config and config.general then - self.malicious_contact_count = tonumber(config.general.malicious_contact_count) or MALICIOUS_CONTACT_COUNT; - self.malicious_contact_time_span = tonumber(config.general.malicious_contact_time_span) or MALICIOUS_CONTACT_TIME_SPAN; - self.ban_futile = tonumber(config.general.ban_futile) or BAN_FUTILE; - self.execute = config.general.execute; - end - - self.log:info('[perimeter] PERIMETER - setup perimeter defense - config: ', self.malicious_contact_count, '/', self.malicious_contact_time_span, ', execute: ', self.execute); -end - - -function Perimeter.sofia_pre_register(self, event) - local ip_address = event:getHeader('network-ip'); - self:check_ip(ip_address); -end - - -function Perimeter.check_ip(self, ip_address) - local event_time = os.time(); - - if not self.ip_address_table[ip_address] then - self.ip_address_table[ip_address] = { last_contact = event_time, contact_count = 0, start_stamp = event_time, banned = 0 } - end - - local ip_record = self.ip_address_table[ip_address]; - ip_record.last_contact = event_time; - ip_record.contact_count = ip_record.contact_count + 1; - - if ip_record.contact_count > MALICIOUS_CONTACT_COUNT then - if (event_time - ip_record.start_stamp) <= MALICIOUS_CONTACT_TIME_SPAN then - self.log:warning('[', ip_address, '] PERIMETER - too many registration attempts'); - ip_record.start_stamp = event_time; - ip_record.contact_count = 0; - if ip_record.banned < BAN_FUTILE then - ip_record.banned = ip_record.banned + 1; - self:ban_ip(ip_address); - else - self.log:error('[', ip_address, '] PERIMETER - ban futile'); - end - end - end -end - - -function Perimeter.ban_ip(self, ip_address) - self.ip_address = ip_address; - - if self.execute then - local command = self:expand_variables(self.execute); - self.log:debug('[', ip_address, '] PERIMETER - execute: ', command); - local result = os.execute(command); - if tostring(result) == '0' then - self.log:warning('[', ip_address, '] PERIMETER - IP banned'); - end - end -end - - -function Perimeter.expand_variables(self, line) - return (line:gsub('{([%a%d_-]+)}', function(captured) - return self[captured]; - end)) -end diff --git a/misc/freeswitch/scripts/event/perimeter_defense.lua b/misc/freeswitch/scripts/event/perimeter_defense.lua new file mode 100644 index 0000000..acdfa8d --- /dev/null +++ b/misc/freeswitch/scripts/event/perimeter_defense.lua @@ -0,0 +1,114 @@ +-- Gemeinschaft 5 module: perimeter defense event handler class +-- (c) AMOOMA GmbH 2013 +-- + +module(...,package.seeall) + +function handler_class() + return PerimeterDefense +end + +PerimeterDefense = {} + +function PerimeterDefense.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.log = arg.log; + self.class = 'perimeterdefense' + self.database = arg.database; + self.domain = arg.domain; + + require 'common.perimeter'; + self.perimeter = common.perimeter.Perimeter:new(arg); + self.perimeter:setup(); + + return object; +end + + +function PerimeterDefense.event_handlers(self) + return { + CUSTOM = { + ['sofia::pre_register'] = self.sofia_pre_register, + ['sofia::register_attempt'] = self.sofia_register_attempt, + ['sofia::register_failure'] = self.sofia_register_failure, + }, + CHANNEL_HANGUP = { [true] = self.channel_hangup }, + }; +end + + +function PerimeterDefense.to_register_record(self, event, class) + return { + action = 'register', + class = class, + key = event:getHeader('network-ip'), + sequence = tonumber(event:getHeader('Event-Sequence')), + timestamp = tonumber(event:getHeader('Event-Date-Timestamp')), + received_ip = event:getHeader('network-ip'), + received_port = event:getHeader('network-port'), + from_user = event:getHeader('from-user'), + from_host = event:getHeader('from-host'), + to_user = event:getHeader('to-user'), + to_host = event:getHeader('to-host'), + user_agent = event:getHeader('user-agent'), + username = event:getHeader('username'), + realm = event:getHeader('realm'), + auth_result = event:getHeader('auth-result'), + contact = event:getHeader('contact'), + }; +end + + +function PerimeterDefense.to_call_record(self, event, class) + return { + action = 'call', + class = class, + key = event:getHeader('Caller-Network-Addr'), + sequence = tonumber(event:getHeader('Event-Sequence')), + timestamp = tonumber(event:getHeader('Event-Date-Timestamp')), + received_ip = event:getHeader('Caller-Network-Addr'), + received_port = event:getHeader('variable_sip_network_port'), + hangup_cause = event:getHeader('Hangup-Cause'), + endpoint_disposition = event:getHeader('variable_endpoint_disposition'), + direction = event:getHeader('Call-Direction'), + destination_number = event:getHeader('Caller-Destination-Number'); + caller_id_name = event:getHeader('Caller-Caller-ID-Name'); + caller_id_number = event:getHeader('Caller-Caller-ID-Number'); + from_user = event:getHeader('variable_sip_from_user'), + from_host = event:getHeader('variable_sip_from_host'), + to_user = event:getHeader('variable_sip_to_user'), + to_host = event:getHeader('variable_sip_to_host'), + req_user = event:getHeader('variable_sip_req_user'), + req_host = event:getHeader('variable_sip_req_host'), + user_agent = event:getHeader('variable_sip_user_agent'), + username = event:getHeader('Caller-Username'), + contact = event:getHeader('variable_sip_contact_uri'), + }; +end + + +function PerimeterDefense.sofia_pre_register(self, event) + local record = self:to_register_record(event, 'pre_register'); + self.perimeter:check(record); +end + + +function PerimeterDefense.sofia_register_attempt(self, event) + local record = self:to_register_record(event, 'register_attempt'); + self.perimeter:check(record); +end + + +function PerimeterDefense.sofia_register_failure(self, event) + local record = self:to_register_record(event, 'register_failure'); + self.perimeter:check(record); +end + + +function PerimeterDefense.channel_hangup(self, event) + local record = self:to_call_record(event, 'channel_hangup'); + self.perimeter:check(record); +end diff --git a/misc/freeswitch/scripts/event_manager.lua b/misc/freeswitch/scripts/event_manager.lua index 1e61baf..3991f8f 100644 --- a/misc/freeswitch/scripts/event_manager.lua +++ b/misc/freeswitch/scripts/event_manager.lua @@ -7,12 +7,12 @@ require "common.log" local log = common.log.Log:new() log.prefix = "#E# " -log:info('[event] EVENT_MANAGER start'); +log:info('[event] EVENT_MANAGER_LOADER start'); require 'common.database' local database = common.database.Database:new{ log = log }:connect(); if not database:connected() then - log:error('[event] EVENT_MANAGER - cannot connect to Gemeinschaft database'); + log:error('[event] EVENT_MANAGER_LOADER - cannot connect to Gemeinschaft database'); return; end @@ -23,16 +23,23 @@ local domain = '127.0.0.1'; if domains[1] then domain = domains[1]['host']; else - log:error('[event] EVENT_MANAGER - No SIP domains found!'); + log:error('[event] EVENT_MANAGER_LOADER - No SIP domains found!'); end -require 'event.event' -local event_manager = event.event.EventManager:new{ log = log, database = database, domain = domain } -event_manager:run(); +local event_consumer = nil; +freeswitch.setGlobalVariable('gs_event_manager', 'true'); +while freeswitch.getGlobalVariable('gs_event_manager') ~= 'false' do + package.loaded['event.event'] = nil; + local manager_class = require('event.event'); + local event_manager = manager_class.EventManager:new{ log = log, database = database, domain = domain, consumer = event_consumer }; + freeswitch.setGlobalVariable('gs_event_manager', 'true'); + event_manager:run(); + event_consumer = event_manager.consumer; +end -- ensure database handle is released on exit if database then database:release(); end -log:info('[event] EVENT_MANAGER exit'); +log:info('[event] EVENT_MANAGER_LOADER exit'); diff --git a/misc/freeswitch/scripts/fax_daemon.lua b/misc/freeswitch/scripts/fax_daemon.lua index 94d343a..0784890 100644 --- a/misc/freeswitch/scripts/fax_daemon.lua +++ b/misc/freeswitch/scripts/fax_daemon.lua @@ -5,38 +5,37 @@ local MAIN_LOOP_SLEEP_TIME = 30; -- Set logger -require "common.log" +require 'common.log' local log = common.log.Log:new() -log.prefix = "### [faxdaemon] " +log.prefix = '#F# [faxdaemon] ' -log:debug('Starting fax daemon'); +log:info('FAX_DAEMON start'); -local database = nil; -local api = freeswitch.API(); +require 'common.database' +local database = common.database.Database:new{ log = log }:connect(); +if not database:connected() then + log:error('FAX_DAEMON - cannot connect to Gemeinschaft database'); + return; +end +local api = freeswitch.API(); +require 'dialplan.fax' freeswitch.setGlobalVariable('gs_fax_daemon', 'true'); + while freeswitch.getGlobalVariable("gs_fax_daemon") == 'true' do - require 'common.database' - local database = common.database.Database:new{ log = log }:connect(); - - if not database:connected() then - log:error("connection to Gemeinschaft database lost - retry in " .. MAIN_LOOP_SLEEP_TIME .. " seconds") - else - require 'dialplan.fax' - local fax_documents = dialplan.fax.Fax:new{log=log, database=database}:queued_for_sending(); - - for key, fax_document in pairs(fax_documents) do - if table.getn(fax_document.destination_numbers) > 0 and tonumber(fax_document.retry_counter) > 0 then - log:debug('FAX_DAEMON_LOOP - fax_document=', fax_document.id, '/', fax_document.uuid, ', number: ' .. fax_document.destination_numbers[1]); - local result = api:executeString('luarun send_fax.lua ' .. fax_document.id); - end + local fax_documents = dialplan.fax.Fax:new{log=log, database=database}:queued_for_sending(); + + for key, fax_document in pairs(fax_documents) do + if table.getn(fax_document.destination_numbers) > 0 and tonumber(fax_document.retry_counter) > 0 then + log:debug('FAX_DAEMON_LOOP - fax_document=', fax_document.id, '/', fax_document.uuid, ', number: ' .. fax_document.destination_numbers[1]); + local result = api:executeString('luarun send_fax.lua ' .. fax_document.id); end end - database:release(); - + if freeswitch.getGlobalVariable("gs_fax_daemon") == 'true' then freeswitch.msleep(MAIN_LOOP_SLEEP_TIME * 1000); end end -log:debug('Exiting fax daemon'); +database:release(); +log:info('FAX_DAEMON exit'); diff --git a/misc/freeswitch/scripts/http_request.lua b/misc/freeswitch/scripts/http_request.lua index 1d6f791..a74b4d8 100644 --- a/misc/freeswitch/scripts/http_request.lua +++ b/misc/freeswitch/scripts/http_request.lua @@ -29,7 +29,7 @@ end local success, result, response_headers = http.request{url = url, headers = headers }; -if success then +if success and tostring(result) == '200' then log:debug('HTTP_REQUEST - url: ', url, ', auth: ', tostring(headers.Authorization ~= nil), ', result: ', result); else log:notice('HTTP_REQUEST - url: ', url, ', auth: ', tostring(headers.Authorization ~= nil), ', result: ', result); diff --git a/misc/freeswitch/scripts/phones/phone.lua b/misc/freeswitch/scripts/phones/phone.lua index 6da20f0..57997ba 100644 --- a/misc/freeswitch/scripts/phones/phone.lua +++ b/misc/freeswitch/scripts/phones/phone.lua @@ -115,8 +115,9 @@ end function Phone.resync(self, arg) if not self.model then - self.log:notice('PHONE_RESYNC - unsupported phone model'); - return false; + self.log:notice('PHONE_RESYNC phone model not found - trying Snom resync'); + require 'phones.snom' + return phones.snom.Snom:new{ log = self.log }:resync(arg); end arg.ip_address = arg.ip_address or self.record.ip_address; diff --git a/misc/freeswitch/scripts/send_fax.lua b/misc/freeswitch/scripts/send_fax.lua index 9b6ffce..4898cb8 100644 --- a/misc/freeswitch/scripts/send_fax.lua +++ b/misc/freeswitch/scripts/send_fax.lua @@ -6,9 +6,9 @@ local FAX_FILE_PATH = "/opt/GS5/public/uploads/fax_document/tiff/"; local FAX_ANSWERING_TIMEOUT = 20; -- Set logger -require "common.log" +require 'common.log' local log = common.log.Log:new() -log.prefix = "### [sendfax] " +log.prefix = '#F# [sendfax] ' local document_id = argv[1]; @@ -16,12 +16,12 @@ require 'common.database' local database = common.database.Database:new{ log = log }:connect(); if not database:connected() then - log:error('cannot connect to Gemeinschaft database'); + log:error('FAX_SEND - cannot connect to Gemeinschaft database'); return end if not tonumber(document_id) then - log:error('document id not specified'); + log:error('FAX_SEND - document id not specified'); return end @@ -32,7 +32,7 @@ local fax_class = dialplan.fax.Fax:new(defaults); local fax_document = fax_class:find_document_by_id(document_id); if not fax_document then - log:error('document ' .. document_id .. ' not found'); + log:error('FAX_SEND - document ' .. document_id .. ' not found'); return end @@ -45,14 +45,14 @@ end local fax_account = fax_class:find_by_id(fax_document.fax_account_id); if not fax_account then - log:error('fax account ' .. fax_document.fax_account_id .. ' not found'); + log:error('FAX_SEND - fax account ' .. fax_document.fax_account_id .. ' not found'); return end local destination_number = fax_class:destination_number(document_id); if not destination_number or tostring(destination_number) == '' then - log:error('destination number not found'); + log:error('FAX_SEND - destination number not found'); return end |