mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-07-23 04:31:50 +00:00
Merge branch 'master' into v1.4.beta
This commit is contained in:
@@ -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>
|
||||
|
||||
Vendored
+1
-1
@@ -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
@@ -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(¶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;
|
||||
}
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user