From bb4499ec24bd7a5cadb90eab1b1a05515d38eaa2 Mon Sep 17 00:00:00 2001 From: lazedo Date: Thu, 30 Nov 2017 19:29:27 +0000 Subject: [PATCH] FS-10820 [mod_kazoo] eventstream configuration --- src/mod/event_handlers/mod_kazoo/Makefile.am | 14 +- .../event_handlers/mod_kazoo/kazoo.conf.xml | 953 +++++++++++++++++ src/mod/event_handlers/mod_kazoo/kazoo_api.c | 396 +++++++ .../event_handlers/mod_kazoo/kazoo_commands.c | 21 +- .../event_handlers/mod_kazoo/kazoo_config.c | 535 ++++++++++ .../event_handlers/mod_kazoo/kazoo_config.h | 62 ++ .../event_handlers/mod_kazoo/kazoo_dptools.c | 199 +++- src/mod/event_handlers/mod_kazoo/kazoo_ei.h | 262 +++++ .../mod_kazoo/kazoo_ei_config.c | 543 ++++++++++ .../event_handlers/mod_kazoo/kazoo_ei_utils.c | 996 ++++++++++++++++++ .../mod_kazoo/kazoo_event_stream.c | 235 +++-- .../mod_kazoo/kazoo_fetch_agent.c | 180 +++- .../event_handlers/mod_kazoo/kazoo_fields.h | 195 ++++ .../event_handlers/mod_kazoo/kazoo_message.c | 458 ++++++++ .../event_handlers/mod_kazoo/kazoo_message.h | 65 ++ src/mod/event_handlers/mod_kazoo/kazoo_node.c | 221 +++- .../event_handlers/mod_kazoo/kazoo_tweaks.c | 502 +++++++++ .../event_handlers/mod_kazoo/kazoo_utils.c | 711 ++----------- src/mod/event_handlers/mod_kazoo/mod_kazoo.c | 687 +----------- src/mod/event_handlers/mod_kazoo/mod_kazoo.h | 163 +-- 20 files changed, 5723 insertions(+), 1675 deletions(-) create mode 100644 src/mod/event_handlers/mod_kazoo/kazoo.conf.xml create mode 100644 src/mod/event_handlers/mod_kazoo/kazoo_api.c create mode 100644 src/mod/event_handlers/mod_kazoo/kazoo_config.c create mode 100644 src/mod/event_handlers/mod_kazoo/kazoo_config.h create mode 100644 src/mod/event_handlers/mod_kazoo/kazoo_ei.h create mode 100644 src/mod/event_handlers/mod_kazoo/kazoo_ei_config.c create mode 100644 src/mod/event_handlers/mod_kazoo/kazoo_ei_utils.c create mode 100644 src/mod/event_handlers/mod_kazoo/kazoo_fields.h create mode 100644 src/mod/event_handlers/mod_kazoo/kazoo_message.c create mode 100644 src/mod/event_handlers/mod_kazoo/kazoo_message.h create mode 100644 src/mod/event_handlers/mod_kazoo/kazoo_tweaks.c diff --git a/src/mod/event_handlers/mod_kazoo/Makefile.am b/src/mod/event_handlers/mod_kazoo/Makefile.am index 53fd7112e1..ed2eec7b9c 100644 --- a/src/mod/event_handlers/mod_kazoo/Makefile.am +++ b/src/mod/event_handlers/mod_kazoo/Makefile.am @@ -4,11 +4,23 @@ MODNAME=mod_kazoo if HAVE_ERLANG mod_LTLIBRARIES = mod_kazoo.la -mod_kazoo_la_SOURCES = mod_kazoo.c kazoo_utils.c kazoo_node.c kazoo_event_stream.c kazoo_fetch_agent.c kazoo_commands.c kazoo_dptools.c +mod_kazoo_la_SOURCES = mod_kazoo.c kazoo_utils.c kazoo_dptools.c kazoo_tweaks.c +mod_kazoo_la_SOURCES += kazoo_api.c kazoo_commands.c kazoo_config.c +mod_kazoo_la_SOURCES += kazoo_message.c +mod_kazoo_la_SOURCES += kazoo_ei_config.c kazoo_ei_utils.c kazoo_event_stream.c +mod_kazoo_la_SOURCES += kazoo_fetch_agent.c kazoo_node.c + mod_kazoo_la_CFLAGS = $(AM_CFLAGS) @ERLANG_CFLAGS@ -D_REENTRANT mod_kazoo_la_LIBADD = $(switch_builddir)/libfreeswitch.la mod_kazoo_la_LDFLAGS = -avoid-version -module -no-undefined -shared @ERLANG_LDFLAGS@ +mod_kazoo.la: kazoo_definitions.h $(mod_kazoo_la_OBJECTS) $(mod_kazoo_la_DEPENDENCIES) $(EXTRA_mod_kazoo_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_kazoo_la_LINK) $(am_mod_kazoo_la_rpath) $(mod_kazoo_la_OBJECTS) $(mod_kazoo_la_LIBADD) $(LIBS) + + +kazoo_definitions.h: + xxd -i kazoo.conf.xml > kazoo_definitions.h + else install: error all: error 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 index 0000000000..7a5fdb4c7b --- /dev/null +++ b/src/mod/event_handlers/mod_kazoo/kazoo.conf.xml @@ -0,0 +1,953 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 index 0000000000..09d3d05419 --- /dev/null +++ b/src/mod/event_handlers/mod_kazoo/kazoo_api.c @@ -0,0 +1,396 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2012, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Karl Anderson + * Darren Schreiber + * + * + * mod_kazoo.c -- Socket Controlled Event Handler + * + */ +#include "mod_kazoo.h" + +#define KAZOO_DESC "kazoo information" +#define KAZOO_SYNTAX " []" + +#define API_COMMAND_DISCONNECT 0 +#define API_COMMAND_REMOTE_IP 1 +#define API_COMMAND_STREAMS 2 +#define API_COMMAND_BINDINGS 3 + + +static switch_status_t api_erlang_status(switch_stream_handle_t *stream) { + switch_sockaddr_t *sa; + uint16_t port; + char ipbuf[48]; + const char *ip_addr; + ei_node_t *ei_node; + + switch_socket_addr_get(&sa, SWITCH_FALSE, kazoo_globals.acceptor); + + port = switch_sockaddr_get_port(sa); + ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa); + + stream->write_function(stream, "Running %s\n", VERSION); + stream->write_function(stream, "Listening for new Erlang connections on %s:%u with cookie %s\n", ip_addr, port, kazoo_globals.ei_cookie); + stream->write_function(stream, "Registered as Erlang node %s, visible as %s\n", kazoo_globals.ei_cnode.thisnodename, kazoo_globals.ei_cnode.thisalivename); + + if (kazoo_globals.ei_compat_rel) { + stream->write_function(stream, "Using Erlang compatibility mode: %d\n", kazoo_globals.ei_compat_rel); + } + + switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock); + ei_node = kazoo_globals.ei_nodes; + if (!ei_node) { + stream->write_function(stream, "No erlang nodes connected\n"); + } else { + stream->write_function(stream, "Connected to:\n"); + while(ei_node != NULL) { + unsigned int year, day, hour, min, sec, delta; + + delta = (switch_micro_time_now() - ei_node->created_time) / 1000000; + sec = delta % 60; + min = delta / 60 % 60; + hour = delta / 3600 % 24; + day = delta / 86400 % 7; + year = delta / 31556926 % 12; + stream->write_function(stream, " %s (%s:%d) up %d years, %d days, %d hours, %d minutes, %d seconds\n" + ,ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port, year, day, hour, min, sec); + ei_node = ei_node->next; + } + } + switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t api_erlang_event_filter(switch_stream_handle_t *stream) { + switch_hash_index_t *hi = NULL; + int column = 0; + + for (hi = (switch_hash_index_t *)switch_core_hash_first_iter(kazoo_globals.event_filter, hi); hi; hi = switch_core_hash_next(&hi)) { + const void *key; + void *val; + switch_core_hash_this(hi, &key, NULL, &val); + stream->write_function(stream, "%-50s", (char *)key); + if (++column > 2) { + stream->write_function(stream, "\n"); + column = 0; + } + } + + if (++column > 2) { + stream->write_function(stream, "\n"); + column = 0; + } + + stream->write_function(stream, "%-50s", kazoo_globals.kazoo_var_prefix); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t api_erlang_nodes_list(switch_stream_handle_t *stream) { + ei_node_t *ei_node; + + switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock); + ei_node = kazoo_globals.ei_nodes; + while(ei_node != NULL) { + stream->write_function(stream, "%s (%s)\n", ei_node->peer_nodename, ei_node->remote_ip); + ei_node = ei_node->next; + } + switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t api_erlang_nodes_count(switch_stream_handle_t *stream) { + ei_node_t *ei_node; + int count = 0; + + switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock); + ei_node = kazoo_globals.ei_nodes; + while(ei_node != NULL) { + count++; + ei_node = ei_node->next; + } + switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock); + + stream->write_function(stream, "%d\n", count); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t api_complete_erlang_node(const char *line, const char *cursor, switch_console_callback_match_t **matches) { + switch_console_callback_match_t *my_matches = NULL; + switch_status_t status = SWITCH_STATUS_FALSE; + ei_node_t *ei_node; + + switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock); + ei_node = kazoo_globals.ei_nodes; + while(ei_node != NULL) { + switch_console_push_match(&my_matches, ei_node->peer_nodename); + ei_node = ei_node->next; + } + switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock); + + if (my_matches) { + *matches = my_matches; + status = SWITCH_STATUS_SUCCESS; + } + + return status; +} + +static switch_status_t handle_node_api_event_stream(ei_event_stream_t *event_stream, switch_stream_handle_t *stream) { + ei_event_binding_t *binding; + int column = 0; + + switch_mutex_lock(event_stream->socket_mutex); + if (event_stream->connected == SWITCH_FALSE) { + switch_sockaddr_t *sa; + uint16_t port; + char ipbuf[48] = {0}; + const char *ip_addr; + + switch_socket_addr_get(&sa, SWITCH_TRUE, event_stream->acceptor); + port = switch_sockaddr_get_port(sa); + ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa); + + if (zstr(ip_addr)) { + ip_addr = kazoo_globals.ip; + } + + stream->write_function(stream, "%s:%d -> disconnected\n" + ,ip_addr, port); + } else { + stream->write_function(stream, "%s:%d -> %s:%d\n" + ,event_stream->local_ip, event_stream->local_port + ,event_stream->remote_ip, event_stream->remote_port); + } + + binding = event_stream->bindings; + while(binding != NULL) { + if (binding->type == SWITCH_EVENT_CUSTOM) { + stream->write_function(stream, "CUSTOM %-43s", binding->subclass_name); + } else { + stream->write_function(stream, "%-50s", switch_event_name(binding->type)); + } + + if (++column > 2) { + stream->write_function(stream, "\n"); + column = 0; + } + + binding = binding->next; + } + switch_mutex_unlock(event_stream->socket_mutex); + + if (!column) { + stream->write_function(stream, "\n"); + } else { + stream->write_function(stream, "\n\n"); + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t handle_node_api_event_streams(ei_node_t *ei_node, switch_stream_handle_t *stream) { + ei_event_stream_t *event_stream; + + switch_mutex_lock(ei_node->event_streams_mutex); + event_stream = ei_node->event_streams; + while(event_stream != NULL) { + handle_node_api_event_stream(event_stream, stream); + event_stream = event_stream->next; + } + switch_mutex_unlock(ei_node->event_streams_mutex); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t handle_node_api_command(ei_node_t *ei_node, switch_stream_handle_t *stream, uint32_t command) { + unsigned int year, day, hour, min, sec, delta; + + switch (command) { + case API_COMMAND_DISCONNECT: + stream->write_function(stream, "Disconnecting erlang node %s at managers request\n", ei_node->peer_nodename); + switch_clear_flag(ei_node, LFLAG_RUNNING); + break; + case API_COMMAND_REMOTE_IP: + delta = (switch_micro_time_now() - ei_node->created_time) / 1000000; + sec = delta % 60; + min = delta / 60 % 60; + hour = delta / 3600 % 24; + day = delta / 86400 % 7; + year = delta / 31556926 % 12; + + stream->write_function(stream, "Uptime %d years, %d days, %d hours, %d minutes, %d seconds\n", year, day, hour, min, sec); + stream->write_function(stream, "Local Address %s:%d\n", ei_node->local_ip, ei_node->local_port); + stream->write_function(stream, "Remote Address %s:%d\n", ei_node->remote_ip, ei_node->remote_port); + break; + case API_COMMAND_STREAMS: + handle_node_api_event_streams(ei_node, stream); + break; + case API_COMMAND_BINDINGS: + handle_api_command_streams(ei_node, stream); + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t api_erlang_node_command(switch_stream_handle_t *stream, const char *nodename, uint32_t command) { + ei_node_t *ei_node; + + switch_thread_rwlock_rdlock(kazoo_globals.ei_nodes_lock); + ei_node = kazoo_globals.ei_nodes; + while(ei_node != NULL) { + int length = strlen(ei_node->peer_nodename); + + if (!strncmp(ei_node->peer_nodename, nodename, length)) { + handle_node_api_command(ei_node, stream, command); + switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock); + return SWITCH_STATUS_SUCCESS; + } + + ei_node = ei_node->next; + } + switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock); + + return SWITCH_STATUS_NOTFOUND; +} + +SWITCH_STANDARD_API(exec_api_cmd) +{ + char *argv[1024] = { 0 }; + int unknown_command = 1, argc = 0; + char *mycmd = NULL; + + const char *usage_string = "USAGE:\n" + "--------------------------------------------------------------------------------------------------------------------\n" + "erlang status - provides an overview of the current status\n" + "erlang event_filter - lists the event headers that will be sent to Erlang nodes\n" + "erlang nodes list - lists connected Erlang nodes (usefull for monitoring tools)\n" + "erlang nodes count - provides a count of connected Erlang nodes (usefull for monitoring tools)\n" + "erlang node disconnect - disconnects an Erlang node\n" + "erlang node connection - Shows the connection info\n" + "erlang node event_streams - lists the event streams for an Erlang node\n" + "erlang node fetch_bindings - lists the XML fetch bindings for an Erlang node\n" + "---------------------------------------------------------------------------------------------------------------------\n"; + + if (zstr(cmd)) { + stream->write_function(stream, "%s", usage_string); + return SWITCH_STATUS_SUCCESS; + } + + if (!(mycmd = strdup(cmd))) { + return SWITCH_STATUS_MEMERR; + } + + if (!(argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) { + stream->write_function(stream, "%s", usage_string); + switch_safe_free(mycmd); + return SWITCH_STATUS_SUCCESS; + } + + if (zstr(argv[0])) { + stream->write_function(stream, "%s", usage_string); + switch_safe_free(mycmd); + return SWITCH_STATUS_SUCCESS; + } + + if (!strncmp(argv[0], "status", 6)) { + unknown_command = 0; + api_erlang_status(stream); + } else if (!strncmp(argv[0], "event_filter", 6)) { + unknown_command = 0; + api_erlang_event_filter(stream); + } else if (!strncmp(argv[0], "nodes", 6) && !zstr(argv[1])) { + if (!strncmp(argv[1], "list", 6)) { + unknown_command = 0; + api_erlang_nodes_list(stream); + } else if (!strncmp(argv[1], "count", 6)) { + unknown_command = 0; + api_erlang_nodes_count(stream); + } + } else if (!strncmp(argv[0], "node", 6) && !zstr(argv[1]) && !zstr(argv[2])) { + if (!strncmp(argv[2], "disconnect", 6)) { + unknown_command = 0; + api_erlang_node_command(stream, argv[1], API_COMMAND_DISCONNECT); + } else if (!strncmp(argv[2], "connection", 2)) { + unknown_command = 0; + api_erlang_node_command(stream, argv[1], API_COMMAND_REMOTE_IP); + } else if (!strncmp(argv[2], "event_streams", 6)) { + unknown_command = 0; + api_erlang_node_command(stream, argv[1], API_COMMAND_STREAMS); + } else if (!strncmp(argv[2], "fetch_bindings", 6)) { + unknown_command = 0; + api_erlang_node_command(stream, argv[1], API_COMMAND_BINDINGS); + } + } + + if (unknown_command) { + stream->write_function(stream, "%s", usage_string); + } + + switch_safe_free(mycmd); + return SWITCH_STATUS_SUCCESS; +} + +void add_cli_api(switch_loadable_module_interface_t **module_interface, switch_api_interface_t *api_interface) +{ + SWITCH_ADD_API(api_interface, "erlang", KAZOO_DESC, exec_api_cmd, KAZOO_SYNTAX); + switch_console_set_complete("add erlang status"); + switch_console_set_complete("add erlang event_filter"); + switch_console_set_complete("add erlang nodes list"); + switch_console_set_complete("add erlang nodes count"); + switch_console_set_complete("add erlang node ::erlang::node disconnect"); + switch_console_set_complete("add erlang node ::erlang::node connection"); + switch_console_set_complete("add erlang node ::erlang::node event_streams"); + switch_console_set_complete("add erlang node ::erlang::node fetch_bindings"); + switch_console_add_complete_func("::erlang::node", api_complete_erlang_node); + +} + +void remove_cli_api() +{ + switch_console_set_complete("del erlang"); + switch_console_del_complete_func("::erlang::node"); + +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_commands.c b/src/mod/event_handlers/mod_kazoo/kazoo_commands.c index 2494bb9495..5002558398 100644 --- a/src/mod/event_handlers/mod_kazoo/kazoo_commands.c +++ b/src/mod/event_handlers/mod_kazoo/kazoo_commands.c @@ -31,6 +31,7 @@ * */ #include "mod_kazoo.h" +#include #include #define UUID_SET_DESC "Set a variable" @@ -158,6 +159,11 @@ SWITCH_STANDARD_API(uuid_setvar_multi_function) { return SWITCH_STATUS_SUCCESS; } +static size_t body_callback(char *buffer, size_t size, size_t nitems, void *userdata) +{ + return size * nitems; +} + static size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata) { switch_event_t* event = (switch_event_t*)userdata; @@ -182,6 +188,7 @@ SWITCH_STANDARD_API(kz_http_put) switch_event_t *params = NULL; char *url = NULL; char *filename = NULL; + int delete = 0; switch_curl_slist_t *headers = NULL; /* optional linked-list of HTTP headers */ char *ext; /* file extension, used for MIME type identification */ @@ -279,6 +286,8 @@ SWITCH_STANDARD_API(kz_http_put) switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-http-cache/1.0"); switch_curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, stream->param_event); switch_curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_callback); + switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, body_callback); + switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); switch_curl_easy_perform(curl_handle); switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes); @@ -286,22 +295,24 @@ SWITCH_STANDARD_API(kz_http_put) if (httpRes == 200 || httpRes == 201 || httpRes == 202 || httpRes == 204) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s saved to %s\n", filename, url); - switch_event_add_header(stream->param_event, SWITCH_STACK_BOTTOM, "API-Output", "%s saved to %s\n", filename, url); - stream->write_function(stream, "+OK\n"); + switch_event_add_header(stream->param_event, SWITCH_STACK_BOTTOM, "API-Output", "%s saved to %s", filename, url); + stream->write_function(stream, "+OK %s saved to %s", filename, url); + delete = 1; } else { error = switch_mprintf("Received HTTP error %ld trying to save %s to %s", httpRes, filename, url); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s\n", error); switch_event_add_header(stream->param_event, SWITCH_STACK_BOTTOM, "API-Error", "%s", error); switch_event_add_header(stream->param_event, SWITCH_STACK_BOTTOM, "API-HTTP-Error", "%ld", httpRes); - stream->write_function(stream, "-ERR "); - stream->write_function(stream, error); - stream->write_function(stream, "\n"); + stream->write_function(stream, "-ERR %s", error); status = SWITCH_STATUS_GENERR; } done: if (file_to_put) { fclose(file_to_put); + if(delete && kazoo_globals.delete_file_after_put) { + remove(filename); + } } if (headers) { 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 index 0000000000..f953bfc391 --- /dev/null +++ b/src/mod/event_handlers/mod_kazoo/kazoo_config.c @@ -0,0 +1,535 @@ +/* +* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* Copyright (C) 2005-2012, Anthony Minessale II +* +* 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 +* Portions created by the Initial Developer are Copyright (C) +* the Initial Developer. All Rights Reserved. +* +* Based on mod_skel by +* Anthony Minessale II +* +* Contributor(s): +* +* Daniel Bryars +* Tim Brown +* Anthony Minessale II +* William King +* Mike Jerris +* +* kazoo.c -- Sends FreeSWITCH events to an AMQP broker +* +*/ + +#include "mod_kazoo.h" + +static const char *LOG_LEVEL_NAMES[] = { + "SWITCH_LOG_DEBUG10", + "SWITCH_LOG_DEBUG9", + "SWITCH_LOG_DEBUG8", + "SWITCH_LOG_DEBUG7", + "SWITCH_LOG_DEBUG6", + "SWITCH_LOG_DEBUG5", + "SWITCH_LOG_DEBUG4", + "SWITCH_LOG_DEBUG3", + "SWITCH_LOG_DEBUG2", + "SWITCH_LOG_DEBUG1", + "SWITCH_LOG_DEBUG", + "SWITCH_LOG_INFO", + "SWITCH_LOG_NOTICE", + "SWITCH_LOG_WARNING", + "SWITCH_LOG_ERROR", + "SWITCH_LOG_CRIT", + "SWITCH_LOG_ALERT", + "SWITCH_LOG_CONSOLE", + "SWITCH_LOG_INVALID", + "SWITCH_LOG_UNINIT", + NULL +}; + +static const switch_log_level_t LOG_LEVEL_VALUES[] = { + SWITCH_LOG_DEBUG10, + SWITCH_LOG_DEBUG9, + SWITCH_LOG_DEBUG8, + SWITCH_LOG_DEBUG7, + SWITCH_LOG_DEBUG6, + SWITCH_LOG_DEBUG5, + SWITCH_LOG_DEBUG4, + SWITCH_LOG_DEBUG3, + SWITCH_LOG_DEBUG2, + SWITCH_LOG_DEBUG1, + SWITCH_LOG_DEBUG, + SWITCH_LOG_INFO, + SWITCH_LOG_NOTICE, + SWITCH_LOG_WARNING, + SWITCH_LOG_ERROR, + SWITCH_LOG_CRIT, + SWITCH_LOG_ALERT, + SWITCH_LOG_CONSOLE, + SWITCH_LOG_INVALID, + SWITCH_LOG_UNINIT +}; + +switch_log_level_t log_str2level(const char *str) +{ + int x = 0; + switch_log_level_t level = SWITCH_LOG_INVALID; + + if (switch_is_number(str)) { + x = atoi(str); + + if (x > SWITCH_LOG_INVALID) { + return SWITCH_LOG_INVALID - 1; + } else if (x < 0) { + return 0; + } else { + return x; + } + } + + + for (x = 0;; x++) { + if (!LOG_LEVEL_NAMES[x]) { + break; + } + + if (!strcasecmp(LOG_LEVEL_NAMES[x], str)) { + level = LOG_LEVEL_VALUES[x]; //(switch_log_level_t) x; + break; + } + } + + return level; +} + +switch_status_t kazoo_config_loglevels(switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_loglevels_ptr *ptr) +{ + switch_xml_t xml_level, xml_logging; + kazoo_loglevels_ptr loglevels = (kazoo_loglevels_ptr) switch_core_alloc(pool, sizeof(kazoo_loglevels_t)); + + loglevels->failed_log_level = SWITCH_LOG_ALERT; + loglevels->filtered_event_log_level = SWITCH_LOG_DEBUG1; + loglevels->filtered_field_log_level = SWITCH_LOG_DEBUG1; + loglevels->info_log_level = SWITCH_LOG_INFO; + loglevels->warn_log_level = SWITCH_LOG_WARNING; + loglevels->success_log_level = SWITCH_LOG_DEBUG; + loglevels->time_log_level = SWITCH_LOG_DEBUG1; + + if ((xml_logging = switch_xml_child(cfg, "logging")) != NULL) { + for (xml_level = switch_xml_child(xml_logging, "log"); xml_level; xml_level = xml_level->next) { + char *var = (char *) switch_xml_attr_soft(xml_level, "name"); + char *val = (char *) switch_xml_attr_soft(xml_level, "value"); + + if (!var) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "logging param missing 'name' attribute\n"); + continue; + } + + if (!val) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "logging param[%s] missing 'value' attribute\n", var); + continue; + } + + if (!strncmp(var, "success", 7)) { + loglevels->success_log_level = log_str2level(val); + } else if (!strncmp(var, "failed", 6)) { + loglevels->failed_log_level = log_str2level(val); + } else if (!strncmp(var, "info", 4)) { + loglevels->info_log_level = log_str2level(val); + } else if (!strncmp(var, "warn", 4)) { + loglevels->warn_log_level = log_str2level(val); + } else if (!strncmp(var, "time", 4)) { + loglevels->time_log_level = log_str2level(val); + } else if (!strncmp(var, "filtered-event", 14)) { + loglevels->filtered_event_log_level = log_str2level(val); + } else if (!strncmp(var, "filtered-field", 14)) { + loglevels->filtered_field_log_level = log_str2level(val); + } + } /* xml_level for loop */ + } + + *ptr = loglevels; + return SWITCH_STATUS_SUCCESS; + +} + +switch_status_t kazoo_config_filters(switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_filter_ptr *ptr) +{ + switch_xml_t filters, filter; +// char *routing_key = NULL; + kazoo_filter_ptr root = NULL, prv = NULL, cur = NULL; + + + if ((filters = switch_xml_child(cfg, "filters")) != NULL) { + for (filter = switch_xml_child(filters, "filter"); filter; filter = filter->next) { + const char *var = switch_xml_attr(filter, "name"); + const char *val = switch_xml_attr(filter, "value"); + const char *type = switch_xml_attr(filter, "type"); + const char *compare = switch_xml_attr(filter, "compare"); + cur = (kazoo_filter_ptr) switch_core_alloc(pool, sizeof(kazoo_filter)); + memset(cur, 0, sizeof(kazoo_filter)); + if(prv == NULL) { + root = prv = cur; + } else { + prv->next = cur; + prv = cur; + } + cur->type = FILTER_EXCLUDE; + cur->compare = FILTER_COMPARE_VALUE; + + if(var) + cur->name = switch_core_strdup(pool, var); + + if(val) + cur->value = switch_core_strdup(pool, val); + + if(type) { + if (!strncmp(type, "exclude", 7)) { + cur->type = FILTER_EXCLUDE; + } else if (!strncmp(type, "include", 7)) { + cur->type = FILTER_INCLUDE; + } + } + + if(compare) { + if (!strncmp(compare, "value", 7)) { + cur->compare = FILTER_COMPARE_VALUE; + } else if (!strncmp(compare, "prefix", 6)) { + cur->compare = FILTER_COMPARE_PREFIX; + } else if (!strncmp(compare, "list", 4)) { + cur->compare = FILTER_COMPARE_LIST; + } else if (!strncmp(compare, "exists", 6)) { + cur->compare = FILTER_COMPARE_EXISTS; + } else if (!strncmp(compare, "regex", 5)) { + cur->compare = FILTER_COMPARE_REGEX; + } + } + + if(cur->value == NULL) + cur->compare = FILTER_COMPARE_EXISTS; + + if(cur->compare == FILTER_COMPARE_LIST) { + cur->list.size = switch_separate_string(cur->value, '|', cur->list.value, MAX_LIST_FIELDS); + } + + } + + } + + *ptr = root; + + return SWITCH_STATUS_SUCCESS; + +} + +switch_status_t kazoo_config_field(kazoo_config_ptr definitions, switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_field_ptr *ptr) +{ + const char *var = switch_xml_attr(cfg, "name"); + const char *val = switch_xml_attr(cfg, "value"); + const char *as = switch_xml_attr(cfg, "as"); + const char *type = switch_xml_attr(cfg, "type"); + const char *exclude_prefix = switch_xml_attr(cfg, "exclude-prefix"); + const char *serialize_as = switch_xml_attr(cfg, "serialize-as"); + kazoo_field_ptr cur = (kazoo_field_ptr) switch_core_alloc(pool, sizeof(kazoo_field)); + cur->in_type = FIELD_NONE; + cur->out_type = JSON_NONE; + + if(var) + cur->name = switch_core_strdup(pool, var); + + if(val) + cur->value = switch_core_strdup(pool, val); + + if(as) + cur->as = switch_core_strdup(pool, as); + + if(type) { + if (!strncmp(type, "copy", 4)) { + cur->in_type = FIELD_COPY; + } else if (!strncmp(type, "static", 6)) { + cur->in_type = FIELD_STATIC; + } else if (!strncmp(type, "first-of", 8)) { + cur->in_type = FIELD_FIRST_OF; + } else if (!strncmp(type, "expand", 6)) { + cur->in_type = FIELD_EXPAND; + } else if (!strncmp(type, "prefix", 10)) { + cur->in_type = FIELD_PREFIX; + } else if (!strncmp(type, "group", 5)) { + cur->in_type = FIELD_GROUP; + } else if (!strncmp(type, "reference", 9)) { + cur->in_type = FIELD_REFERENCE; + } + } + + if(serialize_as) { + if (!strncmp(serialize_as, "string", 5)) { + cur->out_type = JSON_STRING; + } else if (!strncmp(serialize_as, "number", 6)) { + cur->out_type = JSON_NUMBER; + } else if (!strncmp(serialize_as, "boolean", 7)) { + cur->out_type = JSON_BOOLEAN; + } else if (!strncmp(serialize_as, "object", 6)) { + cur->out_type = JSON_OBJECT; + } else if (!strncmp(serialize_as, "raw", 6)) { + cur->out_type = JSON_RAW; + } + } + + if(exclude_prefix) + cur->exclude_prefix = switch_true(exclude_prefix); + + kazoo_config_filters(pool, cfg, &cur->filter); + kazoo_config_fields(definitions, pool, cfg, &cur->children); + + if(cur->children != NULL + && (cur->in_type == FIELD_STATIC) + && (cur->out_type == JSON_NONE) + ) { + cur->out_type = JSON_OBJECT; + } + if(cur->in_type == FIELD_NONE) { + cur->in_type = FIELD_COPY; + } + + if(cur->out_type == JSON_NONE) { + cur->out_type = JSON_STRING; + } + + if(cur->in_type == FIELD_FIRST_OF) { + cur->list.size = switch_separate_string(cur->value, '|', cur->list.value, MAX_LIST_FIELDS); + } + + if(cur->in_type == FIELD_REFERENCE) { + cur->ref = (kazoo_definition_ptr)switch_core_hash_find(definitions->hash, cur->name); + if(cur->ref == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "referenced field %s not found\n", cur->name); + } + } + + *ptr = cur; + + return SWITCH_STATUS_SUCCESS; + +} + +switch_status_t kazoo_config_fields_loop(kazoo_config_ptr definitions, switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_field_ptr *ptr) +{ + switch_xml_t field; + kazoo_field_ptr root = NULL, prv = NULL; + + + for (field = switch_xml_child(cfg, "field"); field; field = field->next) { + kazoo_field_ptr cur = NULL; + kazoo_config_field(definitions, pool, field, &cur); + if(root == NULL) { + root = prv = cur; + } else { + prv->next = cur; + prv = cur; + } + } + + *ptr = root; + + return SWITCH_STATUS_SUCCESS; + +} + +switch_status_t kazoo_config_fields(kazoo_config_ptr definitions, switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_fields_ptr *ptr) +{ + switch_xml_t fields; +// char *routing_key = NULL; + kazoo_fields_ptr root = NULL; + + + if ((fields = switch_xml_child(cfg, "fields")) != NULL) { + const char *verbose = switch_xml_attr(fields, "verbose"); + root = (kazoo_fields_ptr) switch_core_alloc(pool, sizeof(kazoo_fields)); + root->verbose = SWITCH_TRUE; + if(verbose) { + root->verbose = switch_true(verbose); + } + + kazoo_config_fields_loop(definitions, pool, fields, &root->head); + + } + + *ptr = root; + + return SWITCH_STATUS_SUCCESS; + +} + +kazoo_config_ptr kazoo_config_event_handlers(kazoo_config_ptr definitions, switch_xml_t cfg) +{ + switch_xml_t xml_profiles = NULL, xml_profile = NULL; + kazoo_config_ptr profiles = NULL; + switch_memory_pool_t *pool = NULL; + + if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "error creating memory pool for producers\n"); + return NULL; + } + + profiles = switch_core_alloc(pool, sizeof(kazoo_config)); + profiles->pool = pool; + switch_core_hash_init(&profiles->hash); + + if ((xml_profiles = switch_xml_child(cfg, "event-handlers"))) { + if ((xml_profile = switch_xml_child(xml_profiles, "profile"))) { + for (; xml_profile; xml_profile = xml_profile->next) { + const char *name = switch_xml_attr(xml_profile, "name"); + if(name == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing attr name\n" ); + continue; + } + kazoo_config_event_handler(definitions, profiles, xml_profile, NULL); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unable to locate a event-handler profile for kazoo\n" ); + } + } else { + destroy_config(&profiles); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unable to locate event-handlers section for kazoo\n" ); + } + + return profiles; + +} + +kazoo_config_ptr kazoo_config_fetch_handlers(kazoo_config_ptr definitions, switch_xml_t cfg) +{ + switch_xml_t xml_profiles = NULL, xml_profile = NULL; + kazoo_config_ptr profiles = NULL; + switch_memory_pool_t *pool = NULL; + + if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "error creating memory pool for producers\n"); + return NULL; + } + + profiles = switch_core_alloc(pool, sizeof(kazoo_config)); + profiles->pool = pool; + switch_core_hash_init(&profiles->hash); + + if ((xml_profiles = switch_xml_child(cfg, "fetch-handlers"))) { + if ((xml_profile = switch_xml_child(xml_profiles, "profile"))) { + for (; xml_profile; xml_profile = xml_profile->next) { + const char *name = switch_xml_attr(xml_profile, "name"); + if(name == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing attr name\n" ); + continue; + } + kazoo_config_fetch_handler(definitions, profiles, xml_profile, NULL); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unable to locate a fetch-handler profile for kazoo\n" ); + } + } else { + destroy_config(&profiles); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unable to locate fetch-handlers section for kazoo\n" ); + } + + return profiles; + +} + + +switch_status_t kazoo_config_definition(kazoo_config_ptr root, switch_xml_t cfg) +{ + kazoo_definition_ptr definition = NULL; + char *name = (char *) switch_xml_attr_soft(cfg, "name"); + + if (zstr(name)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to load kazoo profile. Check definition missing name attr\n"); + return SWITCH_STATUS_GENERR; + } + + definition = switch_core_alloc(root->pool, sizeof(kazoo_definition)); + definition->name = switch_core_strdup(root->pool, name); + + kazoo_config_filters(root->pool, cfg, &definition->filter); + kazoo_config_fields_loop(root, root->pool, cfg, &definition->head); + + if ( switch_core_hash_insert(root->hash, name, (void *) definition) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to insert new definition [%s] into kazoo definitions hash\n", name); + return SWITCH_STATUS_GENERR; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Definition[%s] Successfully configured\n", definition->name); + return SWITCH_STATUS_SUCCESS; +} + +kazoo_config_ptr kazoo_config_definitions(switch_xml_t cfg) +{ + switch_xml_t xml_definitions = NULL, xml_definition = NULL; + kazoo_config_ptr definitions = NULL; + switch_memory_pool_t *pool = NULL; + + if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "error creating memory pool for definitions\n"); + return NULL; + } + + definitions = switch_core_alloc(pool, sizeof(kazoo_config)); + definitions->pool = pool; + switch_core_hash_init(&definitions->hash); + + if ((xml_definitions = switch_xml_child(cfg, "definitions"))) { + if ((xml_definition = switch_xml_child(xml_definitions, "definition"))) { + for (; xml_definition; xml_definition = xml_definition->next) { + kazoo_config_definition(definitions, xml_definition); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "no definitions for kazoo\n" ); + } + } else { + destroy_config(&definitions); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "no definitions section for kazoo\n" ); + } + + return definitions; +} + +void destroy_config(kazoo_config_ptr *ptr) +{ + kazoo_config_ptr config = NULL; + switch_memory_pool_t *pool; + + if (!ptr || !*ptr) { + return; + } + config = *ptr; + pool = config->pool; + + switch_core_hash_destroy(&(config->hash)); + switch_core_destroy_memory_pool(&pool); + + *ptr = NULL; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + */ 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 index 0000000000..cb6c92ba59 --- /dev/null +++ b/src/mod/event_handlers/mod_kazoo/kazoo_config.h @@ -0,0 +1,62 @@ +/* +* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* Copyright (C) 2005-2012, Anthony Minessale II +* +* 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 +* Portions created by the Initial Developer are Copyright (C) +* the Initial Developer. All Rights Reserved. +* +* Based on mod_skel by +* Anthony Minessale II +* +* Contributor(s): +* +* Daniel Bryars +* Tim Brown +* Anthony Minessale II +* William King +* Mike Jerris +* +* kazoo.c -- Sends FreeSWITCH events to an AMQP broker +* +*/ + +#ifndef KAZOO_CONFIG_H +#define KAZOO_CONFIG_H + +#include + + +struct kazoo_config_t { + switch_hash_t *hash; + switch_memory_pool_t *pool; +}; + +switch_status_t kazoo_config_loglevels(switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_loglevels_ptr *ptr); +switch_status_t kazoo_config_filters(switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_filter_ptr *ptr); +switch_status_t kazoo_config_fields(kazoo_config_ptr definitions, switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_fields_ptr *ptr); + +switch_status_t kazoo_config_event_handler(kazoo_config_ptr definitions, kazoo_config_ptr root, switch_xml_t cfg, kazoo_event_profile_ptr *ptr); +switch_status_t kazoo_config_fetch_handler(kazoo_config_ptr definitions, kazoo_config_ptr root, switch_xml_t cfg, kazoo_fetch_profile_ptr *ptr); +kazoo_config_ptr kazoo_config_event_handlers(kazoo_config_ptr definitions, switch_xml_t cfg); +kazoo_config_ptr kazoo_config_fetch_handlers(kazoo_config_ptr definitions, switch_xml_t cfg); +kazoo_config_ptr kazoo_config_definitions(switch_xml_t cfg); +void destroy_config(kazoo_config_ptr *ptr); + +#endif /* KAZOO_CONFIG_H */ + diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_dptools.c b/src/mod/event_handlers/mod_kazoo/kazoo_dptools.c index 0ea4cf6f2c..bf90626564 100644 --- a/src/mod/event_handlers/mod_kazoo/kazoo_dptools.c +++ b/src/mod/event_handlers/mod_kazoo/kazoo_dptools.c @@ -52,6 +52,14 @@ #define EXPORT_LONG_DESC "Export many channel variables for the channel calling the application" #define EXPORT_SYNTAX "[^^]= =" +#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 "" + +#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 " [^^]= =" + static void base_set (switch_core_session_t *session, const char *data, switch_stack_t stack) { char *var, *val = NULL; @@ -99,6 +107,22 @@ static void base_set (switch_core_session_t *session, const char *data, switch_s } } +static int kz_is_exported(switch_core_session_t *session, char *varname) +{ + char *array[256] = {0}; + int i, argc; + switch_channel_t *channel = switch_core_session_get_channel(session); + const char *exports = switch_channel_get_variable(channel, SWITCH_EXPORT_VARS_VARIABLE); + char *arg = switch_core_session_strdup(session, exports); + argc = switch_split(arg, ',', array); + for(i=0; i < argc; i++) { + if(!strcasecmp(array[i], varname)) + return 1; + } + + return 0; +} + static void base_export (switch_core_session_t *session, const char *data, switch_stack_t stack) { char *var, *val = NULL; @@ -121,20 +145,52 @@ static void base_export (switch_core_session_t *session, const char *data, switc } } - if (val) { - expanded = switch_channel_expand_variables(channel, val); - } + if(!kz_is_exported(session, var)) { + if (val) { + expanded = switch_channel_expand_variables(channel, val); + } - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s EXPORT [%s]=[%s]\n", switch_channel_get_name(channel), var, - expanded ? expanded : "UNDEF"); - switch_channel_export_variable_var_check(channel, var, expanded, SWITCH_EXPORT_VARS_VARIABLE, SWITCH_FALSE); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s EXPORT [%s]=[%s]\n", switch_channel_get_name(channel), var, + expanded ? expanded : "UNDEF"); + switch_channel_export_variable_var_check(channel, var, expanded, SWITCH_EXPORT_VARS_VARIABLE, SWITCH_FALSE); - if (expanded && expanded != val) { - switch_safe_free(expanded); + if (expanded && expanded != val) { + switch_safe_free(expanded); + } + } else { + switch_channel_set_variable(channel, var, val); } } } +SWITCH_STANDARD_APP(prefix_unset_function) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_event_header_t *ei = NULL; + switch_event_t *clear; + char *arg = (char *) data; + + if(switch_event_create(&clear, SWITCH_EVENT_CLONE) != SWITCH_STATUS_SUCCESS) { + return; + } + + for (ei = switch_channel_variable_first(channel); ei; ei = ei->next) { + const char *name = ei->name; + char *value = ei->value; + if (!strncasecmp(name, arg, strlen(arg))) { + switch_event_add_header_string(clear, SWITCH_STACK_BOTTOM, name, value); + } + } + + switch_channel_variable_last(channel); + for (ei = clear->headers; ei; ei = ei->next) { + char *varname = ei->name; + switch_channel_set_variable(channel, varname, NULL); + } + + switch_event_destroy(&clear); +} + SWITCH_STANDARD_APP(multiset_function) { char delim = ' '; char *arg = (char *) data; @@ -145,25 +201,79 @@ SWITCH_STANDARD_APP(multiset_function) { delim = *arg++; } - if (arg) { + if(delim != '\0') { switch_channel_t *channel = switch_core_session_get_channel(session); - char *array[256] = {0}; - int i, argc; + if (arg) { + char *array[256] = {0}; + int i, argc; - arg = switch_core_session_strdup(session, arg); - argc = switch_split(arg, delim, array); + arg = switch_core_session_strdup(session, arg); + argc = switch_split(arg, delim, array); - for(i = 0; i < argc; i++) { - base_set(session, array[i], SWITCH_STACK_BOTTOM); + for(i = 0; i < argc; i++) { + base_set(session, array[i], SWITCH_STACK_BOTTOM); + } } - if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { switch_channel_event_set_data(channel, event); switch_event_fire(&event); } - } else { - base_set(session, data, SWITCH_STACK_BOTTOM); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "multiset with empty args\n"); + } +} + +SWITCH_STANDARD_APP(uuid_multiset_function) { + + char delim = ' '; + char *arg0 = (char *) data; + char *arg = strchr(arg0, ' '); + switch_event_t *event; + + + if(arg == NULL) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "uuid_multiset with invalid args\n"); + return; + } + *arg = '\0'; + arg++; + + if(zstr(arg0)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "uuid_multiset with invalid uuid\n"); + return; + } + + + if (!zstr(arg) && *arg == '^' && *(arg+1) == '^') { + arg += 2; + delim = *arg++; + } + + if(delim != '\0') { + switch_core_session_t *uuid_session = NULL; + if ((uuid_session = switch_core_session_force_locate(arg0)) != NULL) { + switch_channel_t *uuid_channel = switch_core_session_get_channel(uuid_session); + if (arg) { + char *array[256] = {0}; + int i, argc; + + arg = switch_core_session_strdup(session, arg); + argc = switch_split(arg, delim, array); + + for(i = 0; i < argc; i++) { + base_set(uuid_session, array[i], SWITCH_STACK_BOTTOM); + } + } + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(uuid_channel, event); + switch_event_fire(&event); + } + switch_core_session_rwunlock(uuid_session); + } else { + base_set(session, data, SWITCH_STACK_BOTTOM); + } + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "multiset with empty args\n"); } } @@ -205,19 +315,23 @@ SWITCH_STANDARD_APP(multiunset_function) { delim = *arg++; } - if (arg) { - char *array[256] = {0}; - int i, argc; + if(delim != '\0') { + if (arg) { + char *array[256] = {0}; + int i, argc; - arg = switch_core_session_strdup(session, arg); - argc = switch_split(arg, delim, array); + arg = switch_core_session_strdup(session, arg); + argc = switch_split(arg, delim, array); - for(i = 0; i < argc; i++) { - switch_channel_set_variable(switch_core_session_get_channel(session), array[i], NULL); + for(i = 0; i < argc; i++) { + switch_channel_set_variable(switch_core_session_get_channel(session), array[i], NULL); + } + + } else { + switch_channel_set_variable(switch_core_session_get_channel(session), arg, NULL); } - } else { - switch_channel_set_variable(switch_core_session_get_channel(session), arg, NULL); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "multiunset with empty args\n"); } } @@ -230,18 +344,22 @@ SWITCH_STANDARD_APP(export_function) { delim = *arg++; } - if (arg) { - char *array[256] = {0}; - int i, argc; + if(delim != '\0') { + if (arg) { + char *array[256] = {0}; + int i, argc; - arg = switch_core_session_strdup(session, arg); - argc = switch_split(arg, delim, array); + arg = switch_core_session_strdup(session, arg); + argc = switch_split(arg, delim, array); - for(i = 0; i < argc; i++) { - base_export(session, array[i], SWITCH_STACK_BOTTOM); - } + for(i = 0; i < argc; i++) { + base_export(session, array[i], SWITCH_STACK_BOTTOM); + } + } else { + base_export(session, data, SWITCH_STACK_BOTTOM); + } } else { - base_export(session, data, SWITCH_STACK_BOTTOM); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "export with empty args\n"); } } @@ -254,6 +372,11 @@ void add_kz_dptools(switch_loadable_module_interface_t **module_interface, switc SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "kz_multiunset", MULTISET_SHORT_DESC, MULTISET_LONG_DESC, multiunset_function, MULTIUNSET_SYNTAX, SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); - SWITCH_ADD_APP(app_interface, "kz_export", EXPORT_SHORT_DESC, EXPORT_LONG_DESC, export_function, EXPORT_SYNTAX, - SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); + SWITCH_ADD_APP(app_interface, "kz_export", EXPORT_SHORT_DESC, EXPORT_LONG_DESC, export_function, EXPORT_SYNTAX, + SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); + SWITCH_ADD_APP(app_interface, "kz_prefix_unset", PREFIX_UNSET_SHORT_DESC, PREFIX_UNSET_LONG_DESC, prefix_unset_function, PREFIX_UNSET_SYNTAX, + SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); + SWITCH_ADD_APP(app_interface, "kz_uuid_multiset", UUID_MULTISET_SHORT_DESC, UUID_MULTISET_LONG_DESC, uuid_multiset_function, UUID_MULTISET_SYNTAX, + SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); + } 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 index 0000000000..c317b617be --- /dev/null +++ b/src/mod/event_handlers/mod_kazoo/kazoo_ei.h @@ -0,0 +1,262 @@ +#ifndef KAZOO_EI_H +#define KAZOO_EI_H + +#include +#include + +#define MODNAME "mod_kazoo" +#define BUNDLE "community" +#define RELEASE "v1.5.0-1" +#define VERSION "mod_kazoo v1.5.0-1 community" + +typedef enum {KAZOO_FETCH_PROFILE, KAZOO_EVENT_PROFILE} kazoo_profile_type; + +typedef enum {ERLANG_TUPLE, ERLANG_MAP} kazoo_json_term; + +typedef struct ei_xml_agent_s ei_xml_agent_t; +typedef ei_xml_agent_t *ei_xml_agent_ptr; + +typedef struct kazoo_event kazoo_event_t; +typedef kazoo_event_t *kazoo_event_ptr; + +typedef struct kazoo_event_profile kazoo_event_profile_t; +typedef kazoo_event_profile_t *kazoo_event_profile_ptr; + +typedef struct kazoo_fetch_profile kazoo_fetch_profile_t; +typedef kazoo_fetch_profile_t *kazoo_fetch_profile_ptr; + +typedef struct kazoo_config_t kazoo_config; +typedef kazoo_config *kazoo_config_ptr; + +#include "kazoo_fields.h" +#include "kazoo_config.h" + +struct ei_send_msg_s { + ei_x_buff buf; + erlang_pid pid; +}; +typedef struct ei_send_msg_s ei_send_msg_t; + +struct ei_received_msg_s { + ei_x_buff buf; + erlang_msg msg; +}; +typedef struct ei_received_msg_s ei_received_msg_t; + + +typedef struct ei_event_stream_s ei_event_stream_t; +typedef struct ei_node_s ei_node_t; + +struct ei_event_binding_s { + char id[SWITCH_UUID_FORMATTED_LENGTH + 1]; + switch_event_node_t *node; + switch_event_types_t type; + const char *subclass_name; + ei_event_stream_t* stream; + kazoo_event_ptr event; + + struct ei_event_binding_s *next; +}; +typedef struct ei_event_binding_s ei_event_binding_t; + +struct ei_event_stream_s { + switch_memory_pool_t *pool; + ei_event_binding_t *bindings; + switch_queue_t *queue; + switch_socket_t *acceptor; + switch_pollset_t *pollset; + switch_pollfd_t *pollfd; + switch_socket_t *socket; + switch_mutex_t *socket_mutex; + switch_bool_t connected; + char remote_ip[48]; + uint16_t remote_port; + char local_ip[48]; + uint16_t local_port; + erlang_pid pid; + uint32_t flags; + ei_node_t *node; + struct ei_event_stream_s *next; +}; + +struct ei_node_s { + int nodefd; + switch_atomic_t pending_bgapi; + switch_atomic_t receive_handlers; + switch_memory_pool_t *pool; + ei_event_stream_t *event_streams; + switch_mutex_t *event_streams_mutex; + switch_queue_t *send_msgs; + switch_queue_t *received_msgs; + char *peer_nodename; + switch_time_t created_time; + switch_socket_t *socket; + char remote_ip[48]; + uint16_t remote_port; + char local_ip[48]; + uint16_t local_port; + uint32_t flags; + int legacy; + struct ei_node_s *next; +}; + + +struct xml_fetch_reply_s { + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + char *xml_str; + struct xml_fetch_reply_s *next; +}; +typedef struct xml_fetch_reply_s xml_fetch_reply_t; + +struct fetch_handler_s { + erlang_pid pid; + struct fetch_handler_s *next; +}; +typedef struct fetch_handler_s fetch_handler_t; + +struct ei_xml_client_s { + ei_node_t *ei_node; + fetch_handler_t *fetch_handlers; + struct ei_xml_client_s *next; +}; +typedef struct ei_xml_client_s ei_xml_client_t; + +struct ei_xml_agent_s { + switch_memory_pool_t *pool; + switch_xml_section_t section; + switch_thread_rwlock_t *lock; + ei_xml_client_t *clients; + switch_mutex_t *current_client_mutex; + ei_xml_client_t *current_client; + switch_mutex_t *replies_mutex; + switch_thread_cond_t *new_reply; + xml_fetch_reply_t *replies; + kazoo_fetch_profile_ptr profile; + +}; + +struct globals_s { + switch_memory_pool_t *pool; + switch_atomic_t threads; + switch_socket_t *acceptor; + struct ei_cnode_s ei_cnode; + switch_thread_rwlock_t *ei_nodes_lock; + ei_node_t *ei_nodes; + + switch_xml_binding_t *config_fetch_binding; + switch_xml_binding_t *directory_fetch_binding; + switch_xml_binding_t *dialplan_fetch_binding; + switch_xml_binding_t *channels_fetch_binding; + switch_xml_binding_t *languages_fetch_binding; + switch_xml_binding_t *chatplan_fetch_binding; + + switch_hash_t *event_filter; + int epmdfd; + int num_worker_threads; + switch_bool_t nat_map; + switch_bool_t ei_shortname; + int ei_compat_rel; + char *ip; + char *hostname; + char *ei_cookie; + char *ei_nodename; + char *kazoo_var_prefix; + int var_prefix_length; + uint32_t flags; + int send_all_headers; + int send_all_private_headers; + int connection_timeout; + int receive_timeout; + int receive_msg_preallocate; + int event_stream_preallocate; + int send_msg_batch; + short event_stream_framing; + switch_port_t port; + int config_fetched; + int io_fault_tolerance; + kazoo_event_profile_ptr events; + kazoo_config_ptr definitions; + kazoo_config_ptr event_handlers; + kazoo_config_ptr fetch_handlers; + kazoo_json_term json_encoding; + + int delete_file_after_put; + int enable_legacy; + +}; +typedef struct globals_s globals_t; +extern globals_t kazoo_globals; + +/* kazoo_event_stream.c */ +ei_event_stream_t *find_event_stream(ei_event_stream_t *event_streams, const erlang_pid *from); + +//ei_event_stream_t *new_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from); +ei_event_stream_t *new_event_stream(ei_node_t *ei_node, const erlang_pid *from); + + +switch_status_t remove_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from); +switch_status_t remove_event_streams(ei_event_stream_t **event_streams); +unsigned long get_stream_port(const ei_event_stream_t *event_stream); +switch_status_t add_event_binding(ei_event_stream_t *event_stream, const char *event_name); +//switch_status_t add_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name); +switch_status_t remove_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name); +switch_status_t remove_event_bindings(ei_event_stream_t *event_stream); + +/* kazoo_node.c */ +switch_status_t new_kazoo_node(int nodefd, ErlConnect *conn); + +/* kazoo_ei_utils.c */ +void close_socket(switch_socket_t **sock); +void close_socketfd(int *sockfd); +switch_socket_t *create_socket_with_port(switch_memory_pool_t *pool, switch_port_t port); +switch_socket_t *create_socket(switch_memory_pool_t *pool); +switch_status_t create_ei_cnode(const char *ip_addr, const char *name, struct ei_cnode_s *ei_cnode); +switch_status_t ei_compare_pids(const erlang_pid *pid1, const erlang_pid *pid2); +void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event); +void ei_encode_switch_event_headers_2(ei_x_buff *ebuf, switch_event_t *event, int decode); +void ei_encode_json(ei_x_buff *ebuf, cJSON *JObj); +void ei_link(ei_node_t *ei_node, erlang_pid * from, erlang_pid * to); +void ei_encode_switch_event(ei_x_buff * ebuf, switch_event_t *event); +int ei_helper_send(ei_node_t *ei_node, erlang_pid* to, ei_x_buff *buf); +int ei_decode_atom_safe(char *buf, int *index, char *dst); +int ei_decode_string_or_binary_limited(char *buf, int *index, int maxsize, char *dst); +int ei_decode_string_or_binary(char *buf, int *index, char **dst); +switch_status_t create_acceptor(); +switch_hash_t *create_default_filter(); + +void fetch_config(); + +switch_status_t kazoo_load_config(); +void kazoo_destroy_config(); + + +#define _ei_x_encode_string(buf, string) { ei_x_encode_binary(buf, string, strlen(string)); } + +/* kazoo_fetch_agent.c */ +switch_status_t bind_fetch_agents(); +switch_status_t unbind_fetch_agents(); +switch_status_t remove_xml_clients(ei_node_t *ei_node); +switch_status_t add_fetch_handler(ei_node_t *ei_node, erlang_pid *from, switch_xml_binding_t *binding); +switch_status_t remove_fetch_handlers(ei_node_t *ei_node, erlang_pid *from); +switch_status_t fetch_reply(char *uuid_str, char *xml_str, switch_xml_binding_t *binding); +switch_status_t handle_api_command_streams(ei_node_t *ei_node, switch_stream_handle_t *stream); + +void bind_event_profiles(kazoo_event_ptr event); +void rebind_fetch_profiles(kazoo_config_ptr fetch_handlers); +switch_status_t kazoo_config_handlers(switch_xml_t cfg); + +/* runtime */ +SWITCH_MODULE_RUNTIME_FUNCTION(mod_kazoo_runtime); + +#endif /* KAZOO_EI_H */ + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ 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 index 0000000000..7eb93af5d8 --- /dev/null +++ b/src/mod/event_handlers/mod_kazoo/kazoo_ei_config.c @@ -0,0 +1,543 @@ +/* +* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* Copyright (C) 2005-2012, Anthony Minessale II +* +* 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 +* Portions created by the Initial Developer are Copyright (C) +* the Initial Developer. All Rights Reserved. +* +* Based on mod_skel by +* Anthony Minessale II +* +* Contributor(s): +* +* Daniel Bryars +* Tim Brown +* Anthony Minessale II +* William King +* Mike Jerris +* +* kazoo.c -- Sends FreeSWITCH events to an AMQP broker +* +*/ + +#include "mod_kazoo.h" +#include "kazoo_definitions.h" + +#define KAZOO_DECLARE_GLOBAL_STRING_FUNC(fname, vname) static void __attribute__((__unused__)) fname(const char *string) { if (!string) return;\ + if (vname) {free(vname); vname = NULL;}vname = strdup(string);} static void fname(const char *string) + +KAZOO_DECLARE_GLOBAL_STRING_FUNC(set_pref_ip, kazoo_globals.ip); +KAZOO_DECLARE_GLOBAL_STRING_FUNC(set_pref_ei_cookie, kazoo_globals.ei_cookie); +KAZOO_DECLARE_GLOBAL_STRING_FUNC(set_pref_ei_nodename, kazoo_globals.ei_nodename); +KAZOO_DECLARE_GLOBAL_STRING_FUNC(set_pref_kazoo_var_prefix, kazoo_globals.kazoo_var_prefix); + +static int read_cookie_from_file(char *filename) { + int fd; + char cookie[MAXATOMLEN + 1]; + char *end; + struct stat buf; + ssize_t res; + + if (!stat(filename, &buf)) { + if ((buf.st_mode & S_IRWXG) || (buf.st_mode & S_IRWXO)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s must only be accessible by owner only.\n", filename); + return 2; + } + if (buf.st_size > MAXATOMLEN) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s contains a cookie larger than the maximum atom size of %d.\n", filename, MAXATOMLEN); + return 2; + } + fd = open(filename, O_RDONLY); + if (fd < 1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to open cookie file %s : %d.\n", filename, errno); + return 2; + } + + if ((res = read(fd, cookie, MAXATOMLEN)) < 1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read cookie file %s : %d.\n", filename, errno); + } + + cookie[MAXATOMLEN] = '\0'; + + /* replace any end of line characters with a null */ + if ((end = strchr(cookie, '\n'))) { + *end = '\0'; + } + + if ((end = strchr(cookie, '\r'))) { + *end = '\0'; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set cookie from file %s: %s\n", filename, cookie); + + set_pref_ei_cookie(cookie); + return 0; + } else { + /* don't error here, because we might be blindly trying to read $HOME/.erlang.cookie, and that can fail silently */ + return 1; + } +} + + +switch_status_t kazoo_ei_config(switch_xml_t cfg) { + switch_xml_t child, param; + kazoo_globals.send_all_headers = 0; + kazoo_globals.send_all_private_headers = 1; + kazoo_globals.connection_timeout = 500; + kazoo_globals.receive_timeout = 200; + kazoo_globals.receive_msg_preallocate = 2000; + kazoo_globals.event_stream_preallocate = 4000; + kazoo_globals.send_msg_batch = 10; + kazoo_globals.event_stream_framing = 2; + kazoo_globals.port = 0; + kazoo_globals.io_fault_tolerance = 10; + kazoo_globals.json_encoding = ERLANG_TUPLE; + kazoo_globals.enable_legacy = SWITCH_TRUE; + kazoo_globals.delete_file_after_put = SWITCH_TRUE; + + if ((child = switch_xml_child(cfg, "settings"))) { + for (param = switch_xml_child(child, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcmp(var, "listen-ip")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set bind ip address: %s\n", val); + set_pref_ip(val); + } else if (!strcmp(var, "listen-port")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set bind port: %s\n", val); + kazoo_globals.port = atoi(val); + } else if (!strcmp(var, "cookie")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set cookie: %s\n", val); + set_pref_ei_cookie(val); + } else if (!strcmp(var, "cookie-file")) { + if (read_cookie_from_file(val) == 1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read cookie from %s\n", val); + } + } else if (!strcmp(var, "nodename")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set node name: %s\n", val); + set_pref_ei_nodename(val); + } else if (!strcmp(var, "shortname")) { + kazoo_globals.ei_shortname = switch_true(val); + } else if (!strcmp(var, "kazoo-var-prefix")) { + set_pref_kazoo_var_prefix(val); + } else if (!strcmp(var, "compat-rel")) { + if (atoi(val) >= 7) + kazoo_globals.ei_compat_rel = atoi(val); + else + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid compatibility release '%s' specified\n", val); + } else if (!strcmp(var, "nat-map")) { + kazoo_globals.nat_map = switch_true(val); + } else if (!strcmp(var, "send-all-headers")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-all-headers: %s\n", val); + kazoo_globals.send_all_headers = switch_true(val); + } else if (!strcmp(var, "send-all-private-headers")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-all-private-headers: %s\n", val); + kazoo_globals.send_all_private_headers = switch_true(val); + } else if (!strcmp(var, "connection-timeout")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set connection-timeout: %s\n", val); + kazoo_globals.connection_timeout = atoi(val); + } else if (!strcmp(var, "receive-timeout")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set receive-timeout: %s\n", val); + kazoo_globals.receive_timeout = atoi(val); + } else if (!strcmp(var, "receive-msg-preallocate")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set receive-msg-preallocate: %s\n", val); + kazoo_globals.receive_msg_preallocate = atoi(val); + } else if (!strcmp(var, "event-stream-preallocate")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set event-stream-preallocate: %s\n", val); + kazoo_globals.event_stream_preallocate = atoi(val); + } else if (!strcmp(var, "send-msg-batch-size")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-msg-batch-size: %s\n", val); + kazoo_globals.send_msg_batch = atoi(val); + } else if (!strcmp(var, "event-stream-framing")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set event-stream-framing: %s\n", val); + kazoo_globals.event_stream_framing = atoi(val); + } else if (!strcmp(var, "io-fault-tolerance")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set io-fault-tolerance: %s\n", val); + kazoo_globals.io_fault_tolerance = atoi(val); + } else if (!strcmp(var, "num-worker-threads")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set num-worker-threads: %s\n", val); + kazoo_globals.num_worker_threads = atoi(val); + } else if (!strcmp(var, "json-term-encoding")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set json-term-encoding: %s\n", val); + if(!strcmp(val, "map")) { + kazoo_globals.json_encoding = ERLANG_MAP; + } + } else if (!strcmp(var, "enable-legacy")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set enable-legacy: %s\n", val); + kazoo_globals.enable_legacy = switch_true(val); + } else if (!strcmp(var, "delete-file-after-put")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set delete-file-after-put: %s\n", val); + kazoo_globals.delete_file_after_put = switch_true(val); + } + } + } + + if ((child = switch_xml_child(cfg, "event-filter"))) { + switch_hash_t *filter; + + switch_core_hash_init(&filter); + for (param = switch_xml_child(child, "header"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + switch_core_hash_insert(filter, var, "1"); + } + kazoo_globals.event_filter = filter; + } + + if (kazoo_globals.receive_msg_preallocate < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid receive message preallocate value, disabled\n"); + kazoo_globals.receive_msg_preallocate = 0; + } + + if (kazoo_globals.event_stream_preallocate < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event stream preallocate value, disabled\n"); + kazoo_globals.event_stream_preallocate = 0; + } + + if (kazoo_globals.send_msg_batch < 1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid send message batch size, reverting to default\n"); + kazoo_globals.send_msg_batch = 10; + } + + if (kazoo_globals.io_fault_tolerance < 1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid I/O fault tolerance, reverting to default\n"); + kazoo_globals.io_fault_tolerance = 10; + } + + if (!kazoo_globals.event_filter) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Event filter not found in configuration, using default\n"); + kazoo_globals.event_filter = create_default_filter(); + } + + if (kazoo_globals.event_stream_framing < 1 || kazoo_globals.event_stream_framing > 4) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event stream framing value, using default\n"); + kazoo_globals.event_stream_framing = 2; + } + + if (zstr(kazoo_globals.kazoo_var_prefix)) { + set_pref_kazoo_var_prefix("variable_ecallmgr*"); + kazoo_globals.var_prefix_length = 17; //ignore the * + } else { + /* we could use the global pool but then we would have to conditionally + * free the pointer if it was not drawn from the XML */ + char *buf; + int size = switch_snprintf(NULL, 0, "variable_%s*", kazoo_globals.kazoo_var_prefix) + 1; + + switch_malloc(buf, size); + switch_snprintf(buf, size, "variable_%s*", kazoo_globals.kazoo_var_prefix); + switch_safe_free(kazoo_globals.kazoo_var_prefix); + kazoo_globals.kazoo_var_prefix = buf; + kazoo_globals.var_prefix_length = size - 2; //ignore the * + } + + if (!kazoo_globals.num_worker_threads) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Number of worker threads not found in configuration, using default\n"); + kazoo_globals.num_worker_threads = 10; + } + + if (zstr(kazoo_globals.ip)) { + set_pref_ip("0.0.0.0"); + } + + if (zstr(kazoo_globals.ei_cookie)) { + int res; + char *home_dir = getenv("HOME"); + char path_buf[1024]; + + if (!zstr(home_dir)) { + /* $HOME/.erlang.cookie */ + switch_snprintf(path_buf, sizeof (path_buf), "%s%s%s", home_dir, SWITCH_PATH_SEPARATOR, ".erlang.cookie"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Checking for cookie at path: %s\n", path_buf); + + res = read_cookie_from_file(path_buf); + if (res) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No cookie or valid cookie file specified, using default cookie\n"); + set_pref_ei_cookie("ClueCon"); + } + } + } + + if (!kazoo_globals.ei_nodename) { + set_pref_ei_nodename("freeswitch"); + } + + if (!kazoo_globals.nat_map) { + kazoo_globals.nat_map = 0; + } + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t kazoo_config_handlers(switch_xml_t cfg) +{ + switch_xml_t def; + char* xml = NULL; + kazoo_config_ptr definitions, fetch_handlers, event_handlers; + kazoo_event_profile_ptr events; + + xml = strndup((char*)kazoo_conf_xml, kazoo_conf_xml_len); + def = switch_xml_parse_str_dup(xml); + + kz_xml_process(def); + kz_xml_process(cfg); + + definitions = kazoo_config_definitions(cfg); + if(definitions == NULL) { + definitions = kazoo_config_definitions(def); + } + + fetch_handlers = kazoo_config_fetch_handlers(definitions, cfg); + if(fetch_handlers == NULL) { + fetch_handlers = kazoo_config_fetch_handlers(definitions, def); + } + + event_handlers = kazoo_config_event_handlers(definitions, cfg); + if(event_handlers == NULL) { + event_handlers = kazoo_config_event_handlers(definitions, def); + } + + if(event_handlers != NULL) { + events = (kazoo_event_profile_ptr) switch_core_hash_find(event_handlers->hash, "default"); + } + + if(events == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get default handler for events\n"); + destroy_config(&event_handlers); + destroy_config(&fetch_handlers); + destroy_config(&definitions); + switch_xml_free(def); + switch_safe_free(xml); + return SWITCH_STATUS_GENERR; + } + + bind_event_profiles(events->events); + kazoo_globals.events = events; + + destroy_config(&kazoo_globals.event_handlers); + kazoo_globals.event_handlers = event_handlers; + + rebind_fetch_profiles(fetch_handlers); + destroy_config(&kazoo_globals.fetch_handlers); + kazoo_globals.fetch_handlers = fetch_handlers; + + destroy_config(&kazoo_globals.definitions); + kazoo_globals.definitions = definitions; + + + switch_xml_free(def); + switch_safe_free(xml); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t kazoo_load_config() +{ + char *cf = "kazoo.conf"; + switch_xml_t cfg, xml; + if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open configuration file %s\n", cf); + return SWITCH_STATUS_FALSE; + } else { + kazoo_ei_config(cfg); + kazoo_config_handlers(cfg); + switch_xml_free(xml); + } + + return SWITCH_STATUS_SUCCESS; +} + +void kazoo_destroy_config() +{ + destroy_config(&kazoo_globals.event_handlers); + destroy_config(&kazoo_globals.fetch_handlers); + destroy_config(&kazoo_globals.definitions); +} + +switch_status_t kazoo_config_events(kazoo_config_ptr definitions, switch_memory_pool_t *pool, switch_xml_t cfg, kazoo_event_profile_ptr profile) +{ + switch_xml_t events, event; + kazoo_event_ptr prv = NULL, cur = NULL; + + + if ((events = switch_xml_child(cfg, "events")) != NULL) { + for (event = switch_xml_child(events, "event"); event; event = event->next) { + const char *var = switch_xml_attr(event, "name"); + cur = (kazoo_event_ptr) switch_core_alloc(pool, sizeof(kazoo_event_t)); + memset(cur, 0, sizeof(kazoo_event_t)); + if(prv == NULL) { + profile->events = prv = cur; + } else { + prv->next = cur; + prv = cur; + } + cur->profile = profile; + cur->name = switch_core_strdup(pool, var); + kazoo_config_filters(pool, event, &cur->filter); + kazoo_config_fields(definitions, pool, event, &cur->fields); + + } + + } + + return SWITCH_STATUS_SUCCESS; + +} + + +switch_status_t kazoo_config_fetch_handler(kazoo_config_ptr definitions, kazoo_config_ptr root, switch_xml_t cfg, kazoo_fetch_profile_ptr *ptr) +{ + kazoo_fetch_profile_ptr profile = NULL; + switch_xml_t params, param; + switch_xml_section_t fetch_section; + int fetch_timeout = 2000000; + switch_memory_pool_t *pool = NULL; + + char *name = (char *) switch_xml_attr_soft(cfg, "name"); + if (zstr(name)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing name in profile\n"); + return SWITCH_STATUS_GENERR; + } + + if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error allocation pool for new profile : %s\n", name); + return SWITCH_STATUS_GENERR; + } + + profile = switch_core_alloc(pool, sizeof(kazoo_fetch_profile_t)); + profile->pool = pool; + profile->root = root; + profile->name = switch_core_strdup(profile->pool, name); + + fetch_section = switch_xml_parse_section_string(name); + + if ((params = switch_xml_child(cfg, "params")) != NULL) { + for (param = switch_xml_child(params, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!var) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile[%s] param missing 'name' attribute\n", name); + continue; + } + + if (!val) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile[%s] param[%s] missing 'value' attribute\n", name, var); + continue; + } + + if (!strncmp(var, "fetch-timeout", 13)) { + fetch_timeout = atoi(val); + } else if (!strncmp(var, "fetch-section", 13)) { + fetch_section = switch_xml_parse_section_string(val); + } + } + } + + if (fetch_section == SWITCH_XML_SECTION_RESULT) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Fetch Profile[%s] invalid fetch-section: %s\n", name, switch_xml_toxml(cfg, SWITCH_FALSE)); + goto err; + } + + + profile->fetch_timeout = fetch_timeout; + profile->section = fetch_section; + kazoo_config_fields(definitions, pool, cfg, &profile->fields); + kazoo_config_loglevels(pool, cfg, &profile->logging); + + if(root) { + if ( switch_core_hash_insert(root->hash, name, (void *) profile) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to insert new fetch profile [%s] into kazoo profile hash\n", name); + goto err; + } + } + + if(ptr) + *ptr = profile; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "fetch handler profile %s successfully configured\n", name); + return SWITCH_STATUS_SUCCESS; + + err: + /* Cleanup */ + if(pool) { + switch_core_destroy_memory_pool(&pool); + } + return SWITCH_STATUS_GENERR; + +} + +switch_status_t kazoo_config_event_handler(kazoo_config_ptr definitions, kazoo_config_ptr root, switch_xml_t cfg, kazoo_event_profile_ptr *ptr) +{ + kazoo_event_profile_ptr profile = NULL; + switch_memory_pool_t *pool = NULL; + + char *name = (char *) switch_xml_attr_soft(cfg, "name"); + if (zstr(name)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing name in profile\n"); + return SWITCH_STATUS_GENERR; + } + + if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error allocation pool for new profile : %s\n", name); + return SWITCH_STATUS_GENERR; + } + + profile = switch_core_alloc(pool, sizeof(kazoo_event_profile_t)); + profile->pool = pool; + profile->root = root; + profile->name = switch_core_strdup(profile->pool, name); + + kazoo_config_filters(pool, cfg, &profile->filter); + kazoo_config_fields(definitions, pool, cfg, &profile->fields); + kazoo_config_events(definitions, pool, cfg, profile); + kazoo_config_loglevels(pool, cfg, &profile->logging); + + if(root) { + if ( switch_core_hash_insert(root->hash, name, (void *) profile) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to insert new profile [%s] into kazoo profile hash\n", name); + goto err; + } + } + + if(ptr) + *ptr = profile; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "event handler profile %s successfully configured\n", name); + return SWITCH_STATUS_SUCCESS; + + err: + /* Cleanup */ + if(pool) { + switch_core_destroy_memory_pool(&pool); + } + return SWITCH_STATUS_GENERR; + +} + + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + */ 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 index 0000000000..96381f9b24 --- /dev/null +++ b/src/mod/event_handlers/mod_kazoo/kazoo_ei_utils.c @@ -0,0 +1,996 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2012, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Andrew Thompson + * Rob Charlton + * Karl Anderson + * + * Original from mod_erlang_event. + * ei_helpers.c -- helper functions for ei + * + */ +#include "mod_kazoo.h" + +/* Stolen from code added to ei in R12B-5. + * Since not everyone has this version yet; + * provide our own version. + * */ + +#define put8(s,n) do { \ + (s)[0] = (char)((n) & 0xff); \ + (s) += 1; \ + } while (0) + +#define put32be(s,n) do { \ + (s)[0] = ((n) >> 24) & 0xff; \ + (s)[1] = ((n) >> 16) & 0xff; \ + (s)[2] = ((n) >> 8) & 0xff; \ + (s)[3] = (n) & 0xff; \ + (s) += 4; \ + } while (0) + +#ifdef EI_DEBUG +static void ei_x_print_reg_msg(ei_x_buff *buf, char *dest, int send) { + char *mbuf = NULL; + int i = 1; + + ei_s_print_term(&mbuf, buf->buff, &i); + + if (send) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Encoded term %s to '%s'\n", mbuf, dest); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Decoded term %s for '%s'\n", mbuf, dest); + } + + free(mbuf); +} + +static void ei_x_print_msg(ei_x_buff *buf, erlang_pid *pid, int send) { + char *pbuf = NULL; + int i = 0; + ei_x_buff pidbuf; + + ei_x_new(&pidbuf); + ei_x_encode_pid(&pidbuf, pid); + + ei_s_print_term(&pbuf, pidbuf.buff, &i); + + ei_x_print_reg_msg(buf, pbuf, send); + free(pbuf); +} +#endif + +void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event) { + ei_encode_switch_event_headers_2(ebuf, event, 1); +} + +void ei_encode_switch_event_headers_2(ei_x_buff *ebuf, switch_event_t *event, int encode) { + switch_event_header_t *hp; + char *uuid = switch_event_get_header(event, "unique-id"); + int i; + + for (i = 0, hp = event->headers; hp; hp = hp->next, i++); + + if (event->body) + i++; + + ei_x_encode_list_header(ebuf, i + 1); + + if (uuid) { + char *unique_id = switch_event_get_header(event, "unique-id"); + ei_x_encode_binary(ebuf, unique_id, strlen(unique_id)); + } else { + ei_x_encode_atom(ebuf, "undefined"); + } + + for (hp = event->headers; hp; hp = hp->next) { + ei_x_encode_tuple_header(ebuf, 2); + ei_x_encode_binary(ebuf, hp->name, strlen(hp->name)); + if(encode) + switch_url_decode(hp->value); + ei_x_encode_binary(ebuf, hp->value, strlen(hp->value)); + } + + if (event->body) { + ei_x_encode_tuple_header(ebuf, 2); + ei_x_encode_binary(ebuf, "body", strlen("body")); + ei_x_encode_binary(ebuf, event->body, strlen(event->body)); + } + + ei_x_encode_empty_list(ebuf); +} + +int ei_json_child_count(cJSON *JObj) +{ + int mask = cJSON_False + | cJSON_True + | cJSON_NULL + | cJSON_Number + | cJSON_String + | cJSON_Array + | cJSON_Object + | cJSON_Raw; + + cJSON *item = JObj->child; + int i = 0; + while(item) { + if(item->type & mask) + i++; + item = item->next; + } + return i; + +} + +void ei_encode_json_array(ei_x_buff *ebuf, cJSON *JObj) { + cJSON *item; + int count = ei_json_child_count(JObj); + + ei_x_encode_list_header(ebuf, count); + if(count == 0) + return; + + item = JObj->child; + while(item) { + switch(item->type) { + case cJSON_String: + ei_x_encode_binary(ebuf, item->valuestring, strlen(item->valuestring)); + break; + + case cJSON_Number: + ei_x_encode_double(ebuf, item->valuedouble); + break; + + case cJSON_True: + ei_x_encode_boolean(ebuf, 1); + break; + + case cJSON_False: + ei_x_encode_boolean(ebuf, 0); + break; + + case cJSON_Object: + ei_encode_json(ebuf, item); + break; + + case cJSON_Array: + ei_encode_json_array(ebuf, item); + break; + + case cJSON_Raw: + { + cJSON *Decoded = cJSON_Parse(item->valuestring); + if(!Decoded) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR DECODING RAW JSON %s\n", item->valuestring); + ei_x_encode_tuple_header(ebuf, 0); + } else { + ei_encode_json(ebuf, Decoded); + cJSON_Delete(Decoded); + } + break; + } + + case cJSON_NULL: + ei_x_encode_atom(ebuf, "null"); + break; + + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT ENCODED %i\n", item->type); + break; + + } + item = item->next; + } + + ei_x_encode_empty_list(ebuf); + +} + +void ei_encode_json(ei_x_buff *ebuf, cJSON *JObj) { + cJSON *item; + int count = ei_json_child_count(JObj); + + if(kazoo_globals.json_encoding == ERLANG_TUPLE) { + ei_x_encode_tuple_header(ebuf, 1); + ei_x_encode_list_header(ebuf, count); + } else { + ei_x_encode_map_header(ebuf, count); + } + + if(count == 0) + return; + + item = JObj->child; + while(item) { + if(kazoo_globals.json_encoding == ERLANG_TUPLE) { + ei_x_encode_tuple_header(ebuf, 2); + } + ei_x_encode_binary(ebuf, item->string, strlen(item->string)); + + switch(item->type) { + case cJSON_String: + ei_x_encode_binary(ebuf, item->valuestring, strlen(item->valuestring)); + break; + + case cJSON_Number: + if ((fabs(((double)item->valueint) - item->valuedouble) <= DBL_EPSILON) + && (item->valuedouble <= INT_MAX) + && (item->valuedouble >= INT_MIN)) { + ei_x_encode_longlong(ebuf, item->valueint); + } else { + ei_x_encode_double(ebuf, item->valuedouble); + } + break; + + case cJSON_True: + ei_x_encode_boolean(ebuf, 1); + break; + + case cJSON_False: + ei_x_encode_boolean(ebuf, 0); + break; + + case cJSON_Object: + ei_encode_json(ebuf, item); + break; + + case cJSON_Array: + ei_encode_json_array(ebuf, item); + break; + + case cJSON_Raw: + { + cJSON *Decoded = cJSON_Parse(item->valuestring); + if(!Decoded) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR DECODING RAW JSON %s\n", item->valuestring); + ei_x_encode_tuple_header(ebuf, 0); + } else { + ei_encode_json(ebuf, Decoded); + cJSON_Delete(Decoded); + } + break; + } + + case cJSON_NULL: + ei_x_encode_atom(ebuf, "null"); + break; + + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT ENCODED %i\n", item->type); + break; + + } + item = item->next; + } + + if(kazoo_globals.json_encoding == ERLANG_TUPLE) { + ei_x_encode_empty_list(ebuf); + } + +} + +void close_socket(switch_socket_t ** sock) { + if (*sock) { + switch_socket_shutdown(*sock, SWITCH_SHUTDOWN_READWRITE); + switch_socket_close(*sock); + *sock = NULL; + } +} + +void close_socketfd(int *sockfd) { + if (*sockfd) { + shutdown(*sockfd, SHUT_RDWR); + close(*sockfd); + } +} + +switch_socket_t *create_socket_with_port(switch_memory_pool_t *pool, switch_port_t port) { + switch_sockaddr_t *sa; + switch_socket_t *socket; + + if(switch_sockaddr_info_get(&sa, kazoo_globals.ip, SWITCH_UNSPEC, port, 0, pool)) { + return NULL; + } + + if (switch_socket_create(&socket, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, pool)) { + return NULL; + } + + if (switch_socket_opt_set(socket, SWITCH_SO_REUSEADDR, 1)) { + return NULL; + } + + if (switch_socket_bind(socket, sa)) { + return NULL; + } + + if (switch_socket_listen(socket, 5)){ + return NULL; + } + + switch_getnameinfo(&kazoo_globals.hostname, sa, 0); + + if (kazoo_globals.nat_map && switch_nat_get_type()) { + switch_nat_add_mapping(port, SWITCH_NAT_TCP, NULL, SWITCH_FALSE); + } + + return socket; +} + +switch_socket_t *create_socket(switch_memory_pool_t *pool) { + return create_socket_with_port(pool, 0); + +} + +switch_status_t create_ei_cnode(const char *ip_addr, const char *name, struct ei_cnode_s *ei_cnode) { + char hostname[EI_MAXHOSTNAMELEN + 1] = ""; + char nodename[MAXNODELEN + 1]; + char cnodename[EI_MAXALIVELEN + 1]; + //EI_MAX_COOKIE_SIZE+1 + char *atsign; + + /* copy the erlang interface nodename into something we can modify */ + strncpy(cnodename, name, EI_MAXALIVELEN); + + if ((atsign = strchr(cnodename, '@'))) { + /* we got a qualified node name, don't guess the host/domain */ + snprintf(nodename, MAXNODELEN + 1, "%s", kazoo_globals.ei_nodename); + /* truncate the alivename at the @ */ + *atsign = '\0'; + } else { + if (zstr(kazoo_globals.hostname) || !strncasecmp(kazoo_globals.ip, "0.0.0.0", 7) || !strncasecmp(kazoo_globals.ip, "::", 2)) { + memcpy(hostname, switch_core_get_hostname(), EI_MAXHOSTNAMELEN); + } else { + memcpy(hostname, kazoo_globals.hostname, EI_MAXHOSTNAMELEN); + } + + snprintf(nodename, MAXNODELEN + 1, "%s@%s", kazoo_globals.ei_nodename, hostname); + } + + if (kazoo_globals.ei_shortname) { + char *off; + if ((off = strchr(nodename, '.'))) { + *off = '\0'; + } + } + + /* init the ec stuff */ + if (ei_connect_xinit(ei_cnode, hostname, cnodename, nodename, (Erl_IpAddr) ip_addr, kazoo_globals.ei_cookie, 0) < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize the erlang interface connection structure\n"); + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t ei_compare_pids(const erlang_pid *pid1, const erlang_pid *pid2) { + if ((!strcmp(pid1->node, pid2->node)) + && pid1->creation == pid2->creation + && pid1->num == pid2->num + && pid1->serial == pid2->serial) { + return SWITCH_STATUS_SUCCESS; + } else { + return SWITCH_STATUS_FALSE; + } +} + +void ei_link(ei_node_t *ei_node, erlang_pid * from, erlang_pid * to) { + char msgbuf[2048]; + char *s; + int index = 0; + + index = 5; /* max sizes: */ + ei_encode_version(msgbuf, &index); /* 1 */ + ei_encode_tuple_header(msgbuf, &index, 3); + ei_encode_long(msgbuf, &index, ERL_LINK); + ei_encode_pid(msgbuf, &index, from); /* 268 */ + ei_encode_pid(msgbuf, &index, to); /* 268 */ + + /* 5 byte header missing */ + s = msgbuf; + put32be(s, index - 4); /* 4 */ + put8(s, ERL_PASS_THROUGH); /* 1 */ + /* sum: 542 */ + + if (write(ei_node->nodefd, msgbuf, index) == -1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to link to process on %s\n", ei_node->peer_nodename); + } +} + +void ei_encode_switch_event(ei_x_buff *ebuf, switch_event_t *event) { + ei_x_encode_tuple_header(ebuf, 2); + ei_x_encode_atom(ebuf, "event"); + ei_encode_switch_event_headers(ebuf, event); +} + +int ei_helper_send(ei_node_t *ei_node, erlang_pid *to, ei_x_buff *buf) { + int ret = 0; + + if (ei_node->nodefd) { +#ifdef EI_DEBUG + ei_x_print_msg(buf, to, 1); +#endif + ret = ei_send(ei_node->nodefd, to, buf->buff, buf->index); + } + + return ret; +} + +int ei_decode_atom_safe(char *buf, int *index, char *dst) { + int type, size; + + ei_get_type(buf, index, &type, &size); + + if (type != ERL_ATOM_EXT) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed atom\n", type, size); + return -1; + } else if (size > MAXATOMLEN) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Requested decoding of atom with size %d into a buffer of size %d\n", size, MAXATOMLEN); + return -1; + } else { + return ei_decode_atom(buf, index, dst); + } +} + +int ei_decode_string_or_binary(char *buf, int *index, char **dst) { + int type, size, res; + long len; + + ei_get_type(buf, index, &type, &size); + + if (type != ERL_STRING_EXT && type != ERL_BINARY_EXT && type != ERL_NIL_EXT) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed binary or string\n", type, size); + return -1; + } + + *dst = malloc(size + 1); + + if (type == ERL_NIL_EXT) { + res = 0; + **dst = '\0'; + } else if (type == ERL_BINARY_EXT) { + res = ei_decode_binary(buf, index, *dst, &len); + (*dst)[len] = '\0'; + } else { + res = ei_decode_string(buf, index, *dst); + } + + return res; +} + +int ei_decode_string_or_binary_limited(char *buf, int *index, int maxsize, char *dst) { + int type, size, res; + long len; + + ei_get_type(buf, index, &type, &size); + + if (type != ERL_STRING_EXT && type != ERL_BINARY_EXT && type != ERL_NIL_EXT) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed binary or string\n", type, size); + return -1; + } + + if (size > maxsize) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Requested decoding of %s with size %d into a buffer of size %d\n", + type == ERL_BINARY_EXT ? "binary" : "string", size, maxsize); + return -1; + } + + if (type == ERL_NIL_EXT) { + res = 0; + *dst = '\0'; + } else if (type == ERL_BINARY_EXT) { + res = ei_decode_binary(buf, index, dst, &len); + dst[len] = '\0'; /* binaries aren't null terminated */ + } else { + res = ei_decode_string(buf, index, dst); + } + + return res; +} + + +switch_status_t create_acceptor() { + switch_sockaddr_t *sa; + uint16_t port; + char ipbuf[48]; + const char *ip_addr; + + /* if the config has specified an erlang release compatibility then pass that along to the erlang interface */ + if (kazoo_globals.ei_compat_rel) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Compatability with OTP R%d requested\n", kazoo_globals.ei_compat_rel); + ei_set_compat_rel(kazoo_globals.ei_compat_rel); + } + + if (!(kazoo_globals.acceptor = create_socket_with_port(kazoo_globals.pool, kazoo_globals.port))) { + return SWITCH_STATUS_SOCKERR; + } + + switch_socket_addr_get(&sa, SWITCH_FALSE, kazoo_globals.acceptor); + + port = switch_sockaddr_get_port(sa); + ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Erlang connection acceptor listening on %s:%u\n", ip_addr, port); + + /* try to initialize the erlang interface */ + if (create_ei_cnode(ip_addr, kazoo_globals.ei_nodename, &kazoo_globals.ei_cnode) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_SOCKERR; + } + + /* tell the erlang port manager where we can be reached. this returns a file descriptor pointing to epmd or -1 */ + if ((kazoo_globals.epmdfd = ei_publish(&kazoo_globals.ei_cnode, port)) == -1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to publish port to epmd, trying to start epmd via system()\n"); + if (system("epmd -daemon")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Failed to start epmd manually! Is epmd in $PATH? If not, start it yourself or run an erl shell with -sname or -name\n"); + return SWITCH_STATUS_SOCKERR; + } + switch_yield(100000); + if ((kazoo_globals.epmdfd = ei_publish(&kazoo_globals.ei_cnode, port)) == -1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to publish port to epmd AGAIN\n"); + return SWITCH_STATUS_SOCKERR; + } + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connected to epmd and published erlang cnode name %s at port %d\n", kazoo_globals.ei_cnode.thisnodename, port); + + return SWITCH_STATUS_SUCCESS; +} + +switch_hash_t *create_default_filter() { + switch_hash_t *filter; + + switch_core_hash_init(&filter); + + switch_core_hash_insert(filter, "Acquired-UUID", "1"); + switch_core_hash_insert(filter, "action", "1"); + switch_core_hash_insert(filter, "Action", "1"); + switch_core_hash_insert(filter, "alt_event_type", "1"); + switch_core_hash_insert(filter, "Answer-State", "1"); + switch_core_hash_insert(filter, "Application", "1"); + switch_core_hash_insert(filter, "Application-Data", "1"); + switch_core_hash_insert(filter, "Application-Name", "1"); + switch_core_hash_insert(filter, "Application-Response", "1"); + switch_core_hash_insert(filter, "att_xfer_replaced_by", "1"); + switch_core_hash_insert(filter, "Auth-Method", "1"); + switch_core_hash_insert(filter, "Auth-Realm", "1"); + switch_core_hash_insert(filter, "Auth-User", "1"); + switch_core_hash_insert(filter, "Bridge-A-Unique-ID", "1"); + switch_core_hash_insert(filter, "Bridge-B-Unique-ID", "1"); + switch_core_hash_insert(filter, "Call-Direction", "1"); + switch_core_hash_insert(filter, "Caller-Callee-ID-Name", "1"); + switch_core_hash_insert(filter, "Caller-Callee-ID-Number", "1"); + switch_core_hash_insert(filter, "Caller-Caller-ID-Name", "1"); + switch_core_hash_insert(filter, "Caller-Caller-ID-Number", "1"); + switch_core_hash_insert(filter, "Caller-Screen-Bit", "1"); + switch_core_hash_insert(filter, "Caller-Privacy-Hide-Name", "1"); + switch_core_hash_insert(filter, "Caller-Privacy-Hide-Number", "1"); + switch_core_hash_insert(filter, "Caller-Context", "1"); + switch_core_hash_insert(filter, "Caller-Controls", "1"); + switch_core_hash_insert(filter, "Caller-Destination-Number", "1"); + switch_core_hash_insert(filter, "Caller-Dialplan", "1"); + switch_core_hash_insert(filter, "Caller-Network-Addr", "1"); + switch_core_hash_insert(filter, "Caller-Unique-ID", "1"); + switch_core_hash_insert(filter, "Call-ID", "1"); + switch_core_hash_insert(filter, "Channel-Call-State", "1"); + switch_core_hash_insert(filter, "Channel-Call-UUID", "1"); + switch_core_hash_insert(filter, "Channel-Presence-ID", "1"); + switch_core_hash_insert(filter, "Channel-State", "1"); + switch_core_hash_insert(filter, "Chat-Permissions", "1"); + switch_core_hash_insert(filter, "Conference-Name", "1"); + switch_core_hash_insert(filter, "Conference-Profile-Name", "1"); + switch_core_hash_insert(filter, "Conference-Unique-ID", "1"); + switch_core_hash_insert(filter, "contact", "1"); + switch_core_hash_insert(filter, "Detected-Tone", "1"); + switch_core_hash_insert(filter, "dialog_state", "1"); + switch_core_hash_insert(filter, "direction", "1"); + switch_core_hash_insert(filter, "Distributed-From", "1"); + switch_core_hash_insert(filter, "DTMF-Digit", "1"); + switch_core_hash_insert(filter, "DTMF-Duration", "1"); + switch_core_hash_insert(filter, "Event-Date-Timestamp", "1"); + switch_core_hash_insert(filter, "Event-Name", "1"); + switch_core_hash_insert(filter, "Event-Subclass", "1"); + switch_core_hash_insert(filter, "expires", "1"); + switch_core_hash_insert(filter, "Expires", "1"); + switch_core_hash_insert(filter, "Ext-SIP-IP", "1"); + switch_core_hash_insert(filter, "File", "1"); + switch_core_hash_insert(filter, "FreeSWITCH-Hostname", "1"); + switch_core_hash_insert(filter, "from", "1"); + switch_core_hash_insert(filter, "Hunt-Destination-Number", "1"); + switch_core_hash_insert(filter, "ip", "1"); + switch_core_hash_insert(filter, "Message-Account", "1"); + switch_core_hash_insert(filter, "metadata", "1"); + switch_core_hash_insert(filter, "old_node_channel_uuid", "1"); + switch_core_hash_insert(filter, "Other-Leg-Callee-ID-Name", "1"); + switch_core_hash_insert(filter, "Other-Leg-Callee-ID-Number", "1"); + switch_core_hash_insert(filter, "Other-Leg-Caller-ID-Name", "1"); + switch_core_hash_insert(filter, "Other-Leg-Caller-ID-Number", "1"); + switch_core_hash_insert(filter, "Other-Leg-Destination-Number", "1"); + switch_core_hash_insert(filter, "Other-Leg-Direction", "1"); + switch_core_hash_insert(filter, "Other-Leg-Unique-ID", "1"); + switch_core_hash_insert(filter, "Other-Leg-Channel-Name", "1"); + switch_core_hash_insert(filter, "Participant-Type", "1"); + switch_core_hash_insert(filter, "Path", "1"); + switch_core_hash_insert(filter, "profile_name", "1"); + switch_core_hash_insert(filter, "Profiles", "1"); + switch_core_hash_insert(filter, "proto-specific-event-name", "1"); + switch_core_hash_insert(filter, "Raw-Application-Data", "1"); + switch_core_hash_insert(filter, "realm", "1"); + switch_core_hash_insert(filter, "Resigning-UUID", "1"); + switch_core_hash_insert(filter, "set", "1"); + switch_core_hash_insert(filter, "sip_auto_answer", "1"); + switch_core_hash_insert(filter, "sip_auth_method", "1"); + switch_core_hash_insert(filter, "sip_from_host", "1"); + switch_core_hash_insert(filter, "sip_from_user", "1"); + switch_core_hash_insert(filter, "sip_to_host", "1"); + switch_core_hash_insert(filter, "sip_to_user", "1"); + switch_core_hash_insert(filter, "sub-call-id", "1"); + switch_core_hash_insert(filter, "technology", "1"); + switch_core_hash_insert(filter, "to", "1"); + switch_core_hash_insert(filter, "Unique-ID", "1"); + switch_core_hash_insert(filter, "URL", "1"); + switch_core_hash_insert(filter, "username", "1"); + switch_core_hash_insert(filter, "variable_channel_is_moving", "1"); + switch_core_hash_insert(filter, "variable_collected_digits", "1"); + switch_core_hash_insert(filter, "variable_current_application", "1"); + switch_core_hash_insert(filter, "variable_current_application_data", "1"); + switch_core_hash_insert(filter, "variable_domain_name", "1"); + switch_core_hash_insert(filter, "variable_effective_caller_id_name", "1"); + switch_core_hash_insert(filter, "variable_effective_caller_id_number", "1"); + switch_core_hash_insert(filter, "variable_holding_uuid", "1"); + switch_core_hash_insert(filter, "variable_hold_music", "1"); + switch_core_hash_insert(filter, "variable_media_group_id", "1"); + switch_core_hash_insert(filter, "variable_originate_disposition", "1"); + switch_core_hash_insert(filter, "variable_origination_uuid", "1"); + switch_core_hash_insert(filter, "variable_playback_terminator_used", "1"); + switch_core_hash_insert(filter, "variable_presence_id", "1"); + switch_core_hash_insert(filter, "variable_record_ms", "1"); + switch_core_hash_insert(filter, "variable_recovered", "1"); + switch_core_hash_insert(filter, "variable_silence_hits_exhausted", "1"); + switch_core_hash_insert(filter, "variable_sip_auth_realm", "1"); + switch_core_hash_insert(filter, "variable_sip_from_host", "1"); + switch_core_hash_insert(filter, "variable_sip_from_user", "1"); + switch_core_hash_insert(filter, "variable_sip_from_tag", "1"); + switch_core_hash_insert(filter, "variable_sip_h_X-AUTH-IP", "1"); + switch_core_hash_insert(filter, "variable_sip_received_ip", "1"); + switch_core_hash_insert(filter, "variable_sip_to_host", "1"); + switch_core_hash_insert(filter, "variable_sip_to_user", "1"); + switch_core_hash_insert(filter, "variable_sip_to_tag", "1"); + switch_core_hash_insert(filter, "variable_sofia_profile_name", "1"); + switch_core_hash_insert(filter, "variable_transfer_history", "1"); + switch_core_hash_insert(filter, "variable_user_name", "1"); + switch_core_hash_insert(filter, "variable_endpoint_disposition", "1"); + switch_core_hash_insert(filter, "variable_originate_disposition", "1"); + switch_core_hash_insert(filter, "variable_bridge_hangup_cause", "1"); + switch_core_hash_insert(filter, "variable_hangup_cause", "1"); + switch_core_hash_insert(filter, "variable_last_bridge_proto_specific_hangup_cause", "1"); + switch_core_hash_insert(filter, "variable_proto_specific_hangup_cause", "1"); + switch_core_hash_insert(filter, "VM-Call-ID", "1"); + switch_core_hash_insert(filter, "VM-sub-call-id", "1"); + switch_core_hash_insert(filter, "whistle_application_name", "1"); + switch_core_hash_insert(filter, "whistle_application_response", "1"); + switch_core_hash_insert(filter, "whistle_event_name", "1"); + switch_core_hash_insert(filter, "kazoo_application_name", "1"); + switch_core_hash_insert(filter, "kazoo_application_response", "1"); + switch_core_hash_insert(filter, "kazoo_event_name", "1"); + switch_core_hash_insert(filter, "sip_auto_answer_notify", "1"); + switch_core_hash_insert(filter, "eavesdrop_group", "1"); + switch_core_hash_insert(filter, "origination_caller_id_name", "1"); + switch_core_hash_insert(filter, "origination_caller_id_number", "1"); + switch_core_hash_insert(filter, "origination_callee_id_name", "1"); + switch_core_hash_insert(filter, "origination_callee_id_number", "1"); + switch_core_hash_insert(filter, "sip_auth_username", "1"); + switch_core_hash_insert(filter, "sip_auth_password", "1"); + switch_core_hash_insert(filter, "effective_caller_id_name", "1"); + switch_core_hash_insert(filter, "effective_caller_id_number", "1"); + switch_core_hash_insert(filter, "effective_callee_id_name", "1"); + switch_core_hash_insert(filter, "effective_callee_id_number", "1"); + switch_core_hash_insert(filter, "variable_destination_number", "1"); + switch_core_hash_insert(filter, "variable_effective_callee_id_name", "1"); + switch_core_hash_insert(filter, "variable_effective_callee_id_number", "1"); + switch_core_hash_insert(filter, "variable_record_silence_hits", "1"); + switch_core_hash_insert(filter, "variable_refer_uuid", "1"); + switch_core_hash_insert(filter, "variable_sip_call_id", "1"); + switch_core_hash_insert(filter, "variable_sip_h_Referred-By", "1"); + switch_core_hash_insert(filter, "variable_sip_h_X-AUTH-PORT", "1"); + switch_core_hash_insert(filter, "variable_sip_loopback_req_uri", "1"); + switch_core_hash_insert(filter, "variable_sip_received_port", "1"); + switch_core_hash_insert(filter, "variable_sip_refer_to", "1"); + switch_core_hash_insert(filter, "variable_sip_req_host", "1"); + switch_core_hash_insert(filter, "variable_sip_req_uri", "1"); + switch_core_hash_insert(filter, "variable_transfer_source", "1"); + switch_core_hash_insert(filter, "variable_uuid", "1"); + + /* Registration headers */ + switch_core_hash_insert(filter, "call-id", "1"); + switch_core_hash_insert(filter, "profile-name", "1"); + switch_core_hash_insert(filter, "from-user", "1"); + switch_core_hash_insert(filter, "from-host", "1"); + switch_core_hash_insert(filter, "presence-hosts", "1"); + switch_core_hash_insert(filter, "contact", "1"); + switch_core_hash_insert(filter, "rpid", "1"); + switch_core_hash_insert(filter, "status", "1"); + switch_core_hash_insert(filter, "expires", "1"); + switch_core_hash_insert(filter, "to-user", "1"); + switch_core_hash_insert(filter, "to-host", "1"); + switch_core_hash_insert(filter, "network-ip", "1"); + switch_core_hash_insert(filter, "network-port", "1"); + switch_core_hash_insert(filter, "username", "1"); + switch_core_hash_insert(filter, "realm", "1"); + switch_core_hash_insert(filter, "user-agent", "1"); + + switch_core_hash_insert(filter, "Hangup-Cause", "1"); + switch_core_hash_insert(filter, "Unique-ID", "1"); + switch_core_hash_insert(filter, "variable_switch_r_sdp", "1"); + switch_core_hash_insert(filter, "variable_rtp_local_sdp_str", "1"); + switch_core_hash_insert(filter, "variable_sip_to_uri", "1"); + switch_core_hash_insert(filter, "variable_sip_from_uri", "1"); + switch_core_hash_insert(filter, "variable_sip_user_agent", "1"); + switch_core_hash_insert(filter, "variable_duration", "1"); + switch_core_hash_insert(filter, "variable_billsec", "1"); + switch_core_hash_insert(filter, "variable_billmsec", "1"); + switch_core_hash_insert(filter, "variable_progresssec", "1"); + switch_core_hash_insert(filter, "variable_progress_uepoch", "1"); + switch_core_hash_insert(filter, "variable_progress_media_uepoch", "1"); + switch_core_hash_insert(filter, "variable_start_uepoch", "1"); + switch_core_hash_insert(filter, "variable_digits_dialed", "1"); + switch_core_hash_insert(filter, "Member-ID", "1"); + switch_core_hash_insert(filter, "Floor", "1"); + switch_core_hash_insert(filter, "Video", "1"); + switch_core_hash_insert(filter, "Hear", "1"); + switch_core_hash_insert(filter, "Speak", "1"); + switch_core_hash_insert(filter, "Talking", "1"); + switch_core_hash_insert(filter, "Current-Energy", "1"); + switch_core_hash_insert(filter, "Energy-Level", "1"); + switch_core_hash_insert(filter, "Mute-Detect", "1"); + + /* RTMP headers */ + switch_core_hash_insert(filter, "RTMP-Session-ID", "1"); + switch_core_hash_insert(filter, "RTMP-Profile", "1"); + switch_core_hash_insert(filter, "RTMP-Flash-Version", "1"); + switch_core_hash_insert(filter, "RTMP-SWF-URL", "1"); + switch_core_hash_insert(filter, "RTMP-TC-URL", "1"); + switch_core_hash_insert(filter, "RTMP-Page-URL", "1"); + switch_core_hash_insert(filter, "User", "1"); + switch_core_hash_insert(filter, "Domain", "1"); + + /* Fax headers */ + switch_core_hash_insert(filter, "variable_fax_bad_rows", "1"); + switch_core_hash_insert(filter, "variable_fax_document_total_pages", "1"); + switch_core_hash_insert(filter, "variable_fax_document_transferred_pages", "1"); + switch_core_hash_insert(filter, "variable_fax_ecm_used", "1"); + switch_core_hash_insert(filter, "variable_fax_result_code", "1"); + switch_core_hash_insert(filter, "variable_fax_result_text", "1"); + switch_core_hash_insert(filter, "variable_fax_success", "1"); + switch_core_hash_insert(filter, "variable_fax_transfer_rate", "1"); + switch_core_hash_insert(filter, "variable_fax_local_station_id", "1"); + switch_core_hash_insert(filter, "variable_fax_remote_station_id", "1"); + switch_core_hash_insert(filter, "variable_fax_remote_country", "1"); + switch_core_hash_insert(filter, "variable_fax_remote_vendor", "1"); + switch_core_hash_insert(filter, "variable_fax_remote_model", "1"); + switch_core_hash_insert(filter, "variable_fax_image_resolution", "1"); + switch_core_hash_insert(filter, "variable_fax_file_image_resolution", "1"); + switch_core_hash_insert(filter, "variable_fax_image_size", "1"); + switch_core_hash_insert(filter, "variable_fax_image_pixel_size", "1"); + switch_core_hash_insert(filter, "variable_fax_file_image_pixel_size", "1"); + switch_core_hash_insert(filter, "variable_fax_longest_bad_row_run", "1"); + switch_core_hash_insert(filter, "variable_fax_encoding", "1"); + switch_core_hash_insert(filter, "variable_fax_encoding_name", "1"); + switch_core_hash_insert(filter, "variable_fax_header", "1"); + switch_core_hash_insert(filter, "variable_fax_ident", "1"); + switch_core_hash_insert(filter, "variable_fax_timezone", "1"); + switch_core_hash_insert(filter, "variable_fax_doc_id", "1"); + switch_core_hash_insert(filter, "variable_fax_doc_database", "1"); + switch_core_hash_insert(filter, "variable_has_t38", "1"); + + /* Secure headers */ + switch_core_hash_insert(filter, "variable_sdp_secure_savp_only", "1"); + switch_core_hash_insert(filter, "variable_rtp_has_crypto", "1"); + switch_core_hash_insert(filter, "variable_rtp_secure_media", "1"); + switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed", "1"); + switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed_audio", "1"); + switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed_video", "1"); + switch_core_hash_insert(filter, "variable_zrtp_secure_media", "1"); + switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed", "1"); + switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed_audio", "1"); + switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed_video", "1"); + switch_core_hash_insert(filter, "sdp_secure_savp_only", "1"); + switch_core_hash_insert(filter, "rtp_has_crypto", "1"); + switch_core_hash_insert(filter, "rtp_secure_media", "1"); + switch_core_hash_insert(filter, "rtp_secure_media_confirmed", "1"); + switch_core_hash_insert(filter, "rtp_secure_media_confirmed_audio", "1"); + switch_core_hash_insert(filter, "rtp_secure_media_confirmed_video", "1"); + switch_core_hash_insert(filter, "zrtp_secure_media", "1"); + switch_core_hash_insert(filter, "zrtp_secure_media_confirmed", "1"); + switch_core_hash_insert(filter, "zrtp_secure_media_confirmed_audio", "1"); + switch_core_hash_insert(filter, "zrtp_secure_media_confirmed_video", "1"); + + /* Device Redirect headers */ + switch_core_hash_insert(filter, "variable_last_bridge_hangup_cause", "1"); + switch_core_hash_insert(filter, "variable_sip_redirected_by", "1"); + switch_core_hash_insert(filter, "intercepted_by", "1"); + switch_core_hash_insert(filter, "variable_bridge_uuid", "1"); + switch_core_hash_insert(filter, "Record-File-Path", "1"); + + /* Loopback headers */ + switch_core_hash_insert(filter, "variable_loopback_bowout_on_execute", "1"); + switch_core_hash_insert(filter, "variable_loopback_bowout", "1"); + switch_core_hash_insert(filter, "variable_other_loopback_leg_uuid", "1"); + switch_core_hash_insert(filter, "variable_loopback_leg", "1"); + switch_core_hash_insert(filter, "variable_is_loopback", "1"); + + // SMS + switch_core_hash_insert(filter, "Message-ID", "1"); + switch_core_hash_insert(filter, "Delivery-Failure", "1"); + switch_core_hash_insert(filter, "Delivery-Result-Code", "1"); + + return filter; +} + +static void fetch_config_filters(switch_memory_pool_t *pool) +{ + char *cf = "kazoo.conf"; + switch_xml_t cfg, xml, child, param; + switch_event_t *params; + + switch_event_create(¶ms, 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(¶ms, 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: + */ diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_event_stream.c b/src/mod/event_handlers/mod_kazoo/kazoo_event_stream.c index 3ca0e3acc7..b52b6d4b81 100644 --- a/src/mod/event_handlers/mod_kazoo/kazoo_event_stream.c +++ b/src/mod/event_handlers/mod_kazoo/kazoo_event_stream.c @@ -48,7 +48,8 @@ static char *my_dup(const char *s) { static const char* private_headers[] = {"variable_sip_h_", "sip_h_", "P-", "X-"}; static int is_private_header(const char *name) { - for(int i=0; i < 4; i++) { + int i; + for(i=0; i < 4; i++) { if(!strncmp(name, private_headers[i], strlen(private_headers[i]))) { return 1; } @@ -102,51 +103,105 @@ static switch_status_t kazoo_event_dup(switch_event_t **clone, switch_event_t *e return SWITCH_STATUS_SUCCESS; } -static void event_handler(switch_event_t *event) { +static int encode_event_old(switch_event_t *event, ei_x_buff *ebuf) { switch_event_t *clone = NULL; - ei_event_stream_t *event_stream = (ei_event_stream_t *) event->bind_user_data; + + if (kazoo_event_dup(&clone, event, kazoo_globals.event_filter) != SWITCH_STATUS_SUCCESS) { + return 0; + } + + ei_encode_switch_event(ebuf, clone); + + switch_event_destroy(&clone); + + return 1; +} + +static int encode_event_new(switch_event_t *event, ei_x_buff *ebuf) { + kazoo_message_ptr msg = NULL; + ei_event_binding_t *event_binding = (ei_event_binding_t *) event->bind_user_data; + + msg = kazoo_message_create_event(event, event_binding->event, kazoo_globals.events); + + if(msg == NULL) { + return 0; + } + + ei_x_encode_tuple_header(ebuf, 2); + ei_x_encode_atom(ebuf, "event"); + ei_encode_json(ebuf, msg->JObj); + + kazoo_message_destroy(&msg); + + return 1; +} + +/* + * event_handler is duplicated when there are 2+ nodes connected + * with the same bindings + * we should maintain a list of event_streams in event_binding struct + * and build a ref count in the message + * + */ +static void event_handler(switch_event_t *event) { + ei_event_binding_t *event_binding = (ei_event_binding_t *) event->bind_user_data; + ei_event_stream_t *event_stream = event_binding->stream; + ei_x_buff *ebuf = NULL; + int res = 0; /* if mod_kazoo or the event stream isn't running dont push a new event */ if (!switch_test_flag(event_stream, LFLAG_RUNNING) || !switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) { return; } - if (event->event_id == SWITCH_EVENT_CUSTOM) { - ei_event_binding_t *event_binding = event_stream->bindings; - unsigned short int found = 0; + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Switch-Nodename", kazoo_globals.ei_cnode.thisnodename); - if (!event->subclass_name) { - return; - } - - while(event_binding != NULL) { - if (event_binding->type == SWITCH_EVENT_CUSTOM) { - if(event_binding->subclass_name - && !strcmp(event->subclass_name, event_binding->subclass_name)) { - found = 1; - break; - } - } - event_binding = event_binding->next; - } - - if (!found) { + switch_malloc(ebuf, sizeof(*ebuf)); + if(ebuf == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not allocate erlang buffer for mod_kazoo message\n"); + return; + } + memset(ebuf, 0, sizeof(*ebuf)); + + if(kazoo_globals.event_stream_preallocate > 0) { + ebuf->buff = malloc(kazoo_globals.event_stream_preallocate); + ebuf->buffsz = kazoo_globals.event_stream_preallocate; + ebuf->index = 0; + if(ebuf->buff == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not pre-allocate memory for mod_kazoo message\n"); + switch_safe_free(ebuf); return; } + ei_x_encode_version(ebuf); + } else { + ei_x_new_with_version(ebuf); } - /* try to clone the event and push it to the event stream thread */ - /* TODO: someday maybe the filter comes from the event_stream (set during init only) - * and is per-binding so we only send headers that a process requests */ - if (kazoo_event_dup(&clone, event, kazoo_globals.event_filter) == SWITCH_STATUS_SUCCESS) { - if (switch_queue_trypush(event_stream->queue, clone) != SWITCH_STATUS_SUCCESS) { + + if(event_stream->node->legacy) { + res = encode_event_old(event, ebuf); + } else { + res = encode_event_new(event, ebuf); + } + + if(!res) { + ei_x_free(ebuf); + switch_safe_free(ebuf); + return; + } + + if (kazoo_globals.event_stream_preallocate > 0 && ebuf->buffsz > kazoo_globals.event_stream_preallocate) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "increased event stream buffer size to %d\n", ebuf->buffsz); + } + + if (switch_queue_trypush(event_stream->queue, ebuf) != SWITCH_STATUS_SUCCESS) { /* if we couldn't place the cloned event into the listeners */ /* event queue make sure we destroy it, real good like */ - switch_event_destroy(&clone); - } - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory error: Have a good trip? See you next fall!\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error placing the event in the listeners queue\n"); + ei_x_free(ebuf); + switch_safe_free(ebuf); } + } static void *SWITCH_THREAD_FUNC event_stream_loop(switch_thread_t *thread, void *obj) { @@ -178,7 +233,8 @@ static void *SWITCH_THREAD_FUNC event_stream_loop(switch_thread_t *thread, void /* check if a new connection is pending */ if (switch_pollset_poll(event_stream->pollset, 0, &numfds, &fds) == SWITCH_STATUS_SUCCESS) { - for (int32_t i = 0; i < numfds; i++) { + int32_t i; + for (i = 0; i < numfds; i++) { switch_socket_t *newsocket; /* accept the new client connection */ @@ -217,46 +273,25 @@ static void *SWITCH_THREAD_FUNC event_stream_loop(switch_thread_t *thread, void } /* if there was an event waiting in our queue send it to the client */ - if (switch_queue_pop_timeout(event_stream->queue, &pop, 500000) == SWITCH_STATUS_SUCCESS) { - switch_event_t *event = (switch_event_t *) pop; + if (switch_queue_pop_timeout(event_stream->queue, &pop, 200000) == SWITCH_STATUS_SUCCESS) { + ei_x_buff *ebuf = (ei_x_buff *) pop; if (event_stream->socket) { - ei_x_buff ebuf; char byte; short i = event_stream_framing; switch_size_t size = 1; - if(kazoo_globals.event_stream_preallocate > 0) { - ebuf.buff = malloc(kazoo_globals.event_stream_preallocate); - ebuf.buffsz = kazoo_globals.event_stream_preallocate; - ebuf.index = 0; - if(ebuf.buff == NULL) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not pre-allocate memory for mod_kazoo message\n"); - break; - } - ei_x_encode_version(&ebuf); - } else { - ei_x_new_with_version(&ebuf); - } - - ei_encode_switch_event(&ebuf, event); - - if (kazoo_globals.event_stream_preallocate > 0 && ebuf.buffsz > kazoo_globals.event_stream_preallocate) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "increased event stream buffer size to %d\n", ebuf.buffsz); - } - while (i) { - byte = ebuf.index >> (8 * --i); + byte = ebuf->index >> (8 * --i); switch_socket_send(event_stream->socket, &byte, &size); } - size = (switch_size_t)ebuf.index; - switch_socket_send(event_stream->socket, ebuf.buff, &size); - - ei_x_free(&ebuf); + size = (switch_size_t)ebuf->index; + switch_socket_send(event_stream->socket, ebuf->buff, &size); } - switch_event_destroy(&event); + ei_x_free(ebuf); + switch_safe_free(ebuf); } } @@ -297,11 +332,12 @@ static void *SWITCH_THREAD_FUNC event_stream_loop(switch_thread_t *thread, void return NULL; } -ei_event_stream_t *new_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from) { +ei_event_stream_t *new_event_stream(ei_node_t *ei_node, const erlang_pid *from) { switch_thread_t *thread; switch_threadattr_t *thd_attr = NULL; switch_memory_pool_t *pool = NULL; ei_event_stream_t *event_stream; + ei_event_stream_t **event_streams = &ei_node->event_streams; /* create memory pool for this event stream */ if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { @@ -320,6 +356,7 @@ ei_event_stream_t *new_event_stream(ei_event_stream_t **event_streams, const erl event_stream->bindings = NULL; event_stream->pool = pool; event_stream->connected = SWITCH_FALSE; + event_stream->node = ei_node; memcpy(&event_stream->pid, from, sizeof(erlang_pid)); switch_queue_create(&event_stream->queue, MAX_QUEUE_LEN, pool); @@ -443,17 +480,68 @@ switch_status_t remove_event_streams(ei_event_stream_t **event_streams) { return SWITCH_STATUS_SUCCESS; } -switch_status_t add_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name) { +void bind_event_profile(ei_event_binding_t *event_binding, kazoo_event_ptr event) +{ + switch_event_types_t event_type; + while(event != NULL) { + if (switch_name_event(event->name, &event_type) != SWITCH_STATUS_SUCCESS) { + event_type = SWITCH_EVENT_CUSTOM; + } + if(event_binding->type != SWITCH_EVENT_CUSTOM + && event_binding->type == event_type) { + break; + } + if (event_binding->type == SWITCH_EVENT_CUSTOM + && event_binding->type == event_type + && !strcasecmp(event_binding->subclass_name, event->name)) { + break; + } + event = event->next; + } + event_binding->event = event; + if(event == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "EVENT BINDING ERROR %s - %s\n",switch_event_name(event_binding->type), event_binding->subclass_name); + } +} + +void bind_event_profiles(kazoo_event_ptr event) +{ + ei_node_t *ei_node = kazoo_globals.ei_nodes; + while(ei_node) { + ei_event_stream_t *event_streams = ei_node->event_streams; + while(event_streams) { + ei_event_binding_t *bindings = event_streams->bindings; + while(bindings) { + bind_event_profile(bindings, event); + bindings = bindings->next; + } + event_streams = event_streams->next; + } + ei_node = ei_node->next; + } +} + +switch_status_t add_event_binding(ei_event_stream_t *event_stream, const char *event_name) { ei_event_binding_t *event_binding = event_stream->bindings; + switch_event_types_t event_type; + + if(!strcasecmp(event_name, "CUSTOM")) { + return SWITCH_STATUS_SUCCESS; + } + + if (switch_name_event(event_name, &event_type) != SWITCH_STATUS_SUCCESS) { + event_type = SWITCH_EVENT_CUSTOM; + } /* check if the event binding already exists, ignore if so */ while(event_binding != NULL) { if (event_binding->type == SWITCH_EVENT_CUSTOM) { - if(subclass_name - && event_binding->subclass_name - && !strcmp(subclass_name, event_binding->subclass_name)) { - return SWITCH_STATUS_SUCCESS; - } + if(event_type == SWITCH_EVENT_CUSTOM + && event_name + && event_binding->subclass_name + && !strcasecmp(event_name, event_binding->subclass_name)) { + return SWITCH_STATUS_SUCCESS; + } } else if (event_binding->type == event_type) { return SWITCH_STATUS_SUCCESS; } @@ -467,18 +555,21 @@ switch_status_t add_event_binding(ei_event_stream_t *event_stream, const switch_ } /* prepare the event binding struct */ + event_binding->stream = event_stream; event_binding->type = event_type; - if (!subclass_name || zstr(subclass_name)) { - event_binding->subclass_name = NULL; + if(event_binding->type == SWITCH_EVENT_CUSTOM) { + event_binding->subclass_name = switch_core_strdup(event_stream->pool, event_name); } else { - /* TODO: free strdup? */ - event_binding->subclass_name = strdup(subclass_name); + event_binding->subclass_name = SWITCH_EVENT_SUBCLASS_ANY; } event_binding->next = NULL; + bind_event_profile(event_binding, kazoo_globals.events->events); + + /* bind to the event with a unique ID and capture the event_node pointer */ switch_uuid_str(event_binding->id, sizeof(event_binding->id)); - if (switch_event_bind_removable(event_binding->id, event_type, subclass_name, event_handler, event_stream, &event_binding->node) != SWITCH_STATUS_SUCCESS) { + if (switch_event_bind_removable(event_binding->id, event_type, event_binding->subclass_name, event_handler, event_binding, &event_binding->node) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to bind to event %s %s!\n" ,switch_event_name(event_binding->type), event_binding->subclass_name ? event_binding->subclass_name : ""); return SWITCH_STATUS_GENERR; diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_fetch_agent.c b/src/mod/event_handlers/mod_kazoo/kazoo_fetch_agent.c index 006ffe32f0..0e4e842f2c 100644 --- a/src/mod/event_handlers/mod_kazoo/kazoo_fetch_agent.c +++ b/src/mod/event_handlers/mod_kazoo/kazoo_fetch_agent.c @@ -32,38 +32,9 @@ */ #include "mod_kazoo.h" -struct xml_fetch_reply_s { - char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; - char *xml_str; - struct xml_fetch_reply_s *next; -}; -typedef struct xml_fetch_reply_s xml_fetch_reply_t; -struct fetch_handler_s { - erlang_pid pid; - struct fetch_handler_s *next; -}; -typedef struct fetch_handler_s fetch_handler_t; -struct ei_xml_client_s { - ei_node_t *ei_node; - fetch_handler_t *fetch_handlers; - struct ei_xml_client_s *next; -}; -typedef struct ei_xml_client_s ei_xml_client_t; -struct ei_xml_agent_s { - switch_memory_pool_t *pool; - switch_xml_section_t section; - switch_thread_rwlock_t *lock; - ei_xml_client_t *clients; - switch_mutex_t *current_client_mutex; - ei_xml_client_t *current_client; - switch_mutex_t *replies_mutex; - switch_thread_cond_t *new_reply; - xml_fetch_reply_t *replies; -}; -typedef struct ei_xml_agent_s ei_xml_agent_t; static char *xml_section_to_string(switch_xml_section_t section) { switch(section) { @@ -77,6 +48,8 @@ static char *xml_section_to_string(switch_xml_section_t section) { return "chatplan"; case SWITCH_XML_SECTION_CHANNELS: return "channels"; + case SWITCH_XML_SECTION_LANGUAGES: + return "languages"; default: return "unknown"; } @@ -133,6 +106,11 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con ei_xml_client_t *client; fetch_handler_t *fetch_handler; xml_fetch_reply_t reply, *pending, *prev = NULL; + switch_event_t *event = params; + kazoo_fetch_profile_ptr profile = agent->profile; + const char *fetch_call_id; + ei_send_msg_t *send_msg = NULL; + int sent = 0; now = switch_micro_time_now(); @@ -164,12 +142,60 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con return xml; } + if(event == NULL) { + if (switch_event_create(&event, SWITCH_EVENT_GENERAL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error creating event for fetch handler\n"); + return xml; + } + } + /* prepare the reply collector */ switch_uuid_get(&uuid); switch_uuid_format(reply.uuid_str, &uuid); reply.next = NULL; reply.xml_str = NULL; + if((fetch_call_id = switch_event_get_header(event, "Fetch-Call-UUID")) != NULL) { + switch_core_session_t *session = NULL; + if((session = switch_core_session_force_locate(fetch_call_id)) != NULL) { + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_channel_event_set_data(channel, event); + switch_core_session_rwunlock(session); + } + } + + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Fetch-UUID", reply.uuid_str); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Fetch-Section", section); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Fetch-Tag", tag_name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Fetch-Key-Name", key_name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Fetch-Key-Value", key_value); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Fetch-Timeout", "%u", profile->fetch_timeout); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Fetch-Timestamp-Micro", "%ld", (uint64_t)now); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Kazoo-Version", VERSION); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Kazoo-Bundle", BUNDLE); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Kazoo-Release", RELEASE); + + switch_malloc(send_msg, sizeof(*send_msg)); + + if(client->ei_node->legacy) { + ei_x_new_with_version(&send_msg->buf); + ei_x_encode_tuple_header(&send_msg->buf, 7); + ei_x_encode_atom(&send_msg->buf, "fetch"); + ei_x_encode_atom(&send_msg->buf, section); + _ei_x_encode_string(&send_msg->buf, tag_name ? tag_name : "undefined"); + _ei_x_encode_string(&send_msg->buf, key_name ? key_name : "undefined"); + _ei_x_encode_string(&send_msg->buf, key_value ? key_value : "undefined"); + _ei_x_encode_string(&send_msg->buf, reply.uuid_str); + ei_encode_switch_event_headers(&send_msg->buf, event); + } else { + kazoo_message_ptr msg = kazoo_message_create_fetch(event, profile); + ei_x_new_with_version(&send_msg->buf); + ei_x_encode_tuple_header(&send_msg->buf, 2); + ei_x_encode_atom(&send_msg->buf, "fetch"); + ei_encode_json(&send_msg->buf, msg->JObj); + kazoo_message_destroy(&msg); + } + /* add our reply placeholder to the replies list */ switch_mutex_lock(agent->replies_mutex); if (!agent->replies) { @@ -181,28 +207,8 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con switch_mutex_unlock(agent->replies_mutex); fetch_handler = client->fetch_handlers; - while (fetch_handler != NULL) { - ei_send_msg_t *send_msg; - - switch_malloc(send_msg, sizeof(*send_msg)); + while (fetch_handler != NULL && sent == 0) { memcpy(&send_msg->pid, &fetch_handler->pid, sizeof(erlang_pid)); - - ei_x_new_with_version(&send_msg->buf); - - ei_x_encode_tuple_header(&send_msg->buf, 7); - ei_x_encode_atom(&send_msg->buf, "fetch"); - ei_x_encode_atom(&send_msg->buf, section); - _ei_x_encode_string(&send_msg->buf, tag_name ? tag_name : "undefined"); - _ei_x_encode_string(&send_msg->buf, key_name ? key_name : "undefined"); - _ei_x_encode_string(&send_msg->buf, key_value ? key_value : "undefined"); - _ei_x_encode_string(&send_msg->buf, reply.uuid_str); - - if (params) { - ei_encode_switch_event_headers(&send_msg->buf, params); - } else { - ei_x_encode_empty_list(&send_msg->buf); - } - if (switch_queue_trypush(client->ei_node->send_msgs, send_msg) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send %s XML request to %s <%d.%d.%d>\n" ,section @@ -210,8 +216,6 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con ,fetch_handler->pid.creation ,fetch_handler->pid.num ,fetch_handler->pid.serial); - ei_x_free(&send_msg->buf); - switch_safe_free(send_msg); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sending %s XML request (%s) to %s <%d.%d.%d>\n" ,section @@ -220,11 +224,19 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con ,fetch_handler->pid.creation ,fetch_handler->pid.num ,fetch_handler->pid.serial); + sent = 1; } - fetch_handler = fetch_handler->next; } + if(!sent) { + ei_x_free(&send_msg->buf); + switch_safe_free(send_msg); + } + + if(params == NULL) + switch_event_destroy(&event); + /* wait for a reply (if there isnt already one...amazingly improbable but lets not take shortcuts */ switch_mutex_lock(agent->replies_mutex); @@ -292,6 +304,42 @@ static switch_xml_t fetch_handler(const char *section, const char *tag_name, con return xml; } +void bind_fetch_profile(ei_xml_agent_t *agent, kazoo_config_ptr fetch_handlers) +{ + switch_hash_index_t *hi; + kazoo_fetch_profile_ptr val = NULL, ptr = NULL; + + for (hi = switch_core_hash_first(fetch_handlers->hash); hi; hi = switch_core_hash_next(&hi)) { + switch_core_hash_this(hi, NULL, NULL, (void**) &val); + if (val && val->section == agent->section) { + ptr = val; + break; + } + } + agent->profile = ptr; +} + +void rebind_fetch_profiles(kazoo_config_ptr fetch_handlers) +{ + if(kazoo_globals.config_fetch_binding != NULL) + bind_fetch_profile((ei_xml_agent_t *) switch_xml_get_binding_user_data(kazoo_globals.config_fetch_binding), fetch_handlers); + + if(kazoo_globals.directory_fetch_binding != NULL) + bind_fetch_profile((ei_xml_agent_t *) switch_xml_get_binding_user_data(kazoo_globals.directory_fetch_binding), fetch_handlers); + + if(kazoo_globals.dialplan_fetch_binding != NULL) + bind_fetch_profile((ei_xml_agent_t *) switch_xml_get_binding_user_data(kazoo_globals.dialplan_fetch_binding), fetch_handlers); + + if(kazoo_globals.channels_fetch_binding != NULL) + bind_fetch_profile((ei_xml_agent_t *) switch_xml_get_binding_user_data(kazoo_globals.channels_fetch_binding), fetch_handlers); + + if(kazoo_globals.languages_fetch_binding != NULL) + bind_fetch_profile((ei_xml_agent_t *) switch_xml_get_binding_user_data(kazoo_globals.languages_fetch_binding), fetch_handlers); + + if(kazoo_globals.chatplan_fetch_binding != NULL) + bind_fetch_profile((ei_xml_agent_t *) switch_xml_get_binding_user_data(kazoo_globals.chatplan_fetch_binding), fetch_handlers); +} + static switch_status_t bind_fetch_agent(switch_xml_section_t section, switch_xml_binding_t **binding) { switch_memory_pool_t *pool = NULL; ei_xml_agent_t *agent; @@ -326,6 +374,8 @@ static switch_status_t bind_fetch_agent(switch_xml_section_t section, switch_xml switch_thread_cond_create(&agent->new_reply, pool); agent->replies = NULL; + bind_fetch_profile(agent, kazoo_globals.fetch_handlers); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Bound to %s XML requests\n" ,xml_section_to_string(section)); @@ -336,6 +386,9 @@ static switch_status_t unbind_fetch_agent(switch_xml_binding_t **binding) { ei_xml_agent_t *agent; ei_xml_client_t *client; + if(*binding == NULL) + return SWITCH_STATUS_GENERR; + /* get a pointer to our user_data */ agent = (ei_xml_agent_t *)switch_xml_get_binding_user_data(*binding); @@ -399,6 +452,9 @@ static switch_status_t remove_xml_client(ei_node_t *ei_node, switch_xml_binding_ ei_xml_client_t *client, *prev = NULL; int found = 0; + if(binding == NULL) + return SWITCH_STATUS_GENERR; + agent = (ei_xml_agent_t *)switch_xml_get_binding_user_data(binding); /* write-lock the agent */ @@ -575,8 +631,9 @@ switch_status_t bind_fetch_agents() { bind_fetch_agent(SWITCH_XML_SECTION_CONFIG, &kazoo_globals.config_fetch_binding); bind_fetch_agent(SWITCH_XML_SECTION_DIRECTORY, &kazoo_globals.directory_fetch_binding); bind_fetch_agent(SWITCH_XML_SECTION_DIALPLAN, &kazoo_globals.dialplan_fetch_binding); - bind_fetch_agent(SWITCH_XML_SECTION_CHATPLAN, &kazoo_globals.chatplan_fetch_binding); bind_fetch_agent(SWITCH_XML_SECTION_CHANNELS, &kazoo_globals.channels_fetch_binding); + bind_fetch_agent(SWITCH_XML_SECTION_LANGUAGES, &kazoo_globals.languages_fetch_binding); + bind_fetch_agent(SWITCH_XML_SECTION_CHATPLAN, &kazoo_globals.chatplan_fetch_binding); return SWITCH_STATUS_SUCCESS; } @@ -585,8 +642,9 @@ switch_status_t unbind_fetch_agents() { unbind_fetch_agent(&kazoo_globals.config_fetch_binding); unbind_fetch_agent(&kazoo_globals.directory_fetch_binding); unbind_fetch_agent(&kazoo_globals.dialplan_fetch_binding); - unbind_fetch_agent(&kazoo_globals.chatplan_fetch_binding); unbind_fetch_agent(&kazoo_globals.channels_fetch_binding); + unbind_fetch_agent(&kazoo_globals.languages_fetch_binding); + unbind_fetch_agent(&kazoo_globals.chatplan_fetch_binding); return SWITCH_STATUS_SUCCESS; } @@ -595,8 +653,9 @@ switch_status_t remove_xml_clients(ei_node_t *ei_node) { remove_xml_client(ei_node, kazoo_globals.config_fetch_binding); remove_xml_client(ei_node, kazoo_globals.directory_fetch_binding); remove_xml_client(ei_node, kazoo_globals.dialplan_fetch_binding); - remove_xml_client(ei_node, kazoo_globals.chatplan_fetch_binding); remove_xml_client(ei_node, kazoo_globals.channels_fetch_binding); + remove_xml_client(ei_node, kazoo_globals.languages_fetch_binding); + remove_xml_client(ei_node, kazoo_globals.chatplan_fetch_binding); return SWITCH_STATUS_SUCCESS; } @@ -606,6 +665,9 @@ switch_status_t add_fetch_handler(ei_node_t *ei_node, erlang_pid *from, switch_x ei_xml_client_t *client; fetch_handler_t *fetch_handler; + if(binding == NULL) + return SWITCH_STATUS_GENERR; + agent = (ei_xml_agent_t *)switch_xml_get_binding_user_data(binding); /* write-lock the agent */ @@ -653,8 +715,9 @@ switch_status_t remove_fetch_handlers(ei_node_t *ei_node, erlang_pid *from) { remove_fetch_handler(ei_node, from, kazoo_globals.config_fetch_binding); remove_fetch_handler(ei_node, from, kazoo_globals.directory_fetch_binding); remove_fetch_handler(ei_node, from, kazoo_globals.dialplan_fetch_binding); - remove_fetch_handler(ei_node, from, kazoo_globals.chatplan_fetch_binding); remove_fetch_handler(ei_node, from, kazoo_globals.channels_fetch_binding); + remove_fetch_handler(ei_node, from, kazoo_globals.languages_fetch_binding); + remove_fetch_handler(ei_node, from, kazoo_globals.chatplan_fetch_binding); return SWITCH_STATUS_SUCCESS; } @@ -689,8 +752,9 @@ switch_status_t handle_api_command_streams(ei_node_t *ei_node, switch_stream_han handle_api_command_stream(ei_node, stream, kazoo_globals.config_fetch_binding); handle_api_command_stream(ei_node, stream, kazoo_globals.directory_fetch_binding); handle_api_command_stream(ei_node, stream, kazoo_globals.dialplan_fetch_binding); - handle_api_command_stream(ei_node, stream, kazoo_globals.chatplan_fetch_binding); handle_api_command_stream(ei_node, stream, kazoo_globals.channels_fetch_binding); + handle_api_command_stream(ei_node, stream, kazoo_globals.languages_fetch_binding); + handle_api_command_stream(ei_node, stream, kazoo_globals.chatplan_fetch_binding); return SWITCH_STATUS_SUCCESS; } 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 index 0000000000..95aab4e917 --- /dev/null +++ b/src/mod/event_handlers/mod_kazoo/kazoo_fields.h @@ -0,0 +1,195 @@ +/* +* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* Copyright (C) 2005-2012, Anthony Minessale II +* +* 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 +* Portions created by the Initial Developer are Copyright (C) +* the Initial Developer. All Rights Reserved. +* +* Based on mod_skel by +* Anthony Minessale II +* +* Contributor(s): +* +* Daniel Bryars +* Tim Brown +* Anthony Minessale II +* William King +* Mike Jerris +* +* kazoo.c -- Sends FreeSWITCH events to an AMQP broker +* +*/ + +#ifndef KAZOO_FIELDS_H +#define KAZOO_FIELDS_H + +#include + +#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 index 0000000000..c95a254062 --- /dev/null +++ b/src/mod/event_handlers/mod_kazoo/kazoo_message.c @@ -0,0 +1,458 @@ +/* +* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* Copyright (C) 2005-2012, Anthony Minessale II +* +* 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 +* Portions created by the Initial Developer are Copyright (C) +* the Initial Developer. All Rights Reserved. +* +* Based on mod_skel by +* Anthony Minessale II +* +* Contributor(s): +* +* Daniel Bryars +* Tim Brown +* Anthony Minessale II +* William King +* Mike Jerris +* +* kazoo.c -- Sends FreeSWITCH events to an AMQP broker +* +*/ + +#include "mod_kazoo.h" + +/* deletes then add */ +void kazoo_cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + cJSON_DeleteItemFromObject(object, string); + cJSON_AddItemToObject(object, string, item); +} + +static int inline filter_compare(switch_event_t* evt, kazoo_filter_ptr filter) +{ + switch_event_header_t *header; + int hasValue = 0, /*size, */n; + char *value; + + switch(filter->compare) { + + case FILTER_COMPARE_EXISTS: + hasValue = switch_event_get_header(evt, filter->name) != NULL ? 1 : 0; + break; + + case FILTER_COMPARE_VALUE: + value = switch_event_get_header_nil(evt, filter->name); + hasValue = !strcmp(value, filter->value); + break; + + case FILTER_COMPARE_PREFIX: + for (header = evt->headers; header; header = header->next) { + if(!strncmp(header->name, filter->value, strlen(filter->value))) { + hasValue = 1; + break; + } + } + break; + + case FILTER_COMPARE_LIST: + value = switch_event_get_header(evt, filter->name); + if(value) { + for(n = 0; n < filter->list.size; n++) { + if(!strncmp(value, filter->list.value[n], strlen(filter->list.value[n]))) { + hasValue = 1; + break; + } + } + } + break; + + case FILTER_COMPARE_REGEX: + break; + + default: + break; + } + + return hasValue; +} + +static kazoo_filter_ptr inline filter_event(switch_event_t* evt, kazoo_filter_ptr filter) +{ + while(filter) { + int hasValue = filter_compare(evt, filter); + if(filter->type == FILTER_EXCLUDE) { + if(hasValue) + break; + } else if(filter->type == FILTER_INCLUDE) { + if(!hasValue) + break; + } + filter = filter->next; + } + return filter; +} + +static void kazoo_event_init_json_fields(switch_event_t *event, cJSON *json) +{ + switch_event_header_t *hp; + for (hp = event->headers; hp; hp = hp->next) { + if (hp->idx) { + cJSON *a = cJSON_CreateArray(); + int i; + + for(i = 0; i < hp->idx; i++) { + cJSON_AddItemToArray(a, cJSON_CreateString(hp->array[i])); + } + + cJSON_AddItemToObject(json, hp->name, a); + + } else { + cJSON_AddItemToObject(json, hp->name, cJSON_CreateString(hp->value)); + } + } +} + +static switch_status_t kazoo_event_init_json(kazoo_fields_ptr fields1, kazoo_fields_ptr fields2, switch_event_t* evt, cJSON** clone) +{ + switch_status_t status; + if( (fields2 && fields2->verbose) + || (fields1 && fields1->verbose) + || ( (!fields2) && (!fields1)) ) { + status = switch_event_serialize_json_obj(evt, clone); + } else { + status = SWITCH_STATUS_SUCCESS; + *clone = cJSON_CreateObject(); + if((*clone) == NULL) { + status = SWITCH_STATUS_GENERR; + } + } + return status; +} + +static cJSON * kazoo_event_json_value(kazoo_json_field_type type, const char *value) { + cJSON *item = NULL; + switch(type) { + case JSON_STRING: + item = cJSON_CreateString(value); + break; + + case JSON_NUMBER: + item = cJSON_CreateNumber(strtod(value, NULL)); + break; + + case JSON_BOOLEAN: + item = cJSON_CreateBool(switch_true(value)); + break; + + case JSON_OBJECT: + item = cJSON_CreateObject(); + break; + + case JSON_RAW: + item = cJSON_CreateRaw(value); + break; + + default: + break; + }; + + return item; +} + +static cJSON * kazoo_event_add_json_value(cJSON *dst, kazoo_field_ptr field, const char *as, const char *value) { + cJSON *item = NULL; + if(value || field->out_type == JSON_OBJECT) { + if((item = kazoo_event_json_value(field->out_type, value)) != NULL) { + kazoo_cJSON_AddItemToObject(dst, as, item); + } + } + return item; +} + +#define MAX_FIRST_OF 25 + +char * first_of(switch_event_t *src, char * in) +{ + switch_event_header_t *header; + char *y1, *y2, *y3 = NULL; + char *value[MAX_FIRST_OF]; + int n, size; + + y1 = strdup(in); + if((y2 = (char *) switch_stristr("first-of", y1)) != NULL) { + char tmp[2048] = ""; + y3 = switch_find_end_paren((const char *)y2 + 8, '(', ')'); + *++y3='\0'; + *y2 = '\0'; + size = switch_separate_string(y2 + 9, '|', value, MAX_FIRST_OF); + for(n=0; n < size; n++) { + header = switch_event_get_header_ptr(src, value[n]); + if(header) { + switch_snprintf(tmp, sizeof(tmp), "%s%s%s", y1, header->name, y3); + free(y1); + y2 = strdup(tmp); + y3 = first_of(src, y2); + if(y2 == y3) { + return y2; + } else { + return y3; + } + } + } + } + free(y1); + return in; + +} + +cJSON * kazoo_event_add_field_to_json(cJSON *dst, switch_event_t *src, kazoo_field_ptr field) +{ + switch_event_header_t *header; + char *expanded, *firstOf; + uint i, n; + cJSON *item = NULL; + + switch(field->in_type) { + case FIELD_COPY: + if((header = switch_event_get_header_ptr(src, field->name)) != NULL) { + if (header->idx) { + item = cJSON_CreateArray(); + for(i = 0; i < header->idx; i++) { + cJSON_AddItemToArray(item, kazoo_event_json_value(field->out_type, header->array[i])); + } + kazoo_cJSON_AddItemToObject(dst, field->as ? field->as : field->name, item); + } else { + item = kazoo_event_add_json_value(dst, field, field->as ? field->as : field->name, header->value); + } + } + break; + + case FIELD_EXPAND: + firstOf = first_of(src, field->value); + expanded = switch_event_expand_headers(src, firstOf); + if(expanded != NULL && !zstr(expanded)) { + item = kazoo_event_add_json_value(dst, field, field->as ? field->as : field->name, expanded); + } + if(expanded != firstOf) { + free(expanded); + } + if(firstOf != field->value) { + free(firstOf); + } + break; + + case FIELD_FIRST_OF: + for(n = 0; n < field->list.size; n++) { + if(*field->list.value[n] == '#') { + item = kazoo_event_add_json_value(dst, field, field->as ? field->as : field->name, ++field->list.value[n]); + break; + } else { + header = switch_event_get_header_ptr(src, field->list.value[n]); + if(header) { + if (header->idx) { + item = cJSON_CreateArray(); + for(i = 0; i < header->idx; i++) { + cJSON_AddItemToArray(item, kazoo_event_json_value(field->out_type, header->array[i])); + } + kazoo_cJSON_AddItemToObject(dst, field->as ? field->as : field->name, item); + } else { + item = kazoo_event_add_json_value(dst, field, field->as ? field->as : field->name, header->value); + } + break; + } + } + } + break; + + case FIELD_PREFIX: + for (header = src->headers; header; header = header->next) { + if(!strncmp(header->name, field->name, strlen(field->name))) { + if (header->idx) { + cJSON *array = cJSON_CreateArray(); + for(i = 0; i < header->idx; i++) { + cJSON_AddItemToArray(array, kazoo_event_json_value(field->out_type, header->array[i])); + } + kazoo_cJSON_AddItemToObject(dst, field->exclude_prefix ? header->name+strlen(field->name) : header->name, array); + } else { + kazoo_event_add_json_value(dst, field, field->exclude_prefix ? header->name+strlen(field->name) : header->name, header->value); + } + } + } + break; + + case FIELD_STATIC: + item = kazoo_event_add_json_value(dst, field, field->name, field->value); + break; + + case FIELD_GROUP: + item = dst; + break; + + default: + break; + } + + return item; +} + +static switch_status_t kazoo_event_add_fields_to_json(kazoo_logging_ptr logging, cJSON *dst, switch_event_t *src, kazoo_field_ptr field) { + + kazoo_filter_ptr filter; + cJSON *item = NULL; + while(field) { + if(field->in_type == FIELD_REFERENCE) { + if(field->ref) { + if((filter = filter_event(src, field->ref->filter)) != NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, logging->levels->filtered_field_log_level, "profile[%s] event %s, referenced field %s filtered by settings %s : %s\n", logging->profile_name, logging->event_name, field->ref->name, filter->name, filter->value); + } else { + kazoo_event_add_fields_to_json(logging, dst, src, field->ref->head); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "profile[%s] event %s, referenced field %s not found\n", logging->profile_name, logging->event_name, field->name); + } + } else { + if((filter = filter_event(src, field->filter)) != NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, logging->levels->filtered_field_log_level, "profile[%s] event %s, field %s filtered by settings %s : %s\n", logging->profile_name, logging->event_name, field->name, filter->name, filter->value); + } else { + item = kazoo_event_add_field_to_json(dst, src, field); + if(field->children && item != NULL) { + if(field->children->verbose && field->out_type == JSON_OBJECT) { + kazoo_event_init_json_fields(src, item); + } + kazoo_event_add_fields_to_json(logging, field->out_type == JSON_OBJECT ? item : dst, src, field->children->head); + } + } + } + + field = field->next; + } + + return SWITCH_STATUS_SUCCESS; +} + + +kazoo_message_ptr kazoo_message_create_event(switch_event_t* evt, kazoo_event_ptr event, kazoo_event_profile_ptr profile) +{ + kazoo_message_ptr message; + cJSON *JObj = NULL; + kazoo_filter_ptr filtered; + kazoo_logging_t logging; + + logging.levels = profile->logging; + logging.event_name = switch_event_get_header_nil(evt, "Event-Name"); + logging.profile_name = profile->name; + + switch_event_add_header(evt, SWITCH_STACK_BOTTOM, "Switch-Nodename", "%s@%s", "freeswitch", switch_event_get_header(evt, "FreeSWITCH-Hostname")); + + + message = malloc(sizeof(kazoo_message_t)); + if(message == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error allocating memory for serializing event to json\n"); + return NULL; + } + memset(message, 0, sizeof(kazoo_message_t)); + + if(profile->filter) { + // filtering + if((filtered = filter_event(evt, profile->filter)) != NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, logging.levels->filtered_event_log_level, "profile[%s] event %s filtered by profile settings %s : %s\n", logging.profile_name, logging.event_name, filtered->name, filtered->value); + kazoo_message_destroy(&message); + return NULL; + } + } + + if(event && event->filter) { + if((filtered = filter_event(evt, event->filter)) != NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, logging.levels->filtered_event_log_level, "profile[%s] event %s filtered by event settings %s : %s\n", logging.profile_name, logging.event_name, filtered->name, filtered->value); + kazoo_message_destroy(&message); + return NULL; + } + } + + kazoo_event_init_json(profile->fields, event ? event->fields : NULL, evt, &JObj); + + if(profile->fields) + kazoo_event_add_fields_to_json(&logging, JObj, evt, profile->fields->head); + + if(event && event->fields) + kazoo_event_add_fields_to_json(&logging, JObj, evt, event->fields->head); + + message->JObj = JObj; + + + return message; + + +} + +kazoo_message_ptr kazoo_message_create_fetch(switch_event_t* evt, kazoo_fetch_profile_ptr profile) +{ + kazoo_message_ptr message; + cJSON *JObj = NULL; + kazoo_logging_t logging; + + logging.levels = profile->logging; + logging.event_name = switch_event_get_header_nil(evt, "Event-Name"); + logging.profile_name = profile->name; + + switch_event_add_header(evt, SWITCH_STACK_BOTTOM, "Switch-Nodename", "%s@%s", "freeswitch", switch_event_get_header(evt, "FreeSWITCH-Hostname")); + + message = malloc(sizeof(kazoo_message_t)); + if(message == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error allocating memory for serializing event to json\n"); + return NULL; + } + memset(message, 0, sizeof(kazoo_message_t)); + + + kazoo_event_init_json(profile->fields, NULL, evt, &JObj); + + if(profile->fields) + kazoo_event_add_fields_to_json(&logging, JObj, evt, profile->fields->head); + + message->JObj = JObj; + + + return message; + + +} + + +void kazoo_message_destroy(kazoo_message_ptr *msg) +{ + if (!msg || !*msg) return; + if((*msg)->JObj != NULL) + cJSON_Delete((*msg)->JObj); + switch_safe_free(*msg); + +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + */ 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 index 0000000000..0674c71f0b --- /dev/null +++ b/src/mod/event_handlers/mod_kazoo/kazoo_message.h @@ -0,0 +1,65 @@ +/* +* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* Copyright (C) 2005-2012, Anthony Minessale II +* +* 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 +* Portions created by the Initial Developer are Copyright (C) +* the Initial Developer. All Rights Reserved. +* +* Based on mod_skel by +* Anthony Minessale II +* +* Contributor(s): +* +* Daniel Bryars +* Tim Brown +* Anthony Minessale II +* William King +* Mike Jerris +* +* kazoo.c -- Sends FreeSWITCH events to an AMQP broker +* +*/ + +#ifndef KAZOO_MESSAGE_H +#define KAZOO_MESSAGE_H + +#include + +typedef struct { + uint64_t timestamp; + uint64_t start; + uint64_t filter; + uint64_t serialize; + uint64_t print; + uint64_t rk; +} kazoo_message_times_t, *kazoo_message_times_ptr; + +typedef struct { + cJSON *JObj; +} kazoo_message_t, *kazoo_message_ptr; + + +kazoo_message_ptr kazoo_message_create_event(switch_event_t* evt, kazoo_event_ptr event, kazoo_event_profile_ptr profile); +kazoo_message_ptr kazoo_message_create_fetch(switch_event_t* evt, kazoo_fetch_profile_ptr profile); + +void kazoo_message_destroy(kazoo_message_ptr *msg); + + +#endif /* KAZOO_MESSAGE_H */ + diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_node.c b/src/mod/event_handlers/mod_kazoo/kazoo_node.c index 17da6dc582..3ec739e74b 100644 --- a/src/mod/event_handlers/mod_kazoo/kazoo_node.c +++ b/src/mod/event_handlers/mod_kazoo/kazoo_node.c @@ -53,6 +53,8 @@ static char *REQUEST_ATOMS[] = { "nixevent", "sendevent", "sendmsg", + "commands", + "command", "bind", "getpid", "version", @@ -62,7 +64,8 @@ static char *REQUEST_ATOMS[] = { "fetch_reply", "config", "bgapi4", - "api4" + "api4", + "no_legacy" }; typedef enum { @@ -72,6 +75,8 @@ typedef enum { REQUEST_NIXEVENT, REQUEST_SENDEVENT, REQUEST_SENDMSG, + REQUEST_COMMANDS, + REQUEST_COMMAND, REQUEST_BIND, REQUEST_GETPID, REQUEST_VERSION, @@ -82,11 +87,13 @@ typedef enum { REQUEST_CONFIG, REQUEST_BGAPI4, REQUEST_API4, + REQUEST_NO_LEGACY, REQUEST_MAX } request_atoms_t; static switch_status_t find_request(char *atom, int *request) { - for (int i = 0; i < REQUEST_MAX; i++) { + int i; + for (i = 0; i < REQUEST_MAX; i++) { if(!strncmp(atom, REQUEST_ATOMS[i], MAXATOMLEN)) { *request = i; return SWITCH_STATUS_SUCCESS; @@ -267,7 +274,7 @@ static switch_status_t api_exec_stream(char *cmd, char *arg, switch_stream_handl } } else if (!stream->data || !strlen(stream->data)) { *reply = switch_mprintf("%s: Command returned no output", cmd); - status = SWITCH_STATUS_FALSE; + status = SWITCH_STATUS_SUCCESS; } else { *reply = strdup(stream->data); status = SWITCH_STATUS_SUCCESS; @@ -412,11 +419,10 @@ static void log_sendmsg_request(char *uuid, switch_event_t *event) switch_ssize_t hlen = -1; unsigned long CMD_EXECUTE = switch_hashfunc_default("execute", &hlen); unsigned long CMD_XFEREXT = switch_hashfunc_default("xferext", &hlen); - // unsigned long CMD_HANGUP = switch_hashfunc_default("hangup", &hlen); - // unsigned long CMD_NOMEDIA = switch_hashfunc_default("nomedia", &hlen); - // unsigned long CMD_UNICAST = switch_hashfunc_default("unicast", &hlen); if (zstr(cmd)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "log|%s|invalid \n", uuid); + DUMP_EVENT(event); return; } @@ -453,6 +459,7 @@ static void log_sendmsg_request(char *uuid, switch_event_t *event) static switch_status_t build_event(switch_event_t *event, ei_x_buff * buf) { int propslist_length, arity; + int n=0; if(!event) { return SWITCH_STATUS_FALSE; @@ -462,7 +469,7 @@ static switch_status_t build_event(switch_event_t *event, ei_x_buff * buf) { return SWITCH_STATUS_FALSE; } - while (!ei_decode_tuple_header(buf->buff, &buf->index, &arity)) { + while (!ei_decode_tuple_header(buf->buff, &buf->index, &arity) && n < propslist_length) { char key[1024]; char *value; @@ -482,9 +489,21 @@ static switch_status_t build_event(switch_event_t *event, ei_x_buff * buf) { switch_safe_free(event->body); event->body = value; } else { + if(!strcasecmp(key, "Call-ID")) { + switch_core_session_t *session = NULL; + if(!zstr(value)) { + if ((session = switch_core_session_force_locate(value)) != NULL) { + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_channel_event_set_data(channel, event); + switch_core_session_rwunlock(session); + } + } + } switch_event_add_header_string(event, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, key, value); } + n++; } + ei_skip_term(buf->buff, &buf->index); return SWITCH_STATUS_SUCCESS; } @@ -525,6 +544,16 @@ static switch_status_t erlang_response_ok(ei_x_buff *rbuf) { return SWITCH_STATUS_SUCCESS; } +static switch_status_t erlang_response_ok_uuid(ei_x_buff *rbuf, const char * uuid) { + if (rbuf) { + ei_x_encode_tuple_header(rbuf, 2); + ei_x_encode_atom(rbuf, "ok"); + ei_x_encode_binary(rbuf, uuid, strlen(uuid)); + } + + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t handle_request_noevents(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) { ei_event_stream_t *event_stream; @@ -554,6 +583,7 @@ static switch_status_t handle_request_nixevent(ei_node_t *ei_node, erlang_pid *p switch_event_types_t event_type; ei_event_stream_t *event_stream; int custom = 0, length = 0; + int i; if (ei_decode_list_header(buf->buff, &buf->index, &length) || length == 0) { @@ -566,7 +596,7 @@ static switch_status_t handle_request_nixevent(ei_node_t *ei_node, erlang_pid *p return erlang_response_ok(rbuf); } - for (int i = 1; i <= length; i++) { + for (i = 1; i <= length; i++) { if (ei_decode_atom_safe(buf->buff, &buf->index, event_name)) { switch_mutex_unlock(ei_node->event_streams_mutex); return erlang_response_badarg(rbuf); @@ -576,16 +606,22 @@ static switch_status_t handle_request_nixevent(ei_node_t *ei_node, erlang_pid *p remove_event_binding(event_stream, SWITCH_EVENT_CUSTOM, event_name); } else if (switch_name_event(event_name, &event_type) == SWITCH_STATUS_SUCCESS) { switch (event_type) { + case SWITCH_EVENT_CUSTOM: custom++; break; + case SWITCH_EVENT_ALL: - for (switch_event_types_t type = 0; type < SWITCH_EVENT_ALL; type++) { + { + switch_event_types_t type; + for (type = 0; type < SWITCH_EVENT_ALL; type++) { if(type != SWITCH_EVENT_CUSTOM) { remove_event_binding(event_stream, type, NULL); } } break; + } + default: remove_event_binding(event_stream, event_type, NULL); } @@ -632,6 +668,82 @@ static switch_status_t handle_request_sendevent(ei_node_t *ei_node, erlang_pid * return erlang_response_badarg(rbuf); } +static switch_status_t handle_request_command(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) { + switch_core_session_t *session; + switch_event_t *event = NULL; + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + switch_uuid_t cmd_uuid; + char cmd_uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + + if (ei_decode_string_or_binary_limited(buf->buff, &buf->index, sizeof(uuid_str), uuid_str)) { + return erlang_response_badarg(rbuf); + } + + switch_uuid_get(&cmd_uuid); + switch_uuid_format(cmd_uuid_str, &cmd_uuid); + + switch_event_create(&event, SWITCH_EVENT_COMMAND); + if (build_event(event, buf) != SWITCH_STATUS_SUCCESS) { + return erlang_response_badarg(rbuf); + } + + log_sendmsg_request(uuid_str, event); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event-uuid", cmd_uuid_str); + + if (zstr_buf(uuid_str) || !(session = switch_core_session_locate(uuid_str))) { + return erlang_response_baduuid(rbuf); + } + switch_core_session_queue_private_event(session, &event, SWITCH_FALSE); + switch_core_session_rwunlock(session); + + return erlang_response_ok_uuid(rbuf, cmd_uuid_str); +} + +static switch_status_t handle_request_commands(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) { + switch_core_session_t *session; + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + int propslist_length, n; + switch_uuid_t cmd_uuid; + char cmd_uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + + if (ei_decode_string_or_binary_limited(buf->buff, &buf->index, sizeof(uuid_str), uuid_str)) { + return erlang_response_badarg(rbuf); + } + + if (zstr_buf(uuid_str) || !(session = switch_core_session_locate(uuid_str))) { + return erlang_response_baduuid(rbuf); + } + + switch_uuid_get(&cmd_uuid); + switch_uuid_format(cmd_uuid_str, &cmd_uuid); + + if (ei_decode_list_header(buf->buff, &buf->index, &propslist_length)) { + switch_core_session_rwunlock(session); + return SWITCH_STATUS_FALSE; + } + + for(n = 0; n < propslist_length; n++) { + switch_event_t *event = NULL; + switch_event_create(&event, SWITCH_EVENT_COMMAND); + if (build_event(event, buf) != SWITCH_STATUS_SUCCESS) { + switch_core_session_rwunlock(session); + return erlang_response_badarg(rbuf); + } + log_sendmsg_request(uuid_str, event); + if(n == (propslist_length - 1)) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event-uuid", cmd_uuid_str); + } else { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event-uuid", "null"); + } + switch_core_session_queue_private_event(session, &event, SWITCH_FALSE); + } + + switch_core_session_rwunlock(session); + + return erlang_response_ok_uuid(rbuf, cmd_uuid_str); + +} + static switch_status_t handle_request_sendmsg(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) { switch_core_session_t *session; switch_event_t *event = NULL; @@ -659,8 +771,8 @@ static switch_status_t handle_request_sendmsg(ei_node_t *ei_node, erlang_pid *pi static switch_status_t handle_request_config(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) { - fetch_config_filters(); - return erlang_response_ok(rbuf); + fetch_config(); + return erlang_response_ok(rbuf); } static switch_status_t handle_request_bind(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) { @@ -675,8 +787,8 @@ static switch_status_t handle_request_bind(ei_node_t *ei_node, erlang_pid *pid, switch(section) { case SWITCH_XML_SECTION_CONFIG: add_fetch_handler(ei_node, pid, kazoo_globals.config_fetch_binding); - if(!kazoo_globals.config_filters_fetched) - fetch_config_filters(); + if(!kazoo_globals.config_fetched) + fetch_config(); break; case SWITCH_XML_SECTION_DIRECTORY: add_fetch_handler(ei_node, pid, kazoo_globals.directory_fetch_binding); @@ -684,12 +796,15 @@ static switch_status_t handle_request_bind(ei_node_t *ei_node, erlang_pid *pid, case SWITCH_XML_SECTION_DIALPLAN: add_fetch_handler(ei_node, pid, kazoo_globals.dialplan_fetch_binding); break; - case SWITCH_XML_SECTION_CHATPLAN: - add_fetch_handler(ei_node, pid, kazoo_globals.chatplan_fetch_binding); - break; case SWITCH_XML_SECTION_CHANNELS: add_fetch_handler(ei_node, pid, kazoo_globals.channels_fetch_binding); break; + case SWITCH_XML_SECTION_LANGUAGES: + add_fetch_handler(ei_node, pid, kazoo_globals.languages_fetch_binding); + break; + case SWITCH_XML_SECTION_CHATPLAN: + add_fetch_handler(ei_node, pid, kazoo_globals.chatplan_fetch_binding); + break; default: return erlang_response_badarg(rbuf); } @@ -856,11 +971,19 @@ static switch_status_t handle_request_api(ei_node_t *ei_node, erlang_pid *pid, e return SWITCH_STATUS_SUCCESS; } +static switch_status_t handle_request_no_legacy(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "nolegacy: %s\n", ei_node->peer_nodename); + ei_node->legacy = SWITCH_FALSE; + + return erlang_response_ok(rbuf); +} + static switch_status_t handle_request_event(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) { char event_name[MAXATOMLEN + 1]; - switch_event_types_t event_type; ei_event_stream_t *event_stream; - int custom = 0, length = 0; + int length = 0; + int i; if (ei_decode_list_header(buf->buff, &buf->index, &length) || !length) { return erlang_response_badarg(rbuf); @@ -868,38 +991,18 @@ static switch_status_t handle_request_event(ei_node_t *ei_node, erlang_pid *pid, switch_mutex_lock(ei_node->event_streams_mutex); if (!(event_stream = find_event_stream(ei_node->event_streams, pid))) { - event_stream = new_event_stream(&ei_node->event_streams, pid); + event_stream = new_event_stream(ei_node, pid); /* ensure we are notified if the requesting processes dies so we can clean up */ ei_link(ei_node, ei_self(&kazoo_globals.ei_cnode), pid); } - for (int i = 1; i <= length; i++) { + for (i = 1; i <= length; i++) { + if (ei_decode_atom_safe(buf->buff, &buf->index, event_name)) { switch_mutex_unlock(ei_node->event_streams_mutex); return erlang_response_badarg(rbuf); } - - if (custom) { - add_event_binding(event_stream, SWITCH_EVENT_CUSTOM, event_name); - } else if (switch_name_event(event_name, &event_type) == SWITCH_STATUS_SUCCESS) { - switch (event_type) { - case SWITCH_EVENT_CUSTOM: - custom++; - break; - case SWITCH_EVENT_ALL: - for (switch_event_types_t type = 0; type < SWITCH_EVENT_ALL; type++) { - if(type != SWITCH_EVENT_CUSTOM) { - add_event_binding(event_stream, type, NULL); - } - } - break; - default: - add_event_binding(event_stream, event_type, NULL); - } - } else { - switch_mutex_unlock(ei_node->event_streams_mutex); - return erlang_response_badarg(rbuf); - } + add_event_binding(event_stream, event_name); } switch_mutex_unlock(ei_node->event_streams_mutex); @@ -955,21 +1058,24 @@ static switch_status_t handle_request_fetch_reply(ei_node_t *ei_node, erlang_pid case SWITCH_XML_SECTION_DIALPLAN: result = fetch_reply(uuid_str, xml_str, kazoo_globals.dialplan_fetch_binding); break; - case SWITCH_XML_SECTION_CHATPLAN: - result = fetch_reply(uuid_str, xml_str, kazoo_globals.chatplan_fetch_binding); - break; case SWITCH_XML_SECTION_CHANNELS: result = fetch_reply(uuid_str, xml_str, kazoo_globals.channels_fetch_binding); break; + case SWITCH_XML_SECTION_LANGUAGES: + result = fetch_reply(uuid_str, xml_str, kazoo_globals.languages_fetch_binding); + break; + case SWITCH_XML_SECTION_CHATPLAN: + result = fetch_reply(uuid_str, xml_str, kazoo_globals.chatplan_fetch_binding); + break; default: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved fetch reply for an unknown configuration section: %s\n", section_str); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received fetch reply for an unknown configuration section: %s\n", section_str); return erlang_response_badarg(rbuf); } if (result == SWITCH_STATUS_SUCCESS) { return erlang_response_ok(rbuf); } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved fetch reply for an unknown/expired UUID: %s\n", uuid_str); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received fetch reply for an unknown/expired UUID: %s\n", uuid_str); return erlang_response_baduuid(rbuf); } } @@ -1010,6 +1116,10 @@ static switch_status_t handle_kazoo_request(ei_node_t *ei_node, erlang_pid *pid, return handle_request_sendevent(ei_node, pid, buf, rbuf); case REQUEST_SENDMSG: return handle_request_sendmsg(ei_node, pid, buf, rbuf); + case REQUEST_COMMAND: + return handle_request_command(ei_node, pid, buf, rbuf); + case REQUEST_COMMANDS: + return handle_request_commands(ei_node, pid, buf, rbuf); case REQUEST_BIND: return handle_request_bind(ei_node, pid, buf, rbuf); case REQUEST_GETPID: @@ -1024,12 +1134,14 @@ static switch_status_t handle_kazoo_request(ei_node_t *ei_node, erlang_pid *pid, return handle_request_event(ei_node, pid, buf, rbuf); case REQUEST_FETCH_REPLY: return handle_request_fetch_reply(ei_node, pid, buf, rbuf); - case REQUEST_CONFIG: - return handle_request_config(ei_node, pid, buf, rbuf); - case REQUEST_BGAPI4: - return handle_request_bgapi4(ei_node, pid, buf, rbuf); + case REQUEST_CONFIG: + return handle_request_config(ei_node, pid, buf, rbuf); + case REQUEST_BGAPI4: + return handle_request_bgapi4(ei_node, pid, buf, rbuf); case REQUEST_API4: return handle_request_api4(ei_node, pid, buf, rbuf); + case REQUEST_NO_LEGACY: + return handle_request_no_legacy(ei_node, pid, buf, rbuf); default: return erlang_response_notimplemented(rbuf); } @@ -1214,7 +1326,7 @@ static switch_status_t handle_erl_send(ei_node_t *ei_node, erlang_msg *msg, ei_x } else if (!strncmp(msg->toname, "mod_kazoo", MAXATOMLEN)) { return handle_mod_kazoo_request(ei_node, msg, buf); } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved erlang message to unknown process \"%s\" (ensure you are using Kazoo v2.14+).\n", msg->toname); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received erlang message to unknown process \"%s\" (ensure you are using Kazoo v2.14+).\n", msg->toname); return SWITCH_STATUS_GENERR; } } @@ -1262,7 +1374,7 @@ static void *SWITCH_THREAD_FUNC receive_handler(switch_thread_t *thread, void *o while (switch_test_flag(ei_node, LFLAG_RUNNING) && switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) { void *pop; - if (switch_queue_pop_timeout(ei_node->received_msgs, &pop, 500000) == SWITCH_STATUS_SUCCESS) { + if (switch_queue_pop_timeout(ei_node->received_msgs, &pop, 100000) == SWITCH_STATUS_SUCCESS) { ei_received_msg_t *received_msg = (ei_received_msg_t *) pop; handle_erl_msg(ei_node, &received_msg->msg, &received_msg->buf); ei_x_free(&received_msg->buf); @@ -1313,7 +1425,7 @@ static void *SWITCH_THREAD_FUNC handle_node(switch_thread_t *thread, void *obj) } while (++send_msg_count <= kazoo_globals.send_msg_batch - && switch_queue_trypop(ei_node->send_msgs, &pop) == SWITCH_STATUS_SUCCESS) { + && switch_queue_pop_timeout(ei_node->send_msgs, &pop, 20000) == SWITCH_STATUS_SUCCESS) { ei_send_msg_t *send_msg = (ei_send_msg_t *) pop; ei_helper_send(ei_node, &send_msg->pid, &send_msg->buf); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sent erlang message to %s <%d.%d.%d>\n" @@ -1431,6 +1543,7 @@ switch_status_t new_kazoo_node(int nodefd, ErlConnect *conn) { ei_node->nodefd = nodefd; ei_node->peer_nodename = switch_core_strdup(ei_node->pool, conn->nodename); ei_node->created_time = switch_micro_time_now(); + ei_node->legacy = kazoo_globals.enable_legacy; /* store the IP and node name we are talking with */ switch_os_sock_put(&ei_node->socket, (switch_os_socket_t *)&nodefd, pool); 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 index 0000000000..2aefb5ab98 --- /dev/null +++ b/src/mod/event_handlers/mod_kazoo/kazoo_tweaks.c @@ -0,0 +1,502 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2012, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Luis Azedo + * + * mod_hacks.c -- hacks with state handlers + * + */ +#include "mod_kazoo.h" + +static const char *bridge_variables[] = { + "Call-Control-Queue", + "Call-Control-PID", + "ecallmgr_Call-Interaction-ID", + "ecallmgr_Ecallmgr-Node", + NULL +}; + +static switch_status_t kz_tweaks_signal_bridge_on_hangup(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_event_t *my_event; + + const char *peer_uuid = switch_channel_get_variable(channel, "Bridge-B-Unique-ID"); + + if (switch_event_create(&my_event, SWITCH_EVENT_CHANNEL_UNBRIDGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(my_event, SWITCH_STACK_BOTTOM, "Bridge-A-Unique-ID", switch_core_session_get_uuid(session)); + switch_event_add_header_string(my_event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", peer_uuid); + switch_channel_event_set_data(channel, my_event); + switch_event_fire(&my_event); + } + + return SWITCH_STATUS_SUCCESS; +} + +static const switch_state_handler_table_t kz_tweaks_signal_bridge_state_handlers = { + /*.on_init */ NULL, + /*.on_routing */ NULL, + /*.on_execute */ NULL, + /*.on_hangup */ kz_tweaks_signal_bridge_on_hangup, + /*.on_exchange_media */ NULL, + /*.on_soft_execute */ NULL, + /*.on_consume_media */ NULL, + /*.on_hibernate */ NULL +}; + +static void kz_tweaks_handle_bridge_variables(switch_event_t *event) +{ + switch_core_session_t *a_session = NULL, *b_session = NULL; + const char *a_leg = switch_event_get_header(event, "Bridge-A-Unique-ID"); + const char *b_leg = switch_event_get_header(event, "Bridge-B-Unique-ID"); + int i; + + if (a_leg && (a_session = switch_core_session_force_locate(a_leg)) != NULL) { + switch_channel_t *a_channel = switch_core_session_get_channel(a_session); + if(switch_channel_get_variable_dup(a_channel, bridge_variables[0], SWITCH_FALSE, -1) == NULL) { + if(b_leg && (b_session = switch_core_session_force_locate(b_leg)) != NULL) { + switch_channel_t *b_channel = switch_core_session_get_channel(b_session); + for(i = 0; bridge_variables[i] != NULL; i++) { + const char *val = switch_channel_get_variable_dup(b_channel, bridge_variables[i], SWITCH_TRUE, -1); + switch_channel_set_variable(a_channel, bridge_variables[i], val); + switch_safe_strdup(val); + } + switch_core_session_rwunlock(b_session); + } + } else { + if(b_leg && (b_session = switch_core_session_force_locate(b_leg)) != NULL) { + switch_channel_t *b_channel = switch_core_session_get_channel(b_session); + if(switch_channel_get_variable_dup(b_channel, bridge_variables[0], SWITCH_FALSE, -1) == NULL) { + for(i = 0; bridge_variables[i] != NULL; i++) { + const char *val = switch_channel_get_variable_dup(b_channel, bridge_variables[i], SWITCH_TRUE, -1); + switch_channel_set_variable(b_channel, bridge_variables[i], val); + switch_safe_strdup(val); + } + } + switch_core_session_rwunlock(b_session); + } + } + switch_core_session_rwunlock(a_session); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "NOT FOUND : %s\n", a_leg); + } + +} + +static void kz_tweaks_handle_bridge_replaces(switch_event_t *event) +{ + switch_event_t *my_event; + + const char *replaced_call_id = switch_event_get_header(event, "variable_sip_replaces_call_id"); + const char *a_leg_call_id = switch_event_get_header(event, "variable_sip_replaces_a-leg"); + const char *peer_uuid = switch_event_get_header(event, "Unique-ID"); + int processed = 0; + + if(a_leg_call_id && replaced_call_id) { + const char *call_id = switch_event_get_header(event, "Bridge-B-Unique-ID"); + switch_core_session_t *session = NULL; + if ((session = switch_core_session_force_locate(peer_uuid)) != NULL) { + switch_channel_t *channel = switch_core_session_get_channel(session); + processed = switch_true(switch_channel_get_variable_dup(channel, "Bridge-Event-Processed", SWITCH_FALSE, -1)); + switch_channel_set_variable(channel, "Bridge-Event-Processed", "true"); + switch_core_session_rwunlock(session); + } + + if ((processed) && call_id && (session = switch_core_session_force_locate(call_id)) != NULL) { + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_channel_set_variable(channel, "Bridge-Event-Processed", "true"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "creating channel_bridge event A - %s , B - %s\n", switch_core_session_get_uuid(session), peer_uuid); + if (switch_event_create(&my_event, SWITCH_EVENT_CHANNEL_BRIDGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(my_event, SWITCH_STACK_BOTTOM, "Bridge-A-Unique-ID", switch_core_session_get_uuid(session)); + switch_event_add_header_string(my_event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", peer_uuid); + switch_channel_event_set_data(channel, my_event); + switch_event_fire(&my_event); + } + switch_channel_set_variable(channel, "Bridge-B-Unique-ID", peer_uuid); + switch_channel_add_state_handler(channel, &kz_tweaks_signal_bridge_state_handlers); + switch_core_session_rwunlock(session); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "NOT FOUND : %s\n", call_id); + } + + } + +} + +static void kz_tweaks_channel_bridge_event_handler(switch_event_t *event) +{ + kz_tweaks_handle_bridge_replaces(event); + kz_tweaks_handle_bridge_variables(event); +} + +// TRANSFERS + +static void kz_tweaks_channel_replaced_event_handler(switch_event_t *event) +{ + const char *uuid = switch_event_get_header(event, "Unique-ID"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "REPLACED : %s\n", uuid); +} + +static void kz_tweaks_channel_intercepted_event_handler(switch_event_t *event) +{ + const char *uuid = switch_event_get_header(event, "Unique-ID"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "INTERCEPTED : %s\n", uuid); +} + +static void kz_tweaks_channel_transferor_event_handler(switch_event_t *event) +{ + switch_core_session_t *uuid_session = NULL; + const char *uuid = switch_event_get_header(event, "Unique-ID"); + const char *call_id = switch_event_get_header(event, "att_xfer_destination_peer_uuid"); + const char *peer_uuid = switch_event_get_header(event, "att_xfer_destination_call_id"); + + const char *file = switch_event_get_header(event, "Event-Calling-File"); + const char *func = switch_event_get_header(event, "Event-Calling-Function"); + const char *line = switch_event_get_header(event, "Event-Calling-Line-Number"); + + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TRANSFEROR : %s , %s , %s, %s , %s , %s \n", uuid, call_id, peer_uuid, file, func, line); + if ((uuid_session = switch_core_session_force_locate(uuid)) != NULL) { + switch_channel_t *uuid_channel = switch_core_session_get_channel(uuid_session); + const char* interaction_id = switch_channel_get_variable_dup(uuid_channel, "ecallmgr_Call-Interaction-ID", SWITCH_TRUE, -1); + // set to uuid & peer_uuid + if(interaction_id != NULL) { + switch_core_session_t *session = NULL; + if(call_id && (session = switch_core_session_force_locate(call_id)) != NULL) { + switch_channel_t *channel = switch_core_session_get_channel(session); + const char* prv_interaction_id = switch_channel_get_variable_dup(channel, "ecallmgr_Call-Interaction-ID", SWITCH_TRUE, -1); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "LOCATING UUID PRV : %s : %s\n", prv_interaction_id, interaction_id); + switch_channel_set_variable(channel, "ecallmgr_Call-Interaction-ID", interaction_id); + switch_core_session_rwunlock(session); + switch_safe_strdup(prv_interaction_id); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TRANSFEROR NO UUID SESSION: %s , %s , %s \n", uuid, call_id, peer_uuid); + } + if(peer_uuid && (session = switch_core_session_force_locate(peer_uuid)) != NULL) { + switch_channel_t *channel = switch_core_session_get_channel(session); + const char* prv_interaction_id = switch_channel_get_variable_dup(channel, "ecallmgr_Call-Interaction-ID", SWITCH_TRUE, -1); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "LOCATING PEER UUID PRV : %s : %s\n", prv_interaction_id, interaction_id); + switch_channel_set_variable(channel, "ecallmgr_Call-Interaction-ID", interaction_id); + switch_core_session_rwunlock(session); + switch_safe_strdup(prv_interaction_id); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TRANSFEROR NO PEER SESSION: %s , %s , %s \n", uuid, call_id, peer_uuid); + } + switch_safe_strdup(interaction_id); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TRANSFEROR ID = NULL : %s , %s , %s \n", uuid, call_id, peer_uuid); + } + switch_core_session_rwunlock(uuid_session); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SESSION NOT FOUND : %s\n", call_id); + } +} + +static void kz_tweaks_channel_transferee_event_handler(switch_event_t *event) +{ + const char *uuid = switch_event_get_header(event, "Unique-ID"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TRANSFEREE : %s\n", uuid); +} + +// END TRANSFERS + + +static switch_status_t kz_tweaks_handle_loopback(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + const char * loopback_leg = NULL; + const char * loopback_aleg = NULL; + switch_event_t *event = NULL; + switch_event_header_t *header = NULL; + switch_event_t *to_add = NULL; + switch_event_t *to_remove = NULL; + switch_caller_profile_t *caller; + int n = 0; + + caller = switch_channel_get_caller_profile(channel); + if(strncmp(caller->source, "mod_loopback", 12)) + return SWITCH_STATUS_SUCCESS; + + if((loopback_leg = switch_channel_get_variable(channel, "loopback_leg")) == NULL) + return SWITCH_STATUS_SUCCESS; + + if(strncmp(loopback_leg, "B", 1)) + return SWITCH_STATUS_SUCCESS; + + switch_channel_get_variables(channel, &event); + switch_event_create_plain(&to_add, SWITCH_EVENT_CHANNEL_DATA); + switch_event_create_plain(&to_remove, SWITCH_EVENT_CHANNEL_DATA); + + for(header = event->headers; header; header = header->next) { + if(!strncmp(header->name, "Export-Loopback-", 16)) { + switch_event_add_variable_name_printf(to_add, SWITCH_STACK_BOTTOM, header->value, "%s", header->name+16); + switch_channel_set_variable(channel, header->name, NULL); + n++; + } else if(!strncmp(header->name, "ecallmgr_", 9)) { + switch_event_add_header_string(to_remove, SWITCH_STACK_BOTTOM, header->name, header->value); + } + } + if(n) { + for(header = to_remove->headers; header; header = header->next) { + switch_channel_set_variable(channel, header->name, NULL); + } + for(header = to_add->headers; header; header = header->next) { + switch_channel_set_variable(channel, header->name, header->value); + } + + // cleanup leg A + loopback_aleg = switch_channel_get_variable(channel, "other_loopback_leg_uuid"); + if(loopback_aleg != NULL) { + switch_core_session_t *a_session = NULL; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "found loopback a-leg uuid - %s\n", loopback_aleg); + if ((a_session = switch_core_session_locate(loopback_aleg))) { + switch_channel_t *a_channel = switch_core_session_get_channel(a_session); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "found loopback session a - %s\n", loopback_aleg); + switch_channel_del_variable_prefix(a_channel, "Export-Loopback-"); + switch_core_session_rwunlock(a_session); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Couldn't locate loopback session a - %s\n", loopback_aleg); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Couldn't find loopback a-leg uuid!\n"); + } + } + + switch_event_destroy(&event); + switch_event_destroy(&to_add); + switch_event_destroy(&to_remove); + + return SWITCH_STATUS_SUCCESS; + +} + +static void kz_tweaks_handle_caller_id(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + const char *acl_token = switch_channel_get_variable(channel, "acl_token"); + if(acl_token) { + switch_ivr_set_user(session, acl_token); + } +} + +static switch_status_t kz_tweaks_handle_auth_token(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_event_t *event; + const char *token = switch_channel_get_variable(channel, "sip_h_X-FS-Auth-Token"); + if(token) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Authenticating user for nightmare xfer %s\n", token); + if (switch_ivr_set_user(session, token) == SWITCH_STATUS_SUCCESS) { + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(channel, event); + switch_event_fire(&event); + } + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Authenticated user from nightmare xfer %s\n", token); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Error Authenticating user for nightmare xfer %s\n", token); + } + } + + return SWITCH_STATUS_SUCCESS; + +} + +static switch_status_t kz_tweaks_handle_nightmare_xfer(switch_core_session_t *session) +{ + switch_core_session_t *replace_session = NULL; + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_event_t *event; + const char *replaced_call_id = switch_channel_get_variable(channel, "sip_replaces_call_id"); + const char *core_uuid = switch_channel_get_variable(channel, "sip_h_X-FS-From-Core-UUID"); + const char *partner_uuid = switch_channel_get_variable(channel, "sip_h_X-FS-Refer-Partner-UUID"); + const char *interaction_id = switch_channel_get_variable(channel, "sip_h_X-FS-Call-Interaction-ID"); + if(core_uuid && partner_uuid && replaced_call_id && interaction_id) { + switch_channel_set_variable(channel, "ecallmgr_Call-Interaction-ID", interaction_id); + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(channel, event); + switch_event_fire(&event); + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "checking nightmare xfer tweak for %s\n", switch_channel_get_uuid(channel)); + if ((replace_session = switch_core_session_locate(replaced_call_id))) { + switch_channel_t *replaced_call_channel = switch_core_session_get_channel(replace_session); + switch_channel_set_variable(replaced_call_channel, "ecallmgr_Call-Interaction-ID", interaction_id); + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(replaced_call_channel, event); + switch_event_fire(&event); + } + switch_core_session_rwunlock(replace_session); + } + if ((replace_session = switch_core_session_locate(partner_uuid))) { + switch_channel_t *replaced_call_channel = switch_core_session_get_channel(replace_session); + switch_channel_set_variable(replaced_call_channel, "ecallmgr_Call-Interaction-ID", interaction_id); + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(replaced_call_channel, event); + switch_event_fire(&event); + } + switch_core_session_rwunlock(replace_session); + } + } + + return SWITCH_STATUS_SUCCESS; + +} + +static switch_status_t kz_tweaks_handle_replaces_id(switch_core_session_t *session) +{ + switch_core_session_t *replace_call_session = NULL; + switch_event_t *event; + switch_channel_t *channel = switch_core_session_get_channel(session); + const char *replaced_call_id = switch_channel_get_variable(channel, "sip_replaces_call_id"); + const char *core_uuid = switch_channel_get_variable(channel, "sip_h_X-FS-From-Core-UUID"); + if((!core_uuid) && replaced_call_id) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "checking replaces header tweak for %s\n", replaced_call_id); + if ((replace_call_session = switch_core_session_locate(replaced_call_id))) { + switch_channel_t *replaced_call_channel = switch_core_session_get_channel(replace_call_session); + int i; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "setting bridge variables from %s to %s\n", replaced_call_id, switch_channel_get_uuid(channel)); + for(i = 0; bridge_variables[i] != NULL; i++) { + const char *val = switch_channel_get_variable_dup(replaced_call_channel, bridge_variables[i], SWITCH_TRUE, -1); + switch_channel_set_variable(channel, bridge_variables[i], val); + switch_safe_strdup(val); + } + if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(channel, event); + switch_event_fire(&event); + } + switch_core_session_rwunlock(replace_call_session); + } + } + + return SWITCH_STATUS_SUCCESS; + +} + + +static switch_status_t kz_tweaks_handle_switch_uri(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + const char *profile_url = switch_channel_get_variable(channel, "sofia_profile_url"); + if(profile_url) { + int n = strcspn(profile_url, "@"); + switch_channel_set_variable(channel, "Switch-URL", profile_url); + switch_channel_set_variable_printf(channel, "Switch-URI", "sip:%s", n > 0 ? profile_url + n + 1 : profile_url); + } + + return SWITCH_STATUS_SUCCESS; + +} + + +static switch_status_t kz_tweaks_on_init(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "checking tweaks for %s\n", switch_channel_get_uuid(channel)); + kz_tweaks_handle_switch_uri(session); + kz_tweaks_handle_caller_id(session); + kz_tweaks_handle_auth_token(session); + kz_tweaks_handle_nightmare_xfer(session); + kz_tweaks_handle_replaces_id(session); + kz_tweaks_handle_loopback(session); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_state_handler_table_t kz_tweaks_state_handlers = { + /*.on_init */ kz_tweaks_on_init, + /*.on_routing */ NULL, + /*.on_execute */ NULL, + /*.on_hangup */ NULL, + /*.on_exchange_media */ NULL, + /*.on_soft_execute */ NULL, + /*.on_consume_media */ NULL, + /*.on_hibernate */ NULL, + /*.on_reset */ NULL, + /*.on_park */ NULL, + /*.on_reporting */ NULL +}; + + +static void kz_tweaks_register_state_handlers() +{ + switch_core_add_state_handler(&kz_tweaks_state_handlers); +} + +static void kz_tweaks_unregister_state_handlers() +{ + switch_core_remove_state_handler(&kz_tweaks_state_handlers); +} + +static void kz_tweaks_bind_events() +{ + if (switch_event_bind("kz_tweaks", SWITCH_EVENT_CHANNEL_BRIDGE, SWITCH_EVENT_SUBCLASS_ANY, kz_tweaks_channel_bridge_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind to channel_bridge event!\n"); + } + if (switch_event_bind("kz_tweaks", SWITCH_EVENT_CUSTOM, "sofia::replaced", kz_tweaks_channel_replaced_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind to channel_bridge event!\n"); + } + if (switch_event_bind("kz_tweaks", SWITCH_EVENT_CUSTOM, "sofia::intercepted", kz_tweaks_channel_intercepted_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind to channel_bridge event!\n"); + } + if (switch_event_bind("kz_tweaks", SWITCH_EVENT_CUSTOM, "sofia::transferor", kz_tweaks_channel_transferor_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind to channel_bridge event!\n"); + } + if (switch_event_bind("kz_tweaks", SWITCH_EVENT_CUSTOM, "sofia::transferee", kz_tweaks_channel_transferee_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind to channel_bridge event!\n"); + } +} + +static void kz_tweaks_unbind_events() +{ + switch_event_unbind_callback(kz_tweaks_channel_bridge_event_handler); + switch_event_unbind_callback(kz_tweaks_channel_replaced_event_handler); + switch_event_unbind_callback(kz_tweaks_channel_intercepted_event_handler); + switch_event_unbind_callback(kz_tweaks_channel_transferor_event_handler); + switch_event_unbind_callback(kz_tweaks_channel_transferee_event_handler); +} + +void kz_tweaks_start() +{ + kz_tweaks_register_state_handlers(); + kz_tweaks_bind_events(); +} + +void kz_tweaks_stop() +{ + kz_tweaks_unbind_events(); + kz_tweaks_unregister_state_handlers(); +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ + + diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_utils.c b/src/mod/event_handlers/mod_kazoo/kazoo_utils.c index 024e98b858..c793b3d566 100644 --- a/src/mod/event_handlers/mod_kazoo/kazoo_utils.c +++ b/src/mod/event_handlers/mod_kazoo/kazoo_utils.c @@ -34,664 +34,99 @@ */ #include "mod_kazoo.h" -/* Stolen from code added to ei in R12B-5. - * Since not everyone has this version yet; - * provide our own version. - * */ +char *kazoo_expand_header(switch_memory_pool_t *pool, switch_event_t *event, char *val) +{ + char *expanded; + char *dup = NULL; -#define put8(s,n) do { \ - (s)[0] = (char)((n) & 0xff); \ - (s) += 1; \ - } while (0) + expanded = switch_event_expand_headers(event, val); + dup = switch_core_strdup(pool, expanded); -#define put32be(s,n) do { \ - (s)[0] = ((n) >> 24) & 0xff; \ - (s)[1] = ((n) >> 16) & 0xff; \ - (s)[2] = ((n) >> 8) & 0xff; \ - (s)[3] = (n) & 0xff; \ - (s) += 4; \ - } while (0) + if (expanded != val) { + free(expanded); + } -#ifdef EI_DEBUG -static void ei_x_print_reg_msg(ei_x_buff *buf, char *dest, int send) { - char *mbuf = NULL; - int i = 1; - - ei_s_print_term(&mbuf, buf->buff, &i); - - if (send) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Encoded term %s to '%s'\n", mbuf, dest); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Decoded term %s for '%s'\n", mbuf, dest); - } - - free(mbuf); + return dup; } -static void ei_x_print_msg(ei_x_buff *buf, erlang_pid *pid, int send) { - char *pbuf = NULL; - int i = 0; - ei_x_buff pidbuf; - - ei_x_new(&pidbuf); - ei_x_encode_pid(&pidbuf, pid); - - ei_s_print_term(&pbuf, pidbuf.buff, &i); - - ei_x_print_reg_msg(buf, pbuf, send); - free(pbuf); -} -#endif - -void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event) { - ei_encode_switch_event_headers_2(ebuf, event, 1); -} - -void ei_encode_switch_event_headers_2(ei_x_buff *ebuf, switch_event_t *event, int encode) { - switch_event_header_t *hp; - char *uuid = switch_event_get_header(event, "unique-id"); - int i; - - for (i = 0, hp = event->headers; hp; hp = hp->next, i++); - - if (event->body) - i++; - - ei_x_encode_list_header(ebuf, i + 1); - - if (uuid) { - char *unique_id = switch_event_get_header(event, "unique-id"); - ei_x_encode_binary(ebuf, unique_id, strlen(unique_id)); - } else { - ei_x_encode_atom(ebuf, "undefined"); - } - - for (hp = event->headers; hp; hp = hp->next) { - ei_x_encode_tuple_header(ebuf, 2); - ei_x_encode_binary(ebuf, hp->name, strlen(hp->name)); - if(encode) - switch_url_decode(hp->value); - ei_x_encode_binary(ebuf, hp->value, strlen(hp->value)); - } - - if (event->body) { - ei_x_encode_tuple_header(ebuf, 2); - ei_x_encode_binary(ebuf, "body", strlen("body")); - ei_x_encode_binary(ebuf, event->body, strlen(event->body)); - } - - ei_x_encode_empty_list(ebuf); -} - -void close_socket(switch_socket_t ** sock) { - if (*sock) { - switch_socket_shutdown(*sock, SWITCH_SHUTDOWN_READWRITE); - switch_socket_close(*sock); - *sock = NULL; +char* switch_event_get_first_of(switch_event_t *event, const char *list[]) +{ + switch_event_header_t *header = NULL; + int i = 0; + while(list[i] != NULL) { + if((header = switch_event_get_header_ptr(event, list[i])) != NULL) + break; + i++; + } + if(header != NULL) { + return header->value; + } else { + return "nodomain"; } } -void close_socketfd(int *sockfd) { - if (*sockfd) { - shutdown(*sockfd, SHUT_RDWR); - close(*sockfd); +SWITCH_DECLARE(switch_status_t) switch_event_add_variable_name_printf(switch_event_t *event, switch_stack_t stack, const char *val, const char *fmt, ...) +{ + int ret = 0; + char *varname; + va_list ap; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + switch_assert(event != NULL); + + + va_start(ap, fmt); + ret = switch_vasprintf(&varname, fmt, ap); + va_end(ap); + + if (ret == -1) { + return SWITCH_STATUS_MEMERR; } + + status = switch_event_add_header_string(event, stack, varname, val); + + free(varname); + + return status; } -switch_socket_t *create_socket_with_port(switch_memory_pool_t *pool, switch_port_t port) { - switch_sockaddr_t *sa; - switch_socket_t *socket; - - if(switch_sockaddr_info_get(&sa, kazoo_globals.ip, SWITCH_UNSPEC, port, 0, pool)) { - return NULL; - } - - if (switch_socket_create(&socket, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, pool)) { - return NULL; - } - - if (switch_socket_opt_set(socket, SWITCH_SO_REUSEADDR, 1)) { - return NULL; - } - - if (switch_socket_bind(socket, sa)) { - return NULL; - } - - if (switch_socket_listen(socket, 5)){ - return NULL; - } - - switch_getnameinfo(&kazoo_globals.hostname, sa, 0); - - // if (kazoo_globals.nat_map && switch_nat_get_type()) { - // switch_nat_add_mapping(port, SWITCH_NAT_TCP, NULL, SWITCH_FALSE); - // } - - return socket; +SWITCH_DECLARE(switch_xml_t) kz_xml_child(switch_xml_t xml, const char *name) +{ + xml = (xml) ? xml->child : NULL; + while (xml && strcasecmp(name, xml->name)) + xml = xml->sibling; + return xml; } -switch_socket_t *create_socket(switch_memory_pool_t *pool) { - return create_socket_with_port(pool, 0); +void kz_xml_process(switch_xml_t cfg) +{ + switch_xml_t xml_process; + for (xml_process = kz_xml_child(cfg, "X-PRE-PROCESS"); xml_process; xml_process = xml_process->next) { + const char *cmd = switch_xml_attr(xml_process, "cmd"); + const char *data = switch_xml_attr(xml_process, "data"); + if(cmd != NULL && !strcasecmp(cmd, "set") && data) { + char *name = (char *) data; + char *val = strchr(name, '='); -} + if (val) { + char *ve = val++; + while (*val && *val == ' ') { + val++; + } + *ve-- = '\0'; + while (*ve && *ve == ' ') { + *ve-- = '\0'; + } + } -switch_status_t create_ei_cnode(const char *ip_addr, const char *name, struct ei_cnode_s *ei_cnode) { - char hostname[EI_MAXHOSTNAMELEN + 1] = ""; - char nodename[MAXNODELEN + 1]; - char cnodename[EI_MAXALIVELEN + 1]; - //EI_MAX_COOKIE_SIZE+1 - char *atsign; - - /* copy the erlang interface nodename into something we can modify */ - strncpy(cnodename, name, EI_MAXALIVELEN); - - if ((atsign = strchr(cnodename, '@'))) { - /* we got a qualified node name, don't guess the host/domain */ - snprintf(nodename, MAXNODELEN + 1, "%s", kazoo_globals.ei_nodename); - /* truncate the alivename at the @ */ - *atsign = '\0'; - } else { - if (zstr(kazoo_globals.hostname) || !strncasecmp(kazoo_globals.ip, "0.0.0.0", 7) || !strncasecmp(kazoo_globals.ip, "::", 2)) { - memcpy(hostname, switch_core_get_hostname(), EI_MAXHOSTNAMELEN); - } else { - memcpy(hostname, kazoo_globals.hostname, EI_MAXHOSTNAMELEN); - } - - snprintf(nodename, MAXNODELEN + 1, "%s@%s", kazoo_globals.ei_nodename, hostname); - } - - if (kazoo_globals.ei_shortname) { - char *off; - if ((off = strchr(nodename, '.'))) { - *off = '\0'; + if (name && val) { + switch_core_set_variable(name, val); + } } } - /* init the ec stuff */ - if (ei_connect_xinit(ei_cnode, hostname, cnodename, nodename, (Erl_IpAddr) ip_addr, kazoo_globals.ei_cookie, 0) < 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize the erlang interface connection structure\n"); - return SWITCH_STATUS_FALSE; - } - - return SWITCH_STATUS_SUCCESS; } -switch_status_t ei_compare_pids(const erlang_pid *pid1, const erlang_pid *pid2) { - if ((!strcmp(pid1->node, pid2->node)) - && pid1->creation == pid2->creation - && pid1->num == pid2->num - && pid1->serial == pid2->serial) { - return SWITCH_STATUS_SUCCESS; - } else { - return SWITCH_STATUS_FALSE; - } -} - -void ei_link(ei_node_t *ei_node, erlang_pid * from, erlang_pid * to) { - char msgbuf[2048]; - char *s; - int index = 0; - - index = 5; /* max sizes: */ - ei_encode_version(msgbuf, &index); /* 1 */ - ei_encode_tuple_header(msgbuf, &index, 3); - ei_encode_long(msgbuf, &index, ERL_LINK); - ei_encode_pid(msgbuf, &index, from); /* 268 */ - ei_encode_pid(msgbuf, &index, to); /* 268 */ - - /* 5 byte header missing */ - s = msgbuf; - put32be(s, index - 4); /* 4 */ - put8(s, ERL_PASS_THROUGH); /* 1 */ - /* sum: 542 */ - - if (write(ei_node->nodefd, msgbuf, index) == -1) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to link to process on %s\n", ei_node->peer_nodename); - } -} - -void ei_encode_switch_event(ei_x_buff *ebuf, switch_event_t *event) { - ei_x_encode_tuple_header(ebuf, 2); - ei_x_encode_atom(ebuf, "event"); - ei_encode_switch_event_headers(ebuf, event); -} - -int ei_helper_send(ei_node_t *ei_node, erlang_pid *to, ei_x_buff *buf) { - int ret = 0; - - if (ei_node->nodefd) { -#ifdef EI_DEBUG - ei_x_print_msg(buf, to, 1); -#endif - ret = ei_send(ei_node->nodefd, to, buf->buff, buf->index); - } - - return ret; -} - -int ei_decode_atom_safe(char *buf, int *index, char *dst) { - int type, size; - - ei_get_type(buf, index, &type, &size); - - if (type != ERL_ATOM_EXT) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed atom\n", type, size); - return -1; - } else if (size > MAXATOMLEN) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Requested decoding of atom with size %d into a buffer of size %d\n", size, MAXATOMLEN); - return -1; - } else { - return ei_decode_atom(buf, index, dst); - } -} - -int ei_decode_string_or_binary(char *buf, int *index, char **dst) { - int type, size, res; - long len; - - ei_get_type(buf, index, &type, &size); - - if (type != ERL_STRING_EXT && type != ERL_BINARY_EXT && type != ERL_NIL_EXT) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed binary or string\n", type, size); - return -1; - } - - *dst = malloc(size + 1); - - if (type == ERL_NIL_EXT) { - res = 0; - **dst = '\0'; - } else if (type == ERL_BINARY_EXT) { - res = ei_decode_binary(buf, index, *dst, &len); - (*dst)[len] = '\0'; - } else { - res = ei_decode_string(buf, index, *dst); - } - - return res; -} - -int ei_decode_string_or_binary_limited(char *buf, int *index, int maxsize, char *dst) { - int type, size, res; - long len; - - ei_get_type(buf, index, &type, &size); - - if (type != ERL_STRING_EXT && type != ERL_BINARY_EXT && type != ERL_NIL_EXT) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed binary or string\n", type, size); - return -1; - } - - if (size > maxsize) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Requested decoding of %s with size %d into a buffer of size %d\n", - type == ERL_BINARY_EXT ? "binary" : "string", size, maxsize); - return -1; - } - - if (type == ERL_NIL_EXT) { - res = 0; - *dst = '\0'; - } else if (type == ERL_BINARY_EXT) { - res = ei_decode_binary(buf, index, dst, &len); - dst[len] = '\0'; /* binaries aren't null terminated */ - } else { - res = ei_decode_string(buf, index, dst); - } - - return res; -} - -switch_hash_t *create_default_filter() { - switch_hash_t *filter; - - switch_core_hash_init(&filter); - - switch_core_hash_insert(filter, "Acquired-UUID", "1"); - switch_core_hash_insert(filter, "action", "1"); - switch_core_hash_insert(filter, "Action", "1"); - switch_core_hash_insert(filter, "alt_event_type", "1"); - switch_core_hash_insert(filter, "Answer-State", "1"); - switch_core_hash_insert(filter, "Application", "1"); - switch_core_hash_insert(filter, "Application-Data", "1"); - switch_core_hash_insert(filter, "Application-Name", "1"); - switch_core_hash_insert(filter, "Application-Response", "1"); - switch_core_hash_insert(filter, "att_xfer_replaced_by", "1"); - switch_core_hash_insert(filter, "Auth-Method", "1"); - switch_core_hash_insert(filter, "Auth-Realm", "1"); - switch_core_hash_insert(filter, "Auth-User", "1"); - switch_core_hash_insert(filter, "Bridge-A-Unique-ID", "1"); - switch_core_hash_insert(filter, "Bridge-B-Unique-ID", "1"); - switch_core_hash_insert(filter, "Call-Direction", "1"); - switch_core_hash_insert(filter, "Caller-Callee-ID-Name", "1"); - switch_core_hash_insert(filter, "Caller-Callee-ID-Number", "1"); - switch_core_hash_insert(filter, "Caller-Caller-ID-Name", "1"); - switch_core_hash_insert(filter, "Caller-Caller-ID-Number", "1"); - switch_core_hash_insert(filter, "Caller-Screen-Bit", "1"); - switch_core_hash_insert(filter, "Caller-Privacy-Hide-Name", "1"); - switch_core_hash_insert(filter, "Caller-Privacy-Hide-Number", "1"); - switch_core_hash_insert(filter, "Caller-Context", "1"); - switch_core_hash_insert(filter, "Caller-Controls", "1"); - switch_core_hash_insert(filter, "Caller-Destination-Number", "1"); - switch_core_hash_insert(filter, "Caller-Dialplan", "1"); - switch_core_hash_insert(filter, "Caller-Network-Addr", "1"); - switch_core_hash_insert(filter, "Caller-Unique-ID", "1"); - switch_core_hash_insert(filter, "Call-ID", "1"); - switch_core_hash_insert(filter, "Channel-Call-State", "1"); - switch_core_hash_insert(filter, "Channel-Call-UUID", "1"); - switch_core_hash_insert(filter, "Channel-Presence-ID", "1"); - switch_core_hash_insert(filter, "Channel-State", "1"); - switch_core_hash_insert(filter, "Chat-Permissions", "1"); - switch_core_hash_insert(filter, "Conference-Name", "1"); - switch_core_hash_insert(filter, "Conference-Profile-Name", "1"); - switch_core_hash_insert(filter, "Conference-Unique-ID", "1"); - switch_core_hash_insert(filter, "contact", "1"); - switch_core_hash_insert(filter, "Detected-Tone", "1"); - switch_core_hash_insert(filter, "dialog_state", "1"); - switch_core_hash_insert(filter, "direction", "1"); - switch_core_hash_insert(filter, "Distributed-From", "1"); - switch_core_hash_insert(filter, "DTMF-Digit", "1"); - switch_core_hash_insert(filter, "DTMF-Duration", "1"); - switch_core_hash_insert(filter, "Event-Date-Timestamp", "1"); - switch_core_hash_insert(filter, "Event-Name", "1"); - switch_core_hash_insert(filter, "Event-Subclass", "1"); - switch_core_hash_insert(filter, "expires", "1"); - switch_core_hash_insert(filter, "Expires", "1"); - switch_core_hash_insert(filter, "Ext-SIP-IP", "1"); - switch_core_hash_insert(filter, "File", "1"); - switch_core_hash_insert(filter, "FreeSWITCH-Hostname", "1"); - switch_core_hash_insert(filter, "from", "1"); - switch_core_hash_insert(filter, "Hunt-Destination-Number", "1"); - switch_core_hash_insert(filter, "ip", "1"); - switch_core_hash_insert(filter, "Message-Account", "1"); - switch_core_hash_insert(filter, "metadata", "1"); - switch_core_hash_insert(filter, "old_node_channel_uuid", "1"); - switch_core_hash_insert(filter, "Other-Leg-Callee-ID-Name", "1"); - switch_core_hash_insert(filter, "Other-Leg-Callee-ID-Number", "1"); - switch_core_hash_insert(filter, "Other-Leg-Caller-ID-Name", "1"); - switch_core_hash_insert(filter, "Other-Leg-Caller-ID-Number", "1"); - switch_core_hash_insert(filter, "Other-Leg-Destination-Number", "1"); - switch_core_hash_insert(filter, "Other-Leg-Direction", "1"); - switch_core_hash_insert(filter, "Other-Leg-Unique-ID", "1"); - switch_core_hash_insert(filter, "Other-Leg-Channel-Name", "1"); - switch_core_hash_insert(filter, "Participant-Type", "1"); - switch_core_hash_insert(filter, "Path", "1"); - switch_core_hash_insert(filter, "profile_name", "1"); - switch_core_hash_insert(filter, "Profiles", "1"); - switch_core_hash_insert(filter, "proto-specific-event-name", "1"); - switch_core_hash_insert(filter, "Raw-Application-Data", "1"); - switch_core_hash_insert(filter, "realm", "1"); - switch_core_hash_insert(filter, "Resigning-UUID", "1"); - switch_core_hash_insert(filter, "set", "1"); - switch_core_hash_insert(filter, "sip_auto_answer", "1"); - switch_core_hash_insert(filter, "sip_auth_method", "1"); - switch_core_hash_insert(filter, "sip_from_host", "1"); - switch_core_hash_insert(filter, "sip_from_user", "1"); - switch_core_hash_insert(filter, "sip_to_host", "1"); - switch_core_hash_insert(filter, "sip_to_user", "1"); - switch_core_hash_insert(filter, "sub-call-id", "1"); - switch_core_hash_insert(filter, "technology", "1"); - switch_core_hash_insert(filter, "to", "1"); - switch_core_hash_insert(filter, "Unique-ID", "1"); - switch_core_hash_insert(filter, "URL", "1"); - switch_core_hash_insert(filter, "username", "1"); - switch_core_hash_insert(filter, "variable_channel_is_moving", "1"); - switch_core_hash_insert(filter, "variable_collected_digits", "1"); - switch_core_hash_insert(filter, "variable_current_application", "1"); - switch_core_hash_insert(filter, "variable_current_application_data", "1"); - switch_core_hash_insert(filter, "variable_domain_name", "1"); - switch_core_hash_insert(filter, "variable_effective_caller_id_name", "1"); - switch_core_hash_insert(filter, "variable_effective_caller_id_number", "1"); - switch_core_hash_insert(filter, "variable_holding_uuid", "1"); - switch_core_hash_insert(filter, "variable_hold_music", "1"); - switch_core_hash_insert(filter, "variable_media_group_id", "1"); - switch_core_hash_insert(filter, "variable_originate_disposition", "1"); - switch_core_hash_insert(filter, "variable_origination_uuid", "1"); - switch_core_hash_insert(filter, "variable_playback_terminator_used", "1"); - switch_core_hash_insert(filter, "variable_presence_id", "1"); - switch_core_hash_insert(filter, "variable_record_ms", "1"); - switch_core_hash_insert(filter, "variable_recovered", "1"); - switch_core_hash_insert(filter, "variable_silence_hits_exhausted", "1"); - switch_core_hash_insert(filter, "variable_sip_auth_realm", "1"); - switch_core_hash_insert(filter, "variable_sip_from_host", "1"); - switch_core_hash_insert(filter, "variable_sip_from_user", "1"); - switch_core_hash_insert(filter, "variable_sip_from_tag", "1"); - switch_core_hash_insert(filter, "variable_sip_h_X-AUTH-IP", "1"); - switch_core_hash_insert(filter, "variable_sip_received_ip", "1"); - switch_core_hash_insert(filter, "variable_sip_to_host", "1"); - switch_core_hash_insert(filter, "variable_sip_to_user", "1"); - switch_core_hash_insert(filter, "variable_sip_to_tag", "1"); - switch_core_hash_insert(filter, "variable_sofia_profile_name", "1"); - switch_core_hash_insert(filter, "variable_transfer_history", "1"); - switch_core_hash_insert(filter, "variable_user_name", "1"); - switch_core_hash_insert(filter, "variable_endpoint_disposition", "1"); - switch_core_hash_insert(filter, "variable_originate_disposition", "1"); - switch_core_hash_insert(filter, "variable_bridge_hangup_cause", "1"); - switch_core_hash_insert(filter, "variable_hangup_cause", "1"); - switch_core_hash_insert(filter, "variable_last_bridge_proto_specific_hangup_cause", "1"); - switch_core_hash_insert(filter, "variable_proto_specific_hangup_cause", "1"); - switch_core_hash_insert(filter, "VM-Call-ID", "1"); - switch_core_hash_insert(filter, "VM-sub-call-id", "1"); - switch_core_hash_insert(filter, "whistle_application_name", "1"); - switch_core_hash_insert(filter, "whistle_application_response", "1"); - switch_core_hash_insert(filter, "whistle_event_name", "1"); - switch_core_hash_insert(filter, "kazoo_application_name", "1"); - switch_core_hash_insert(filter, "kazoo_application_response", "1"); - switch_core_hash_insert(filter, "kazoo_event_name", "1"); - switch_core_hash_insert(filter, "sip_auto_answer_notify", "1"); - switch_core_hash_insert(filter, "eavesdrop_group", "1"); - switch_core_hash_insert(filter, "origination_caller_id_name", "1"); - switch_core_hash_insert(filter, "origination_caller_id_number", "1"); - switch_core_hash_insert(filter, "origination_callee_id_name", "1"); - switch_core_hash_insert(filter, "origination_callee_id_number", "1"); - switch_core_hash_insert(filter, "sip_auth_username", "1"); - switch_core_hash_insert(filter, "sip_auth_password", "1"); - switch_core_hash_insert(filter, "effective_caller_id_name", "1"); - switch_core_hash_insert(filter, "effective_caller_id_number", "1"); - switch_core_hash_insert(filter, "effective_callee_id_name", "1"); - switch_core_hash_insert(filter, "effective_callee_id_number", "1"); - switch_core_hash_insert(filter, "variable_destination_number", "1"); - switch_core_hash_insert(filter, "variable_effective_callee_id_name", "1"); - switch_core_hash_insert(filter, "variable_effective_callee_id_number", "1"); - switch_core_hash_insert(filter, "variable_record_silence_hits", "1"); - switch_core_hash_insert(filter, "variable_refer_uuid", "1"); - switch_core_hash_insert(filter, "variable_sip_call_id", "1"); - switch_core_hash_insert(filter, "variable_sip_h_Referred-By", "1"); - switch_core_hash_insert(filter, "variable_sip_h_X-AUTH-PORT", "1"); - switch_core_hash_insert(filter, "variable_sip_loopback_req_uri", "1"); - switch_core_hash_insert(filter, "variable_sip_received_port", "1"); - switch_core_hash_insert(filter, "variable_sip_refer_to", "1"); - switch_core_hash_insert(filter, "variable_sip_req_host", "1"); - switch_core_hash_insert(filter, "variable_sip_req_uri", "1"); - switch_core_hash_insert(filter, "variable_transfer_source", "1"); - switch_core_hash_insert(filter, "variable_uuid", "1"); - - /* Registration headers */ - switch_core_hash_insert(filter, "call-id", "1"); - switch_core_hash_insert(filter, "profile-name", "1"); - switch_core_hash_insert(filter, "from-user", "1"); - switch_core_hash_insert(filter, "from-host", "1"); - switch_core_hash_insert(filter, "presence-hosts", "1"); - switch_core_hash_insert(filter, "contact", "1"); - switch_core_hash_insert(filter, "rpid", "1"); - switch_core_hash_insert(filter, "status", "1"); - switch_core_hash_insert(filter, "expires", "1"); - switch_core_hash_insert(filter, "to-user", "1"); - switch_core_hash_insert(filter, "to-host", "1"); - switch_core_hash_insert(filter, "network-ip", "1"); - switch_core_hash_insert(filter, "network-port", "1"); - switch_core_hash_insert(filter, "username", "1"); - switch_core_hash_insert(filter, "realm", "1"); - switch_core_hash_insert(filter, "user-agent", "1"); - - switch_core_hash_insert(filter, "Hangup-Cause", "1"); - switch_core_hash_insert(filter, "Unique-ID", "1"); - switch_core_hash_insert(filter, "variable_switch_r_sdp", "1"); - switch_core_hash_insert(filter, "variable_rtp_local_sdp_str", "1"); - switch_core_hash_insert(filter, "variable_sip_to_uri", "1"); - switch_core_hash_insert(filter, "variable_sip_from_uri", "1"); - switch_core_hash_insert(filter, "variable_sip_user_agent", "1"); - switch_core_hash_insert(filter, "variable_duration", "1"); - switch_core_hash_insert(filter, "variable_billsec", "1"); - switch_core_hash_insert(filter, "variable_billmsec", "1"); - switch_core_hash_insert(filter, "variable_progresssec", "1"); - switch_core_hash_insert(filter, "variable_progress_uepoch", "1"); - switch_core_hash_insert(filter, "variable_progress_media_uepoch", "1"); - switch_core_hash_insert(filter, "variable_start_uepoch", "1"); - switch_core_hash_insert(filter, "variable_digits_dialed", "1"); - switch_core_hash_insert(filter, "Member-ID", "1"); - switch_core_hash_insert(filter, "Floor", "1"); - switch_core_hash_insert(filter, "Video", "1"); - switch_core_hash_insert(filter, "Hear", "1"); - switch_core_hash_insert(filter, "Speak", "1"); - switch_core_hash_insert(filter, "Talking", "1"); - switch_core_hash_insert(filter, "Current-Energy", "1"); - switch_core_hash_insert(filter, "Energy-Level", "1"); - switch_core_hash_insert(filter, "Mute-Detect", "1"); - - /* RTMP headers */ - switch_core_hash_insert(filter, "RTMP-Session-ID", "1"); - switch_core_hash_insert(filter, "RTMP-Profile", "1"); - switch_core_hash_insert(filter, "RTMP-Flash-Version", "1"); - switch_core_hash_insert(filter, "RTMP-SWF-URL", "1"); - switch_core_hash_insert(filter, "RTMP-TC-URL", "1"); - switch_core_hash_insert(filter, "RTMP-Page-URL", "1"); - switch_core_hash_insert(filter, "User", "1"); - switch_core_hash_insert(filter, "Domain", "1"); - - /* Fax headers */ - switch_core_hash_insert(filter, "variable_fax_bad_rows", "1"); - switch_core_hash_insert(filter, "variable_fax_document_total_pages", "1"); - switch_core_hash_insert(filter, "variable_fax_document_transferred_pages", "1"); - switch_core_hash_insert(filter, "variable_fax_ecm_used", "1"); - switch_core_hash_insert(filter, "variable_fax_result_code", "1"); - switch_core_hash_insert(filter, "variable_fax_result_text", "1"); - switch_core_hash_insert(filter, "variable_fax_success", "1"); - switch_core_hash_insert(filter, "variable_fax_transfer_rate", "1"); - switch_core_hash_insert(filter, "variable_fax_local_station_id", "1"); - switch_core_hash_insert(filter, "variable_fax_remote_station_id", "1"); - switch_core_hash_insert(filter, "variable_fax_remote_country", "1"); - switch_core_hash_insert(filter, "variable_fax_remote_vendor", "1"); - switch_core_hash_insert(filter, "variable_fax_remote_model", "1"); - switch_core_hash_insert(filter, "variable_fax_image_resolution", "1"); - switch_core_hash_insert(filter, "variable_fax_file_image_resolution", "1"); - switch_core_hash_insert(filter, "variable_fax_image_size", "1"); - switch_core_hash_insert(filter, "variable_fax_image_pixel_size", "1"); - switch_core_hash_insert(filter, "variable_fax_file_image_pixel_size", "1"); - switch_core_hash_insert(filter, "variable_fax_longest_bad_row_run", "1"); - switch_core_hash_insert(filter, "variable_fax_encoding", "1"); - switch_core_hash_insert(filter, "variable_fax_encoding_name", "1"); - switch_core_hash_insert(filter, "variable_fax_header", "1"); - switch_core_hash_insert(filter, "variable_fax_ident", "1"); - switch_core_hash_insert(filter, "variable_fax_timezone", "1"); - switch_core_hash_insert(filter, "variable_fax_doc_id", "1"); - switch_core_hash_insert(filter, "variable_fax_doc_database", "1"); - switch_core_hash_insert(filter, "variable_has_t38", "1"); - - /* Secure headers */ - switch_core_hash_insert(filter, "variable_sdp_secure_savp_only", "1"); - switch_core_hash_insert(filter, "variable_rtp_has_crypto", "1"); - switch_core_hash_insert(filter, "variable_rtp_secure_media", "1"); - switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed", "1"); - switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed_audio", "1"); - switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed_video", "1"); - switch_core_hash_insert(filter, "variable_zrtp_secure_media", "1"); - switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed", "1"); - switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed_audio", "1"); - switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed_video", "1"); - switch_core_hash_insert(filter, "sdp_secure_savp_only", "1"); - switch_core_hash_insert(filter, "rtp_has_crypto", "1"); - switch_core_hash_insert(filter, "rtp_secure_media", "1"); - switch_core_hash_insert(filter, "rtp_secure_media_confirmed", "1"); - switch_core_hash_insert(filter, "rtp_secure_media_confirmed_audio", "1"); - switch_core_hash_insert(filter, "rtp_secure_media_confirmed_video", "1"); - switch_core_hash_insert(filter, "zrtp_secure_media", "1"); - switch_core_hash_insert(filter, "zrtp_secure_media_confirmed", "1"); - switch_core_hash_insert(filter, "zrtp_secure_media_confirmed_audio", "1"); - switch_core_hash_insert(filter, "zrtp_secure_media_confirmed_video", "1"); - - /* Device Redirect headers */ - switch_core_hash_insert(filter, "variable_last_bridge_hangup_cause", "1"); - switch_core_hash_insert(filter, "variable_sip_redirected_by", "1"); - switch_core_hash_insert(filter, "intercepted_by", "1"); - switch_core_hash_insert(filter, "variable_bridge_uuid", "1"); - switch_core_hash_insert(filter, "Record-File-Path", "1"); - - /* Loopback headers */ - switch_core_hash_insert(filter, "variable_loopback_bowout_on_execute", "1"); - switch_core_hash_insert(filter, "variable_loopback_bowout", "1"); - switch_core_hash_insert(filter, "variable_other_loopback_leg_uuid", "1"); - switch_core_hash_insert(filter, "variable_loopback_leg", "1"); - switch_core_hash_insert(filter, "variable_is_loopback", "1"); - - // SMS - switch_core_hash_insert(filter, "Message-ID", "1"); - switch_core_hash_insert(filter, "Delivery-Failure", "1"); - switch_core_hash_insert(filter, "Delivery-Result-Code", "1"); - - return filter; -} - -static void *SWITCH_THREAD_FUNC fetch_config_filters_exec(switch_thread_t *thread, void *obj) -{ - char *cf = "kazoo.conf"; - switch_xml_t cfg, xml, child, param; - switch_event_t *params; - switch_memory_pool_t *pool = (switch_memory_pool_t *)obj; - - switch_event_create(¶ms, SWITCH_EVENT_REQUEST_PARAMS); - switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "Action", "request-filter"); - - if (!(xml = switch_xml_open_cfg(cf, &cfg, params))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open configuration file %s\n", cf); - } else if ((child = switch_xml_child(cfg, "event-filter"))) { - switch_hash_t *filter; - switch_hash_t *old_filter; - - switch_core_hash_init(&filter); - for (param = switch_xml_child(child, "header"); param; param = param->next) { - char *var = (char *) switch_xml_attr_soft(param, "name"); - switch_core_hash_insert(filter, var, "1"); - } - - old_filter = kazoo_globals.event_filter; - kazoo_globals.config_filters_fetched = 1; - kazoo_globals.event_filter = filter; - if (old_filter) { - switch_core_hash_destroy(&old_filter); - } - - switch_xml_free(xml); - } - - if (params) switch_event_destroy(¶ms); - switch_core_destroy_memory_pool(&pool); - - return NULL; -} - -void fetch_config_filters() { - switch_memory_pool_t *pool; - switch_thread_t *thread; - switch_threadattr_t *thd_attr = NULL; - switch_uuid_t uuid; - - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "fetching kazoo filters\n"); - - switch_core_new_memory_pool(&pool); - - switch_threadattr_create(&thd_attr, pool); - switch_threadattr_detach_set(thd_attr, 1); - switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); - - switch_uuid_get(&uuid); - switch_thread_create(&thread, thd_attr, fetch_config_filters_exec, pool, pool); - -} - - - /* For Emacs: * Local Variables: * mode:c diff --git a/src/mod/event_handlers/mod_kazoo/mod_kazoo.c b/src/mod/event_handlers/mod_kazoo/mod_kazoo.c index b666920d3f..5b255738ff 100644 --- a/src/mod/event_handlers/mod_kazoo/mod_kazoo.c +++ b/src/mod/event_handlers/mod_kazoo/mod_kazoo.c @@ -32,608 +32,12 @@ */ #include "mod_kazoo.h" -#define KAZOO_DESC "kazoo information" -#define KAZOO_SYNTAX " []" +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 disconnect - disconnects an Erlang node\n" - "erlang node connection - Shows the connection info\n" - "erlang node event_streams - lists the event streams for an Erlang node\n" - "erlang node fetch_bindings - lists the XML fetch bindings for an Erlang node\n" - "---------------------------------------------------------------------------------------------------------------------\n"; - - if (zstr(cmd)) { - stream->write_function(stream, "%s", usage_string); - return SWITCH_STATUS_SUCCESS; - } - - if (!(mycmd = strdup(cmd))) { - return SWITCH_STATUS_MEMERR; - } - - if (!(argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) { - stream->write_function(stream, "%s", usage_string); - switch_safe_free(mycmd); - return SWITCH_STATUS_SUCCESS; - } - - if (zstr(argv[0])) { - stream->write_function(stream, "%s", usage_string); - switch_safe_free(mycmd); - return SWITCH_STATUS_SUCCESS; - } - - if (!strncmp(argv[0], "status", 6)) { - unknown_command = 0; - api_erlang_status(stream); - } else if (!strncmp(argv[0], "event_filter", 6)) { - unknown_command = 0; - api_erlang_event_filter(stream); - } else if (!strncmp(argv[0], "nodes", 6) && !zstr(argv[1])) { - if (!strncmp(argv[1], "list", 6)) { - unknown_command = 0; - api_erlang_nodes_list(stream); - } else if (!strncmp(argv[1], "count", 6)) { - unknown_command = 0; - api_erlang_nodes_count(stream); - } - } else if (!strncmp(argv[0], "node", 6) && !zstr(argv[1]) && !zstr(argv[2])) { - if (!strncmp(argv[2], "disconnect", 6)) { - unknown_command = 0; - api_erlang_node_command(stream, argv[1], API_COMMAND_DISCONNECT); - } else if (!strncmp(argv[2], "connection", 2)) { - unknown_command = 0; - api_erlang_node_command(stream, argv[1], API_COMMAND_REMOTE_IP); - } else if (!strncmp(argv[2], "event_streams", 6)) { - unknown_command = 0; - api_erlang_node_command(stream, argv[1], API_COMMAND_STREAMS); - } else if (!strncmp(argv[2], "fetch_bindings", 6)) { - unknown_command = 0; - api_erlang_node_command(stream, argv[1], API_COMMAND_BINDINGS); - } - } - - if (unknown_command) { - stream->write_function(stream, "%s", usage_string); - } - - switch_safe_free(mycmd); - return SWITCH_STATUS_SUCCESS; -} - SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load) { switch_api_interface_t *api_interface = NULL; switch_application_interface_t *app_interface = NULL; @@ -643,34 +47,17 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load) { kazoo_globals.pool = pool; kazoo_globals.ei_nodes = NULL; - if(config() != SWITCH_STATUS_SUCCESS) { + // ensure epmd is running + + if(kazoo_load_config() != SWITCH_STATUS_SUCCESS) { // TODO: what would we need to clean up here? switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Improper configuration!\n"); return SWITCH_STATUS_TERM; } - if(create_acceptor() != SWITCH_STATUS_SUCCESS) { - // TODO: what would we need to clean up here - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to create erlang connection acceptor!\n"); - close_socket(&kazoo_globals.acceptor); - return SWITCH_STATUS_TERM; - } - /* connect my internal structure to the blank pointer passed to me */ *module_interface = switch_loadable_module_create_module_interface(pool, modname); - /* create an api for cli debug commands */ - SWITCH_ADD_API(api_interface, "erlang", KAZOO_DESC, exec_api_cmd, KAZOO_SYNTAX); - switch_console_set_complete("add erlang status"); - switch_console_set_complete("add erlang event_filter"); - switch_console_set_complete("add erlang nodes list"); - switch_console_set_complete("add erlang nodes count"); - switch_console_set_complete("add erlang node ::erlang::node disconnect"); - switch_console_set_complete("add erlang node ::erlang::node connection"); - switch_console_set_complete("add erlang node ::erlang::node event_streams"); - switch_console_set_complete("add erlang node ::erlang::node fetch_bindings"); - switch_console_add_complete_func("::erlang::node", api_complete_erlang_node); - switch_thread_rwlock_create(&kazoo_globals.ei_nodes_lock, pool); switch_set_flag(&kazoo_globals, LFLAG_RUNNING); @@ -678,12 +65,18 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load) { /* create all XML fetch agents */ bind_fetch_agents(); + /* create an api for cli debug commands */ + add_cli_api(module_interface, api_interface); + /* add our modified commands */ add_kz_commands(module_interface, api_interface); /* add our modified dptools */ add_kz_dptools(module_interface, app_interface); + /* add tweaks */ + kz_tweaks_start(); + /* indicate that the module should continue to be loaded */ return SWITCH_STATUS_SUCCESS; } @@ -691,8 +84,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load) { SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown) { int sanity = 0; - switch_console_set_complete("del erlang"); - switch_console_del_complete_func("::erlang::node"); + + remove_cli_api(); + + kz_tweaks_stop(); /* stop taking new requests and start shuting down the threads */ switch_clear_flag(&kazoo_globals, LFLAG_RUNNING); @@ -710,6 +105,8 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown) { switch_core_hash_destroy(&kazoo_globals.event_filter); } + kazoo_destroy_config(); + switch_thread_rwlock_wrlock(kazoo_globals.ei_nodes_lock); switch_thread_rwlock_unlock(kazoo_globals.ei_nodes_lock); switch_thread_rwlock_destroy(kazoo_globals.ei_nodes_lock); @@ -722,9 +119,9 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown) { unbind_fetch_agents(); /* Close the port we reserved for uPnP/Switch behind firewall, if necessary */ - // if (kazoo_globals.nat_map && switch_nat_get_type()) { - // switch_nat_del_mapping(kazoo_globals.port, SWITCH_NAT_TCP); - // } + if (kazoo_globals.nat_map && switch_nat_get_type()) { + switch_nat_del_mapping(kazoo_globals.port, SWITCH_NAT_TCP); + } /* clean up our allocated preferences */ switch_safe_free(kazoo_globals.ip); @@ -735,50 +132,6 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown) { return SWITCH_STATUS_SUCCESS; } -SWITCH_MODULE_RUNTIME_FUNCTION(mod_kazoo_runtime) { - switch_os_socket_t os_socket; - - switch_atomic_inc(&kazoo_globals.threads); - - switch_os_sock_get(&os_socket, kazoo_globals.acceptor); - - while (switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) { - int nodefd; - ErlConnect conn; - - /* zero out errno because ei_accept doesn't differentiate between a */ - /* failed authentication or a socket failure, or a client version */ - /* mismatch or a godzilla attack (and a godzilla attack is highly likely) */ - errno = 0; - - /* wait here for an erlang node to connect, timming out to check if our module is still running every now-and-again */ - if ((nodefd = ei_accept_tmo(&kazoo_globals.ei_cnode, (int) os_socket, &conn, kazoo_globals.connection_timeout)) == ERL_ERROR) { - if (erl_errno == ETIMEDOUT) { - continue; - } else if (errno) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Erlang connection acceptor socket error %d %d\n", erl_errno, errno); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, - "Erlang node connection failed - ensure your cookie matches '%s' and you are using a good nodename\n", kazoo_globals.ei_cookie); - } - continue; - } - - if (!switch_test_flag(&kazoo_globals, LFLAG_RUNNING)) { - break; - } - - /* NEW ERLANG NODE CONNECTION! Hello friend! */ - new_kazoo_node(nodefd, &conn); - } - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Erlang connection acceptor shut down\n"); - - switch_atomic_dec(&kazoo_globals.threads); - - return SWITCH_STATUS_TERM; -} - /* For Emacs: * Local Variables: diff --git a/src/mod/event_handlers/mod_kazoo/mod_kazoo.h b/src/mod/event_handlers/mod_kazoo/mod_kazoo.h index 9de7db6ea0..165e9ff1ce 100644 --- a/src/mod/event_handlers/mod_kazoo/mod_kazoo.h +++ b/src/mod/event_handlers/mod_kazoo/mod_kazoo.h @@ -1,167 +1,32 @@ #include #include +#include #include #include #include #include #include +#include #define MAX_ACL 100 #define CMD_BUFLEN 1024 * 1000 #define MAX_QUEUE_LEN 25000 #define MAX_MISSED 500 #define MAX_PID_CHARS 255 -#define VERSION "mod_kazoo v1.4.0-1" -#define API_COMMAND_DISCONNECT 0 -#define API_COMMAND_REMOTE_IP 1 -#define API_COMMAND_STREAMS 2 -#define API_COMMAND_BINDINGS 3 + +#include "kazoo_ei.h" +#include "kazoo_message.h" typedef enum { LFLAG_RUNNING = (1 << 0) } event_flag_t; -struct ei_send_msg_s { - ei_x_buff buf; - erlang_pid pid; -}; -typedef struct ei_send_msg_s ei_send_msg_t; -struct ei_received_msg_s { - ei_x_buff buf; - erlang_msg msg; -}; -typedef struct ei_received_msg_s ei_received_msg_t; -struct ei_event_binding_s { - char id[SWITCH_UUID_FORMATTED_LENGTH + 1]; - switch_event_node_t *node; - switch_event_types_t type; - const char *subclass_name; - struct ei_event_binding_s *next; -}; -typedef struct ei_event_binding_s ei_event_binding_t; -struct ei_event_stream_s { - switch_memory_pool_t *pool; - ei_event_binding_t *bindings; - switch_queue_t *queue; - switch_socket_t *acceptor; - switch_pollset_t *pollset; - switch_pollfd_t *pollfd; - switch_socket_t *socket; - switch_mutex_t *socket_mutex; - switch_bool_t connected; - char remote_ip[48]; - uint16_t remote_port; - char local_ip[48]; - uint16_t local_port; - erlang_pid pid; - uint32_t flags; - struct ei_event_stream_s *next; -}; -typedef struct ei_event_stream_s ei_event_stream_t; -struct ei_node_s { - int nodefd; - switch_atomic_t pending_bgapi; - switch_atomic_t receive_handlers; - switch_memory_pool_t *pool; - ei_event_stream_t *event_streams; - switch_mutex_t *event_streams_mutex; - switch_queue_t *send_msgs; - switch_queue_t *received_msgs; - char *peer_nodename; - switch_time_t created_time; - switch_socket_t *socket; - char remote_ip[48]; - uint16_t remote_port; - char local_ip[48]; - uint16_t local_port; - uint32_t flags; - struct ei_node_s *next; -}; -typedef struct ei_node_s ei_node_t; -struct globals_s { - switch_memory_pool_t *pool; - switch_atomic_t threads; - switch_socket_t *acceptor; - struct ei_cnode_s ei_cnode; - switch_thread_rwlock_t *ei_nodes_lock; - ei_node_t *ei_nodes; - switch_xml_binding_t *config_fetch_binding; - switch_xml_binding_t *directory_fetch_binding; - switch_xml_binding_t *dialplan_fetch_binding; - switch_xml_binding_t *chatplan_fetch_binding; - switch_xml_binding_t *channels_fetch_binding; - switch_hash_t *event_filter; - int epmdfd; - int num_worker_threads; - switch_bool_t nat_map; - switch_bool_t ei_shortname; - int ei_compat_rel; - char *ip; - char *hostname; - char *ei_cookie; - char *ei_nodename; - char *kazoo_var_prefix; - int var_prefix_length; - uint32_t flags; - int send_all_headers; - int send_all_private_headers; - int connection_timeout; - int receive_timeout; - int receive_msg_preallocate; - int event_stream_preallocate; - int send_msg_batch; - short event_stream_framing; - switch_port_t port; - int config_filters_fetched; - int io_fault_tolerance; -}; -typedef struct globals_s globals_t; -extern globals_t kazoo_globals; - -/* kazoo_node.c */ -switch_status_t new_kazoo_node(int nodefd, ErlConnect *conn); - -/* kazoo_event_stream.c */ -ei_event_stream_t *find_event_stream(ei_event_stream_t *event_streams, const erlang_pid *from); -ei_event_stream_t *new_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from); -switch_status_t remove_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from); -switch_status_t remove_event_streams(ei_event_stream_t **event_streams); -unsigned long get_stream_port(const ei_event_stream_t *event_stream); -switch_status_t add_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name); -switch_status_t remove_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name); -switch_status_t remove_event_bindings(ei_event_stream_t *event_stream); - -/* kazoo_fetch_agent.c */ -switch_status_t bind_fetch_agents(); -switch_status_t unbind_fetch_agents(); -switch_status_t remove_xml_clients(ei_node_t *ei_node); -switch_status_t add_fetch_handler(ei_node_t *ei_node, erlang_pid *from, switch_xml_binding_t *binding); -switch_status_t remove_fetch_handlers(ei_node_t *ei_node, erlang_pid *from); -switch_status_t fetch_reply(char *uuid_str, char *xml_str, switch_xml_binding_t *binding); -switch_status_t handle_api_command_streams(ei_node_t *ei_node, switch_stream_handle_t *stream); - -/* kazoo_utils.c */ -void close_socket(switch_socket_t **sock); -void close_socketfd(int *sockfd); -switch_socket_t *create_socket_with_port(switch_memory_pool_t *pool, switch_port_t port); -switch_socket_t *create_socket(switch_memory_pool_t *pool); -switch_status_t create_ei_cnode(const char *ip_addr, const char *name, struct ei_cnode_s *ei_cnode); -switch_status_t ei_compare_pids(const erlang_pid *pid1, const erlang_pid *pid2); -void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event); -void ei_encode_switch_event_headers_2(ei_x_buff *ebuf, switch_event_t *event, int decode); -void ei_link(ei_node_t *ei_node, erlang_pid * from, erlang_pid * to); -void ei_encode_switch_event(ei_x_buff * ebuf, switch_event_t *event); -int ei_helper_send(ei_node_t *ei_node, erlang_pid* to, ei_x_buff *buf); -int ei_decode_atom_safe(char *buf, int *index, char *dst); -int ei_decode_string_or_binary_limited(char *buf, int *index, int maxsize, char *dst); -int ei_decode_string_or_binary(char *buf, int *index, char **dst); -switch_hash_t *create_default_filter(); /* kazoo_commands.c */ void add_kz_commands(switch_loadable_module_interface_t **module_interface, switch_api_interface_t *api_interface); @@ -169,9 +34,23 @@ void add_kz_commands(switch_loadable_module_interface_t **module_interface, swit /* kazoo_dptools.c */ void add_kz_dptools(switch_loadable_module_interface_t **module_interface, switch_application_interface_t *app_interface); -#define _ei_x_encode_string(buf, string) { ei_x_encode_binary(buf, string, strlen(string)); } +/* kazoo_api.c */ +void add_cli_api(switch_loadable_module_interface_t **module_interface, switch_api_interface_t *api_interface); +void remove_cli_api(); + +/* kazoo_utils.c */ +char *kazoo_expand_header(switch_memory_pool_t *pool, switch_event_t *event, char *val); +char* switch_event_get_first_of(switch_event_t *event, const char *list[]); +SWITCH_DECLARE(switch_status_t) switch_event_add_variable_name_printf(switch_event_t *event, switch_stack_t stack, const char *val, const char *fmt, ...); +void kz_xml_process(switch_xml_t cfg); + +/* kazoo_tweaks.c */ +void kz_tweaks_start(); +void kz_tweaks_stop(); + +SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown); -void fetch_config_filters(); /* For Emacs: * Local Variables: