]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-10820 [mod_kazoo] eventstream configuration
authorlazedo <luis.azedo@factorlusitano.com>
Thu, 30 Nov 2017 19:29:27 +0000 (19:29 +0000)
committerlazedo <luis.azedo@factorlusitano.com>
Mon, 14 Jan 2019 14:24:51 +0000 (14:24 +0000)
21 files changed:
src/mod/event_handlers/mod_kazoo/Makefile.am
src/mod/event_handlers/mod_kazoo/kazoo.conf.xml [new file with mode: 0644]
src/mod/event_handlers/mod_kazoo/kazoo_api.c [new file with mode: 0644]
src/mod/event_handlers/mod_kazoo/kazoo_commands.c
src/mod/event_handlers/mod_kazoo/kazoo_config.c [new file with mode: 0644]
src/mod/event_handlers/mod_kazoo/kazoo_config.h [new file with mode: 0644]
src/mod/event_handlers/mod_kazoo/kazoo_definitions.S [new file with mode: 0644]
src/mod/event_handlers/mod_kazoo/kazoo_dptools.c
src/mod/event_handlers/mod_kazoo/kazoo_ei.h [new file with mode: 0644]
src/mod/event_handlers/mod_kazoo/kazoo_ei_config.c [new file with mode: 0644]
src/mod/event_handlers/mod_kazoo/kazoo_ei_utils.c [new file with mode: 0644]
src/mod/event_handlers/mod_kazoo/kazoo_event_stream.c
src/mod/event_handlers/mod_kazoo/kazoo_fetch_agent.c
src/mod/event_handlers/mod_kazoo/kazoo_fields.h [new file with mode: 0644]
src/mod/event_handlers/mod_kazoo/kazoo_message.c [new file with mode: 0644]
src/mod/event_handlers/mod_kazoo/kazoo_message.h [new file with mode: 0644]
src/mod/event_handlers/mod_kazoo/kazoo_node.c
src/mod/event_handlers/mod_kazoo/kazoo_tweaks.c [new file with mode: 0644]
src/mod/event_handlers/mod_kazoo/kazoo_utils.c
src/mod/event_handlers/mod_kazoo/mod_kazoo.c
src/mod/event_handlers/mod_kazoo/mod_kazoo.h

index 53fd7112e139a69925d92dc5bd5ef3ff797903aa..96b5c6cc30081dd968c827911f3dcebc4b286f6e 100644 (file)
@@ -3,12 +3,29 @@ MODNAME=mod_kazoo
 
 if HAVE_ERLANG
 
+KAZOO_DEFS=kazoo_definitions.o
+
 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_LIBADD   = $(KAZOO_DEFS) $(switch_builddir)/libfreeswitch.la
 mod_kazoo_la_LDFLAGS  = -avoid-version -module -no-undefined -shared @ERLANG_LDFLAGS@
 
+BUILT_SOURCES = $(KAZOO_DEFS)
+
+$(KAZOO_DEFS): kazoo.conf.xml
+
+.S.o: $<
+       @$(CC) $(CFLAGS) -o $@ -c $<
+
+install-exec-am:
+       @install `which epmd` $(DESTDIR)$(bindir)/fs_epmd
+
 else
 install: error
 all: error
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo.conf.xml b/src/mod/event_handlers/mod_kazoo/kazoo.conf.xml
new file mode 100644 (file)
index 0000000..b1a6756
--- /dev/null
@@ -0,0 +1,1160 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration name="kazoo.conf"
+       description="General purpose Erlang C-node produced to better fit the Kazoo project">
+       <settings>
+               <param name="listen-ip" value="0.0.0.0" />
+               <param name="listen-port" value="6031" />
+               <!--<param name="cookie-file" value="/etc/freeswitch/autoload_configs/.erlang.cookie" 
+                       /> -->
+               <param name="cookie" value="change_me" />
+               <param name="shortname" value="false" />
+               <param name="nodename" value="freeswitch" />
+               <!--<param name="kazoo-var-prefix" value="ecallmgr" /> -->
+               <!--<param name="compat-rel" value="12"/> -->
+               <param name="send-all-headers" value="false" />
+
+               <param name="event-stream-preallocate" value="8192" />
+               <param name="receive-msg-preallocate" value="8192" />
+               <param name="node-worker-threads" value="5" />
+               <param name="receive-timeout" value="1" />
+               <param name="event-stream-framing" value="2" />
+
+       </settings>
+
+       <X-PRE-PROCESS cmd="set" data="UNIX_EPOCH_IN_GREGORIAN=62167219200" />
+
+    <variables>
+        <variable name="UNIX_EPOCH_IN_GREGORIAN" value="62167219200"/>
+    </variables>
+
+    <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="Metaflow-Control">
+            <field name="Metaflow-Control" type="static" serialize-as="object">
+                <filters>
+                    <filter name="ecallmgr" type="include" compare="prefix"
+                        value="variable_Metaflow-Control-" />
+                </filters>
+                <fields verbose="false">
+                    <field name="variable_Metaflow-Control-" type="prefix" exclude-prefix="true" />
+                </fields>
+            </field>
+        </definition>
+
+        <definition name="Call-Control">
+            <field name="Call-Control" type="static" serialize-as="object">
+                <filters>
+                    <filter name="ecallmgr" type="include" compare="prefix"
+                        value="variable_Call-Control-" />
+                </filters>
+                <fields verbose="false">
+                    <field name="variable_Call-Control-" type="prefix" exclude-prefix="true" />
+                </fields>
+            </field>
+        </definition>
+
+        <definition name="Control">
+            <field name="Call-Control" type="reference" />
+            <field name="Metaflow-Control" type="reference" />
+        </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="${regex(${url_decode(${variable_sip_refer_to})}|&lt;sips?:(.*)&gt;|%1)}">
+                        <filters>
+                            <filter name="variable_sip_refer_to" type="include" compare="exists" />
+                        </filters>
+                    </field>
+                    <field name="Referred-By" type="expand" value="${regex(${variable_sip_h_Referred-By}|&lt;sips?:(.*)&gt;|%1)}">
+                        <filters>
+                            <filter name="variable_sip_h_Referred-By" type="include" compare="exists" />
+                        </filters>
+                    </field>
+
+                    <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="Unique-ID" type="include" compare="exists" />
+                            <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" />
+
+                    <field name="redirect" type="group">
+                        <filters>
+                            <!-- <filter name="variable_last_bridge_hangup_cause" type="include" compare="value" value="REDIRECTION_TO_NEW_DESTINATION" /> -->
+                            <filter name="variable_sip_redirected_by" type="include" compare="exists" />
+                        </filters>
+                        <fields verbose="false">
+                            <field name="Redirected-By" type="expand" value="${regex(${variable_sip_redirected_by}|&lt;sips?:(.*)&gt;|%1)}"/>
+                            <field name="Redirected-Reason" type="expand" value="${regex(${variable_sip_redirected_by}|reason=(.*)|%1)}"/>
+                        </fields>
+                    </field>
+
+                </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-UUID-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="1" />
+                </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="${first-of(variable_sip_to_uri|variable_sip_req_uri|#${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_uuid" as="Origination-Call-ID" />
+            <field name="Call-ID" type="first-of" value="Unique-ID|Caller-Unique-ID|caller-unique-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" />
+            <field name="Control" 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="Call-ID" type="first-of" value="Fetch-Call-UUID|Unique-ID" 
+                /> -->
+            <field name="Call-ID" type="first-of" value="Unique-ID" />
+            <field name="Fetch-Section" />
+            <field name="Fetch-Timeout" />
+            <field name="Fetch-Timestamp-Micro" />
+            <field name="Kazoo-Bundle" as="Fetch-Version" />
+            <field name="Custom-SIP-Headers" type="reference" />
+            <field name="Custom-Channel-Vars" type="reference" />
+            <field name="Custom-Application-Vars" type="reference" />
+            <field name="freeswitch-url" type="reference" />
+            <field name="Control" type="reference" />
+        </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|variable_sip_loopback_from_uri" />
+            <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_originate_failed_cause|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_originate_failed_cause|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-caller-id">
+            <field name="CONF-CALLER-ID-IN" type="group">
+                <filters>
+                    <filter name="Call-Direction" type="include" compare="value" value="inbound" />
+                </filters>
+                <fields verbose="false">
+                    <field name="Caller-Caller-ID-Name" as="Caller-ID-Name" />
+                    <field name="Caller-Caller-ID-Number" as="Caller-ID-Number" />
+                </fields>
+            </field>
+            <field name="CONF-CALLER-ID-OUT" type="group">
+                <filters>
+                    <filter name="Call-Direction" type="include" compare="value" value="outbound" />
+                </filters>
+                <fields verbose="false">
+                    <field name="variable_origination_callee_id_name" as="Caller-ID-Name" />
+                    <field name="variable_origination_callee_id_number" as="Caller-ID-Number" />
+                </fields>
+            </field>
+        </definition>
+
+        <definition name="conference-bgdial">
+            <field name="Dial-Result" type="static" serialize-as="object">
+                <filters>
+                    <filter name="Action" type="include" compare="value" value="bgdial-result" />
+                </filters>
+                <fields verbose="false">
+                    <field name="Result"/>
+                    <field name="Job-UUID"/>
+                    <field name="Peer-UUID"/>
+                </fields>
+            </field>
+        </definition>
+
+        <definition name="conference-vars">
+            <field name="Conference-Vars" type="static"
+                serialize-as="object">
+                <fields verbose="false">
+                    <field name="Conference-" type="prefix" exclude-prefix="true" />
+                </fields>
+            </field>
+        </definition>
+
+        <definition name="conference-channel-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="Conference-Node" />
+            <field name="Conference-Profile" as="Profile" />
+            <field name="Unique-ID" as="Call-ID" />
+            <field name="Account-ID" type="first-of"
+                value="Conference-Account-ID|variable_ecallmgr_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="variable_origination_callee_id_name" as="Callee-ID-Name" />
+            <field name="variable_origination_callee_id_number" as="Callee-ID-Number" />
+            <field name="call-direction" type="reference" />
+            -->
+            
+            <field name="conference-caller-id" type="reference" />
+            
+            <field name="Channel-Presence-ID" />
+            <field name="Member-ID" as="Participant-ID" serialize-as="number" />
+            <field name="Custom-Channel-Vars" type="reference" />
+            <field name="Custom-Application-Vars" type="reference" />
+            <field name="conference-vars" type="reference" />
+            <field name="conference-channel-vars" type="reference" />
+            <field name="conference-bgdial" type="reference" />
+        </definition>
+
+        <definition name="recording_vars">
+            <field name="Recording" type="static" serialize-as="object">
+                <filters>
+                    <filter name="recording" type="include" compare="prefix"
+                        value="Recording-Variable-" />
+                </filters>
+                <fields verbose="false">
+                    <field name="Recording-Variable-" type="prefix" exclude-prefix="true" />
+                </fields>
+            </field>
+        </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" />
+            <field name="recording_vars" type="reference" />
+        </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_t38_status" as="Fax-T38-Status" />
+            <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>
+
+
+        <definition name="voice_dialplan">
+                <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="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" />
+        </definition>
+
+        <definition name="metaflow_dialplan">
+                <field name="metaflow" type="group">
+                    <filters>
+                        <filter name="Hunt-Context" type="include" compare="value"
+                            value="metaflow" />
+                    </filters>
+                    <fields verbose="false">
+                        <field name="Hunt-Context" as="Context" />
+                        <field name="Resource-Type" type="static" value="metaflow" />
+                        <!--
+                        <field name="variable_Call-Control-Queue" as="Metaflow-Control-Queue" />
+                        <field name="variable_Call-Control-PID" as="Metaflow-Control-PID" />
+                        -->
+                        <field name="variable_Metaflow-Control-Queue" as="Metaflow-Control-Queue" />
+                        <field name="variable_Metaflow-Control-PID" as="Metaflow-Control-PID" />
+                        <field name="Custom-Routing-Headers" type="static"
+                            serialize-as="object">
+                            <fields verbose="false">
+                                <field name="Metaflow-Request-Type" type="static" value="in-call" />
+                                <field name="Other-Leg-Call-ID" type="first-of" value="variable_Application-Other-Leg-UUID|Other-Leg-Unique-ID" />
+                                <field name="Hunt-Destination-Number" as="Metaflow-Request" />
+                            </fields>
+                        </field>
+                    </fields>
+
+                </field>        
+        </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="to-did" type="reference" />
+                                               <field name="destination-number" 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="other-leg" type="reference" />
+                                               <field name="sdp" type="reference" />
+                                               <field name="call-duration" type="reference" />
+                                               <field name="hangup-fields" type="reference" />
+                                               <field name="variable_Media-Recordings" as="Media-Recordings" />
+                                               <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="Custom-Channel-Vars" type="reference" />
+                                               <field name="Custom-Application-Vars" 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" />
+                                               <field name="Custom-Channel-Vars" type="reference" />
+                                               <field name="Custom-Application-Vars" 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="exclude" compare="value"
+                            name="Application-UUID" value="null" />
+                                               <filter type="include" compare="exists"
+                                                       name="variable_Call-Control-Queue" />
+                        <filter type="include" compare="exists"
+                            name="Application-UUID-Name" />
+                                               <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="Application-Response|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" />
+                                               <field name="Call-ID" type="first-of" value="variable_sip_origination_call_uuid|Unique-ID" />
+                                       </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" />
+                        <field name="Call-ID" type="first-of" value="variable_sip_origination_call_uuid|Unique-ID" />
+                                       </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" />
+                        <field name="Call-ID" type="first-of" value="variable_sip_origination_call_uuid|Unique-ID" />
+                                       </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">
+                                   <filters>
+                        <filter type="exclude" compare="exists" name="API-Command" />
+                    </filters>
+                                       <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="false">
+                                               <field name="call_event" type="reference" />
+                                               <field name="Custom-Channel-Vars" type="reference" />
+                                               <field name="Custom-Application-Vars" 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" /> -->
+                        <field name="call_event" type="reference" />
+                        <field name="Event-Name" type="static" value="CHANNEL_REPLACED"/>                    
+                        <field name="Resigning-UUID" as="Call-ID"/>
+                        <field name="Resigning-UUID"/>
+                        <field name="Resigning-Peer-UUID"/>
+                        <field name="Acquired-UUID"/>
+                        <field name="Acquired-UUID" as="Replaced-By"/>
+                                       </fields>
+                               </event>
+
+                <event name="loopback::direct">
+                    <fields verbose="true">
+                        <!-- <field name="call_event_headers" type="reference" /> -->
+                        <field name="call_event" type="reference" />
+                        <field name="Event-Name" type="static" value="CHANNEL_DIRECT"/>
+                        <field name="Resigning-UUID" as="Call-ID"/>
+                        <field name="Resigning-UUID"/>
+                        <field name="Resigning-Peer-UUID"/>
+                        <field name="Acquired-UUID"/>
+                        <field name="Acquired-UUID" as="Replaced-By"/>
+                    </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 verbose="false">
+                <field name="voice_dialplan" type="reference" />
+                <field name="metaflow_dialplan" type="reference" />
+                <field name="debug-call" type="reference" />
+            </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="channel_req" />
+                       </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="language_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>
+
+</configuration>
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_api.c b/src/mod/event_handlers/mod_kazoo/kazoo_api.c
new file mode 100644 (file)
index 0000000..b070f86
--- /dev/null
@@ -0,0 +1,551 @@
+/*
+ * 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
+#define API_COMMAND_OPTION 4
+
+#define API_NODE_OPTION_FRAMING 0
+#define API_NODE_OPTION_LEGACY 1
+#define API_NODE_OPTION_MAX 99
+
+static const char *node_runtime_options[] = {
+               "event-stream-framing",
+               "enable-legacy",
+               NULL
+};
+
+static int api_find_node_option(char *option) {
+       int i;
+       for(i = 0; node_runtime_options[i] != NULL; i++) {
+               if(!strcasecmp(option, node_runtime_options[i])) {
+                       return i;
+               }
+       }
+       return API_NODE_OPTION_MAX;
+}
+
+static switch_status_t api_get_node_option(ei_node_t *ei_node, switch_stream_handle_t *stream, char *arg) {
+       int option = api_find_node_option(arg);
+       switch_status_t ret = SWITCH_STATUS_SUCCESS;
+       switch (option) {
+       case API_NODE_OPTION_FRAMING:
+               stream->write_function(stream, "+OK %i", ei_node->event_stream_framing);
+               break;
+
+       case API_NODE_OPTION_LEGACY:
+               stream->write_function(stream, "+OK %s", ei_node->legacy ? "true" : "false");
+               break;
+
+       default:
+               stream->write_function(stream, "-ERR invalid option %s", arg);
+               ret = SWITCH_STATUS_NOTFOUND;
+               break;
+       }
+
+       return ret;
+}
+
+static switch_status_t api_set_node_option(ei_node_t *ei_node, switch_stream_handle_t *stream, char *name, char *value) {
+       int option = api_find_node_option(name);
+       short val;
+       switch_status_t ret = SWITCH_STATUS_SUCCESS;
+       switch (option) {
+       case API_NODE_OPTION_FRAMING:
+               val = atoi(value);
+               if (val != 1 && val != 2 && val != 4) {
+                       stream->write_function(stream, "-ERR Invalid event stream framing value (%i)", val);
+                       ret = SWITCH_STATUS_GENERR;
+               } else {
+                       stream->write_function(stream, "+OK %i", val);
+                       ei_node->event_stream_framing = val;
+               }
+               break;
+
+       case API_NODE_OPTION_LEGACY:
+               ei_node->legacy = switch_true(value);
+               stream->write_function(stream, "+OK %s", ei_node->legacy ? "true" : "false");
+               break;
+
+       default:
+               stream->write_function(stream, "-ERR invalid option %s", name);
+               ret = SWITCH_STATUS_NOTFOUND;
+               break;
+       }
+
+       return ret;
+}
+
+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;
+       int idx = 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;
+       }
+
+       while(kazoo_globals.kazoo_var_prefixes[idx] != NULL) {
+               char var[100];
+               char *prefix = kazoo_globals.kazoo_var_prefixes[idx];
+               sprintf(var, "%s*", prefix);
+               stream->write_function(stream, "%-50s", var);
+               idx++;
+       }
+
+
+       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 switch_status_t handle_node_api_command_arg(ei_node_t *ei_node, switch_stream_handle_t *stream, uint32_t command, char *arg) {
+
+       switch (command) {
+       case API_COMMAND_OPTION:
+               return api_get_node_option(ei_node, stream, arg);
+               break;
+       default:
+               break;
+       }
+
+       return SWITCH_STATUS_NOTFOUND;
+}
+
+static switch_status_t handle_node_api_command_args(ei_node_t *ei_node, switch_stream_handle_t *stream, uint32_t command, int argc, char *argv[]) {
+
+       switch (command) {
+       case API_COMMAND_OPTION:
+               return api_set_node_option(ei_node, stream, argv[0], argv[1]);
+               break;
+       default:
+               break;
+       }
+
+       return SWITCH_STATUS_NOTFOUND;
+}
+
+static switch_status_t api_erlang_node_command_arg(switch_stream_handle_t *stream, const char *nodename, uint32_t command, char *arg) {
+       ei_node_t *ei_node;
+       switch_status_t ret = SWITCH_STATUS_NOTFOUND;
+
+       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)) {
+                       ret = handle_node_api_command_arg(ei_node, stream, command, arg);
+                       switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
+                       return ret ;
+               }
+
+               ei_node = ei_node->next;
+       }
+       switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
+
+       return ret;
+}
+
+static switch_status_t api_erlang_node_command_args(switch_stream_handle_t *stream, const char *nodename, uint32_t command, int argc, char *argv[]) {
+       ei_node_t *ei_node;
+       switch_status_t ret = SWITCH_STATUS_NOTFOUND;
+
+       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)) {
+                       ret = handle_node_api_command_args(ei_node, stream, command, argc, argv);
+                       switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
+                       return ret;
+               }
+
+               ei_node = ei_node->next;
+       }
+       switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
+
+       return ret;
+}
+
+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", 7)) {
+               unknown_command = 0;
+               api_erlang_status(stream);
+       } else if (!strncmp(argv[0], "event_filter", 13)) {
+               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", 11)) {
+                       unknown_command = 0;
+                       api_erlang_node_command(stream, argv[1], API_COMMAND_DISCONNECT);
+               } else if (!strncmp(argv[2], "connection", 11)) {
+                       unknown_command = 0;
+                       api_erlang_node_command(stream, argv[1], API_COMMAND_REMOTE_IP);
+               } else if (!strncmp(argv[2], "event_streams", 14)) {
+                       unknown_command = 0;
+                       api_erlang_node_command(stream, argv[1], API_COMMAND_STREAMS);
+               } else if (!strncmp(argv[2], "fetch_bindings", 15)) {
+                       unknown_command = 0;
+                       api_erlang_node_command(stream, argv[1], API_COMMAND_BINDINGS);
+               } else if (!strncmp(argv[2], "option", 7) && !zstr(argv[3])) {
+                       unknown_command = 0;
+                       if(argc > 4 && !zstr(argv[4]))
+                               api_erlang_node_command_args(stream, argv[1], API_COMMAND_OPTION, argc - 3, &argv[3]);
+                       else
+                               api_erlang_node_command_arg(stream, argv[1], API_COMMAND_OPTION, argv[3]);
+               }
+       }
+
+       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:
+ */
index 2494bb9495d3bd9287c7cefd5b811124a0f30207..017c99959a65f387becd23a900fa217befcaa811 100644 (file)
@@ -31,6 +31,7 @@
  *
  */
 #include "mod_kazoo.h"
+#include <curl/curl.h>
 #include <switch_curl.h>
 
 #define UUID_SET_DESC "Set a variable"
 #define KZ_HTTP_PUT_DESC "upload a local freeswitch file to a url"
 #define KZ_HTTP_PUT_SYNTAX "localfile url"
 
-SWITCH_STANDARD_API(uuid_setvar_function) {
+#define KZ_FIRST_OF_DESC "returns first-of existing event header in params"
+#define KZ_FIRST_OF_SYNTAX "list of headers to check"
+
+#define MAX_FIRST_OF 25
+
+SWITCH_STANDARD_API(kz_first_of) {
+       char delim = '|';
+       char *mycmd = NULL, *argv[MAX_FIRST_OF] = { 0 };
+       int n, argc = 0;
+       switch_event_header_t *header = NULL;
+       if (!zstr(cmd) && (mycmd = strdup(cmd))) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "FIRST-OF %s\n", mycmd);
+               if (!zstr(mycmd) && *mycmd == '^' && *(mycmd+1) == '^') {
+                       mycmd += 2;
+                       delim = *mycmd++;
+               }
+               argc = switch_separate_string(mycmd, delim, argv, (sizeof(argv) / sizeof(argv[0])));
+               for(n=0; n < argc; n++) {
+                       char* item = argv[n];
+                       if(*item == '#') {
+                               if(*(++item) != '\0') {
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "RETURNING default %s\n", item);
+                                       stream->write_function(stream, item);
+                                       break;
+                               }
+                       } else {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "CHECKING %s\n", item);
+                               header = switch_event_get_header_ptr(stream->param_event, item);
+                               if(header) {
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "RETURNING %s : %s\n", item, header->value);
+                                       stream->write_function(stream, header->value);
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       switch_safe_free(mycmd);
+       return SWITCH_STATUS_SUCCESS;
+}
+
+switch_status_t kz_uuid_setvar(int urldecode, const char *cmd,  switch_core_session_t *session,  switch_stream_handle_t *stream)
+{
        switch_core_session_t *psession = NULL;
        char *mycmd = NULL, *argv[3] = { 0 };
        int argc = 0;
@@ -67,7 +110,11 @@ SWITCH_STANDARD_API(uuid_setvar_function) {
                                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No variable name specified.\n");
                                        stream->write_function(stream, "-ERR No variable specified\n");
                                } else {
+                                       if(urldecode) {
+                                               switch_url_decode(var_value);
+                                       }
                                        switch_channel_set_variable(channel, var_name, var_value);
+                                       kz_check_set_profile_var(channel, var_name, var_value);
                                        stream->write_function(stream, "+OK\n");
                                }
 
@@ -92,8 +139,20 @@ SWITCH_STANDARD_API(uuid_setvar_function) {
        return SWITCH_STATUS_SUCCESS;
 }
 
-SWITCH_STANDARD_API(uuid_setvar_multi_function) {
+SWITCH_STANDARD_API(uuid_setvar_function)
+{
+       return kz_uuid_setvar(0, cmd, session, stream);
+}
+
+SWITCH_STANDARD_API(uuid_setvar_encoded_function)
+{
+       return kz_uuid_setvar(1, cmd, session, stream);
+}
+
+switch_status_t kz_uuid_setvar_multi(int urldecode, const char *cmd,  switch_core_session_t *session,  switch_stream_handle_t *stream)
+{
        switch_core_session_t *psession = NULL;
+       char delim = ';';
        char *mycmd = NULL, *vars, *argv[64] = { 0 };
        int argc = 0;
        char *var_name, *var_value = NULL;
@@ -104,12 +163,15 @@ SWITCH_STANDARD_API(uuid_setvar_multi_function) {
                        goto done;
                }
                *vars++ = '\0';
-
+               if (*vars == '^' && *(vars+1) == '^') {
+                       vars += 2;
+                       delim = *vars++;
+               }
                if ((psession = switch_core_session_locate(uuid))) {
                        switch_channel_t *channel = switch_core_session_get_channel(psession);
                        switch_event_t *event;
                        int x, y = 0;
-                       argc = switch_separate_string(vars, ';', argv, (sizeof(argv) / sizeof(argv[0])));
+                       argc = switch_separate_string(vars, delim, argv, (sizeof(argv) / sizeof(argv[0])));
 
                        for (x = 0; x < argc; x++) {
                                var_name = argv[x];
@@ -120,16 +182,11 @@ SWITCH_STANDARD_API(uuid_setvar_multi_function) {
                                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No variable name specified.\n");
                                        stream->write_function(stream, "-ERR No variable specified\n");
                                } else {
+                                       if(urldecode) {
+                                               switch_url_decode(var_value);
+                                       }
                                        switch_channel_set_variable(channel, var_name, var_value);
-                                       if (!strcasecmp(var_name, "effective_callee_id_number")) {
-                                               switch_channel_set_profile_var(channel, "callee_id_number", var_value);
-                                       } else if (!strcasecmp(var_name, "effective_callee_id_name")) {
-                                               switch_channel_set_profile_var(channel, "callee_id_name", var_value);
-                                       } else if (!strcasecmp(var_name, "effective_caller_id_number")) {
-                                               switch_channel_set_profile_var(channel, "caller_id_number", var_value);
-                                       } else if (!strcasecmp(var_name, "effective_caller_id_name")) {
-                                               switch_channel_set_profile_var(channel, "caller_id_name", var_value);
-                                       };
+                                       kz_check_set_profile_var(channel, var_name, var_value);
 
                                        y++;
                                }
@@ -158,6 +215,21 @@ SWITCH_STANDARD_API(uuid_setvar_multi_function) {
        return SWITCH_STATUS_SUCCESS;
 }
 
+SWITCH_STANDARD_API(uuid_setvar_multi_function)
+{
+       return kz_uuid_setvar_multi(0, cmd, session, stream);
+}
+
+SWITCH_STANDARD_API(uuid_setvar_multi_encoded_function)
+{
+       return kz_uuid_setvar_multi(1, cmd, session, stream);
+}
+
+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 +254,7 @@ SWITCH_STANDARD_API(kz_http_put)
        switch_event_t *params = NULL;
        char *url = NULL;
        char *filename = NULL;
+       int delete_file = 0;
 
        switch_curl_slist_t *headers = NULL;  /* optional linked-list of HTTP headers */
        char *ext;  /* file extension, used for MIME type identification */
@@ -276,9 +349,12 @@ SWITCH_STANDARD_API(kz_http_put)
        switch_curl_easy_setopt(curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size);
        switch_curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
        switch_curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 10);
+       switch_curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
        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 +362,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_file = 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_file) {
+                       remove(filename);
+               }
        }
 
        if (headers) {
@@ -326,9 +404,14 @@ done:
 
 void add_kz_commands(switch_loadable_module_interface_t **module_interface, switch_api_interface_t *api_interface) {
        SWITCH_ADD_API(api_interface, "kz_uuid_setvar_multi", UUID_SET_DESC, uuid_setvar_multi_function, UUID_MULTISET_SYNTAX);
+       SWITCH_ADD_API(api_interface, "kz_uuid_setvar_multi_encoded", UUID_SET_DESC, uuid_setvar_multi_encoded_function, UUID_MULTISET_SYNTAX);
        switch_console_set_complete("add kz_uuid_setvar_multi ::console::list_uuid");
+       switch_console_set_complete("add kz_uuid_setvar_multi_encoded ::console::list_uuid");
        SWITCH_ADD_API(api_interface, "kz_uuid_setvar", UUID_MULTISET_DESC, uuid_setvar_function, UUID_SET_SYNTAX);
+       SWITCH_ADD_API(api_interface, "kz_uuid_setvar_encoded", UUID_MULTISET_DESC, uuid_setvar_encoded_function, UUID_SET_SYNTAX);
        switch_console_set_complete("add kz_uuid_setvar ::console::list_uuid");
+       switch_console_set_complete("add kz_uuid_setvar_encoded ::console::list_uuid");
        SWITCH_ADD_API(api_interface, "kz_http_put", KZ_HTTP_PUT_DESC, kz_http_put, KZ_HTTP_PUT_SYNTAX);
+       SWITCH_ADD_API(api_interface, "first-of", KZ_FIRST_OF_DESC, kz_first_of, KZ_FIRST_OF_SYNTAX);
 }
 
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_config.c b/src/mod/event_handlers/mod_kazoo/kazoo_config.c
new file mode 100644 (file)
index 0000000..5608b10
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+* 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;
+       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
+ */
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_config.h b/src/mod/event_handlers/mod_kazoo/kazoo_config.h
new file mode 100644 (file)
index 0000000..d0117fd
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+* 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);
+switch_status_t kazoo_config_field(kazoo_config_ptr definitions, switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_field_ptr *ptr);
+void destroy_config(kazoo_config_ptr *ptr);
+
+#endif /* KAZOO_CONFIG_H */
+
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_definitions.S b/src/mod/event_handlers/mod_kazoo/kazoo_definitions.S
new file mode 100644 (file)
index 0000000..0bbe013
--- /dev/null
@@ -0,0 +1,8 @@
+    .global kz_default_config
+    .global kz_default_config_size
+    .section .rodata
+kz_default_config:
+    .incbin "kazoo.conf.xml"
+1:
+kz_default_config_size:
+    .int 1b - kz_default_config
index 0ea4cf6f2c9aee92529d32038d9cceabf6e4f792..fe555f95bb3f79318f60823ddcc609956c3642cb 100644 (file)
 #define EXPORT_LONG_DESC "Export many channel variables for the channel calling the application"
 #define EXPORT_SYNTAX "[^^<delim>]<varname>=<value> <var2>=<val2>"
 
-static void base_set (switch_core_session_t *session, const char *data, switch_stack_t stack) {
+#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>"
+
+#define KZ_ENDLESS_PLAYBACK_SHORT_DESC "Playback File Endlessly until break"
+#define KZ_ENDLESS_PLAYBACK_LONG_DESC "Endlessly Playback a file to the channel until a break occurs"
+#define KZ_ENDLESS_PLAYBACK_SYNTAX "<path>"
+
+#define NOOP_SHORT_DESC "no operation"
+#define NOOP_LONG_DESC "no operation. serves as a control point"
+#define NOOP_SYNTAX "[<noop-id>]"
+
+static void base_set (switch_core_session_t *session, const char *data, int urldecode, switch_stack_t stack)
+{
        char *var, *val = NULL;
 
        if (zstr(data)) {
@@ -75,31 +92,40 @@ static void base_set (switch_core_session_t *session, const char *data, switch_s
                }
 
                if (val) {
+                       if(urldecode) {
+                               switch_url_decode(val);
+                       }
                        expanded = switch_channel_expand_variables(channel, val);
                }
 
                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s SET [%s]=[%s]\n", switch_channel_get_name(channel), var,
                                                  expanded ? expanded : "UNDEF");
                switch_channel_add_variable_var_check(channel, var, expanded, SWITCH_FALSE, stack);
-
-               if (!strcasecmp(var, "effective_callee_id_number")) {
-                       switch_channel_set_profile_var(channel, "callee_id_number", expanded);
-               } else if (!strcasecmp(var, "effective_callee_id_name")) {
-                       switch_channel_set_profile_var(channel, "callee_id_name", expanded);
-               } else if (!strcasecmp(var, "effective_caller_id_number")) {
-                       switch_channel_set_profile_var(channel, "caller_id_number", expanded);
-               } else if (!strcasecmp(var, "effective_caller_id_name")) {
-                       switch_channel_set_profile_var(channel, "caller_id_name", expanded);
-               };
-
-
+               kz_check_set_profile_var(channel, var, expanded);
                if (expanded && expanded != val) {
                        switch_safe_free(expanded);
                }
        }
 }
 
-static void base_export (switch_core_session_t *session, const char *data, switch_stack_t stack) {
+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, int urldecode, switch_stack_t stack)
+{
         char *var, *val = NULL;
 
         if (zstr(data)) {
@@ -121,21 +147,57 @@ static void base_export (switch_core_session_t *session, const char *data, switc
                         }
                 }
 
-                if (val) {
-                        expanded = switch_channel_expand_variables(channel, val);
-                }
+                               if (val) {
+                                       if(urldecode) {
+                                               switch_url_decode(val);
+                                       }
+                                       expanded = switch_channel_expand_variables(channel, val);
+
+                                       if(!kz_is_exported(session, var)) {
+                                               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);
+                                       } else {
+                                               if(strcmp(switch_str_nil(switch_channel_get_variable_dup(channel, var, SWITCH_FALSE, -1)), expanded)) {
+                                                       switch_channel_set_variable(channel, var, expanded);
+                                               }
+                                       }
+                                       if (expanded && expanded != val) {
+                                               switch_safe_free(expanded);
+                                       }
+                               }
+        }
+}
+
+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;
 
-                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(switch_event_create(&clear, SWITCH_EVENT_CLONE) != SWITCH_STATUS_SUCCESS) {
+               return;
+       }
 
-                if (expanded && expanded != val) {
-                        switch_safe_free(expanded);
-                }
-        }
+       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) {
+void kz_multiset(switch_core_session_t *session, const char* data, int urldecode)
+{
        char delim = ' ';
        char *arg = (char *) data;
        switch_event_t *event;
@@ -145,33 +207,107 @@ 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], urldecode, 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 {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "multiset with empty args\n");
+       }
+}
+
+SWITCH_STANDARD_APP(multiset_function)
+{
+       kz_multiset(session, data, 0);
+}
+
+SWITCH_STANDARD_APP(multiset_encoded_function)
+{
+       kz_multiset(session, data, 1);
+}
+
+void kz_uuid_multiset(switch_core_session_t *session, const char* data, int urldecode)
+{
+       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], urldecode, 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, urldecode, SWITCH_STACK_BOTTOM);
+               }
        } 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(set_function) {
+SWITCH_STANDARD_APP(uuid_multiset_function)
+{
+       kz_uuid_multiset(session, data, 0);
+}
+
+SWITCH_STANDARD_APP(uuid_multiset_encoded_function)
+{
+       kz_uuid_multiset(session, data, 1);
+}
+
+void kz_set(switch_core_session_t *session, const char* data, int urldecode) {
        switch_channel_t *channel = switch_core_session_get_channel(session);
        switch_event_t *event;
 
-       base_set(session, data, SWITCH_STACK_BOTTOM);
+       base_set(session, data, urldecode, SWITCH_STACK_BOTTOM);
 
        if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
                switch_channel_event_set_data(channel, event);
@@ -179,6 +315,16 @@ SWITCH_STANDARD_APP(set_function) {
        }
 }
 
+SWITCH_STANDARD_APP(set_function)
+{
+       kz_set(session, data, 0);
+}
+
+SWITCH_STANDARD_APP(set_encoded_function)
+{
+       kz_set(session, data, 1);
+}
+
 SWITCH_STANDARD_APP(unset_function) {
        switch_channel_t *channel = switch_core_session_get_channel(session);
        switch_event_t *event;
@@ -205,55 +351,125 @@ 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");
        }
 }
 
-SWITCH_STANDARD_APP(export_function) {
-        char delim = ' ';
-        char *arg = (char *) data;
 
-        if (!zstr(arg) && *arg == '^' && *(arg+1) == '^') {
-                arg += 2;
-                delim = *arg++;
-        }
+void kz_export(switch_core_session_t *session, const char* data, int urldecode)
+{
+    char delim = ' ';
+    char *arg = (char *) data;
 
-        if (arg) {
-                char *array[256] = {0};
-                int i, argc;
+    if (!zstr(arg) && *arg == '^' && *(arg+1) == '^') {
+            arg += 2;
+            delim = *arg++;
+    }
 
-                arg = switch_core_session_strdup(session, arg);
-                argc = switch_split(arg, delim, array);
+    if(delim != '\0') {
+               if (arg) {
+                               char *array[256] = {0};
+                               int i, argc;
 
-                for(i = 0; i < argc; i++) {
-                        base_export(session, array[i], SWITCH_STACK_BOTTOM);
-                }
-        } else {
-                base_export(session, data, SWITCH_STACK_BOTTOM);
-        }
+                               arg = switch_core_session_strdup(session, arg);
+                               argc = switch_split(arg, delim, array);
+
+                               for(i = 0; i < argc; i++) {
+                                               base_export(session, array[i], urldecode, SWITCH_STACK_BOTTOM);
+                               }
+               } else {
+                               base_export(session, data, urldecode, SWITCH_STACK_BOTTOM);
+               }
+    } else {
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "export with empty args\n");
+    }
+}
+
+SWITCH_STANDARD_APP(export_function)
+{
+       kz_export(session, data, 0);
+}
+
+SWITCH_STANDARD_APP(export_encoded_function)
+{
+       kz_export(session, data, 1);
+}
+
+// copied from mod_dptools with allow SWITCH_STATUS_BREAK
+SWITCH_STANDARD_APP(kz_endless_playback_function)
+{
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
+       const char *file = data;
+
+       while (switch_channel_ready(channel)) {
+               status = switch_ivr_play_file(session, NULL, file, NULL);
+
+               if (status != SWITCH_STATUS_SUCCESS) {
+                       break;
+               }
+       }
+
+       switch (status) {
+       case SWITCH_STATUS_SUCCESS:
+               switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "FILE PLAYED");
+               break;
+       case SWITCH_STATUS_BREAK:
+               switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "PLAYBACK_INTERRUPTED");
+               break;
+       case SWITCH_STATUS_NOTFOUND:
+               switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "FILE NOT FOUND");
+               break;
+       default:
+               switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "PLAYBACK ERROR");
+               break;
+       }
+
+}
+
+SWITCH_STANDARD_APP(noop_function)
+{
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
+       const char *response = uuid_str;
+
+       if (zstr(data)) {
+               switch_uuid_str(uuid_str, sizeof(uuid_str));
+       } else {
+               response = data;
+       }
+
+       switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, response);
 }
 
 void add_kz_dptools(switch_loadable_module_interface_t **module_interface, switch_application_interface_t *app_interface) {
-       SWITCH_ADD_APP(app_interface, "kz_set", SET_SHORT_DESC, SET_LONG_DESC, set_function, SET_SYNTAX,
-                                  SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
-       SWITCH_ADD_APP(app_interface, "kz_multiset", MULTISET_SHORT_DESC, MULTISET_LONG_DESC, multiset_function, MULTISET_SYNTAX,
-                                  SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
-       SWITCH_ADD_APP(app_interface, "kz_unset", UNSET_SHORT_DESC, UNSET_LONG_DESC, unset_function, UNSET_SYNTAX,
-                                  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_set", SET_SHORT_DESC, SET_LONG_DESC, set_function, SET_SYNTAX, SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
+       SWITCH_ADD_APP(app_interface, "kz_set_encoded", SET_SHORT_DESC, SET_LONG_DESC, set_encoded_function, SET_SYNTAX, SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
+       SWITCH_ADD_APP(app_interface, "kz_multiset", MULTISET_SHORT_DESC, MULTISET_LONG_DESC, multiset_function, MULTISET_SYNTAX, SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
+       SWITCH_ADD_APP(app_interface, "kz_multiset_encoded", MULTISET_SHORT_DESC, MULTISET_LONG_DESC, multiset_encoded_function, MULTISET_SYNTAX, SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
+       SWITCH_ADD_APP(app_interface, "kz_unset", UNSET_SHORT_DESC, UNSET_LONG_DESC, unset_function, UNSET_SYNTAX, 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_encoded", EXPORT_SHORT_DESC, EXPORT_LONG_DESC, export_encoded_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);
+       SWITCH_ADD_APP(app_interface, "kz_uuid_multiset_encoded", UUID_MULTISET_SHORT_DESC, UUID_MULTISET_LONG_DESC, uuid_multiset_encoded_function, UUID_MULTISET_SYNTAX, SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
+       SWITCH_ADD_APP(app_interface, "kz_endless_playback", KZ_ENDLESS_PLAYBACK_SHORT_DESC, KZ_ENDLESS_PLAYBACK_LONG_DESC, kz_endless_playback_function, KZ_ENDLESS_PLAYBACK_SYNTAX, SAF_NONE);
+       SWITCH_ADD_APP(app_interface, "noop", NOOP_SHORT_DESC, NOOP_LONG_DESC, noop_function, NOOP_SYNTAX, SAF_NONE);
 }
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_ei.h b/src/mod/event_handlers/mod_kazoo/kazoo_ei.h
new file mode 100644 (file)
index 0000000..fd03239
--- /dev/null
@@ -0,0 +1,271 @@
+#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"
+
+#define KZ_MAX_SEPARATE_STRINGS 10
+
+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;
+       short event_stream_framing;
+       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;
+       short event_stream_framing;
+       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 enable_legacy;
+//     char *profile_vars_prefixes[KZ_MAX_SEPARATE_STRINGS];
+//     char *kazoo_var_prefixes[KZ_MAX_SEPARATE_STRINGS];
+//     char *profile_vars_prefixes;
+//     char *kazoo_var_prefixes;
+       char **profile_vars_prefixes;
+       char **kazoo_var_prefixes;
+
+};
+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:
+ */
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_ei_config.c b/src/mod/event_handlers/mod_kazoo/kazoo_ei_config.c
new file mode 100644 (file)
index 0000000..67c8d5d
--- /dev/null
@@ -0,0 +1,563 @@
+/*
+* 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"
+
+#define KZ_DEFAULT_STREAM_PRE_ALLOCATE 8192
+
+#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;
+       char* kazoo_var_prefix = NULL;
+       char* profile_vars_prefix = NULL;
+       char* sep_array[KZ_MAX_SEPARATE_STRINGS];
+       int array_len, i;
+       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 = KZ_DEFAULT_STREAM_PRE_ALLOCATE;
+       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;
+
+
+       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")) {
+                               kazoo_var_prefix = switch_core_strdup(kazoo_globals.pool, val);
+                       } else if (!strcmp(var, "set-profile-vars-prefix")) {
+                               profile_vars_prefix = switch_core_strdup(kazoo_globals.pool, 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);
+                       }
+               }
+       }
+
+       if ((child = switch_xml_child(cfg, "variables"))) {
+               for (param = switch_xml_child(child, "variable"); param; param = param->next) {
+                       char *var = (char *) switch_xml_attr_soft(param, "name");
+                       char *val = (char *) switch_xml_attr_soft(param, "value");
+                       if(var && val) {
+                               switch_core_set_variable(var, 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_var_prefix)) {
+               kazoo_var_prefix = switch_core_strdup(kazoo_globals.pool, "ecallmgr_;cav_");
+       }
+
+       if (zstr(profile_vars_prefix)) {
+               profile_vars_prefix = switch_core_strdup(kazoo_globals.pool, "effective_;origination_");
+       }
+
+       kazoo_globals.kazoo_var_prefixes = switch_core_alloc(kazoo_globals.pool, sizeof(char*) * KZ_MAX_SEPARATE_STRINGS);
+       array_len = switch_separate_string(kazoo_var_prefix, ';', sep_array, KZ_MAX_SEPARATE_STRINGS - 1);
+       for(i=0; i < array_len; i++) {
+               char var[100];
+               sprintf(var, "variable_%s", sep_array[i]);
+               kazoo_globals.kazoo_var_prefixes[i] = switch_core_strdup(kazoo_globals.pool, var);
+       }
+
+       kazoo_globals.profile_vars_prefixes = switch_core_alloc(kazoo_globals.pool, sizeof(char*) * KZ_MAX_SEPARATE_STRINGS);
+       array_len = switch_separate_string(profile_vars_prefix, ';', sep_array, KZ_MAX_SEPARATE_STRINGS - 1);
+       for(i=0; i < array_len; i++) {
+               kazoo_globals.profile_vars_prefixes[i] = switch_core_strdup(kazoo_globals.pool, sep_array[i]);
+       }
+
+       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 = NULL;
+               char* xml = NULL;
+               kazoo_config_ptr definitions = NULL, fetch_handlers = NULL, event_handlers = NULL;
+               kazoo_event_profile_ptr events = NULL;
+
+               xml = strndup(kz_default_config, kz_default_config_size);
+               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
+ */
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_ei_utils.c b/src/mod/event_handlers/mod_kazoo/kazoo_ei_utils.c
new file mode 100644 (file)
index 0000000..d1e925a
--- /dev/null
@@ -0,0 +1,997 @@
+/*
+ * 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_LIST_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_LIST_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("fs_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:
+ */
index 3ca0e3acc7d9f9105e90080698a5a9e12267e959..094b9107514b553cee2337d9d1ea98bb5d3b99af 100644 (file)
@@ -32,6 +32,8 @@
  */
 #include "mod_kazoo.h"
 
+#define MAX_FRAMING 4
+
 /* Blatantly repurposed from switch_eventc */
 static char *my_dup(const char *s) {
        size_t len = strlen(s) + 1;
@@ -48,7 +50,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;
                }
@@ -56,6 +59,20 @@ static int is_private_header(const char *name) {
        return 0;
 }
 
+static int is_kazoo_var(char* header)
+{
+       int idx = 0;
+       while(kazoo_globals.kazoo_var_prefixes[idx] != NULL) {
+               char *prefix = kazoo_globals.kazoo_var_prefixes[idx];
+               if(!strncasecmp(header, prefix, strlen(prefix))) {
+                       return 1;
+               }
+               idx++;
+       }
+
+       return 0;
+}
+
 static switch_status_t kazoo_event_dup(switch_event_t **clone, switch_event_t *event, switch_hash_t *filter) {
        switch_event_header_t *header;
 
@@ -73,7 +90,7 @@ static switch_status_t kazoo_event_dup(switch_event_t **clone, switch_event_t *e
                        continue;
                }
 
-               if (strncmp(header->name, kazoo_globals.kazoo_var_prefix, kazoo_globals.var_prefix_length)
+               if (!is_kazoo_var(header->name)
                        && filter
                        && !switch_core_hash_find(filter, header->name)
                        && (!kazoo_globals.send_all_headers)
@@ -102,51 +119,113 @@ 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, 3);
+       ei_x_encode_atom(ebuf, "event");
+       if(kazoo_globals.json_encoding == ERLANG_TUPLE) {
+               ei_x_encode_atom(ebuf, "json");
+       } else {
+               ei_x_encode_atom(ebuf, "map");
+       }
+       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;
+       kz_event_decode(event);
 
-               if (!event->subclass_name) {
+       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;
                }
+       } else {
+               ei_x_new(ebuf);
+       }
 
-               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;
-               }
+       ebuf->index = MAX_FRAMING;
 
-               if (!found) {
-                       return;
-               }
+       ei_x_encode_version(ebuf);
+
+       if(event_stream->node->legacy) {
+               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Switch-Nodename", kazoo_globals.ei_cnode.thisnodename);
+               res = encode_event_old(event, ebuf);
+       } else {
+               res = encode_event_new(event, 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(!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) {
@@ -157,12 +236,15 @@ static void *SWITCH_THREAD_FUNC event_stream_loop(switch_thread_t *thread, void
     char ipbuf[48];
     const char *ip_addr;
        void *pop;
-       short event_stream_framing = kazoo_globals.event_stream_framing;
+       short event_stream_framing;
+       short ok = 1;
 
        switch_atomic_inc(&kazoo_globals.threads);
 
        switch_assert(event_stream != NULL);
 
+       event_stream_framing = event_stream->event_stream_framing;
+
        /* figure out what socket we just opened */
        switch_socket_addr_get(&sa, SWITCH_FALSE, event_stream->acceptor);
        port = switch_sockaddr_get_port(sa);
@@ -172,13 +254,14 @@ static void *SWITCH_THREAD_FUNC event_stream_loop(switch_thread_t *thread, void
                                          ,(void *)event_stream, ip_addr, port, event_stream->pid.node, event_stream->pid.creation
                                          ,event_stream->pid.num, event_stream->pid.serial);
 
-       while (switch_test_flag(event_stream, LFLAG_RUNNING) && switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) {
+       while (switch_test_flag(event_stream, LFLAG_RUNNING) && switch_test_flag(&kazoo_globals, LFLAG_RUNNING) && ok) {
                const switch_pollfd_t *fds;
                int32_t numfds;
 
                /* 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 */
@@ -201,11 +284,11 @@ static void *SWITCH_THREAD_FUNC event_stream_loop(switch_thread_t *thread, void
                                        event_stream->socket = newsocket;
 
                                        switch_socket_addr_get(&sa, SWITCH_TRUE, newsocket);
-                                       event_stream->local_port = switch_sockaddr_get_port(sa);
+                                       event_stream->remote_port = switch_sockaddr_get_port(sa);
                                        switch_get_addr(event_stream->remote_ip, sizeof (event_stream->remote_ip), sa);
 
                                        switch_socket_addr_get(&sa, SWITCH_FALSE, newsocket);
-                                       event_stream->remote_port = switch_sockaddr_get_port(sa);
+                                       event_stream->local_port = switch_sockaddr_get_port(sa);
                                        switch_get_addr(event_stream->local_ip, sizeof (event_stream->local_ip), sa);
 
                                        event_stream->connected = SWITCH_TRUE;
@@ -217,46 +300,38 @@ 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);
-                               }
+                               switch_size_t size = 1, expected = 0;
+                               switch_status_t status = SWITCH_STATUS_SUCCESS;
 
-                               while (i) {
-                                       byte = ebuf.index >> (8 * --i);
-                                       switch_socket_send(event_stream->socket, &byte, &size);
+                               if(ebuf->index >= pow(2, 8 * event_stream_framing)) {
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sending frame size %d with insufficient frame capacity, change event_stream_framing here and tcp_packet_type in ecallmgr\n", ebuf->index);
+                               } else {
+                                       if(event_stream_framing) {
+                                               int index = ebuf->index - MAX_FRAMING;
+                                               char byte;
+                                               short i = event_stream_framing;
+                                               while (i) {
+                                                       byte = index >> (8 * --i);
+                                                       ebuf->buff[MAX_FRAMING - i - 1] = byte;
+                                               }
+                                       }
+                                       expected = size = (switch_size_t)ebuf->index - MAX_FRAMING + event_stream_framing;
+                                       if((status = switch_socket_send(event_stream->socket, ebuf->buff + (MAX_FRAMING - event_stream_framing), &size)) != SWITCH_STATUS_SUCCESS) {
+                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error %d sending event stream\n", status);
+                                               ok = 0;
+                                       } else if(expected != size) {
+                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error sending event stream, sent bytes is different of expected\n");
+                                               ok = 0;
+                                       }
                                }
-
-                               size = (switch_size_t)ebuf.index;
-                               switch_socket_send(event_stream->socket, ebuf.buff, &size);
-
-                               ei_x_free(&ebuf);
                        }
 
-                       switch_event_destroy(&event);
+                       ei_x_free(ebuf);
+                       switch_safe_free(ebuf);
                }
        }
 
@@ -297,11 +372,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 +396,8 @@ 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;
+       event_stream->event_stream_framing = ei_node->event_stream_framing;
        memcpy(&event_stream->pid, from, sizeof(erlang_pid));
        switch_queue_create(&event_stream->queue, MAX_QUEUE_LEN, pool);
 
@@ -443,17 +521,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 && (!event_binding->stream->node->legacy)) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "event binding to an event without profile in non legacy mode => %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 +596,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;
index 006ffe32f04085addd7225c67aa22045899b4d41..f2fe47e54e0b35833271cdced9ece4eb9d6063aa 100644 (file)
  */
 #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;
+static const char *fetch_uuid_sources[] = {
+               "Fetch-Call-UUID",
+               "refer-from-channel-id",
+               "sip_call_id",
+               NULL
 };
-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 +51,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";
        }
@@ -105,8 +81,10 @@ static char *expand_vars(char *xml_str) {
                                *e++ = '\0';
                                rp = e;
 
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "trying to expand %s \n", var);
                                if ((val = switch_core_get_variable_dup(var))) {
                                        char *p;
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "expanded %s to %s\n", var, val);
                                        for (p = val; p && *p && wp <= ep; p++) {
                                                *wp++ = *p;
                                        }
@@ -133,6 +111,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,31 +147,54 @@ 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;
 
-       /* add our reply placeholder to the replies list */
-       switch_mutex_lock(agent->replies_mutex);
-       if (!agent->replies) {
-               agent->replies = &reply;
-       } else {
-               reply.next = agent->replies;
-               agent->replies = &reply;
+       if(switch_event_get_header(event, "Unique-ID") == NULL) {
+               int i;
+               for(i = 0; fetch_uuid_sources[i] != NULL; i++) {
+                       if((fetch_call_id = switch_event_get_header(event, fetch_uuid_sources[i])) != 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);
+                                       uint32_t verbose = switch_channel_test_flag(channel, CF_VERBOSE_EVENTS);
+                                       switch_channel_set_flag(channel, CF_VERBOSE_EVENTS);
+                                       switch_channel_event_set_data(channel, event);
+                                       switch_channel_set_flag_value(channel, CF_VERBOSE_EVENTS, verbose);
+                                       switch_core_session_rwunlock(session);
+                                       break;
+                               }
+                       }
+               }
        }
-       switch_mutex_unlock(agent->replies_mutex);
 
-       fetch_handler = client->fetch_handlers;
-       while (fetch_handler != NULL) {
-               ei_send_msg_t *send_msg;
+       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));
-               memcpy(&send_msg->pid, &fetch_handler->pid, sizeof(erlang_pid));
+       kz_event_decode(event);
 
-               ei_x_new_with_version(&send_msg->buf);
+       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);
@@ -196,13 +202,29 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con
                _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);
+       }
 
-               if (params) {
-                       ei_encode_switch_event_headers(&send_msg->buf, params);
-               } else {
-                       ei_x_encode_empty_list(&send_msg->buf);
-               }
+       /* add our reply placeholder to the replies list */
+       switch_mutex_lock(agent->replies_mutex);
+       if (!agent->replies) {
+               agent->replies = &reply;
+       } else {
+               reply.next = agent->replies;
+               agent->replies = &reply;
+       }
+       switch_mutex_unlock(agent->replies_mutex);
 
+       fetch_handler = client->fetch_handlers;
+       while (fetch_handler != NULL && sent == 0) {
+               memcpy(&send_msg->pid, &fetch_handler->pid, sizeof(erlang_pid));
                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 +232,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 +240,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 +320,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 +390,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 +402,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 +468,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 +647,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 +658,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 +669,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 +681,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 +731,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 +768,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;
 }
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_fields.h b/src/mod/event_handlers/mod_kazoo/kazoo_fields.h
new file mode 100644 (file)
index 0000000..95aab4e
--- /dev/null
@@ -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 */
+
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_message.c b/src/mod/event_handlers/mod_kazoo/kazoo_message.c
new file mode 100644 (file)
index 0000000..2c4d81d
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+* 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, 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;
+}
+
+
+cJSON * kazoo_event_add_field_to_json(cJSON *dst, switch_event_t *src, kazoo_field_ptr field)
+{
+       switch_event_header_t *header;
+       char *expanded;
+       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:
+                       expanded = kz_event_expand_headers(src, field->value);
+                       if(expanded != NULL && !zstr(expanded)) {
+                               item = kazoo_event_add_json_value(dst, field, field->as ? field->as : field->name, expanded);
+                               free(expanded);
+                       }
+                       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_string(evt, SWITCH_STACK_BOTTOM, "Switch-Nodename", kazoo_globals.ei_cnode.thisnodename);
+
+
+       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_string(evt, SWITCH_STACK_BOTTOM, "Switch-Nodename", kazoo_globals.ei_cnode.thisnodename);
+
+       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
+ */
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_message.h b/src/mod/event_handlers/mod_kazoo/kazoo_message.h
new file mode 100644 (file)
index 0000000..eade6c4
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+* 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;
+
+void kazoo_cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+cJSON * kazoo_event_add_field_to_json(cJSON *dst, switch_event_t *src, kazoo_field_ptr field);
+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 */
+
index 17da6dc5820a5400626007a3f7f7b62d29b50aa3..32129150a08aa488d5871bd8af5b48f2cc3d1414 100644 (file)
@@ -53,6 +53,8 @@ static char *REQUEST_ATOMS[] = {
        "nixevent",
        "sendevent",
        "sendmsg",
+       "commands",
+       "command",
        "bind",
        "getpid",
        "version",
@@ -72,6 +74,8 @@ typedef enum {
        REQUEST_NIXEVENT,
        REQUEST_SENDEVENT,
        REQUEST_SENDMSG,
+       REQUEST_COMMANDS,
+       REQUEST_COMMAND,
        REQUEST_BIND,
        REQUEST_GETPID,
        REQUEST_VERSION,
@@ -86,7 +90,8 @@ typedef enum {
 } 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;
@@ -200,14 +205,21 @@ SWITCH_DECLARE(switch_status_t) kazoo_api_execute(const char *cmd, const char *a
        char *arg_used;
        char *cmd_used;
        int  fire_event = 0;
+       char *arg_expanded;
+       switch_event_t* evt;
 
        switch_assert(stream != NULL);
        switch_assert(stream->data != NULL);
        switch_assert(stream->write_function != NULL);
 
-       cmd_used = (char *) cmd;
-       arg_used = (char *) arg;
+       arg_expanded = (char *) arg;
+
+       switch_event_create(&evt, SWITCH_EVENT_GENERAL);
+       arg_expanded = switch_event_expand_headers(evt, arg);
+       switch_event_destroy(&evt);
 
+       cmd_used = (char *) cmd;
+       arg_used = arg_expanded;
 
        if (!stream->param_event) {
                switch_event_create(&stream->param_event, SWITCH_EVENT_API);
@@ -221,6 +233,9 @@ SWITCH_DECLARE(switch_status_t) kazoo_api_execute(const char *cmd, const char *a
                if (arg_used && *arg_used) {
                        switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command-Argument", arg_used);
                }
+               if (arg_expanded && *arg_expanded) {
+                       switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command-Argument-Expanded", arg_expanded);
+               }
        }
 
 
@@ -247,10 +262,14 @@ SWITCH_DECLARE(switch_status_t) kazoo_api_execute(const char *cmd, const char *a
                switch_safe_free(cmd_used);
        }
 
-       if (arg_used != arg) {
+       if (arg_used != arg_expanded) {
                switch_safe_free(arg_used);
        }
 
+       if (arg_expanded != arg) {
+               switch_safe_free(arg_expanded);
+       }
+
        return status;
 }
 
@@ -267,7 +286,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;
@@ -357,14 +376,13 @@ static void *SWITCH_THREAD_FUNC bgapi4_exec(switch_thread_t *thread, void *obj)
                return NULL;
        }
 
-        SWITCH_STANDARD_STREAM(stream);
+       SWITCH_STANDARD_STREAM(stream);
        switch_event_create(&stream.param_event, SWITCH_EVENT_API);
 
-        switch_malloc(send_msg, sizeof(*send_msg));
-        memcpy(&send_msg->pid, &acs->pid, sizeof(erlang_pid));
+       switch_malloc(send_msg, sizeof(*send_msg));
+       memcpy(&send_msg->pid, &acs->pid, sizeof(erlang_pid));
 
        ei_x_new_with_version(&send_msg->buf);
-
        ei_x_encode_tuple_header(&send_msg->buf, (stream.param_event ? 4 : 3));
 
        if (api_exec_stream(cmd, arg, &stream, &reply) == SWITCH_STATUS_SUCCESS) {
@@ -376,9 +394,9 @@ static void *SWITCH_THREAD_FUNC bgapi4_exec(switch_thread_t *thread, void *obj)
        _ei_x_encode_string(&send_msg->buf, acs->uuid_str);
        _ei_x_encode_string(&send_msg->buf, reply);
 
-        if (stream.param_event) {
+       if (stream.param_event) {
                ei_encode_switch_event_headers_2(&send_msg->buf, stream.param_event, 0);
-        }
+       }
 
        if (switch_queue_trypush(ei_node->send_msgs, send_msg) != SWITCH_STATUS_SUCCESS) {
                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send bgapi response %s to %s <%d.%d.%d>\n"
@@ -393,9 +411,9 @@ static void *SWITCH_THREAD_FUNC bgapi4_exec(switch_thread_t *thread, void *obj)
 
        switch_atomic_dec(&ei_node->pending_bgapi);
 
-        if (stream.param_event) {
-                switch_event_fire(&stream.param_event);
-        }
+       if (stream.param_event) {
+               switch_event_fire(&stream.param_event);
+       }
 
        switch_safe_free(reply);
        switch_safe_free(acs->arg);
@@ -412,11 +430,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 +470,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 +480,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 +500,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 +555,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 +594,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 +607,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 +617,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 +679,83 @@ 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_event_del_header_val(event, "event-uuid-name", 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 +783,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 +799,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 +808,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);
        }
@@ -858,9 +985,9 @@ static switch_status_t handle_request_api(ei_node_t *ei_node, erlang_pid *pid, e
 
 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 +995,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++) {
-               if (ei_decode_atom_safe(buf->buff, &buf->index, event_name)) {
-                       switch_mutex_unlock(ei_node->event_streams_mutex);
-                       return erlang_response_badarg(rbuf);
-               }
+       for (i = 1; i <= length; i++) {
 
-               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 {
+               if (ei_decode_atom_safe(buf->buff, &buf->index, event_name)) {
                        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);
 
@@ -935,13 +1042,13 @@ static switch_status_t handle_request_fetch_reply(ei_node_t *ei_node, erlang_pid
        }
 
        if (ei_decode_string_or_binary(buf->buff, &buf->index, &xml_str)) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Ignoring a fetch reply without XML\n");
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Ignoring a fetch reply without XML : %s \n", uuid_str);
                return erlang_response_badarg(rbuf);
        }
 
        if (zstr(xml_str)) {
                switch_safe_free(xml_str);
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Ignoring an empty fetch reply\n");
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Ignoring an empty fetch reply : %s\n", uuid_str);
                return erlang_response_badarg(rbuf);
        }
 
@@ -955,21 +1062,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 %s for an unknown configuration section: %s : %s\n", uuid_str, section_str, xml_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 %s is unknown or has expired : %s\n", uuid_str, xml_str);
                return erlang_response_baduuid(rbuf);
        }
 }
@@ -1010,6 +1120,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,10 +1138,10 @@ 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);
        default:
@@ -1214,7 +1328,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 +1376,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 +1427,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,16 +1545,18 @@ 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;
+       ei_node->event_stream_framing = kazoo_globals.event_stream_framing;
 
        /* store the IP and node name we are talking with */
        switch_os_sock_put(&ei_node->socket, (switch_os_socket_t *)&nodefd, pool);
 
        switch_socket_addr_get(&sa, SWITCH_TRUE, ei_node->socket);
-       ei_node->local_port = switch_sockaddr_get_port(sa);
+       ei_node->remote_port = switch_sockaddr_get_port(sa);
        switch_get_addr(ei_node->remote_ip, sizeof (ei_node->remote_ip), sa);
 
        switch_socket_addr_get(&sa, SWITCH_FALSE, ei_node->socket);
-       ei_node->remote_port = switch_sockaddr_get_port(sa);
+       ei_node->local_port = switch_sockaddr_get_port(sa);
        switch_get_addr(ei_node->local_ip, sizeof (ei_node->local_ip), sa);
 
        switch_queue_create(&ei_node->send_msgs, MAX_QUEUE_LEN, pool);
@@ -1451,8 +1567,7 @@ switch_status_t new_kazoo_node(int nodefd, ErlConnect *conn) {
        /* when we start we are running */
        switch_set_flag(ei_node, LFLAG_RUNNING);
 
-       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "New erlang connection from node %s (%s:%d)\n", ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port);
-       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "New erlang connection to node %s (%s:%d)\n", ei_node->peer_nodename, ei_node->local_ip, ei_node->local_port);
+       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "New erlang connection from node %s (%s:%d) -> (%s:%d)\n", ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port, ei_node->local_ip, ei_node->local_port);
 
        for(i = 0; i < kazoo_globals.num_worker_threads; i++) {
                switch_threadattr_create(&thd_attr, ei_node->pool);
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_tweaks.c b/src/mod/event_handlers/mod_kazoo/kazoo_tweaks.c
new file mode 100644 (file)
index 0000000..7b8d83f
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * 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",
+               "Call-Control-Node",
+               "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_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) {
+                       if(call_id) {
+                               if((session = switch_core_session_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, "invalid session : %s\n", call_id);
+                                       DUMP_EVENT(event);
+                               }
+                       }
+               }
+       }
+}
+
+static void kz_tweaks_handle_bridge_replaces_caller_id(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");
+
+       if(a_leg_call_id && replaced_call_id) {
+               switch_core_session_t *call_session = NULL;
+               const char *call_id = switch_event_get_header(event, "Bridge-B-Unique-ID");
+               if (call_id && (call_session = switch_core_session_force_locate(call_id)) != NULL) {
+                       switch_channel_t *call_channel = switch_core_session_get_channel(call_session);
+                       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(call_session));
+                               switch_event_add_header_string(my_event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", peer_uuid);
+                               switch_channel_event_set_data(call_channel, my_event);
+                               switch_event_fire(&my_event);
+                       }
+                       switch_channel_set_variable(call_channel, "Bridge-B-Unique-ID", peer_uuid);
+                       switch_channel_add_state_handler(call_channel, &kz_tweaks_signal_bridge_state_handlers);
+                       switch_core_session_rwunlock(call_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_variables(event);
+       kz_tweaks_handle_bridge_replaces_caller_id(event);
+       kz_tweaks_handle_bridge_replaces(event);
+}
+
+// TRANSFERS
+
+static void kz_tweaks_channel_replaced_event_handler(switch_event_t *event)
+{
+       const char *uuid = switch_event_get_header(event, "Unique-ID");
+       const char *replaced_by = switch_event_get_header(event, "att_xfer_replaced_by");
+       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "REPLACED : %s , %s\n", uuid, replaced_by);
+}
+
+static void kz_tweaks_channel_intercepted_event_handler(switch_event_t *event)
+{
+       const char *uuid = switch_event_get_header(event, "Unique-ID");
+       const char *peer_uuid = switch_event_get_header(event, "intercepted_by");
+       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "INTERCEPTED : %s => %s\n", uuid, peer_uuid);
+}
+
+static void kz_tweaks_channel_transferor_event_handler(switch_event_t *event)
+{
+       switch_core_session_t *uuid_session = NULL;
+       switch_event_t *evt = NULL;
+       const char *uuid = switch_event_get_header(event, "Unique-ID");
+
+       const char *orig_call_id = switch_event_get_header(event, "att_xfer_original_call_id");
+       const char *dest_peer_uuid = switch_event_get_header(event, "att_xfer_destination_peer_uuid");
+       const char *dest_call_id = 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 , %s \n", uuid, orig_call_id, dest_peer_uuid, dest_call_id, 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(dest_call_id && (session = switch_core_session_force_locate(dest_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);
+                               if (switch_event_create(&evt, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
+                                       switch_channel_event_set_data(channel, evt);
+                                       switch_event_fire(&evt);
+                               }
+                               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, dest_call_id, dest_peer_uuid);
+                       }
+                       if(dest_peer_uuid && (session = switch_core_session_force_locate(dest_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);
+                               if (switch_event_create(&evt, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
+                                       switch_channel_event_set_data(channel, evt);
+                                       switch_event_fire(&evt);
+                               }
+                               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, dest_call_id, dest_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, dest_call_id, dest_peer_uuid);
+               }
+               switch_core_session_rwunlock(uuid_session);
+       } else {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SESSION NOT FOUND : %s\n", uuid);
+       }
+}
+
+static void kz_tweaks_channel_transferee_event_handler(switch_event_t *event)
+{
+       const char *uuid = switch_event_get_header(event, "Unique-ID");
+       const char *replaced_by_uuid = switch_event_get_header(event, "att_xfer_replaced_call_id");
+       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TRANSFEREE : %s replaced by %s\n", uuid, replaced_by_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, "sip_loopback_", 13)) {
+                       switch_event_add_variable_name_printf(to_add, SWITCH_STACK_BOTTOM, header->value, "sip_%s", header->name+13);
+               } 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);
+       switch_memory_pool_t *pool = switch_core_session_get_pool(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))) {
+                       const char* tmp_caller = NULL;
+                       switch_caller_profile_t * cp = switch_channel_get_caller_profile(channel);
+                       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);
+                       }
+                       if((tmp_caller = switch_channel_get_variable(channel, "Internal-Caller-ID-Number")) != NULL) {
+                               profile_dup_clean(tmp_caller, cp->caller_id_number, pool);
+                       }
+                       if((tmp_caller = switch_channel_get_variable(channel, "Internal-Caller-ID-Name")) != NULL) {
+                               profile_dup_clean(tmp_caller, cp->caller_id_name, pool);
+                       }
+                       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));
+       switch_channel_set_flag(channel, CF_VERBOSE_EVENTS);
+       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:
+ */
+
+
index 024e98b8588595cebe873531d0acfe20af0b355e..d97b5b5a9318580d40b8b1198f2523a3e4c8addf 100644 (file)
  */
 #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 kz_check_set_profile_var(switch_channel_t *channel, char* var, char *val)
+{
+       int idx = 0;
+       while(kazoo_globals.profile_vars_prefixes[idx] != NULL) {
+               char *prefix = kazoo_globals.profile_vars_prefixes[idx];
+               if (!strncasecmp(var, prefix, strlen(prefix))) {
+                       switch_channel_set_profile_var(channel, var + strlen(prefix), val);
 
-void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event) {
-       ei_encode_switch_event_headers_2(ebuf, event, 1);
+               }
+               idx++;
+       }
 }
 
-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);
+SWITCH_DECLARE(switch_status_t) kz_expand_api_execute(const char *cmd, const char *arg, switch_core_session_t *session, switch_stream_handle_t *stream)
+{
+       switch_api_interface_t *api;
+       switch_status_t status;
+       char *arg_used;
+       char *cmd_used;
+
+       switch_assert(stream != NULL);
+       switch_assert(stream->data != NULL);
+       switch_assert(stream->write_function != NULL);
+
+       if (strcasecmp(cmd, "console_complete")) {
+               cmd_used = switch_strip_whitespace(cmd);
+               arg_used = switch_strip_whitespace(arg);
+       } else {
+               cmd_used = (char *) cmd;
+               arg_used = (char *) arg;
+       }
 
-    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");
-    }
+       if (cmd_used && (api = switch_loadable_module_get_api_interface(cmd_used)) != 0) {
+               if ((status = api->function(arg_used, session, stream)) != SWITCH_STATUS_SUCCESS) {
+                       stream->write_function(stream, "COMMAND RETURNED ERROR!\n");
+               }
+               UNPROTECT_INTERFACE(api);
+       } else {
+               status = SWITCH_STATUS_FALSE;
+               stream->write_function(stream, "INVALID COMMAND!\n");
+       }
 
-    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 (cmd_used != cmd) {
+               switch_safe_free(cmd_used);
+       }
 
-    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));
-    }
+       if (arg_used != arg) {
+               switch_safe_free(arg_used);
+       }
 
-    ei_x_encode_empty_list(ebuf);
+       return status;
 }
 
-void close_socket(switch_socket_t ** sock) {
-       if (*sock) {
-               switch_socket_shutdown(*sock, SWITCH_SHUTDOWN_READWRITE);
-               switch_socket_close(*sock);
-               *sock = NULL;
+#define resize(l) {\
+char *dp;\
+olen += (len + l + block);\
+cpos = c - data;\
+if ((dp = realloc(data, olen))) {\
+       data = dp;\
+       c = data + cpos;\
+       memset(c, 0, olen - cpos);\
+ }}                           \
+
+SWITCH_DECLARE(char *) kz_event_expand_headers_check(switch_event_t *event, const char *in, switch_event_t *var_list, switch_event_t *api_list, uint32_t recur)
+{
+       char *p, *c = NULL;
+       char *data, *indup, *endof_indup;
+       size_t sp = 0, len = 0, olen = 0, vtype = 0, br = 0, cpos, block = 128;
+       const char *sub_val = NULL;
+       char *cloned_sub_val = NULL, *expanded_sub_val = NULL;
+       char *func_val = NULL;
+       int nv = 0;
+       char *gvar = NULL, *sb = NULL;
+
+       if (recur > 100) {
+               return (char *) in;
        }
-}
 
-void close_socketfd(int *sockfd) {
-       if (*sockfd) {
-               shutdown(*sockfd, SHUT_RDWR);
-               close(*sockfd);
+       if (zstr(in)) {
+               return (char *) in;
        }
-}
 
-switch_socket_t *create_socket_with_port(switch_memory_pool_t *pool, switch_port_t port) {
-       switch_sockaddr_t *sa;
-       switch_socket_t *socket;
+       nv = switch_string_var_check_const(in) || switch_string_has_escaped_data(in);
 
-       if(switch_sockaddr_info_get(&sa, kazoo_globals.ip, SWITCH_UNSPEC, port, 0, pool)) {
-               return NULL;
+       if (!nv) {
+               return (char *) in;
        }
 
-       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;
-       }
+       nv = 0;
+       olen = strlen(in) + 1;
+       indup = strdup(in);
+       endof_indup = end_of_p(indup) + 1;
+
+       if ((data = malloc(olen))) {
+               memset(data, 0, olen);
+               c = data;
+               for (p = indup; p && p < endof_indup && *p; p++) {
+                       int global = 0;
+                       vtype = 0;
+
+                       if (*p == '\\') {
+                               if (*(p + 1) == '$') {
+                                       nv = 1;
+                                       p++;
+                                       if (*(p + 1) == '$') {
+                                               p++;
+                                       }
+                               } else if (*(p + 1) == '\'') {
+                                       p++;
+                                       continue;
+                               } else if (*(p + 1) == '\\') {
+                                       if (len + 1 >= olen) {
+                                               resize(1);
+                                       }
+
+                                       *c++ = *p++;
+                                       len++;
+                                       continue;
+                               }
+                       }
 
-       if (switch_socket_bind(socket, sa)) {
-               return NULL;
-       }
+                       if (*p == '$' && !nv) {
+                               if (*(p + 1) == '$') {
+                                       p++;
+                                       global++;
+                               }
+
+                               if (*(p + 1)) {
+                                       if (*(p + 1) == '{') {
+                                               vtype = global ? 3 : 1;
+                                       } else {
+                                               nv = 1;
+                                       }
+                               } else {
+                                       nv = 1;
+                               }
+                       }
 
-       if (switch_socket_listen(socket, 5)){
-               return NULL;
-       }
+                       if (nv) {
+                               if (len + 1 >= olen) {
+                                       resize(1);
+                               }
 
-       switch_getnameinfo(&kazoo_globals.hostname, sa, 0);
+                               *c++ = *p;
+                               len++;
+                               nv = 0;
+                               continue;
+                       }
 
-       //      if (kazoo_globals.nat_map && switch_nat_get_type()) {
-       //              switch_nat_add_mapping(port, SWITCH_NAT_TCP, NULL, SWITCH_FALSE);
-       //      }
+                       if (vtype) {
+                               char *s = p, *e, *vname, *vval = NULL;
+                               size_t nlen;
+
+                               s++;
+
+                               if ((vtype == 1 || vtype == 3) && *s == '{') {
+                                       br = 1;
+                                       s++;
+                               }
+
+                               e = s;
+                               vname = s;
+                               while (*e) {
+                                       if (br == 1 && *e == '}') {
+                                               br = 0;
+                                               *e++ = '\0';
+                                               break;
+                                       }
+
+                                       if (br > 0) {
+                                               if (e != s && *e == '{') {
+                                                       br++;
+                                               } else if (br > 1 && *e == '}') {
+                                                       br--;
+                                               }
+                                       }
+
+                                       e++;
+                               }
+                               p = e > endof_indup ? endof_indup : e;
+
+                               vval = NULL;
+                               for(sb = vname; sb && *sb; sb++) {
+                                       if (*sb == ' ') {
+                                               vval = sb;
+                                               break;
+                                       } else if (*sb == '(') {
+                                               vval = sb;
+                                               br = 1;
+                                               break;
+                                       }
+                               }
+
+                               if (vval) {
+                                       e = vval - 1;
+                                       *vval++ = '\0';
+
+                                       while (*e == ' ') {
+                                               *e-- = '\0';
+                                       }
+                                       e = vval;
+
+                                       while (e && *e) {
+                                               if (*e == '(') {
+                                                       br++;
+                                               } else if (br > 1 && *e == ')') {
+                                                       br--;
+                                               } else if (br == 1 && *e == ')') {
+                                                       *e = '\0';
+                                                       break;
+                                               }
+                                               e++;
+                                       }
+
+                                       vtype = 2;
+                               }
+
+                               if (vtype == 1 || vtype == 3) {
+                                       char *expanded = NULL;
+                                       int offset = 0;
+                                       int ooffset = 0;
+                                       char *ptr;
+                                       int idx = -1;
+
+                                       if ((expanded = kz_event_expand_headers_check(event, (char *) vname, var_list, api_list, recur+1)) == vname) {
+                                               expanded = NULL;
+                                       } else {
+                                               vname = expanded;
+                                       }
+                                       if ((ptr = strchr(vname, ':'))) {
+                                               *ptr++ = '\0';
+                                               offset = atoi(ptr);
+                                               if ((ptr = strchr(ptr, ':'))) {
+                                                       ptr++;
+                                                       ooffset = atoi(ptr);
+                                               }
+                                       }
+
+                                       if ((ptr = strchr(vname, '[')) && strchr(ptr, ']')) {
+                                               *ptr++ = '\0';
+                                               idx = atoi(ptr);
+                                       }
+
+                                       if (vtype == 3 || !(sub_val = switch_event_get_header_idx(event, vname, idx))) {
+                                               switch_safe_free(gvar);
+                                               if ((gvar = switch_core_get_variable_dup(vname))) {
+                                                       sub_val = gvar;
+                                               }
+
+                                               if (var_list && !switch_event_check_permission_list(var_list, vname)) {
+                                                       sub_val = "<Variable Expansion Permission Denied>";
+                                               }
+
+
+                                               if ((expanded_sub_val = kz_event_expand_headers_check(event, sub_val, var_list, api_list, recur+1)) == sub_val) {
+                                                       expanded_sub_val = NULL;
+                                               } else {
+                                                       sub_val = expanded_sub_val;
+                                               }
+                                       }
+
+                                       if (sub_val) {
+                                               if (offset || ooffset) {
+                                                       cloned_sub_val = strdup(sub_val);
+                                                       switch_assert(cloned_sub_val);
+                                                       sub_val = cloned_sub_val;
+                                               }
+
+                                               if (offset >= 0) {
+                                                       sub_val += offset;
+                                               } else if ((size_t) abs(offset) <= strlen(sub_val)) {
+                                                       sub_val = cloned_sub_val + (strlen(cloned_sub_val) + offset);
+                                               }
+
+                                               if (ooffset > 0 && (size_t) ooffset < strlen(sub_val)) {
+                                                       if ((ptr = (char *) sub_val + ooffset)) {
+                                                               *ptr = '\0';
+                                                       }
+                                               }
+                                       }
+
+                                       switch_safe_free(expanded);
+                               } else {
+                                       switch_stream_handle_t stream = { 0 };
+                                       char *expanded = NULL;
+
+                                       SWITCH_STANDARD_STREAM(stream);
+
+                                       if (stream.data) {
+                                               char *expanded_vname = NULL;
+
+                                               if ((expanded_vname = kz_event_expand_headers_check(event, (char *) vname, var_list, api_list, recur+1)) == vname) {
+                                                       expanded_vname = NULL;
+                                               } else {
+                                                       vname = expanded_vname;
+                                               }
+
+                                               if ((expanded = kz_event_expand_headers_check(event, vval, var_list, api_list, recur+1)) == vval) {
+                                                       expanded = NULL;
+                                               } else {
+                                                       vval = expanded;
+                                               }
+
+                                               if (!switch_core_test_flag(SCF_API_EXPANSION) || (api_list && !switch_event_check_permission_list(api_list, vname))) {
+                                                       func_val = NULL;
+                                                       sub_val = "<API execute Permission Denied>";
+                                               } else {
+                                                       stream.param_event = event;
+                                                       if (kz_expand_api_execute(vname, vval, NULL, &stream) == SWITCH_STATUS_SUCCESS) {
+                                                               func_val = stream.data;
+                                                               sub_val = func_val;
+                                                       } else {
+                                                               free(stream.data);
+                                                       }
+                                               }
+
+                                               switch_safe_free(expanded);
+                                               switch_safe_free(expanded_vname);
+
+                                       } else {
+                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
+                                               free(data);
+                                               free(indup);
+                                               return (char *) in;
+                                       }
+                               }
+                               if ((nlen = sub_val ? strlen(sub_val) : 0)) {
+                                       if (len + nlen >= olen) {
+                                               resize(nlen);
+                                       }
+
+                                       len += nlen;
+                                       strcat(c, sub_val);
+                                       c += nlen;
+                               }
+
+                               switch_safe_free(func_val);
+                               switch_safe_free(cloned_sub_val);
+                               switch_safe_free(expanded_sub_val);
+                               sub_val = NULL;
+                               vname = NULL;
+                               vtype = 0;
+                               br = 0;
+                       }
 
-       return socket;
-}
+                       if (sp) {
+                               if (len + 1 >= olen) {
+                                       resize(1);
+                               }
 
-switch_socket_t *create_socket(switch_memory_pool_t *pool) {
-       return create_socket_with_port(pool, 0);
+                               *c++ = ' ';
+                               sp = 0;
+                               len++;
+                       }
 
-}
+                       if (*p == '$') {
+                               p--;
+                       } else {
+                               if (len + 1 >= olen) {
+                                       resize(1);
+                               }
 
-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';
+                               *c++ = *p;
+                               len++;
+                       }
                }
        }
+       free(indup);
+       switch_safe_free(gvar);
 
-    /* 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;
-    }
+       return data;
 }
 
-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);
-    }
+SWITCH_DECLARE(char *) kz_event_expand_headers(switch_event_t *event, const char *in)
+{
+       return kz_event_expand_headers_check(event, in, NULL, NULL, 0);
 }
 
-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);
-}
+char *kazoo_expand_header(switch_memory_pool_t *pool, switch_event_t *event, char *val)
+{
+       char *expanded;
+       char *dup = NULL;
 
-int ei_helper_send(ei_node_t *ei_node, erlang_pid *to, ei_x_buff *buf) {
-    int ret = 0;
+       expanded = kz_event_expand_headers(event, val);
+       dup = switch_core_strdup(pool, expanded);
 
-    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);
-    }
+       if (expanded != val) {
+               free(expanded);
+       }
 
-    return ret;
+       return dup;
 }
 
-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;
+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 ei_decode_atom(buf, index, dst);
+               return "nodomain";
        }
 }
 
-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);
+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;
 
-       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);
-    }
+       switch_assert(event != NULL);
 
-    return res;
-}
 
-int ei_decode_string_or_binary_limited(char *buf, int *index, int maxsize, char *dst) {
-    int type, size, res;
-    long len;
+       va_start(ap, fmt);
+       ret = switch_vasprintf(&varname, fmt, ap);
+       va_end(ap);
 
-    ei_get_type(buf, index, &type, &size);
+       if (ret == -1) {
+               return SWITCH_STATUS_MEMERR;
+       }
 
-    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;
-    }
+       status = switch_event_add_header_string(event, stack, varname, val);
 
-       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;
-       }
+       free(varname);
 
-       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;
+       return status;
 }
 
-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;
+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;
 }
 
-static void *SWITCH_THREAD_FUNC fetch_config_filters_exec(switch_thread_t *thread, void *obj)
+void kz_xml_process(switch_xml_t cfg)
 {
-       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");
+       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';
+                               }
                        }
 
-                       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);
+                       if (name && val) {
+                               switch_core_set_variable(name, val);
                        }
-
-                       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;
+void kz_event_decode(switch_event_t *event)
+{
+       switch_event_header_t *hp;
+       int i;
+       for (hp = event->headers; hp; hp = hp->next) {
+               if (hp->idx) {
+                       for(i = 0; i < hp->idx; i++) {
+                               switch_url_decode(hp->array[i]);
+                       }
+               } else {
+                       switch_url_decode(hp->value);
+               }
+       }
+}
 
+char * kz_expand_vars(char *xml_str) {
+       return kz_expand_vars_pool(xml_str, NULL);
+}
 
-       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "fetching kazoo filters\n");
+char * kz_expand_vars_pool(char *xml_str, switch_memory_pool_t *pool) {
+       char *var, *val;
+       char *rp = xml_str; /* read pointer */
+       char *ep, *wp, *buff; /* end pointer, write pointer, write buffer */
 
-       switch_core_new_memory_pool(&pool);
+       if (!(strstr(xml_str, "$${"))) {
+               return xml_str;
+       }
 
-       switch_threadattr_create(&thd_attr, pool);
-       switch_threadattr_detach_set(thd_attr, 1);
-       switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+       switch_zmalloc(buff, strlen(xml_str) * 2);
+       wp = buff;
+       ep = buff + (strlen(xml_str) * 2) - 1;
+
+       while (*rp && wp < ep) {
+               if (*rp == '$' && *(rp + 1) == '$' && *(rp + 2) == '{') {
+                       char *e = switch_find_end_paren(rp + 2, '{', '}');
+
+                       if (e) {
+                               rp += 3;
+                               var = rp;
+                               *e++ = '\0';
+                               rp = e;
+
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "trying to expand %s \n", var);
+                               if ((val = switch_core_get_variable_dup(var))) {
+                                       char *p;
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "expanded %s to %s\n", var, val);
+                                       for (p = val; p && *p && wp <= ep; p++) {
+                                               *wp++ = *p;
+                                       }
+                                       switch_safe_free(val);
+                               }
+                               continue;
+                       }
+               }
 
-       switch_uuid_get(&uuid);
-       switch_thread_create(&thread, thd_attr, fetch_config_filters_exec, pool, pool);
+               *wp++ = *rp++;
+       }
 
-}
+       *wp++ = '\0';
 
+       if(pool) {
+               char * ret = switch_core_strdup(pool, buff);
+               switch_safe_free(buff);
+               return ret;
+       } else {
+               switch_safe_free(xml_str);
+               return buff;
+       }
 
+}
 
 /* For Emacs:
  * Local Variables:
index b666920d3fb20fd68b1450c0acde3349d4bd479b..31d998b5dcb7f19d01a6f82afb7a89d90b02664f 100644 (file)
  */
 #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_DEFINITION(mod_kazoo, mod_kazoo_load, mod_kazoo_shutdown, mod_kazoo_runtime);
 
 SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load) {
        switch_api_interface_t *api_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);
@@ -706,6 +101,13 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown) {
                }
        }
 
+       /* close the connection to epmd and the acceptor */
+       close_socketfd(&kazoo_globals.epmdfd);
+       close_socket(&kazoo_globals.acceptor);
+
+       /* remove all XML fetch agents */
+       unbind_fetch_agents();
+
        if (kazoo_globals.event_filter) {
                switch_core_hash_destroy(&kazoo_globals.event_filter);
        }
@@ -714,71 +116,21 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown) {
        switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock);
        switch_thread_rwlock_destroy(kazoo_globals.ei_nodes_lock);
 
-       /* close the connection to epmd and the acceptor */
-       close_socketfd(&kazoo_globals.epmdfd);
-       close_socket(&kazoo_globals.acceptor);
-
-       /* remove all XML fetch agents */
-       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);
+       }
+
+       kazoo_destroy_config();
 
        /* clean up our allocated preferences */
        switch_safe_free(kazoo_globals.ip);
        switch_safe_free(kazoo_globals.ei_cookie);
        switch_safe_free(kazoo_globals.ei_nodename);
-       switch_safe_free(kazoo_globals.kazoo_var_prefix);
 
        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:
index 9de7db6ea02ae2aa7560904ca5366f94c823926f..9848f2a2b8969434a79b9e61f4060f6aa69b36f2 100644 (file)
 #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
+extern const char kz_default_config[];
+extern const int kz_default_config_size;
+
+#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 +31,28 @@ 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 */
+void kz_check_set_profile_var(switch_channel_t *channel, char* var, char *val);
+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);
+void kz_event_decode(switch_event_t *event);
+char * kz_expand_vars(char *xml_str);
+char * kz_expand_vars_pool(char *xml_str, switch_memory_pool_t *pool);
+SWITCH_DECLARE(char *) kz_event_expand_headers(switch_event_t *event, const char *in);
+
+/* 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: