diff --git a/conf/rayo/autoload_configs/rayo.conf.xml b/conf/rayo/autoload_configs/rayo.conf.xml
index 9a8900dbfa..50af0f7ecd 100644
--- a/conf/rayo/autoload_configs/rayo.conf.xml
+++ b/conf/rayo/autoload_configs/rayo.conf.xml
@@ -22,6 +22,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -56,6 +116,9 @@
+ ]]>
+ ]]>
+ ]]>
]]>
]]>
]]>
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}")
.
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) {
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;
}
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 @@
-
-
+
+
diff --git a/src/mod/event_handlers/mod_rayo/Makefile b/src/mod/event_handlers/mod_rayo/Makefile
index a3bfc45d47..070f41f98c 100644
--- a/src/mod/event_handlers/mod_rayo/Makefile
+++ b/src/mod/event_handlers/mod_rayo/Makefile
@@ -10,6 +10,8 @@ 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 \
rayo_input_component.o \
@@ -23,6 +25,8 @@ 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 \
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..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
@@ -22,6 +22,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -56,6 +116,9 @@
+ ]]>
+ ]]>
+ ]]>
]]>
]]>
]]>
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 c650e66877..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
@@ -1191,6 +1265,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 +1280,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 +1299,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);
@@ -2437,13 +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_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.
@@ -2659,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 */
@@ -2678,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) {
@@ -2709,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);
@@ -3125,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, ':')) {
@@ -3277,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) {
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
new file mode 100644
index 0000000000..ed1d87d152
--- /dev/null
+++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c
@@ -0,0 +1,451 @@
+/*
+ * 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);
+ switch_core_session_t *session;
+ if (detector) {
+ 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) {
+ /* 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);
+ switch_core_session_t *session;
+ if (detector) {
+ 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) {
+ 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;
+ 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_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_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_string(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..fcdc35f2f4
--- /dev/null
+++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.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_detector.h -- Rayo call progress analysis
+ *
+ */
+#ifndef RAYO_CPA_DETECTOR_H
+#define RAYO_CPA_DETECTOR_H
+
+#include
+
+#include "mod_rayo.h"
+
+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_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 8238deb48a..72f52cb552 100644
--- a/src/mod/event_handlers/mod_rayo/rayo_input_component.c
+++ b/src/mod/event_handlers/mod_rayo/rayo_input_component.c
@@ -27,11 +27,12 @@
*
*/
#include "rayo_components.h"
+#include "rayo_cpa_component.h"
#include "rayo_elements.h"
#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
@@ -182,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) {
@@ -359,10 +365,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");
}
@@ -537,17 +547,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);
}
/**
@@ -760,7 +773,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_component_load(module_interface, pool, config_file);
}
/**
@@ -771,6 +784,9 @@ switch_status_t rayo_input_component_shutdown(void)
{
srgs_parser_destroy(globals.parser);
switch_event_unbind_callback(on_detected_speech_event);
+
+ rayo_cpa_component_shutdown();
+
return SWITCH_STATUS_SUCCESS;
}
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;
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,