Merge pull request #1451 in FS/freeswitch from ~LAZEDO/freeswitch:feature/FS-10820 to master

* commit 'bb4499ec24bd7a5cadb90eab1b1a05515d38eaa2':
  FS-10820 [mod_kazoo] eventstream configuration
This commit is contained in:
Mike Jerris 2018-01-19 18:57:14 +00:00
commit 11f8647ec2
20 changed files with 5723 additions and 1675 deletions

View File

@ -4,11 +4,23 @@ MODNAME=mod_kazoo
if HAVE_ERLANG
mod_LTLIBRARIES = mod_kazoo.la
mod_kazoo_la_SOURCES = mod_kazoo.c kazoo_utils.c kazoo_node.c kazoo_event_stream.c kazoo_fetch_agent.c kazoo_commands.c kazoo_dptools.c
mod_kazoo_la_SOURCES = mod_kazoo.c kazoo_utils.c kazoo_dptools.c kazoo_tweaks.c
mod_kazoo_la_SOURCES += kazoo_api.c kazoo_commands.c kazoo_config.c
mod_kazoo_la_SOURCES += kazoo_message.c
mod_kazoo_la_SOURCES += kazoo_ei_config.c kazoo_ei_utils.c kazoo_event_stream.c
mod_kazoo_la_SOURCES += kazoo_fetch_agent.c kazoo_node.c
mod_kazoo_la_CFLAGS = $(AM_CFLAGS) @ERLANG_CFLAGS@ -D_REENTRANT
mod_kazoo_la_LIBADD = $(switch_builddir)/libfreeswitch.la
mod_kazoo_la_LDFLAGS = -avoid-version -module -no-undefined -shared @ERLANG_LDFLAGS@
mod_kazoo.la: kazoo_definitions.h $(mod_kazoo_la_OBJECTS) $(mod_kazoo_la_DEPENDENCIES) $(EXTRA_mod_kazoo_la_DEPENDENCIES)
$(AM_V_CCLD)$(mod_kazoo_la_LINK) $(am_mod_kazoo_la_rpath) $(mod_kazoo_la_OBJECTS) $(mod_kazoo_la_LIBADD) $(LIBS)
kazoo_definitions.h:
xxd -i kazoo.conf.xml > kazoo_definitions.h
else
install: error
all: error

View File

@ -0,0 +1,953 @@
<?xml version="1.0" encoding="UTF-8"?>
<default>
<X-PRE-PROCESS cmd="set" data="UNIX_EPOCH_IN_GREGORIAN=62167219200"/>
<definitions>
<definition name="debug-call">
<filters>
<filter name="variable_debug_call" type="exclude" value="true" />
</filters>
<field name="Call-Debug" type="static" serialize-as="object">
<fields verbose="true" />
</field>
</definition>
<definition name="Custom-Channel-Vars">
<field name="Custom-Channel-Vars" type="static" serialize-as="object">
<fields verbose="false">
<field name="variable_sip_h_X-ecallmgr_" type="prefix"
exclude-prefix="true" />
<field name="variable_ecallmgr_" type="prefix"
exclude-prefix="true" />
<!-- special case for booleans -->
<field name="variable_ecallmgr_Caller-Privacy-Hide-Number"
as="Caller-Privacy-Hide-Number" serialize-as="boolean" />
<field name="variable_ecallmgr_Caller-Privacy-Hide-Name" as="Caller-Privacy-Hide-Name"
serialize-as="boolean" />
<field name="variable_ecallmgr_Caller-Screen-Bit" as="Caller-Screen-Bit"
serialize-as="boolean" />
<field name="Caller-Privacy-Hide-Number" serialize-as="boolean" />
<field name="Caller-Privacy-Hide-Name" serialize-as="boolean" />
<field name="Caller-Screen-Bit" serialize-as="boolean" />
<field name="Fetch-UUID" as="Fetch-ID" />
<field name="Referred-To" type="expand"
value="${url_decode(${variable_sip_refer_to})}">
<filters>
<filter name="variable_sip_refer_to" type="include"
compare="exists" />
</filters>
</field>
<field name="variable_sip_h_Referred-By" as="Referred-By" />
<field name="Call-Interaction-ID" type="expand"
value="${expr(ceil((${Event-Date-Timestamp} / 1000000) + $${UNIX_EPOCH_IN_GREGORIAN}))}-${regex(${create_uuid()}|^([^-]*)|%1)}">
<filters>
<filter name="ecallmgr_Call-Interaction-ID" type="exclude"
compare="exists" />
<filter name="variable_ecallmgr_Call-Interaction-ID" type="exclude"
compare="exists" />
<filter name="variable_sip_h_X-ecallmgr_Call-Interaction-ID"
type="exclude" compare="exists" />
</filters>
</field>
<field name="variable_presence_id" as="Presence-ID"/>
</fields>
</field>
</definition>
<definition name="Custom-Application-Vars">
<field name="Custom-Application-Vars" type="static"
serialize-as="object">
<filters>
<filter name="ecallmgr" type="include" compare="prefix"
value="variable_cav_" />
</filters>
<fields verbose="false">
<field name="variable_cav_" type="prefix" exclude-prefix="true" />
</fields>
</field>
</definition>
<definition name="Custom-SIP-Headers">
<field name="Custom-SIP-Headers" type="static" serialize-as="object">
<filters>
<filter name="ecallmgr" type="include" compare="prefix"
value="variable_sip_h_" />
</filters>
<fields verbose="false">
<field name="variable_sip_h_" type="prefix" exclude-prefix="true" />
</fields>
</field>
</definition>
<definition name="to-did">
<field name="To-DID" type="first-of"
value="variable_ecallmgr_E164-Destination|variable_ecallmgr_Original-Number|Caller-Destination-Number" />
</definition>
<definition name="from-network">
<field name="From-Network-Addr" type="first-of"
value="variable_sip_h_X-AUTH-IP|variable_sip_received_ip" />
<field name="From-Network-Port" type="first-of"
value="variable_sip_h_X-AUTH-PORT|variable_sip_received_port" />
</definition>
<definition name="call-direction">
<!-- <field name="Call-Direction" type="first-of" value="Caller-Logical-Direction|Call-Direction"
/> -->
<field name="Call-Direction" />
</definition>
<definition name="caller-id">
<field name="Caller-ID-Number" type="first-of"
value="variable_origination_caller_id_number|variable_effective_caller_id_number|Caller-Caller-ID-Number" />
<field name="Caller-ID-Name" type="first-of"
value="variable_origination_caller_id_name|variable_effective_caller_id_name|Caller-Caller-ID-Name" />
<field name="Callee-ID-Number" type="first-of"
value="variable_origination_callee_id_number|variable_effective_callee_id_number|Caller-Callee-ID-Number|Other-Leg-Caller-ID-Number" />
<field name="Callee-ID-Name" type="first-of"
value="variable_origination_callee_id_name|variable_effective_callee_id_name|Caller-Callee-ID-Name|Other-Leg-Caller-ID-Name" />
</definition>
<definition name="other-leg">
<field name="Other-Leg-Direction" />
<field name="Other-Leg-Caller-ID-Name" />
<field name="Other-Leg-Caller-ID-Number" />
<field name="Other-Leg-Destination-Number" />
<field name="Other-Leg-Call-ID" type="first-of"
value="Other-Leg-Unique-ID|Other-Leg-Call-ID|variable_origination_uuid" />
</definition>
<definition name="application">
<field name="Application-Name" type="first-of"
value="kazoo_application_name|Application|Event-Subclass" />
<field name="Application-Response" type="first-of"
value="kazoo_application_response|Application-Response" />
</definition>
<definition name="raw-application">
<field name="Raw-Application-Name" type="first-of"
value="Application|kazoo_application_name|Event-Subclass" />
<field name="Application-Data" as="Raw-Application-Data" />
</definition>
<definition name="application-all">
<field name="raw-application" type="reference" />
<field name="application" type="reference" />
</definition>
<definition name="application-uuid">
<field name="application-all" type="reference" />
<field name="Application-UUID" type="first-of"
value="app_uuid|variable_app_uuid|Application-UUID" />
</definition>
<definition name="Switch-URI">
<field name="variable_Switch-URI" as="Switch-URI" />
</definition>
<definition name="freeswitch-url">
<field name="FreeSWITCH-Hostname" as="Media-Server" />
<field name="FreeSWITCH-Hostname" as="Switch-Hostname" />
<field name="Switch-Nodename" />
<field name="variable_Switch-URL" as="Switch-URL" />
<field name="Switch-URI" type="reference" />
</definition>
<definition name="loopback">
<field name="variable_is_loopback" as="Channel-Is-Loopback"
serialize-as="boolean" />
<field name="Loopback" type="group">
<filters>
<filter name="variable_is_loopback" type="include" compare="value"
value="true" />
</filters>
<fields verbose="false">
<field name="variable_loopback_leg" as="Channel-Loopback-Leg" />
<field name="variable_other_loopback_leg_uuid" as="Channel-Loopback-Other-Leg-ID" />
<field name="variable_loopback_bowout" as="Channel-Loopback-Bowout"
serialize-as="boolean" />
<field name="variable_loopback_bowout_on_execute" as="Channel-Loopback-Bowout-Execute"
serialize-as="boolean" />
</fields>
</field>
</definition>
<definition name="caller-context">
<field name="variable_sofia_profile_name" as="Caller-Profile" />
<field name="Caller-Context" />
<field name="Caller-Dialplan" />
<field name="Caller-Destination-Number" />
</definition>
<definition name="hunt-context">
<field name="variable_sofia_profile_name" as="Hunt-Profile" />
<field name="Hunt-Context" />
<field name="Hunt-Dialplan" />
<field name="Hunt-Destination-Number" />
<field name="Request" type="expand"
value="${Hunt-Destination-Number}@${first-of(variable_sip_invite_domain|variable_sip_to_host|variable_sip_req_host)}" />
<field name="To" type="expand"
value="${Hunt-Destination-Number}@${first-of(variable_sip_invite_domain|variable_sip_to_host|variable_sip_req_host)}" />
</definition>
<definition name="sip-tags">
<field name="variable_sip_to_tag" as="To-Tag" />
<field name="variable_sip_from_tag" as="From-Tag" />
</definition>
<definition name="channel-state">
<!-- erlang strips CS_ from beginning, lets do that -->
<field name="Channel-State" type="expand"
value="${regex(${Channel-State}|^CS_(.*)$|%1)}" />
<field name="Channel-Call-State" />
<field name="Channel-Name" />
<field name="Answer-State" />
</definition>
<definition name="presence-id">
<field name="variable_presence_id" as="Presence-ID" />
</definition>
<definition name="call_event_headers">
<field name="Event-Category" type="static" value="call_event" />
<field name="Event-Name" type="first-of"
value="kazoo_event_name|Event-Subclass|Event-Name" />
<field name="Event-Date-Timestamp" as="Msg-ID" />
<field name="Timestamp" type="expand"
value="${expr(ceil((${Event-Date-Timestamp} / 1000000) + $${UNIX_EPOCH_IN_GREGORIAN}))}"
serialize-as="number" />
<field name="variable_sip_origination_call_id" as="Origination-Call-ID" />
<field name="Unique-ID" as="Call-ID" />
<field name="Caller-Channel-Created-Time" as="Channel-Created-Time" />
<field name="Switch-Nodename" as="Node" />
<field name="variable_sip_invite_domain" as="Domain-Name" />
<field name="App-Name" type="static" value="freeswitch" />
<field name="App-Version" type="static" value="1.0" />
</definition>
<definition name="user-agent">
<field name="variable_sip_user_agent" as="User-Agent" />
</definition>
<definition name="call_event">
<field name="call_event_headers" type="reference" />
<field name="application-all" type="reference" />
<field name="call-direction" type="reference" />
<field name="channel-state" type="reference" />
<field name="caller-id" type="reference" />
<field name="caller-context" type="reference" />
<field name="sip-tags" type="reference" />
<field name="presence-id" type="reference" />
<field name="loopback" type="reference" />
<field name="freeswitch-url" type="reference" />
<field name="other-leg" type="reference" />
<field name="user-agent" type="reference" />
<field name="Custom-Channel-Vars" type="reference" />
<field name="Custom-Application-Vars" type="reference" />
</definition>
<definition name="fetch-info">
<field name="Event-Category" type="static" value="fetch" />
<field name="App-Name" type="static" value="freeswitch" />
<field name="App-Version" type="static" value="1.0" />
<field name="Switch-Nodename" as="Node" />
<field name="Fetch-UUID" />
<field name="Fetch-UUID" as="Msg-ID" />
<field name="Fetch-Section" />
<field name="Fetch-Timeout" />
<field name="Fetch-Timestamp-Micro" />
<field name="Kazoo-Bundle" as="Fetch-Version" />
</definition>
<definition name="destination-number">
<field name="Destination-Number" type="first-of"
value="Hunt-Destination-Number|Caller-Destination-Number" />
</definition>
<definition name="from-to">
<field name="Request" type="first-of"
value="variable_sip_req_uri|variable_sip_loopback_req_uri" />
<field name="To" type="first-of"
value="variable_sip_to_uri|variable_sip_req_uri|variable_sip_loopback_req_uri" />
<field name="variable_sip_to_uri" as="To-Uri" />
<field name="From" type="first-of"
value="variable_sip_from_uri|variable_presence_id" />
<field name="variable_sip_from_uri" as="From-Uri" />
</definition>
<definition name="sdp">
<field name="variable_switch_r_sdp" as="Remote-SDP" />
<field name="variable_rtp_local_sdp_str" as="Local-SDP" />
</definition>
<definition name="call-duration">
<field name="variable_duration" as="Duration-Seconds"
serialize-as="number" />
<field name="variable_progresssec" as="Ringing-Seconds"
serialize-as="number" />
<field name="Billing-Seconds" type="expand"
value="${expr(ceil(${variable_billmsec} / 1000))}" serialize-as="number" />
</definition>
<definition name="hangup-disposition">
<field name="Disposition" type="first-of"
value="variable_originate_disposition|variable_endpoint_disposition" />
</definition>
<definition name="hangup-code">
<field name="Hangup-Code" type="first-of"
value="variable_proto_specific_hangup_cause|variable_last_bridge_proto_specific_hangup_cause" />
</definition>
<definition name="hangup-cause">
<field name="Hangup-Cause" type="first-of"
value="variable_bridge_hangup_cause|variable_hangup_cause|Hangup-Cause">
<filters>
<filter name="variable_current_application" type="include"
compare="value" value="bridge" />
</filters>
</field>
<field name="Hangup-Cause" type="first-of"
value="variable_hangup_cause|variable_bridge_hangup_cause|Hangup-Cause">
<filters>
<filter name="variable_current_application" type="exclude"
compare="value" value="bridge" />
</filters>
</field>
</definition>
<definition name="hangup-fields">
<field name="hangup-code" type="reference" />
<field name="hangup-cause" type="reference" />
<field name="hangup-disposition" type="reference" />
</definition>
<definition name="conference-vars">
<field name="Conference-Channel-Vars" type="static"
serialize-as="object">
<fields verbose="false">
<field name="variable_conference_moderator" as="Is-Moderator"
serialize-as="boolean" />
<field name="Floor" serialize-as="boolean" />
<field name="Video" serialize-as="boolean" />
<field name="See" serialize-as="boolean" />
<field name="Speak" serialize-as="boolean" />
<field name="Hear" serialize-as="boolean" />
<field name="Talking" serialize-as="boolean" />
<field name="Mute-Detect" serialize-as="boolean" />
<field name="Energy-Level" serialize-as="number" />
<field name="Current-Energy" serialize-as="number" />
<field name="Member-ID" serialize-as="number" />
<field name="Member-Ghost" serialize-as="boolean" />
</fields>
</field>
</definition>
<definition name="conference-event">
<field name="Event-Category" type="static" value="conference" />
<field name="Event-Name" type="static" value="event" />
<field name="Switch-Nodename" as="Node" />
<field name="Core-UUID" />
<field name="freeswitch-url" type="reference" />
<field name="App-Name" type="static" value="freeswitch" />
<field name="App-Version" type="static" value="1.0" />
<field name="Action" as="Event" />
<field name="Conference-Name" as="Conference-ID" />
<field name="Conference-Unique-ID" as="Instance-ID" />
<field name="Unique-ID" as="Call-ID" />
<field name="Account-ID" type="first-of"
value="variable_ecallmgr_Account-ID|Account-ID" />
<!-- <field name="Account-ID"/> -->
<!-- <field name="variable_ecallmgr_Account-ID" as="Account-ID"/> -->
<field name="Caller-Caller-ID-Name" as="Caller-ID-Name" />
<field name="Caller-Caller-ID-Number" as="Caller-ID-Number" />
<field name="Channel-Presence-ID" />
<field name="Member-ID" as="Participant-ID" serialize-as="number" />
<field name="Custom-Channel-Vars" type="reference" />
<field name="conference-vars" type="reference" />
<field name="Custom-Application-Vars" type="reference" />
</definition>
<definition name="recording">
<field name="Application-Name" type="static" value="record" />
<field name="Application-Response" type="first-of"
value="Record-File-Path|kazoo_application_response" />
</definition>
<definition name="fax_data">
<field name="variable_fax_success" as="Fax-Success"
serialize-as="boolean" />
<field name="variable_has_t38" as="Fax-T38-Used" serialize-as="boolean" />
<field name="variable_fax_ecm_used" as="Fax-ECM-Used"
serialize-as="boolean" />
<field name="variable_fax_document_transferred_pages" as="Fax-Transferred-Pages"
serialize-as="number" />
<field name="variable_fax_document_total_pages" as="Fax-Total-Pages"
serialize-as="number" />
<field name="variable_fax_bad_rows" as="Fax-Bad-Rows"
serialize-as="number" />
<field name="variable_fax_transfer_rate" as="Fax-Transfer-Rate"
serialize-as="number" />
<field name="variable_fax_local_station_id" as="Fax-Local-Station-ID" />
<field name="variable_fax_remote_station_id" as="Fax-Remote-Station-ID" />
<field name="variable_fax_remote_country" as="Fax-Remote-Country" />
<field name="variable_fax_remote_vendor" as="Fax-Remote-Vendor" />
<field name="variable_fax_remote_model" as="Fax-Remote-Model" />
<field name="variable_fax_image_resolution" as="Fax-Image-Resolution" />
<field name="variable_fax_file_image_resolution" as="Fax-File-Image-Resolution" />
<field name="variable_fax_image_size" as="Fax-Image-Size"
serialize-as="number" />
<field name="variable_fax_image_pixel_size" as="Fax-Image-Pixel-Size" />
<field name="variable_fax_file_image_pixel_size" as="Fax-File-Image-Pixel-Size" />
<field name="variable_fax_longest_bad_row_run" as="Fax-Longest-Bad-Row-Run"
serialize-as="number" />
<field name="variable_fax_encoding" as="Fax-Encoding" />
<field name="variable_fax_encoding_name" as="Fax-Encoding-Name" />
<field name="variable_fax_timezone" as="Fax-Timezone" />
<field name="variable_fax_ident" as="Fax-Identity-Number" />
<field name="variable_fax_header" as="Fax-Identity-Name" />
<field name="variable_fax_doc_id" as="Fax-Doc-ID" />
<field name="variable_fax_doc_database" as="Fax-Doc-DB" />
</definition>
<definition name="fax_event">
<field name="call_event_headers" type="reference" />
<field name="Event-Name" type="static" value="CHANNEL_FAX_STATUS" />
<field name="Application-Data" type="static" serialize-as="object">
<fields verbose="false">
<field name="fax_data" type="reference" />
</fields>
</field>
<field name="user-agent" as="reference" />
<field name="Custom-Channel-Vars" type="reference" />
<field name="Custom-Application-Vars" type="reference" />
</definition>
</definitions>
<event-handlers>
<profile name="default">
<events>
<event name="CHANNEL_CREATE">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="from-to" type="reference" />
<field name="debug-call" type="reference" />
</fields>
</event>
<event name="CHANNEL_ANSWER">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="from-to" type="reference" />
</fields>
</event>
<event name="CHANNEL_DESTROY">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="from-to" type="reference" />
<field name="sdp" type="reference" />
<field name="call-duration" type="reference" />
<field name="hangup-fields" type="reference" />
<field name="debug-call" type="reference" />
</fields>
<filters>
<filter name="variable_hangup_cause" type="exclude"
compare="value" value="CALL_REJECTED" />
</filters>
</event>
<event name="CHANNEL_PROGRESS_MEDIA">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="from-to" type="reference" />
<field name="debug-call" type="reference" />
</fields>
</event>
<event name="CHANNEL_BRIDGE">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="Bridge-A-Unique-ID" />
<field name="Bridge-B-Unique-ID" />
<field name="debug-call" type="reference" />
</fields>
</event>
<event name="CHANNEL_UNBRIDGE">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="Bridge-A-Unique-ID" />
<field name="Bridge-B-Unique-ID" />
<field name="application-uuid" type="reference" />
<field name="Application-Response" type="first-of"
value="variable_originate_disposition|#FAIL" />
<field name="Disposition" type="first-of"
value="variable_originate_disposition|variable_endpoint_disposition" />
<field name="debug-call" type="reference" />
</fields>
</event>
<event name="sofia::intercepted">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="Event-Name" type="static" value="CHANNEL_INTERCEPTED" />
<field name="Intercepted-By" type="first-of"
value="variable_intercepted_by|intercepted_by" />
</fields>
<routing-key>
<param name="format_fields" value="#call,#CHANNEL_INTERCEPTED,Unique-ID" />
</routing-key>
</event>
<event name="sofia::replaced">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="Event-Name" type="static" value="CHANNEL_REPLACED" />
<field name="att_xfer_replaced_by" type="first-of" as="Replaced-By"
value="variable_att_xfer_replaced_by|att_xfer_replaced_by" />
</fields>
<routing-key>
<param name="format_fields" value="#call,#CHANNEL_REPLACED,Unique-ID" />
</routing-key>
</event>
<event name="sofia::transferor">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="Event-Name" type="static" value="CHANNEL_TRANSFEROR" />
<!-- <field name="Intercepted-By" type="first-of" value="variable_intercepted_by|intercepted_by"/> -->
</fields>
<routing-key>
<param name="format_fields" value="#call,#CHANNEL_TRANSFEROR,Unique-ID" />
</routing-key>
</event>
<event name="sofia::transferee">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="Event-Name" type="static" value="CHANNEL_TRANSFEREE" />
<!-- <field name="Intercepted-By" type="first-of" value="variable_intercepted_by|intercepted_by"/> -->
</fields>
<routing-key>
<param name="format_fields" value="#call,#CHANNEL_TRANSFEREE,Unique-ID" />
</routing-key>
</event>
<event name="DTMF">
<filters>
<filter name="variable_conference_name" type="exclude"
compare="exists" />
</filters>
<fields verbose="false">
<field name="call_event_headers" type="reference" />
<field name="DTMF-Digit" />
<field name="DTMF-Duration" serialize-as="number" />
</fields>
</event>
<event name="DETECTED_TONE">
<fields verbose="true">
<field name="call_event_headers" type="reference" />
</fields>
</event>
<event name="RECORD_START">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="recording" type="reference" />
<field name="debug-call" type="reference" />
</fields>
</event>
<event name="RECORD_STOP">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="recording" type="reference" />
<field name="variable_playback_terminator_used" as="Terminator" />
<field name="variable_record_ms" as="Length" />
<field name="from-to" type="reference" />
<field name="variable_silence_hits_exhausted" as="Silence-Terminated"
serialize-as="boolean" />
<field name="Silence-Terminated" type="expand"
value="${cond(${variable_record_silence_hits} == 0 ? true : false)}"
serialize-as="boolean">
<filters>
<filter name="variable_silence_hits_exhausted" type="exclude"
compare="exists" />
</filters>
</field>
<field name="debug-call" type="reference" />
</fields>
</event>
<event name="conference::maintenance">
<filters>
<filter name="Action" type="include" compare="list"
value="conference-create|conference-destroy|lock|unlock|add-member|del-member|start-talking|stop-talking|mute-member|unmute-member|deaf-member|undeaf-member" />
</filters>
<fields verbose="false">
<field name="conference-event" type="reference" />
<field name="create-group" type="group">
<filters>
<filter name="Action" type="include" compare="value"
value="conference-create" />
</filters>
<fields verbose="false">
<field name="variable_ecallmgr_Ecallmgr-Node" as="Conference-Node" />
<field name="Conference-Profile-Name" as="Profile" />
</fields>
</field>
</fields>
</event>
<event name="kazoo::noop">
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="application-uuid" type="reference" />
<field name="Application-Name" type="static" value="noop" />
<field name="kazoo_application_response" as="Application-Response" />
<field name="debug-call" type="reference" />
</fields>
</event>
<event name="kazoo::masquerade">
<filters>
<filter type="exclude" name="kazoo_event_name" compare="value"
value="CHANNEL_EXECUTE_COMPLETE" />
</filters>
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="Application-Response" type="first-of"
value="variable_originate_disposition|#FAIL" />
<field name="Disposition" type="first-of"
value="variable_originate_disposition|variable_endpoint_disposition" />
</fields>
</event>
<event name="CHANNEL_EXECUTE_COMPLETE">
<filters>
<filter type="include" compare="exists"
name="variable_Call-Control-Queue" />
<filter type="exclude" name="Application" value="set" />
<filter name="Application" value="park" />
<filter name="Application" value="export" />
<filter name="Application" value="event" />
<filter name="Application" value="unshift" />
<filter name="Application" value="kz_multiset" />
<filter name="Application" value="kz_multiunset" />
<filter name="Application" value="kz_prefix_unset" />
<filter name="Application" value="kz_export" />
<filter name="Application" value="ring_ready" />
<filter name="Application" value="log" />
<filter name="Application" value="execute_extension" />
</filters>
<fields verbose="false">
<field name="call_event" type="reference" />
<field name="application-uuid" type="reference" />
<field name="Application-Response" type="first-of"
value="variable_originate_disposition|variable_endpoint_disposition|#NONE" />
<field name="Disposition" type="first-of"
value="variable_originate_disposition|variable_endpoint_disposition" />
<field name="variable_last_bridge_hangup_cause" as="Bridge-Hangup-Cause" />
<field name="debug-call" type="reference" />
</fields>
</event>
<event name="spandsp::txfaxnegociateresult">
<fields verbose="false">
<field name="fax_event" type="reference" />
<field name="Application-Name" type="static" value="send_fax" />
<field name="Application-Event" type="static" value="negociateresult" />
</fields>
</event>
<event name="spandsp::txfaxpageresult">
<fields verbose="false">
<field name="fax_event" type="reference" />
<field name="Application-Name" type="static" value="send_fax" />
<field name="Application-Event" type="static" value="pageresult" />
</fields>
</event>
<event name="spandsp::txfaxresult">
<fields verbose="true">
<field name="fax_event" type="reference" />
<field name="Application-Name" type="static" value="send_fax" />
<field name="Application-Event" type="static" value="result" />
</fields>
</event>
<event name="spandsp::rxfaxnegociateresult">
<fields verbose="false">
<field name="fax_event" type="reference" />
<field name="Application-Name" type="static" value="receive_fax" />
<field name="Application-Event" type="static" value="negociateresult" />
</fields>
</event>
<event name="spandsp::rxfaxpageresult">
<fields verbose="false">
<field name="fax_event" type="reference" />
<field name="Application-Name" type="static" value="receive_fax" />
<field name="Application-Event" type="static" value="pageresult" />
</fields>
</event>
<event name="spandsp::rxfaxresult">
<fields verbose="true">
<field name="fax_event" type="reference" />
<field name="Application-Name" type="static" value="receive_fax" />
<field name="Application-Event" type="static" value="result" />
</fields>
</event>
<event name="PRESENCE_IN">
<fields verbose="true">
<field name="call_event" type="reference" />
</fields>
</event>
<event name="CHANNEL_DATA">
<fields verbose="true">
<field name="call_event" type="reference" />
</fields>
</event>
<event name="CHANNEL_SYNC">
<fields verbose="true">
<field name="call_event" type="reference" />
</fields>
</event>
<event name="CALL_SECURE">
<fields verbose="true">
<field name="call_event_headers" type="reference" />
</fields>
</event>
<event name="CALL_UPDATE">
<fields verbose="true">
<field name="call_event" type="reference" />
</fields>
</event>
<event name="CHANNEL_HOLD">
<fields verbose="true">
<field name="call_event" type="reference" />
</fields>
</event>
<event name="CHANNEL_UNHOLD">
<fields verbose="true">
<field name="call_event" type="reference" />
</fields>
</event>
<event name="loopback::bowout">
<fields verbose="true">
<field name="call_event_headers" type="reference" />
</fields>
</event>
<event name="HEARTBEAT">
<fields verbose="true">
<field name="Core-UUID" />
<field name="FreeSWITCH-Hostname" as="AS-FreeSWITCH-Hostname" />
<field name="First-Expanded-FreeSWITCH-Hostname" type="expand"
value="${FreeSWITCH-Hostname}" />
<field name="Second-Expanded-FreeSWITCH-Hostname" type="expand"
value="${Event-Date-Timestamp}@${FreeSWITCH-Hostname}" />
<field name="Event-Category" type="static" value="myfreeswitch-category" />
<field name="Event-Info" as="FreeSWITCH-Is-Ready" />
<field name="First-Check-First-Of" type="first-of"
value="Event-Calling-Line-Number|Event-Sequence" />
<field name="Second-Check-First-Of" type="first-of"
value="Non-Existing|Event-Calling-Line-Number|Event-Sequence" />
<field name="FreeSWITCH" type="start-with" />
<field name="Session-Since-Startup" serialize-as="number" />
</fields>
</event>
<event name="ROUTE_WINNER">
<fields verbose="false">
<field name="Event-Category" type="static" value="dialplan" />
<field name="Event-Subclass" as="Event-Name" />
<field name="Unique-ID" as="Call-ID" />
<field name="Fetch-UUID" />
<field name="Fetch-Winning-PID" />
<field name="Controller-Queue" />
<field name="Controller-PID" />
<field name="Request-From-PID" as="Reply-To-PID" />
<field name="App-Name" type="static" value="mod_kazoo" />
<field name="App-Version" type="static" value="1.0" />
<field name="Custom-Channel-Vars" type="reference" />
</fields>
</event>
</events>
</profile>
</event-handlers>
<fetch-handlers>
<profile name="configuration">
<fields verbose="true">
<field name="fetch-info" type="reference" />
</fields>
<params>
<param name="fetch-timeout" value="3500000" />
<param name="fetch-section" value="configuration" />
</params>
<logging>
<log name="info" value="SWITCH_LOG_INFO" />
<log name="success" value="SWITCH_LOG_INFO" />
<log name="time" value="SWITCH_LOG_INFO" />
<log name="filtered-event" value="SWITCH_LOG_INFO" />
<log name="filtered-field" value="SWITCH_LOG_INFO" />
</logging>
</profile>
<profile name="dialplan" profile-type="fetch">
<fields>
<field name="fetch-info" type="reference" />
<field name="Timestamp" type="expand"
value="${expr(ceil((${Event-Date-Timestamp} / 1000000) + $${UNIX_EPOCH_IN_GREGORIAN}))}"
serialize-as="number" />
<field name="Unique-ID" as="Call-ID" />
<field name="variable_sip_invite_domain" as="Domain-Name" />
<field name="call-direction" type="reference" />
<field name="caller-id" type="reference" />
<field name="from-network" type="reference" />
<field name="user-agent" type="reference" />
<field name="from-to" type="reference" />
<field name="to-did" type="reference" />
<field name="sip-tags" type="reference" />
<field name="freeswitch-url" type="reference" />
<field name="Custom-SIP-Headers" type="reference" />
<field name="Custom-Channel-Vars" type="reference" />
<field name="Custom-Application-Vars" type="reference" />
<field name="destination-number" type="reference" />
<field name="Resource-Type" type="static" value="audio" />
<field name="hunt-context" type="reference" />
<field name="Call-Setup" type="static" value="true"
serialize-as="boolean" />
<field name="Event-Category" type="static" value="dialplan" />
<field name="Event-Name" type="static" value="route_req" />
<field name="Hunt-Context" as="Fetch-Key-Value" />
</fields>
<params>
<param name="fetch-timeout" value="3500000" />
<param name="fetch-section" value="dialplan" />
</params>
</profile>
<profile name="directory">
<fields verbose="true">
<field name="fetch-info" type="reference" />
</fields>
<params>
<param name="fetch-timeout" value="3500000" />
<param name="fetch-section" value="directory" />
</params>
</profile>
<profile name="channels">
<fields verbose="true">
<field name="fetch-info" type="reference" />
<field name="Event-Category" type="static" value="channels" />
<field name="Event-Name" type="static" value="query" />
</fields>
<params>
<param name="fetch-timeout" value="3500000" />
<param name="fetch-section" value="channels" />
</params>
</profile>
<profile name="languages">
<fields verbose="true">
<field name="fetch-info" type="reference" />
<field name="Event-Category" type="static" value="languages" />
<field name="Event-Name" type="static" value="route_req" />
</fields>
<params>
<param name="fetch-timeout" value="3500000" />
<param name="fetch-section" value="languages" />
</params>
</profile>
<profile name="chatplan">
<fields verbose="true">
<field name="fetch-info" type="reference" />
</fields>
<params>
<param name="fetch-timeout" value="3500000" />
<param name="fetch-section" value="chatplan" />
</params>
<logging>
<log name="info" value="SWITCH_LOG_INFO" />
<log name="success" value="SWITCH_LOG_INFO" />
<log name="time" value="SWITCH_LOG_INFO" />
<log name="filtered-event" value="SWITCH_LOG_INFO" />
<log name="filtered-field" value="SWITCH_LOG_INFO" />
</logging>
</profile>
</fetch-handlers>
</default>

View File

@ -0,0 +1,396 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Karl Anderson <karl@2600hz.com>
* Darren Schreiber <darren@2600hz.com>
*
*
* mod_kazoo.c -- Socket Controlled Event Handler
*
*/
#include "mod_kazoo.h"
#define KAZOO_DESC "kazoo information"
#define KAZOO_SYNTAX "<command> [<args>]"
#define API_COMMAND_DISCONNECT 0
#define API_COMMAND_REMOTE_IP 1
#define API_COMMAND_STREAMS 2
#define API_COMMAND_BINDINGS 3
static switch_status_t api_erlang_status(switch_stream_handle_t *stream) {
switch_sockaddr_t *sa;
uint16_t port;
char ipbuf[48];
const char *ip_addr;
ei_node_t *ei_node;
switch_socket_addr_get(&sa, SWITCH_FALSE, kazoo_globals.acceptor);
port = switch_sockaddr_get_port(sa);
ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa);
stream->write_function(stream, "Running %s\n", VERSION);
stream->write_function(stream, "Listening for new Erlang connections on %s:%u with cookie %s\n", ip_addr, port, kazoo_globals.ei_cookie);
stream->write_function(stream, "Registered as Erlang node %s, visible as %s\n", kazoo_globals.ei_cnode.thisnodename, kazoo_globals.ei_cnode.thisalivename);
if (kazoo_globals.ei_compat_rel) {
stream->write_function(stream, "Using Erlang compatibility mode: %d\n", kazoo_globals.ei_compat_rel);
}
switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock);
ei_node = kazoo_globals.ei_nodes;
if (!ei_node) {
stream->write_function(stream, "No erlang nodes connected\n");
} else {
stream->write_function(stream, "Connected to:\n");
while(ei_node != NULL) {
unsigned int year, day, hour, min, sec, delta;
delta = (switch_micro_time_now() - ei_node->created_time) / 1000000;
sec = delta % 60;
min = delta / 60 % 60;
hour = delta / 3600 % 24;
day = delta / 86400 % 7;
year = delta / 31556926 % 12;
stream->write_function(stream, " %s (%s:%d) up %d years, %d days, %d hours, %d minutes, %d seconds\n"
,ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port, year, day, hour, min, sec);
ei_node = ei_node->next;
}
}
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_erlang_event_filter(switch_stream_handle_t *stream) {
switch_hash_index_t *hi = NULL;
int column = 0;
for (hi = (switch_hash_index_t *)switch_core_hash_first_iter(kazoo_globals.event_filter, hi); hi; hi = switch_core_hash_next(&hi)) {
const void *key;
void *val;
switch_core_hash_this(hi, &key, NULL, &val);
stream->write_function(stream, "%-50s", (char *)key);
if (++column > 2) {
stream->write_function(stream, "\n");
column = 0;
}
}
if (++column > 2) {
stream->write_function(stream, "\n");
column = 0;
}
stream->write_function(stream, "%-50s", kazoo_globals.kazoo_var_prefix);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_erlang_nodes_list(switch_stream_handle_t *stream) {
ei_node_t *ei_node;
switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock);
ei_node = kazoo_globals.ei_nodes;
while(ei_node != NULL) {
stream->write_function(stream, "%s (%s)\n", ei_node->peer_nodename, ei_node->remote_ip);
ei_node = ei_node->next;
}
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_erlang_nodes_count(switch_stream_handle_t *stream) {
ei_node_t *ei_node;
int count = 0;
switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock);
ei_node = kazoo_globals.ei_nodes;
while(ei_node != NULL) {
count++;
ei_node = ei_node->next;
}
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
stream->write_function(stream, "%d\n", count);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_complete_erlang_node(const char *line, const char *cursor, switch_console_callback_match_t **matches) {
switch_console_callback_match_t *my_matches = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
ei_node_t *ei_node;
switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock);
ei_node = kazoo_globals.ei_nodes;
while(ei_node != NULL) {
switch_console_push_match(&my_matches, ei_node->peer_nodename);
ei_node = ei_node->next;
}
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
if (my_matches) {
*matches = my_matches;
status = SWITCH_STATUS_SUCCESS;
}
return status;
}
static switch_status_t handle_node_api_event_stream(ei_event_stream_t *event_stream, switch_stream_handle_t *stream) {
ei_event_binding_t *binding;
int column = 0;
switch_mutex_lock(event_stream->socket_mutex);
if (event_stream->connected == SWITCH_FALSE) {
switch_sockaddr_t *sa;
uint16_t port;
char ipbuf[48] = {0};
const char *ip_addr;
switch_socket_addr_get(&sa, SWITCH_TRUE, event_stream->acceptor);
port = switch_sockaddr_get_port(sa);
ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa);
if (zstr(ip_addr)) {
ip_addr = kazoo_globals.ip;
}
stream->write_function(stream, "%s:%d -> disconnected\n"
,ip_addr, port);
} else {
stream->write_function(stream, "%s:%d -> %s:%d\n"
,event_stream->local_ip, event_stream->local_port
,event_stream->remote_ip, event_stream->remote_port);
}
binding = event_stream->bindings;
while(binding != NULL) {
if (binding->type == SWITCH_EVENT_CUSTOM) {
stream->write_function(stream, "CUSTOM %-43s", binding->subclass_name);
} else {
stream->write_function(stream, "%-50s", switch_event_name(binding->type));
}
if (++column > 2) {
stream->write_function(stream, "\n");
column = 0;
}
binding = binding->next;
}
switch_mutex_unlock(event_stream->socket_mutex);
if (!column) {
stream->write_function(stream, "\n");
} else {
stream->write_function(stream, "\n\n");
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t handle_node_api_event_streams(ei_node_t *ei_node, switch_stream_handle_t *stream) {
ei_event_stream_t *event_stream;
switch_mutex_lock(ei_node->event_streams_mutex);
event_stream = ei_node->event_streams;
while(event_stream != NULL) {
handle_node_api_event_stream(event_stream, stream);
event_stream = event_stream->next;
}
switch_mutex_unlock(ei_node->event_streams_mutex);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t handle_node_api_command(ei_node_t *ei_node, switch_stream_handle_t *stream, uint32_t command) {
unsigned int year, day, hour, min, sec, delta;
switch (command) {
case API_COMMAND_DISCONNECT:
stream->write_function(stream, "Disconnecting erlang node %s at managers request\n", ei_node->peer_nodename);
switch_clear_flag(ei_node, LFLAG_RUNNING);
break;
case API_COMMAND_REMOTE_IP:
delta = (switch_micro_time_now() - ei_node->created_time) / 1000000;
sec = delta % 60;
min = delta / 60 % 60;
hour = delta / 3600 % 24;
day = delta / 86400 % 7;
year = delta / 31556926 % 12;
stream->write_function(stream, "Uptime %d years, %d days, %d hours, %d minutes, %d seconds\n", year, day, hour, min, sec);
stream->write_function(stream, "Local Address %s:%d\n", ei_node->local_ip, ei_node->local_port);
stream->write_function(stream, "Remote Address %s:%d\n", ei_node->remote_ip, ei_node->remote_port);
break;
case API_COMMAND_STREAMS:
handle_node_api_event_streams(ei_node, stream);
break;
case API_COMMAND_BINDINGS:
handle_api_command_streams(ei_node, stream);
break;
default:
break;
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_erlang_node_command(switch_stream_handle_t *stream, const char *nodename, uint32_t command) {
ei_node_t *ei_node;
switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock);
ei_node = kazoo_globals.ei_nodes;
while(ei_node != NULL) {
int length = strlen(ei_node->peer_nodename);
if (!strncmp(ei_node->peer_nodename, nodename, length)) {
handle_node_api_command(ei_node, stream, command);
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
return SWITCH_STATUS_SUCCESS;
}
ei_node = ei_node->next;
}
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
return SWITCH_STATUS_NOTFOUND;
}
SWITCH_STANDARD_API(exec_api_cmd)
{
char *argv[1024] = { 0 };
int unknown_command = 1, argc = 0;
char *mycmd = NULL;
const char *usage_string = "USAGE:\n"
"--------------------------------------------------------------------------------------------------------------------\n"
"erlang status - provides an overview of the current status\n"
"erlang event_filter - lists the event headers that will be sent to Erlang nodes\n"
"erlang nodes list - lists connected Erlang nodes (usefull for monitoring tools)\n"
"erlang nodes count - provides a count of connected Erlang nodes (usefull for monitoring tools)\n"
"erlang node <node_name> disconnect - disconnects an Erlang node\n"
"erlang node <node_name> connection - Shows the connection info\n"
"erlang node <node_name> event_streams - lists the event streams for an Erlang node\n"
"erlang node <node_name> fetch_bindings - lists the XML fetch bindings for an Erlang node\n"
"---------------------------------------------------------------------------------------------------------------------\n";
if (zstr(cmd)) {
stream->write_function(stream, "%s", usage_string);
return SWITCH_STATUS_SUCCESS;
}
if (!(mycmd = strdup(cmd))) {
return SWITCH_STATUS_MEMERR;
}
if (!(argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
stream->write_function(stream, "%s", usage_string);
switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS;
}
if (zstr(argv[0])) {
stream->write_function(stream, "%s", usage_string);
switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS;
}
if (!strncmp(argv[0], "status", 6)) {
unknown_command = 0;
api_erlang_status(stream);
} else if (!strncmp(argv[0], "event_filter", 6)) {
unknown_command = 0;
api_erlang_event_filter(stream);
} else if (!strncmp(argv[0], "nodes", 6) && !zstr(argv[1])) {
if (!strncmp(argv[1], "list", 6)) {
unknown_command = 0;
api_erlang_nodes_list(stream);
} else if (!strncmp(argv[1], "count", 6)) {
unknown_command = 0;
api_erlang_nodes_count(stream);
}
} else if (!strncmp(argv[0], "node", 6) && !zstr(argv[1]) && !zstr(argv[2])) {
if (!strncmp(argv[2], "disconnect", 6)) {
unknown_command = 0;
api_erlang_node_command(stream, argv[1], API_COMMAND_DISCONNECT);
} else if (!strncmp(argv[2], "connection", 2)) {
unknown_command = 0;
api_erlang_node_command(stream, argv[1], API_COMMAND_REMOTE_IP);
} else if (!strncmp(argv[2], "event_streams", 6)) {
unknown_command = 0;
api_erlang_node_command(stream, argv[1], API_COMMAND_STREAMS);
} else if (!strncmp(argv[2], "fetch_bindings", 6)) {
unknown_command = 0;
api_erlang_node_command(stream, argv[1], API_COMMAND_BINDINGS);
}
}
if (unknown_command) {
stream->write_function(stream, "%s", usage_string);
}
switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS;
}
void add_cli_api(switch_loadable_module_interface_t **module_interface, switch_api_interface_t *api_interface)
{
SWITCH_ADD_API(api_interface, "erlang", KAZOO_DESC, exec_api_cmd, KAZOO_SYNTAX);
switch_console_set_complete("add erlang status");
switch_console_set_complete("add erlang event_filter");
switch_console_set_complete("add erlang nodes list");
switch_console_set_complete("add erlang nodes count");
switch_console_set_complete("add erlang node ::erlang::node disconnect");
switch_console_set_complete("add erlang node ::erlang::node connection");
switch_console_set_complete("add erlang node ::erlang::node event_streams");
switch_console_set_complete("add erlang node ::erlang::node fetch_bindings");
switch_console_add_complete_func("::erlang::node", api_complete_erlang_node);
}
void remove_cli_api()
{
switch_console_set_complete("del erlang");
switch_console_del_complete_func("::erlang::node");
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -31,6 +31,7 @@
*
*/
#include "mod_kazoo.h"
#include <curl/curl.h>
#include <switch_curl.h>
#define UUID_SET_DESC "Set a variable"
@ -158,6 +159,11 @@ SWITCH_STANDARD_API(uuid_setvar_multi_function) {
return SWITCH_STATUS_SUCCESS;
}
static size_t body_callback(char *buffer, size_t size, size_t nitems, void *userdata)
{
return size * nitems;
}
static size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata)
{
switch_event_t* event = (switch_event_t*)userdata;
@ -182,6 +188,7 @@ SWITCH_STANDARD_API(kz_http_put)
switch_event_t *params = NULL;
char *url = NULL;
char *filename = NULL;
int delete = 0;
switch_curl_slist_t *headers = NULL; /* optional linked-list of HTTP headers */
char *ext; /* file extension, used for MIME type identification */
@ -279,6 +286,8 @@ SWITCH_STANDARD_API(kz_http_put)
switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-http-cache/1.0");
switch_curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, stream->param_event);
switch_curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_callback);
switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, body_callback);
switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
switch_curl_easy_perform(curl_handle);
switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes);
@ -286,22 +295,24 @@ SWITCH_STANDARD_API(kz_http_put)
if (httpRes == 200 || httpRes == 201 || httpRes == 202 || httpRes == 204) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s saved to %s\n", filename, url);
switch_event_add_header(stream->param_event, SWITCH_STACK_BOTTOM, "API-Output", "%s saved to %s\n", filename, url);
stream->write_function(stream, "+OK\n");
switch_event_add_header(stream->param_event, SWITCH_STACK_BOTTOM, "API-Output", "%s saved to %s", filename, url);
stream->write_function(stream, "+OK %s saved to %s", filename, url);
delete = 1;
} else {
error = switch_mprintf("Received HTTP error %ld trying to save %s to %s", httpRes, filename, url);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s\n", error);
switch_event_add_header(stream->param_event, SWITCH_STACK_BOTTOM, "API-Error", "%s", error);
switch_event_add_header(stream->param_event, SWITCH_STACK_BOTTOM, "API-HTTP-Error", "%ld", httpRes);
stream->write_function(stream, "-ERR ");
stream->write_function(stream, error);
stream->write_function(stream, "\n");
stream->write_function(stream, "-ERR %s", error);
status = SWITCH_STATUS_GENERR;
}
done:
if (file_to_put) {
fclose(file_to_put);
if(delete && kazoo_globals.delete_file_after_put) {
remove(filename);
}
}
if (headers) {

View File

@ -0,0 +1,535 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Based on mod_skel by
* Anthony Minessale II <anthm@freeswitch.org>
*
* Contributor(s):
*
* Daniel Bryars <danb@aeriandi.com>
* Tim Brown <tim.brown@aeriandi.com>
* Anthony Minessale II <anthm@freeswitch.org>
* William King <william.king@quentustech.com>
* Mike Jerris <mike@jerris.com>
*
* kazoo.c -- Sends FreeSWITCH events to an AMQP broker
*
*/
#include "mod_kazoo.h"
static const char *LOG_LEVEL_NAMES[] = {
"SWITCH_LOG_DEBUG10",
"SWITCH_LOG_DEBUG9",
"SWITCH_LOG_DEBUG8",
"SWITCH_LOG_DEBUG7",
"SWITCH_LOG_DEBUG6",
"SWITCH_LOG_DEBUG5",
"SWITCH_LOG_DEBUG4",
"SWITCH_LOG_DEBUG3",
"SWITCH_LOG_DEBUG2",
"SWITCH_LOG_DEBUG1",
"SWITCH_LOG_DEBUG",
"SWITCH_LOG_INFO",
"SWITCH_LOG_NOTICE",
"SWITCH_LOG_WARNING",
"SWITCH_LOG_ERROR",
"SWITCH_LOG_CRIT",
"SWITCH_LOG_ALERT",
"SWITCH_LOG_CONSOLE",
"SWITCH_LOG_INVALID",
"SWITCH_LOG_UNINIT",
NULL
};
static const switch_log_level_t LOG_LEVEL_VALUES[] = {
SWITCH_LOG_DEBUG10,
SWITCH_LOG_DEBUG9,
SWITCH_LOG_DEBUG8,
SWITCH_LOG_DEBUG7,
SWITCH_LOG_DEBUG6,
SWITCH_LOG_DEBUG5,
SWITCH_LOG_DEBUG4,
SWITCH_LOG_DEBUG3,
SWITCH_LOG_DEBUG2,
SWITCH_LOG_DEBUG1,
SWITCH_LOG_DEBUG,
SWITCH_LOG_INFO,
SWITCH_LOG_NOTICE,
SWITCH_LOG_WARNING,
SWITCH_LOG_ERROR,
SWITCH_LOG_CRIT,
SWITCH_LOG_ALERT,
SWITCH_LOG_CONSOLE,
SWITCH_LOG_INVALID,
SWITCH_LOG_UNINIT
};
switch_log_level_t log_str2level(const char *str)
{
int x = 0;
switch_log_level_t level = SWITCH_LOG_INVALID;
if (switch_is_number(str)) {
x = atoi(str);
if (x > SWITCH_LOG_INVALID) {
return SWITCH_LOG_INVALID - 1;
} else if (x < 0) {
return 0;
} else {
return x;
}
}
for (x = 0;; x++) {
if (!LOG_LEVEL_NAMES[x]) {
break;
}
if (!strcasecmp(LOG_LEVEL_NAMES[x], str)) {
level = LOG_LEVEL_VALUES[x]; //(switch_log_level_t) x;
break;
}
}
return level;
}
switch_status_t kazoo_config_loglevels(switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_loglevels_ptr *ptr)
{
switch_xml_t xml_level, xml_logging;
kazoo_loglevels_ptr loglevels = (kazoo_loglevels_ptr) switch_core_alloc(pool, sizeof(kazoo_loglevels_t));
loglevels->failed_log_level = SWITCH_LOG_ALERT;
loglevels->filtered_event_log_level = SWITCH_LOG_DEBUG1;
loglevels->filtered_field_log_level = SWITCH_LOG_DEBUG1;
loglevels->info_log_level = SWITCH_LOG_INFO;
loglevels->warn_log_level = SWITCH_LOG_WARNING;
loglevels->success_log_level = SWITCH_LOG_DEBUG;
loglevels->time_log_level = SWITCH_LOG_DEBUG1;
if ((xml_logging = switch_xml_child(cfg, "logging")) != NULL) {
for (xml_level = switch_xml_child(xml_logging, "log"); xml_level; xml_level = xml_level->next) {
char *var = (char *) switch_xml_attr_soft(xml_level, "name");
char *val = (char *) switch_xml_attr_soft(xml_level, "value");
if (!var) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "logging param missing 'name' attribute\n");
continue;
}
if (!val) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "logging param[%s] missing 'value' attribute\n", var);
continue;
}
if (!strncmp(var, "success", 7)) {
loglevels->success_log_level = log_str2level(val);
} else if (!strncmp(var, "failed", 6)) {
loglevels->failed_log_level = log_str2level(val);
} else if (!strncmp(var, "info", 4)) {
loglevels->info_log_level = log_str2level(val);
} else if (!strncmp(var, "warn", 4)) {
loglevels->warn_log_level = log_str2level(val);
} else if (!strncmp(var, "time", 4)) {
loglevels->time_log_level = log_str2level(val);
} else if (!strncmp(var, "filtered-event", 14)) {
loglevels->filtered_event_log_level = log_str2level(val);
} else if (!strncmp(var, "filtered-field", 14)) {
loglevels->filtered_field_log_level = log_str2level(val);
}
} /* xml_level for loop */
}
*ptr = loglevels;
return SWITCH_STATUS_SUCCESS;
}
switch_status_t kazoo_config_filters(switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_filter_ptr *ptr)
{
switch_xml_t filters, filter;
// char *routing_key = NULL;
kazoo_filter_ptr root = NULL, prv = NULL, cur = NULL;
if ((filters = switch_xml_child(cfg, "filters")) != NULL) {
for (filter = switch_xml_child(filters, "filter"); filter; filter = filter->next) {
const char *var = switch_xml_attr(filter, "name");
const char *val = switch_xml_attr(filter, "value");
const char *type = switch_xml_attr(filter, "type");
const char *compare = switch_xml_attr(filter, "compare");
cur = (kazoo_filter_ptr) switch_core_alloc(pool, sizeof(kazoo_filter));
memset(cur, 0, sizeof(kazoo_filter));
if(prv == NULL) {
root = prv = cur;
} else {
prv->next = cur;
prv = cur;
}
cur->type = FILTER_EXCLUDE;
cur->compare = FILTER_COMPARE_VALUE;
if(var)
cur->name = switch_core_strdup(pool, var);
if(val)
cur->value = switch_core_strdup(pool, val);
if(type) {
if (!strncmp(type, "exclude", 7)) {
cur->type = FILTER_EXCLUDE;
} else if (!strncmp(type, "include", 7)) {
cur->type = FILTER_INCLUDE;
}
}
if(compare) {
if (!strncmp(compare, "value", 7)) {
cur->compare = FILTER_COMPARE_VALUE;
} else if (!strncmp(compare, "prefix", 6)) {
cur->compare = FILTER_COMPARE_PREFIX;
} else if (!strncmp(compare, "list", 4)) {
cur->compare = FILTER_COMPARE_LIST;
} else if (!strncmp(compare, "exists", 6)) {
cur->compare = FILTER_COMPARE_EXISTS;
} else if (!strncmp(compare, "regex", 5)) {
cur->compare = FILTER_COMPARE_REGEX;
}
}
if(cur->value == NULL)
cur->compare = FILTER_COMPARE_EXISTS;
if(cur->compare == FILTER_COMPARE_LIST) {
cur->list.size = switch_separate_string(cur->value, '|', cur->list.value, MAX_LIST_FIELDS);
}
}
}
*ptr = root;
return SWITCH_STATUS_SUCCESS;
}
switch_status_t kazoo_config_field(kazoo_config_ptr definitions, switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_field_ptr *ptr)
{
const char *var = switch_xml_attr(cfg, "name");
const char *val = switch_xml_attr(cfg, "value");
const char *as = switch_xml_attr(cfg, "as");
const char *type = switch_xml_attr(cfg, "type");
const char *exclude_prefix = switch_xml_attr(cfg, "exclude-prefix");
const char *serialize_as = switch_xml_attr(cfg, "serialize-as");
kazoo_field_ptr cur = (kazoo_field_ptr) switch_core_alloc(pool, sizeof(kazoo_field));
cur->in_type = FIELD_NONE;
cur->out_type = JSON_NONE;
if(var)
cur->name = switch_core_strdup(pool, var);
if(val)
cur->value = switch_core_strdup(pool, val);
if(as)
cur->as = switch_core_strdup(pool, as);
if(type) {
if (!strncmp(type, "copy", 4)) {
cur->in_type = FIELD_COPY;
} else if (!strncmp(type, "static", 6)) {
cur->in_type = FIELD_STATIC;
} else if (!strncmp(type, "first-of", 8)) {
cur->in_type = FIELD_FIRST_OF;
} else if (!strncmp(type, "expand", 6)) {
cur->in_type = FIELD_EXPAND;
} else if (!strncmp(type, "prefix", 10)) {
cur->in_type = FIELD_PREFIX;
} else if (!strncmp(type, "group", 5)) {
cur->in_type = FIELD_GROUP;
} else if (!strncmp(type, "reference", 9)) {
cur->in_type = FIELD_REFERENCE;
}
}
if(serialize_as) {
if (!strncmp(serialize_as, "string", 5)) {
cur->out_type = JSON_STRING;
} else if (!strncmp(serialize_as, "number", 6)) {
cur->out_type = JSON_NUMBER;
} else if (!strncmp(serialize_as, "boolean", 7)) {
cur->out_type = JSON_BOOLEAN;
} else if (!strncmp(serialize_as, "object", 6)) {
cur->out_type = JSON_OBJECT;
} else if (!strncmp(serialize_as, "raw", 6)) {
cur->out_type = JSON_RAW;
}
}
if(exclude_prefix)
cur->exclude_prefix = switch_true(exclude_prefix);
kazoo_config_filters(pool, cfg, &cur->filter);
kazoo_config_fields(definitions, pool, cfg, &cur->children);
if(cur->children != NULL
&& (cur->in_type == FIELD_STATIC)
&& (cur->out_type == JSON_NONE)
) {
cur->out_type = JSON_OBJECT;
}
if(cur->in_type == FIELD_NONE) {
cur->in_type = FIELD_COPY;
}
if(cur->out_type == JSON_NONE) {
cur->out_type = JSON_STRING;
}
if(cur->in_type == FIELD_FIRST_OF) {
cur->list.size = switch_separate_string(cur->value, '|', cur->list.value, MAX_LIST_FIELDS);
}
if(cur->in_type == FIELD_REFERENCE) {
cur->ref = (kazoo_definition_ptr)switch_core_hash_find(definitions->hash, cur->name);
if(cur->ref == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "referenced field %s not found\n", cur->name);
}
}
*ptr = cur;
return SWITCH_STATUS_SUCCESS;
}
switch_status_t kazoo_config_fields_loop(kazoo_config_ptr definitions, switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_field_ptr *ptr)
{
switch_xml_t field;
kazoo_field_ptr root = NULL, prv = NULL;
for (field = switch_xml_child(cfg, "field"); field; field = field->next) {
kazoo_field_ptr cur = NULL;
kazoo_config_field(definitions, pool, field, &cur);
if(root == NULL) {
root = prv = cur;
} else {
prv->next = cur;
prv = cur;
}
}
*ptr = root;
return SWITCH_STATUS_SUCCESS;
}
switch_status_t kazoo_config_fields(kazoo_config_ptr definitions, switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_fields_ptr *ptr)
{
switch_xml_t fields;
// char *routing_key = NULL;
kazoo_fields_ptr root = NULL;
if ((fields = switch_xml_child(cfg, "fields")) != NULL) {
const char *verbose = switch_xml_attr(fields, "verbose");
root = (kazoo_fields_ptr) switch_core_alloc(pool, sizeof(kazoo_fields));
root->verbose = SWITCH_TRUE;
if(verbose) {
root->verbose = switch_true(verbose);
}
kazoo_config_fields_loop(definitions, pool, fields, &root->head);
}
*ptr = root;
return SWITCH_STATUS_SUCCESS;
}
kazoo_config_ptr kazoo_config_event_handlers(kazoo_config_ptr definitions, switch_xml_t cfg)
{
switch_xml_t xml_profiles = NULL, xml_profile = NULL;
kazoo_config_ptr profiles = NULL;
switch_memory_pool_t *pool = NULL;
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "error creating memory pool for producers\n");
return NULL;
}
profiles = switch_core_alloc(pool, sizeof(kazoo_config));
profiles->pool = pool;
switch_core_hash_init(&profiles->hash);
if ((xml_profiles = switch_xml_child(cfg, "event-handlers"))) {
if ((xml_profile = switch_xml_child(xml_profiles, "profile"))) {
for (; xml_profile; xml_profile = xml_profile->next) {
const char *name = switch_xml_attr(xml_profile, "name");
if(name == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing attr name\n" );
continue;
}
kazoo_config_event_handler(definitions, profiles, xml_profile, NULL);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unable to locate a event-handler profile for kazoo\n" );
}
} else {
destroy_config(&profiles);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unable to locate event-handlers section for kazoo\n" );
}
return profiles;
}
kazoo_config_ptr kazoo_config_fetch_handlers(kazoo_config_ptr definitions, switch_xml_t cfg)
{
switch_xml_t xml_profiles = NULL, xml_profile = NULL;
kazoo_config_ptr profiles = NULL;
switch_memory_pool_t *pool = NULL;
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "error creating memory pool for producers\n");
return NULL;
}
profiles = switch_core_alloc(pool, sizeof(kazoo_config));
profiles->pool = pool;
switch_core_hash_init(&profiles->hash);
if ((xml_profiles = switch_xml_child(cfg, "fetch-handlers"))) {
if ((xml_profile = switch_xml_child(xml_profiles, "profile"))) {
for (; xml_profile; xml_profile = xml_profile->next) {
const char *name = switch_xml_attr(xml_profile, "name");
if(name == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing attr name\n" );
continue;
}
kazoo_config_fetch_handler(definitions, profiles, xml_profile, NULL);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unable to locate a fetch-handler profile for kazoo\n" );
}
} else {
destroy_config(&profiles);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unable to locate fetch-handlers section for kazoo\n" );
}
return profiles;
}
switch_status_t kazoo_config_definition(kazoo_config_ptr root, switch_xml_t cfg)
{
kazoo_definition_ptr definition = NULL;
char *name = (char *) switch_xml_attr_soft(cfg, "name");
if (zstr(name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to load kazoo profile. Check definition missing name attr\n");
return SWITCH_STATUS_GENERR;
}
definition = switch_core_alloc(root->pool, sizeof(kazoo_definition));
definition->name = switch_core_strdup(root->pool, name);
kazoo_config_filters(root->pool, cfg, &definition->filter);
kazoo_config_fields_loop(root, root->pool, cfg, &definition->head);
if ( switch_core_hash_insert(root->hash, name, (void *) definition) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to insert new definition [%s] into kazoo definitions hash\n", name);
return SWITCH_STATUS_GENERR;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Definition[%s] Successfully configured\n", definition->name);
return SWITCH_STATUS_SUCCESS;
}
kazoo_config_ptr kazoo_config_definitions(switch_xml_t cfg)
{
switch_xml_t xml_definitions = NULL, xml_definition = NULL;
kazoo_config_ptr definitions = NULL;
switch_memory_pool_t *pool = NULL;
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "error creating memory pool for definitions\n");
return NULL;
}
definitions = switch_core_alloc(pool, sizeof(kazoo_config));
definitions->pool = pool;
switch_core_hash_init(&definitions->hash);
if ((xml_definitions = switch_xml_child(cfg, "definitions"))) {
if ((xml_definition = switch_xml_child(xml_definitions, "definition"))) {
for (; xml_definition; xml_definition = xml_definition->next) {
kazoo_config_definition(definitions, xml_definition);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "no definitions for kazoo\n" );
}
} else {
destroy_config(&definitions);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "no definitions section for kazoo\n" );
}
return definitions;
}
void destroy_config(kazoo_config_ptr *ptr)
{
kazoo_config_ptr config = NULL;
switch_memory_pool_t *pool;
if (!ptr || !*ptr) {
return;
}
config = *ptr;
pool = config->pool;
switch_core_hash_destroy(&(config->hash));
switch_core_destroy_memory_pool(&pool);
*ptr = NULL;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4
*/

View File

@ -0,0 +1,62 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Based on mod_skel by
* Anthony Minessale II <anthm@freeswitch.org>
*
* Contributor(s):
*
* Daniel Bryars <danb@aeriandi.com>
* Tim Brown <tim.brown@aeriandi.com>
* Anthony Minessale II <anthm@freeswitch.org>
* William King <william.king@quentustech.com>
* Mike Jerris <mike@jerris.com>
*
* kazoo.c -- Sends FreeSWITCH events to an AMQP broker
*
*/
#ifndef KAZOO_CONFIG_H
#define KAZOO_CONFIG_H
#include <switch.h>
struct kazoo_config_t {
switch_hash_t *hash;
switch_memory_pool_t *pool;
};
switch_status_t kazoo_config_loglevels(switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_loglevels_ptr *ptr);
switch_status_t kazoo_config_filters(switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_filter_ptr *ptr);
switch_status_t kazoo_config_fields(kazoo_config_ptr definitions, switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_fields_ptr *ptr);
switch_status_t kazoo_config_event_handler(kazoo_config_ptr definitions, kazoo_config_ptr root, switch_xml_t cfg, kazoo_event_profile_ptr *ptr);
switch_status_t kazoo_config_fetch_handler(kazoo_config_ptr definitions, kazoo_config_ptr root, switch_xml_t cfg, kazoo_fetch_profile_ptr *ptr);
kazoo_config_ptr kazoo_config_event_handlers(kazoo_config_ptr definitions, switch_xml_t cfg);
kazoo_config_ptr kazoo_config_fetch_handlers(kazoo_config_ptr definitions, switch_xml_t cfg);
kazoo_config_ptr kazoo_config_definitions(switch_xml_t cfg);
void destroy_config(kazoo_config_ptr *ptr);
#endif /* KAZOO_CONFIG_H */

View File

@ -52,6 +52,14 @@
#define EXPORT_LONG_DESC "Export many channel variables for the channel calling the application"
#define EXPORT_SYNTAX "[^^<delim>]<varname>=<value> <var2>=<val2>"
#define PREFIX_UNSET_SHORT_DESC "clear variables by prefix"
#define PREFIX_UNSET_LONG_DESC "clears the channel variables that start with prefix supplied"
#define PREFIX_UNSET_SYNTAX "<prefix>"
#define UUID_MULTISET_SHORT_DESC "Set many channel variables"
#define UUID_MULTISET_LONG_DESC "Set many channel variables for a specific channel"
#define UUID_MULTISET_SYNTAX "<uuid> [^^<delim>]<varname>=<value> <var2>=<val2>"
static void base_set (switch_core_session_t *session, const char *data, switch_stack_t stack) {
char *var, *val = NULL;
@ -99,6 +107,22 @@ static void base_set (switch_core_session_t *session, const char *data, switch_s
}
}
static int kz_is_exported(switch_core_session_t *session, char *varname)
{
char *array[256] = {0};
int i, argc;
switch_channel_t *channel = switch_core_session_get_channel(session);
const char *exports = switch_channel_get_variable(channel, SWITCH_EXPORT_VARS_VARIABLE);
char *arg = switch_core_session_strdup(session, exports);
argc = switch_split(arg, ',', array);
for(i=0; i < argc; i++) {
if(!strcasecmp(array[i], varname))
return 1;
}
return 0;
}
static void base_export (switch_core_session_t *session, const char *data, switch_stack_t stack) {
char *var, *val = NULL;
@ -121,20 +145,52 @@ static void base_export (switch_core_session_t *session, const char *data, switc
}
}
if (val) {
expanded = switch_channel_expand_variables(channel, val);
}
if(!kz_is_exported(session, var)) {
if (val) {
expanded = switch_channel_expand_variables(channel, val);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s EXPORT [%s]=[%s]\n", switch_channel_get_name(channel), var,
expanded ? expanded : "UNDEF");
switch_channel_export_variable_var_check(channel, var, expanded, SWITCH_EXPORT_VARS_VARIABLE, SWITCH_FALSE);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s EXPORT [%s]=[%s]\n", switch_channel_get_name(channel), var,
expanded ? expanded : "UNDEF");
switch_channel_export_variable_var_check(channel, var, expanded, SWITCH_EXPORT_VARS_VARIABLE, SWITCH_FALSE);
if (expanded && expanded != val) {
switch_safe_free(expanded);
if (expanded && expanded != val) {
switch_safe_free(expanded);
}
} else {
switch_channel_set_variable(channel, var, val);
}
}
}
SWITCH_STANDARD_APP(prefix_unset_function)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_event_header_t *ei = NULL;
switch_event_t *clear;
char *arg = (char *) data;
if(switch_event_create(&clear, SWITCH_EVENT_CLONE) != SWITCH_STATUS_SUCCESS) {
return;
}
for (ei = switch_channel_variable_first(channel); ei; ei = ei->next) {
const char *name = ei->name;
char *value = ei->value;
if (!strncasecmp(name, arg, strlen(arg))) {
switch_event_add_header_string(clear, SWITCH_STACK_BOTTOM, name, value);
}
}
switch_channel_variable_last(channel);
for (ei = clear->headers; ei; ei = ei->next) {
char *varname = ei->name;
switch_channel_set_variable(channel, varname, NULL);
}
switch_event_destroy(&clear);
}
SWITCH_STANDARD_APP(multiset_function) {
char delim = ' ';
char *arg = (char *) data;
@ -145,25 +201,79 @@ SWITCH_STANDARD_APP(multiset_function) {
delim = *arg++;
}
if (arg) {
if(delim != '\0') {
switch_channel_t *channel = switch_core_session_get_channel(session);
char *array[256] = {0};
int i, argc;
if (arg) {
char *array[256] = {0};
int i, argc;
arg = switch_core_session_strdup(session, arg);
argc = switch_split(arg, delim, array);
arg = switch_core_session_strdup(session, arg);
argc = switch_split(arg, delim, array);
for(i = 0; i < argc; i++) {
base_set(session, array[i], SWITCH_STACK_BOTTOM);
for(i = 0; i < argc; i++) {
base_set(session, array[i], SWITCH_STACK_BOTTOM);
}
}
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
} else {
base_set(session, data, SWITCH_STACK_BOTTOM);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "multiset with empty args\n");
}
}
SWITCH_STANDARD_APP(uuid_multiset_function) {
char delim = ' ';
char *arg0 = (char *) data;
char *arg = strchr(arg0, ' ');
switch_event_t *event;
if(arg == NULL) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "uuid_multiset with invalid args\n");
return;
}
*arg = '\0';
arg++;
if(zstr(arg0)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "uuid_multiset with invalid uuid\n");
return;
}
if (!zstr(arg) && *arg == '^' && *(arg+1) == '^') {
arg += 2;
delim = *arg++;
}
if(delim != '\0') {
switch_core_session_t *uuid_session = NULL;
if ((uuid_session = switch_core_session_force_locate(arg0)) != NULL) {
switch_channel_t *uuid_channel = switch_core_session_get_channel(uuid_session);
if (arg) {
char *array[256] = {0};
int i, argc;
arg = switch_core_session_strdup(session, arg);
argc = switch_split(arg, delim, array);
for(i = 0; i < argc; i++) {
base_set(uuid_session, array[i], SWITCH_STACK_BOTTOM);
}
}
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(uuid_channel, event);
switch_event_fire(&event);
}
switch_core_session_rwunlock(uuid_session);
} else {
base_set(session, data, SWITCH_STACK_BOTTOM);
}
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "multiset with empty args\n");
}
}
@ -205,19 +315,23 @@ SWITCH_STANDARD_APP(multiunset_function) {
delim = *arg++;
}
if (arg) {
char *array[256] = {0};
int i, argc;
if(delim != '\0') {
if (arg) {
char *array[256] = {0};
int i, argc;
arg = switch_core_session_strdup(session, arg);
argc = switch_split(arg, delim, array);
arg = switch_core_session_strdup(session, arg);
argc = switch_split(arg, delim, array);
for(i = 0; i < argc; i++) {
switch_channel_set_variable(switch_core_session_get_channel(session), array[i], NULL);
for(i = 0; i < argc; i++) {
switch_channel_set_variable(switch_core_session_get_channel(session), array[i], NULL);
}
} else {
switch_channel_set_variable(switch_core_session_get_channel(session), arg, NULL);
}
} else {
switch_channel_set_variable(switch_core_session_get_channel(session), arg, NULL);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "multiunset with empty args\n");
}
}
@ -230,18 +344,22 @@ SWITCH_STANDARD_APP(export_function) {
delim = *arg++;
}
if (arg) {
char *array[256] = {0};
int i, argc;
if(delim != '\0') {
if (arg) {
char *array[256] = {0};
int i, argc;
arg = switch_core_session_strdup(session, arg);
argc = switch_split(arg, delim, array);
arg = switch_core_session_strdup(session, arg);
argc = switch_split(arg, delim, array);
for(i = 0; i < argc; i++) {
base_export(session, array[i], SWITCH_STACK_BOTTOM);
}
for(i = 0; i < argc; i++) {
base_export(session, array[i], SWITCH_STACK_BOTTOM);
}
} else {
base_export(session, data, SWITCH_STACK_BOTTOM);
}
} else {
base_export(session, data, SWITCH_STACK_BOTTOM);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "export with empty args\n");
}
}
@ -254,6 +372,11 @@ void add_kz_dptools(switch_loadable_module_interface_t **module_interface, switc
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
SWITCH_ADD_APP(app_interface, "kz_multiunset", MULTISET_SHORT_DESC, MULTISET_LONG_DESC, multiunset_function, MULTIUNSET_SYNTAX,
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
SWITCH_ADD_APP(app_interface, "kz_export", EXPORT_SHORT_DESC, EXPORT_LONG_DESC, export_function, EXPORT_SYNTAX,
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
SWITCH_ADD_APP(app_interface, "kz_export", EXPORT_SHORT_DESC, EXPORT_LONG_DESC, export_function, EXPORT_SYNTAX,
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
SWITCH_ADD_APP(app_interface, "kz_prefix_unset", PREFIX_UNSET_SHORT_DESC, PREFIX_UNSET_LONG_DESC, prefix_unset_function, PREFIX_UNSET_SYNTAX,
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
SWITCH_ADD_APP(app_interface, "kz_uuid_multiset", UUID_MULTISET_SHORT_DESC, UUID_MULTISET_LONG_DESC, uuid_multiset_function, UUID_MULTISET_SYNTAX,
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
}

View File

@ -0,0 +1,262 @@
#ifndef KAZOO_EI_H
#define KAZOO_EI_H
#include <switch.h>
#include <ei.h>
#define MODNAME "mod_kazoo"
#define BUNDLE "community"
#define RELEASE "v1.5.0-1"
#define VERSION "mod_kazoo v1.5.0-1 community"
typedef enum {KAZOO_FETCH_PROFILE, KAZOO_EVENT_PROFILE} kazoo_profile_type;
typedef enum {ERLANG_TUPLE, ERLANG_MAP} kazoo_json_term;
typedef struct ei_xml_agent_s ei_xml_agent_t;
typedef ei_xml_agent_t *ei_xml_agent_ptr;
typedef struct kazoo_event kazoo_event_t;
typedef kazoo_event_t *kazoo_event_ptr;
typedef struct kazoo_event_profile kazoo_event_profile_t;
typedef kazoo_event_profile_t *kazoo_event_profile_ptr;
typedef struct kazoo_fetch_profile kazoo_fetch_profile_t;
typedef kazoo_fetch_profile_t *kazoo_fetch_profile_ptr;
typedef struct kazoo_config_t kazoo_config;
typedef kazoo_config *kazoo_config_ptr;
#include "kazoo_fields.h"
#include "kazoo_config.h"
struct ei_send_msg_s {
ei_x_buff buf;
erlang_pid pid;
};
typedef struct ei_send_msg_s ei_send_msg_t;
struct ei_received_msg_s {
ei_x_buff buf;
erlang_msg msg;
};
typedef struct ei_received_msg_s ei_received_msg_t;
typedef struct ei_event_stream_s ei_event_stream_t;
typedef struct ei_node_s ei_node_t;
struct ei_event_binding_s {
char id[SWITCH_UUID_FORMATTED_LENGTH + 1];
switch_event_node_t *node;
switch_event_types_t type;
const char *subclass_name;
ei_event_stream_t* stream;
kazoo_event_ptr event;
struct ei_event_binding_s *next;
};
typedef struct ei_event_binding_s ei_event_binding_t;
struct ei_event_stream_s {
switch_memory_pool_t *pool;
ei_event_binding_t *bindings;
switch_queue_t *queue;
switch_socket_t *acceptor;
switch_pollset_t *pollset;
switch_pollfd_t *pollfd;
switch_socket_t *socket;
switch_mutex_t *socket_mutex;
switch_bool_t connected;
char remote_ip[48];
uint16_t remote_port;
char local_ip[48];
uint16_t local_port;
erlang_pid pid;
uint32_t flags;
ei_node_t *node;
struct ei_event_stream_s *next;
};
struct ei_node_s {
int nodefd;
switch_atomic_t pending_bgapi;
switch_atomic_t receive_handlers;
switch_memory_pool_t *pool;
ei_event_stream_t *event_streams;
switch_mutex_t *event_streams_mutex;
switch_queue_t *send_msgs;
switch_queue_t *received_msgs;
char *peer_nodename;
switch_time_t created_time;
switch_socket_t *socket;
char remote_ip[48];
uint16_t remote_port;
char local_ip[48];
uint16_t local_port;
uint32_t flags;
int legacy;
struct ei_node_s *next;
};
struct xml_fetch_reply_s {
char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
char *xml_str;
struct xml_fetch_reply_s *next;
};
typedef struct xml_fetch_reply_s xml_fetch_reply_t;
struct fetch_handler_s {
erlang_pid pid;
struct fetch_handler_s *next;
};
typedef struct fetch_handler_s fetch_handler_t;
struct ei_xml_client_s {
ei_node_t *ei_node;
fetch_handler_t *fetch_handlers;
struct ei_xml_client_s *next;
};
typedef struct ei_xml_client_s ei_xml_client_t;
struct ei_xml_agent_s {
switch_memory_pool_t *pool;
switch_xml_section_t section;
switch_thread_rwlock_t *lock;
ei_xml_client_t *clients;
switch_mutex_t *current_client_mutex;
ei_xml_client_t *current_client;
switch_mutex_t *replies_mutex;
switch_thread_cond_t *new_reply;
xml_fetch_reply_t *replies;
kazoo_fetch_profile_ptr profile;
};
struct globals_s {
switch_memory_pool_t *pool;
switch_atomic_t threads;
switch_socket_t *acceptor;
struct ei_cnode_s ei_cnode;
switch_thread_rwlock_t *ei_nodes_lock;
ei_node_t *ei_nodes;
switch_xml_binding_t *config_fetch_binding;
switch_xml_binding_t *directory_fetch_binding;
switch_xml_binding_t *dialplan_fetch_binding;
switch_xml_binding_t *channels_fetch_binding;
switch_xml_binding_t *languages_fetch_binding;
switch_xml_binding_t *chatplan_fetch_binding;
switch_hash_t *event_filter;
int epmdfd;
int num_worker_threads;
switch_bool_t nat_map;
switch_bool_t ei_shortname;
int ei_compat_rel;
char *ip;
char *hostname;
char *ei_cookie;
char *ei_nodename;
char *kazoo_var_prefix;
int var_prefix_length;
uint32_t flags;
int send_all_headers;
int send_all_private_headers;
int connection_timeout;
int receive_timeout;
int receive_msg_preallocate;
int event_stream_preallocate;
int send_msg_batch;
short event_stream_framing;
switch_port_t port;
int config_fetched;
int io_fault_tolerance;
kazoo_event_profile_ptr events;
kazoo_config_ptr definitions;
kazoo_config_ptr event_handlers;
kazoo_config_ptr fetch_handlers;
kazoo_json_term json_encoding;
int delete_file_after_put;
int enable_legacy;
};
typedef struct globals_s globals_t;
extern globals_t kazoo_globals;
/* kazoo_event_stream.c */
ei_event_stream_t *find_event_stream(ei_event_stream_t *event_streams, const erlang_pid *from);
//ei_event_stream_t *new_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from);
ei_event_stream_t *new_event_stream(ei_node_t *ei_node, const erlang_pid *from);
switch_status_t remove_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from);
switch_status_t remove_event_streams(ei_event_stream_t **event_streams);
unsigned long get_stream_port(const ei_event_stream_t *event_stream);
switch_status_t add_event_binding(ei_event_stream_t *event_stream, const char *event_name);
//switch_status_t add_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name);
switch_status_t remove_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name);
switch_status_t remove_event_bindings(ei_event_stream_t *event_stream);
/* kazoo_node.c */
switch_status_t new_kazoo_node(int nodefd, ErlConnect *conn);
/* kazoo_ei_utils.c */
void close_socket(switch_socket_t **sock);
void close_socketfd(int *sockfd);
switch_socket_t *create_socket_with_port(switch_memory_pool_t *pool, switch_port_t port);
switch_socket_t *create_socket(switch_memory_pool_t *pool);
switch_status_t create_ei_cnode(const char *ip_addr, const char *name, struct ei_cnode_s *ei_cnode);
switch_status_t ei_compare_pids(const erlang_pid *pid1, const erlang_pid *pid2);
void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event);
void ei_encode_switch_event_headers_2(ei_x_buff *ebuf, switch_event_t *event, int decode);
void ei_encode_json(ei_x_buff *ebuf, cJSON *JObj);
void ei_link(ei_node_t *ei_node, erlang_pid * from, erlang_pid * to);
void ei_encode_switch_event(ei_x_buff * ebuf, switch_event_t *event);
int ei_helper_send(ei_node_t *ei_node, erlang_pid* to, ei_x_buff *buf);
int ei_decode_atom_safe(char *buf, int *index, char *dst);
int ei_decode_string_or_binary_limited(char *buf, int *index, int maxsize, char *dst);
int ei_decode_string_or_binary(char *buf, int *index, char **dst);
switch_status_t create_acceptor();
switch_hash_t *create_default_filter();
void fetch_config();
switch_status_t kazoo_load_config();
void kazoo_destroy_config();
#define _ei_x_encode_string(buf, string) { ei_x_encode_binary(buf, string, strlen(string)); }
/* kazoo_fetch_agent.c */
switch_status_t bind_fetch_agents();
switch_status_t unbind_fetch_agents();
switch_status_t remove_xml_clients(ei_node_t *ei_node);
switch_status_t add_fetch_handler(ei_node_t *ei_node, erlang_pid *from, switch_xml_binding_t *binding);
switch_status_t remove_fetch_handlers(ei_node_t *ei_node, erlang_pid *from);
switch_status_t fetch_reply(char *uuid_str, char *xml_str, switch_xml_binding_t *binding);
switch_status_t handle_api_command_streams(ei_node_t *ei_node, switch_stream_handle_t *stream);
void bind_event_profiles(kazoo_event_ptr event);
void rebind_fetch_profiles(kazoo_config_ptr fetch_handlers);
switch_status_t kazoo_config_handlers(switch_xml_t cfg);
/* runtime */
SWITCH_MODULE_RUNTIME_FUNCTION(mod_kazoo_runtime);
#endif /* KAZOO_EI_H */
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -0,0 +1,543 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Based on mod_skel by
* Anthony Minessale II <anthm@freeswitch.org>
*
* Contributor(s):
*
* Daniel Bryars <danb@aeriandi.com>
* Tim Brown <tim.brown@aeriandi.com>
* Anthony Minessale II <anthm@freeswitch.org>
* William King <william.king@quentustech.com>
* Mike Jerris <mike@jerris.com>
*
* kazoo.c -- Sends FreeSWITCH events to an AMQP broker
*
*/
#include "mod_kazoo.h"
#include "kazoo_definitions.h"
#define KAZOO_DECLARE_GLOBAL_STRING_FUNC(fname, vname) static void __attribute__((__unused__)) fname(const char *string) { if (!string) return;\
if (vname) {free(vname); vname = NULL;}vname = strdup(string);} static void fname(const char *string)
KAZOO_DECLARE_GLOBAL_STRING_FUNC(set_pref_ip, kazoo_globals.ip);
KAZOO_DECLARE_GLOBAL_STRING_FUNC(set_pref_ei_cookie, kazoo_globals.ei_cookie);
KAZOO_DECLARE_GLOBAL_STRING_FUNC(set_pref_ei_nodename, kazoo_globals.ei_nodename);
KAZOO_DECLARE_GLOBAL_STRING_FUNC(set_pref_kazoo_var_prefix, kazoo_globals.kazoo_var_prefix);
static int read_cookie_from_file(char *filename) {
int fd;
char cookie[MAXATOMLEN + 1];
char *end;
struct stat buf;
ssize_t res;
if (!stat(filename, &buf)) {
if ((buf.st_mode & S_IRWXG) || (buf.st_mode & S_IRWXO)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s must only be accessible by owner only.\n", filename);
return 2;
}
if (buf.st_size > MAXATOMLEN) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s contains a cookie larger than the maximum atom size of %d.\n", filename, MAXATOMLEN);
return 2;
}
fd = open(filename, O_RDONLY);
if (fd < 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to open cookie file %s : %d.\n", filename, errno);
return 2;
}
if ((res = read(fd, cookie, MAXATOMLEN)) < 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read cookie file %s : %d.\n", filename, errno);
}
cookie[MAXATOMLEN] = '\0';
/* replace any end of line characters with a null */
if ((end = strchr(cookie, '\n'))) {
*end = '\0';
}
if ((end = strchr(cookie, '\r'))) {
*end = '\0';
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set cookie from file %s: %s\n", filename, cookie);
set_pref_ei_cookie(cookie);
return 0;
} else {
/* don't error here, because we might be blindly trying to read $HOME/.erlang.cookie, and that can fail silently */
return 1;
}
}
switch_status_t kazoo_ei_config(switch_xml_t cfg) {
switch_xml_t child, param;
kazoo_globals.send_all_headers = 0;
kazoo_globals.send_all_private_headers = 1;
kazoo_globals.connection_timeout = 500;
kazoo_globals.receive_timeout = 200;
kazoo_globals.receive_msg_preallocate = 2000;
kazoo_globals.event_stream_preallocate = 4000;
kazoo_globals.send_msg_batch = 10;
kazoo_globals.event_stream_framing = 2;
kazoo_globals.port = 0;
kazoo_globals.io_fault_tolerance = 10;
kazoo_globals.json_encoding = ERLANG_TUPLE;
kazoo_globals.enable_legacy = SWITCH_TRUE;
kazoo_globals.delete_file_after_put = SWITCH_TRUE;
if ((child = switch_xml_child(cfg, "settings"))) {
for (param = switch_xml_child(child, "param"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
char *val = (char *) switch_xml_attr_soft(param, "value");
if (!strcmp(var, "listen-ip")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set bind ip address: %s\n", val);
set_pref_ip(val);
} else if (!strcmp(var, "listen-port")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set bind port: %s\n", val);
kazoo_globals.port = atoi(val);
} else if (!strcmp(var, "cookie")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set cookie: %s\n", val);
set_pref_ei_cookie(val);
} else if (!strcmp(var, "cookie-file")) {
if (read_cookie_from_file(val) == 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read cookie from %s\n", val);
}
} else if (!strcmp(var, "nodename")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set node name: %s\n", val);
set_pref_ei_nodename(val);
} else if (!strcmp(var, "shortname")) {
kazoo_globals.ei_shortname = switch_true(val);
} else if (!strcmp(var, "kazoo-var-prefix")) {
set_pref_kazoo_var_prefix(val);
} else if (!strcmp(var, "compat-rel")) {
if (atoi(val) >= 7)
kazoo_globals.ei_compat_rel = atoi(val);
else
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid compatibility release '%s' specified\n", val);
} else if (!strcmp(var, "nat-map")) {
kazoo_globals.nat_map = switch_true(val);
} else if (!strcmp(var, "send-all-headers")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-all-headers: %s\n", val);
kazoo_globals.send_all_headers = switch_true(val);
} else if (!strcmp(var, "send-all-private-headers")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-all-private-headers: %s\n", val);
kazoo_globals.send_all_private_headers = switch_true(val);
} else if (!strcmp(var, "connection-timeout")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set connection-timeout: %s\n", val);
kazoo_globals.connection_timeout = atoi(val);
} else if (!strcmp(var, "receive-timeout")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set receive-timeout: %s\n", val);
kazoo_globals.receive_timeout = atoi(val);
} else if (!strcmp(var, "receive-msg-preallocate")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set receive-msg-preallocate: %s\n", val);
kazoo_globals.receive_msg_preallocate = atoi(val);
} else if (!strcmp(var, "event-stream-preallocate")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set event-stream-preallocate: %s\n", val);
kazoo_globals.event_stream_preallocate = atoi(val);
} else if (!strcmp(var, "send-msg-batch-size")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-msg-batch-size: %s\n", val);
kazoo_globals.send_msg_batch = atoi(val);
} else if (!strcmp(var, "event-stream-framing")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set event-stream-framing: %s\n", val);
kazoo_globals.event_stream_framing = atoi(val);
} else if (!strcmp(var, "io-fault-tolerance")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set io-fault-tolerance: %s\n", val);
kazoo_globals.io_fault_tolerance = atoi(val);
} else if (!strcmp(var, "num-worker-threads")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set num-worker-threads: %s\n", val);
kazoo_globals.num_worker_threads = atoi(val);
} else if (!strcmp(var, "json-term-encoding")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set json-term-encoding: %s\n", val);
if(!strcmp(val, "map")) {
kazoo_globals.json_encoding = ERLANG_MAP;
}
} else if (!strcmp(var, "enable-legacy")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set enable-legacy: %s\n", val);
kazoo_globals.enable_legacy = switch_true(val);
} else if (!strcmp(var, "delete-file-after-put")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set delete-file-after-put: %s\n", val);
kazoo_globals.delete_file_after_put = switch_true(val);
}
}
}
if ((child = switch_xml_child(cfg, "event-filter"))) {
switch_hash_t *filter;
switch_core_hash_init(&filter);
for (param = switch_xml_child(child, "header"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
switch_core_hash_insert(filter, var, "1");
}
kazoo_globals.event_filter = filter;
}
if (kazoo_globals.receive_msg_preallocate < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid receive message preallocate value, disabled\n");
kazoo_globals.receive_msg_preallocate = 0;
}
if (kazoo_globals.event_stream_preallocate < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event stream preallocate value, disabled\n");
kazoo_globals.event_stream_preallocate = 0;
}
if (kazoo_globals.send_msg_batch < 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid send message batch size, reverting to default\n");
kazoo_globals.send_msg_batch = 10;
}
if (kazoo_globals.io_fault_tolerance < 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid I/O fault tolerance, reverting to default\n");
kazoo_globals.io_fault_tolerance = 10;
}
if (!kazoo_globals.event_filter) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Event filter not found in configuration, using default\n");
kazoo_globals.event_filter = create_default_filter();
}
if (kazoo_globals.event_stream_framing < 1 || kazoo_globals.event_stream_framing > 4) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event stream framing value, using default\n");
kazoo_globals.event_stream_framing = 2;
}
if (zstr(kazoo_globals.kazoo_var_prefix)) {
set_pref_kazoo_var_prefix("variable_ecallmgr*");
kazoo_globals.var_prefix_length = 17; //ignore the *
} else {
/* we could use the global pool but then we would have to conditionally
* free the pointer if it was not drawn from the XML */
char *buf;
int size = switch_snprintf(NULL, 0, "variable_%s*", kazoo_globals.kazoo_var_prefix) + 1;
switch_malloc(buf, size);
switch_snprintf(buf, size, "variable_%s*", kazoo_globals.kazoo_var_prefix);
switch_safe_free(kazoo_globals.kazoo_var_prefix);
kazoo_globals.kazoo_var_prefix = buf;
kazoo_globals.var_prefix_length = size - 2; //ignore the *
}
if (!kazoo_globals.num_worker_threads) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Number of worker threads not found in configuration, using default\n");
kazoo_globals.num_worker_threads = 10;
}
if (zstr(kazoo_globals.ip)) {
set_pref_ip("0.0.0.0");
}
if (zstr(kazoo_globals.ei_cookie)) {
int res;
char *home_dir = getenv("HOME");
char path_buf[1024];
if (!zstr(home_dir)) {
/* $HOME/.erlang.cookie */
switch_snprintf(path_buf, sizeof (path_buf), "%s%s%s", home_dir, SWITCH_PATH_SEPARATOR, ".erlang.cookie");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Checking for cookie at path: %s\n", path_buf);
res = read_cookie_from_file(path_buf);
if (res) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No cookie or valid cookie file specified, using default cookie\n");
set_pref_ei_cookie("ClueCon");
}
}
}
if (!kazoo_globals.ei_nodename) {
set_pref_ei_nodename("freeswitch");
}
if (!kazoo_globals.nat_map) {
kazoo_globals.nat_map = 0;
}
return SWITCH_STATUS_SUCCESS;
}
switch_status_t kazoo_config_handlers(switch_xml_t cfg)
{
switch_xml_t def;
char* xml = NULL;
kazoo_config_ptr definitions, fetch_handlers, event_handlers;
kazoo_event_profile_ptr events;
xml = strndup((char*)kazoo_conf_xml, kazoo_conf_xml_len);
def = switch_xml_parse_str_dup(xml);
kz_xml_process(def);
kz_xml_process(cfg);
definitions = kazoo_config_definitions(cfg);
if(definitions == NULL) {
definitions = kazoo_config_definitions(def);
}
fetch_handlers = kazoo_config_fetch_handlers(definitions, cfg);
if(fetch_handlers == NULL) {
fetch_handlers = kazoo_config_fetch_handlers(definitions, def);
}
event_handlers = kazoo_config_event_handlers(definitions, cfg);
if(event_handlers == NULL) {
event_handlers = kazoo_config_event_handlers(definitions, def);
}
if(event_handlers != NULL) {
events = (kazoo_event_profile_ptr) switch_core_hash_find(event_handlers->hash, "default");
}
if(events == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get default handler for events\n");
destroy_config(&event_handlers);
destroy_config(&fetch_handlers);
destroy_config(&definitions);
switch_xml_free(def);
switch_safe_free(xml);
return SWITCH_STATUS_GENERR;
}
bind_event_profiles(events->events);
kazoo_globals.events = events;
destroy_config(&kazoo_globals.event_handlers);
kazoo_globals.event_handlers = event_handlers;
rebind_fetch_profiles(fetch_handlers);
destroy_config(&kazoo_globals.fetch_handlers);
kazoo_globals.fetch_handlers = fetch_handlers;
destroy_config(&kazoo_globals.definitions);
kazoo_globals.definitions = definitions;
switch_xml_free(def);
switch_safe_free(xml);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t kazoo_load_config()
{
char *cf = "kazoo.conf";
switch_xml_t cfg, xml;
if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open configuration file %s\n", cf);
return SWITCH_STATUS_FALSE;
} else {
kazoo_ei_config(cfg);
kazoo_config_handlers(cfg);
switch_xml_free(xml);
}
return SWITCH_STATUS_SUCCESS;
}
void kazoo_destroy_config()
{
destroy_config(&kazoo_globals.event_handlers);
destroy_config(&kazoo_globals.fetch_handlers);
destroy_config(&kazoo_globals.definitions);
}
switch_status_t kazoo_config_events(kazoo_config_ptr definitions, switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_event_profile_ptr profile)
{
switch_xml_t events, event;
kazoo_event_ptr prv = NULL, cur = NULL;
if ((events = switch_xml_child(cfg, "events")) != NULL) {
for (event = switch_xml_child(events, "event"); event; event = event->next) {
const char *var = switch_xml_attr(event, "name");
cur = (kazoo_event_ptr) switch_core_alloc(pool, sizeof(kazoo_event_t));
memset(cur, 0, sizeof(kazoo_event_t));
if(prv == NULL) {
profile->events = prv = cur;
} else {
prv->next = cur;
prv = cur;
}
cur->profile = profile;
cur->name = switch_core_strdup(pool, var);
kazoo_config_filters(pool, event, &cur->filter);
kazoo_config_fields(definitions, pool, event, &cur->fields);
}
}
return SWITCH_STATUS_SUCCESS;
}
switch_status_t kazoo_config_fetch_handler(kazoo_config_ptr definitions, kazoo_config_ptr root, switch_xml_t cfg, kazoo_fetch_profile_ptr *ptr)
{
kazoo_fetch_profile_ptr profile = NULL;
switch_xml_t params, param;
switch_xml_section_t fetch_section;
int fetch_timeout = 2000000;
switch_memory_pool_t *pool = NULL;
char *name = (char *) switch_xml_attr_soft(cfg, "name");
if (zstr(name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing name in profile\n");
return SWITCH_STATUS_GENERR;
}
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error allocation pool for new profile : %s\n", name);
return SWITCH_STATUS_GENERR;
}
profile = switch_core_alloc(pool, sizeof(kazoo_fetch_profile_t));
profile->pool = pool;
profile->root = root;
profile->name = switch_core_strdup(profile->pool, name);
fetch_section = switch_xml_parse_section_string(name);
if ((params = switch_xml_child(cfg, "params")) != NULL) {
for (param = switch_xml_child(params, "param"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
char *val = (char *) switch_xml_attr_soft(param, "value");
if (!var) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile[%s] param missing 'name' attribute\n", name);
continue;
}
if (!val) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile[%s] param[%s] missing 'value' attribute\n", name, var);
continue;
}
if (!strncmp(var, "fetch-timeout", 13)) {
fetch_timeout = atoi(val);
} else if (!strncmp(var, "fetch-section", 13)) {
fetch_section = switch_xml_parse_section_string(val);
}
}
}
if (fetch_section == SWITCH_XML_SECTION_RESULT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Fetch Profile[%s] invalid fetch-section: %s\n", name, switch_xml_toxml(cfg, SWITCH_FALSE));
goto err;
}
profile->fetch_timeout = fetch_timeout;
profile->section = fetch_section;
kazoo_config_fields(definitions, pool, cfg, &profile->fields);
kazoo_config_loglevels(pool, cfg, &profile->logging);
if(root) {
if ( switch_core_hash_insert(root->hash, name, (void *) profile) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to insert new fetch profile [%s] into kazoo profile hash\n", name);
goto err;
}
}
if(ptr)
*ptr = profile;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "fetch handler profile %s successfully configured\n", name);
return SWITCH_STATUS_SUCCESS;
err:
/* Cleanup */
if(pool) {
switch_core_destroy_memory_pool(&pool);
}
return SWITCH_STATUS_GENERR;
}
switch_status_t kazoo_config_event_handler(kazoo_config_ptr definitions, kazoo_config_ptr root, switch_xml_t cfg, kazoo_event_profile_ptr *ptr)
{
kazoo_event_profile_ptr profile = NULL;
switch_memory_pool_t *pool = NULL;
char *name = (char *) switch_xml_attr_soft(cfg, "name");
if (zstr(name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing name in profile\n");
return SWITCH_STATUS_GENERR;
}
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error allocation pool for new profile : %s\n", name);
return SWITCH_STATUS_GENERR;
}
profile = switch_core_alloc(pool, sizeof(kazoo_event_profile_t));
profile->pool = pool;
profile->root = root;
profile->name = switch_core_strdup(profile->pool, name);
kazoo_config_filters(pool, cfg, &profile->filter);
kazoo_config_fields(definitions, pool, cfg, &profile->fields);
kazoo_config_events(definitions, pool, cfg, profile);
kazoo_config_loglevels(pool, cfg, &profile->logging);
if(root) {
if ( switch_core_hash_insert(root->hash, name, (void *) profile) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to insert new profile [%s] into kazoo profile hash\n", name);
goto err;
}
}
if(ptr)
*ptr = profile;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "event handler profile %s successfully configured\n", name);
return SWITCH_STATUS_SUCCESS;
err:
/* Cleanup */
if(pool) {
switch_core_destroy_memory_pool(&pool);
}
return SWITCH_STATUS_GENERR;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4
*/

View File

@ -0,0 +1,996 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Anthony Minessale II <anthm@freeswitch.org>
* Andrew Thompson <andrew@hijacked.us>
* Rob Charlton <rob.charlton@savageminds.com>
* Karl Anderson <karl@2600hz.com>
*
* Original from mod_erlang_event.
* ei_helpers.c -- helper functions for ei
*
*/
#include "mod_kazoo.h"
/* Stolen from code added to ei in R12B-5.
* Since not everyone has this version yet;
* provide our own version.
* */
#define put8(s,n) do { \
(s)[0] = (char)((n) & 0xff); \
(s) += 1; \
} while (0)
#define put32be(s,n) do { \
(s)[0] = ((n) >> 24) & 0xff; \
(s)[1] = ((n) >> 16) & 0xff; \
(s)[2] = ((n) >> 8) & 0xff; \
(s)[3] = (n) & 0xff; \
(s) += 4; \
} while (0)
#ifdef EI_DEBUG
static void ei_x_print_reg_msg(ei_x_buff *buf, char *dest, int send) {
char *mbuf = NULL;
int i = 1;
ei_s_print_term(&mbuf, buf->buff, &i);
if (send) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Encoded term %s to '%s'\n", mbuf, dest);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Decoded term %s for '%s'\n", mbuf, dest);
}
free(mbuf);
}
static void ei_x_print_msg(ei_x_buff *buf, erlang_pid *pid, int send) {
char *pbuf = NULL;
int i = 0;
ei_x_buff pidbuf;
ei_x_new(&pidbuf);
ei_x_encode_pid(&pidbuf, pid);
ei_s_print_term(&pbuf, pidbuf.buff, &i);
ei_x_print_reg_msg(buf, pbuf, send);
free(pbuf);
}
#endif
void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event) {
ei_encode_switch_event_headers_2(ebuf, event, 1);
}
void ei_encode_switch_event_headers_2(ei_x_buff *ebuf, switch_event_t *event, int encode) {
switch_event_header_t *hp;
char *uuid = switch_event_get_header(event, "unique-id");
int i;
for (i = 0, hp = event->headers; hp; hp = hp->next, i++);
if (event->body)
i++;
ei_x_encode_list_header(ebuf, i + 1);
if (uuid) {
char *unique_id = switch_event_get_header(event, "unique-id");
ei_x_encode_binary(ebuf, unique_id, strlen(unique_id));
} else {
ei_x_encode_atom(ebuf, "undefined");
}
for (hp = event->headers; hp; hp = hp->next) {
ei_x_encode_tuple_header(ebuf, 2);
ei_x_encode_binary(ebuf, hp->name, strlen(hp->name));
if(encode)
switch_url_decode(hp->value);
ei_x_encode_binary(ebuf, hp->value, strlen(hp->value));
}
if (event->body) {
ei_x_encode_tuple_header(ebuf, 2);
ei_x_encode_binary(ebuf, "body", strlen("body"));
ei_x_encode_binary(ebuf, event->body, strlen(event->body));
}
ei_x_encode_empty_list(ebuf);
}
int ei_json_child_count(cJSON *JObj)
{
int mask = cJSON_False
| cJSON_True
| cJSON_NULL
| cJSON_Number
| cJSON_String
| cJSON_Array
| cJSON_Object
| cJSON_Raw;
cJSON *item = JObj->child;
int i = 0;
while(item) {
if(item->type & mask)
i++;
item = item->next;
}
return i;
}
void ei_encode_json_array(ei_x_buff *ebuf, cJSON *JObj) {
cJSON *item;
int count = ei_json_child_count(JObj);
ei_x_encode_list_header(ebuf, count);
if(count == 0)
return;
item = JObj->child;
while(item) {
switch(item->type) {
case cJSON_String:
ei_x_encode_binary(ebuf, item->valuestring, strlen(item->valuestring));
break;
case cJSON_Number:
ei_x_encode_double(ebuf, item->valuedouble);
break;
case cJSON_True:
ei_x_encode_boolean(ebuf, 1);
break;
case cJSON_False:
ei_x_encode_boolean(ebuf, 0);
break;
case cJSON_Object:
ei_encode_json(ebuf, item);
break;
case cJSON_Array:
ei_encode_json_array(ebuf, item);
break;
case cJSON_Raw:
{
cJSON *Decoded = cJSON_Parse(item->valuestring);
if(!Decoded) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR DECODING RAW JSON %s\n", item->valuestring);
ei_x_encode_tuple_header(ebuf, 0);
} else {
ei_encode_json(ebuf, Decoded);
cJSON_Delete(Decoded);
}
break;
}
case cJSON_NULL:
ei_x_encode_atom(ebuf, "null");
break;
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT ENCODED %i\n", item->type);
break;
}
item = item->next;
}
ei_x_encode_empty_list(ebuf);
}
void ei_encode_json(ei_x_buff *ebuf, cJSON *JObj) {
cJSON *item;
int count = ei_json_child_count(JObj);
if(kazoo_globals.json_encoding == ERLANG_TUPLE) {
ei_x_encode_tuple_header(ebuf, 1);
ei_x_encode_list_header(ebuf, count);
} else {
ei_x_encode_map_header(ebuf, count);
}
if(count == 0)
return;
item = JObj->child;
while(item) {
if(kazoo_globals.json_encoding == ERLANG_TUPLE) {
ei_x_encode_tuple_header(ebuf, 2);
}
ei_x_encode_binary(ebuf, item->string, strlen(item->string));
switch(item->type) {
case cJSON_String:
ei_x_encode_binary(ebuf, item->valuestring, strlen(item->valuestring));
break;
case cJSON_Number:
if ((fabs(((double)item->valueint) - item->valuedouble) <= DBL_EPSILON)
&& (item->valuedouble <= INT_MAX)
&& (item->valuedouble >= INT_MIN)) {
ei_x_encode_longlong(ebuf, item->valueint);
} else {
ei_x_encode_double(ebuf, item->valuedouble);
}
break;
case cJSON_True:
ei_x_encode_boolean(ebuf, 1);
break;
case cJSON_False:
ei_x_encode_boolean(ebuf, 0);
break;
case cJSON_Object:
ei_encode_json(ebuf, item);
break;
case cJSON_Array:
ei_encode_json_array(ebuf, item);
break;
case cJSON_Raw:
{
cJSON *Decoded = cJSON_Parse(item->valuestring);
if(!Decoded) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR DECODING RAW JSON %s\n", item->valuestring);
ei_x_encode_tuple_header(ebuf, 0);
} else {
ei_encode_json(ebuf, Decoded);
cJSON_Delete(Decoded);
}
break;
}
case cJSON_NULL:
ei_x_encode_atom(ebuf, "null");
break;
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT ENCODED %i\n", item->type);
break;
}
item = item->next;
}
if(kazoo_globals.json_encoding == ERLANG_TUPLE) {
ei_x_encode_empty_list(ebuf);
}
}
void close_socket(switch_socket_t ** sock) {
if (*sock) {
switch_socket_shutdown(*sock, SWITCH_SHUTDOWN_READWRITE);
switch_socket_close(*sock);
*sock = NULL;
}
}
void close_socketfd(int *sockfd) {
if (*sockfd) {
shutdown(*sockfd, SHUT_RDWR);
close(*sockfd);
}
}
switch_socket_t *create_socket_with_port(switch_memory_pool_t *pool, switch_port_t port) {
switch_sockaddr_t *sa;
switch_socket_t *socket;
if(switch_sockaddr_info_get(&sa, kazoo_globals.ip, SWITCH_UNSPEC, port, 0, pool)) {
return NULL;
}
if (switch_socket_create(&socket, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, pool)) {
return NULL;
}
if (switch_socket_opt_set(socket, SWITCH_SO_REUSEADDR, 1)) {
return NULL;
}
if (switch_socket_bind(socket, sa)) {
return NULL;
}
if (switch_socket_listen(socket, 5)){
return NULL;
}
switch_getnameinfo(&kazoo_globals.hostname, sa, 0);
if (kazoo_globals.nat_map && switch_nat_get_type()) {
switch_nat_add_mapping(port, SWITCH_NAT_TCP, NULL, SWITCH_FALSE);
}
return socket;
}
switch_socket_t *create_socket(switch_memory_pool_t *pool) {
return create_socket_with_port(pool, 0);
}
switch_status_t create_ei_cnode(const char *ip_addr, const char *name, struct ei_cnode_s *ei_cnode) {
char hostname[EI_MAXHOSTNAMELEN + 1] = "";
char nodename[MAXNODELEN + 1];
char cnodename[EI_MAXALIVELEN + 1];
//EI_MAX_COOKIE_SIZE+1
char *atsign;
/* copy the erlang interface nodename into something we can modify */
strncpy(cnodename, name, EI_MAXALIVELEN);
if ((atsign = strchr(cnodename, '@'))) {
/* we got a qualified node name, don't guess the host/domain */
snprintf(nodename, MAXNODELEN + 1, "%s", kazoo_globals.ei_nodename);
/* truncate the alivename at the @ */
*atsign = '\0';
} else {
if (zstr(kazoo_globals.hostname) || !strncasecmp(kazoo_globals.ip, "0.0.0.0", 7) || !strncasecmp(kazoo_globals.ip, "::", 2)) {
memcpy(hostname, switch_core_get_hostname(), EI_MAXHOSTNAMELEN);
} else {
memcpy(hostname, kazoo_globals.hostname, EI_MAXHOSTNAMELEN);
}
snprintf(nodename, MAXNODELEN + 1, "%s@%s", kazoo_globals.ei_nodename, hostname);
}
if (kazoo_globals.ei_shortname) {
char *off;
if ((off = strchr(nodename, '.'))) {
*off = '\0';
}
}
/* init the ec stuff */
if (ei_connect_xinit(ei_cnode, hostname, cnodename, nodename, (Erl_IpAddr) ip_addr, kazoo_globals.ei_cookie, 0) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize the erlang interface connection structure\n");
return SWITCH_STATUS_FALSE;
}
return SWITCH_STATUS_SUCCESS;
}
switch_status_t ei_compare_pids(const erlang_pid *pid1, const erlang_pid *pid2) {
if ((!strcmp(pid1->node, pid2->node))
&& pid1->creation == pid2->creation
&& pid1->num == pid2->num
&& pid1->serial == pid2->serial) {
return SWITCH_STATUS_SUCCESS;
} else {
return SWITCH_STATUS_FALSE;
}
}
void ei_link(ei_node_t *ei_node, erlang_pid * from, erlang_pid * to) {
char msgbuf[2048];
char *s;
int index = 0;
index = 5; /* max sizes: */
ei_encode_version(msgbuf, &index); /* 1 */
ei_encode_tuple_header(msgbuf, &index, 3);
ei_encode_long(msgbuf, &index, ERL_LINK);
ei_encode_pid(msgbuf, &index, from); /* 268 */
ei_encode_pid(msgbuf, &index, to); /* 268 */
/* 5 byte header missing */
s = msgbuf;
put32be(s, index - 4); /* 4 */
put8(s, ERL_PASS_THROUGH); /* 1 */
/* sum: 542 */
if (write(ei_node->nodefd, msgbuf, index) == -1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to link to process on %s\n", ei_node->peer_nodename);
}
}
void ei_encode_switch_event(ei_x_buff *ebuf, switch_event_t *event) {
ei_x_encode_tuple_header(ebuf, 2);
ei_x_encode_atom(ebuf, "event");
ei_encode_switch_event_headers(ebuf, event);
}
int ei_helper_send(ei_node_t *ei_node, erlang_pid *to, ei_x_buff *buf) {
int ret = 0;
if (ei_node->nodefd) {
#ifdef EI_DEBUG
ei_x_print_msg(buf, to, 1);
#endif
ret = ei_send(ei_node->nodefd, to, buf->buff, buf->index);
}
return ret;
}
int ei_decode_atom_safe(char *buf, int *index, char *dst) {
int type, size;
ei_get_type(buf, index, &type, &size);
if (type != ERL_ATOM_EXT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed atom\n", type, size);
return -1;
} else if (size > MAXATOMLEN) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Requested decoding of atom with size %d into a buffer of size %d\n", size, MAXATOMLEN);
return -1;
} else {
return ei_decode_atom(buf, index, dst);
}
}
int ei_decode_string_or_binary(char *buf, int *index, char **dst) {
int type, size, res;
long len;
ei_get_type(buf, index, &type, &size);
if (type != ERL_STRING_EXT && type != ERL_BINARY_EXT && type != ERL_NIL_EXT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed binary or string\n", type, size);
return -1;
}
*dst = malloc(size + 1);
if (type == ERL_NIL_EXT) {
res = 0;
**dst = '\0';
} else if (type == ERL_BINARY_EXT) {
res = ei_decode_binary(buf, index, *dst, &len);
(*dst)[len] = '\0';
} else {
res = ei_decode_string(buf, index, *dst);
}
return res;
}
int ei_decode_string_or_binary_limited(char *buf, int *index, int maxsize, char *dst) {
int type, size, res;
long len;
ei_get_type(buf, index, &type, &size);
if (type != ERL_STRING_EXT && type != ERL_BINARY_EXT && type != ERL_NIL_EXT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed binary or string\n", type, size);
return -1;
}
if (size > maxsize) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Requested decoding of %s with size %d into a buffer of size %d\n",
type == ERL_BINARY_EXT ? "binary" : "string", size, maxsize);
return -1;
}
if (type == ERL_NIL_EXT) {
res = 0;
*dst = '\0';
} else if (type == ERL_BINARY_EXT) {
res = ei_decode_binary(buf, index, dst, &len);
dst[len] = '\0'; /* binaries aren't null terminated */
} else {
res = ei_decode_string(buf, index, dst);
}
return res;
}
switch_status_t create_acceptor() {
switch_sockaddr_t *sa;
uint16_t port;
char ipbuf[48];
const char *ip_addr;
/* if the config has specified an erlang release compatibility then pass that along to the erlang interface */
if (kazoo_globals.ei_compat_rel) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Compatability with OTP R%d requested\n", kazoo_globals.ei_compat_rel);
ei_set_compat_rel(kazoo_globals.ei_compat_rel);
}
if (!(kazoo_globals.acceptor = create_socket_with_port(kazoo_globals.pool, kazoo_globals.port))) {
return SWITCH_STATUS_SOCKERR;
}
switch_socket_addr_get(&sa, SWITCH_FALSE, kazoo_globals.acceptor);
port = switch_sockaddr_get_port(sa);
ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Erlang connection acceptor listening on %s:%u\n", ip_addr, port);
/* try to initialize the erlang interface */
if (create_ei_cnode(ip_addr, kazoo_globals.ei_nodename, &kazoo_globals.ei_cnode) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_SOCKERR;
}
/* tell the erlang port manager where we can be reached. this returns a file descriptor pointing to epmd or -1 */
if ((kazoo_globals.epmdfd = ei_publish(&kazoo_globals.ei_cnode, port)) == -1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to publish port to epmd, trying to start epmd via system()\n");
if (system("epmd -daemon")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
"Failed to start epmd manually! Is epmd in $PATH? If not, start it yourself or run an erl shell with -sname or -name\n");
return SWITCH_STATUS_SOCKERR;
}
switch_yield(100000);
if ((kazoo_globals.epmdfd = ei_publish(&kazoo_globals.ei_cnode, port)) == -1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to publish port to epmd AGAIN\n");
return SWITCH_STATUS_SOCKERR;
}
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connected to epmd and published erlang cnode name %s at port %d\n", kazoo_globals.ei_cnode.thisnodename, port);
return SWITCH_STATUS_SUCCESS;
}
switch_hash_t *create_default_filter() {
switch_hash_t *filter;
switch_core_hash_init(&filter);
switch_core_hash_insert(filter, "Acquired-UUID", "1");
switch_core_hash_insert(filter, "action", "1");
switch_core_hash_insert(filter, "Action", "1");
switch_core_hash_insert(filter, "alt_event_type", "1");
switch_core_hash_insert(filter, "Answer-State", "1");
switch_core_hash_insert(filter, "Application", "1");
switch_core_hash_insert(filter, "Application-Data", "1");
switch_core_hash_insert(filter, "Application-Name", "1");
switch_core_hash_insert(filter, "Application-Response", "1");
switch_core_hash_insert(filter, "att_xfer_replaced_by", "1");
switch_core_hash_insert(filter, "Auth-Method", "1");
switch_core_hash_insert(filter, "Auth-Realm", "1");
switch_core_hash_insert(filter, "Auth-User", "1");
switch_core_hash_insert(filter, "Bridge-A-Unique-ID", "1");
switch_core_hash_insert(filter, "Bridge-B-Unique-ID", "1");
switch_core_hash_insert(filter, "Call-Direction", "1");
switch_core_hash_insert(filter, "Caller-Callee-ID-Name", "1");
switch_core_hash_insert(filter, "Caller-Callee-ID-Number", "1");
switch_core_hash_insert(filter, "Caller-Caller-ID-Name", "1");
switch_core_hash_insert(filter, "Caller-Caller-ID-Number", "1");
switch_core_hash_insert(filter, "Caller-Screen-Bit", "1");
switch_core_hash_insert(filter, "Caller-Privacy-Hide-Name", "1");
switch_core_hash_insert(filter, "Caller-Privacy-Hide-Number", "1");
switch_core_hash_insert(filter, "Caller-Context", "1");
switch_core_hash_insert(filter, "Caller-Controls", "1");
switch_core_hash_insert(filter, "Caller-Destination-Number", "1");
switch_core_hash_insert(filter, "Caller-Dialplan", "1");
switch_core_hash_insert(filter, "Caller-Network-Addr", "1");
switch_core_hash_insert(filter, "Caller-Unique-ID", "1");
switch_core_hash_insert(filter, "Call-ID", "1");
switch_core_hash_insert(filter, "Channel-Call-State", "1");
switch_core_hash_insert(filter, "Channel-Call-UUID", "1");
switch_core_hash_insert(filter, "Channel-Presence-ID", "1");
switch_core_hash_insert(filter, "Channel-State", "1");
switch_core_hash_insert(filter, "Chat-Permissions", "1");
switch_core_hash_insert(filter, "Conference-Name", "1");
switch_core_hash_insert(filter, "Conference-Profile-Name", "1");
switch_core_hash_insert(filter, "Conference-Unique-ID", "1");
switch_core_hash_insert(filter, "contact", "1");
switch_core_hash_insert(filter, "Detected-Tone", "1");
switch_core_hash_insert(filter, "dialog_state", "1");
switch_core_hash_insert(filter, "direction", "1");
switch_core_hash_insert(filter, "Distributed-From", "1");
switch_core_hash_insert(filter, "DTMF-Digit", "1");
switch_core_hash_insert(filter, "DTMF-Duration", "1");
switch_core_hash_insert(filter, "Event-Date-Timestamp", "1");
switch_core_hash_insert(filter, "Event-Name", "1");
switch_core_hash_insert(filter, "Event-Subclass", "1");
switch_core_hash_insert(filter, "expires", "1");
switch_core_hash_insert(filter, "Expires", "1");
switch_core_hash_insert(filter, "Ext-SIP-IP", "1");
switch_core_hash_insert(filter, "File", "1");
switch_core_hash_insert(filter, "FreeSWITCH-Hostname", "1");
switch_core_hash_insert(filter, "from", "1");
switch_core_hash_insert(filter, "Hunt-Destination-Number", "1");
switch_core_hash_insert(filter, "ip", "1");
switch_core_hash_insert(filter, "Message-Account", "1");
switch_core_hash_insert(filter, "metadata", "1");
switch_core_hash_insert(filter, "old_node_channel_uuid", "1");
switch_core_hash_insert(filter, "Other-Leg-Callee-ID-Name", "1");
switch_core_hash_insert(filter, "Other-Leg-Callee-ID-Number", "1");
switch_core_hash_insert(filter, "Other-Leg-Caller-ID-Name", "1");
switch_core_hash_insert(filter, "Other-Leg-Caller-ID-Number", "1");
switch_core_hash_insert(filter, "Other-Leg-Destination-Number", "1");
switch_core_hash_insert(filter, "Other-Leg-Direction", "1");
switch_core_hash_insert(filter, "Other-Leg-Unique-ID", "1");
switch_core_hash_insert(filter, "Other-Leg-Channel-Name", "1");
switch_core_hash_insert(filter, "Participant-Type", "1");
switch_core_hash_insert(filter, "Path", "1");
switch_core_hash_insert(filter, "profile_name", "1");
switch_core_hash_insert(filter, "Profiles", "1");
switch_core_hash_insert(filter, "proto-specific-event-name", "1");
switch_core_hash_insert(filter, "Raw-Application-Data", "1");
switch_core_hash_insert(filter, "realm", "1");
switch_core_hash_insert(filter, "Resigning-UUID", "1");
switch_core_hash_insert(filter, "set", "1");
switch_core_hash_insert(filter, "sip_auto_answer", "1");
switch_core_hash_insert(filter, "sip_auth_method", "1");
switch_core_hash_insert(filter, "sip_from_host", "1");
switch_core_hash_insert(filter, "sip_from_user", "1");
switch_core_hash_insert(filter, "sip_to_host", "1");
switch_core_hash_insert(filter, "sip_to_user", "1");
switch_core_hash_insert(filter, "sub-call-id", "1");
switch_core_hash_insert(filter, "technology", "1");
switch_core_hash_insert(filter, "to", "1");
switch_core_hash_insert(filter, "Unique-ID", "1");
switch_core_hash_insert(filter, "URL", "1");
switch_core_hash_insert(filter, "username", "1");
switch_core_hash_insert(filter, "variable_channel_is_moving", "1");
switch_core_hash_insert(filter, "variable_collected_digits", "1");
switch_core_hash_insert(filter, "variable_current_application", "1");
switch_core_hash_insert(filter, "variable_current_application_data", "1");
switch_core_hash_insert(filter, "variable_domain_name", "1");
switch_core_hash_insert(filter, "variable_effective_caller_id_name", "1");
switch_core_hash_insert(filter, "variable_effective_caller_id_number", "1");
switch_core_hash_insert(filter, "variable_holding_uuid", "1");
switch_core_hash_insert(filter, "variable_hold_music", "1");
switch_core_hash_insert(filter, "variable_media_group_id", "1");
switch_core_hash_insert(filter, "variable_originate_disposition", "1");
switch_core_hash_insert(filter, "variable_origination_uuid", "1");
switch_core_hash_insert(filter, "variable_playback_terminator_used", "1");
switch_core_hash_insert(filter, "variable_presence_id", "1");
switch_core_hash_insert(filter, "variable_record_ms", "1");
switch_core_hash_insert(filter, "variable_recovered", "1");
switch_core_hash_insert(filter, "variable_silence_hits_exhausted", "1");
switch_core_hash_insert(filter, "variable_sip_auth_realm", "1");
switch_core_hash_insert(filter, "variable_sip_from_host", "1");
switch_core_hash_insert(filter, "variable_sip_from_user", "1");
switch_core_hash_insert(filter, "variable_sip_from_tag", "1");
switch_core_hash_insert(filter, "variable_sip_h_X-AUTH-IP", "1");
switch_core_hash_insert(filter, "variable_sip_received_ip", "1");
switch_core_hash_insert(filter, "variable_sip_to_host", "1");
switch_core_hash_insert(filter, "variable_sip_to_user", "1");
switch_core_hash_insert(filter, "variable_sip_to_tag", "1");
switch_core_hash_insert(filter, "variable_sofia_profile_name", "1");
switch_core_hash_insert(filter, "variable_transfer_history", "1");
switch_core_hash_insert(filter, "variable_user_name", "1");
switch_core_hash_insert(filter, "variable_endpoint_disposition", "1");
switch_core_hash_insert(filter, "variable_originate_disposition", "1");
switch_core_hash_insert(filter, "variable_bridge_hangup_cause", "1");
switch_core_hash_insert(filter, "variable_hangup_cause", "1");
switch_core_hash_insert(filter, "variable_last_bridge_proto_specific_hangup_cause", "1");
switch_core_hash_insert(filter, "variable_proto_specific_hangup_cause", "1");
switch_core_hash_insert(filter, "VM-Call-ID", "1");
switch_core_hash_insert(filter, "VM-sub-call-id", "1");
switch_core_hash_insert(filter, "whistle_application_name", "1");
switch_core_hash_insert(filter, "whistle_application_response", "1");
switch_core_hash_insert(filter, "whistle_event_name", "1");
switch_core_hash_insert(filter, "kazoo_application_name", "1");
switch_core_hash_insert(filter, "kazoo_application_response", "1");
switch_core_hash_insert(filter, "kazoo_event_name", "1");
switch_core_hash_insert(filter, "sip_auto_answer_notify", "1");
switch_core_hash_insert(filter, "eavesdrop_group", "1");
switch_core_hash_insert(filter, "origination_caller_id_name", "1");
switch_core_hash_insert(filter, "origination_caller_id_number", "1");
switch_core_hash_insert(filter, "origination_callee_id_name", "1");
switch_core_hash_insert(filter, "origination_callee_id_number", "1");
switch_core_hash_insert(filter, "sip_auth_username", "1");
switch_core_hash_insert(filter, "sip_auth_password", "1");
switch_core_hash_insert(filter, "effective_caller_id_name", "1");
switch_core_hash_insert(filter, "effective_caller_id_number", "1");
switch_core_hash_insert(filter, "effective_callee_id_name", "1");
switch_core_hash_insert(filter, "effective_callee_id_number", "1");
switch_core_hash_insert(filter, "variable_destination_number", "1");
switch_core_hash_insert(filter, "variable_effective_callee_id_name", "1");
switch_core_hash_insert(filter, "variable_effective_callee_id_number", "1");
switch_core_hash_insert(filter, "variable_record_silence_hits", "1");
switch_core_hash_insert(filter, "variable_refer_uuid", "1");
switch_core_hash_insert(filter, "variable_sip_call_id", "1");
switch_core_hash_insert(filter, "variable_sip_h_Referred-By", "1");
switch_core_hash_insert(filter, "variable_sip_h_X-AUTH-PORT", "1");
switch_core_hash_insert(filter, "variable_sip_loopback_req_uri", "1");
switch_core_hash_insert(filter, "variable_sip_received_port", "1");
switch_core_hash_insert(filter, "variable_sip_refer_to", "1");
switch_core_hash_insert(filter, "variable_sip_req_host", "1");
switch_core_hash_insert(filter, "variable_sip_req_uri", "1");
switch_core_hash_insert(filter, "variable_transfer_source", "1");
switch_core_hash_insert(filter, "variable_uuid", "1");
/* Registration headers */
switch_core_hash_insert(filter, "call-id", "1");
switch_core_hash_insert(filter, "profile-name", "1");
switch_core_hash_insert(filter, "from-user", "1");
switch_core_hash_insert(filter, "from-host", "1");
switch_core_hash_insert(filter, "presence-hosts", "1");
switch_core_hash_insert(filter, "contact", "1");
switch_core_hash_insert(filter, "rpid", "1");
switch_core_hash_insert(filter, "status", "1");
switch_core_hash_insert(filter, "expires", "1");
switch_core_hash_insert(filter, "to-user", "1");
switch_core_hash_insert(filter, "to-host", "1");
switch_core_hash_insert(filter, "network-ip", "1");
switch_core_hash_insert(filter, "network-port", "1");
switch_core_hash_insert(filter, "username", "1");
switch_core_hash_insert(filter, "realm", "1");
switch_core_hash_insert(filter, "user-agent", "1");
switch_core_hash_insert(filter, "Hangup-Cause", "1");
switch_core_hash_insert(filter, "Unique-ID", "1");
switch_core_hash_insert(filter, "variable_switch_r_sdp", "1");
switch_core_hash_insert(filter, "variable_rtp_local_sdp_str", "1");
switch_core_hash_insert(filter, "variable_sip_to_uri", "1");
switch_core_hash_insert(filter, "variable_sip_from_uri", "1");
switch_core_hash_insert(filter, "variable_sip_user_agent", "1");
switch_core_hash_insert(filter, "variable_duration", "1");
switch_core_hash_insert(filter, "variable_billsec", "1");
switch_core_hash_insert(filter, "variable_billmsec", "1");
switch_core_hash_insert(filter, "variable_progresssec", "1");
switch_core_hash_insert(filter, "variable_progress_uepoch", "1");
switch_core_hash_insert(filter, "variable_progress_media_uepoch", "1");
switch_core_hash_insert(filter, "variable_start_uepoch", "1");
switch_core_hash_insert(filter, "variable_digits_dialed", "1");
switch_core_hash_insert(filter, "Member-ID", "1");
switch_core_hash_insert(filter, "Floor", "1");
switch_core_hash_insert(filter, "Video", "1");
switch_core_hash_insert(filter, "Hear", "1");
switch_core_hash_insert(filter, "Speak", "1");
switch_core_hash_insert(filter, "Talking", "1");
switch_core_hash_insert(filter, "Current-Energy", "1");
switch_core_hash_insert(filter, "Energy-Level", "1");
switch_core_hash_insert(filter, "Mute-Detect", "1");
/* RTMP headers */
switch_core_hash_insert(filter, "RTMP-Session-ID", "1");
switch_core_hash_insert(filter, "RTMP-Profile", "1");
switch_core_hash_insert(filter, "RTMP-Flash-Version", "1");
switch_core_hash_insert(filter, "RTMP-SWF-URL", "1");
switch_core_hash_insert(filter, "RTMP-TC-URL", "1");
switch_core_hash_insert(filter, "RTMP-Page-URL", "1");
switch_core_hash_insert(filter, "User", "1");
switch_core_hash_insert(filter, "Domain", "1");
/* Fax headers */
switch_core_hash_insert(filter, "variable_fax_bad_rows", "1");
switch_core_hash_insert(filter, "variable_fax_document_total_pages", "1");
switch_core_hash_insert(filter, "variable_fax_document_transferred_pages", "1");
switch_core_hash_insert(filter, "variable_fax_ecm_used", "1");
switch_core_hash_insert(filter, "variable_fax_result_code", "1");
switch_core_hash_insert(filter, "variable_fax_result_text", "1");
switch_core_hash_insert(filter, "variable_fax_success", "1");
switch_core_hash_insert(filter, "variable_fax_transfer_rate", "1");
switch_core_hash_insert(filter, "variable_fax_local_station_id", "1");
switch_core_hash_insert(filter, "variable_fax_remote_station_id", "1");
switch_core_hash_insert(filter, "variable_fax_remote_country", "1");
switch_core_hash_insert(filter, "variable_fax_remote_vendor", "1");
switch_core_hash_insert(filter, "variable_fax_remote_model", "1");
switch_core_hash_insert(filter, "variable_fax_image_resolution", "1");
switch_core_hash_insert(filter, "variable_fax_file_image_resolution", "1");
switch_core_hash_insert(filter, "variable_fax_image_size", "1");
switch_core_hash_insert(filter, "variable_fax_image_pixel_size", "1");
switch_core_hash_insert(filter, "variable_fax_file_image_pixel_size", "1");
switch_core_hash_insert(filter, "variable_fax_longest_bad_row_run", "1");
switch_core_hash_insert(filter, "variable_fax_encoding", "1");
switch_core_hash_insert(filter, "variable_fax_encoding_name", "1");
switch_core_hash_insert(filter, "variable_fax_header", "1");
switch_core_hash_insert(filter, "variable_fax_ident", "1");
switch_core_hash_insert(filter, "variable_fax_timezone", "1");
switch_core_hash_insert(filter, "variable_fax_doc_id", "1");
switch_core_hash_insert(filter, "variable_fax_doc_database", "1");
switch_core_hash_insert(filter, "variable_has_t38", "1");
/* Secure headers */
switch_core_hash_insert(filter, "variable_sdp_secure_savp_only", "1");
switch_core_hash_insert(filter, "variable_rtp_has_crypto", "1");
switch_core_hash_insert(filter, "variable_rtp_secure_media", "1");
switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed", "1");
switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed_audio", "1");
switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed_video", "1");
switch_core_hash_insert(filter, "variable_zrtp_secure_media", "1");
switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed", "1");
switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed_audio", "1");
switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed_video", "1");
switch_core_hash_insert(filter, "sdp_secure_savp_only", "1");
switch_core_hash_insert(filter, "rtp_has_crypto", "1");
switch_core_hash_insert(filter, "rtp_secure_media", "1");
switch_core_hash_insert(filter, "rtp_secure_media_confirmed", "1");
switch_core_hash_insert(filter, "rtp_secure_media_confirmed_audio", "1");
switch_core_hash_insert(filter, "rtp_secure_media_confirmed_video", "1");
switch_core_hash_insert(filter, "zrtp_secure_media", "1");
switch_core_hash_insert(filter, "zrtp_secure_media_confirmed", "1");
switch_core_hash_insert(filter, "zrtp_secure_media_confirmed_audio", "1");
switch_core_hash_insert(filter, "zrtp_secure_media_confirmed_video", "1");
/* Device Redirect headers */
switch_core_hash_insert(filter, "variable_last_bridge_hangup_cause", "1");
switch_core_hash_insert(filter, "variable_sip_redirected_by", "1");
switch_core_hash_insert(filter, "intercepted_by", "1");
switch_core_hash_insert(filter, "variable_bridge_uuid", "1");
switch_core_hash_insert(filter, "Record-File-Path", "1");
/* Loopback headers */
switch_core_hash_insert(filter, "variable_loopback_bowout_on_execute", "1");
switch_core_hash_insert(filter, "variable_loopback_bowout", "1");
switch_core_hash_insert(filter, "variable_other_loopback_leg_uuid", "1");
switch_core_hash_insert(filter, "variable_loopback_leg", "1");
switch_core_hash_insert(filter, "variable_is_loopback", "1");
// SMS
switch_core_hash_insert(filter, "Message-ID", "1");
switch_core_hash_insert(filter, "Delivery-Failure", "1");
switch_core_hash_insert(filter, "Delivery-Result-Code", "1");
return filter;
}
static void fetch_config_filters(switch_memory_pool_t *pool)
{
char *cf = "kazoo.conf";
switch_xml_t cfg, xml, child, param;
switch_event_t *params;
switch_event_create(&params, SWITCH_EVENT_REQUEST_PARAMS);
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "Action", "request-filter");
if (!(xml = switch_xml_open_cfg(cf, &cfg, params))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open configuration file %s\n", cf);
} else if ((child = switch_xml_child(cfg, "event-filter"))) {
switch_hash_t *filter;
switch_hash_t *old_filter;
switch_core_hash_init(&filter);
for (param = switch_xml_child(child, "header"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
switch_core_hash_insert(filter, var, "1");
}
old_filter = kazoo_globals.event_filter;
kazoo_globals.event_filter = filter;
if (old_filter) {
switch_core_hash_destroy(&old_filter);
}
kazoo_globals.config_fetched = 1;
switch_xml_free(xml);
}
}
static void fetch_config_handlers(switch_memory_pool_t *pool)
{
char *cf = "kazoo.conf";
switch_xml_t cfg, xml;
switch_event_t *params;
switch_event_create(&params, SWITCH_EVENT_REQUEST_PARAMS);
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "Action", "request-handlers");
if (!(xml = switch_xml_open_cfg(cf, &cfg, params))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open configuration file %s\n", cf);
} else {
kazoo_config_handlers(cfg);
kazoo_globals.config_fetched = 1;
switch_xml_free(xml);
}
}
static void *SWITCH_THREAD_FUNC fetch_config_exec(switch_thread_t *thread, void *obj)
{
switch_memory_pool_t *pool = (switch_memory_pool_t *)obj;
fetch_config_filters(pool);
fetch_config_handlers(pool);
kazoo_globals.config_fetched = 1;
return NULL;
}
void fetch_config() {
switch_memory_pool_t *pool;
switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL;
switch_uuid_t uuid;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "fetching kazoo config\n");
switch_core_new_memory_pool(&pool);
switch_threadattr_create(&thd_attr, pool);
switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
switch_uuid_get(&uuid);
switch_thread_create(&thread, thd_attr, fetch_config_exec, pool, pool);
}
SWITCH_MODULE_RUNTIME_FUNCTION(mod_kazoo_runtime) {
switch_os_socket_t os_socket;
if(create_acceptor() != SWITCH_STATUS_SUCCESS) {
// TODO: what would we need to clean up here
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to create erlang connection acceptor!\n");
close_socket(&kazoo_globals.acceptor);
return SWITCH_STATUS_TERM;
}
switch_atomic_inc(&kazoo_globals.threads);
switch_os_sock_get(&os_socket, kazoo_globals.acceptor);
while (switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) {
int nodefd;
ErlConnect conn;
/* zero out errno because ei_accept doesn't differentiate between a */
/* failed authentication or a socket failure, or a client version */
/* mismatch or a godzilla attack (and a godzilla attack is highly likely) */
errno = 0;
/* wait here for an erlang node to connect, timming out to check if our module is still running every now-and-again */
if ((nodefd = ei_accept_tmo(&kazoo_globals.ei_cnode, (int) os_socket, &conn, kazoo_globals.connection_timeout)) == ERL_ERROR) {
if (erl_errno == ETIMEDOUT) {
continue;
} else if (errno) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Erlang connection acceptor socket error %d %d\n", erl_errno, errno);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"Erlang node connection failed - ensure your cookie matches '%s' and you are using a good nodename\n", kazoo_globals.ei_cookie);
}
continue;
}
if (!switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) {
break;
}
/* NEW ERLANG NODE CONNECTION! Hello friend! */
new_kazoo_node(nodefd, &conn);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Erlang connection acceptor shut down\n");
switch_atomic_dec(&kazoo_globals.threads);
return SWITCH_STATUS_TERM;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -48,7 +48,8 @@ static char *my_dup(const char *s) {
static const char* private_headers[] = {"variable_sip_h_", "sip_h_", "P-", "X-"};
static int is_private_header(const char *name) {
for(int i=0; i < 4; i++) {
int i;
for(i=0; i < 4; i++) {
if(!strncmp(name, private_headers[i], strlen(private_headers[i]))) {
return 1;
}
@ -102,51 +103,105 @@ static switch_status_t kazoo_event_dup(switch_event_t **clone, switch_event_t *e
return SWITCH_STATUS_SUCCESS;
}
static void event_handler(switch_event_t *event) {
static int encode_event_old(switch_event_t *event, ei_x_buff *ebuf) {
switch_event_t *clone = NULL;
ei_event_stream_t *event_stream = (ei_event_stream_t *) event->bind_user_data;
if (kazoo_event_dup(&clone, event, kazoo_globals.event_filter) != SWITCH_STATUS_SUCCESS) {
return 0;
}
ei_encode_switch_event(ebuf, clone);
switch_event_destroy(&clone);
return 1;
}
static int encode_event_new(switch_event_t *event, ei_x_buff *ebuf) {
kazoo_message_ptr msg = NULL;
ei_event_binding_t *event_binding = (ei_event_binding_t *) event->bind_user_data;
msg = kazoo_message_create_event(event, event_binding->event, kazoo_globals.events);
if(msg == NULL) {
return 0;
}
ei_x_encode_tuple_header(ebuf, 2);
ei_x_encode_atom(ebuf, "event");
ei_encode_json(ebuf, msg->JObj);
kazoo_message_destroy(&msg);
return 1;
}
/*
* event_handler is duplicated when there are 2+ nodes connected
* with the same bindings
* we should maintain a list of event_streams in event_binding struct
* and build a ref count in the message
*
*/
static void event_handler(switch_event_t *event) {
ei_event_binding_t *event_binding = (ei_event_binding_t *) event->bind_user_data;
ei_event_stream_t *event_stream = event_binding->stream;
ei_x_buff *ebuf = NULL;
int res = 0;
/* if mod_kazoo or the event stream isn't running dont push a new event */
if (!switch_test_flag(event_stream, LFLAG_RUNNING) || !switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) {
return;
}
if (event->event_id == SWITCH_EVENT_CUSTOM) {
ei_event_binding_t *event_binding = event_stream->bindings;
unsigned short int found = 0;
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Switch-Nodename", kazoo_globals.ei_cnode.thisnodename);
if (!event->subclass_name) {
return;
}
while(event_binding != NULL) {
if (event_binding->type == SWITCH_EVENT_CUSTOM) {
if(event_binding->subclass_name
&& !strcmp(event->subclass_name, event_binding->subclass_name)) {
found = 1;
break;
}
}
event_binding = event_binding->next;
}
if (!found) {
switch_malloc(ebuf, sizeof(*ebuf));
if(ebuf == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not allocate erlang buffer for mod_kazoo message\n");
return;
}
memset(ebuf, 0, sizeof(*ebuf));
if(kazoo_globals.event_stream_preallocate > 0) {
ebuf->buff = malloc(kazoo_globals.event_stream_preallocate);
ebuf->buffsz = kazoo_globals.event_stream_preallocate;
ebuf->index = 0;
if(ebuf->buff == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not pre-allocate memory for mod_kazoo message\n");
switch_safe_free(ebuf);
return;
}
ei_x_encode_version(ebuf);
} else {
ei_x_new_with_version(ebuf);
}
/* try to clone the event and push it to the event stream thread */
/* TODO: someday maybe the filter comes from the event_stream (set during init only)
* and is per-binding so we only send headers that a process requests */
if (kazoo_event_dup(&clone, event, kazoo_globals.event_filter) == SWITCH_STATUS_SUCCESS) {
if (switch_queue_trypush(event_stream->queue, clone) != SWITCH_STATUS_SUCCESS) {
if(event_stream->node->legacy) {
res = encode_event_old(event, ebuf);
} else {
res = encode_event_new(event, ebuf);
}
if(!res) {
ei_x_free(ebuf);
switch_safe_free(ebuf);
return;
}
if (kazoo_globals.event_stream_preallocate > 0 && ebuf->buffsz > kazoo_globals.event_stream_preallocate) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "increased event stream buffer size to %d\n", ebuf->buffsz);
}
if (switch_queue_trypush(event_stream->queue, ebuf) != SWITCH_STATUS_SUCCESS) {
/* if we couldn't place the cloned event into the listeners */
/* event queue make sure we destroy it, real good like */
switch_event_destroy(&clone);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory error: Have a good trip? See you next fall!\n");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error placing the event in the listeners queue\n");
ei_x_free(ebuf);
switch_safe_free(ebuf);
}
}
static void *SWITCH_THREAD_FUNC event_stream_loop(switch_thread_t *thread, void *obj) {
@ -178,7 +233,8 @@ static void *SWITCH_THREAD_FUNC event_stream_loop(switch_thread_t *thread, void
/* check if a new connection is pending */
if (switch_pollset_poll(event_stream->pollset, 0, &numfds, &fds) == SWITCH_STATUS_SUCCESS) {
for (int32_t i = 0; i < numfds; i++) {
int32_t i;
for (i = 0; i < numfds; i++) {
switch_socket_t *newsocket;
/* accept the new client connection */
@ -217,46 +273,25 @@ static void *SWITCH_THREAD_FUNC event_stream_loop(switch_thread_t *thread, void
}
/* if there was an event waiting in our queue send it to the client */
if (switch_queue_pop_timeout(event_stream->queue, &pop, 500000) == SWITCH_STATUS_SUCCESS) {
switch_event_t *event = (switch_event_t *) pop;
if (switch_queue_pop_timeout(event_stream->queue, &pop, 200000) == SWITCH_STATUS_SUCCESS) {
ei_x_buff *ebuf = (ei_x_buff *) pop;
if (event_stream->socket) {
ei_x_buff ebuf;
char byte;
short i = event_stream_framing;
switch_size_t size = 1;
if(kazoo_globals.event_stream_preallocate > 0) {
ebuf.buff = malloc(kazoo_globals.event_stream_preallocate);
ebuf.buffsz = kazoo_globals.event_stream_preallocate;
ebuf.index = 0;
if(ebuf.buff == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not pre-allocate memory for mod_kazoo message\n");
break;
}
ei_x_encode_version(&ebuf);
} else {
ei_x_new_with_version(&ebuf);
}
ei_encode_switch_event(&ebuf, event);
if (kazoo_globals.event_stream_preallocate > 0 && ebuf.buffsz > kazoo_globals.event_stream_preallocate) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "increased event stream buffer size to %d\n", ebuf.buffsz);
}
while (i) {
byte = ebuf.index >> (8 * --i);
byte = ebuf->index >> (8 * --i);
switch_socket_send(event_stream->socket, &byte, &size);
}
size = (switch_size_t)ebuf.index;
switch_socket_send(event_stream->socket, ebuf.buff, &size);
ei_x_free(&ebuf);
size = (switch_size_t)ebuf->index;
switch_socket_send(event_stream->socket, ebuf->buff, &size);
}
switch_event_destroy(&event);
ei_x_free(ebuf);
switch_safe_free(ebuf);
}
}
@ -297,11 +332,12 @@ static void *SWITCH_THREAD_FUNC event_stream_loop(switch_thread_t *thread, void
return NULL;
}
ei_event_stream_t *new_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from) {
ei_event_stream_t *new_event_stream(ei_node_t *ei_node, const erlang_pid *from) {
switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL;
switch_memory_pool_t *pool = NULL;
ei_event_stream_t *event_stream;
ei_event_stream_t **event_streams = &ei_node->event_streams;
/* create memory pool for this event stream */
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
@ -320,6 +356,7 @@ ei_event_stream_t *new_event_stream(ei_event_stream_t **event_streams, const erl
event_stream->bindings = NULL;
event_stream->pool = pool;
event_stream->connected = SWITCH_FALSE;
event_stream->node = ei_node;
memcpy(&event_stream->pid, from, sizeof(erlang_pid));
switch_queue_create(&event_stream->queue, MAX_QUEUE_LEN, pool);
@ -443,17 +480,68 @@ switch_status_t remove_event_streams(ei_event_stream_t **event_streams) {
return SWITCH_STATUS_SUCCESS;
}
switch_status_t add_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name) {
void bind_event_profile(ei_event_binding_t *event_binding, kazoo_event_ptr event)
{
switch_event_types_t event_type;
while(event != NULL) {
if (switch_name_event(event->name, &event_type) != SWITCH_STATUS_SUCCESS) {
event_type = SWITCH_EVENT_CUSTOM;
}
if(event_binding->type != SWITCH_EVENT_CUSTOM
&& event_binding->type == event_type) {
break;
}
if (event_binding->type == SWITCH_EVENT_CUSTOM
&& event_binding->type == event_type
&& !strcasecmp(event_binding->subclass_name, event->name)) {
break;
}
event = event->next;
}
event_binding->event = event;
if(event == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "EVENT BINDING ERROR %s - %s\n",switch_event_name(event_binding->type), event_binding->subclass_name);
}
}
void bind_event_profiles(kazoo_event_ptr event)
{
ei_node_t *ei_node = kazoo_globals.ei_nodes;
while(ei_node) {
ei_event_stream_t *event_streams = ei_node->event_streams;
while(event_streams) {
ei_event_binding_t *bindings = event_streams->bindings;
while(bindings) {
bind_event_profile(bindings, event);
bindings = bindings->next;
}
event_streams = event_streams->next;
}
ei_node = ei_node->next;
}
}
switch_status_t add_event_binding(ei_event_stream_t *event_stream, const char *event_name) {
ei_event_binding_t *event_binding = event_stream->bindings;
switch_event_types_t event_type;
if(!strcasecmp(event_name, "CUSTOM")) {
return SWITCH_STATUS_SUCCESS;
}
if (switch_name_event(event_name, &event_type) != SWITCH_STATUS_SUCCESS) {
event_type = SWITCH_EVENT_CUSTOM;
}
/* check if the event binding already exists, ignore if so */
while(event_binding != NULL) {
if (event_binding->type == SWITCH_EVENT_CUSTOM) {
if(subclass_name
&& event_binding->subclass_name
&& !strcmp(subclass_name, event_binding->subclass_name)) {
return SWITCH_STATUS_SUCCESS;
}
if(event_type == SWITCH_EVENT_CUSTOM
&& event_name
&& event_binding->subclass_name
&& !strcasecmp(event_name, event_binding->subclass_name)) {
return SWITCH_STATUS_SUCCESS;
}
} else if (event_binding->type == event_type) {
return SWITCH_STATUS_SUCCESS;
}
@ -467,18 +555,21 @@ switch_status_t add_event_binding(ei_event_stream_t *event_stream, const switch_
}
/* prepare the event binding struct */
event_binding->stream = event_stream;
event_binding->type = event_type;
if (!subclass_name || zstr(subclass_name)) {
event_binding->subclass_name = NULL;
if(event_binding->type == SWITCH_EVENT_CUSTOM) {
event_binding->subclass_name = switch_core_strdup(event_stream->pool, event_name);
} else {
/* TODO: free strdup? */
event_binding->subclass_name = strdup(subclass_name);
event_binding->subclass_name = SWITCH_EVENT_SUBCLASS_ANY;
}
event_binding->next = NULL;
bind_event_profile(event_binding, kazoo_globals.events->events);
/* bind to the event with a unique ID and capture the event_node pointer */
switch_uuid_str(event_binding->id, sizeof(event_binding->id));
if (switch_event_bind_removable(event_binding->id, event_type, subclass_name, event_handler, event_stream, &event_binding->node) != SWITCH_STATUS_SUCCESS) {
if (switch_event_bind_removable(event_binding->id, event_type, event_binding->subclass_name, event_handler, event_binding, &event_binding->node) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to bind to event %s %s!\n"
,switch_event_name(event_binding->type), event_binding->subclass_name ? event_binding->subclass_name : "");
return SWITCH_STATUS_GENERR;

View File

@ -32,38 +32,9 @@
*/
#include "mod_kazoo.h"
struct xml_fetch_reply_s {
char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
char *xml_str;
struct xml_fetch_reply_s *next;
};
typedef struct xml_fetch_reply_s xml_fetch_reply_t;
struct fetch_handler_s {
erlang_pid pid;
struct fetch_handler_s *next;
};
typedef struct fetch_handler_s fetch_handler_t;
struct ei_xml_client_s {
ei_node_t *ei_node;
fetch_handler_t *fetch_handlers;
struct ei_xml_client_s *next;
};
typedef struct ei_xml_client_s ei_xml_client_t;
struct ei_xml_agent_s {
switch_memory_pool_t *pool;
switch_xml_section_t section;
switch_thread_rwlock_t *lock;
ei_xml_client_t *clients;
switch_mutex_t *current_client_mutex;
ei_xml_client_t *current_client;
switch_mutex_t *replies_mutex;
switch_thread_cond_t *new_reply;
xml_fetch_reply_t *replies;
};
typedef struct ei_xml_agent_s ei_xml_agent_t;
static char *xml_section_to_string(switch_xml_section_t section) {
switch(section) {
@ -77,6 +48,8 @@ static char *xml_section_to_string(switch_xml_section_t section) {
return "chatplan";
case SWITCH_XML_SECTION_CHANNELS:
return "channels";
case SWITCH_XML_SECTION_LANGUAGES:
return "languages";
default:
return "unknown";
}
@ -133,6 +106,11 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con
ei_xml_client_t *client;
fetch_handler_t *fetch_handler;
xml_fetch_reply_t reply, *pending, *prev = NULL;
switch_event_t *event = params;
kazoo_fetch_profile_ptr profile = agent->profile;
const char *fetch_call_id;
ei_send_msg_t *send_msg = NULL;
int sent = 0;
now = switch_micro_time_now();
@ -164,12 +142,60 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con
return xml;
}
if(event == NULL) {
if (switch_event_create(&event, SWITCH_EVENT_GENERAL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error creating event for fetch handler\n");
return xml;
}
}
/* prepare the reply collector */
switch_uuid_get(&uuid);
switch_uuid_format(reply.uuid_str, &uuid);
reply.next = NULL;
reply.xml_str = NULL;
if((fetch_call_id = switch_event_get_header(event, "Fetch-Call-UUID")) != NULL) {
switch_core_session_t *session = NULL;
if((session = switch_core_session_force_locate(fetch_call_id)) != NULL) {
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_channel_event_set_data(channel, event);
switch_core_session_rwunlock(session);
}
}
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Fetch-UUID", reply.uuid_str);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Fetch-Section", section);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Fetch-Tag", tag_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Fetch-Key-Name", key_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Fetch-Key-Value", key_value);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Fetch-Timeout", "%u", profile->fetch_timeout);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Fetch-Timestamp-Micro", "%ld", (uint64_t)now);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Kazoo-Version", VERSION);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Kazoo-Bundle", BUNDLE);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Kazoo-Release", RELEASE);
switch_malloc(send_msg, sizeof(*send_msg));
if(client->ei_node->legacy) {
ei_x_new_with_version(&send_msg->buf);
ei_x_encode_tuple_header(&send_msg->buf, 7);
ei_x_encode_atom(&send_msg->buf, "fetch");
ei_x_encode_atom(&send_msg->buf, section);
_ei_x_encode_string(&send_msg->buf, tag_name ? tag_name : "undefined");
_ei_x_encode_string(&send_msg->buf, key_name ? key_name : "undefined");
_ei_x_encode_string(&send_msg->buf, key_value ? key_value : "undefined");
_ei_x_encode_string(&send_msg->buf, reply.uuid_str);
ei_encode_switch_event_headers(&send_msg->buf, event);
} else {
kazoo_message_ptr msg = kazoo_message_create_fetch(event, profile);
ei_x_new_with_version(&send_msg->buf);
ei_x_encode_tuple_header(&send_msg->buf, 2);
ei_x_encode_atom(&send_msg->buf, "fetch");
ei_encode_json(&send_msg->buf, msg->JObj);
kazoo_message_destroy(&msg);
}
/* add our reply placeholder to the replies list */
switch_mutex_lock(agent->replies_mutex);
if (!agent->replies) {
@ -181,28 +207,8 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con
switch_mutex_unlock(agent->replies_mutex);
fetch_handler = client->fetch_handlers;
while (fetch_handler != NULL) {
ei_send_msg_t *send_msg;
switch_malloc(send_msg, sizeof(*send_msg));
while (fetch_handler != NULL && sent == 0) {
memcpy(&send_msg->pid, &fetch_handler->pid, sizeof(erlang_pid));
ei_x_new_with_version(&send_msg->buf);
ei_x_encode_tuple_header(&send_msg->buf, 7);
ei_x_encode_atom(&send_msg->buf, "fetch");
ei_x_encode_atom(&send_msg->buf, section);
_ei_x_encode_string(&send_msg->buf, tag_name ? tag_name : "undefined");
_ei_x_encode_string(&send_msg->buf, key_name ? key_name : "undefined");
_ei_x_encode_string(&send_msg->buf, key_value ? key_value : "undefined");
_ei_x_encode_string(&send_msg->buf, reply.uuid_str);
if (params) {
ei_encode_switch_event_headers(&send_msg->buf, params);
} else {
ei_x_encode_empty_list(&send_msg->buf);
}
if (switch_queue_trypush(client->ei_node->send_msgs, send_msg) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send %s XML request to %s <%d.%d.%d>\n"
,section
@ -210,8 +216,6 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con
,fetch_handler->pid.creation
,fetch_handler->pid.num
,fetch_handler->pid.serial);
ei_x_free(&send_msg->buf);
switch_safe_free(send_msg);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sending %s XML request (%s) to %s <%d.%d.%d>\n"
,section
@ -220,11 +224,19 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con
,fetch_handler->pid.creation
,fetch_handler->pid.num
,fetch_handler->pid.serial);
sent = 1;
}
fetch_handler = fetch_handler->next;
}
if(!sent) {
ei_x_free(&send_msg->buf);
switch_safe_free(send_msg);
}
if(params == NULL)
switch_event_destroy(&event);
/* wait for a reply (if there isnt already one...amazingly improbable but lets not take shortcuts */
switch_mutex_lock(agent->replies_mutex);
@ -292,6 +304,42 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con
return xml;
}
void bind_fetch_profile(ei_xml_agent_t *agent, kazoo_config_ptr fetch_handlers)
{
switch_hash_index_t *hi;
kazoo_fetch_profile_ptr val = NULL, ptr = NULL;
for (hi = switch_core_hash_first(fetch_handlers->hash); hi; hi = switch_core_hash_next(&hi)) {
switch_core_hash_this(hi, NULL, NULL, (void**) &val);
if (val && val->section == agent->section) {
ptr = val;
break;
}
}
agent->profile = ptr;
}
void rebind_fetch_profiles(kazoo_config_ptr fetch_handlers)
{
if(kazoo_globals.config_fetch_binding != NULL)
bind_fetch_profile((ei_xml_agent_t *) switch_xml_get_binding_user_data(kazoo_globals.config_fetch_binding), fetch_handlers);
if(kazoo_globals.directory_fetch_binding != NULL)
bind_fetch_profile((ei_xml_agent_t *) switch_xml_get_binding_user_data(kazoo_globals.directory_fetch_binding), fetch_handlers);
if(kazoo_globals.dialplan_fetch_binding != NULL)
bind_fetch_profile((ei_xml_agent_t *) switch_xml_get_binding_user_data(kazoo_globals.dialplan_fetch_binding), fetch_handlers);
if(kazoo_globals.channels_fetch_binding != NULL)
bind_fetch_profile((ei_xml_agent_t *) switch_xml_get_binding_user_data(kazoo_globals.channels_fetch_binding), fetch_handlers);
if(kazoo_globals.languages_fetch_binding != NULL)
bind_fetch_profile((ei_xml_agent_t *) switch_xml_get_binding_user_data(kazoo_globals.languages_fetch_binding), fetch_handlers);
if(kazoo_globals.chatplan_fetch_binding != NULL)
bind_fetch_profile((ei_xml_agent_t *) switch_xml_get_binding_user_data(kazoo_globals.chatplan_fetch_binding), fetch_handlers);
}
static switch_status_t bind_fetch_agent(switch_xml_section_t section, switch_xml_binding_t **binding) {
switch_memory_pool_t *pool = NULL;
ei_xml_agent_t *agent;
@ -326,6 +374,8 @@ static switch_status_t bind_fetch_agent(switch_xml_section_t section, switch_xml
switch_thread_cond_create(&agent->new_reply, pool);
agent->replies = NULL;
bind_fetch_profile(agent, kazoo_globals.fetch_handlers);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Bound to %s XML requests\n"
,xml_section_to_string(section));
@ -336,6 +386,9 @@ static switch_status_t unbind_fetch_agent(switch_xml_binding_t **binding) {
ei_xml_agent_t *agent;
ei_xml_client_t *client;
if(*binding == NULL)
return SWITCH_STATUS_GENERR;
/* get a pointer to our user_data */
agent = (ei_xml_agent_t *)switch_xml_get_binding_user_data(*binding);
@ -399,6 +452,9 @@ static switch_status_t remove_xml_client(ei_node_t *ei_node, switch_xml_binding_
ei_xml_client_t *client, *prev = NULL;
int found = 0;
if(binding == NULL)
return SWITCH_STATUS_GENERR;
agent = (ei_xml_agent_t *)switch_xml_get_binding_user_data(binding);
/* write-lock the agent */
@ -575,8 +631,9 @@ switch_status_t bind_fetch_agents() {
bind_fetch_agent(SWITCH_XML_SECTION_CONFIG, &kazoo_globals.config_fetch_binding);
bind_fetch_agent(SWITCH_XML_SECTION_DIRECTORY, &kazoo_globals.directory_fetch_binding);
bind_fetch_agent(SWITCH_XML_SECTION_DIALPLAN, &kazoo_globals.dialplan_fetch_binding);
bind_fetch_agent(SWITCH_XML_SECTION_CHATPLAN, &kazoo_globals.chatplan_fetch_binding);
bind_fetch_agent(SWITCH_XML_SECTION_CHANNELS, &kazoo_globals.channels_fetch_binding);
bind_fetch_agent(SWITCH_XML_SECTION_LANGUAGES, &kazoo_globals.languages_fetch_binding);
bind_fetch_agent(SWITCH_XML_SECTION_CHATPLAN, &kazoo_globals.chatplan_fetch_binding);
return SWITCH_STATUS_SUCCESS;
}
@ -585,8 +642,9 @@ switch_status_t unbind_fetch_agents() {
unbind_fetch_agent(&kazoo_globals.config_fetch_binding);
unbind_fetch_agent(&kazoo_globals.directory_fetch_binding);
unbind_fetch_agent(&kazoo_globals.dialplan_fetch_binding);
unbind_fetch_agent(&kazoo_globals.chatplan_fetch_binding);
unbind_fetch_agent(&kazoo_globals.channels_fetch_binding);
unbind_fetch_agent(&kazoo_globals.languages_fetch_binding);
unbind_fetch_agent(&kazoo_globals.chatplan_fetch_binding);
return SWITCH_STATUS_SUCCESS;
}
@ -595,8 +653,9 @@ switch_status_t remove_xml_clients(ei_node_t *ei_node) {
remove_xml_client(ei_node, kazoo_globals.config_fetch_binding);
remove_xml_client(ei_node, kazoo_globals.directory_fetch_binding);
remove_xml_client(ei_node, kazoo_globals.dialplan_fetch_binding);
remove_xml_client(ei_node, kazoo_globals.chatplan_fetch_binding);
remove_xml_client(ei_node, kazoo_globals.channels_fetch_binding);
remove_xml_client(ei_node, kazoo_globals.languages_fetch_binding);
remove_xml_client(ei_node, kazoo_globals.chatplan_fetch_binding);
return SWITCH_STATUS_SUCCESS;
}
@ -606,6 +665,9 @@ switch_status_t add_fetch_handler(ei_node_t *ei_node, erlang_pid *from, switch_x
ei_xml_client_t *client;
fetch_handler_t *fetch_handler;
if(binding == NULL)
return SWITCH_STATUS_GENERR;
agent = (ei_xml_agent_t *)switch_xml_get_binding_user_data(binding);
/* write-lock the agent */
@ -653,8 +715,9 @@ switch_status_t remove_fetch_handlers(ei_node_t *ei_node, erlang_pid *from) {
remove_fetch_handler(ei_node, from, kazoo_globals.config_fetch_binding);
remove_fetch_handler(ei_node, from, kazoo_globals.directory_fetch_binding);
remove_fetch_handler(ei_node, from, kazoo_globals.dialplan_fetch_binding);
remove_fetch_handler(ei_node, from, kazoo_globals.chatplan_fetch_binding);
remove_fetch_handler(ei_node, from, kazoo_globals.channels_fetch_binding);
remove_fetch_handler(ei_node, from, kazoo_globals.languages_fetch_binding);
remove_fetch_handler(ei_node, from, kazoo_globals.chatplan_fetch_binding);
return SWITCH_STATUS_SUCCESS;
}
@ -689,8 +752,9 @@ switch_status_t handle_api_command_streams(ei_node_t *ei_node, switch_stream_han
handle_api_command_stream(ei_node, stream, kazoo_globals.config_fetch_binding);
handle_api_command_stream(ei_node, stream, kazoo_globals.directory_fetch_binding);
handle_api_command_stream(ei_node, stream, kazoo_globals.dialplan_fetch_binding);
handle_api_command_stream(ei_node, stream, kazoo_globals.chatplan_fetch_binding);
handle_api_command_stream(ei_node, stream, kazoo_globals.channels_fetch_binding);
handle_api_command_stream(ei_node, stream, kazoo_globals.languages_fetch_binding);
handle_api_command_stream(ei_node, stream, kazoo_globals.chatplan_fetch_binding);
return SWITCH_STATUS_SUCCESS;
}

View File

@ -0,0 +1,195 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Based on mod_skel by
* Anthony Minessale II <anthm@freeswitch.org>
*
* Contributor(s):
*
* Daniel Bryars <danb@aeriandi.com>
* Tim Brown <tim.brown@aeriandi.com>
* Anthony Minessale II <anthm@freeswitch.org>
* William King <william.king@quentustech.com>
* Mike Jerris <mike@jerris.com>
*
* kazoo.c -- Sends FreeSWITCH events to an AMQP broker
*
*/
#ifndef KAZOO_FIELDS_H
#define KAZOO_FIELDS_H
#include <switch.h>
#define MAX_LIST_FIELDS 25
typedef struct kazoo_log_levels kazoo_loglevels_t;
typedef kazoo_loglevels_t *kazoo_loglevels_ptr;
struct kazoo_log_levels
{
switch_log_level_t success_log_level;
switch_log_level_t failed_log_level;
switch_log_level_t warn_log_level;
switch_log_level_t info_log_level;
switch_log_level_t time_log_level;
switch_log_level_t filtered_event_log_level;
switch_log_level_t filtered_field_log_level;
};
typedef struct kazoo_logging kazoo_logging_t;
typedef kazoo_logging_t *kazoo_logging_ptr;
struct kazoo_logging
{
kazoo_loglevels_ptr levels;
const char *profile_name;
const char *event_name;
};
typedef struct kazoo_list_s {
char *value[MAX_LIST_FIELDS];
int size;
} kazoo_list_t;
typedef enum {
FILTER_COMPARE_REGEX,
FILTER_COMPARE_LIST,
FILTER_COMPARE_VALUE,
FILTER_COMPARE_PREFIX,
FILTER_COMPARE_EXISTS
} kazoo_filter_compare_type;
typedef enum {
FILTER_EXCLUDE,
FILTER_INCLUDE,
FILTER_ENSURE
} kazoo_filter_type;
typedef struct kazoo_filter_t {
kazoo_filter_type type;
kazoo_filter_compare_type compare;
char* name;
char* value;
kazoo_list_t list;
struct kazoo_filter_t* next;
} kazoo_filter, *kazoo_filter_ptr;
typedef enum {
JSON_NONE,
JSON_STRING,
JSON_NUMBER,
JSON_BOOLEAN,
JSON_OBJECT,
JSON_RAW
} kazoo_json_field_type;
typedef enum {
FIELD_NONE,
FIELD_COPY,
FIELD_STATIC,
FIELD_FIRST_OF,
FIELD_EXPAND,
FIELD_PREFIX,
FIELD_OBJECT,
FIELD_GROUP,
FIELD_REFERENCE,
} kazoo_field_type;
typedef struct kazoo_field_t kazoo_field;
typedef kazoo_field *kazoo_field_ptr;
typedef struct kazoo_fields_t kazoo_fields;
typedef kazoo_fields *kazoo_fields_ptr;
typedef struct kazoo_definition_t kazoo_definition;
typedef kazoo_definition *kazoo_definition_ptr;
struct kazoo_field_t {
char* name;
char* value;
char* as;
kazoo_list_t list;
switch_bool_t exclude_prefix;
kazoo_field_type in_type;
kazoo_json_field_type out_type;
kazoo_filter_ptr filter;
kazoo_definition_ptr ref;
kazoo_field_ptr next;
kazoo_fields_ptr children;
};
struct kazoo_fields_t {
kazoo_field_ptr head;
int verbose;
};
struct kazoo_definition_t {
char* name;
kazoo_field_ptr head;
kazoo_filter_ptr filter;
};
struct kazoo_event {
kazoo_event_profile_ptr profile;
char *name;
kazoo_fields_ptr fields;
kazoo_filter_ptr filter;
kazoo_event_t* next;
};
struct kazoo_event_profile {
char *name;
kazoo_config_ptr root;
switch_bool_t running;
switch_memory_pool_t *pool;
kazoo_filter_ptr filter;
kazoo_fields_ptr fields;
kazoo_event_ptr events;
kazoo_loglevels_ptr logging;
};
struct kazoo_fetch_profile {
char *name;
kazoo_config_ptr root;
switch_bool_t running;
switch_memory_pool_t *pool;
kazoo_fields_ptr fields;
int fetch_timeout;
switch_mutex_t *fetch_reply_mutex;
switch_hash_t *fetch_reply_hash;
switch_xml_binding_t *fetch_binding;
switch_xml_section_t section;
kazoo_loglevels_ptr logging;
};
#endif /* KAZOO_FIELDS_H */

View File

@ -0,0 +1,458 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Based on mod_skel by
* Anthony Minessale II <anthm@freeswitch.org>
*
* Contributor(s):
*
* Daniel Bryars <danb@aeriandi.com>
* Tim Brown <tim.brown@aeriandi.com>
* Anthony Minessale II <anthm@freeswitch.org>
* William King <william.king@quentustech.com>
* Mike Jerris <mike@jerris.com>
*
* kazoo.c -- Sends FreeSWITCH events to an AMQP broker
*
*/
#include "mod_kazoo.h"
/* deletes then add */
void kazoo_cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
{
cJSON_DeleteItemFromObject(object, string);
cJSON_AddItemToObject(object, string, item);
}
static int inline filter_compare(switch_event_t* evt, kazoo_filter_ptr filter)
{
switch_event_header_t *header;
int hasValue = 0, /*size, */n;
char *value;
switch(filter->compare) {
case FILTER_COMPARE_EXISTS:
hasValue = switch_event_get_header(evt, filter->name) != NULL ? 1 : 0;
break;
case FILTER_COMPARE_VALUE:
value = switch_event_get_header_nil(evt, filter->name);
hasValue = !strcmp(value, filter->value);
break;
case FILTER_COMPARE_PREFIX:
for (header = evt->headers; header; header = header->next) {
if(!strncmp(header->name, filter->value, strlen(filter->value))) {
hasValue = 1;
break;
}
}
break;
case FILTER_COMPARE_LIST:
value = switch_event_get_header(evt, filter->name);
if(value) {
for(n = 0; n < filter->list.size; n++) {
if(!strncmp(value, filter->list.value[n], strlen(filter->list.value[n]))) {
hasValue = 1;
break;
}
}
}
break;
case FILTER_COMPARE_REGEX:
break;
default:
break;
}
return hasValue;
}
static kazoo_filter_ptr inline filter_event(switch_event_t* evt, kazoo_filter_ptr filter)
{
while(filter) {
int hasValue = filter_compare(evt, filter);
if(filter->type == FILTER_EXCLUDE) {
if(hasValue)
break;
} else if(filter->type == FILTER_INCLUDE) {
if(!hasValue)
break;
}
filter = filter->next;
}
return filter;
}
static void kazoo_event_init_json_fields(switch_event_t *event, cJSON *json)
{
switch_event_header_t *hp;
for (hp = event->headers; hp; hp = hp->next) {
if (hp->idx) {
cJSON *a = cJSON_CreateArray();
int i;
for(i = 0; i < hp->idx; i++) {
cJSON_AddItemToArray(a, cJSON_CreateString(hp->array[i]));
}
cJSON_AddItemToObject(json, hp->name, a);
} else {
cJSON_AddItemToObject(json, hp->name, cJSON_CreateString(hp->value));
}
}
}
static switch_status_t kazoo_event_init_json(kazoo_fields_ptr fields1, kazoo_fields_ptr fields2, switch_event_t* evt, cJSON** clone)
{
switch_status_t status;
if( (fields2 && fields2->verbose)
|| (fields1 && fields1->verbose)
|| ( (!fields2) && (!fields1)) ) {
status = switch_event_serialize_json_obj(evt, clone);
} else {
status = SWITCH_STATUS_SUCCESS;
*clone = cJSON_CreateObject();
if((*clone) == NULL) {
status = SWITCH_STATUS_GENERR;
}
}
return status;
}
static cJSON * kazoo_event_json_value(kazoo_json_field_type type, const char *value) {
cJSON *item = NULL;
switch(type) {
case JSON_STRING:
item = cJSON_CreateString(value);
break;
case JSON_NUMBER:
item = cJSON_CreateNumber(strtod(value, NULL));
break;
case JSON_BOOLEAN:
item = cJSON_CreateBool(switch_true(value));
break;
case JSON_OBJECT:
item = cJSON_CreateObject();
break;
case JSON_RAW:
item = cJSON_CreateRaw(value);
break;
default:
break;
};
return item;
}
static cJSON * kazoo_event_add_json_value(cJSON *dst, kazoo_field_ptr field, const char *as, const char *value) {
cJSON *item = NULL;
if(value || field->out_type == JSON_OBJECT) {
if((item = kazoo_event_json_value(field->out_type, value)) != NULL) {
kazoo_cJSON_AddItemToObject(dst, as, item);
}
}
return item;
}
#define MAX_FIRST_OF 25
char * first_of(switch_event_t *src, char * in)
{
switch_event_header_t *header;
char *y1, *y2, *y3 = NULL;
char *value[MAX_FIRST_OF];
int n, size;
y1 = strdup(in);
if((y2 = (char *) switch_stristr("first-of", y1)) != NULL) {
char tmp[2048] = "";
y3 = switch_find_end_paren((const char *)y2 + 8, '(', ')');
*++y3='\0';
*y2 = '\0';
size = switch_separate_string(y2 + 9, '|', value, MAX_FIRST_OF);
for(n=0; n < size; n++) {
header = switch_event_get_header_ptr(src, value[n]);
if(header) {
switch_snprintf(tmp, sizeof(tmp), "%s%s%s", y1, header->name, y3);
free(y1);
y2 = strdup(tmp);
y3 = first_of(src, y2);
if(y2 == y3) {
return y2;
} else {
return y3;
}
}
}
}
free(y1);
return in;
}
cJSON * kazoo_event_add_field_to_json(cJSON *dst, switch_event_t *src, kazoo_field_ptr field)
{
switch_event_header_t *header;
char *expanded, *firstOf;
uint i, n;
cJSON *item = NULL;
switch(field->in_type) {
case FIELD_COPY:
if((header = switch_event_get_header_ptr(src, field->name)) != NULL) {
if (header->idx) {
item = cJSON_CreateArray();
for(i = 0; i < header->idx; i++) {
cJSON_AddItemToArray(item, kazoo_event_json_value(field->out_type, header->array[i]));
}
kazoo_cJSON_AddItemToObject(dst, field->as ? field->as : field->name, item);
} else {
item = kazoo_event_add_json_value(dst, field, field->as ? field->as : field->name, header->value);
}
}
break;
case FIELD_EXPAND:
firstOf = first_of(src, field->value);
expanded = switch_event_expand_headers(src, firstOf);
if(expanded != NULL && !zstr(expanded)) {
item = kazoo_event_add_json_value(dst, field, field->as ? field->as : field->name, expanded);
}
if(expanded != firstOf) {
free(expanded);
}
if(firstOf != field->value) {
free(firstOf);
}
break;
case FIELD_FIRST_OF:
for(n = 0; n < field->list.size; n++) {
if(*field->list.value[n] == '#') {
item = kazoo_event_add_json_value(dst, field, field->as ? field->as : field->name, ++field->list.value[n]);
break;
} else {
header = switch_event_get_header_ptr(src, field->list.value[n]);
if(header) {
if (header->idx) {
item = cJSON_CreateArray();
for(i = 0; i < header->idx; i++) {
cJSON_AddItemToArray(item, kazoo_event_json_value(field->out_type, header->array[i]));
}
kazoo_cJSON_AddItemToObject(dst, field->as ? field->as : field->name, item);
} else {
item = kazoo_event_add_json_value(dst, field, field->as ? field->as : field->name, header->value);
}
break;
}
}
}
break;
case FIELD_PREFIX:
for (header = src->headers; header; header = header->next) {
if(!strncmp(header->name, field->name, strlen(field->name))) {
if (header->idx) {
cJSON *array = cJSON_CreateArray();
for(i = 0; i < header->idx; i++) {
cJSON_AddItemToArray(array, kazoo_event_json_value(field->out_type, header->array[i]));
}
kazoo_cJSON_AddItemToObject(dst, field->exclude_prefix ? header->name+strlen(field->name) : header->name, array);
} else {
kazoo_event_add_json_value(dst, field, field->exclude_prefix ? header->name+strlen(field->name) : header->name, header->value);
}
}
}
break;
case FIELD_STATIC:
item = kazoo_event_add_json_value(dst, field, field->name, field->value);
break;
case FIELD_GROUP:
item = dst;
break;
default:
break;
}
return item;
}
static switch_status_t kazoo_event_add_fields_to_json(kazoo_logging_ptr logging, cJSON *dst, switch_event_t *src, kazoo_field_ptr field) {
kazoo_filter_ptr filter;
cJSON *item = NULL;
while(field) {
if(field->in_type == FIELD_REFERENCE) {
if(field->ref) {
if((filter = filter_event(src, field->ref->filter)) != NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, logging->levels->filtered_field_log_level, "profile[%s] event %s, referenced field %s filtered by settings %s : %s\n", logging->profile_name, logging->event_name, field->ref->name, filter->name, filter->value);
} else {
kazoo_event_add_fields_to_json(logging, dst, src, field->ref->head);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "profile[%s] event %s, referenced field %s not found\n", logging->profile_name, logging->event_name, field->name);
}
} else {
if((filter = filter_event(src, field->filter)) != NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, logging->levels->filtered_field_log_level, "profile[%s] event %s, field %s filtered by settings %s : %s\n", logging->profile_name, logging->event_name, field->name, filter->name, filter->value);
} else {
item = kazoo_event_add_field_to_json(dst, src, field);
if(field->children && item != NULL) {
if(field->children->verbose && field->out_type == JSON_OBJECT) {
kazoo_event_init_json_fields(src, item);
}
kazoo_event_add_fields_to_json(logging, field->out_type == JSON_OBJECT ? item : dst, src, field->children->head);
}
}
}
field = field->next;
}
return SWITCH_STATUS_SUCCESS;
}
kazoo_message_ptr kazoo_message_create_event(switch_event_t* evt, kazoo_event_ptr event, kazoo_event_profile_ptr profile)
{
kazoo_message_ptr message;
cJSON *JObj = NULL;
kazoo_filter_ptr filtered;
kazoo_logging_t logging;
logging.levels = profile->logging;
logging.event_name = switch_event_get_header_nil(evt, "Event-Name");
logging.profile_name = profile->name;
switch_event_add_header(evt, SWITCH_STACK_BOTTOM, "Switch-Nodename", "%s@%s", "freeswitch", switch_event_get_header(evt, "FreeSWITCH-Hostname"));
message = malloc(sizeof(kazoo_message_t));
if(message == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error allocating memory for serializing event to json\n");
return NULL;
}
memset(message, 0, sizeof(kazoo_message_t));
if(profile->filter) {
// filtering
if((filtered = filter_event(evt, profile->filter)) != NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, logging.levels->filtered_event_log_level, "profile[%s] event %s filtered by profile settings %s : %s\n", logging.profile_name, logging.event_name, filtered->name, filtered->value);
kazoo_message_destroy(&message);
return NULL;
}
}
if(event && event->filter) {
if((filtered = filter_event(evt, event->filter)) != NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, logging.levels->filtered_event_log_level, "profile[%s] event %s filtered by event settings %s : %s\n", logging.profile_name, logging.event_name, filtered->name, filtered->value);
kazoo_message_destroy(&message);
return NULL;
}
}
kazoo_event_init_json(profile->fields, event ? event->fields : NULL, evt, &JObj);
if(profile->fields)
kazoo_event_add_fields_to_json(&logging, JObj, evt, profile->fields->head);
if(event && event->fields)
kazoo_event_add_fields_to_json(&logging, JObj, evt, event->fields->head);
message->JObj = JObj;
return message;
}
kazoo_message_ptr kazoo_message_create_fetch(switch_event_t* evt, kazoo_fetch_profile_ptr profile)
{
kazoo_message_ptr message;
cJSON *JObj = NULL;
kazoo_logging_t logging;
logging.levels = profile->logging;
logging.event_name = switch_event_get_header_nil(evt, "Event-Name");
logging.profile_name = profile->name;
switch_event_add_header(evt, SWITCH_STACK_BOTTOM, "Switch-Nodename", "%s@%s", "freeswitch", switch_event_get_header(evt, "FreeSWITCH-Hostname"));
message = malloc(sizeof(kazoo_message_t));
if(message == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error allocating memory for serializing event to json\n");
return NULL;
}
memset(message, 0, sizeof(kazoo_message_t));
kazoo_event_init_json(profile->fields, NULL, evt, &JObj);
if(profile->fields)
kazoo_event_add_fields_to_json(&logging, JObj, evt, profile->fields->head);
message->JObj = JObj;
return message;
}
void kazoo_message_destroy(kazoo_message_ptr *msg)
{
if (!msg || !*msg) return;
if((*msg)->JObj != NULL)
cJSON_Delete((*msg)->JObj);
switch_safe_free(*msg);
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4
*/

View File

@ -0,0 +1,65 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Based on mod_skel by
* Anthony Minessale II <anthm@freeswitch.org>
*
* Contributor(s):
*
* Daniel Bryars <danb@aeriandi.com>
* Tim Brown <tim.brown@aeriandi.com>
* Anthony Minessale II <anthm@freeswitch.org>
* William King <william.king@quentustech.com>
* Mike Jerris <mike@jerris.com>
*
* kazoo.c -- Sends FreeSWITCH events to an AMQP broker
*
*/
#ifndef KAZOO_MESSAGE_H
#define KAZOO_MESSAGE_H
#include <switch.h>
typedef struct {
uint64_t timestamp;
uint64_t start;
uint64_t filter;
uint64_t serialize;
uint64_t print;
uint64_t rk;
} kazoo_message_times_t, *kazoo_message_times_ptr;
typedef struct {
cJSON *JObj;
} kazoo_message_t, *kazoo_message_ptr;
kazoo_message_ptr kazoo_message_create_event(switch_event_t* evt, kazoo_event_ptr event, kazoo_event_profile_ptr profile);
kazoo_message_ptr kazoo_message_create_fetch(switch_event_t* evt, kazoo_fetch_profile_ptr profile);
void kazoo_message_destroy(kazoo_message_ptr *msg);
#endif /* KAZOO_MESSAGE_H */

View File

@ -53,6 +53,8 @@ static char *REQUEST_ATOMS[] = {
"nixevent",
"sendevent",
"sendmsg",
"commands",
"command",
"bind",
"getpid",
"version",
@ -62,7 +64,8 @@ static char *REQUEST_ATOMS[] = {
"fetch_reply",
"config",
"bgapi4",
"api4"
"api4",
"no_legacy"
};
typedef enum {
@ -72,6 +75,8 @@ typedef enum {
REQUEST_NIXEVENT,
REQUEST_SENDEVENT,
REQUEST_SENDMSG,
REQUEST_COMMANDS,
REQUEST_COMMAND,
REQUEST_BIND,
REQUEST_GETPID,
REQUEST_VERSION,
@ -82,11 +87,13 @@ typedef enum {
REQUEST_CONFIG,
REQUEST_BGAPI4,
REQUEST_API4,
REQUEST_NO_LEGACY,
REQUEST_MAX
} request_atoms_t;
static switch_status_t find_request(char *atom, int *request) {
for (int i = 0; i < REQUEST_MAX; i++) {
int i;
for (i = 0; i < REQUEST_MAX; i++) {
if(!strncmp(atom, REQUEST_ATOMS[i], MAXATOMLEN)) {
*request = i;
return SWITCH_STATUS_SUCCESS;
@ -267,7 +274,7 @@ static switch_status_t api_exec_stream(char *cmd, char *arg, switch_stream_handl
}
} else if (!stream->data || !strlen(stream->data)) {
*reply = switch_mprintf("%s: Command returned no output", cmd);
status = SWITCH_STATUS_FALSE;
status = SWITCH_STATUS_SUCCESS;
} else {
*reply = strdup(stream->data);
status = SWITCH_STATUS_SUCCESS;
@ -412,11 +419,10 @@ static void log_sendmsg_request(char *uuid, switch_event_t *event)
switch_ssize_t hlen = -1;
unsigned long CMD_EXECUTE = switch_hashfunc_default("execute", &hlen);
unsigned long CMD_XFEREXT = switch_hashfunc_default("xferext", &hlen);
// unsigned long CMD_HANGUP = switch_hashfunc_default("hangup", &hlen);
// unsigned long CMD_NOMEDIA = switch_hashfunc_default("nomedia", &hlen);
// unsigned long CMD_UNICAST = switch_hashfunc_default("unicast", &hlen);
if (zstr(cmd)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "log|%s|invalid \n", uuid);
DUMP_EVENT(event);
return;
}
@ -453,6 +459,7 @@ static void log_sendmsg_request(char *uuid, switch_event_t *event)
static switch_status_t build_event(switch_event_t *event, ei_x_buff * buf) {
int propslist_length, arity;
int n=0;
if(!event) {
return SWITCH_STATUS_FALSE;
@ -462,7 +469,7 @@ static switch_status_t build_event(switch_event_t *event, ei_x_buff * buf) {
return SWITCH_STATUS_FALSE;
}
while (!ei_decode_tuple_header(buf->buff, &buf->index, &arity)) {
while (!ei_decode_tuple_header(buf->buff, &buf->index, &arity) && n < propslist_length) {
char key[1024];
char *value;
@ -482,9 +489,21 @@ static switch_status_t build_event(switch_event_t *event, ei_x_buff * buf) {
switch_safe_free(event->body);
event->body = value;
} else {
if(!strcasecmp(key, "Call-ID")) {
switch_core_session_t *session = NULL;
if(!zstr(value)) {
if ((session = switch_core_session_force_locate(value)) != NULL) {
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_channel_event_set_data(channel, event);
switch_core_session_rwunlock(session);
}
}
}
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, key, value);
}
n++;
}
ei_skip_term(buf->buff, &buf->index);
return SWITCH_STATUS_SUCCESS;
}
@ -525,6 +544,16 @@ static switch_status_t erlang_response_ok(ei_x_buff *rbuf) {
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t erlang_response_ok_uuid(ei_x_buff *rbuf, const char * uuid) {
if (rbuf) {
ei_x_encode_tuple_header(rbuf, 2);
ei_x_encode_atom(rbuf, "ok");
ei_x_encode_binary(rbuf, uuid, strlen(uuid));
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t handle_request_noevents(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
ei_event_stream_t *event_stream;
@ -554,6 +583,7 @@ static switch_status_t handle_request_nixevent(ei_node_t *ei_node, erlang_pid *p
switch_event_types_t event_type;
ei_event_stream_t *event_stream;
int custom = 0, length = 0;
int i;
if (ei_decode_list_header(buf->buff, &buf->index, &length)
|| length == 0) {
@ -566,7 +596,7 @@ static switch_status_t handle_request_nixevent(ei_node_t *ei_node, erlang_pid *p
return erlang_response_ok(rbuf);
}
for (int i = 1; i <= length; i++) {
for (i = 1; i <= length; i++) {
if (ei_decode_atom_safe(buf->buff, &buf->index, event_name)) {
switch_mutex_unlock(ei_node->event_streams_mutex);
return erlang_response_badarg(rbuf);
@ -576,16 +606,22 @@ static switch_status_t handle_request_nixevent(ei_node_t *ei_node, erlang_pid *p
remove_event_binding(event_stream, SWITCH_EVENT_CUSTOM, event_name);
} else if (switch_name_event(event_name, &event_type) == SWITCH_STATUS_SUCCESS) {
switch (event_type) {
case SWITCH_EVENT_CUSTOM:
custom++;
break;
case SWITCH_EVENT_ALL:
for (switch_event_types_t type = 0; type < SWITCH_EVENT_ALL; type++) {
{
switch_event_types_t type;
for (type = 0; type < SWITCH_EVENT_ALL; type++) {
if(type != SWITCH_EVENT_CUSTOM) {
remove_event_binding(event_stream, type, NULL);
}
}
break;
}
default:
remove_event_binding(event_stream, event_type, NULL);
}
@ -632,6 +668,82 @@ static switch_status_t handle_request_sendevent(ei_node_t *ei_node, erlang_pid *
return erlang_response_badarg(rbuf);
}
static switch_status_t handle_request_command(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
switch_core_session_t *session;
switch_event_t *event = NULL;
char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
switch_uuid_t cmd_uuid;
char cmd_uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
if (ei_decode_string_or_binary_limited(buf->buff, &buf->index, sizeof(uuid_str), uuid_str)) {
return erlang_response_badarg(rbuf);
}
switch_uuid_get(&cmd_uuid);
switch_uuid_format(cmd_uuid_str, &cmd_uuid);
switch_event_create(&event, SWITCH_EVENT_COMMAND);
if (build_event(event, buf) != SWITCH_STATUS_SUCCESS) {
return erlang_response_badarg(rbuf);
}
log_sendmsg_request(uuid_str, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event-uuid", cmd_uuid_str);
if (zstr_buf(uuid_str) || !(session = switch_core_session_locate(uuid_str))) {
return erlang_response_baduuid(rbuf);
}
switch_core_session_queue_private_event(session, &event, SWITCH_FALSE);
switch_core_session_rwunlock(session);
return erlang_response_ok_uuid(rbuf, cmd_uuid_str);
}
static switch_status_t handle_request_commands(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
switch_core_session_t *session;
char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
int propslist_length, n;
switch_uuid_t cmd_uuid;
char cmd_uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
if (ei_decode_string_or_binary_limited(buf->buff, &buf->index, sizeof(uuid_str), uuid_str)) {
return erlang_response_badarg(rbuf);
}
if (zstr_buf(uuid_str) || !(session = switch_core_session_locate(uuid_str))) {
return erlang_response_baduuid(rbuf);
}
switch_uuid_get(&cmd_uuid);
switch_uuid_format(cmd_uuid_str, &cmd_uuid);
if (ei_decode_list_header(buf->buff, &buf->index, &propslist_length)) {
switch_core_session_rwunlock(session);
return SWITCH_STATUS_FALSE;
}
for(n = 0; n < propslist_length; n++) {
switch_event_t *event = NULL;
switch_event_create(&event, SWITCH_EVENT_COMMAND);
if (build_event(event, buf) != SWITCH_STATUS_SUCCESS) {
switch_core_session_rwunlock(session);
return erlang_response_badarg(rbuf);
}
log_sendmsg_request(uuid_str, event);
if(n == (propslist_length - 1)) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event-uuid", cmd_uuid_str);
} else {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event-uuid", "null");
}
switch_core_session_queue_private_event(session, &event, SWITCH_FALSE);
}
switch_core_session_rwunlock(session);
return erlang_response_ok_uuid(rbuf, cmd_uuid_str);
}
static switch_status_t handle_request_sendmsg(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
switch_core_session_t *session;
switch_event_t *event = NULL;
@ -659,8 +771,8 @@ static switch_status_t handle_request_sendmsg(ei_node_t *ei_node, erlang_pid *pi
static switch_status_t handle_request_config(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
fetch_config_filters();
return erlang_response_ok(rbuf);
fetch_config();
return erlang_response_ok(rbuf);
}
static switch_status_t handle_request_bind(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
@ -675,8 +787,8 @@ static switch_status_t handle_request_bind(ei_node_t *ei_node, erlang_pid *pid,
switch(section) {
case SWITCH_XML_SECTION_CONFIG:
add_fetch_handler(ei_node, pid, kazoo_globals.config_fetch_binding);
if(!kazoo_globals.config_filters_fetched)
fetch_config_filters();
if(!kazoo_globals.config_fetched)
fetch_config();
break;
case SWITCH_XML_SECTION_DIRECTORY:
add_fetch_handler(ei_node, pid, kazoo_globals.directory_fetch_binding);
@ -684,12 +796,15 @@ static switch_status_t handle_request_bind(ei_node_t *ei_node, erlang_pid *pid,
case SWITCH_XML_SECTION_DIALPLAN:
add_fetch_handler(ei_node, pid, kazoo_globals.dialplan_fetch_binding);
break;
case SWITCH_XML_SECTION_CHATPLAN:
add_fetch_handler(ei_node, pid, kazoo_globals.chatplan_fetch_binding);
break;
case SWITCH_XML_SECTION_CHANNELS:
add_fetch_handler(ei_node, pid, kazoo_globals.channels_fetch_binding);
break;
case SWITCH_XML_SECTION_LANGUAGES:
add_fetch_handler(ei_node, pid, kazoo_globals.languages_fetch_binding);
break;
case SWITCH_XML_SECTION_CHATPLAN:
add_fetch_handler(ei_node, pid, kazoo_globals.chatplan_fetch_binding);
break;
default:
return erlang_response_badarg(rbuf);
}
@ -856,11 +971,19 @@ static switch_status_t handle_request_api(ei_node_t *ei_node, erlang_pid *pid, e
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t handle_request_no_legacy(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "nolegacy: %s\n", ei_node->peer_nodename);
ei_node->legacy = SWITCH_FALSE;
return erlang_response_ok(rbuf);
}
static switch_status_t handle_request_event(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
char event_name[MAXATOMLEN + 1];
switch_event_types_t event_type;
ei_event_stream_t *event_stream;
int custom = 0, length = 0;
int length = 0;
int i;
if (ei_decode_list_header(buf->buff, &buf->index, &length) || !length) {
return erlang_response_badarg(rbuf);
@ -868,38 +991,18 @@ static switch_status_t handle_request_event(ei_node_t *ei_node, erlang_pid *pid,
switch_mutex_lock(ei_node->event_streams_mutex);
if (!(event_stream = find_event_stream(ei_node->event_streams, pid))) {
event_stream = new_event_stream(&ei_node->event_streams, pid);
event_stream = new_event_stream(ei_node, pid);
/* ensure we are notified if the requesting processes dies so we can clean up */
ei_link(ei_node, ei_self(&kazoo_globals.ei_cnode), pid);
}
for (int i = 1; i <= length; i++) {
for (i = 1; i <= length; i++) {
if (ei_decode_atom_safe(buf->buff, &buf->index, event_name)) {
switch_mutex_unlock(ei_node->event_streams_mutex);
return erlang_response_badarg(rbuf);
}
if (custom) {
add_event_binding(event_stream, SWITCH_EVENT_CUSTOM, event_name);
} else if (switch_name_event(event_name, &event_type) == SWITCH_STATUS_SUCCESS) {
switch (event_type) {
case SWITCH_EVENT_CUSTOM:
custom++;
break;
case SWITCH_EVENT_ALL:
for (switch_event_types_t type = 0; type < SWITCH_EVENT_ALL; type++) {
if(type != SWITCH_EVENT_CUSTOM) {
add_event_binding(event_stream, type, NULL);
}
}
break;
default:
add_event_binding(event_stream, event_type, NULL);
}
} else {
switch_mutex_unlock(ei_node->event_streams_mutex);
return erlang_response_badarg(rbuf);
}
add_event_binding(event_stream, event_name);
}
switch_mutex_unlock(ei_node->event_streams_mutex);
@ -955,21 +1058,24 @@ static switch_status_t handle_request_fetch_reply(ei_node_t *ei_node, erlang_pid
case SWITCH_XML_SECTION_DIALPLAN:
result = fetch_reply(uuid_str, xml_str, kazoo_globals.dialplan_fetch_binding);
break;
case SWITCH_XML_SECTION_CHATPLAN:
result = fetch_reply(uuid_str, xml_str, kazoo_globals.chatplan_fetch_binding);
break;
case SWITCH_XML_SECTION_CHANNELS:
result = fetch_reply(uuid_str, xml_str, kazoo_globals.channels_fetch_binding);
break;
case SWITCH_XML_SECTION_LANGUAGES:
result = fetch_reply(uuid_str, xml_str, kazoo_globals.languages_fetch_binding);
break;
case SWITCH_XML_SECTION_CHATPLAN:
result = fetch_reply(uuid_str, xml_str, kazoo_globals.chatplan_fetch_binding);
break;
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved fetch reply for an unknown configuration section: %s\n", section_str);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received fetch reply for an unknown configuration section: %s\n", section_str);
return erlang_response_badarg(rbuf);
}
if (result == SWITCH_STATUS_SUCCESS) {
return erlang_response_ok(rbuf);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved fetch reply for an unknown/expired UUID: %s\n", uuid_str);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received fetch reply for an unknown/expired UUID: %s\n", uuid_str);
return erlang_response_baduuid(rbuf);
}
}
@ -1010,6 +1116,10 @@ static switch_status_t handle_kazoo_request(ei_node_t *ei_node, erlang_pid *pid,
return handle_request_sendevent(ei_node, pid, buf, rbuf);
case REQUEST_SENDMSG:
return handle_request_sendmsg(ei_node, pid, buf, rbuf);
case REQUEST_COMMAND:
return handle_request_command(ei_node, pid, buf, rbuf);
case REQUEST_COMMANDS:
return handle_request_commands(ei_node, pid, buf, rbuf);
case REQUEST_BIND:
return handle_request_bind(ei_node, pid, buf, rbuf);
case REQUEST_GETPID:
@ -1024,12 +1134,14 @@ static switch_status_t handle_kazoo_request(ei_node_t *ei_node, erlang_pid *pid,
return handle_request_event(ei_node, pid, buf, rbuf);
case REQUEST_FETCH_REPLY:
return handle_request_fetch_reply(ei_node, pid, buf, rbuf);
case REQUEST_CONFIG:
return handle_request_config(ei_node, pid, buf, rbuf);
case REQUEST_BGAPI4:
return handle_request_bgapi4(ei_node, pid, buf, rbuf);
case REQUEST_CONFIG:
return handle_request_config(ei_node, pid, buf, rbuf);
case REQUEST_BGAPI4:
return handle_request_bgapi4(ei_node, pid, buf, rbuf);
case REQUEST_API4:
return handle_request_api4(ei_node, pid, buf, rbuf);
case REQUEST_NO_LEGACY:
return handle_request_no_legacy(ei_node, pid, buf, rbuf);
default:
return erlang_response_notimplemented(rbuf);
}
@ -1214,7 +1326,7 @@ static switch_status_t handle_erl_send(ei_node_t *ei_node, erlang_msg *msg, ei_x
} else if (!strncmp(msg->toname, "mod_kazoo", MAXATOMLEN)) {
return handle_mod_kazoo_request(ei_node, msg, buf);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved erlang message to unknown process \"%s\" (ensure you are using Kazoo v2.14+).\n", msg->toname);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received erlang message to unknown process \"%s\" (ensure you are using Kazoo v2.14+).\n", msg->toname);
return SWITCH_STATUS_GENERR;
}
}
@ -1262,7 +1374,7 @@ static void *SWITCH_THREAD_FUNC receive_handler(switch_thread_t *thread, void *o
while (switch_test_flag(ei_node, LFLAG_RUNNING) && switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) {
void *pop;
if (switch_queue_pop_timeout(ei_node->received_msgs, &pop, 500000) == SWITCH_STATUS_SUCCESS) {
if (switch_queue_pop_timeout(ei_node->received_msgs, &pop, 100000) == SWITCH_STATUS_SUCCESS) {
ei_received_msg_t *received_msg = (ei_received_msg_t *) pop;
handle_erl_msg(ei_node, &received_msg->msg, &received_msg->buf);
ei_x_free(&received_msg->buf);
@ -1313,7 +1425,7 @@ static void *SWITCH_THREAD_FUNC handle_node(switch_thread_t *thread, void *obj)
}
while (++send_msg_count <= kazoo_globals.send_msg_batch
&& switch_queue_trypop(ei_node->send_msgs, &pop) == SWITCH_STATUS_SUCCESS) {
&& switch_queue_pop_timeout(ei_node->send_msgs, &pop, 20000) == SWITCH_STATUS_SUCCESS) {
ei_send_msg_t *send_msg = (ei_send_msg_t *) pop;
ei_helper_send(ei_node, &send_msg->pid, &send_msg->buf);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sent erlang message to %s <%d.%d.%d>\n"
@ -1431,6 +1543,7 @@ switch_status_t new_kazoo_node(int nodefd, ErlConnect *conn) {
ei_node->nodefd = nodefd;
ei_node->peer_nodename = switch_core_strdup(ei_node->pool, conn->nodename);
ei_node->created_time = switch_micro_time_now();
ei_node->legacy = kazoo_globals.enable_legacy;
/* store the IP and node name we are talking with */
switch_os_sock_put(&ei_node->socket, (switch_os_socket_t *)&nodefd, pool);

View File

@ -0,0 +1,502 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Luis Azedo <luis@2600hz.com>
*
* mod_hacks.c -- hacks with state handlers
*
*/
#include "mod_kazoo.h"
static const char *bridge_variables[] = {
"Call-Control-Queue",
"Call-Control-PID",
"ecallmgr_Call-Interaction-ID",
"ecallmgr_Ecallmgr-Node",
NULL
};
static switch_status_t kz_tweaks_signal_bridge_on_hangup(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_event_t *my_event;
const char *peer_uuid = switch_channel_get_variable(channel, "Bridge-B-Unique-ID");
if (switch_event_create(&my_event, SWITCH_EVENT_CHANNEL_UNBRIDGE) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(my_event, SWITCH_STACK_BOTTOM, "Bridge-A-Unique-ID", switch_core_session_get_uuid(session));
switch_event_add_header_string(my_event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", peer_uuid);
switch_channel_event_set_data(channel, my_event);
switch_event_fire(&my_event);
}
return SWITCH_STATUS_SUCCESS;
}
static const switch_state_handler_table_t kz_tweaks_signal_bridge_state_handlers = {
/*.on_init */ NULL,
/*.on_routing */ NULL,
/*.on_execute */ NULL,
/*.on_hangup */ kz_tweaks_signal_bridge_on_hangup,
/*.on_exchange_media */ NULL,
/*.on_soft_execute */ NULL,
/*.on_consume_media */ NULL,
/*.on_hibernate */ NULL
};
static void kz_tweaks_handle_bridge_variables(switch_event_t *event)
{
switch_core_session_t *a_session = NULL, *b_session = NULL;
const char *a_leg = switch_event_get_header(event, "Bridge-A-Unique-ID");
const char *b_leg = switch_event_get_header(event, "Bridge-B-Unique-ID");
int i;
if (a_leg && (a_session = switch_core_session_force_locate(a_leg)) != NULL) {
switch_channel_t *a_channel = switch_core_session_get_channel(a_session);
if(switch_channel_get_variable_dup(a_channel, bridge_variables[0], SWITCH_FALSE, -1) == NULL) {
if(b_leg && (b_session = switch_core_session_force_locate(b_leg)) != NULL) {
switch_channel_t *b_channel = switch_core_session_get_channel(b_session);
for(i = 0; bridge_variables[i] != NULL; i++) {
const char *val = switch_channel_get_variable_dup(b_channel, bridge_variables[i], SWITCH_TRUE, -1);
switch_channel_set_variable(a_channel, bridge_variables[i], val);
switch_safe_strdup(val);
}
switch_core_session_rwunlock(b_session);
}
} else {
if(b_leg && (b_session = switch_core_session_force_locate(b_leg)) != NULL) {
switch_channel_t *b_channel = switch_core_session_get_channel(b_session);
if(switch_channel_get_variable_dup(b_channel, bridge_variables[0], SWITCH_FALSE, -1) == NULL) {
for(i = 0; bridge_variables[i] != NULL; i++) {
const char *val = switch_channel_get_variable_dup(b_channel, bridge_variables[i], SWITCH_TRUE, -1);
switch_channel_set_variable(b_channel, bridge_variables[i], val);
switch_safe_strdup(val);
}
}
switch_core_session_rwunlock(b_session);
}
}
switch_core_session_rwunlock(a_session);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "NOT FOUND : %s\n", a_leg);
}
}
static void kz_tweaks_handle_bridge_replaces(switch_event_t *event)
{
switch_event_t *my_event;
const char *replaced_call_id = switch_event_get_header(event, "variable_sip_replaces_call_id");
const char *a_leg_call_id = switch_event_get_header(event, "variable_sip_replaces_a-leg");
const char *peer_uuid = switch_event_get_header(event, "Unique-ID");
int processed = 0;
if(a_leg_call_id && replaced_call_id) {
const char *call_id = switch_event_get_header(event, "Bridge-B-Unique-ID");
switch_core_session_t *session = NULL;
if ((session = switch_core_session_force_locate(peer_uuid)) != NULL) {
switch_channel_t *channel = switch_core_session_get_channel(session);
processed = switch_true(switch_channel_get_variable_dup(channel, "Bridge-Event-Processed", SWITCH_FALSE, -1));
switch_channel_set_variable(channel, "Bridge-Event-Processed", "true");
switch_core_session_rwunlock(session);
}
if ((processed) && call_id && (session = switch_core_session_force_locate(call_id)) != NULL) {
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_channel_set_variable(channel, "Bridge-Event-Processed", "true");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "creating channel_bridge event A - %s , B - %s\n", switch_core_session_get_uuid(session), peer_uuid);
if (switch_event_create(&my_event, SWITCH_EVENT_CHANNEL_BRIDGE) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(my_event, SWITCH_STACK_BOTTOM, "Bridge-A-Unique-ID", switch_core_session_get_uuid(session));
switch_event_add_header_string(my_event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", peer_uuid);
switch_channel_event_set_data(channel, my_event);
switch_event_fire(&my_event);
}
switch_channel_set_variable(channel, "Bridge-B-Unique-ID", peer_uuid);
switch_channel_add_state_handler(channel, &kz_tweaks_signal_bridge_state_handlers);
switch_core_session_rwunlock(session);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "NOT FOUND : %s\n", call_id);
}
}
}
static void kz_tweaks_channel_bridge_event_handler(switch_event_t *event)
{
kz_tweaks_handle_bridge_replaces(event);
kz_tweaks_handle_bridge_variables(event);
}
// TRANSFERS
static void kz_tweaks_channel_replaced_event_handler(switch_event_t *event)
{
const char *uuid = switch_event_get_header(event, "Unique-ID");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "REPLACED : %s\n", uuid);
}
static void kz_tweaks_channel_intercepted_event_handler(switch_event_t *event)
{
const char *uuid = switch_event_get_header(event, "Unique-ID");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "INTERCEPTED : %s\n", uuid);
}
static void kz_tweaks_channel_transferor_event_handler(switch_event_t *event)
{
switch_core_session_t *uuid_session = NULL;
const char *uuid = switch_event_get_header(event, "Unique-ID");
const char *call_id = switch_event_get_header(event, "att_xfer_destination_peer_uuid");
const char *peer_uuid = switch_event_get_header(event, "att_xfer_destination_call_id");
const char *file = switch_event_get_header(event, "Event-Calling-File");
const char *func = switch_event_get_header(event, "Event-Calling-Function");
const char *line = switch_event_get_header(event, "Event-Calling-Line-Number");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TRANSFEROR : %s , %s , %s, %s , %s , %s \n", uuid, call_id, peer_uuid, file, func, line);
if ((uuid_session = switch_core_session_force_locate(uuid)) != NULL) {
switch_channel_t *uuid_channel = switch_core_session_get_channel(uuid_session);
const char* interaction_id = switch_channel_get_variable_dup(uuid_channel, "ecallmgr_Call-Interaction-ID", SWITCH_TRUE, -1);
// set to uuid & peer_uuid
if(interaction_id != NULL) {
switch_core_session_t *session = NULL;
if(call_id && (session = switch_core_session_force_locate(call_id)) != NULL) {
switch_channel_t *channel = switch_core_session_get_channel(session);
const char* prv_interaction_id = switch_channel_get_variable_dup(channel, "ecallmgr_Call-Interaction-ID", SWITCH_TRUE, -1);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "LOCATING UUID PRV : %s : %s\n", prv_interaction_id, interaction_id);
switch_channel_set_variable(channel, "ecallmgr_Call-Interaction-ID", interaction_id);
switch_core_session_rwunlock(session);
switch_safe_strdup(prv_interaction_id);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TRANSFEROR NO UUID SESSION: %s , %s , %s \n", uuid, call_id, peer_uuid);
}
if(peer_uuid && (session = switch_core_session_force_locate(peer_uuid)) != NULL) {
switch_channel_t *channel = switch_core_session_get_channel(session);
const char* prv_interaction_id = switch_channel_get_variable_dup(channel, "ecallmgr_Call-Interaction-ID", SWITCH_TRUE, -1);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "LOCATING PEER UUID PRV : %s : %s\n", prv_interaction_id, interaction_id);
switch_channel_set_variable(channel, "ecallmgr_Call-Interaction-ID", interaction_id);
switch_core_session_rwunlock(session);
switch_safe_strdup(prv_interaction_id);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TRANSFEROR NO PEER SESSION: %s , %s , %s \n", uuid, call_id, peer_uuid);
}
switch_safe_strdup(interaction_id);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TRANSFEROR ID = NULL : %s , %s , %s \n", uuid, call_id, peer_uuid);
}
switch_core_session_rwunlock(uuid_session);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SESSION NOT FOUND : %s\n", call_id);
}
}
static void kz_tweaks_channel_transferee_event_handler(switch_event_t *event)
{
const char *uuid = switch_event_get_header(event, "Unique-ID");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TRANSFEREE : %s\n", uuid);
}
// END TRANSFERS
static switch_status_t kz_tweaks_handle_loopback(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
const char * loopback_leg = NULL;
const char * loopback_aleg = NULL;
switch_event_t *event = NULL;
switch_event_header_t *header = NULL;
switch_event_t *to_add = NULL;
switch_event_t *to_remove = NULL;
switch_caller_profile_t *caller;
int n = 0;
caller = switch_channel_get_caller_profile(channel);
if(strncmp(caller->source, "mod_loopback", 12))
return SWITCH_STATUS_SUCCESS;
if((loopback_leg = switch_channel_get_variable(channel, "loopback_leg")) == NULL)
return SWITCH_STATUS_SUCCESS;
if(strncmp(loopback_leg, "B", 1))
return SWITCH_STATUS_SUCCESS;
switch_channel_get_variables(channel, &event);
switch_event_create_plain(&to_add, SWITCH_EVENT_CHANNEL_DATA);
switch_event_create_plain(&to_remove, SWITCH_EVENT_CHANNEL_DATA);
for(header = event->headers; header; header = header->next) {
if(!strncmp(header->name, "Export-Loopback-", 16)) {
switch_event_add_variable_name_printf(to_add, SWITCH_STACK_BOTTOM, header->value, "%s", header->name+16);
switch_channel_set_variable(channel, header->name, NULL);
n++;
} else if(!strncmp(header->name, "ecallmgr_", 9)) {
switch_event_add_header_string(to_remove, SWITCH_STACK_BOTTOM, header->name, header->value);
}
}
if(n) {
for(header = to_remove->headers; header; header = header->next) {
switch_channel_set_variable(channel, header->name, NULL);
}
for(header = to_add->headers; header; header = header->next) {
switch_channel_set_variable(channel, header->name, header->value);
}
// cleanup leg A
loopback_aleg = switch_channel_get_variable(channel, "other_loopback_leg_uuid");
if(loopback_aleg != NULL) {
switch_core_session_t *a_session = NULL;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "found loopback a-leg uuid - %s\n", loopback_aleg);
if ((a_session = switch_core_session_locate(loopback_aleg))) {
switch_channel_t *a_channel = switch_core_session_get_channel(a_session);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "found loopback session a - %s\n", loopback_aleg);
switch_channel_del_variable_prefix(a_channel, "Export-Loopback-");
switch_core_session_rwunlock(a_session);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Couldn't locate loopback session a - %s\n", loopback_aleg);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Couldn't find loopback a-leg uuid!\n");
}
}
switch_event_destroy(&event);
switch_event_destroy(&to_add);
switch_event_destroy(&to_remove);
return SWITCH_STATUS_SUCCESS;
}
static void kz_tweaks_handle_caller_id(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
const char *acl_token = switch_channel_get_variable(channel, "acl_token");
if(acl_token) {
switch_ivr_set_user(session, acl_token);
}
}
static switch_status_t kz_tweaks_handle_auth_token(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_event_t *event;
const char *token = switch_channel_get_variable(channel, "sip_h_X-FS-Auth-Token");
if(token) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Authenticating user for nightmare xfer %s\n", token);
if (switch_ivr_set_user(session, token) == SWITCH_STATUS_SUCCESS) {
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Authenticated user from nightmare xfer %s\n", token);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Error Authenticating user for nightmare xfer %s\n", token);
}
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t kz_tweaks_handle_nightmare_xfer(switch_core_session_t *session)
{
switch_core_session_t *replace_session = NULL;
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_event_t *event;
const char *replaced_call_id = switch_channel_get_variable(channel, "sip_replaces_call_id");
const char *core_uuid = switch_channel_get_variable(channel, "sip_h_X-FS-From-Core-UUID");
const char *partner_uuid = switch_channel_get_variable(channel, "sip_h_X-FS-Refer-Partner-UUID");
const char *interaction_id = switch_channel_get_variable(channel, "sip_h_X-FS-Call-Interaction-ID");
if(core_uuid && partner_uuid && replaced_call_id && interaction_id) {
switch_channel_set_variable(channel, "ecallmgr_Call-Interaction-ID", interaction_id);
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "checking nightmare xfer tweak for %s\n", switch_channel_get_uuid(channel));
if ((replace_session = switch_core_session_locate(replaced_call_id))) {
switch_channel_t *replaced_call_channel = switch_core_session_get_channel(replace_session);
switch_channel_set_variable(replaced_call_channel, "ecallmgr_Call-Interaction-ID", interaction_id);
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(replaced_call_channel, event);
switch_event_fire(&event);
}
switch_core_session_rwunlock(replace_session);
}
if ((replace_session = switch_core_session_locate(partner_uuid))) {
switch_channel_t *replaced_call_channel = switch_core_session_get_channel(replace_session);
switch_channel_set_variable(replaced_call_channel, "ecallmgr_Call-Interaction-ID", interaction_id);
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(replaced_call_channel, event);
switch_event_fire(&event);
}
switch_core_session_rwunlock(replace_session);
}
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t kz_tweaks_handle_replaces_id(switch_core_session_t *session)
{
switch_core_session_t *replace_call_session = NULL;
switch_event_t *event;
switch_channel_t *channel = switch_core_session_get_channel(session);
const char *replaced_call_id = switch_channel_get_variable(channel, "sip_replaces_call_id");
const char *core_uuid = switch_channel_get_variable(channel, "sip_h_X-FS-From-Core-UUID");
if((!core_uuid) && replaced_call_id) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "checking replaces header tweak for %s\n", replaced_call_id);
if ((replace_call_session = switch_core_session_locate(replaced_call_id))) {
switch_channel_t *replaced_call_channel = switch_core_session_get_channel(replace_call_session);
int i;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "setting bridge variables from %s to %s\n", replaced_call_id, switch_channel_get_uuid(channel));
for(i = 0; bridge_variables[i] != NULL; i++) {
const char *val = switch_channel_get_variable_dup(replaced_call_channel, bridge_variables[i], SWITCH_TRUE, -1);
switch_channel_set_variable(channel, bridge_variables[i], val);
switch_safe_strdup(val);
}
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
switch_core_session_rwunlock(replace_call_session);
}
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t kz_tweaks_handle_switch_uri(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
const char *profile_url = switch_channel_get_variable(channel, "sofia_profile_url");
if(profile_url) {
int n = strcspn(profile_url, "@");
switch_channel_set_variable(channel, "Switch-URL", profile_url);
switch_channel_set_variable_printf(channel, "Switch-URI", "sip:%s", n > 0 ? profile_url + n + 1 : profile_url);
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t kz_tweaks_on_init(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "checking tweaks for %s\n", switch_channel_get_uuid(channel));
kz_tweaks_handle_switch_uri(session);
kz_tweaks_handle_caller_id(session);
kz_tweaks_handle_auth_token(session);
kz_tweaks_handle_nightmare_xfer(session);
kz_tweaks_handle_replaces_id(session);
kz_tweaks_handle_loopback(session);
return SWITCH_STATUS_SUCCESS;
}
static switch_state_handler_table_t kz_tweaks_state_handlers = {
/*.on_init */ kz_tweaks_on_init,
/*.on_routing */ NULL,
/*.on_execute */ NULL,
/*.on_hangup */ NULL,
/*.on_exchange_media */ NULL,
/*.on_soft_execute */ NULL,
/*.on_consume_media */ NULL,
/*.on_hibernate */ NULL,
/*.on_reset */ NULL,
/*.on_park */ NULL,
/*.on_reporting */ NULL
};
static void kz_tweaks_register_state_handlers()
{
switch_core_add_state_handler(&kz_tweaks_state_handlers);
}
static void kz_tweaks_unregister_state_handlers()
{
switch_core_remove_state_handler(&kz_tweaks_state_handlers);
}
static void kz_tweaks_bind_events()
{
if (switch_event_bind("kz_tweaks", SWITCH_EVENT_CHANNEL_BRIDGE, SWITCH_EVENT_SUBCLASS_ANY, kz_tweaks_channel_bridge_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind to channel_bridge event!\n");
}
if (switch_event_bind("kz_tweaks", SWITCH_EVENT_CUSTOM, "sofia::replaced", kz_tweaks_channel_replaced_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind to channel_bridge event!\n");
}
if (switch_event_bind("kz_tweaks", SWITCH_EVENT_CUSTOM, "sofia::intercepted", kz_tweaks_channel_intercepted_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind to channel_bridge event!\n");
}
if (switch_event_bind("kz_tweaks", SWITCH_EVENT_CUSTOM, "sofia::transferor", kz_tweaks_channel_transferor_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind to channel_bridge event!\n");
}
if (switch_event_bind("kz_tweaks", SWITCH_EVENT_CUSTOM, "sofia::transferee", kz_tweaks_channel_transferee_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind to channel_bridge event!\n");
}
}
static void kz_tweaks_unbind_events()
{
switch_event_unbind_callback(kz_tweaks_channel_bridge_event_handler);
switch_event_unbind_callback(kz_tweaks_channel_replaced_event_handler);
switch_event_unbind_callback(kz_tweaks_channel_intercepted_event_handler);
switch_event_unbind_callback(kz_tweaks_channel_transferor_event_handler);
switch_event_unbind_callback(kz_tweaks_channel_transferee_event_handler);
}
void kz_tweaks_start()
{
kz_tweaks_register_state_handlers();
kz_tweaks_bind_events();
}
void kz_tweaks_stop()
{
kz_tweaks_unbind_events();
kz_tweaks_unregister_state_handlers();
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -34,664 +34,99 @@
*/
#include "mod_kazoo.h"
/* Stolen from code added to ei in R12B-5.
* Since not everyone has this version yet;
* provide our own version.
* */
char *kazoo_expand_header(switch_memory_pool_t *pool, switch_event_t *event, char *val)
{
char *expanded;
char *dup = NULL;
#define put8(s,n) do { \
(s)[0] = (char)((n) & 0xff); \
(s) += 1; \
} while (0)
expanded = switch_event_expand_headers(event, val);
dup = switch_core_strdup(pool, expanded);
#define put32be(s,n) do { \
(s)[0] = ((n) >> 24) & 0xff; \
(s)[1] = ((n) >> 16) & 0xff; \
(s)[2] = ((n) >> 8) & 0xff; \
(s)[3] = (n) & 0xff; \
(s) += 4; \
} while (0)
if (expanded != val) {
free(expanded);
}
#ifdef EI_DEBUG
static void ei_x_print_reg_msg(ei_x_buff *buf, char *dest, int send) {
char *mbuf = NULL;
int i = 1;
ei_s_print_term(&mbuf, buf->buff, &i);
if (send) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Encoded term %s to '%s'\n", mbuf, dest);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Decoded term %s for '%s'\n", mbuf, dest);
}
free(mbuf);
return dup;
}
static void ei_x_print_msg(ei_x_buff *buf, erlang_pid *pid, int send) {
char *pbuf = NULL;
int i = 0;
ei_x_buff pidbuf;
ei_x_new(&pidbuf);
ei_x_encode_pid(&pidbuf, pid);
ei_s_print_term(&pbuf, pidbuf.buff, &i);
ei_x_print_reg_msg(buf, pbuf, send);
free(pbuf);
}
#endif
void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event) {
ei_encode_switch_event_headers_2(ebuf, event, 1);
}
void ei_encode_switch_event_headers_2(ei_x_buff *ebuf, switch_event_t *event, int encode) {
switch_event_header_t *hp;
char *uuid = switch_event_get_header(event, "unique-id");
int i;
for (i = 0, hp = event->headers; hp; hp = hp->next, i++);
if (event->body)
i++;
ei_x_encode_list_header(ebuf, i + 1);
if (uuid) {
char *unique_id = switch_event_get_header(event, "unique-id");
ei_x_encode_binary(ebuf, unique_id, strlen(unique_id));
} else {
ei_x_encode_atom(ebuf, "undefined");
}
for (hp = event->headers; hp; hp = hp->next) {
ei_x_encode_tuple_header(ebuf, 2);
ei_x_encode_binary(ebuf, hp->name, strlen(hp->name));
if(encode)
switch_url_decode(hp->value);
ei_x_encode_binary(ebuf, hp->value, strlen(hp->value));
}
if (event->body) {
ei_x_encode_tuple_header(ebuf, 2);
ei_x_encode_binary(ebuf, "body", strlen("body"));
ei_x_encode_binary(ebuf, event->body, strlen(event->body));
}
ei_x_encode_empty_list(ebuf);
}
void close_socket(switch_socket_t ** sock) {
if (*sock) {
switch_socket_shutdown(*sock, SWITCH_SHUTDOWN_READWRITE);
switch_socket_close(*sock);
*sock = NULL;
char* switch_event_get_first_of(switch_event_t *event, const char *list[])
{
switch_event_header_t *header = NULL;
int i = 0;
while(list[i] != NULL) {
if((header = switch_event_get_header_ptr(event, list[i])) != NULL)
break;
i++;
}
if(header != NULL) {
return header->value;
} else {
return "nodomain";
}
}
void close_socketfd(int *sockfd) {
if (*sockfd) {
shutdown(*sockfd, SHUT_RDWR);
close(*sockfd);
SWITCH_DECLARE(switch_status_t) switch_event_add_variable_name_printf(switch_event_t *event, switch_stack_t stack, const char *val, const char *fmt, ...)
{
int ret = 0;
char *varname;
va_list ap;
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_assert(event != NULL);
va_start(ap, fmt);
ret = switch_vasprintf(&varname, fmt, ap);
va_end(ap);
if (ret == -1) {
return SWITCH_STATUS_MEMERR;
}
status = switch_event_add_header_string(event, stack, varname, val);
free(varname);
return status;
}
switch_socket_t *create_socket_with_port(switch_memory_pool_t *pool, switch_port_t port) {
switch_sockaddr_t *sa;
switch_socket_t *socket;
if(switch_sockaddr_info_get(&sa, kazoo_globals.ip, SWITCH_UNSPEC, port, 0, pool)) {
return NULL;
}
if (switch_socket_create(&socket, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, pool)) {
return NULL;
}
if (switch_socket_opt_set(socket, SWITCH_SO_REUSEADDR, 1)) {
return NULL;
}
if (switch_socket_bind(socket, sa)) {
return NULL;
}
if (switch_socket_listen(socket, 5)){
return NULL;
}
switch_getnameinfo(&kazoo_globals.hostname, sa, 0);
// if (kazoo_globals.nat_map && switch_nat_get_type()) {
// switch_nat_add_mapping(port, SWITCH_NAT_TCP, NULL, SWITCH_FALSE);
// }
return socket;
SWITCH_DECLARE(switch_xml_t) kz_xml_child(switch_xml_t xml, const char *name)
{
xml = (xml) ? xml->child : NULL;
while (xml && strcasecmp(name, xml->name))
xml = xml->sibling;
return xml;
}
switch_socket_t *create_socket(switch_memory_pool_t *pool) {
return create_socket_with_port(pool, 0);
void kz_xml_process(switch_xml_t cfg)
{
switch_xml_t xml_process;
for (xml_process = kz_xml_child(cfg, "X-PRE-PROCESS"); xml_process; xml_process = xml_process->next) {
const char *cmd = switch_xml_attr(xml_process, "cmd");
const char *data = switch_xml_attr(xml_process, "data");
if(cmd != NULL && !strcasecmp(cmd, "set") && data) {
char *name = (char *) data;
char *val = strchr(name, '=');
}
if (val) {
char *ve = val++;
while (*val && *val == ' ') {
val++;
}
*ve-- = '\0';
while (*ve && *ve == ' ') {
*ve-- = '\0';
}
}
switch_status_t create_ei_cnode(const char *ip_addr, const char *name, struct ei_cnode_s *ei_cnode) {
char hostname[EI_MAXHOSTNAMELEN + 1] = "";
char nodename[MAXNODELEN + 1];
char cnodename[EI_MAXALIVELEN + 1];
//EI_MAX_COOKIE_SIZE+1
char *atsign;
/* copy the erlang interface nodename into something we can modify */
strncpy(cnodename, name, EI_MAXALIVELEN);
if ((atsign = strchr(cnodename, '@'))) {
/* we got a qualified node name, don't guess the host/domain */
snprintf(nodename, MAXNODELEN + 1, "%s", kazoo_globals.ei_nodename);
/* truncate the alivename at the @ */
*atsign = '\0';
} else {
if (zstr(kazoo_globals.hostname) || !strncasecmp(kazoo_globals.ip, "0.0.0.0", 7) || !strncasecmp(kazoo_globals.ip, "::", 2)) {
memcpy(hostname, switch_core_get_hostname(), EI_MAXHOSTNAMELEN);
} else {
memcpy(hostname, kazoo_globals.hostname, EI_MAXHOSTNAMELEN);
}
snprintf(nodename, MAXNODELEN + 1, "%s@%s", kazoo_globals.ei_nodename, hostname);
}
if (kazoo_globals.ei_shortname) {
char *off;
if ((off = strchr(nodename, '.'))) {
*off = '\0';
if (name && val) {
switch_core_set_variable(name, val);
}
}
}
/* init the ec stuff */
if (ei_connect_xinit(ei_cnode, hostname, cnodename, nodename, (Erl_IpAddr) ip_addr, kazoo_globals.ei_cookie, 0) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize the erlang interface connection structure\n");
return SWITCH_STATUS_FALSE;
}
return SWITCH_STATUS_SUCCESS;
}
switch_status_t ei_compare_pids(const erlang_pid *pid1, const erlang_pid *pid2) {
if ((!strcmp(pid1->node, pid2->node))
&& pid1->creation == pid2->creation
&& pid1->num == pid2->num
&& pid1->serial == pid2->serial) {
return SWITCH_STATUS_SUCCESS;
} else {
return SWITCH_STATUS_FALSE;
}
}
void ei_link(ei_node_t *ei_node, erlang_pid * from, erlang_pid * to) {
char msgbuf[2048];
char *s;
int index = 0;
index = 5; /* max sizes: */
ei_encode_version(msgbuf, &index); /* 1 */
ei_encode_tuple_header(msgbuf, &index, 3);
ei_encode_long(msgbuf, &index, ERL_LINK);
ei_encode_pid(msgbuf, &index, from); /* 268 */
ei_encode_pid(msgbuf, &index, to); /* 268 */
/* 5 byte header missing */
s = msgbuf;
put32be(s, index - 4); /* 4 */
put8(s, ERL_PASS_THROUGH); /* 1 */
/* sum: 542 */
if (write(ei_node->nodefd, msgbuf, index) == -1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to link to process on %s\n", ei_node->peer_nodename);
}
}
void ei_encode_switch_event(ei_x_buff *ebuf, switch_event_t *event) {
ei_x_encode_tuple_header(ebuf, 2);
ei_x_encode_atom(ebuf, "event");
ei_encode_switch_event_headers(ebuf, event);
}
int ei_helper_send(ei_node_t *ei_node, erlang_pid *to, ei_x_buff *buf) {
int ret = 0;
if (ei_node->nodefd) {
#ifdef EI_DEBUG
ei_x_print_msg(buf, to, 1);
#endif
ret = ei_send(ei_node->nodefd, to, buf->buff, buf->index);
}
return ret;
}
int ei_decode_atom_safe(char *buf, int *index, char *dst) {
int type, size;
ei_get_type(buf, index, &type, &size);
if (type != ERL_ATOM_EXT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed atom\n", type, size);
return -1;
} else if (size > MAXATOMLEN) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Requested decoding of atom with size %d into a buffer of size %d\n", size, MAXATOMLEN);
return -1;
} else {
return ei_decode_atom(buf, index, dst);
}
}
int ei_decode_string_or_binary(char *buf, int *index, char **dst) {
int type, size, res;
long len;
ei_get_type(buf, index, &type, &size);
if (type != ERL_STRING_EXT && type != ERL_BINARY_EXT && type != ERL_NIL_EXT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed binary or string\n", type, size);
return -1;
}
*dst = malloc(size + 1);
if (type == ERL_NIL_EXT) {
res = 0;
**dst = '\0';
} else if (type == ERL_BINARY_EXT) {
res = ei_decode_binary(buf, index, *dst, &len);
(*dst)[len] = '\0';
} else {
res = ei_decode_string(buf, index, *dst);
}
return res;
}
int ei_decode_string_or_binary_limited(char *buf, int *index, int maxsize, char *dst) {
int type, size, res;
long len;
ei_get_type(buf, index, &type, &size);
if (type != ERL_STRING_EXT && type != ERL_BINARY_EXT && type != ERL_NIL_EXT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed binary or string\n", type, size);
return -1;
}
if (size > maxsize) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Requested decoding of %s with size %d into a buffer of size %d\n",
type == ERL_BINARY_EXT ? "binary" : "string", size, maxsize);
return -1;
}
if (type == ERL_NIL_EXT) {
res = 0;
*dst = '\0';
} else if (type == ERL_BINARY_EXT) {
res = ei_decode_binary(buf, index, dst, &len);
dst[len] = '\0'; /* binaries aren't null terminated */
} else {
res = ei_decode_string(buf, index, dst);
}
return res;
}
switch_hash_t *create_default_filter() {
switch_hash_t *filter;
switch_core_hash_init(&filter);
switch_core_hash_insert(filter, "Acquired-UUID", "1");
switch_core_hash_insert(filter, "action", "1");
switch_core_hash_insert(filter, "Action", "1");
switch_core_hash_insert(filter, "alt_event_type", "1");
switch_core_hash_insert(filter, "Answer-State", "1");
switch_core_hash_insert(filter, "Application", "1");
switch_core_hash_insert(filter, "Application-Data", "1");
switch_core_hash_insert(filter, "Application-Name", "1");
switch_core_hash_insert(filter, "Application-Response", "1");
switch_core_hash_insert(filter, "att_xfer_replaced_by", "1");
switch_core_hash_insert(filter, "Auth-Method", "1");
switch_core_hash_insert(filter, "Auth-Realm", "1");
switch_core_hash_insert(filter, "Auth-User", "1");
switch_core_hash_insert(filter, "Bridge-A-Unique-ID", "1");
switch_core_hash_insert(filter, "Bridge-B-Unique-ID", "1");
switch_core_hash_insert(filter, "Call-Direction", "1");
switch_core_hash_insert(filter, "Caller-Callee-ID-Name", "1");
switch_core_hash_insert(filter, "Caller-Callee-ID-Number", "1");
switch_core_hash_insert(filter, "Caller-Caller-ID-Name", "1");
switch_core_hash_insert(filter, "Caller-Caller-ID-Number", "1");
switch_core_hash_insert(filter, "Caller-Screen-Bit", "1");
switch_core_hash_insert(filter, "Caller-Privacy-Hide-Name", "1");
switch_core_hash_insert(filter, "Caller-Privacy-Hide-Number", "1");
switch_core_hash_insert(filter, "Caller-Context", "1");
switch_core_hash_insert(filter, "Caller-Controls", "1");
switch_core_hash_insert(filter, "Caller-Destination-Number", "1");
switch_core_hash_insert(filter, "Caller-Dialplan", "1");
switch_core_hash_insert(filter, "Caller-Network-Addr", "1");
switch_core_hash_insert(filter, "Caller-Unique-ID", "1");
switch_core_hash_insert(filter, "Call-ID", "1");
switch_core_hash_insert(filter, "Channel-Call-State", "1");
switch_core_hash_insert(filter, "Channel-Call-UUID", "1");
switch_core_hash_insert(filter, "Channel-Presence-ID", "1");
switch_core_hash_insert(filter, "Channel-State", "1");
switch_core_hash_insert(filter, "Chat-Permissions", "1");
switch_core_hash_insert(filter, "Conference-Name", "1");
switch_core_hash_insert(filter, "Conference-Profile-Name", "1");
switch_core_hash_insert(filter, "Conference-Unique-ID", "1");
switch_core_hash_insert(filter, "contact", "1");
switch_core_hash_insert(filter, "Detected-Tone", "1");
switch_core_hash_insert(filter, "dialog_state", "1");
switch_core_hash_insert(filter, "direction", "1");
switch_core_hash_insert(filter, "Distributed-From", "1");
switch_core_hash_insert(filter, "DTMF-Digit", "1");
switch_core_hash_insert(filter, "DTMF-Duration", "1");
switch_core_hash_insert(filter, "Event-Date-Timestamp", "1");
switch_core_hash_insert(filter, "Event-Name", "1");
switch_core_hash_insert(filter, "Event-Subclass", "1");
switch_core_hash_insert(filter, "expires", "1");
switch_core_hash_insert(filter, "Expires", "1");
switch_core_hash_insert(filter, "Ext-SIP-IP", "1");
switch_core_hash_insert(filter, "File", "1");
switch_core_hash_insert(filter, "FreeSWITCH-Hostname", "1");
switch_core_hash_insert(filter, "from", "1");
switch_core_hash_insert(filter, "Hunt-Destination-Number", "1");
switch_core_hash_insert(filter, "ip", "1");
switch_core_hash_insert(filter, "Message-Account", "1");
switch_core_hash_insert(filter, "metadata", "1");
switch_core_hash_insert(filter, "old_node_channel_uuid", "1");
switch_core_hash_insert(filter, "Other-Leg-Callee-ID-Name", "1");
switch_core_hash_insert(filter, "Other-Leg-Callee-ID-Number", "1");
switch_core_hash_insert(filter, "Other-Leg-Caller-ID-Name", "1");
switch_core_hash_insert(filter, "Other-Leg-Caller-ID-Number", "1");
switch_core_hash_insert(filter, "Other-Leg-Destination-Number", "1");
switch_core_hash_insert(filter, "Other-Leg-Direction", "1");
switch_core_hash_insert(filter, "Other-Leg-Unique-ID", "1");
switch_core_hash_insert(filter, "Other-Leg-Channel-Name", "1");
switch_core_hash_insert(filter, "Participant-Type", "1");
switch_core_hash_insert(filter, "Path", "1");
switch_core_hash_insert(filter, "profile_name", "1");
switch_core_hash_insert(filter, "Profiles", "1");
switch_core_hash_insert(filter, "proto-specific-event-name", "1");
switch_core_hash_insert(filter, "Raw-Application-Data", "1");
switch_core_hash_insert(filter, "realm", "1");
switch_core_hash_insert(filter, "Resigning-UUID", "1");
switch_core_hash_insert(filter, "set", "1");
switch_core_hash_insert(filter, "sip_auto_answer", "1");
switch_core_hash_insert(filter, "sip_auth_method", "1");
switch_core_hash_insert(filter, "sip_from_host", "1");
switch_core_hash_insert(filter, "sip_from_user", "1");
switch_core_hash_insert(filter, "sip_to_host", "1");
switch_core_hash_insert(filter, "sip_to_user", "1");
switch_core_hash_insert(filter, "sub-call-id", "1");
switch_core_hash_insert(filter, "technology", "1");
switch_core_hash_insert(filter, "to", "1");
switch_core_hash_insert(filter, "Unique-ID", "1");
switch_core_hash_insert(filter, "URL", "1");
switch_core_hash_insert(filter, "username", "1");
switch_core_hash_insert(filter, "variable_channel_is_moving", "1");
switch_core_hash_insert(filter, "variable_collected_digits", "1");
switch_core_hash_insert(filter, "variable_current_application", "1");
switch_core_hash_insert(filter, "variable_current_application_data", "1");
switch_core_hash_insert(filter, "variable_domain_name", "1");
switch_core_hash_insert(filter, "variable_effective_caller_id_name", "1");
switch_core_hash_insert(filter, "variable_effective_caller_id_number", "1");
switch_core_hash_insert(filter, "variable_holding_uuid", "1");
switch_core_hash_insert(filter, "variable_hold_music", "1");
switch_core_hash_insert(filter, "variable_media_group_id", "1");
switch_core_hash_insert(filter, "variable_originate_disposition", "1");
switch_core_hash_insert(filter, "variable_origination_uuid", "1");
switch_core_hash_insert(filter, "variable_playback_terminator_used", "1");
switch_core_hash_insert(filter, "variable_presence_id", "1");
switch_core_hash_insert(filter, "variable_record_ms", "1");
switch_core_hash_insert(filter, "variable_recovered", "1");
switch_core_hash_insert(filter, "variable_silence_hits_exhausted", "1");
switch_core_hash_insert(filter, "variable_sip_auth_realm", "1");
switch_core_hash_insert(filter, "variable_sip_from_host", "1");
switch_core_hash_insert(filter, "variable_sip_from_user", "1");
switch_core_hash_insert(filter, "variable_sip_from_tag", "1");
switch_core_hash_insert(filter, "variable_sip_h_X-AUTH-IP", "1");
switch_core_hash_insert(filter, "variable_sip_received_ip", "1");
switch_core_hash_insert(filter, "variable_sip_to_host", "1");
switch_core_hash_insert(filter, "variable_sip_to_user", "1");
switch_core_hash_insert(filter, "variable_sip_to_tag", "1");
switch_core_hash_insert(filter, "variable_sofia_profile_name", "1");
switch_core_hash_insert(filter, "variable_transfer_history", "1");
switch_core_hash_insert(filter, "variable_user_name", "1");
switch_core_hash_insert(filter, "variable_endpoint_disposition", "1");
switch_core_hash_insert(filter, "variable_originate_disposition", "1");
switch_core_hash_insert(filter, "variable_bridge_hangup_cause", "1");
switch_core_hash_insert(filter, "variable_hangup_cause", "1");
switch_core_hash_insert(filter, "variable_last_bridge_proto_specific_hangup_cause", "1");
switch_core_hash_insert(filter, "variable_proto_specific_hangup_cause", "1");
switch_core_hash_insert(filter, "VM-Call-ID", "1");
switch_core_hash_insert(filter, "VM-sub-call-id", "1");
switch_core_hash_insert(filter, "whistle_application_name", "1");
switch_core_hash_insert(filter, "whistle_application_response", "1");
switch_core_hash_insert(filter, "whistle_event_name", "1");
switch_core_hash_insert(filter, "kazoo_application_name", "1");
switch_core_hash_insert(filter, "kazoo_application_response", "1");
switch_core_hash_insert(filter, "kazoo_event_name", "1");
switch_core_hash_insert(filter, "sip_auto_answer_notify", "1");
switch_core_hash_insert(filter, "eavesdrop_group", "1");
switch_core_hash_insert(filter, "origination_caller_id_name", "1");
switch_core_hash_insert(filter, "origination_caller_id_number", "1");
switch_core_hash_insert(filter, "origination_callee_id_name", "1");
switch_core_hash_insert(filter, "origination_callee_id_number", "1");
switch_core_hash_insert(filter, "sip_auth_username", "1");
switch_core_hash_insert(filter, "sip_auth_password", "1");
switch_core_hash_insert(filter, "effective_caller_id_name", "1");
switch_core_hash_insert(filter, "effective_caller_id_number", "1");
switch_core_hash_insert(filter, "effective_callee_id_name", "1");
switch_core_hash_insert(filter, "effective_callee_id_number", "1");
switch_core_hash_insert(filter, "variable_destination_number", "1");
switch_core_hash_insert(filter, "variable_effective_callee_id_name", "1");
switch_core_hash_insert(filter, "variable_effective_callee_id_number", "1");
switch_core_hash_insert(filter, "variable_record_silence_hits", "1");
switch_core_hash_insert(filter, "variable_refer_uuid", "1");
switch_core_hash_insert(filter, "variable_sip_call_id", "1");
switch_core_hash_insert(filter, "variable_sip_h_Referred-By", "1");
switch_core_hash_insert(filter, "variable_sip_h_X-AUTH-PORT", "1");
switch_core_hash_insert(filter, "variable_sip_loopback_req_uri", "1");
switch_core_hash_insert(filter, "variable_sip_received_port", "1");
switch_core_hash_insert(filter, "variable_sip_refer_to", "1");
switch_core_hash_insert(filter, "variable_sip_req_host", "1");
switch_core_hash_insert(filter, "variable_sip_req_uri", "1");
switch_core_hash_insert(filter, "variable_transfer_source", "1");
switch_core_hash_insert(filter, "variable_uuid", "1");
/* Registration headers */
switch_core_hash_insert(filter, "call-id", "1");
switch_core_hash_insert(filter, "profile-name", "1");
switch_core_hash_insert(filter, "from-user", "1");
switch_core_hash_insert(filter, "from-host", "1");
switch_core_hash_insert(filter, "presence-hosts", "1");
switch_core_hash_insert(filter, "contact", "1");
switch_core_hash_insert(filter, "rpid", "1");
switch_core_hash_insert(filter, "status", "1");
switch_core_hash_insert(filter, "expires", "1");
switch_core_hash_insert(filter, "to-user", "1");
switch_core_hash_insert(filter, "to-host", "1");
switch_core_hash_insert(filter, "network-ip", "1");
switch_core_hash_insert(filter, "network-port", "1");
switch_core_hash_insert(filter, "username", "1");
switch_core_hash_insert(filter, "realm", "1");
switch_core_hash_insert(filter, "user-agent", "1");
switch_core_hash_insert(filter, "Hangup-Cause", "1");
switch_core_hash_insert(filter, "Unique-ID", "1");
switch_core_hash_insert(filter, "variable_switch_r_sdp", "1");
switch_core_hash_insert(filter, "variable_rtp_local_sdp_str", "1");
switch_core_hash_insert(filter, "variable_sip_to_uri", "1");
switch_core_hash_insert(filter, "variable_sip_from_uri", "1");
switch_core_hash_insert(filter, "variable_sip_user_agent", "1");
switch_core_hash_insert(filter, "variable_duration", "1");
switch_core_hash_insert(filter, "variable_billsec", "1");
switch_core_hash_insert(filter, "variable_billmsec", "1");
switch_core_hash_insert(filter, "variable_progresssec", "1");
switch_core_hash_insert(filter, "variable_progress_uepoch", "1");
switch_core_hash_insert(filter, "variable_progress_media_uepoch", "1");
switch_core_hash_insert(filter, "variable_start_uepoch", "1");
switch_core_hash_insert(filter, "variable_digits_dialed", "1");
switch_core_hash_insert(filter, "Member-ID", "1");
switch_core_hash_insert(filter, "Floor", "1");
switch_core_hash_insert(filter, "Video", "1");
switch_core_hash_insert(filter, "Hear", "1");
switch_core_hash_insert(filter, "Speak", "1");
switch_core_hash_insert(filter, "Talking", "1");
switch_core_hash_insert(filter, "Current-Energy", "1");
switch_core_hash_insert(filter, "Energy-Level", "1");
switch_core_hash_insert(filter, "Mute-Detect", "1");
/* RTMP headers */
switch_core_hash_insert(filter, "RTMP-Session-ID", "1");
switch_core_hash_insert(filter, "RTMP-Profile", "1");
switch_core_hash_insert(filter, "RTMP-Flash-Version", "1");
switch_core_hash_insert(filter, "RTMP-SWF-URL", "1");
switch_core_hash_insert(filter, "RTMP-TC-URL", "1");
switch_core_hash_insert(filter, "RTMP-Page-URL", "1");
switch_core_hash_insert(filter, "User", "1");
switch_core_hash_insert(filter, "Domain", "1");
/* Fax headers */
switch_core_hash_insert(filter, "variable_fax_bad_rows", "1");
switch_core_hash_insert(filter, "variable_fax_document_total_pages", "1");
switch_core_hash_insert(filter, "variable_fax_document_transferred_pages", "1");
switch_core_hash_insert(filter, "variable_fax_ecm_used", "1");
switch_core_hash_insert(filter, "variable_fax_result_code", "1");
switch_core_hash_insert(filter, "variable_fax_result_text", "1");
switch_core_hash_insert(filter, "variable_fax_success", "1");
switch_core_hash_insert(filter, "variable_fax_transfer_rate", "1");
switch_core_hash_insert(filter, "variable_fax_local_station_id", "1");
switch_core_hash_insert(filter, "variable_fax_remote_station_id", "1");
switch_core_hash_insert(filter, "variable_fax_remote_country", "1");
switch_core_hash_insert(filter, "variable_fax_remote_vendor", "1");
switch_core_hash_insert(filter, "variable_fax_remote_model", "1");
switch_core_hash_insert(filter, "variable_fax_image_resolution", "1");
switch_core_hash_insert(filter, "variable_fax_file_image_resolution", "1");
switch_core_hash_insert(filter, "variable_fax_image_size", "1");
switch_core_hash_insert(filter, "variable_fax_image_pixel_size", "1");
switch_core_hash_insert(filter, "variable_fax_file_image_pixel_size", "1");
switch_core_hash_insert(filter, "variable_fax_longest_bad_row_run", "1");
switch_core_hash_insert(filter, "variable_fax_encoding", "1");
switch_core_hash_insert(filter, "variable_fax_encoding_name", "1");
switch_core_hash_insert(filter, "variable_fax_header", "1");
switch_core_hash_insert(filter, "variable_fax_ident", "1");
switch_core_hash_insert(filter, "variable_fax_timezone", "1");
switch_core_hash_insert(filter, "variable_fax_doc_id", "1");
switch_core_hash_insert(filter, "variable_fax_doc_database", "1");
switch_core_hash_insert(filter, "variable_has_t38", "1");
/* Secure headers */
switch_core_hash_insert(filter, "variable_sdp_secure_savp_only", "1");
switch_core_hash_insert(filter, "variable_rtp_has_crypto", "1");
switch_core_hash_insert(filter, "variable_rtp_secure_media", "1");
switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed", "1");
switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed_audio", "1");
switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed_video", "1");
switch_core_hash_insert(filter, "variable_zrtp_secure_media", "1");
switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed", "1");
switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed_audio", "1");
switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed_video", "1");
switch_core_hash_insert(filter, "sdp_secure_savp_only", "1");
switch_core_hash_insert(filter, "rtp_has_crypto", "1");
switch_core_hash_insert(filter, "rtp_secure_media", "1");
switch_core_hash_insert(filter, "rtp_secure_media_confirmed", "1");
switch_core_hash_insert(filter, "rtp_secure_media_confirmed_audio", "1");
switch_core_hash_insert(filter, "rtp_secure_media_confirmed_video", "1");
switch_core_hash_insert(filter, "zrtp_secure_media", "1");
switch_core_hash_insert(filter, "zrtp_secure_media_confirmed", "1");
switch_core_hash_insert(filter, "zrtp_secure_media_confirmed_audio", "1");
switch_core_hash_insert(filter, "zrtp_secure_media_confirmed_video", "1");
/* Device Redirect headers */
switch_core_hash_insert(filter, "variable_last_bridge_hangup_cause", "1");
switch_core_hash_insert(filter, "variable_sip_redirected_by", "1");
switch_core_hash_insert(filter, "intercepted_by", "1");
switch_core_hash_insert(filter, "variable_bridge_uuid", "1");
switch_core_hash_insert(filter, "Record-File-Path", "1");
/* Loopback headers */
switch_core_hash_insert(filter, "variable_loopback_bowout_on_execute", "1");
switch_core_hash_insert(filter, "variable_loopback_bowout", "1");
switch_core_hash_insert(filter, "variable_other_loopback_leg_uuid", "1");
switch_core_hash_insert(filter, "variable_loopback_leg", "1");
switch_core_hash_insert(filter, "variable_is_loopback", "1");
// SMS
switch_core_hash_insert(filter, "Message-ID", "1");
switch_core_hash_insert(filter, "Delivery-Failure", "1");
switch_core_hash_insert(filter, "Delivery-Result-Code", "1");
return filter;
}
static void *SWITCH_THREAD_FUNC fetch_config_filters_exec(switch_thread_t *thread, void *obj)
{
char *cf = "kazoo.conf";
switch_xml_t cfg, xml, child, param;
switch_event_t *params;
switch_memory_pool_t *pool = (switch_memory_pool_t *)obj;
switch_event_create(&params, SWITCH_EVENT_REQUEST_PARAMS);
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "Action", "request-filter");
if (!(xml = switch_xml_open_cfg(cf, &cfg, params))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open configuration file %s\n", cf);
} else if ((child = switch_xml_child(cfg, "event-filter"))) {
switch_hash_t *filter;
switch_hash_t *old_filter;
switch_core_hash_init(&filter);
for (param = switch_xml_child(child, "header"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
switch_core_hash_insert(filter, var, "1");
}
old_filter = kazoo_globals.event_filter;
kazoo_globals.config_filters_fetched = 1;
kazoo_globals.event_filter = filter;
if (old_filter) {
switch_core_hash_destroy(&old_filter);
}
switch_xml_free(xml);
}
if (params) switch_event_destroy(&params);
switch_core_destroy_memory_pool(&pool);
return NULL;
}
void fetch_config_filters() {
switch_memory_pool_t *pool;
switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL;
switch_uuid_t uuid;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "fetching kazoo filters\n");
switch_core_new_memory_pool(&pool);
switch_threadattr_create(&thd_attr, pool);
switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
switch_uuid_get(&uuid);
switch_thread_create(&thread, thd_attr, fetch_config_filters_exec, pool, pool);
}
/* For Emacs:
* Local Variables:
* mode:c

View File

@ -32,608 +32,12 @@
*/
#include "mod_kazoo.h"
#define KAZOO_DESC "kazoo information"
#define KAZOO_SYNTAX "<command> [<args>]"
globals_t kazoo_globals = {0};
globals_t kazoo_globals;
SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown);
SWITCH_MODULE_RUNTIME_FUNCTION(mod_kazoo_runtime);
SWITCH_MODULE_DEFINITION(mod_kazoo, mod_kazoo_load, mod_kazoo_shutdown, mod_kazoo_runtime);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_pref_ip, kazoo_globals.ip);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_pref_ei_cookie, kazoo_globals.ei_cookie);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_pref_ei_nodename, kazoo_globals.ei_nodename);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_pref_kazoo_var_prefix, kazoo_globals.kazoo_var_prefix);
static switch_status_t api_erlang_status(switch_stream_handle_t *stream) {
switch_sockaddr_t *sa;
uint16_t port;
char ipbuf[48];
const char *ip_addr;
ei_node_t *ei_node;
switch_socket_addr_get(&sa, SWITCH_FALSE, kazoo_globals.acceptor);
port = switch_sockaddr_get_port(sa);
ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa);
stream->write_function(stream, "Running %s\n", VERSION);
stream->write_function(stream, "Listening for new Erlang connections on %s:%u with cookie %s\n", ip_addr, port, kazoo_globals.ei_cookie);
stream->write_function(stream, "Registered as Erlang node %s, visible as %s\n", kazoo_globals.ei_cnode.thisnodename, kazoo_globals.ei_cnode.thisalivename);
if (kazoo_globals.ei_compat_rel) {
stream->write_function(stream, "Using Erlang compatibility mode: %d\n", kazoo_globals.ei_compat_rel);
}
switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock);
ei_node = kazoo_globals.ei_nodes;
if (!ei_node) {
stream->write_function(stream, "No erlang nodes connected\n");
} else {
stream->write_function(stream, "Connected to:\n");
while(ei_node != NULL) {
unsigned int year, day, hour, min, sec, delta;
delta = (switch_micro_time_now() - ei_node->created_time) / 1000000;
sec = delta % 60;
min = delta / 60 % 60;
hour = delta / 3600 % 24;
day = delta / 86400 % 7;
year = delta / 31556926 % 12;
stream->write_function(stream, " %s (%s:%d) up %d years, %d days, %d hours, %d minutes, %d seconds\n"
,ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port, year, day, hour, min, sec);
ei_node = ei_node->next;
}
}
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_erlang_event_filter(switch_stream_handle_t *stream) {
switch_hash_index_t *hi = NULL;
int column = 0;
for (hi = (switch_hash_index_t *)switch_core_hash_first_iter(kazoo_globals.event_filter, hi); hi; hi = switch_core_hash_next(&hi)) {
const void *key;
void *val;
switch_core_hash_this(hi, &key, NULL, &val);
stream->write_function(stream, "%-50s", (char *)key);
if (++column > 2) {
stream->write_function(stream, "\n");
column = 0;
}
}
if (++column > 2) {
stream->write_function(stream, "\n");
column = 0;
}
stream->write_function(stream, "%-50s", kazoo_globals.kazoo_var_prefix);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_erlang_nodes_list(switch_stream_handle_t *stream) {
ei_node_t *ei_node;
switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock);
ei_node = kazoo_globals.ei_nodes;
while(ei_node != NULL) {
stream->write_function(stream, "%s (%s)\n", ei_node->peer_nodename, ei_node->remote_ip);
ei_node = ei_node->next;
}
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_erlang_nodes_count(switch_stream_handle_t *stream) {
ei_node_t *ei_node;
int count = 0;
switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock);
ei_node = kazoo_globals.ei_nodes;
while(ei_node != NULL) {
count++;
ei_node = ei_node->next;
}
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
stream->write_function(stream, "%d\n", count);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_complete_erlang_node(const char *line, const char *cursor, switch_console_callback_match_t **matches) {
switch_console_callback_match_t *my_matches = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
ei_node_t *ei_node;
switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock);
ei_node = kazoo_globals.ei_nodes;
while(ei_node != NULL) {
switch_console_push_match(&my_matches, ei_node->peer_nodename);
ei_node = ei_node->next;
}
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
if (my_matches) {
*matches = my_matches;
status = SWITCH_STATUS_SUCCESS;
}
return status;
}
static switch_status_t handle_node_api_event_stream(ei_event_stream_t *event_stream, switch_stream_handle_t *stream) {
ei_event_binding_t *binding;
int column = 0;
switch_mutex_lock(event_stream->socket_mutex);
if (event_stream->connected == SWITCH_FALSE) {
switch_sockaddr_t *sa;
uint16_t port;
char ipbuf[48] = {0};
const char *ip_addr;
switch_socket_addr_get(&sa, SWITCH_TRUE, event_stream->acceptor);
port = switch_sockaddr_get_port(sa);
ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa);
if (zstr(ip_addr)) {
ip_addr = kazoo_globals.ip;
}
stream->write_function(stream, "%s:%d -> disconnected\n"
,ip_addr, port);
} else {
stream->write_function(stream, "%s:%d -> %s:%d\n"
,event_stream->local_ip, event_stream->local_port
,event_stream->remote_ip, event_stream->remote_port);
}
binding = event_stream->bindings;
while(binding != NULL) {
if (binding->type == SWITCH_EVENT_CUSTOM) {
stream->write_function(stream, "CUSTOM %-43s", binding->subclass_name);
} else {
stream->write_function(stream, "%-50s", switch_event_name(binding->type));
}
if (++column > 2) {
stream->write_function(stream, "\n");
column = 0;
}
binding = binding->next;
}
switch_mutex_unlock(event_stream->socket_mutex);
if (!column) {
stream->write_function(stream, "\n");
} else {
stream->write_function(stream, "\n\n");
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t handle_node_api_event_streams(ei_node_t *ei_node, switch_stream_handle_t *stream) {
ei_event_stream_t *event_stream;
switch_mutex_lock(ei_node->event_streams_mutex);
event_stream = ei_node->event_streams;
while(event_stream != NULL) {
handle_node_api_event_stream(event_stream, stream);
event_stream = event_stream->next;
}
switch_mutex_unlock(ei_node->event_streams_mutex);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t handle_node_api_command(ei_node_t *ei_node, switch_stream_handle_t *stream, uint32_t command) {
unsigned int year, day, hour, min, sec, delta;
switch (command) {
case API_COMMAND_DISCONNECT:
stream->write_function(stream, "Disconnecting erlang node %s at managers request\n", ei_node->peer_nodename);
switch_clear_flag(ei_node, LFLAG_RUNNING);
break;
case API_COMMAND_REMOTE_IP:
delta = (switch_micro_time_now() - ei_node->created_time) / 1000000;
sec = delta % 60;
min = delta / 60 % 60;
hour = delta / 3600 % 24;
day = delta / 86400 % 7;
year = delta / 31556926 % 12;
stream->write_function(stream, "Uptime %d years, %d days, %d hours, %d minutes, %d seconds\n", year, day, hour, min, sec);
stream->write_function(stream, "Local Address %s:%d\n", ei_node->local_ip, ei_node->local_port);
stream->write_function(stream, "Remote Address %s:%d\n", ei_node->remote_ip, ei_node->remote_port);
break;
case API_COMMAND_STREAMS:
handle_node_api_event_streams(ei_node, stream);
break;
case API_COMMAND_BINDINGS:
handle_api_command_streams(ei_node, stream);
break;
default:
break;
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_erlang_node_command(switch_stream_handle_t *stream, const char *nodename, uint32_t command) {
ei_node_t *ei_node;
switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock);
ei_node = kazoo_globals.ei_nodes;
while(ei_node != NULL) {
int length = strlen(ei_node->peer_nodename);
if (!strncmp(ei_node->peer_nodename, nodename, length)) {
handle_node_api_command(ei_node, stream, command);
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
return SWITCH_STATUS_SUCCESS;
}
ei_node = ei_node->next;
}
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
return SWITCH_STATUS_NOTFOUND;
}
static int read_cookie_from_file(char *filename) {
int fd;
char cookie[MAXATOMLEN + 1];
char *end;
struct stat buf;
ssize_t res;
if (!stat(filename, &buf)) {
if ((buf.st_mode & S_IRWXG) || (buf.st_mode & S_IRWXO)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s must only be accessible by owner only.\n", filename);
return 2;
}
if (buf.st_size > MAXATOMLEN) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s contains a cookie larger than the maximum atom size of %d.\n", filename, MAXATOMLEN);
return 2;
}
fd = open(filename, O_RDONLY);
if (fd < 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to open cookie file %s : %d.\n", filename, errno);
return 2;
}
if ((res = read(fd, cookie, MAXATOMLEN)) < 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read cookie file %s : %d.\n", filename, errno);
}
cookie[MAXATOMLEN] = '\0';
/* replace any end of line characters with a null */
if ((end = strchr(cookie, '\n'))) {
*end = '\0';
}
if ((end = strchr(cookie, '\r'))) {
*end = '\0';
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set cookie from file %s: %s\n", filename, cookie);
set_pref_ei_cookie(cookie);
return 0;
} else {
/* don't error here, because we might be blindly trying to read $HOME/.erlang.cookie, and that can fail silently */
return 1;
}
}
static switch_status_t config(void) {
char *cf = "kazoo.conf";
switch_xml_t cfg, xml, child, param;
kazoo_globals.send_all_headers = 0;
kazoo_globals.send_all_private_headers = 1;
kazoo_globals.connection_timeout = 500;
kazoo_globals.receive_timeout = 200;
kazoo_globals.receive_msg_preallocate = 2000;
kazoo_globals.event_stream_preallocate = 4000;
kazoo_globals.send_msg_batch = 10;
kazoo_globals.event_stream_framing = 2;
kazoo_globals.port = 0;
kazoo_globals.io_fault_tolerance = 10;
if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open configuration file %s\n", cf);
return SWITCH_STATUS_FALSE;
} else {
if ((child = switch_xml_child(cfg, "settings"))) {
for (param = switch_xml_child(child, "param"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
char *val = (char *) switch_xml_attr_soft(param, "value");
if (!strcmp(var, "listen-ip")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set bind ip address: %s\n", val);
set_pref_ip(val);
} else if (!strcmp(var, "listen-port")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set bind port: %s\n", val);
kazoo_globals.port = atoi(val);
} else if (!strcmp(var, "cookie")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set cookie: %s\n", val);
set_pref_ei_cookie(val);
} else if (!strcmp(var, "cookie-file")) {
if (read_cookie_from_file(val) == 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read cookie from %s\n", val);
}
} else if (!strcmp(var, "nodename")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set node name: %s\n", val);
set_pref_ei_nodename(val);
} else if (!strcmp(var, "shortname")) {
kazoo_globals.ei_shortname = switch_true(val);
} else if (!strcmp(var, "kazoo-var-prefix")) {
set_pref_kazoo_var_prefix(val);
} else if (!strcmp(var, "compat-rel")) {
if (atoi(val) >= 7)
kazoo_globals.ei_compat_rel = atoi(val);
else
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid compatibility release '%s' specified\n", val);
} else if (!strcmp(var, "nat-map")) {
kazoo_globals.nat_map = switch_true(val);
} else if (!strcmp(var, "send-all-headers")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-all-headers: %s\n", val);
kazoo_globals.send_all_headers = switch_true(val);
} else if (!strcmp(var, "send-all-private-headers")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-all-private-headers: %s\n", val);
kazoo_globals.send_all_private_headers = switch_true(val);
} else if (!strcmp(var, "connection-timeout")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set connection-timeout: %s\n", val);
kazoo_globals.connection_timeout = atoi(val);
} else if (!strcmp(var, "receive-timeout")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set receive-timeout: %s\n", val);
kazoo_globals.receive_timeout = atoi(val);
} else if (!strcmp(var, "receive-msg-preallocate")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set receive-msg-preallocate: %s\n", val);
kazoo_globals.receive_msg_preallocate = atoi(val);
} else if (!strcmp(var, "event-stream-preallocate")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set event-stream-preallocate: %s\n", val);
kazoo_globals.event_stream_preallocate = atoi(val);
} else if (!strcmp(var, "send-msg-batch-size")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-msg-batch-size: %s\n", val);
kazoo_globals.send_msg_batch = atoi(val);
} else if (!strcmp(var, "event-stream-framing")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set event-stream-framing: %s\n", val);
kazoo_globals.event_stream_framing = atoi(val);
} else if (!strcmp(var, "io-fault-tolerance")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set io-fault-tolerance: %s\n", val);
kazoo_globals.io_fault_tolerance = atoi(val);
}
}
}
if ((child = switch_xml_child(cfg, "event-filter"))) {
switch_hash_t *filter;
switch_core_hash_init(&filter);
for (param = switch_xml_child(child, "header"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
switch_core_hash_insert(filter, var, "1");
}
kazoo_globals.event_filter = filter;
}
switch_xml_free(xml);
}
if (kazoo_globals.receive_msg_preallocate < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid receive message preallocate value, disabled\n");
kazoo_globals.receive_msg_preallocate = 0;
}
if (kazoo_globals.event_stream_preallocate < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event stream preallocate value, disabled\n");
kazoo_globals.event_stream_preallocate = 0;
}
if (kazoo_globals.send_msg_batch < 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid send message batch size, reverting to default\n");
kazoo_globals.send_msg_batch = 10;
}
if (kazoo_globals.io_fault_tolerance < 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid I/O fault tolerance, reverting to default\n");
kazoo_globals.io_fault_tolerance = 10;
}
if (!kazoo_globals.event_filter) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Event filter not found in configuration, using default\n");
kazoo_globals.event_filter = create_default_filter();
}
if (kazoo_globals.event_stream_framing < 1 || kazoo_globals.event_stream_framing > 4) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event stream framing value, using default\n");
kazoo_globals.event_stream_framing = 2;
}
if (zstr(kazoo_globals.kazoo_var_prefix)) {
set_pref_kazoo_var_prefix("variable_ecallmgr*");
kazoo_globals.var_prefix_length = 17; //ignore the *
} else {
/* we could use the global pool but then we would have to conditionally
* free the pointer if it was not drawn from the XML */
char *buf;
int size = switch_snprintf(NULL, 0, "variable_%s*", kazoo_globals.kazoo_var_prefix) + 1;
switch_malloc(buf, size);
switch_snprintf(buf, size, "variable_%s*", kazoo_globals.kazoo_var_prefix);
switch_safe_free(kazoo_globals.kazoo_var_prefix);
kazoo_globals.kazoo_var_prefix = buf;
kazoo_globals.var_prefix_length = size - 2; //ignore the *
}
if (!kazoo_globals.num_worker_threads) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Number of worker threads not found in configuration, using default\n");
kazoo_globals.num_worker_threads = 10;
}
if (zstr(kazoo_globals.ip)) {
set_pref_ip("0.0.0.0");
}
if (zstr(kazoo_globals.ei_cookie)) {
int res;
char *home_dir = getenv("HOME");
char path_buf[1024];
if (!zstr(home_dir)) {
/* $HOME/.erlang.cookie */
switch_snprintf(path_buf, sizeof (path_buf), "%s%s%s", home_dir, SWITCH_PATH_SEPARATOR, ".erlang.cookie");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Checking for cookie at path: %s\n", path_buf);
res = read_cookie_from_file(path_buf);
if (res) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No cookie or valid cookie file specified, using default cookie\n");
set_pref_ei_cookie("ClueCon");
}
}
}
if (!kazoo_globals.ei_nodename) {
set_pref_ei_nodename("freeswitch");
}
if (!kazoo_globals.nat_map) {
kazoo_globals.nat_map = 0;
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t create_acceptor() {
switch_sockaddr_t *sa;
uint16_t port;
char ipbuf[48];
const char *ip_addr;
/* if the config has specified an erlang release compatibility then pass that along to the erlang interface */
if (kazoo_globals.ei_compat_rel) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Compatability with OTP R%d requested\n", kazoo_globals.ei_compat_rel);
ei_set_compat_rel(kazoo_globals.ei_compat_rel);
}
if (!(kazoo_globals.acceptor = create_socket_with_port(kazoo_globals.pool, kazoo_globals.port))) {
return SWITCH_STATUS_SOCKERR;
}
switch_socket_addr_get(&sa, SWITCH_FALSE, kazoo_globals.acceptor);
port = switch_sockaddr_get_port(sa);
ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Erlang connection acceptor listening on %s:%u\n", ip_addr, port);
/* try to initialize the erlang interface */
if (create_ei_cnode(ip_addr, kazoo_globals.ei_nodename, &kazoo_globals.ei_cnode) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_SOCKERR;
}
/* tell the erlang port manager where we can be reached. this returns a file descriptor pointing to epmd or -1 */
if ((kazoo_globals.epmdfd = ei_publish(&kazoo_globals.ei_cnode, port)) == -1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
"Failed to publish port to epmd. Try starting it yourself or run an erl shell with the -sname or -name option.\n");
return SWITCH_STATUS_SOCKERR;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connected to epmd and published erlang cnode name %s at port %d\n", kazoo_globals.ei_cnode.thisnodename, port);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_STANDARD_API(exec_api_cmd)
{
char *argv[1024] = { 0 };
int unknown_command = 1, argc = 0;
char *mycmd = NULL;
const char *usage_string = "USAGE:\n"
"--------------------------------------------------------------------------------------------------------------------\n"
"erlang status - provides an overview of the current status\n"
"erlang event_filter - lists the event headers that will be sent to Erlang nodes\n"
"erlang nodes list - lists connected Erlang nodes (usefull for monitoring tools)\n"
"erlang nodes count - provides a count of connected Erlang nodes (usefull for monitoring tools)\n"
"erlang node <node_name> disconnect - disconnects an Erlang node\n"
"erlang node <node_name> connection - Shows the connection info\n"
"erlang node <node_name> event_streams - lists the event streams for an Erlang node\n"
"erlang node <node_name> fetch_bindings - lists the XML fetch bindings for an Erlang node\n"
"---------------------------------------------------------------------------------------------------------------------\n";
if (zstr(cmd)) {
stream->write_function(stream, "%s", usage_string);
return SWITCH_STATUS_SUCCESS;
}
if (!(mycmd = strdup(cmd))) {
return SWITCH_STATUS_MEMERR;
}
if (!(argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
stream->write_function(stream, "%s", usage_string);
switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS;
}
if (zstr(argv[0])) {
stream->write_function(stream, "%s", usage_string);
switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS;
}
if (!strncmp(argv[0], "status", 6)) {
unknown_command = 0;
api_erlang_status(stream);
} else if (!strncmp(argv[0], "event_filter", 6)) {
unknown_command = 0;
api_erlang_event_filter(stream);
} else if (!strncmp(argv[0], "nodes", 6) && !zstr(argv[1])) {
if (!strncmp(argv[1], "list", 6)) {
unknown_command = 0;
api_erlang_nodes_list(stream);
} else if (!strncmp(argv[1], "count", 6)) {
unknown_command = 0;
api_erlang_nodes_count(stream);
}
} else if (!strncmp(argv[0], "node", 6) && !zstr(argv[1]) && !zstr(argv[2])) {
if (!strncmp(argv[2], "disconnect", 6)) {
unknown_command = 0;
api_erlang_node_command(stream, argv[1], API_COMMAND_DISCONNECT);
} else if (!strncmp(argv[2], "connection", 2)) {
unknown_command = 0;
api_erlang_node_command(stream, argv[1], API_COMMAND_REMOTE_IP);
} else if (!strncmp(argv[2], "event_streams", 6)) {
unknown_command = 0;
api_erlang_node_command(stream, argv[1], API_COMMAND_STREAMS);
} else if (!strncmp(argv[2], "fetch_bindings", 6)) {
unknown_command = 0;
api_erlang_node_command(stream, argv[1], API_COMMAND_BINDINGS);
}
}
if (unknown_command) {
stream->write_function(stream, "%s", usage_string);
}
switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load) {
switch_api_interface_t *api_interface = NULL;
switch_application_interface_t *app_interface = NULL;
@ -643,34 +47,17 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load) {
kazoo_globals.pool = pool;
kazoo_globals.ei_nodes = NULL;
if(config() != SWITCH_STATUS_SUCCESS) {
// ensure epmd is running
if(kazoo_load_config() != SWITCH_STATUS_SUCCESS) {
// TODO: what would we need to clean up here?
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Improper configuration!\n");
return SWITCH_STATUS_TERM;
}
if(create_acceptor() != SWITCH_STATUS_SUCCESS) {
// TODO: what would we need to clean up here
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to create erlang connection acceptor!\n");
close_socket(&kazoo_globals.acceptor);
return SWITCH_STATUS_TERM;
}
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
/* create an api for cli debug commands */
SWITCH_ADD_API(api_interface, "erlang", KAZOO_DESC, exec_api_cmd, KAZOO_SYNTAX);
switch_console_set_complete("add erlang status");
switch_console_set_complete("add erlang event_filter");
switch_console_set_complete("add erlang nodes list");
switch_console_set_complete("add erlang nodes count");
switch_console_set_complete("add erlang node ::erlang::node disconnect");
switch_console_set_complete("add erlang node ::erlang::node connection");
switch_console_set_complete("add erlang node ::erlang::node event_streams");
switch_console_set_complete("add erlang node ::erlang::node fetch_bindings");
switch_console_add_complete_func("::erlang::node", api_complete_erlang_node);
switch_thread_rwlock_create(&kazoo_globals.ei_nodes_lock, pool);
switch_set_flag(&kazoo_globals, LFLAG_RUNNING);
@ -678,12 +65,18 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load) {
/* create all XML fetch agents */
bind_fetch_agents();
/* create an api for cli debug commands */
add_cli_api(module_interface, api_interface);
/* add our modified commands */
add_kz_commands(module_interface, api_interface);
/* add our modified dptools */
add_kz_dptools(module_interface, app_interface);
/* add tweaks */
kz_tweaks_start();
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
@ -691,8 +84,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load) {
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown) {
int sanity = 0;
switch_console_set_complete("del erlang");
switch_console_del_complete_func("::erlang::node");
remove_cli_api();
kz_tweaks_stop();
/* stop taking new requests and start shuting down the threads */
switch_clear_flag(&kazoo_globals, LFLAG_RUNNING);
@ -710,6 +105,8 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown) {
switch_core_hash_destroy(&kazoo_globals.event_filter);
}
kazoo_destroy_config();
switch_thread_rwlock_wrlock(kazoo_globals.ei_nodes_lock);
switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
switch_thread_rwlock_destroy(kazoo_globals.ei_nodes_lock);
@ -722,9 +119,9 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown) {
unbind_fetch_agents();
/* Close the port we reserved for uPnP/Switch behind firewall, if necessary */
// if (kazoo_globals.nat_map && switch_nat_get_type()) {
// switch_nat_del_mapping(kazoo_globals.port, SWITCH_NAT_TCP);
// }
if (kazoo_globals.nat_map && switch_nat_get_type()) {
switch_nat_del_mapping(kazoo_globals.port, SWITCH_NAT_TCP);
}
/* clean up our allocated preferences */
switch_safe_free(kazoo_globals.ip);
@ -735,50 +132,6 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown) {
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_RUNTIME_FUNCTION(mod_kazoo_runtime) {
switch_os_socket_t os_socket;
switch_atomic_inc(&kazoo_globals.threads);
switch_os_sock_get(&os_socket, kazoo_globals.acceptor);
while (switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) {
int nodefd;
ErlConnect conn;
/* zero out errno because ei_accept doesn't differentiate between a */
/* failed authentication or a socket failure, or a client version */
/* mismatch or a godzilla attack (and a godzilla attack is highly likely) */
errno = 0;
/* wait here for an erlang node to connect, timming out to check if our module is still running every now-and-again */
if ((nodefd = ei_accept_tmo(&kazoo_globals.ei_cnode, (int) os_socket, &conn, kazoo_globals.connection_timeout)) == ERL_ERROR) {
if (erl_errno == ETIMEDOUT) {
continue;
} else if (errno) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Erlang connection acceptor socket error %d %d\n", erl_errno, errno);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"Erlang node connection failed - ensure your cookie matches '%s' and you are using a good nodename\n", kazoo_globals.ei_cookie);
}
continue;
}
if (!switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) {
break;
}
/* NEW ERLANG NODE CONNECTION! Hello friend! */
new_kazoo_node(nodefd, &conn);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Erlang connection acceptor shut down\n");
switch_atomic_dec(&kazoo_globals.threads);
return SWITCH_STATUS_TERM;
}
/* For Emacs:
* Local Variables:

View File

@ -1,167 +1,32 @@
#include <switch.h>
#include <switch_event.h>
#include <switch_json.h>
#include <ei.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <float.h>
#define MAX_ACL 100
#define CMD_BUFLEN 1024 * 1000
#define MAX_QUEUE_LEN 25000
#define MAX_MISSED 500
#define MAX_PID_CHARS 255
#define VERSION "mod_kazoo v1.4.0-1"
#define API_COMMAND_DISCONNECT 0
#define API_COMMAND_REMOTE_IP 1
#define API_COMMAND_STREAMS 2
#define API_COMMAND_BINDINGS 3
#include "kazoo_ei.h"
#include "kazoo_message.h"
typedef enum {
LFLAG_RUNNING = (1 << 0)
} event_flag_t;
struct ei_send_msg_s {
ei_x_buff buf;
erlang_pid pid;
};
typedef struct ei_send_msg_s ei_send_msg_t;
struct ei_received_msg_s {
ei_x_buff buf;
erlang_msg msg;
};
typedef struct ei_received_msg_s ei_received_msg_t;
struct ei_event_binding_s {
char id[SWITCH_UUID_FORMATTED_LENGTH + 1];
switch_event_node_t *node;
switch_event_types_t type;
const char *subclass_name;
struct ei_event_binding_s *next;
};
typedef struct ei_event_binding_s ei_event_binding_t;
struct ei_event_stream_s {
switch_memory_pool_t *pool;
ei_event_binding_t *bindings;
switch_queue_t *queue;
switch_socket_t *acceptor;
switch_pollset_t *pollset;
switch_pollfd_t *pollfd;
switch_socket_t *socket;
switch_mutex_t *socket_mutex;
switch_bool_t connected;
char remote_ip[48];
uint16_t remote_port;
char local_ip[48];
uint16_t local_port;
erlang_pid pid;
uint32_t flags;
struct ei_event_stream_s *next;
};
typedef struct ei_event_stream_s ei_event_stream_t;
struct ei_node_s {
int nodefd;
switch_atomic_t pending_bgapi;
switch_atomic_t receive_handlers;
switch_memory_pool_t *pool;
ei_event_stream_t *event_streams;
switch_mutex_t *event_streams_mutex;
switch_queue_t *send_msgs;
switch_queue_t *received_msgs;
char *peer_nodename;
switch_time_t created_time;
switch_socket_t *socket;
char remote_ip[48];
uint16_t remote_port;
char local_ip[48];
uint16_t local_port;
uint32_t flags;
struct ei_node_s *next;
};
typedef struct ei_node_s ei_node_t;
struct globals_s {
switch_memory_pool_t *pool;
switch_atomic_t threads;
switch_socket_t *acceptor;
struct ei_cnode_s ei_cnode;
switch_thread_rwlock_t *ei_nodes_lock;
ei_node_t *ei_nodes;
switch_xml_binding_t *config_fetch_binding;
switch_xml_binding_t *directory_fetch_binding;
switch_xml_binding_t *dialplan_fetch_binding;
switch_xml_binding_t *chatplan_fetch_binding;
switch_xml_binding_t *channels_fetch_binding;
switch_hash_t *event_filter;
int epmdfd;
int num_worker_threads;
switch_bool_t nat_map;
switch_bool_t ei_shortname;
int ei_compat_rel;
char *ip;
char *hostname;
char *ei_cookie;
char *ei_nodename;
char *kazoo_var_prefix;
int var_prefix_length;
uint32_t flags;
int send_all_headers;
int send_all_private_headers;
int connection_timeout;
int receive_timeout;
int receive_msg_preallocate;
int event_stream_preallocate;
int send_msg_batch;
short event_stream_framing;
switch_port_t port;
int config_filters_fetched;
int io_fault_tolerance;
};
typedef struct globals_s globals_t;
extern globals_t kazoo_globals;
/* kazoo_node.c */
switch_status_t new_kazoo_node(int nodefd, ErlConnect *conn);
/* kazoo_event_stream.c */
ei_event_stream_t *find_event_stream(ei_event_stream_t *event_streams, const erlang_pid *from);
ei_event_stream_t *new_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from);
switch_status_t remove_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from);
switch_status_t remove_event_streams(ei_event_stream_t **event_streams);
unsigned long get_stream_port(const ei_event_stream_t *event_stream);
switch_status_t add_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name);
switch_status_t remove_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name);
switch_status_t remove_event_bindings(ei_event_stream_t *event_stream);
/* kazoo_fetch_agent.c */
switch_status_t bind_fetch_agents();
switch_status_t unbind_fetch_agents();
switch_status_t remove_xml_clients(ei_node_t *ei_node);
switch_status_t add_fetch_handler(ei_node_t *ei_node, erlang_pid *from, switch_xml_binding_t *binding);
switch_status_t remove_fetch_handlers(ei_node_t *ei_node, erlang_pid *from);
switch_status_t fetch_reply(char *uuid_str, char *xml_str, switch_xml_binding_t *binding);
switch_status_t handle_api_command_streams(ei_node_t *ei_node, switch_stream_handle_t *stream);
/* kazoo_utils.c */
void close_socket(switch_socket_t **sock);
void close_socketfd(int *sockfd);
switch_socket_t *create_socket_with_port(switch_memory_pool_t *pool, switch_port_t port);
switch_socket_t *create_socket(switch_memory_pool_t *pool);
switch_status_t create_ei_cnode(const char *ip_addr, const char *name, struct ei_cnode_s *ei_cnode);
switch_status_t ei_compare_pids(const erlang_pid *pid1, const erlang_pid *pid2);
void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event);
void ei_encode_switch_event_headers_2(ei_x_buff *ebuf, switch_event_t *event, int decode);
void ei_link(ei_node_t *ei_node, erlang_pid * from, erlang_pid * to);
void ei_encode_switch_event(ei_x_buff * ebuf, switch_event_t *event);
int ei_helper_send(ei_node_t *ei_node, erlang_pid* to, ei_x_buff *buf);
int ei_decode_atom_safe(char *buf, int *index, char *dst);
int ei_decode_string_or_binary_limited(char *buf, int *index, int maxsize, char *dst);
int ei_decode_string_or_binary(char *buf, int *index, char **dst);
switch_hash_t *create_default_filter();
/* kazoo_commands.c */
void add_kz_commands(switch_loadable_module_interface_t **module_interface, switch_api_interface_t *api_interface);
@ -169,9 +34,23 @@ void add_kz_commands(switch_loadable_module_interface_t **module_interface, swit
/* kazoo_dptools.c */
void add_kz_dptools(switch_loadable_module_interface_t **module_interface, switch_application_interface_t *app_interface);
#define _ei_x_encode_string(buf, string) { ei_x_encode_binary(buf, string, strlen(string)); }
/* kazoo_api.c */
void add_cli_api(switch_loadable_module_interface_t **module_interface, switch_api_interface_t *api_interface);
void remove_cli_api();
/* kazoo_utils.c */
char *kazoo_expand_header(switch_memory_pool_t *pool, switch_event_t *event, char *val);
char* switch_event_get_first_of(switch_event_t *event, const char *list[]);
SWITCH_DECLARE(switch_status_t) switch_event_add_variable_name_printf(switch_event_t *event, switch_stack_t stack, const char *val, const char *fmt, ...);
void kz_xml_process(switch_xml_t cfg);
/* kazoo_tweaks.c */
void kz_tweaks_start();
void kz_tweaks_stop();
SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown);
void fetch_config_filters();
/* For Emacs:
* Local Variables: