From e6ec1478efe72aba2afa0ff4169f0f3256c84a94 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 4 Jan 2014 19:57:25 +0000 Subject: [PATCH 01/14] Fix dependency of freeswitch-all-dbg Previously we were trying to rely on freeswitch-all providing freeswitch, but that doesn't seem to work as expected. --- debian/bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/bootstrap.sh b/debian/bootstrap.sh index dbe0491ee4..aea8a3fe68 100755 --- a/debian/bootstrap.sh +++ b/debian/bootstrap.sh @@ -744,7 +744,7 @@ Architecture: any Provides: $(list_freeswitch_all_dbg_provides) Replaces: $(list_freeswitch_all_dbg_replaces) Breaks: $(list_freeswitch_all_dbg_replaces) -Depends: \${misc:Depends}, freeswitch (= \${binary:Version}) +Depends: \${misc:Depends}, freeswitch-all (= \${binary:Version}) Description: debugging symbols for FreeSWITCH $(debian_wrap "${fs_description}") . From 91f210a5fc09828a8fcb8197fb9c329a3bc46ced Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Sat, 4 Jan 2014 15:18:10 -0500 Subject: [PATCH 02/14] mod_rayo CPA - added detector abstraction --- conf/rayo/autoload_configs/rayo.conf.xml | 53 +++ src/mod/event_handlers/mod_rayo/Makefile | 2 + .../conf/autoload_configs/rayo.conf.xml | 53 +++ .../mod_rayo/rayo_cpa_detector.c | 435 ++++++++++++++++++ .../mod_rayo/rayo_cpa_detector.h | 68 +++ .../mod_rayo/rayo_input_component.c | 6 +- 6 files changed, 616 insertions(+), 1 deletion(-) create mode 100644 src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c create mode 100644 src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h diff --git a/conf/rayo/autoload_configs/rayo.conf.xml b/conf/rayo/autoload_configs/rayo.conf.xml index 9a8900dbfa..45a8ab04bb 100644 --- a/conf/rayo/autoload_configs/rayo.conf.xml +++ b/conf/rayo/autoload_configs/rayo.conf.xml @@ -22,6 +22,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/event_handlers/mod_rayo/Makefile b/src/mod/event_handlers/mod_rayo/Makefile index a3bfc45d47..3b7e5dba3f 100644 --- a/src/mod/event_handlers/mod_rayo/Makefile +++ b/src/mod/event_handlers/mod_rayo/Makefile @@ -10,6 +10,7 @@ LOCAL_OBJS= $(IKS_LA) \ iks_helpers.o \ nlsml.o \ rayo_components.o \ + rayo_cpa_detector.o \ rayo_elements.o \ rayo_fax_components.o \ rayo_input_component.o \ @@ -23,6 +24,7 @@ LOCAL_SOURCES= \ iks_helpers.c \ nlsml.c \ rayo_components.c \ + rayo_cpa_detector.c \ rayo_elements.c \ rayo_fax_components.c \ rayo_input_component.c \ diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml index 9a8900dbfa..45a8ab04bb 100644 --- a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml +++ b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml @@ -22,6 +22,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c new file mode 100644 index 0000000000..9267e05456 --- /dev/null +++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c @@ -0,0 +1,435 @@ +/* + * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2014, Grasshopper + * + * 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 mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is Grasshopper + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Rienzo + * + * rayo_cpa_detector.c -- Glue to normalize events from and to allow multiple instantiation of various detectors in FreeSWITCH + */ + +#include "rayo_cpa_detector.h" + +static struct { + /** detectors supported by this module mapped by signal-type */ + switch_hash_t *detectors; +} globals; + +struct rayo_cpa_detector; + +/** + * Detector definition + */ +struct rayo_cpa_detector { + /** unique internal name of this detector */ + const char *name; + /** detector ID */ + const char *uuid; + /** start detection APP */ + const char *start_app; + /** args to pass to start detection app */ + const char *start_app_args; + /** stop detection APP */ + const char *stop_app; + /** args to pass to stop detection app */ + const char *stop_app_args; + /** (optional) name of header to get the signal type from */ + const char *signal_type_header; + /** (optional) where to get the signal value from the event */ + const char *signal_value_header; + /** (optional) where to get the signal duration from the event */ + const char *signal_duration_header; + /** detector event to signal type mapping */ + switch_hash_t *signal_type_map; +}; + +/** + * Detection state + */ +struct rayo_cpa_detector_state { + /** reference count */ + int refs; +}; + +/** + * Start detecting + * @param call_uuid call to detect signal on + * @param signal_ns namespace of signal to detect + * @param error_detail on failure, describes the error + * @return 1 if successful, 0 if failed + */ +int rayo_cpa_detector_start(const char *call_uuid, const char *signal_ns, const char **error_detail) +{ + struct rayo_cpa_detector *detector = switch_core_hash_find(globals.detectors, signal_ns); + if (detector) { + switch_core_session_t *session = switch_core_session_locate(call_uuid); + if (session) { + struct rayo_cpa_detector_state *detector_state = switch_channel_get_private(switch_core_session_get_channel(session), detector->uuid); + if (detector_state) { + /* detector is already running */ + detector_state->refs++; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Start detector %s, refs = %d\n", detector->name, detector_state->refs); + switch_core_session_rwunlock(session); + return 1; + } + detector_state = switch_core_session_alloc(session, sizeof(*detector_state)); + detector_state->refs = 1; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Starting detector %s, refs = 1\n", detector->name); + switch_channel_set_private(switch_core_session_get_channel(session), detector->uuid, detector_state); + switch_core_session_execute_application_async(session, detector->start_app, zstr(detector->start_app_args) ? NULL : detector->start_app_args); + switch_core_session_rwunlock(session); + return 1; + } else { + *error_detail = "session gone"; + return 0; + } + } + *error_detail = "detector not supported"; + return 0; +} + +/** + * Stop detecting + * @param call_uuid call to stop detecting signal on + * @param signal_ns name of signal to stop detecting + */ +void rayo_cpa_detector_stop(const char *call_uuid, const char *signal_ns) +{ + struct rayo_cpa_detector *detector = switch_core_hash_find(globals.detectors, signal_ns); + if (detector) { + switch_core_session_t *session = switch_core_session_locate(call_uuid); + if (session) { + struct rayo_cpa_detector_state *detector_state = switch_channel_get_private(switch_core_session_get_channel(session), detector->uuid); + if (detector_state) { + detector_state->refs--; + if (detector_state->refs < 0) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Stop detector %s refs = %d\n", detector->name, detector_state->refs); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Stop detector %s refs = %d\n", detector->name, detector_state->refs); + } + if (detector_state->refs == 0) { + /* nobody interested in detector events- shut it down */ + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Stopping detector %s\n", detector->name); + switch_core_session_execute_application_async(session, detector->stop_app, zstr(detector->stop_app_args) ? NULL : detector->stop_app_args); + switch_channel_set_private(switch_core_session_get_channel(session), detector->uuid, NULL); + } + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Detector %s is already stopped\n", detector->name); + } + switch_core_session_rwunlock(session); + } + } +} + +/** + * Handle event from detector + */ +static void rayo_cpa_detector_event(switch_event_t *event) +{ + struct rayo_cpa_detector *detector = (struct rayo_cpa_detector *)event->bind_user_data; + if (detector) { + const char *signal_type = "rayo_default"; + if (!zstr(detector->signal_type_header)) { + signal_type = switch_event_get_header(event, detector->signal_type_header); + } + if (!zstr(signal_type)) { + signal_type = switch_core_hash_find(detector->signal_type_map, signal_type); + } + if (!zstr(signal_type)) { + switch_event_t *cpa_event; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got Rayo CPA event %s\n", signal_type); + if (switch_event_create_subclass(&cpa_event, SWITCH_EVENT_CUSTOM, "rayo::cpa") == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "detector-name", detector->name); + switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "detector-uuid", detector->uuid); + switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "signal-type", signal_type); + if (!zstr(detector->signal_value_header)) { + const char *value = switch_event_get_header(event, detector->signal_value_header); + if (!zstr(value)) { + switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "value", value); + } + } + if (!zstr(detector->signal_duration_header)) { + const char *duration = switch_event_get_header(event, detector->signal_duration_header); + if (!zstr(duration)) { + switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "duration", duration); + } + } + switch_event_fire(&cpa_event); + } + } else { + /* couldn't map event to Rayo signal-type */ + const char *event_name = switch_event_get_header(event, "Event-Name"); + const char *event_subclass = switch_event_get_header(event, "Event-Subclass"); + if (zstr(event_subclass)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Failed to find Rayo signal-type for event %s\n", event_name); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Failed to find Rayo signal-type for event %s %s\n", event_name, event_subclass); + } + } + } +} + +#define RAYO_CPA_DETECTOR_SYNTAX "rayo_cpa " +SWITCH_STANDARD_API(rayo_cpa_detector_api) +{ + char *cmd_dup = NULL; + char *argv[4] = { 0 }; + int argc = 0; + + if (zstr(cmd)) { + stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_CPA_DETECTOR_SYNTAX); + goto done; + } + + cmd_dup = strdup(cmd); + argc = switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0])); + + if (argc != 3) { + stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_CPA_DETECTOR_SYNTAX); + } else { + const char *err_reason = NULL; + if (!strcmp(argv[2], "stop")) { + rayo_cpa_detector_stop(argv[0], argv[1]); + stream->write_function(stream, "+OK\n"); + } else if (!strcmp(argv[2], "start")) { + if (!rayo_cpa_detector_start(argv[0], argv[1], &err_reason)) { + if (err_reason) { + stream->write_function(stream, "-ERR: %s\n", err_reason); + } else { + stream->write_function(stream, "-ERR\n"); + } + } else { + stream->write_function(stream, "+OK\n"); + } + } else { + stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_CPA_DETECTOR_SYNTAX); + } + } + +done: + switch_safe_free(cmd_dup); + + return SWITCH_STATUS_SUCCESS; +} + +/** + * Configure CPA + */ +static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_file) +{ + switch_xml_t cfg, xml, cpa_xml; + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_hash_t *bound_events; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Configuring CPA\n"); + if (!(xml = switch_xml_open_cfg(config_file, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", config_file); + return SWITCH_STATUS_TERM; + } + + switch_core_hash_init(&bound_events, pool); + + cpa_xml = switch_xml_child(cfg, "cpa"); + if (cpa_xml) { + switch_xml_t detector_xml; + + for (detector_xml = switch_xml_child(cpa_xml, "detector"); detector_xml; detector_xml = detector_xml->next) { + switch_xml_t start_xml, stop_xml, event_xml; + struct rayo_cpa_detector *detector; + char id[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 }; + const char *name = switch_xml_attr_soft(detector_xml, "name"); + if (zstr(name)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing name of CPA detector!\n"); + status = SWITCH_STATUS_TERM; + goto done; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CPA detector: %s\n", name); + detector = switch_core_alloc(pool, sizeof(*detector)); + switch_core_hash_init(&detector->signal_type_map, pool); + detector->name = switch_core_strdup(pool, name); + switch_uuid_str(id, sizeof(id)); + detector->uuid = switch_core_strdup(pool, id); + + start_xml = switch_xml_child(detector_xml, "start"); + if (start_xml) { + detector->start_app = switch_core_strdup(pool, switch_xml_attr_soft(start_xml, "application")); + detector->start_app_args = switch_core_strdup(pool, switch_xml_attr_soft(start_xml, "data")); + } + + stop_xml = switch_xml_child(detector_xml, "stop"); + if (stop_xml) { + detector->stop_app = switch_core_strdup(pool, switch_xml_attr_soft(stop_xml, "application")); + detector->stop_app_args = switch_core_strdup(pool, switch_xml_attr_soft(stop_xml, "data")); + } + + event_xml = switch_xml_child(detector_xml, "event"); + if (event_xml) { + int event_ok = 0; + switch_xml_t signal_type_xml; + const char *event_class = switch_xml_attr_soft(event_xml, "class"); + const char *event_subclass = switch_xml_attr_soft(event_xml, "subclass"); + switch_event_types_t event_type; + if (zstr(event_class)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing event class for CPA detector: %s\n", detector->name); + status = SWITCH_STATUS_TERM; + goto done; + } + + if (switch_name_event(event_class, &event_type) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event class %s for CPA detector: %s\n", event_class, detector->name); + status = SWITCH_STATUS_TERM; + goto done; + } + + /* bind detector to event if not already done... */ + { + struct rayo_cpa_detector *bound_detector; + const char *event_name = switch_core_sprintf(pool, "%s %s", event_class, event_subclass); + if (!(bound_detector = switch_core_hash_find(bound_events, event_name))) { + /* not yet bound */ + if (zstr(event_subclass)) { + event_subclass = NULL; + } + switch_event_bind("rayo_cpa_detector", event_type, event_subclass, rayo_cpa_detector_event, detector); + switch_core_hash_insert(bound_events, event_name, detector); /* mark as bound */ + } else if (bound_detector != detector) { + /* can't have multiple detectors generating the same event! */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s attempted to bind to event %s that is already bound by %s\n", detector->name, event_name, bound_detector->name); + status = SWITCH_STATUS_TERM; + goto done; + } + } + + /* configure native event -> rayo CPA event mapping */ + detector->signal_type_header = switch_core_strdup(pool, switch_xml_attr_soft(event_xml, "type-header")); + detector->signal_value_header = switch_core_strdup(pool, switch_xml_attr_soft(event_xml, "value-header")); + detector->signal_duration_header = switch_core_strdup(pool, switch_xml_attr_soft(event_xml, "duration-header")); + + /* configure native event type -> rayo CPA signal type mapping */ + for (signal_type_xml = switch_xml_child(event_xml, "signal-type"); signal_type_xml; signal_type_xml = signal_type_xml->next) { + const char *header_value = switch_core_strdup(pool, switch_xml_attr_soft(signal_type_xml, "header-value")); + const char *signal_type = switch_core_strdup(pool, switch_xml_attr_soft(signal_type_xml, "value")); + if (zstr(signal_type)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s missing signal-type value!\n", detector->name); + status = SWITCH_STATUS_TERM; + goto done; + } else { + /* add signal-type to detector mapping */ + const char *signal_type_ns = switch_core_sprintf(pool, "%s%s:%s", RAYO_CPA_BASE, signal_type, RAYO_VERSION); + event_ok = 1; + switch_core_hash_insert(globals.detectors, signal_type_ns, detector); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding CPA %s => %s\n", signal_type_ns, detector->name); + } + + /* map event value to signal-type */ + if (zstr(header_value)) { + switch_core_hash_insert(detector->signal_type_map, "rayo_default", signal_type); + } else { + switch_core_hash_insert(detector->signal_type_map, header_value, signal_type); + } + } + + if (!event_ok) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s is missing Rayo signal-type for event\n", detector->name); + status = SWITCH_STATUS_TERM; + goto done; + } + } + } + } + +done: + switch_core_hash_destroy(&bound_events); + switch_xml_free(xml); + + return status; +} + +/** + * Console auto-completion for signal types + */ +static switch_status_t rayo_cpa_detector_signal_types(const char *line, const char *cursor, switch_console_callback_match_t **matches) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + switch_hash_index_t *hi; + void *val; + const void *vvar; + switch_console_callback_match_t *my_matches = NULL; + + for (hi = switch_hash_first(NULL, globals.detectors); hi; hi = switch_hash_next(hi)) { + switch_hash_this(hi, &vvar, NULL, &val); + switch_console_push_match(&my_matches, (const char *) vvar); + } + + if (my_matches) { + *matches = my_matches; + status = SWITCH_STATUS_SUCCESS; + } + + return status; +} + +/** + * Load CPA signal detection features + * @param module_interface + * @param pool memory pool + * @param config_file + * @return SWITCH_STATUS_SUCCESS if successfully loaded + */ +switch_status_t rayo_cpa_detector_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) +{ + switch_api_interface_t *api_interface; + + SWITCH_ADD_API(api_interface, "rayo_cpa", "Query rayo status", rayo_cpa_detector_api, RAYO_CPA_DETECTOR_SYNTAX); + + switch_console_set_complete("add rayo_cpa ::console::list_uuid ::rayo_cpa::list_signal_types start"); + switch_console_set_complete("add rayo_cpa ::console::list_uuid ::rayo_cpa::list_signal_types stop"); + switch_console_add_complete_func("::rayo_cpa::list_signal_types", rayo_cpa_detector_signal_types); + + switch_core_hash_init(&globals.detectors, pool); + if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_TERM; + } + return SWITCH_STATUS_SUCCESS; +} + +/** + * Disable CPA signal detection features + */ +void rayo_cpa_detector_shutdown(void) +{ + switch_console_set_complete("del rayo_cpa"); + switch_console_del_complete_func("::rayo_cpa::list_signal_types"); + switch_core_hash_destroy(&globals.detectors); + switch_event_unbind_callback(rayo_cpa_detector_event); +} + + +/* 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 noet + */ diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h new file mode 100644 index 0000000000..335d1c3a1b --- /dev/null +++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h @@ -0,0 +1,68 @@ +/* + * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2014, Grasshopper + * + * 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 mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is Grasshopper + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Rienzo + * + * rayo_cpa_detector.h -- Rayo call progress analysis + * + */ +#ifndef RAYO_CPA_DETECTOR_H +#define RAYO_CPA_DETECTOR_H + +#include + +#include "mod_rayo.h" + +#define RAYO_CPA_BASE RAYO_BASE "cpa:" +#define RAYO_CPA_NS RAYO_CPA_BASE RAYO_VERSION + +#define RAYO_CPA_BEEP_NS RAYO_CPA_BASE "beep:" RAYO_VERSION +#define RAYO_CPA_DTMF_NS RAYO_CPA_BASE "dtmf:" RAYO_VERSION +#define RAYO_CPA_SPEECH_NS RAYO_CPA_BASE "speech:" RAYO_VERSION +#define RAYO_CPA_FAX_CED_NS RAYO_CPA_BASE "fax-ced:" RAYO_VERSION +#define RAYO_CPA_FAX_CNG_NS RAYO_CPA_BASE "fax-cng:" RAYO_VERSION +#define RAYO_CPA_RING_NS RAYO_CPA_BASE "ring:" RAYO_VERSION +#define RAYO_CPA_BUSY_NS RAYO_CPA_BASE "busy:" RAYO_VERSION +#define RAYO_CPA_CONGESTION_NS RAYO_CPA_BASE "congestion:" RAYO_VERSION +#define RAYO_CPA_SIT_NS RAYO_CPA_BASE "sit:" RAYO_VERSION +#define RAYO_CPA_MODEM_NS RAYO_CPA_BASE "modem:" RAYO_VERSION +#define RAYO_CPA_OFFHOOK_NS RAYO_CPA_BASE "offhook:" RAYO_VERSION + +extern switch_status_t rayo_cpa_detector_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); +extern void rayo_cpa_detector_shutdown(void); +extern int rayo_cpa_detector_start(const char *call_uuid, const char *signal_ns, const char **error_detail); +extern void rayo_cpa_detector_stop(const char *call_uuid, const char *signal_ns); + +#endif + + +/* 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 noet + */ diff --git a/src/mod/event_handlers/mod_rayo/rayo_input_component.c b/src/mod/event_handlers/mod_rayo/rayo_input_component.c index 8238deb48a..4ea0e5f600 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_input_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_input_component.c @@ -27,6 +27,7 @@ * */ #include "rayo_components.h" +#include "rayo_cpa_detector.h" #include "rayo_elements.h" #include "srgs.h" #include "nlsml.h" @@ -760,7 +761,7 @@ switch_status_t rayo_input_component_load(switch_loadable_module_interface_t **m rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "input", "set:"RAYO_INPUT_NS":start-timers", start_timers_call_input_component); switch_event_bind("rayo_input_component", SWITCH_EVENT_DETECTED_SPEECH, SWITCH_EVENT_SUBCLASS_ANY, on_detected_speech_event, NULL); - return SWITCH_STATUS_SUCCESS; + return rayo_cpa_detector_load(module_interface, pool, config_file); } /** @@ -771,6 +772,9 @@ switch_status_t rayo_input_component_shutdown(void) { srgs_parser_destroy(globals.parser); switch_event_unbind_callback(on_detected_speech_event); + + rayo_cpa_detector_shutdown(); + return SWITCH_STATUS_SUCCESS; } From 85423194f72130b1e4312622ebb98114be5f97fa Mon Sep 17 00:00:00 2001 From: Jeff Lenk Date: Sat, 4 Jan 2014 21:36:29 -0600 Subject: [PATCH 03/14] FS-5894 --resolve --- .../mod_nibblebill/conf/autoload_configs/nibblebill.conf.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod/applications/mod_nibblebill/conf/autoload_configs/nibblebill.conf.xml b/src/mod/applications/mod_nibblebill/conf/autoload_configs/nibblebill.conf.xml index 043c985482..e359049ab5 100644 --- a/src/mod/applications/mod_nibblebill/conf/autoload_configs/nibblebill.conf.xml +++ b/src/mod/applications/mod_nibblebill/conf/autoload_configs/nibblebill.conf.xml @@ -2,8 +2,8 @@ - - + + From 8db351858ba38dba56d1456f0c7fba43e9d8775b Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Sun, 5 Jan 2014 00:39:40 -0500 Subject: [PATCH 04/14] mod_rayo CPA implemented - still needs testing --- conf/rayo/autoload_configs/rayo.conf.xml | 3 + src/mod/event_handlers/mod_rayo/Makefile | 2 + .../conf/autoload_configs/rayo.conf.xml | 3 + src/mod/event_handlers/mod_rayo/mod_rayo.c | 12 +- src/mod/event_handlers/mod_rayo/mod_rayo.h | 10 +- .../mod_rayo/rayo_cpa_component.c | 401 ++++++++++++++++++ .../mod_rayo/rayo_cpa_component.h | 53 +++ .../mod_rayo/rayo_cpa_detector.c | 18 +- .../mod_rayo/rayo_cpa_detector.h | 15 - .../event_handlers/mod_rayo/rayo_elements.c | 2 +- .../mod_rayo/rayo_input_component.c | 21 +- 11 files changed, 508 insertions(+), 32 deletions(-) create mode 100644 src/mod/event_handlers/mod_rayo/rayo_cpa_component.c create mode 100644 src/mod/event_handlers/mod_rayo/rayo_cpa_component.h diff --git a/conf/rayo/autoload_configs/rayo.conf.xml b/conf/rayo/autoload_configs/rayo.conf.xml index 45a8ab04bb..52a1a35977 100644 --- a/conf/rayo/autoload_configs/rayo.conf.xml +++ b/conf/rayo/autoload_configs/rayo.conf.xml @@ -109,6 +109,9 @@ + ]]> + ]]> + ]]> ]]> ]]> ]]> diff --git a/src/mod/event_handlers/mod_rayo/Makefile b/src/mod/event_handlers/mod_rayo/Makefile index 3b7e5dba3f..070f41f98c 100644 --- a/src/mod/event_handlers/mod_rayo/Makefile +++ b/src/mod/event_handlers/mod_rayo/Makefile @@ -10,6 +10,7 @@ LOCAL_OBJS= $(IKS_LA) \ iks_helpers.o \ nlsml.o \ rayo_components.o \ + rayo_cpa_component.o \ rayo_cpa_detector.o \ rayo_elements.o \ rayo_fax_components.o \ @@ -24,6 +25,7 @@ LOCAL_SOURCES= \ iks_helpers.c \ nlsml.c \ rayo_components.c \ + rayo_cpa_component.c \ rayo_cpa_detector.c \ rayo_elements.c \ rayo_fax_components.c \ diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml index 45a8ab04bb..52a1a35977 100644 --- a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml +++ b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml @@ -109,6 +109,9 @@ + ]]> + ]]> + ]]> ]]> ]]> ]]> diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index c650e66877..95162093c8 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -1191,6 +1191,10 @@ static struct rayo_mixer *_rayo_mixer_create(const char *name, const char *file, */ static void rayo_component_cleanup(struct rayo_actor *actor) { + if (RAYO_COMPONENT(actor)->cleanup_fn) { + RAYO_COMPONENT(actor)->cleanup_fn(actor); + } + /* parent can now be destroyed */ RAYO_UNLOCK(RAYO_COMPONENT(actor)->parent); } @@ -1202,9 +1206,12 @@ static void rayo_component_cleanup(struct rayo_actor *actor) * @param id internal ID of this component * @param parent the parent that owns this component * @param client_jid the client that created this component + * @param cleanup optional cleanup function + * @param file file that called this function + * @param line line number that called this function * @return the component */ -struct rayo_component *_rayo_component_init(struct rayo_component *component, switch_memory_pool_t *pool, const char *type, const char *subtype, const char *id, struct rayo_actor *parent, const char *client_jid, const char *file, int line) +struct rayo_component *_rayo_component_init(struct rayo_component *component, switch_memory_pool_t *pool, const char *type, const char *subtype, const char *id, struct rayo_actor *parent, const char *client_jid, rayo_actor_cleanup_fn cleanup, const char *file, int line) { char *ref = switch_mprintf("%s-%d", subtype, rayo_actor_seq_next(parent)); char *jid = switch_mprintf("%s/%s", RAYO_JID(parent), ref); @@ -1218,6 +1225,7 @@ struct rayo_component *_rayo_component_init(struct rayo_component *component, sw component->client_jid = switch_core_strdup(pool, client_jid); component->ref = switch_core_strdup(pool, ref); component->parent = parent; + component->cleanup_fn = cleanup; switch_safe_free(ref); switch_safe_free(jid); @@ -2443,6 +2451,8 @@ static iks *on_iq_get_xmpp_disco(struct rayo_actor *server, struct rayo_message feature = iks_insert(x, "feature"); iks_insert_attrib(feature, "var", RAYO_NS); feature = iks_insert(x, "feature"); + iks_insert_attrib(feature, "var", RAYO_CPA_NS); + feature = iks_insert(x, "feature"); iks_insert_attrib(feature, "var", RAYO_FAX_NS); /* TODO The response MUST also include features for the application formats and transport methods supported by diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.h b/src/mod/event_handlers/mod_rayo/mod_rayo.h index b862802f31..9d0bf05490 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.h +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.h @@ -42,6 +42,9 @@ #define RAYO_CALL_NS RAYO_BASE "call:" RAYO_VERSION #define RAYO_MIXER_NS RAYO_BASE "mixer:" RAYO_VERSION +#define RAYO_CPA_BASE RAYO_BASE "cpa:" +#define RAYO_CPA_NS RAYO_CPA_BASE RAYO_VERSION + #define RAT_CALL "CALL" #define RAT_COMPONENT "COMPONENT" #define RAT_CALL_COMPONENT RAT_COMPONENT"_CALL" @@ -119,6 +122,8 @@ struct rayo_component { const char *client_jid; /** external ref */ const char *ref; + /** optional cleanup */ + rayo_actor_cleanup_fn cleanup_fn; }; #define RAYO_ACTOR(x) ((struct rayo_actor *)x) @@ -160,8 +165,9 @@ extern const char *rayo_call_get_dcp_jid(struct rayo_call *call); #define rayo_mixer_get_name(mixer) RAYO_ID(mixer) -#define rayo_component_init(component, pool, type, subtype, id, parent, client_jid) _rayo_component_init(component, pool, type, subtype, id, parent, client_jid, __FILE__, __LINE__) -extern struct rayo_component *_rayo_component_init(struct rayo_component *component, switch_memory_pool_t *pool, const char *type, const char *subtype, const char *id, struct rayo_actor *parent, const char *client_jid, const char *file, int line); +#define rayo_component_init(component, pool, type, subtype, id, parent, client_jid) _rayo_component_init(component, pool, type, subtype, id, parent, client_jid, NULL, __FILE__, __LINE__) +#define rayo_component_init_cleanup(component, pool, type, subtype, id, parent, client_jid, cleanup) _rayo_component_init(component, pool, type, subtype, id, parent, client_jid, cleanup, __FILE__, __LINE__) +extern struct rayo_component *_rayo_component_init(struct rayo_component *component, switch_memory_pool_t *pool, const char *type, const char *subtype, const char *id, struct rayo_actor *parent, const char *client_jid, rayo_actor_cleanup_fn cleanup, const char *file, int line); extern switch_bool_t is_component_actor(struct rayo_actor *); typedef iks *(*rayo_actor_xmpp_handler)(struct rayo_actor *, struct rayo_message *, void *); diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_component.c b/src/mod/event_handlers/mod_rayo/rayo_cpa_component.c new file mode 100644 index 0000000000..8004347118 --- /dev/null +++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_component.c @@ -0,0 +1,401 @@ +/* + * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2014, Grasshopper + * + * 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 mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is Grasshopper + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Rienzo + * + * rayo_cpa_component.c -- input component "cpa" mode implementation + */ +#include + +#include "rayo_cpa_component.h" +#include "mod_rayo.h" +#include "rayo_components.h" +#include "rayo_cpa_detector.h" + +/** + * Module globals + */ +struct { + /** signal subscribers */ + switch_hash_t *subscribers; + /** synchronizes access to subscribers */ + switch_mutex_t *subscribers_mutex; + /** module pool */ + switch_memory_pool_t *pool; +} globals; + +/** + * A CPA signal monitored by this component + */ +struct cpa_signal { + /** name of this signal */ + const char *name; + /** true if signal causes component termination */ + int terminate; +}; + +/** + * CPA component state + */ +struct cpa_component { + /** component base class */ + struct rayo_component base; + /** true if ready to forward detector events */ + int ready; + /** signals this component wants */ + switch_hash_t *signals; +}; + +#define CPA_COMPONENT(x) ((struct cpa_component *)x) + +typedef void (* subscriber_execute_fn)(const char *jid, void *user_data); + +/** + * Request signals + */ +static void subscribe(const char *uuid, const char *signal_type, const char *jid) +{ + char *key = switch_mprintf("%s:%s", uuid, signal_type); + switch_mutex_lock(globals.subscribers_mutex); + { + switch_hash_t *signal_subscribers = switch_core_hash_find(globals.subscribers, key); + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Subscribe %s => %s\n", signal_type, jid); + if (!signal_subscribers) { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Create %s subscriber hash\n", signal_type); + switch_core_hash_init(&signal_subscribers, NULL); + switch_core_hash_insert(globals.subscribers, key, signal_subscribers); + } + switch_core_hash_insert(signal_subscribers, jid, "1"); + } + switch_mutex_unlock(globals.subscribers_mutex); + switch_safe_free(key); +} + +/** + * Stop receiving signals + */ +static void unsubscribe(const char *uuid, const char *signal_type, const char *jid) +{ + char *key = switch_mprintf("%s:%s", uuid, signal_type); + switch_mutex_lock(globals.subscribers_mutex); + { + switch_hash_t *signal_subscribers = switch_core_hash_find(globals.subscribers, key); + if (signal_subscribers) { + switch_core_hash_delete(signal_subscribers, jid); + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Unsubscribe %s => %s\n", signal_type, jid); + + /* clean up hash if empty */ + if (!switch_core_hash_first(signal_subscribers)) { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Destroy %s subscriber hash\n", signal_type); + switch_core_hash_destroy(&signal_subscribers); + switch_core_hash_delete(globals.subscribers, key); + } + } + } + switch_mutex_unlock(globals.subscribers_mutex); + switch_safe_free(key); +} + +/** + * Execute function for each subscriber + */ +static void subscriber_execute(const char *uuid, const char *signal_type, subscriber_execute_fn callback, void *user_data) +{ + switch_event_t *subscriber_list = NULL; + switch_event_header_t *subscriber = NULL; + + /* fetch list of subscribers */ + char *key = switch_mprintf("%s:%s", uuid, signal_type); + switch_event_create_subclass(&subscriber_list, SWITCH_EVENT_CLONE, NULL); + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Subscriber execute %s\n", signal_type); + switch_mutex_lock(globals.subscribers_mutex); + { + switch_hash_index_t *hi = NULL; + switch_hash_t *signal_subscribers = switch_core_hash_find(globals.subscribers, key); + if (signal_subscribers) { + for (hi = switch_core_hash_first(signal_subscribers); hi; hi = switch_core_hash_next(hi)) { + const void *jid; + void *dont_care; + switch_core_hash_this(hi, &jid, NULL, &dont_care); + switch_event_add_header_string(subscriber_list, SWITCH_STACK_BOTTOM, "execute", (const char *)jid); + } + } else { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "No subscribers for %s\n", signal_type); + } + } + switch_mutex_unlock(globals.subscribers_mutex); + switch_safe_free(key); + + /* execute function for each subscriber */ + for (subscriber = subscriber_list->headers; subscriber; subscriber = subscriber->next) { + callback(subscriber->value, user_data); + } + + switch_event_destroy(&subscriber_list); +} + +/** + * Stop all CPA detectors + */ +static void stop_cpa_detectors(struct cpa_component *cpa) +{ + if (cpa->signals) { + switch_hash_index_t *hi = NULL; + for (hi = switch_core_hash_first(cpa->signals); hi; hi = switch_core_hash_next(hi)) { + const char *signal_type; + struct cpa_signal *cpa_signal = NULL; + switch_core_hash_this(hi, (const void **)&signal_type, NULL, (void **)&cpa_signal); + if (cpa_signal) { + rayo_cpa_detector_stop(RAYO_COMPONENT(cpa)->parent->id, cpa_signal->name); + unsubscribe(RAYO_COMPONENT(cpa)->parent->id, cpa_signal->name, RAYO_JID(cpa)); + } + } + switch_core_hash_destroy(&cpa->signals); + cpa->signals = NULL; + } + unsubscribe(RAYO_COMPONENT(cpa)->parent->id, "hangup", RAYO_JID(cpa)); +} + +/** + * Stop execution of CPA component + */ +static iks *stop_cpa_component(struct rayo_actor *component, struct rayo_message *msg, void *data) +{ + stop_cpa_detectors(CPA_COMPONENT(component)); + rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP); + return iks_new_iq_result(msg->payload); +} + +/** + * Forward CPA signal to client + */ +static void rayo_cpa_detector_event(const char *jid, void *user_data) +{ + struct rayo_actor *component = RAYO_LOCATE(jid); + if (component) { + if (CPA_COMPONENT(component)->ready) { + switch_event_t *event = (switch_event_t *)user_data; + const char *signal_type = switch_event_get_header(event, "signal-type"); + struct cpa_signal *cpa_signal = switch_core_hash_find(CPA_COMPONENT(component)->signals, signal_type); + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(RAYO_COMPONENT(component)->parent->id), SWITCH_LOG_DEBUG, "Handling CPA event\n"); + if (cpa_signal) { + const char *value = switch_event_get_header(event, "value"); + const char *duration = switch_event_get_header(event, "duration"); + if (cpa_signal->terminate) { + iks *complete_event; + iks *signal_xml; + + stop_cpa_detectors(CPA_COMPONENT(component)); + + /* send complete event to client */ + complete_event = rayo_component_create_complete_event(RAYO_COMPONENT(component), "signal", RAYO_CPA_NS); + signal_xml = iks_find(complete_event, "complete"); + signal_xml = iks_find(signal_xml, "signal"); + iks_insert_attrib(signal_xml, "type", signal_type); + if (!zstr(value)) { + iks_insert_attrib(signal_xml, "value", value); + } + if (!zstr(duration)) { + iks_insert_attrib(signal_xml, "duration", duration); + } + rayo_component_send_complete_event(RAYO_COMPONENT(component), complete_event); + } else { + /* send event to client */ + iks *signal_event = iks_new_presence("signal", RAYO_CPA_NS, RAYO_JID(component), RAYO_COMPONENT(component)->client_jid); + iks *signal_xml = iks_find(signal_event, "signal"); + iks_insert_attrib(signal_xml, "type", signal_type); + if (!zstr(value)) { + iks_insert_attrib(signal_xml, "value", value); + } + if (!zstr(duration)) { + iks_insert_attrib(signal_xml, "duration", duration); + } + RAYO_SEND_REPLY(component, RAYO_COMPONENT(component)->client_jid, signal_event); + } + } + } else { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(RAYO_COMPONENT(component)->parent->id), SWITCH_LOG_DEBUG, "Skipping CPA event\n"); + } + RAYO_UNLOCK(component); + } +} + +/** + * Handle CPA signal-type event + */ +static void on_rayo_cpa_detector_event(switch_event_t *event) +{ + subscriber_execute(switch_event_get_header(event, "Unique-ID"), switch_event_get_header(event, "signal-type"), rayo_cpa_detector_event, event); +} + +/** + * Handle CPA completion because of hangup + */ +static void rayo_cpa_component_hangup(const char *jid, void *user_data) +{ + struct rayo_actor *component = RAYO_LOCATE(jid); + if (component) { + stop_cpa_detectors(CPA_COMPONENT(component)); + rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_HANGUP); + RAYO_UNLOCK(component); + } +} + +/** + * Handle hungup call event + */ +static void on_channel_hangup_complete_event(switch_event_t *event) +{ + subscriber_execute(switch_event_get_header(event, "Unique-ID"), "hangup", rayo_cpa_component_hangup, event); +} + +/** + * Start CPA + */ +iks *rayo_cpa_component_start(struct rayo_actor *call, struct rayo_message *msg, void *session_data) +{ + iks *iq = msg->payload; + switch_core_session_t *session = (switch_core_session_t *)session_data; + iks *input = iks_find(iq, "input"); + switch_memory_pool_t *pool = NULL; + struct cpa_component *component = NULL; + int have_grammar = 0; + iks *grammar = NULL; + + /* create CPA component */ + switch_core_new_memory_pool(&pool); + component = switch_core_alloc(pool, sizeof(*component)); + rayo_component_init((struct rayo_component *)component, pool, RAT_CALL_COMPONENT, "cpa", NULL, call, iks_find_attrib(iq, "from")); + + switch_core_hash_init(&component->signals, pool); + + /* start CPA detectors */ + for (grammar = iks_find(input, "grammar"); grammar; grammar = iks_next_tag(grammar)) { + if (!strcmp("grammar", iks_name(grammar))) { + const char *error_str = ""; + const char *url = iks_find_attrib_soft(grammar, "url"); + char *url_dup; + char *url_params; + + if (zstr(url)) { + stop_cpa_detectors(component); + RAYO_UNLOCK(component); + RAYO_DESTROY(component); + return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Missing grammar URL"); + } + have_grammar = 1; + + url_dup = strdup(url); + if ((url_params = strchr(url_dup, '?'))) { + *url_params = '\0'; + url_params++; + } + + if (switch_core_hash_find(component->signals, url)) { + free(url_dup); + stop_cpa_detectors(component); + RAYO_UNLOCK(component); + RAYO_DESTROY(component); + return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Duplicate URL"); + } + + /* start detector */ + /* TODO return better reasons... */ + if (rayo_cpa_detector_start(switch_core_session_get_uuid(session), url_dup, &error_str)) { + struct cpa_signal *cpa_signal = switch_core_alloc(pool, sizeof(*cpa_signal)); + cpa_signal->terminate = !zstr(url_params) && strstr(url_params, "terminate=true"); + cpa_signal->name = switch_core_strdup(pool, url_dup); + switch_core_hash_insert(component->signals, cpa_signal->name, cpa_signal); + subscribe(switch_core_session_get_uuid(session), cpa_signal->name, RAYO_JID(component)); + } else { + free(url_dup); + stop_cpa_detectors(component); + RAYO_UNLOCK(component); + RAYO_DESTROY(component); + return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, error_str); + } + + free(url_dup); + } + } + + if (!have_grammar) { + stop_cpa_detectors(component); + RAYO_UNLOCK(component); + RAYO_DESTROY(component); + return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "No grammar defined"); + } + + /* acknowledge command */ + rayo_component_send_start(RAYO_COMPONENT(component), iq); + + /* TODO hangup race condition */ + subscribe(switch_core_session_get_uuid(session), "hangup", RAYO_JID(component)); + + /* ready to forward detector events */ + component->ready = 1; + + return NULL; +} + +/** + * Load input CPA + * @param module_interface + * @param pool memory pool + * @param config_file + * @return SWITCH_STATUS_SUCCESS if successfully loaded + */ +switch_status_t rayo_cpa_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) +{ + rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "cpa", "set:"RAYO_EXT_NS":stop", stop_cpa_component); + switch_event_bind("rayo_cpa_component", SWITCH_EVENT_CUSTOM, "rayo::cpa", on_rayo_cpa_detector_event, NULL); + switch_event_bind("rayo_cpa_component", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE, NULL, on_channel_hangup_complete_event, NULL); + + globals.pool = pool; + switch_core_hash_init(&globals.subscribers, pool); + switch_mutex_init(&globals.subscribers_mutex, SWITCH_MUTEX_NESTED, pool); + + return rayo_cpa_detector_load(module_interface, pool, config_file); +} + +/** + * Stop input CPA + */ +void rayo_cpa_component_shutdown(void) +{ + switch_event_unbind_callback(on_rayo_cpa_detector_event); + switch_event_unbind_callback(on_channel_hangup_complete_event); + rayo_cpa_detector_shutdown(); + switch_core_hash_destroy(&globals.subscribers); +} + +/* 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 noet + */ diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_component.h b/src/mod/event_handlers/mod_rayo/rayo_cpa_component.h new file mode 100644 index 0000000000..36354c9508 --- /dev/null +++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_component.h @@ -0,0 +1,53 @@ +/* + * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2014, Grasshopper + * + * 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 mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is Grasshopper + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Rienzo + * + * rayo_cpa_component.h -- Rayo call progress analysis component + * + */ +#ifndef RAYO_CPA_COMPONENT_H +#define RAYO_CPA_COMPONENT_H + +#include +#include + +#include "mod_rayo.h" + +extern switch_status_t rayo_cpa_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); +extern void rayo_cpa_component_shutdown(void); +extern iks *rayo_cpa_component_start(struct rayo_actor *call, struct rayo_message *msg, void *session_data); + +#endif + + +/* 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 noet + */ diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c index 9267e05456..27ce9a13be 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c +++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c @@ -155,21 +155,27 @@ static void rayo_cpa_detector_event(switch_event_t *event) } if (!zstr(signal_type)) { switch_event_t *cpa_event; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got Rayo CPA event %s\n", signal_type); + const char *uuid = switch_event_get_header(event, "Unique-ID"); + if (zstr(uuid)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s %s event is missing call UUID!\n", detector->name, signal_type); + return; + } + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Got Rayo CPA event %s\n", signal_type); if (switch_event_create_subclass(&cpa_event, SWITCH_EVENT_CUSTOM, "rayo::cpa") == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "detector-name", detector->name); - switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "detector-uuid", detector->uuid); - switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "signal-type", signal_type); + switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "Unique-ID", uuid); + switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "detector-name", detector->name); + switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "detector-uuid", detector->uuid); + switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "signal-type", "%s%s:%s", RAYO_CPA_BASE, signal_type, RAYO_VERSION); if (!zstr(detector->signal_value_header)) { const char *value = switch_event_get_header(event, detector->signal_value_header); if (!zstr(value)) { - switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "value", value); + switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "value", value); } } if (!zstr(detector->signal_duration_header)) { const char *duration = switch_event_get_header(event, detector->signal_duration_header); if (!zstr(duration)) { - switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "duration", duration); + switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "duration", duration); } } switch_event_fire(&cpa_event); diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h index 335d1c3a1b..fcdc35f2f4 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h +++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h @@ -33,21 +33,6 @@ #include "mod_rayo.h" -#define RAYO_CPA_BASE RAYO_BASE "cpa:" -#define RAYO_CPA_NS RAYO_CPA_BASE RAYO_VERSION - -#define RAYO_CPA_BEEP_NS RAYO_CPA_BASE "beep:" RAYO_VERSION -#define RAYO_CPA_DTMF_NS RAYO_CPA_BASE "dtmf:" RAYO_VERSION -#define RAYO_CPA_SPEECH_NS RAYO_CPA_BASE "speech:" RAYO_VERSION -#define RAYO_CPA_FAX_CED_NS RAYO_CPA_BASE "fax-ced:" RAYO_VERSION -#define RAYO_CPA_FAX_CNG_NS RAYO_CPA_BASE "fax-cng:" RAYO_VERSION -#define RAYO_CPA_RING_NS RAYO_CPA_BASE "ring:" RAYO_VERSION -#define RAYO_CPA_BUSY_NS RAYO_CPA_BASE "busy:" RAYO_VERSION -#define RAYO_CPA_CONGESTION_NS RAYO_CPA_BASE "congestion:" RAYO_VERSION -#define RAYO_CPA_SIT_NS RAYO_CPA_BASE "sit:" RAYO_VERSION -#define RAYO_CPA_MODEM_NS RAYO_CPA_BASE "modem:" RAYO_VERSION -#define RAYO_CPA_OFFHOOK_NS RAYO_CPA_BASE "offhook:" RAYO_VERSION - extern switch_status_t rayo_cpa_detector_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); extern void rayo_cpa_detector_shutdown(void); extern int rayo_cpa_detector_start(const char *call_uuid, const char *signal_ns, const char **error_detail); diff --git a/src/mod/event_handlers/mod_rayo/rayo_elements.c b/src/mod/event_handlers/mod_rayo/rayo_elements.c index 78209c3bd6..1720471e31 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_elements.c +++ b/src/mod/event_handlers/mod_rayo/rayo_elements.c @@ -33,7 +33,7 @@ */ ELEMENT(RAYO_INPUT) ATTRIB(xmlns,, any) - STRING_ATTRIB(mode, any, "any,dtmf,voice") + STRING_ATTRIB(mode, any, "any,dtmf,voice,cpa") OPTIONAL_ATTRIB(terminator,, dtmf_digit) ATTRIB(recognizer,, any) ATTRIB(language, en-US, any) diff --git a/src/mod/event_handlers/mod_rayo/rayo_input_component.c b/src/mod/event_handlers/mod_rayo/rayo_input_component.c index 4ea0e5f600..41383a1d23 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_input_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_input_component.c @@ -27,7 +27,7 @@ * */ #include "rayo_components.h" -#include "rayo_cpa_detector.h" +#include "rayo_cpa_component.h" #include "rayo_elements.h" #include "srgs.h" #include "nlsml.h" @@ -360,10 +360,14 @@ static iks *start_call_input(struct input_component *component, switch_core_sess component->speech_mode = strcmp(iks_find_attrib_soft(input, "mode"), "dtmf"); if (component->speech_mode && handler->voice_component) { /* don't allow multi voice input */ + RAYO_UNLOCK(component); + RAYO_DESTROY(component); return iks_new_error_detailed(iq, STANZA_ERROR_CONFLICT, "Multiple voice input is not allowed"); } if (!component->speech_mode && handler->dtmf_component) { /* don't allow multi dtmf input */ + RAYO_UNLOCK(component); + RAYO_DESTROY(component); return iks_new_error_detailed(iq, STANZA_ERROR_CONFLICT, "Multiple dtmf input is not allowed"); } @@ -538,17 +542,20 @@ static iks *start_call_input_component(struct rayo_actor *call, struct rayo_mess struct input_component *input_component = NULL; const char *error = NULL; + /* Start CPA */ + if (!strcmp(iks_find_attrib_soft(input, "mode"), "cpa")) { + return rayo_cpa_component_start(call, msg, session_data); + } + + /* start input */ if (!validate_call_input(input, &error)) { return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, error); } - /* create component */ switch_core_new_memory_pool(&pool); input_component = switch_core_alloc(pool, sizeof(*input_component)); rayo_component_init(RAYO_COMPONENT(input_component), pool, RAT_CALL_COMPONENT, "input", component_id, call, iks_find_attrib(iq, "from")); - - /* start input */ - return start_call_input(input_component, session, iks_find(iq, "input"), iq, NULL, 0); + return start_call_input(input_component, session, input, iq, NULL, 0); } /** @@ -761,7 +768,7 @@ switch_status_t rayo_input_component_load(switch_loadable_module_interface_t **m rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "input", "set:"RAYO_INPUT_NS":start-timers", start_timers_call_input_component); switch_event_bind("rayo_input_component", SWITCH_EVENT_DETECTED_SPEECH, SWITCH_EVENT_SUBCLASS_ANY, on_detected_speech_event, NULL); - return rayo_cpa_detector_load(module_interface, pool, config_file); + return rayo_cpa_component_load(module_interface, pool, config_file); } /** @@ -773,7 +780,7 @@ switch_status_t rayo_input_component_shutdown(void) srgs_parser_destroy(globals.parser); switch_event_unbind_callback(on_detected_speech_event); - rayo_cpa_detector_shutdown(); + rayo_cpa_component_shutdown(); return SWITCH_STATUS_SUCCESS; } From 8d30da28d7e4939ec6fd94222e7a4b43bd6b24ec Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Mon, 6 Jan 2014 09:50:28 -0500 Subject: [PATCH 05/14] mod_rayo FS-6071 fix DTMF overflow and increase max DTMF matching from 64 to 256 --- src/mod/event_handlers/mod_rayo/rayo_input_component.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/mod/event_handlers/mod_rayo/rayo_input_component.c b/src/mod/event_handlers/mod_rayo/rayo_input_component.c index 41383a1d23..72f52cb552 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_input_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_input_component.c @@ -32,7 +32,7 @@ #include "srgs.h" #include "nlsml.h" -#define MAX_DTMF 64 +#define MAX_DTMF 256 #define INPUT_MATCH_TAG "match" #define INPUT_MATCH INPUT_MATCH_TAG, RAYO_INPUT_COMPLETE_NS @@ -183,13 +183,18 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c match = srgs_grammar_match(component->grammar, component->digits, &interpretation); - /* adjust result if terminating digit was pressed */ if (is_term_digit) { + /* finalize result if terminating digit was pressed */ if (match == SMT_MATCH_PARTIAL) { match = SMT_NO_MATCH; } else if (match == SMT_MATCH) { match = SMT_MATCH_END; } + } else if (component->num_digits >= MAX_DTMF) { + /* maximum digits collected and still not a definitive match */ + if (match != SMT_MATCH_END) { + match = SMT_NO_MATCH; + } } switch (match) { From 9efe980438faed517a9256411345972861e819f5 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Mon, 6 Jan 2014 14:36:47 -0500 Subject: [PATCH 06/14] mod_rayo --resolve FS-6072 fixed prompt state machine --- .../mod_rayo/rayo_prompt_component.c | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c b/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c index 7bb530fe3f..3debe39c3e 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c @@ -261,17 +261,32 @@ static iks *prompt_component_handle_input_error(struct rayo_actor *prompt, struc switch (PROMPT_COMPONENT(prompt)->state) { case PCS_START_INPUT_TIMERS: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, error: %s\n", RAYO_JID(prompt), iks_string(iks_stack(iq), iq)); + PROMPT_COMPONENT(prompt)->state = PCS_DONE; + + /* forward IQ error to client */ + iq = PROMPT_COMPONENT(prompt)->iq; + iks_insert_attrib(iq, "from", RAYO_JID(RAYO_COMPONENT(prompt)->parent)); + iks_insert_attrib(iq, "to", RAYO_COMPONENT(prompt)->client_jid); + iks_insert_node(iq, iks_copy_within(error, iks_stack(iq))); + RAYO_SEND_REPLY(prompt, RAYO_COMPONENT(prompt)->client_jid, iq); + + /* done */ + RAYO_UNLOCK(prompt); + RAYO_DESTROY(prompt); + + break; + case PCS_START_INPUT: - /* send error to client */ - PROMPT_COMPONENT(prompt)-> state = PCS_DONE; + /* send presence error to client */ + PROMPT_COMPONENT(prompt)->state = PCS_DONE; iks_delete(PROMPT_COMPONENT(prompt)->iq); rayo_component_send_complete(RAYO_COMPONENT(prompt), COMPONENT_COMPLETE_ERROR); break; - break; case PCS_START_INPUT_OUTPUT: PROMPT_COMPONENT(prompt)->state = PCS_DONE_STOP_OUTPUT; - /* forward error to client */ + /* forward IQ error to client */ iq = PROMPT_COMPONENT(prompt)->iq; iks_insert_attrib(iq, "from", RAYO_JID(RAYO_COMPONENT(prompt)->parent)); iks_insert_attrib(iq, "to", RAYO_COMPONENT(prompt)->client_jid); @@ -312,7 +327,7 @@ static iks *prompt_component_handle_output_error(struct rayo_actor *prompt, stru switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, error: %s\n", RAYO_JID(prompt), iks_string(iks_stack(iq), iq)); PROMPT_COMPONENT(prompt)->state = PCS_DONE; - /* forward error to client */ + /* forward IQ error to client */ iq = PROMPT_COMPONENT(prompt)->iq; iks_insert_attrib(iq, "from", RAYO_JID(RAYO_COMPONENT(prompt)->parent)); iks_insert_attrib(iq, "to", RAYO_COMPONENT(prompt)->client_jid); From 3ff0aaa0befa3ff68aa0adeac8ab062d8f9ddcb5 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 7 Jan 2014 00:38:42 +0500 Subject: [PATCH 07/14] FS-5653 --resolve --- libs/esl/fs_cli.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/libs/esl/fs_cli.c b/libs/esl/fs_cli.c index 05286ecc49..968e647e33 100644 --- a/libs/esl/fs_cli.c +++ b/libs/esl/fs_cli.c @@ -85,6 +85,8 @@ static int running = 1; static int thread_running = 0; static char *filter_uuid; static char *logfilter; +static int timeout = 0; +static int connect_timeout = 0; #ifndef WIN32 static EditLine *el; static History *myhistory; @@ -603,6 +605,7 @@ static const char *usage_str = " -d, --debug=level Debug Level (0 - 7)\n" " -b, --batchmode Batch mode\n" " -t, --timeout Timeout for API commands (in miliseconds)\n" + " -T, --connect-timeout Timeout for socket connection (in miliseconds)\n" " -n, --no-color Disable color\n\n"; static int usage(char *name){ @@ -1249,6 +1252,10 @@ static void read_config(const char *dft_cfile, const char *cfile) { profiles[pcount-1].console_fnkeys[i - 1] = strdup(val); } } + } else if (!strcasecmp(var, "timeout")) { + timeout = atoi(val); + } else if (!strcasecmp(var, "connect-timeout")) { + connect_timeout = atoi(val); } } esl_config_close_file(&cfg); @@ -1301,6 +1308,7 @@ int main(int argc, char *argv[]) {"interrupt", 0, 0, 'i'}, {"reconnect", 0, 0, 'R'}, {"timeout", 1, 0, 't'}, + {"connect-timeout", 1, 0, 'T'}, {0, 0, 0, 0} }; char temp_host[128]; @@ -1319,7 +1327,7 @@ int main(int argc, char *argv[]) int argv_log_uuid = 0; int argv_quiet = 0; int argv_batch = 0; - int loops = 2, reconnect = 0, timeout = 0; + int loops = 2, reconnect = 0; char *ccheck; #ifdef WIN32 @@ -1354,7 +1362,7 @@ int main(int argc, char *argv[]) esl_global_set_default_logger(6); /* default debug level to 6 (info) */ for(;;) { int option_index = 0; - opt = getopt_long(argc, argv, "H:P:S:u:p:d:x:l:Ut:qrRhib?n", options, &option_index); + opt = getopt_long(argc, argv, "H:P:S:u:p:d:x:l:Ut:T:qrRhib?n", options, &option_index); if (opt == -1) break; switch (opt) { case 'H': @@ -1418,6 +1426,9 @@ int main(int argc, char *argv[]) case 't': timeout = atoi(optarg); break; + case 'T': + connect_timeout = atoi(optarg); + break; case 'h': case '?': print_banner(stdout, is_color); @@ -1488,7 +1499,7 @@ int main(int argc, char *argv[]) connected = 0; while (--loops > 0) { memset(&handle, 0, sizeof(handle)); - if (esl_connect(&handle, profile->host, profile->port, profile->user, profile->pass)) { + if (esl_connect_timeout(&handle, profile->host, profile->port, profile->user, profile->pass, connect_timeout)) { esl_global_set_default_logger(7); esl_log(ESL_LOG_ERROR, "Error Connecting [%s]\n", handle.err); if (loops == 1) { From 695ac57e4966e16dc5a6634d68133312c9ce8ee8 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 7 Jan 2014 00:47:56 +0500 Subject: [PATCH 08/14] FS-6032 --resolve --- .../applications/mod_commands/mod_commands.c | 35 ++++--------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 2b21821ccc..2767644559 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -1108,7 +1108,7 @@ SWITCH_STANDARD_API(in_group_function) SWITCH_STANDARD_API(user_data_function) { - switch_xml_t x_domain, xml = NULL, x_user = NULL, x_group = NULL, x_param, x_params; + switch_xml_t x_user = NULL, x_param, x_params; int argc; char *mydata = NULL, *argv[3], *key = NULL, *type = NULL, *user, *domain, *dup_domain = NULL; char delim = ' '; @@ -1143,7 +1143,7 @@ SWITCH_STANDARD_API(user_data_function) switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "domain", domain); switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "type", type); - if (key && type && switch_xml_locate_user("id", user, domain, NULL, &xml, &x_domain, &x_user, &x_group, params) == SWITCH_STATUS_SUCCESS) { + if (key && type && switch_xml_locate_user_merged("id:number-alias", user, domain, NULL, &x_user, params) == SWITCH_STATUS_SUCCESS) { if (!strcmp(type, "attr")) { const char *attr = switch_xml_attr_soft(x_user, key); result = attr; @@ -1155,29 +1155,6 @@ SWITCH_STANDARD_API(user_data_function) elem = "variable"; } - if ((x_params = switch_xml_child(x_domain, container))) { - for (x_param = switch_xml_child(x_params, elem); x_param; x_param = x_param->next) { - const char *var = switch_xml_attr(x_param, "name"); - const char *val = switch_xml_attr(x_param, "value"); - - if (var && val && !strcasecmp(var, key)) { - result = val; - } - - } - } - - if (x_group && (x_params = switch_xml_child(x_group, container))) { - for (x_param = switch_xml_child(x_params, elem); x_param; x_param = x_param->next) { - const char *var = switch_xml_attr(x_param, "name"); - const char *val = switch_xml_attr(x_param, "value"); - - if (var && val && !strcasecmp(var, key)) { - result = val; - } - } - } - if ((x_params = switch_xml_child(x_user, container))) { for (x_param = switch_xml_child(x_params, elem); x_param; x_param = x_param->next) { const char *var = switch_xml_attr(x_param, "name"); @@ -1194,7 +1171,7 @@ SWITCH_STANDARD_API(user_data_function) if (result) { stream->write_function(stream, "%s", result); } - switch_xml_free(xml); + switch_xml_free(x_user); switch_safe_free(mydata); switch_safe_free(dup_domain); switch_event_destroy(¶ms); @@ -1204,7 +1181,7 @@ SWITCH_STANDARD_API(user_data_function) static switch_status_t _find_user(const char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream, switch_bool_t tf) { - switch_xml_t x_domain = NULL, x_user = NULL, xml = NULL; + switch_xml_t x_user = NULL; int argc; char *mydata = NULL, *argv[3]; char *key, *user, *domain; @@ -1244,7 +1221,7 @@ static switch_status_t _find_user(const char *cmd, switch_core_session_t *sessio goto end; } - if (switch_xml_locate_user(key, user, domain, NULL, &xml, &x_domain, &x_user, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + if (switch_xml_locate_user_merged(key, user, domain, NULL, &x_user, NULL) != SWITCH_STATUS_SUCCESS) { err = "can't find user"; goto end; } @@ -1274,7 +1251,7 @@ static switch_status_t _find_user(const char *cmd, switch_core_session_t *sessio } } - switch_xml_free(xml); + switch_xml_free(x_user); switch_safe_free(mydata); return SWITCH_STATUS_SUCCESS; } From f8844d1398b60e49ae1b6543fd3532c71d4f4e74 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 7 Jan 2014 01:54:31 +0500 Subject: [PATCH 09/14] FS-6020 --resolve --- src/switch_channel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/switch_channel.c b/src/switch_channel.c index 21b9dedc6f..eb8be218d1 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -1971,6 +1971,7 @@ SWITCH_DECLARE(void) switch_channel_clear_flag(switch_channel_t *channel, switch } switch_mutex_unlock(channel->profile_mutex); + switch_channel_set_callstate(channel, CCS_ACTIVE); } if (flag == CF_ORIGINATOR && switch_channel_test_flag(channel, CF_ANSWERED) && switch_channel_up_nosig(channel)) { From 587ba2ed52cba1e6a51f6aed0b5d67cd4297ae73 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Mon, 6 Jan 2014 21:06:31 -0500 Subject: [PATCH 10/14] mod_rayo FS-6081 --resolve added mixer presence with entity capabilities --- src/mod/event_handlers/mod_rayo/iks_helpers.c | 23 +++ src/mod/event_handlers/mod_rayo/iks_helpers.h | 5 +- src/mod/event_handlers/mod_rayo/mod_rayo.c | 138 ++++++++++++++++-- 3 files changed, 155 insertions(+), 11 deletions(-) diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.c b/src/mod/event_handlers/mod_rayo/iks_helpers.c index 30d9d215a5..93be6db3a2 100644 --- a/src/mod/event_handlers/mod_rayo/iks_helpers.c +++ b/src/mod/event_handlers/mod_rayo/iks_helpers.c @@ -522,6 +522,29 @@ char *iks_server_dialback_key(const char *secret, const char *receiving_server, return NULL; } +/** + * Print base 64 encoded SHA-1 hash + * @param sha hash to print + * @param buf to store baes 64 encoded hash + */ +void iks_sha_print_base64(iksha *sha, char *buf) +{ + int i; + char hex_digit[3] = { 0 }; + char hex_buf[SHA_1_HASH_BUF_SIZE]; + unsigned char bin_buf[SHA_1_HASH_BUF_SIZE / 2]; + iks_sha_print(sha, hex_buf); + + /* convert hex string to octets */ + for (i = 0; i < SHA_1_HASH_BUF_SIZE; i += 2) { + hex_digit[0] = hex_buf[i]; + hex_digit[1] = hex_buf[i + 1]; + bin_buf[i / 2] = strtol(hex_digit, NULL, 16); + } + + switch_b64_encode(bin_buf, SHA_1_HASH_BUF_SIZE / 2, (unsigned char *)buf, SHA_1_HASH_BUF_SIZE); +} + /* For Emacs: * Local Variables: * mode:c diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.h b/src/mod/event_handlers/mod_rayo/iks_helpers.h index 3a7bae02e7..2017134713 100644 --- a/src/mod/event_handlers/mod_rayo/iks_helpers.h +++ b/src/mod/event_handlers/mod_rayo/iks_helpers.h @@ -32,6 +32,8 @@ #include #include +#define SHA_1_HASH_BUF_SIZE 40 + #define IKS_JABBER_SERVER_PORT 5269 #define IKS_NS_XMPP_DISCO "http://jabber.org/protocol/disco#info" @@ -40,6 +42,7 @@ #define IKS_NS_XMPP_STREAMS "http://etherx.jabber.org/streams" #define IKS_NS_XMPP_DIALBACK "jabber:server:dialback" #define IKS_NS_XMPP_TLS "urn:ietf:params:xml:ns:xmpp-tls" +#define IKS_NS_XMPP_ENTITY_CAPABILITIES "http://jabber.org/protocol/caps" struct xmpp_error { const char *name; @@ -53,7 +56,6 @@ struct xmpp_error { #include "xmpp_errors.def" /* See RFC-3920 XMPP core for error definitions */ - extern iks *iks_new_presence(const char *name, const char *namespace, const char *from, const char *to); extern iks *iks_new_error(iks *iq, const struct xmpp_error *err); extern iks *iks_new_error_detailed(iks *iq, const struct xmpp_error *err, const char *detail_text); @@ -70,6 +72,7 @@ extern const char *iks_net_error_to_string(int err); extern iks *iks_insert_attrib_printf(iks *xml, const char *name, const char *fmt, ...); extern char *iks_server_dialback_key(const char *secret, const char *receiving_server, const char *originating_server, const char *stream_id); +extern void iks_sha_print_base64(iksha *sha, char *buf); /** A function to validate attribute value */ typedef int (*iks_attrib_validation_function)(const char *); diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index 95162093c8..d88cc26af0 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -170,9 +170,9 @@ struct rayo_mixer_member { * A subscriber to mixer events */ struct rayo_mixer_subscriber { - /** JID of subscriber */ + /** JID of client */ const char *jid; - /** Number of controlled parties in mixer */ + /** Number of client's calls in mixer */ int ref_count; }; @@ -250,6 +250,54 @@ typedef switch_bool_t (* rayo_actor_match_fn)(struct rayo_actor *); static switch_bool_t is_call_actor(struct rayo_actor *actor); +/** + * Entity features returned by service discovery + */ +struct entity_identity { + /** identity category */ + const char *category; + /** identity type */ + const char *type; +}; + +static struct entity_identity rayo_server_identity = { "server", "im" }; +static const char *rayo_server_features[] = { IKS_NS_XMPP_ENTITY_CAPABILITIES, IKS_NS_XMPP_DISCO, RAYO_NS, RAYO_CPA_NS, RAYO_FAX_NS, 0 }; + +static struct entity_identity rayo_mixer_identity = { "client", "rayo_mixer" }; +static const char *rayo_mixer_features[] = { 0 }; + +static struct entity_identity rayo_call_identity = { "client", "rayo_call" }; +static const char *rayo_call_features[] = { 0 }; + +/** + * Calculate SHA-1 hash of entity capabilities + * @param identity of entity + * @param features of identity (NULL terminated) + * @return base64 hash (free when done) + */ +static char *calculate_entity_sha1_ver(struct entity_identity *identity, const char **features) +{ + int i; + const char *feature; + char ver[SHA_1_HASH_BUF_SIZE + 1] = { 0 }; + iksha *sha; + + sha = iks_sha_new(); + iks_sha_hash(sha, (const unsigned char *)identity->category, strlen(identity->category), 0); + iks_sha_hash(sha, (const unsigned char *)"/", 1, 0); + iks_sha_hash(sha, (const unsigned char *)identity->type, strlen(identity->type), 0); + iks_sha_hash(sha, (const unsigned char *)"//", 2, 0); + i = 0; + while ((feature = features[i++])) { + iks_sha_hash(sha, (const unsigned char *)"<", 1, 0); + iks_sha_hash(sha, (const unsigned char *)feature, strlen(feature), 0); + } + iks_sha_hash(sha, (const unsigned char *)"<", 1, 1); + iks_sha_print_base64(sha, ver); + iks_sha_delete(sha); + + return strdup(ver); +} /** * @param msg to check @@ -408,6 +456,32 @@ static void add_header(iks *node, const char *name, const char *value) } } +/** + * Send event to clients + * @param from event sender + * @param rayo_event the event to send + * @param online_only only send to online clients + */ +static void broadcast_event(struct rayo_actor *from, iks *rayo_event, int online_only) +{ + switch_hash_index_t *hi = NULL; + switch_mutex_lock(globals.clients_mutex); + for (hi = switch_hash_first(NULL, globals.clients_roster); hi; hi = switch_hash_next(hi)) { + struct rayo_client *rclient; + const void *key; + void *val; + switch_hash_this(hi, &key, NULL, &val); + rclient = (struct rayo_client *)val; + switch_assert(rclient); + + if (!online_only || rclient->availability == PS_ONLINE) { + iks_insert_attrib(rayo_event, "to", RAYO_JID(rclient)); + RAYO_SEND_MESSAGE_DUP(from, RAYO_JID(rclient), rayo_event); + } + } + switch_mutex_unlock(globals.clients_mutex); +} + /** * Add an outbound dialing gateway * @param uri_prefix to match @@ -2445,15 +2519,20 @@ static iks *on_iq_get_xmpp_disco(struct rayo_actor *server, struct rayo_message iks *response = NULL; iks *x; iks *feature; + iks *identity; + int i = 0; + const char *feature_string; response = iks_new_iq_result(node); x = iks_insert(response, "query"); iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_DISCO); - feature = iks_insert(x, "feature"); - iks_insert_attrib(feature, "var", RAYO_NS); - feature = iks_insert(x, "feature"); - iks_insert_attrib(feature, "var", RAYO_CPA_NS); - feature = iks_insert(x, "feature"); - iks_insert_attrib(feature, "var", RAYO_FAX_NS); + identity = iks_insert(x, "identity"); + iks_insert_attrib(identity, "category", rayo_server_identity.category); + iks_insert_attrib(identity, "type", rayo_server_identity.type); + i = 0; + while((feature_string = rayo_server_features[i++])) { + feature = iks_insert(x, "feature"); + iks_insert_attrib(feature, "var", feature_string); + } /* TODO The response MUST also include features for the application formats and transport methods supported by * the responding entity, as described in the relevant specifications. @@ -2669,6 +2748,15 @@ static void on_mixer_delete_member_event(struct rayo_mixer *mixer, switch_event_ static void on_mixer_destroy_event(struct rayo_mixer *mixer, switch_event_t *event) { if (mixer) { + iks *presence; + + /* notify online clients of mixer destruction */ + presence = iks_new("presence"); + iks_insert_attrib(presence, "from", RAYO_JID(mixer)); + iks_insert_attrib(presence, "type", "unavailable"); + broadcast_event(RAYO_ACTOR(mixer), presence, 1); + iks_delete(presence); + /* remove from hash and destroy */ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, destroying mixer: %s\n", RAYO_JID(mixer), rayo_mixer_get_name(mixer)); RAYO_UNLOCK(mixer); /* release original lock */ @@ -2688,10 +2776,25 @@ static void on_mixer_add_member_event(struct rayo_mixer *mixer, switch_event_t * struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(uuid); if (!mixer) { + char *ver; + iks *presence, *c; + /* new mixer */ const char *mixer_name = switch_event_get_header(event, "Conference-Name"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "creating mixer: %s\n", mixer_name); mixer = rayo_mixer_create(mixer_name); + + /* notify online clients of mixer presence */ + ver = calculate_entity_sha1_ver(&rayo_mixer_identity, rayo_mixer_features); + + presence = iks_new_presence("c", IKS_NS_XMPP_ENTITY_CAPABILITIES, RAYO_JID(mixer), ""); + c = iks_find(presence, "c"); + iks_insert_attrib(c, "hash", "sha-1"); + iks_insert_attrib(c, "node", RAYO_MIXER_NS); + iks_insert_attrib(c, "ver", ver); + free(ver); + + broadcast_event(RAYO_ACTOR(mixer), presence, 1); } if (call) { @@ -2719,6 +2822,9 @@ static void on_mixer_add_member_event(struct rayo_mixer *mixer, switch_event_t * if (call->pending_join_request) { iks *request = call->pending_join_request; iks *result = iks_new_iq_result(request); + iks *ref = iks_insert(result, "ref"); + iks_insert_attrib(ref, "xmlns", RAYO_NS); + iks_insert_attrib_printf(ref, "uri", "xmpp:%s", RAYO_JID(mixer)); call->pending_join_request = NULL; RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); iks_delete(request); @@ -3135,12 +3241,24 @@ static iks *rayo_create_offer(struct rayo_call *call, switch_core_session_t *ses switch_channel_t *channel = switch_core_session_get_channel(session); switch_caller_profile_t *profile = switch_channel_get_caller_profile(channel); iks *presence = iks_new("presence"); + iks *c = iks_insert(presence, "c"); iks *offer = iks_insert(presence, "offer"); const char *val; + char *ver; + /* */ iks_insert_attrib(presence, "from", RAYO_JID(call)); - iks_insert_attrib(offer, "xmlns", RAYO_NS); + /* */ + ver = calculate_entity_sha1_ver(&rayo_call_identity, rayo_call_features); + iks_insert_attrib(c, "xmlns", IKS_NS_XMPP_ENTITY_CAPABILITIES); + iks_insert_attrib(c, "hash", "sha-1"); + iks_insert_attrib(c, "node", RAYO_CALL_NS); + iks_insert_attrib(c, "ver", ver); + free(ver); + + /* */ + iks_insert_attrib(offer, "xmlns", RAYO_NS); if (globals.offer_uri && (val = switch_channel_get_variable(channel, "sip_from_uri"))) { /* is a SIP call - pass the URI */ if (!strchr(val, ':')) { @@ -3287,8 +3405,8 @@ SWITCH_STANDARD_APP(rayo_app) RAYO_SEND_MESSAGE_DUP(call, RAYO_JID(rclient), offer); } } - iks_delete(offer); switch_mutex_unlock(globals.clients_mutex); + iks_delete(offer); /* nobody to offer to */ if (!ok) { From 5b2799284b1c7dc1cec3ca33e780497a344bec4f Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Tue, 7 Jan 2014 10:10:50 -0500 Subject: [PATCH 11/14] mod_rayo CPA dtmf detection added --- conf/rayo/autoload_configs/rayo.conf.xml | 7 +++++++ .../mod_rayo/conf/autoload_configs/rayo.conf.xml | 7 +++++++ .../event_handlers/mod_rayo/rayo_cpa_detector.c | 14 ++++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/conf/rayo/autoload_configs/rayo.conf.xml b/conf/rayo/autoload_configs/rayo.conf.xml index 52a1a35977..50af0f7ecd 100644 --- a/conf/rayo/autoload_configs/rayo.conf.xml +++ b/conf/rayo/autoload_configs/rayo.conf.xml @@ -26,6 +26,13 @@ + + + + + + + diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml index 52a1a35977..50af0f7ecd 100644 --- a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml +++ b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml @@ -26,6 +26,13 @@ + + + + + + + diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c index 27ce9a13be..ed1d87d152 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c +++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c @@ -79,8 +79,13 @@ struct rayo_cpa_detector_state { int rayo_cpa_detector_start(const char *call_uuid, const char *signal_ns, const char **error_detail) { struct rayo_cpa_detector *detector = switch_core_hash_find(globals.detectors, signal_ns); + switch_core_session_t *session; if (detector) { - switch_core_session_t *session = switch_core_session_locate(call_uuid); + if (zstr(detector->start_app)) { + /* nothing to do */ + return 1; + } + session = switch_core_session_locate(call_uuid); if (session) { struct rayo_cpa_detector_state *detector_state = switch_channel_get_private(switch_core_session_get_channel(session), detector->uuid); if (detector_state) { @@ -114,8 +119,13 @@ int rayo_cpa_detector_start(const char *call_uuid, const char *signal_ns, const void rayo_cpa_detector_stop(const char *call_uuid, const char *signal_ns) { struct rayo_cpa_detector *detector = switch_core_hash_find(globals.detectors, signal_ns); + switch_core_session_t *session; if (detector) { - switch_core_session_t *session = switch_core_session_locate(call_uuid); + if (zstr(detector->stop_app)) { + /* nothing to do */ + return; + } + session = switch_core_session_locate(call_uuid); if (session) { struct rayo_cpa_detector_state *detector_state = switch_channel_get_private(switch_core_session_get_channel(session), detector->uuid); if (detector_state) { From 7e71d85550344a0955fc6d78d53b4af7be84b743 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Tue, 7 Jan 2014 14:07:49 -0500 Subject: [PATCH 12/14] FS-6083 --resolve SSML will pass through when renderer is specified --- .../mod_rayo/rayo_output_component.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/mod/event_handlers/mod_rayo/rayo_output_component.c b/src/mod/event_handlers/mod_rayo/rayo_output_component.c index e092ebd046..3432bcefbf 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_output_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_output_component.c @@ -47,6 +47,8 @@ struct output_component { switch_bool_t start_paused; /** true if stopped */ int stop; + /** output renderer to use */ + const char *renderer; }; #define OUTPUT_FINISH "finish", RAYO_OUTPUT_COMPLETE_NS @@ -71,6 +73,7 @@ static struct rayo_component *create_output_component(struct rayo_actor *actor, output_component->repeat_times = iks_find_int_attrib(output, "repeat-times"); output_component->max_time = iks_find_int_attrib(output, "max-time"); output_component->start_paused = iks_find_bool_attrib(output, "start-paused"); + output_component->renderer = iks_find_attrib(output, "renderer"); return (struct rayo_component *)output_component; } @@ -402,7 +405,13 @@ static switch_status_t next_file(switch_file_handle_t *handle) if (speak) { /* is child node */ char *ssml_str = iks_string(NULL, speak); - context->ssml = switch_mprintf("ssml://%s", ssml_str); + if (zstr(output->renderer)) { + /* FS must parse the SSML */ + context->ssml = switch_mprintf("ssml://%s", ssml_str); + } else { + /* renderer will parse the SSML */ + context->ssml = switch_mprintf("tts://%s||%s", output->renderer, ssml_str); + } iks_free(ssml_str); } else if (iks_has_children(context->cur_doc)) { /* check if is in CDATA */ @@ -415,7 +424,13 @@ static switch_status_t next_file(switch_file_handle_t *handle) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing CDATA\n"); return SWITCH_STATUS_FALSE; } - context->ssml = switch_mprintf("ssml://%s", ssml_str); + if (zstr(output->renderer)) { + /* FS must parse the SSML */ + context->ssml = switch_mprintf("ssml://%s", ssml_str); + } else { + /* renderer will parse the SSML */ + context->ssml = switch_mprintf("tts://%s||%s", output->renderer, ssml_str); + } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing \n"); return SWITCH_STATUS_FALSE; From d25d1293bcd930b9763c4961c78676b805c95d81 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Tue, 7 Jan 2014 14:50:14 -0500 Subject: [PATCH 13/14] FS-6083 --resolve mod_ssml fixed --- src/mod/formats/mod_ssml/mod_ssml.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/mod/formats/mod_ssml/mod_ssml.c b/src/mod/formats/mod_ssml/mod_ssml.c index 8b172721c7..ae2670a74c 100644 --- a/src/mod/formats/mod_ssml/mod_ssml.c +++ b/src/mod/formats/mod_ssml/mod_ssml.c @@ -259,17 +259,6 @@ static int process_tag(struct ssml_parser *parser, const char *name, char **atts return IKS_BADXML; } -/** - * Handle tag attributes that are ignored - * @param parser the parser - * @param atts the attributes - * @return IKS_OK - */ -static int process_attribs_ignore(struct ssml_parser *parser, char **atts) -{ - return IKS_OK; -} - /** * Handle CDATA that is ignored * @param parser the parser @@ -395,6 +384,19 @@ static struct voice *find_say_voice(struct ssml_node *cur_node) return find_voice(cur_node, globals.say_voice_map, "say", 1); } +/** + * Handle tag attributes that are ignored + * @param parser the parser + * @param atts the attributes + * @return IKS_OK + */ +static int process_attribs_ignore(struct ssml_parser *parsed_data, char **atts) +{ + struct ssml_node *cur_node = parsed_data->cur_node; + cur_node->tts_voice = find_tts_voice(cur_node); + return IKS_OK; +} + /** * open next file for reading * @param handle the file handle From af0c6308f561767cbb0ca2ee4259daf2b0c26cdb Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 8 Jan 2014 04:12:01 +0500 Subject: [PATCH 14/14] allow native recording and transcoding at the same time --- src/switch_core_io.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/switch_core_io.c b/src/switch_core_io.c index d571da41b0..c07a428e3c 100644 --- a/src/switch_core_io.c +++ b/src/switch_core_io.c @@ -360,6 +360,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi if (session->read_codec->implementation->impl_id != codec_impl.impl_id) { need_codec = TRUE; + tap_only = 0; } if (codec_impl.actual_samples_per_second != session->read_impl.actual_samples_per_second) {