Merge branch 'master' into v1.4.beta

This commit is contained in:
Ken Rice
2014-01-08 13:53:13 -06:00
22 changed files with 1359 additions and 73 deletions
+63
View File
@@ -22,6 +22,66 @@
<param name="receivefax-file-prefix" value="/tmp/"/>
</fax>
<!-- Call progress analysis configuration -->
<!-- Define CPA detectors. These are sources for Rayo CPA signal events -->
<!-- Detectors must fire an event that can be translated by mod_rayo into a CPA signal event -->
<cpa>
<!-- map DTMF events to CPA -->
<detector name="core_dtmf_event">
<event class="DTMF" value-header="DTMF-Digit" duration-header="DTMF-Duration">
<signal-type value="dtmf"/>
</event>
</detector>
<!-- map mod_spandsp fax detector to the Rayo CPA events. Fires DETECTED_FAX_* event once and quits. -->
<detector name="mod_spandsp_fax_ced">
<start application="spandsp_start_fax_detect" data="event 'Event-Name=CUSTOM,Event-Subclass=DETECTED_FAX_CED' 500 ced"/>
<stop application="spandsp_stop_fax_detect" data=""/>
<event class="CUSTOM" subclass="DETECTED_FAX_CED">
<signal-type value="fax-ced"/>
</event>
</detector>
<detector name="mod_spandsp_fax_cng">
<start application="spandsp_start_fax_detect" data="event 'Event-Name=CUSTOM,Event-Subclass=DETECTED_FAX_CNG' 500"/>
<stop application="spandsp_stop_fax_detect" data=""/>
<event class="CUSTOM" subclass="DETECTED_FAX_CNG">
<signal-type value="fax-cng"/>
</event>
</detector>
<!-- map mod_spandsp call progress tone detector to Rayo CPA signal events. Fires DETECTED_TONE events until stopped. -->
<detector name="mod_spandsp_tone">
<start application="start_tone_detect" data="1"/>
<stop application="stop_tone_detect" data=""/>
<!-- map tone events to Rayo CPA signal type -->
<event class="CUSTOM" subclass="DETECTED_TONE" type-header="Detected-Tone">
<signal-type header-value="SIT" value="sit"/>
<signal-type header-value="BUSY_TONE" value="busy"/>
<signal-type header-value="REORDER_TONE" value="congestion"/>
<signal-type header-value="RING_TONE" value="ring"/>
</event>
</detector>
<!-- map mod_avmd detector to Rayo CPA beep event. Fires avmd::beep event once. -->
<detector name="mod_avmd">
<start application="avmd" data=""/>
<stop application="avmd" data="stop"/>
<event class="CUSTOM" subclass="avmd::beep">
<signal-type value="beep"/>
</event>
</detector>
<!-- Alternative beep detector using mod_vmd. Fires vmd::beep events until stopped. -->
<!--detector name="mod_vmd">
<start application="vmd" data=""/>
<stop application="vmd" data="stop"/>
<event class="CUSTOM" subclass="vmd::beep">
<signal-type value="beep"/>
</event>
</detector-->
</cpa>
<!-- XMPP server domain -->
<domain name="$${rayo_domain_name}" shared-secret="ClueCon">
<!-- use this instead if you want secure XMPP client to server connections. Put .crt and .key file in freeswitch/certs -->
@@ -56,6 +116,9 @@
<!-- IQ request aliases. Used mainly for testing purposes or for controlling a rayo call via the console -->
<aliases>
<alias name="detect" target="call" args="1"><![CDATA[<input xmlns="urn:xmpp:rayo:input:1" mode="cpa"><grammar url="urn:xmpp:rayo:cpa:$1:1"/></input>]]></alias>
<alias name="detect-once" target="call" args="1"><![CDATA[<input xmlns="urn:xmpp:rayo:input:1" mode="cpa"><grammar url="urn:xmpp:rayo:cpa:$1:1?terminate=true"/></input>]]></alias>
<alias name="detect-tones" target="call"><![CDATA[<input xmlns="urn:xmpp:rayo:input:1" mode="cpa"><grammar url="urn:xmpp:rayo:cpa:busy:1"/><grammar url="urn:xmpp:rayo:cpa:congestion:1"/><grammar url="urn:xmpp:rayo:cpa:sit:1"/></input>]]></alias>
<alias name="ping" target="external"><![CDATA[<iq type="get"><ping xmlns="urn:xmpp:ping"/></iq>]]></alias>
<alias name="dial" target="server" args="2"><![CDATA[<dial xmlns="urn:xmpp:rayo:1" from="$1" to="$2"/>]]></alias>
<alias name="answer" target="call"><![CDATA[<answer xmlns="urn:xmpp:rayo:1"/>]]></alias>
+1 -1
View File
@@ -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}")
.
+14 -3
View File
@@ -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) {
@@ -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(&params);
@@ -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;
}
@@ -2,8 +2,8 @@
<settings>
<!-- See http://wiki.freeswitch.org/wiki/Mod_nibblebill for help with these options -->
<!-- Information for connecting to your database -->
<param name="odbc-dsn" value="bandwidth.com"/>
<!-- Information for connecting to your database. Use the format "database:username:password" -->
<param name="odbc-dsn" value="bandwidth.com:user:password"/>
<!-- The database table where your CASH column is located -->
<param name="db_table" value="accounts"/>
+4
View File
@@ -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 \
@@ -22,6 +22,66 @@
<param name="receivefax-file-prefix" value="/tmp/"/>
</fax>
<!-- Call progress analysis configuration -->
<!-- Define CPA detectors. These are sources for Rayo CPA signal events -->
<!-- Detectors must fire an event that can be translated by mod_rayo into a CPA signal event -->
<cpa>
<!-- map DTMF events to CPA -->
<detector name="core_dtmf_event">
<event class="DTMF" value-header="DTMF-Digit" duration-header="DTMF-Duration">
<signal-type value="dtmf"/>
</event>
</detector>
<!-- map mod_spandsp fax detector to the Rayo CPA events. Fires DETECTED_FAX_* event once and quits. -->
<detector name="mod_spandsp_fax_ced">
<start application="spandsp_start_fax_detect" data="event 'Event-Name=CUSTOM,Event-Subclass=DETECTED_FAX_CED' 500 ced"/>
<stop application="spandsp_stop_fax_detect" data=""/>
<event class="CUSTOM" subclass="DETECTED_FAX_CED">
<signal-type value="fax-ced"/>
</event>
</detector>
<detector name="mod_spandsp_fax_cng">
<start application="spandsp_start_fax_detect" data="event 'Event-Name=CUSTOM,Event-Subclass=DETECTED_FAX_CNG' 500"/>
<stop application="spandsp_stop_fax_detect" data=""/>
<event class="CUSTOM" subclass="DETECTED_FAX_CNG">
<signal-type value="fax-cng"/>
</event>
</detector>
<!-- map mod_spandsp call progress tone detector to Rayo CPA signal events. Fires DETECTED_TONE events until stopped. -->
<detector name="mod_spandsp_tone">
<start application="start_tone_detect" data="1"/>
<stop application="stop_tone_detect" data=""/>
<!-- map tone events to Rayo CPA signal type -->
<event class="CUSTOM" subclass="DETECTED_TONE" type-header="Detected-Tone">
<signal-type header-value="SIT" value="sit"/>
<signal-type header-value="BUSY_TONE" value="busy"/>
<signal-type header-value="REORDER_TONE" value="congestion"/>
<signal-type header-value="RING_TONE" value="ring"/>
</event>
</detector>
<!-- map mod_avmd detector to Rayo CPA beep event. Fires avmd::beep event once. -->
<detector name="mod_avmd">
<start application="avmd" data=""/>
<stop application="avmd" data="stop"/>
<event class="CUSTOM" subclass="avmd::beep">
<signal-type value="beep"/>
</event>
</detector>
<!-- Alternative beep detector using mod_vmd. Fires vmd::beep events until stopped. -->
<!--detector name="mod_vmd">
<start application="vmd" data=""/>
<stop application="vmd" data="stop"/>
<event class="CUSTOM" subclass="vmd::beep">
<signal-type value="beep"/>
</event>
</detector-->
</cpa>
<!-- XMPP server domain -->
<domain name="$${rayo_domain_name}" shared-secret="ClueCon">
<!-- use this instead if you want secure XMPP client to server connections. Put .crt and .key file in freeswitch/certs -->
@@ -56,6 +116,9 @@
<!-- IQ request aliases. Used mainly for testing purposes or for controlling a rayo call via the console -->
<aliases>
<alias name="detect" target="call" args="1"><![CDATA[<input xmlns="urn:xmpp:rayo:input:1" mode="cpa"><grammar url="urn:xmpp:rayo:cpa:$1:1"/></input>]]></alias>
<alias name="detect-once" target="call" args="1"><![CDATA[<input xmlns="urn:xmpp:rayo:input:1" mode="cpa"><grammar url="urn:xmpp:rayo:cpa:$1:1?terminate=true"/></input>]]></alias>
<alias name="detect-tones" target="call"><![CDATA[<input xmlns="urn:xmpp:rayo:input:1" mode="cpa"><grammar url="urn:xmpp:rayo:cpa:busy:1"/><grammar url="urn:xmpp:rayo:cpa:congestion:1"/><grammar url="urn:xmpp:rayo:cpa:sit:1"/></input>]]></alias>
<alias name="ping" target="external"><![CDATA[<iq type="get"><ping xmlns="urn:xmpp:ping"/></iq>]]></alias>
<alias name="dial" target="server" args="2"><![CDATA[<dial xmlns="urn:xmpp:rayo:1" from="$1" to="$2"/>]]></alias>
<alias name="answer" target="call"><![CDATA[<answer xmlns="urn:xmpp:rayo:1"/>]]></alias>
@@ -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
@@ -32,6 +32,8 @@
#include <iksemel.h>
#include <switch.h>
#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 *);
+137 -9
View File
@@ -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;
/* <presence> */
iks_insert_attrib(presence, "from", RAYO_JID(call));
iks_insert_attrib(offer, "xmlns", RAYO_NS);
/* <c> */
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);
/* <offer> */
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) {
+8 -2
View File
@@ -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 *);
@@ -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 <chris.rienzo@grasshopper.com>
*
* rayo_cpa_component.c -- input component "cpa" mode implementation
*/
#include <switch.h>
#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
*/
@@ -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 <chris.rienzo@grasshopper.com>
*
* rayo_cpa_component.h -- Rayo call progress analysis component
*
*/
#ifndef RAYO_CPA_COMPONENT_H
#define RAYO_CPA_COMPONENT_H
#include <switch.h>
#include <iksemel.h>
#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
*/
@@ -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 <chris.rienzo@grasshopper.com>
*
* 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 <uuid> <signal-type> <start|stop>"
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
*/
@@ -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 <chris.rienzo@grasshopper.com>
*
* rayo_cpa_detector.h -- Rayo call progress analysis
*
*/
#ifndef RAYO_CPA_DETECTOR_H
#define RAYO_CPA_DETECTOR_H
#include <switch.h>
#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
*/
@@ -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)
@@ -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;
}
@@ -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) {
/* <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 <speak> 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 <document> 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 <speak>\n");
return SWITCH_STATUS_FALSE;
@@ -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, <input> 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, <output> 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);
+13 -11
View File
@@ -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
+1
View File
@@ -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)) {
+1
View File
@@ -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) {