Merge branch 'v1.2.stable' of ssh://git.freeswitch.org:222/freeswitch into v1.2.stable

This commit is contained in:
Brian West 2013-08-21 11:20:14 -05:00
commit f4b71b1082
35 changed files with 1755 additions and 442 deletions

View File

@ -11,6 +11,11 @@
<param name="record-file-prefix" value="$${recordings_dir}/"/> <param name="record-file-prefix" value="$${recordings_dir}/"/>
</record> </record>
<!-- input component params -->
<input>
<param name="default-recognizer" value="pocketsphinx"/>
</input>
<!-- XMPP server domain --> <!-- XMPP server domain -->
<domain name="$${rayo_domain_name}" shared-secret="ClueCon"> <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 --> <!-- use this instead if you want secure XMPP client to server connections. Put .crt and .key file in freeswitch/certs -->
@ -42,4 +47,211 @@
<dial-gateway uriprefix="sofia" dialprefix="" strip=""/> <dial-gateway uriprefix="sofia" dialprefix="" strip=""/>
</dial-gateways> </dial-gateways>
<!-- IQ request aliases. Used mainly for testing purposes or for controlling a rayo call via the console -->
<aliases>
<alias name="ping" target="external"><![CDATA[<iq type="get"><ping xmlns="urn:xmpp:ping"/></iq>]]></alias>
<alias name="answer" target="call"><![CDATA[<answer xmlns="urn:xmpp:rayo:1"/>]]></alias>
<alias name="hangup" target="call"><![CDATA[<hangup xmlns="urn:xmpp:rayo:1"/>]]></alias>
<alias name="join_mixer_duplex" target="call"><![CDATA[<join xmlns="urn:xmpp:rayo:1" mixer-name="test" direction="duplex"/>]]></alias>
<alias name="join_mixer_send" target="call"><![CDATA[<join xmlns="urn:xmpp:rayo:1" mixer-name="test" direction="send"/>]]></alias>
<alias name="join_mixer_recv" target="call"><![CDATA[<join xmlns="urn:xmpp:rayo:1" mixer-name="test" direction="recv"/>]]></alias>
<alias name="unjoin_mixer" target="call"><![CDATA[<unjoin xmlns="urn:xmpp:rayo:1" mixer-name="test"/>]]></alias>
<alias name="unjoin" target="call"><![CDATA[<unjoin xmlns="urn:xmpp:rayo:1"/>]]></alias>
<alias name="stop" target="component"><![CDATA[<stop xmlns="urn:xmpp:rayo:ext:1"/>]]></alias>
<alias name="output_bad" target="call"><![CDATA[<output xmlns="urn:xmpp:rayo:output:1" repeat-time="100"></output>]]></alias>
<alias name="pause" target="output"><![CDATA[<pause xmlns="urn:xmpp:rayo:output:1"/>]]></alias>
<alias name="resume" target="output"><![CDATA[<resume xmlns="urn:xmpp:rayo:output:1"/>]]></alias>
<alias name="speed-up" target="output"><![CDATA[<speed-up xmlns="urn:xmpp:rayo:output:1"/>]]></alias>
<alias name="speed-down" target="output"><![CDATA[<speed-down xmlns="urn:xmpp:rayo:output:1"/>]]></alias>
<alias name="volume-up" target="output"><![CDATA[<volume-up xmlns="urn:xmpp:rayo:output:1"/>]]></alias>
<alias name="volume-down" target="output"><![CDATA[<volume-down xmlns="urn:xmpp:rayo:output:1"/>]]></alias>
<alias name="record" target="call"><![CDATA[<record xmlns="urn:xmpp:rayo:record:1"/>]]></alias>
<alias name="record_pause" target="record"><![CDATA[<pause xmlns="urn:xmpp:rayo:record:1"/>]]></alias>
<alias name="record_resume" target="record"><![CDATA[<resume xmlns="urn:xmpp:rayo:record:1"/>]]></alias>
<alias name="prompt_barge" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="true">
<output xmlns="urn:xmpp:rayo:output:1" repeat-times="5">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="prompt_no_barge" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="false">
<output xmlns="urn:xmpp:rayo:output:1" repeat-times="5">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="prompt_long" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="true">
<output xmlns="urn:xmpp:rayo:output:1" repeat-times="100">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="prompt_multi_digit" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="true">
<output xmlns="urn:xmpp:rayo:output:1" repeat-times="100">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><item repeat="1-4"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="prompt_terminator" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="true">
<output xmlns="urn:xmpp:rayo:output:1" repeat-times="100">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000" terminator="#">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><item repeat="1-4"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="prompt_input_bad" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="true">
<output xmlns="urn:xmpp:rayo:output:1" repeat-times="100">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtf" initial-timeout="5000" inter-digit-timeout="3000" terminator="#">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><item repeat="1-4"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="prompt_output_bad" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="true">
<output xmlns="urn:xmpp:rayo:output:1" repeat-time="100">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000" terminator="#">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><item repeat="1-4"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="input" target="call">
<![CDATA[
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
]]>
</alias>
<alias name="input_voice_yesno_unimrcp" target="call">
<![CDATA[
<input xmlns="urn:xmpp:rayo:input:1" mode="voice" recognizer="unimrcp">
<grammar content-type="application/srgs+xml>
<![CDATA[
<grammar xmlns="http://www.w3.org/2001/06/grammar"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/06/grammar http://www.w3.org/TR/speech-grammar/grammar.xsd"
xml:lang="en-US" version="1.0">
<rule id="yesno"><one-of><item>yes</item><item>no</item></one-of></rule></grammar>
]]]]><![CDATA[>
</grammar>
</input>
]]>
</alias>
<alias name="input_voice_yesno_unimrcp_timeout" target="call">
<![CDATA[
<input xmlns="urn:xmpp:rayo:input:1" mode="voice" recognizer="unimrcp" max-silence="5000" initial-timeout="5000">
<grammar content-type="application/srgs+xml>
<![CDATA[
<grammar xmlns="http://www.w3.org/2001/06/grammar"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/06/grammar http://www.w3.org/TR/speech-grammar/grammar.xsd"
xml:lang="en-US" version="1.0">
<rule id="yesno"><one-of><item>yes</item><item>no</item></one-of></rule></grammar>
]]]]><![CDATA[>
</grammar>
</input>
]]>
</alias>
<alias name="input_voice_yesno_pocketsphinx" target="call">
<![CDATA[
<input xmlns="urn:xmpp:rayo:input:1" mode="voice" recognizer="pocketsphinx" max-silence="5000" initial-timeout="5000">
<grammar content-type="application/srgs+xml>
<![CDATA[
<grammar xmlns="http://www.w3.org/2001/06/grammar"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/06/grammar http://www.w3.org/TR/speech-grammar/grammar.xsd"
xml:lang="en-US" version="1.0">
<rule id="yesno"><one-of><item>yes</item><item>no</item></one-of></rule></grammar>
]]]]><![CDATA[>
</grammar>
</input>
]]>
</alias>
<alias name="input_voice_yesno_default" target="call">
<![CDATA[
<input xmlns="urn:xmpp:rayo:input:1" mode="voice" max-silence="5000" initial-timeout="5000">
<grammar content-type="application/srgs+xml>
<![CDATA[
<grammar xmlns="http://www.w3.org/2001/06/grammar"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/06/grammar http://www.w3.org/TR/speech-grammar/grammar.xsd"
xml:lang="en-US" version="1.0">
<rule id="yesno"><one-of><item>yes</item><item>no</item></one-of></rule></grammar>
]]]]><![CDATA[>
</grammar>
</input>
]]>
</alias>
</aliases>
</configuration> </configuration>

View File

@ -0,0 +1,26 @@
<include>
<!-- Vestec VASRE MRCP Server -->
<profile name="vestec-mrcp-v1" version="1">
<param name="server-ip" value="127.0.0.1"/>
<param name="server-port" value="1554"/>
<param name="resource-location" value=""/>
<param name="speechsynth" value="speechsynthesizer"/>
<param name="speechrecog" value="speechrecognizer"/>
<param name="rtp-ip" value="auto"/>
<param name="rtp-port-min" value="14000"/>
<param name="rtp-port-max" value="15000"/>
<!--param name="playout-delay" value="50"/-->
<!--param name="max-playout-delay" value="200"/-->
<!--param name="ptime" value="20"/-->
<param name="codecs" value="PCMU PCMA L16/96/8000"/>
<!-- Add any default MRCP params for SPEAK requests here -->
<synthparams>
</synthparams>
<!-- Add any default MRCP params for RECOGNIZE requests here -->
<recogparams>
<!--param name="start-input-timers" value="false"/-->
</recogparams>
</profile>
</include>

View File

@ -2241,7 +2241,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal)
{ {
switch_core_session_t *session = NULL; switch_core_session_t *session = NULL;
switch_channel_t *channel = NULL; switch_channel_t *channel = NULL;
ftdm_status_t status; ftdm_status_t status = FTDM_SUCCESS;
uint32_t spanid; uint32_t spanid;
uint32_t chanid; uint32_t chanid;
ftdm_caller_data_t *caller_data; ftdm_caller_data_t *caller_data;
@ -2296,6 +2296,45 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal)
break; break;
case FTDM_SIGEVENT_SIGSTATUS_CHANGED: case FTDM_SIGEVENT_SIGSTATUS_CHANGED:
case FTDM_SIGEVENT_COLLECTED_DIGIT: /* Analog E&M */ case FTDM_SIGEVENT_COLLECTED_DIGIT: /* Analog E&M */
{
int span_id = ftdm_channel_get_span_id(sigmsg->channel);
char *dtmf = sigmsg->ev_data.collected.digits;
char *regex = SPAN_CONFIG[span_id].dial_regex;
char *fail_regex = SPAN_CONFIG[span_id].fail_dial_regex;
ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(sigmsg->channel);
if (zstr(regex)) {
regex = NULL;
}
if (zstr(fail_regex)) {
fail_regex = NULL;
}
ftdm_log(FTDM_LOG_DEBUG, "got DTMF sig [%s]\n", dtmf);
switch_set_string(caller_data->collected, dtmf);
if ((regex || fail_regex) && !zstr(dtmf)) {
switch_regex_t *re = NULL;
int ovector[30];
int match = 0;
if (fail_regex) {
match = switch_regex_perform(dtmf, fail_regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0]));
status = match ? FTDM_SUCCESS : FTDM_BREAK;
switch_regex_safe_free(re);
ftdm_log(FTDM_LOG_DEBUG, "DTMF [%s] vs fail regex %s %s\n", dtmf, fail_regex, match ? "matched" : "did not match");
}
if (status == FTDM_SUCCESS && regex) {
match = switch_regex_perform(dtmf, regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0]));
status = match ? FTDM_BREAK : FTDM_SUCCESS;
switch_regex_safe_free(re);
ftdm_log(FTDM_LOG_DEBUG, "DTMF [%s] vs dial regex %s %s\n", dtmf, regex, match ? "matched" : "did not match");
}
ftdm_log(FTDM_LOG_DEBUG, "returning %s to COLLECT event with DTMF %s\n", status == FTDM_SUCCESS ? "success" : "break", dtmf);
}
}
break; break;
default: default:
{ {
@ -2305,7 +2344,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal)
break; break;
} }
return FTDM_SUCCESS; return status;
} }
static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal) static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal)
@ -3785,7 +3824,10 @@ static switch_status_t load_config(void)
char *hold_music = NULL; char *hold_music = NULL;
char *fail_dial_regex = NULL; char *fail_dial_regex = NULL;
char str_false[] = "false"; char str_false[] = "false";
char str_empty[] = "";
char *answer_supervision = str_false; char *answer_supervision = str_false;
char *ringback_during_collect = str_false;
char *ringback_file = str_empty;
uint32_t span_id = 0, to = 0, max = 0, dial_timeout_int = 0; uint32_t span_id = 0, to = 0, max = 0, dial_timeout_int = 0;
ftdm_span_t *span = NULL; ftdm_span_t *span = NULL;
analog_option_t analog_options = ANALOG_OPTION_NONE; analog_option_t analog_options = ANALOG_OPTION_NONE;
@ -3814,6 +3856,10 @@ static switch_status_t load_config(void)
max_digits = val; max_digits = val;
} else if (!strcasecmp(var, "answer-supervision")) { } else if (!strcasecmp(var, "answer-supervision")) {
answer_supervision = val; answer_supervision = val;
} else if (!strcasecmp(var, "ringback-during-collect")) {
ringback_during_collect = val;
} else if (!strcasecmp(var, "ringback-file")) {
ringback_file = val;
} else if (!strcasecmp(var, "enable-analog-option")) { } else if (!strcasecmp(var, "enable-analog-option")) {
analog_options = enable_analog_option(val, analog_options); analog_options = enable_analog_option(val, analog_options);
} }
@ -3867,6 +3913,8 @@ static switch_status_t load_config(void)
if (ftdm_configure_span(span, "analog_em", on_analog_signal, if (ftdm_configure_span(span, "analog_em", on_analog_signal,
"tonemap", tonegroup, "tonemap", tonegroup,
"answer_supervision", answer_supervision, "answer_supervision", answer_supervision,
"ringback_during_collect", ringback_during_collect,
"ringback_file", ringback_file,
"digit_timeout", &to, "digit_timeout", &to,
"dial_timeout", &dial_timeout_int, "dial_timeout", &dial_timeout_int,
"max_dialstr", &max, "max_dialstr", &max,

View File

@ -6072,6 +6072,7 @@ static void execute_safety_hangup(void *data)
FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t *sigmsg) FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t *sigmsg)
{ {
ftdm_channel_t *fchan = NULL; ftdm_channel_t *fchan = NULL;
ftdm_status_t status = FTDM_SUCCESS;
if (sigmsg->channel) { if (sigmsg->channel) {
fchan = sigmsg->channel; fchan = sigmsg->channel;
ftdm_channel_lock(fchan); ftdm_channel_lock(fchan);
@ -6176,7 +6177,7 @@ FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t
if (ftdm_test_flag(span, FTDM_SPAN_USE_SIGNALS_QUEUE)) { if (ftdm_test_flag(span, FTDM_SPAN_USE_SIGNALS_QUEUE)) {
ftdm_span_queue_signal(span, sigmsg); ftdm_span_queue_signal(span, sigmsg);
} else { } else {
ftdm_span_trigger_signal(span, sigmsg); status = ftdm_span_trigger_signal(span, sigmsg);
} }
done: done:
@ -6185,7 +6186,7 @@ done:
ftdm_channel_unlock(fchan); ftdm_channel_unlock(fchan);
} }
return FTDM_SUCCESS; return status;
} }
static void *ftdm_cpu_monitor_run(ftdm_thread_t *me, void *obj) static void *ftdm_cpu_monitor_run(ftdm_thread_t *me, void *obj)

View File

@ -33,6 +33,7 @@
* Contributor(s): * Contributor(s):
* *
* John Wehle (john@feith.com) * John Wehle (john@feith.com)
* Moises Silva (moy@sangoma.com)
* *
*/ */
@ -53,6 +54,8 @@ struct ftdm_analog_data {
uint32_t digit_timeout; uint32_t digit_timeout;
uint32_t dial_timeout; uint32_t dial_timeout;
ftdm_bool_t answer_supervision; ftdm_bool_t answer_supervision;
ftdm_bool_t ringback_during_collect;
char ringback_file[512];
}; };
static void *ftdm_analog_em_run(ftdm_thread_t *me, void *obj); static void *ftdm_analog_em_run(ftdm_thread_t *me, void *obj);

View File

@ -33,6 +33,7 @@
* Contributor(s): * Contributor(s):
* *
* John Wehle (john@feith.com) * John Wehle (john@feith.com)
* Moises Silva (moy@sangoma.com)
* *
*/ */
@ -43,6 +44,106 @@
struct tm * localtime_r(const time_t *clock, struct tm *result); struct tm * localtime_r(const time_t *clock, struct tm *result);
#endif #endif
/* check if the given file is a wave file and skip the header if it is */
#define WAVE_CHUNK_ID "RIFF"
#define WAVE_FMT "WAVEfmt "
#define WAVE_HEADER_LEN 44
static int skip_wave_header(const char *fname, FILE *f)
{
char rbuff[10] = { 0 };
unsigned int hz = 0;
unsigned int hs = 0;
unsigned short fmt = 0;
unsigned short chans = 0;
unsigned int size = 0;
/* check chunk id */
if (fread(rbuff, 1, 4, f) != 4) {
ftdm_log(FTDM_LOG_ERROR, "Unable to read wav chunk id from file %s\n", fname);
goto error;
}
rbuff[4] = 0;
if (strncasecmp(rbuff, WAVE_CHUNK_ID, sizeof(WAVE_CHUNK_ID)-1)) {
goto notwave;
}
/* read chunk size */
if (fread(&size, 1, 4, f) != 4) {
ftdm_log(FTDM_LOG_ERROR, "Unable to read wav chunk size from file %s\n", fname);
goto error;
}
/* check format and sub chunk id */
if (fread(rbuff, 1, 8, f) != 8) {
ftdm_log(FTDM_LOG_ERROR, "Unable to read wav format and sub chunk id from file %s\n", fname);
goto error;
}
rbuff[8] = 0;
if (strncasecmp(rbuff, WAVE_FMT, sizeof(WAVE_FMT)-1)) {
goto notwave;
}
/* At this point we know is a wav file ... */
/* validate sub chunk size */
if (fread(&hs, 1, 4, f) != 4) {
ftdm_log(FTDM_LOG_ERROR, "Unable to read wav sub chunk size from file %s\n", fname);
goto error;
}
if (hs != 16) {
ftdm_log(FTDM_LOG_ERROR, "Unsupported wav sub chunk size %d from file %s\n", hs, fname);
goto error;
}
/* validate audio format */
if (fread(&fmt, 1, 2, f) != 2) {
ftdm_log(FTDM_LOG_ERROR, "Unable to read wav audio format from file %s\n", fname);
goto error;
}
if (fmt != 1) {
ftdm_log(FTDM_LOG_ERROR, "Unsupported wav audio format %d in file %s, we only support PCM\n", fmt, fname);
goto error;
}
/* validate channels */
if (fread(&chans, 1, 2, f) != 2) {
ftdm_log(FTDM_LOG_ERROR, "Unable to read wav channels from file %s\n", fname);
goto error;
}
if (chans != 1) {
ftdm_log(FTDM_LOG_ERROR, "Unsupported number of channels %d in file %s, we only support 1 (mono)\n", chans, fname);
goto error;
}
/* validate sampling rate */
if (fread(&hz, 1, 2, f) != 2) {
ftdm_log(FTDM_LOG_ERROR, "Unable to read wav sampling rate from file %s\n", fname);
goto error;
}
if (hz != 8000) {
ftdm_log(FTDM_LOG_ERROR, "Invalid input wav sampling rate %dHz, only 8000Hz supported\n", hz);
goto error;
}
ftdm_log(FTDM_LOG_DEBUG, "Found input file %s. PCM mono wav of %d bytes at %dHz, skipping header ...\n", fname, size, hz);
fseek(f, WAVE_HEADER_LEN, SEEK_SET);
return 0;
notwave:
ftdm_log(FTDM_LOG_ERROR, "File %s is not a wav file\n", fname);
return -1;
error:
return -1;
}
static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj); static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj);
/** /**
@ -82,6 +183,19 @@ static ftdm_status_t ftdm_analog_em_start(ftdm_span_t *span)
return ftdm_thread_create_detached(ftdm_analog_em_run, span); return ftdm_thread_create_detached(ftdm_analog_em_run, span);
} }
/**
* \brief Stops EM span thread (monitor)
* \param span Span to monitor
* \return Success or failure
*/
static ftdm_status_t ftdm_analog_em_stop(ftdm_span_t *span)
{
ftdm_analog_em_data_t *analog_data = span->signal_data;
ftdm_clear_flag(analog_data, FTDM_ANALOG_EM_RUNNING);
ftdm_sleep(100);
return FTDM_SUCCESS;
}
/** /**
* \brief Returns the signalling status on a channel * \brief Returns the signalling status on a channel
* \param ftdmchan Channel to get status on * \param ftdmchan Channel to get status on
@ -118,8 +232,10 @@ static FIO_SPAN_GET_SIG_STATUS_FUNCTION(analog_em_get_span_sig_status)
static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span) static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span)
//ftdm_status_t ftdm_analog_em_configure_span(ftdm_span_t *span, char *tonemap, uint32_t digit_timeout, uint32_t max_dialstr, fio_signal_cb_t sig_cb) //ftdm_status_t ftdm_analog_em_configure_span(ftdm_span_t *span, char *tonemap, uint32_t digit_timeout, uint32_t max_dialstr, fio_signal_cb_t sig_cb)
{ {
ftdm_analog_em_data_t *analog_data; ftdm_analog_em_data_t *analog_data = NULL;
const char *tonemap = "us"; const char *tonemap = "us";
const char *ringback_file = "";
ftdm_bool_t ringback_during_collect = FTDM_FALSE;
uint32_t digit_timeout = 2000; uint32_t digit_timeout = 2000;
uint32_t max_dialstr = 11; uint32_t max_dialstr = 11;
uint32_t dial_timeout = 0; uint32_t dial_timeout = 0;
@ -134,9 +250,8 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span)
return FTDM_FAIL; return FTDM_FAIL;
} }
analog_data = ftdm_malloc(sizeof(*analog_data)); analog_data = ftdm_calloc(1, sizeof(*analog_data));
assert(analog_data != NULL); assert(analog_data != NULL);
memset(analog_data, 0, sizeof(*analog_data));
while((var = va_arg(ap, char *))) { while((var = va_arg(ap, char *))) {
ftdm_log(FTDM_LOG_DEBUG, "Parsing analog em parameter '%s'\n", var); ftdm_log(FTDM_LOG_DEBUG, "Parsing analog em parameter '%s'\n", var);
@ -145,6 +260,16 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span)
break; break;
} }
tonemap = val; tonemap = val;
} else if (!strcasecmp(var, "ringback_during_collect")) {
if (!(val = va_arg(ap, char *))) {
break;
}
ringback_during_collect = ftdm_true(val);
} else if (!strcasecmp(var, "ringback_file")) {
if (!(val = va_arg(ap, char *))) {
break;
}
ringback_file = val;
} else if (!strcasecmp(var, "answer_supervision")) { } else if (!strcasecmp(var, "answer_supervision")) {
if (!(val = va_arg(ap, char *))) { if (!(val = va_arg(ap, char *))) {
break; break;
@ -182,6 +307,7 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span)
} }
span->start = ftdm_analog_em_start; span->start = ftdm_analog_em_start;
span->stop = ftdm_analog_em_stop;
analog_data->digit_timeout = digit_timeout; analog_data->digit_timeout = digit_timeout;
analog_data->max_dialstr = max_dialstr; analog_data->max_dialstr = max_dialstr;
analog_data->dial_timeout = dial_timeout; analog_data->dial_timeout = dial_timeout;
@ -193,6 +319,10 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span)
span->get_channel_sig_status = analog_em_get_channel_sig_status; span->get_channel_sig_status = analog_em_get_channel_sig_status;
span->get_span_sig_status = analog_em_get_span_sig_status; span->get_span_sig_status = analog_em_get_span_sig_status;
ftdm_span_load_tones(span, tonemap); ftdm_span_load_tones(span, tonemap);
if (ringback_during_collect) {
analog_data->ringback_during_collect = FTDM_TRUE;
ftdm_set_string(analog_data->ringback_file, ringback_file);
}
return FTDM_SUCCESS; return FTDM_SUCCESS;
@ -239,6 +369,7 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
int cas_bits = 0; int cas_bits = 0;
uint32_t cas_answer = 0; uint32_t cas_answer = 0;
int cas_answer_ms = 500; int cas_answer_ms = 500;
FILE *ringback_f = NULL;
ftdm_bool_t digits_sent = FTDM_FALSE; ftdm_bool_t digits_sent = FTDM_FALSE;
ftdm_log(FTDM_LOG_DEBUG, "ANALOG EM CHANNEL thread starting.\n"); ftdm_log(FTDM_LOG_DEBUG, "ANALOG EM CHANNEL thread starting.\n");
@ -280,6 +411,18 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
assert(interval != 0); assert(interval != 0);
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "IO Interval: %u\n", interval); ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "IO Interval: %u\n", interval);
if (analog_data->ringback_during_collect && !ftdm_strlen_zero(analog_data->ringback_file)) {
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Using ringback file '%s'\n", analog_data->ringback_file);
ringback_f = fopen(analog_data->ringback_file, "rb");
if (!ringback_f) {
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failed to open ringback file '%s'\n", analog_data->ringback_file);
} else {
if (skip_wave_header(analog_data->ringback_file, ringback_f)) {
ringback_f = NULL;
}
}
}
while (ftdm_running() && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) { while (ftdm_running() && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INTHREAD)) {
ftdm_wait_flag_t flags = FTDM_READ; ftdm_wait_flag_t flags = FTDM_READ;
ftdm_size_t dlen = 0; ftdm_size_t dlen = 0;
@ -512,7 +655,6 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
} }
} }
if (last_digit && (!collecting || ((elapsed - last_digit > analog_data->digit_timeout) || strlen(dtmf) > analog_data->max_dialstr))) { if (last_digit && (!collecting || ((elapsed - last_digit > analog_data->digit_timeout) || strlen(dtmf) > analog_data->max_dialstr))) {
ftdm_log(FTDM_LOG_DEBUG, "Number obtained [%s]\n", dtmf); ftdm_log(FTDM_LOG_DEBUG, "Number obtained [%s]\n", dtmf);
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RING); ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RING);
@ -574,6 +716,16 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
continue; continue;
} }
if (analog_data->ringback_during_collect && ringback_f &&
(ftdmchan->state == FTDM_CHANNEL_STATE_COLLECT ||
ftdmchan->state == FTDM_CHANNEL_STATE_RING ||
ftdmchan->state == FTDM_CHANNEL_STATE_RINGING ||
ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS ||
ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA
)) {
indicate = 1;
}
if (!indicate) { if (!indicate) {
continue; continue;
} }
@ -582,7 +734,25 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
len *= 2; len *= 2;
} }
if (ringback_f) {
uint8_t failed_read = 0;
read_try:
rlen = fread(frame, 1, len, ringback_f);
if (rlen != len) {
if (!feof(ringback_f)) {
ftdm_log(FTDM_LOG_ERROR, "Error reading from ringback file: %zd != %zd\n", rlen, len);
}
if (failed_read) {
continue;
}
/* return cursor to start of wav file */
fseek(ringback_f, WAVE_HEADER_LEN, SEEK_SET);
failed_read++;
goto read_try;
}
} else {
rlen = ftdm_buffer_read_loop(dt_buffer, frame, len); rlen = ftdm_buffer_read_loop(dt_buffer, frame, len);
}
if (ftdmchan->effective_codec != FTDM_CODEC_SLIN) { if (ftdmchan->effective_codec != FTDM_CODEC_SLIN) {
fio_codec_t codec_func = NULL; fio_codec_t codec_func = NULL;
@ -596,7 +766,7 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
if (codec_func) { if (codec_func) {
codec_func(frame, sizeof(frame), &rlen); codec_func(frame, sizeof(frame), &rlen);
} else { } else {
snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "codec error!"); ftdm_log(FTDM_LOG_ERROR, "codec error, no codec function for native codec %d!", ftdmchan->native_codec);
goto done; goto done;
} }
} }
@ -621,6 +791,10 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
ftdm_buffer_destroy(&dt_buffer); ftdm_buffer_destroy(&dt_buffer);
} }
if (ringback_f) {
fclose(ringback_f);
}
ftdm_clear_flag(closed_chan, FTDM_CHANNEL_INTHREAD); ftdm_clear_flag(closed_chan, FTDM_CHANNEL_INTHREAD);
ftdm_log(FTDM_LOG_DEBUG, "ANALOG EM CHANNEL thread ended.\n"); ftdm_log(FTDM_LOG_DEBUG, "ANALOG EM CHANNEL thread ended.\n");

View File

@ -0,0 +1,103 @@
-- zrtp_sas_proxy.lua
--
-- Copyright (c) 2011-2013 Travis Cross
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.
--
--
-- When we're acting as a ZRTP man-in-the-middle, proxy the SAS (Short
-- Authentication String) from one leg of the call to the other.
--
-- This script should be called asynchonously with luarun. e.g.:
--
-- <action application="export" data="nolocal:api_on_answer=luarun zrtp_sas_proxy.lua ${uuid}"/>
--
aleg=argv[1]
api=freeswitch.API()
function log(level,msg) return freeswitch.consoleLog(level,"zrtp_sas: "..msg.."\n") end
function sleep(sec) return freeswitch.msleep(sec*1000) end
function ready() return api:execute("uuid_exists",aleg)=="true" end
function getvar(uuid,var)
local x=api:execute("uuid_getvar",uuid.." "..var)
if x=="_undef_" then return nil end
return x
end
function getvarp(uuid,var) return getvar(uuid,var)=="true" end
function display(uuid,msg)
local cidn=getvar(uuid,"caller_id_name")
return api:execute("uuid_display",uuid.." "..msg.." "..cidn)
end
function mk_sas(sas1,sas2)
if sas1 and sas2 then return sas1.." "..sas2
else return sas1 or sas2 or "" end
end
function get_sas(uuid)
return mk_sas(getvar(uuid,"zrtp_sas1_string_audio"),
getvar(uuid,"zrtp_sas2_string"))
end
function log_sas(leg,uuid)
return log("notice",leg..": "..uuid.." sas: "..get_sas(uuid))
end
function display_sas(to,from)
return display(to," ("..get_sas(from)..")")
end
function get_bleg(aleg)
local retries=15 bleg=nil
while ready() do
if retries<1 then return nil end
local bleg=getvar(aleg,"signal_bond")
if bleg then return bleg end
log("debug","waiting for bleg uuid...")
sleep(1)
retries=retries-1
end
end
function handle_sas(aleg,bleg)
local retries=45 af=false bf=false
while ready() do
if retries<1 then return nil end
if not af and getvarp(aleg,"zrtp_secure_media_confirmed_audio") then
af=true
log_sas("aleg",aleg)
display_sas(bleg,aleg)
end
if not bf and getvarp(bleg,"zrtp_secure_media_confirmed_audio") then
bf=true
log_sas("bleg",bleg)
display_sas(aleg,bleg)
end
if (af and bf) then break
elseif af then log("debug","waiting on bleg zrtp...")
elseif bf then log("debug","waiting on aleg zrtp...")
else log("debug","waiting for zrtp...") end
sleep(1)
retries=retries-1
end
end
if not (getvarp(aleg,"zrtp_passthru") or getvarp(aleg,"proxy_media")) then
handle_sas(aleg,get_bleg(aleg))
end

View File

@ -83,6 +83,7 @@ SWITCH_DECLARE(switch_status_t) switch_console_add_complete_func(const char *nam
SWITCH_DECLARE(switch_status_t) switch_console_del_complete_func(const char *name); SWITCH_DECLARE(switch_status_t) switch_console_del_complete_func(const char *name);
SWITCH_DECLARE(switch_status_t) switch_console_run_complete_func(const char *func, const char *line, SWITCH_DECLARE(switch_status_t) switch_console_run_complete_func(const char *func, const char *line,
const char *last_word, switch_console_callback_match_t **matches); const char *last_word, switch_console_callback_match_t **matches);
SWITCH_DECLARE(void) switch_console_push_match_unique(switch_console_callback_match_t **matches, const char *new_val);
SWITCH_DECLARE(void) switch_console_push_match(switch_console_callback_match_t **matches, const char *new_val); SWITCH_DECLARE(void) switch_console_push_match(switch_console_callback_match_t **matches, const char *new_val);
SWITCH_DECLARE(void) switch_console_free_matches(switch_console_callback_match_t **matches); SWITCH_DECLARE(void) switch_console_free_matches(switch_console_callback_match_t **matches);
SWITCH_DECLARE(unsigned char) switch_console_complete(const char *line, const char *last_word, SWITCH_DECLARE(unsigned char) switch_console_complete(const char *line, const char *last_word,

View File

@ -464,14 +464,27 @@ SWITCH_DECLARE(switch_status_t) switch_resolve_host(const char *host, char *buf,
/*! /*!
\brief find local ip of the box \brief find local ip of the box
\param buf the buffer to write the ip adress found into \param buf the buffer to write the ip address found into
\param len the length of the buf \param len the length of the buf
\param mask the CIDR found (AF_INET only)
\param family the address family to return (AF_INET or AF_INET6) \param family the address family to return (AF_INET or AF_INET6)
\return SWITCH_STATUS_SUCCESSS for success, otherwise failure \return SWITCH_STATUS_SUCCESSS for success, otherwise failure
*/ */
SWITCH_DECLARE(switch_status_t) switch_find_local_ip(_Out_opt_bytecapcount_(len) SWITCH_DECLARE(switch_status_t) switch_find_local_ip(_Out_opt_bytecapcount_(len)
char *buf, _In_ int len, _In_opt_ int *mask, _In_ int family); char *buf, _In_ int len, _In_opt_ int *mask, _In_ int family);
/*!
\brief find primary ip of the specified interface
\param buf the buffer to write the ip address found into
\param len the length of the buf
\param mask the CIDR found (AF_INET only)
\param ifname interface name to check
\param family the address family to return (AF_INET or AF_INET6)
\return SWITCH_STATUS_SUCCESSS for success, otherwise failure
*/
SWITCH_DECLARE(switch_status_t) switch_find_interface_ip(_Out_opt_bytecapcount_(len)
char *buf, _In_ int len, _In_opt_ int *mask, _In_ const char *ifname, _In_ int family);
/*! /*!
\brief find the char representation of an ip adress \brief find the char representation of an ip adress
\param buf the buffer to write the ip adress found into \param buf the buffer to write the ip adress found into

View File

@ -6028,6 +6028,49 @@ SWITCH_STANDARD_API(file_exists_function)
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
#define INTERFACE_IP_SYNTAX "[auto|ipv4|ipv6] <ifname>"
SWITCH_STANDARD_API(interface_ip_function)
{
char *mydata = NULL, *argv[3] = { 0 };
int argc = 0;
char addr[INET6_ADDRSTRLEN];
if (!zstr(cmd)) {
mydata = strdup(cmd);
switch_assert(mydata);
argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc < 2) {
stream->write_function(stream, "USAGE: interface_ip %s\n", INTERFACE_IP_SYNTAX);
goto end;
}
if (!strcasecmp(argv[0], "ipv4")) {
if (switch_find_interface_ip(addr, sizeof(addr), NULL, argv[1], AF_INET) == SWITCH_STATUS_SUCCESS) {
stream->write_function(stream, "%s", addr);
}
}
else if (!strcasecmp(argv[0], "ipv6")) {
if (switch_find_interface_ip(addr, sizeof(addr), NULL, argv[1], AF_INET6) == SWITCH_STATUS_SUCCESS) {
stream->write_function(stream, "%s", addr);
}
}
else if (!strcasecmp(argv[0], "auto")) {
if (switch_find_interface_ip(addr, sizeof(addr), NULL, argv[1], AF_UNSPEC) == SWITCH_STATUS_SUCCESS) {
stream->write_function(stream, "%s", addr);
}
}
else {
stream->write_function(stream, "USAGE: interface_ip %s\n", INTERFACE_IP_SYNTAX);
}
end:
switch_safe_free(mydata);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
{ {
switch_api_interface_t *commands_api_interface; switch_api_interface_t *commands_api_interface;
@ -6065,6 +6108,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
SWITCH_ADD_API(commands_api_interface, "help", "Show help for all the api commands", help_function, ""); SWITCH_ADD_API(commands_api_interface, "help", "Show help for all the api commands", help_function, "");
SWITCH_ADD_API(commands_api_interface, "host_lookup", "Lookup host", host_lookup_function, "<hostname>"); SWITCH_ADD_API(commands_api_interface, "host_lookup", "Lookup host", host_lookup_function, "<hostname>");
SWITCH_ADD_API(commands_api_interface, "hostname", "Return the system hostname", hostname_api_function, ""); SWITCH_ADD_API(commands_api_interface, "hostname", "Return the system hostname", hostname_api_function, "");
SWITCH_ADD_API(commands_api_interface, "interface_ip", "Return the primary IP of an interface", interface_ip_function, INTERFACE_IP_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "switchname", "Return the switch name", switchname_api_function, ""); SWITCH_ADD_API(commands_api_interface, "switchname", "Return the switch name", switchname_api_function, "");
SWITCH_ADD_API(commands_api_interface, "hupall", "hupall", hupall_api_function, "<cause> [<var> <value>]"); SWITCH_ADD_API(commands_api_interface, "hupall", "hupall", hupall_api_function, "<cause> [<var> <value>]");
SWITCH_ADD_API(commands_api_interface, "in_group", "Determine if a user is in a group", in_group_function, "<user>[@<domain>] <group_name>"); SWITCH_ADD_API(commands_api_interface, "in_group", "Determine if a user is in a group", in_group_function, "<user>[@<domain>] <group_name>");
@ -6217,6 +6261,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
switch_console_set_complete("add fsctl flush_db_handles"); switch_console_set_complete("add fsctl flush_db_handles");
switch_console_set_complete("add fsctl min_idle_cpu"); switch_console_set_complete("add fsctl min_idle_cpu");
switch_console_set_complete("add fsctl send_sighup"); switch_console_set_complete("add fsctl send_sighup");
switch_console_set_complete("add interface_ip auto ::console::list_interfaces");
switch_console_set_complete("add interface_ip ipv4 ::console::list_interfaces");
switch_console_set_complete("add interface_ip ipv6 ::console::list_interfaces");
switch_console_set_complete("add load ::console::list_available_modules"); switch_console_set_complete("add load ::console::list_available_modules");
switch_console_set_complete("add nat_map reinit"); switch_console_set_complete("add nat_map reinit");
switch_console_set_complete("add nat_map republish"); switch_console_set_complete("add nat_map republish");

View File

@ -7670,7 +7670,9 @@ SWITCH_STANDARD_APP(conference_function)
} }
while (!pin_valid && pin_retries && status == SWITCH_STATUS_SUCCESS) { while (!pin_valid && pin_retries && status == SWITCH_STATUS_SUCCESS) {
int maxpin = strlen(dpin) > strlen(mdpin) ? strlen(dpin) : strlen(mdpin); size_t dpin_length = dpin ? strlen(dpin) : 0;
size_t mdpin_length = mdpin ? strlen(mdpin) : 0;
int maxpin = dpin_length > mdpin_length ? dpin_length : mdpin_length;
switch_status_t pstatus = SWITCH_STATUS_FALSE; switch_status_t pstatus = SWITCH_STATUS_FALSE;
/* be friendly */ /* be friendly */

View File

@ -2876,7 +2876,7 @@ static switch_status_t deliver_vm(vm_profile_t *profile,
update_mwi(profile, myid, domain_name, myfolder, MWI_REASON_NEW); update_mwi(profile, myid, domain_name, myfolder, MWI_REASON_NEW);
} }
if (send_mail && !zstr(vm_email) && switch_file_exists(file_path, pool) == SWITCH_STATUS_SUCCESS) { if (send_mail && (!zstr(vm_email) || !zstr(vm_notify_email)) && switch_file_exists(file_path, pool) == SWITCH_STATUS_SUCCESS) {
switch_event_t *event; switch_event_t *event;
char *from; char *from;
char *body; char *body;

View File

@ -894,6 +894,7 @@ SWITCH_STANDARD_API(sangoma_function)
char *argv[10] = { 0 }; char *argv[10] = { 0 };
int argc = 0; int argc = 0;
char *mycmd = NULL; char *mycmd = NULL;
switch_bool_t locked = SWITCH_FALSE;
if (zstr(cmd)) { if (zstr(cmd)) {
stream->write_function(stream, "%s", SANGOMA_SYNTAX); stream->write_function(stream, "%s", SANGOMA_SYNTAX);
@ -910,6 +911,10 @@ SWITCH_STANDARD_API(sangoma_function)
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
/* Most operations in this API require the global session lock anyways since sessions can disappear at any moment ... */
switch_mutex_lock(g_sessions_lock);
locked = SWITCH_TRUE;
if (!strcasecmp(argv[0], "settings")) { if (!strcasecmp(argv[0], "settings")) {
char addrbuff[50]; char addrbuff[50];
int addr; int addr;
@ -922,7 +927,6 @@ SWITCH_STANDARD_API(sangoma_function)
const void *var; const void *var;
void *val; void *val;
unsigned totalsess = 0; unsigned totalsess = 0;
switch_mutex_lock(g_sessions_lock);
#define STATS_FORMAT "%-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-15.15s %-15.15s\n" #define STATS_FORMAT "%-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-15.15s %-15.15s\n"
stream->write_function(stream, STATS_FORMAT, stream->write_function(stream, STATS_FORMAT,
"Session", "Codec", "Enc", "Dec", "Enc Tx", "Enc Rx", "Dec Tx", "Dec Rx", "Enc Lost", "Dec Lost", "Enc AvgRxMs", "Dec AvgRxMs"); "Session", "Codec", "Enc", "Dec", "Enc Tx", "Enc Rx", "Dec Tx", "Dec Rx", "Enc Lost", "Dec Lost", "Enc AvgRxMs", "Dec AvgRxMs");
@ -967,7 +971,6 @@ SWITCH_STANDARD_API(sangoma_function)
decoder_avgrxus_str); decoder_avgrxus_str);
totalsess++; totalsess++;
} }
switch_mutex_unlock(g_sessions_lock);
stream->write_function(stream, "Total sessions: %d\n", totalsess); stream->write_function(stream, "Total sessions: %d\n", totalsess);
} else if (!strcasecmp(argv[0], "stats")) { } else if (!strcasecmp(argv[0], "stats")) {
struct sangoma_transcoding_session *sess; struct sangoma_transcoding_session *sess;
@ -983,6 +986,7 @@ SWITCH_STANDARD_API(sangoma_function)
stream->write_function(stream, "%s", SANGOMA_SYNTAX); stream->write_function(stream, "%s", SANGOMA_SYNTAX);
goto done; goto done;
} }
sess = sangoma_find_session(sessid); sess = sangoma_find_session(sessid);
if (!sess) { if (!sess) {
stream->write_function(stream, "Failed to find session %lu\n", sessid); stream->write_function(stream, "Failed to find session %lu\n", sessid);
@ -1076,6 +1080,9 @@ SWITCH_STANDARD_API(sangoma_function)
} }
done: done:
if (locked) {
switch_mutex_unlock(g_sessions_lock);
}
switch_safe_free(mycmd); switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;

View File

@ -458,10 +458,7 @@ static switch_status_t channel_on_execute(switch_core_session_t *session)
if ((find_non_loopback_bridge(tech_pvt->other_session, &other_session, &other_uuid) == SWITCH_STATUS_SUCCESS)) { if ((find_non_loopback_bridge(tech_pvt->other_session, &other_session, &other_uuid) == SWITCH_STATUS_SUCCESS)) {
switch_channel_t *other_channel = switch_core_session_get_channel(other_session); switch_channel_t *other_channel = switch_core_session_get_channel(other_session);
if (switch_channel_test_flag(other_channel, CF_BRIDGED)) { switch_channel_wait_for_state_timeout(other_channel, CS_EXCHANGE_MEDIA, 5000);
/* Wait for real channel to be exchanging media */
switch_channel_wait_for_state(other_channel, channel, CS_EXCHANGE_MEDIA);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_INFO, "BOWOUT Replacing loopback channel with real channel: %s\n", switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_INFO, "BOWOUT Replacing loopback channel with real channel: %s\n",
switch_channel_get_name(other_channel)); switch_channel_get_name(other_channel));

View File

@ -438,6 +438,7 @@ switch_status_t skinny_session_send_call_info_all(switch_core_session_t *session
struct skinny_session_set_variables_helper { struct skinny_session_set_variables_helper {
private_t *tech_pvt; private_t *tech_pvt;
switch_channel_t *channel; switch_channel_t *channel;
listener_t *listener;
uint32_t count; uint32_t count;
}; };
@ -463,6 +464,9 @@ int skinny_session_set_variables_callback(void *pArg, int argc, char **argv, cha
struct skinny_session_set_variables_helper *helper = pArg; struct skinny_session_set_variables_helper *helper = pArg;
char *tmp; char *tmp;
listener_t *listener;
switch_xml_t xroot, xdomain, xuser, xvariables, xvariable;
helper->count++; helper->count++;
switch_channel_set_variable_name_printf(helper->channel, device_name, "skinny_device_name_%d", helper->count); switch_channel_set_variable_name_printf(helper->channel, device_name, "skinny_device_name_%d", helper->count);
@ -482,6 +486,50 @@ int skinny_session_set_variables_callback(void *pArg, int argc, char **argv, cha
switch_channel_set_variable_name_printf(helper->channel, value, "skinny_line_value_%d", helper->count); switch_channel_set_variable_name_printf(helper->channel, value, "skinny_line_value_%d", helper->count);
switch_channel_set_variable_name_printf(helper->channel, caller_name, "skinny_line_caller_name_%d", helper->count); switch_channel_set_variable_name_printf(helper->channel, caller_name, "skinny_line_caller_name_%d", helper->count);
listener = helper->listener;
if ( ! listener ) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(helper->tech_pvt->session), SWITCH_LOG_DEBUG,
"no defined listener on channel var setup, will not attempt to set variables\n");
return(0);
}
/* Process through and extract any variables from the user and set in the channel */
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(helper->tech_pvt->session), SWITCH_LOG_DEBUG,
"searching for user (id=%s) in profile %s in channel var setup\n",
listener->device_name, listener->profile->domain);
if (switch_xml_locate_user("id", listener->device_name, listener->profile->domain, "",
&xroot, &xdomain, &xuser, NULL, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(helper->tech_pvt->session), SWITCH_LOG_WARNING,
"unable to find user (id=%s) in channel var setup\n", listener->device_name);
}
if ( xuser ) {
char *uid = (char *) switch_xml_attr_soft(xuser, "id");
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(helper->tech_pvt->session), SWITCH_LOG_DEBUG,
"found user (id=%s) in channel var setup\n", uid);
if ((xvariables = switch_xml_child(xuser, "variables"))) {
for (xvariable = switch_xml_child(xvariables, "variable"); xvariable; xvariable = xvariable->next) {
char *var = (char *) switch_xml_attr_soft(xvariable, "name");
char *val = (char *) switch_xml_attr_soft(xvariable, "value");
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(helper->tech_pvt->session), SWITCH_LOG_DEBUG,
"found variable (%s=%s) for user (%s) in channel var setup\n", listener->device_name, val, var);
switch_channel_set_variable_name_printf(helper->channel, var, "%s", val);
}
}
}
if ( xroot ) {
switch_xml_free(xroot);
}
return 0; return 0;
} }
@ -492,6 +540,7 @@ switch_status_t skinny_session_set_variables(switch_core_session_t *session, lis
helper.tech_pvt = switch_core_session_get_private(session); helper.tech_pvt = switch_core_session_get_private(session);
helper.channel = switch_core_session_get_channel(session); helper.channel = switch_core_session_get_channel(session);
helper.listener = listener;
helper.count = 0; helper.count = 0;
switch_channel_set_variable(helper.channel, "skinny_profile_name", helper.tech_pvt->profile->name); switch_channel_set_variable(helper.channel, "skinny_profile_name", helper.tech_pvt->profile->name);

View File

@ -297,11 +297,13 @@ char *generate_pai_str(private_object_t *tech_pvt)
pai = switch_core_session_sprintf(tech_pvt->session, "%s: \"%s\" <%s>%s\n" pai = switch_core_session_sprintf(tech_pvt->session, "%s: \"%s\" <%s>%s\n"
"X-FS-Display-Name: %s\nX-FS-Display-Number: %s\n", "X-FS-Display-Name: %s\nX-FS-Display-Number: %s\n",
header, callee_name, callee_number, header, callee_name, callee_number,
tech_pvt->cid_type == CID_TYPE_RPID ? ";party=calling;privacy=off;screen=no" : "", tech_pvt->cid_type == CID_TYPE_RPID && !switch_stristr("aastra", ua) ?
";party=calling;privacy=off;screen=no" : "",
callee_name, callee_number); callee_name, callee_number);
} else { } else {
pai = switch_core_session_sprintf(tech_pvt->session, "%s: \"%s\" <%s>%s\n", header, callee_name, callee_number, pai = switch_core_session_sprintf(tech_pvt->session, "%s: \"%s\" <%s>%s\n", header, callee_name, callee_number,
tech_pvt->cid_type == CID_TYPE_RPID ? ";party=calling;privacy=off;screen=no" : ""); tech_pvt->cid_type == CID_TYPE_RPID && !switch_stristr("aastra", ua) ?
";party=calling;privacy=off;screen=no" : "");
} }
} }

View File

@ -4052,9 +4052,22 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name)
} }
} else if (!strcasecmp(var, "rtp-ip")) { } else if (!strcasecmp(var, "rtp-ip")) {
char *ip = mod_sofia_globals.guess_ip; char *ip = mod_sofia_globals.guess_ip;
char buf[64];
if (!strcmp(val, "0.0.0.0")) { if (!strcmp(val, "0.0.0.0")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid IP 0.0.0.0 replaced with %s\n", mod_sofia_globals.guess_ip); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid IP 0.0.0.0 replaced with %s\n", mod_sofia_globals.guess_ip);
} else if (!strncasecmp(val, "interface:", 10)) {
char *ifname = val+10;
int family = AF_UNSPEC;
if (!strncasecmp(ifname, "auto/", 5)) { ifname += 5; family = AF_UNSPEC; }
if (!strncasecmp(ifname, "ipv4/", 5)) { ifname += 5; family = AF_INET; }
if (!strncasecmp(ifname, "ipv6/", 5)) { ifname += 5; family = AF_INET6; }
if (switch_find_interface_ip(buf, sizeof(buf), NULL, ifname, family) == SWITCH_STATUS_SUCCESS) {
ip = buf;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Using %s IP for interface %s for rtp-ip\n", ip, val+10);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown IP for interface %s for rtp-ip\n", val+10);
}
} else { } else {
ip = strcasecmp(val, "auto") ? val : mod_sofia_globals.guess_ip; ip = strcasecmp(val, "auto") ? val : mod_sofia_globals.guess_ip;
} }
@ -4065,9 +4078,22 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name)
} }
} else if (!strcasecmp(var, "sip-ip")) { } else if (!strcasecmp(var, "sip-ip")) {
char *ip = mod_sofia_globals.guess_ip; char *ip = mod_sofia_globals.guess_ip;
char buf[64];
if (!strcmp(val, "0.0.0.0")) { if (!strcmp(val, "0.0.0.0")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid IP 0.0.0.0 replaced with %s\n", mod_sofia_globals.guess_ip); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid IP 0.0.0.0 replaced with %s\n", mod_sofia_globals.guess_ip);
} else if (!strncasecmp(val, "interface:", 10)) {
char *ifname = val+10;
int family = AF_UNSPEC;
if (!strncasecmp(ifname, "auto/", 5)) { ifname += 5; family = AF_UNSPEC; }
if (!strncasecmp(ifname, "ipv4/", 5)) { ifname += 5; family = AF_INET; }
if (!strncasecmp(ifname, "ipv6/", 5)) { ifname += 5; family = AF_INET6; }
if (switch_find_interface_ip(buf, sizeof(buf), NULL, ifname, family) == SWITCH_STATUS_SUCCESS) {
ip = buf;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Using %s IP for interface %s for sip-ip\n", ip, val+10);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown IP for interface %s for sip-ip\n", val+10);
}
} else { } else {
ip = strcasecmp(val, "auto") ? val : mod_sofia_globals.guess_ip; ip = strcasecmp(val, "auto") ? val : mod_sofia_globals.guess_ip;
} }
@ -8582,6 +8608,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia
profile_dup_clean(displayname, tech_pvt->caller_profile->caller_id_name, tech_pvt->caller_profile->pool); profile_dup_clean(displayname, tech_pvt->caller_profile->caller_id_name, tech_pvt->caller_profile->pool);
profile_dup_clean(from_user, tech_pvt->caller_profile->caller_id_number, tech_pvt->caller_profile->pool); profile_dup_clean(from_user, tech_pvt->caller_profile->caller_id_number, tech_pvt->caller_profile->pool);
profile_dup_clean(network_ip, tech_pvt->caller_profile->network_addr, tech_pvt->caller_profile->pool); profile_dup_clean(network_ip, tech_pvt->caller_profile->network_addr, tech_pvt->caller_profile->pool);
profile_dup_clean(from_user, tech_pvt->caller_profile->ani, tech_pvt->caller_profile->pool);
profile_dup_clean(aniii, tech_pvt->caller_profile->aniii, tech_pvt->caller_profile->pool); profile_dup_clean(aniii, tech_pvt->caller_profile->aniii, tech_pvt->caller_profile->pool);
profile_dup_clean(context, tech_pvt->caller_profile->context, tech_pvt->caller_profile->pool); profile_dup_clean(context, tech_pvt->caller_profile->context, tech_pvt->caller_profile->pool);
profile_dup_clean(destination_number, tech_pvt->caller_profile->destination_number, tech_pvt->caller_profile->pool); profile_dup_clean(destination_number, tech_pvt->caller_profile->destination_number, tech_pvt->caller_profile->pool);

View File

@ -154,6 +154,11 @@ switch_status_t sofia_presence_chat_send(switch_event_t *message_event)
goto end; goto end;
} }
if (!from) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing From: header.\n");
goto end;
}
if (!zstr(type)) { if (!zstr(type)) {
ct = type; ct = type;
} }

View File

@ -2745,7 +2745,7 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile,
skip_auth: skip_auth:
if (first && (ret == AUTH_OK || ret == AUTH_RENEWED)) { if (first && (ret == AUTH_OK || ret == AUTH_RENEWED)) {
if (!v_event) { if (v_event && !*v_event) {
switch_event_create_plain(v_event, SWITCH_EVENT_REQUEST_PARAMS); switch_event_create_plain(v_event, SWITCH_EVENT_REQUEST_PARAMS);
} }

View File

@ -11,6 +11,11 @@
<param name="record-file-prefix" value="$${recordings_dir}/"/> <param name="record-file-prefix" value="$${recordings_dir}/"/>
</record> </record>
<!-- input component params -->
<input>
<param name="default-recognizer" value="pocketsphinx"/>
</input>
<!-- XMPP server domain --> <!-- XMPP server domain -->
<domain name="$${rayo_domain_name}" shared-secret="ClueCon"> <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 --> <!-- use this instead if you want secure XMPP client to server connections. Put .crt and .key file in freeswitch/certs -->
@ -42,4 +47,211 @@
<dial-gateway uriprefix="sofia" dialprefix="" strip=""/> <dial-gateway uriprefix="sofia" dialprefix="" strip=""/>
</dial-gateways> </dial-gateways>
<!-- IQ request aliases. Used mainly for testing purposes or for controlling a rayo call via the console -->
<aliases>
<alias name="ping" target="external"><![CDATA[<iq type="get"><ping xmlns="urn:xmpp:ping"/></iq>]]></alias>
<alias name="answer" target="call"><![CDATA[<answer xmlns="urn:xmpp:rayo:1"/>]]></alias>
<alias name="hangup" target="call"><![CDATA[<hangup xmlns="urn:xmpp:rayo:1"/>]]></alias>
<alias name="join_mixer_duplex" target="call"><![CDATA[<join xmlns="urn:xmpp:rayo:1" mixer-name="test" direction="duplex"/>]]></alias>
<alias name="join_mixer_send" target="call"><![CDATA[<join xmlns="urn:xmpp:rayo:1" mixer-name="test" direction="send"/>]]></alias>
<alias name="join_mixer_recv" target="call"><![CDATA[<join xmlns="urn:xmpp:rayo:1" mixer-name="test" direction="recv"/>]]></alias>
<alias name="unjoin_mixer" target="call"><![CDATA[<unjoin xmlns="urn:xmpp:rayo:1" mixer-name="test"/>]]></alias>
<alias name="unjoin" target="call"><![CDATA[<unjoin xmlns="urn:xmpp:rayo:1"/>]]></alias>
<alias name="stop" target="component"><![CDATA[<stop xmlns="urn:xmpp:rayo:ext:1"/>]]></alias>
<alias name="output_bad" target="call"><![CDATA[<output xmlns="urn:xmpp:rayo:output:1" repeat-time="100"></output>]]></alias>
<alias name="pause" target="output"><![CDATA[<pause xmlns="urn:xmpp:rayo:output:1"/>]]></alias>
<alias name="resume" target="output"><![CDATA[<resume xmlns="urn:xmpp:rayo:output:1"/>]]></alias>
<alias name="speed-up" target="output"><![CDATA[<speed-up xmlns="urn:xmpp:rayo:output:1"/>]]></alias>
<alias name="speed-down" target="output"><![CDATA[<speed-down xmlns="urn:xmpp:rayo:output:1"/>]]></alias>
<alias name="volume-up" target="output"><![CDATA[<volume-up xmlns="urn:xmpp:rayo:output:1"/>]]></alias>
<alias name="volume-down" target="output"><![CDATA[<volume-down xmlns="urn:xmpp:rayo:output:1"/>]]></alias>
<alias name="record" target="call"><![CDATA[<record xmlns="urn:xmpp:rayo:record:1"/>]]></alias>
<alias name="record_pause" target="record"><![CDATA[<pause xmlns="urn:xmpp:rayo:record:1"/>]]></alias>
<alias name="record_resume" target="record"><![CDATA[<resume xmlns="urn:xmpp:rayo:record:1"/>]]></alias>
<alias name="prompt_barge" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="true">
<output xmlns="urn:xmpp:rayo:output:1" repeat-times="5">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="prompt_no_barge" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="false">
<output xmlns="urn:xmpp:rayo:output:1" repeat-times="5">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="prompt_long" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="true">
<output xmlns="urn:xmpp:rayo:output:1" repeat-times="100">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="prompt_multi_digit" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="true">
<output xmlns="urn:xmpp:rayo:output:1" repeat-times="100">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><item repeat="1-4"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="prompt_terminator" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="true">
<output xmlns="urn:xmpp:rayo:output:1" repeat-times="100">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000" terminator="#">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><item repeat="1-4"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="prompt_input_bad" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="true">
<output xmlns="urn:xmpp:rayo:output:1" repeat-times="100">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtf" initial-timeout="5000" inter-digit-timeout="3000" terminator="#">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><item repeat="1-4"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="prompt_output_bad" target="call">
<![CDATA[
<prompt xmlns="urn:xmpp:rayo:prompt:1" barge-in="true">
<output xmlns="urn:xmpp:rayo:output:1" repeat-time="100">
<document content-type="application/ssml+xml">
<![CDATA[<speak><p>Please press a digit.</p></speak>]]]]><![CDATA[>
</document>
</output>
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000" terminator="#">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><item repeat="1-4"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
</prompt>
]]>
</alias>
<alias name="input" target="call">
<![CDATA[
<input xmlns="urn:xmpp:rayo:input:1" mode="dtmf" initial-timeout="5000" inter-digit-timeout="3000">
<grammar content-type="application/srgs+xml">
<![CDATA[<grammar mode="dtmf"><rule id="digit" scope="public"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]]]><![CDATA[>
</grammar>
</input>
]]>
</alias>
<alias name="input_voice_yesno_unimrcp" target="call">
<![CDATA[
<input xmlns="urn:xmpp:rayo:input:1" mode="voice" recognizer="unimrcp">
<grammar content-type="application/srgs+xml>
<![CDATA[
<grammar xmlns="http://www.w3.org/2001/06/grammar"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/06/grammar http://www.w3.org/TR/speech-grammar/grammar.xsd"
xml:lang="en-US" version="1.0">
<rule id="yesno"><one-of><item>yes</item><item>no</item></one-of></rule></grammar>
]]]]><![CDATA[>
</grammar>
</input>
]]>
</alias>
<alias name="input_voice_yesno_unimrcp_timeout" target="call">
<![CDATA[
<input xmlns="urn:xmpp:rayo:input:1" mode="voice" recognizer="unimrcp" max-silence="5000" initial-timeout="5000">
<grammar content-type="application/srgs+xml>
<![CDATA[
<grammar xmlns="http://www.w3.org/2001/06/grammar"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/06/grammar http://www.w3.org/TR/speech-grammar/grammar.xsd"
xml:lang="en-US" version="1.0">
<rule id="yesno"><one-of><item>yes</item><item>no</item></one-of></rule></grammar>
]]]]><![CDATA[>
</grammar>
</input>
]]>
</alias>
<alias name="input_voice_yesno_pocketsphinx" target="call">
<![CDATA[
<input xmlns="urn:xmpp:rayo:input:1" mode="voice" recognizer="pocketsphinx" max-silence="5000" initial-timeout="5000">
<grammar content-type="application/srgs+xml>
<![CDATA[
<grammar xmlns="http://www.w3.org/2001/06/grammar"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/06/grammar http://www.w3.org/TR/speech-grammar/grammar.xsd"
xml:lang="en-US" version="1.0">
<rule id="yesno"><one-of><item>yes</item><item>no</item></one-of></rule></grammar>
]]]]><![CDATA[>
</grammar>
</input>
]]>
</alias>
<alias name="input_voice_yesno_default" target="call">
<![CDATA[
<input xmlns="urn:xmpp:rayo:input:1" mode="voice" max-silence="5000" initial-timeout="5000">
<grammar content-type="application/srgs+xml>
<![CDATA[
<grammar xmlns="http://www.w3.org/2001/06/grammar"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/06/grammar http://www.w3.org/TR/speech-grammar/grammar.xsd"
xml:lang="en-US" version="1.0">
<rule id="yesno"><one-of><item>yes</item><item>no</item></one-of></rule></grammar>
]]]]><![CDATA[>
</grammar>
</input>
]]>
</alias>
</aliases>
</configuration> </configuration>

View File

@ -216,6 +216,17 @@ double iks_find_decimal_attrib(iks *xml, const char *attrib)
return atof(iks_find_attrib_soft(xml, attrib)); return atof(iks_find_attrib_soft(xml, attrib));
} }
/**
* Get attribute character value of node
* @param xml the XML node to search
* @param attrib the Attribute name
* @return the attribute value
*/
char iks_find_char_attrib(iks *xml, const char *attrib)
{
return iks_find_attrib_soft(xml, attrib)[0];
}
/** /**
* Convert iksemel XML node type to string * Convert iksemel XML node type to string
* @param type the XML node type * @param type the XML node type
@ -392,6 +403,54 @@ int iks_attrib_is_decimal_between_zero_and_one(const char *value)
return SWITCH_FALSE; return SWITCH_FALSE;
} }
/**
* Validate dtmf digit
* @param value
* @return SWITCH_TRUE if 0-9,a,b,c,d,A,B,C,D,*,#
*/
int iks_attrib_is_dtmf_digit(const char *value)
{
if (value && *value && strlen(value) == 1) {
switch (*value) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'A':
case 'a':
case 'B':
case 'b':
case 'C':
case 'c':
case 'D':
case 'd':
case '*':
case '#':
return SWITCH_TRUE;
}
}
return SWITCH_FALSE;
}
/**
* @param fn to evaluate attribute
* @param attrib to evaluate
* @return true if not set or is valid
*/
int validate_optional_attrib(iks_attrib_validation_function fn, const char *attrib)
{
if (!attrib || !*attrib) {
return SWITCH_TRUE;
}
return fn(attrib);
}
#define IKS_SHA256_HEX_DIGEST_LENGTH ((SHA256_DIGEST_LENGTH * 2) + 1) #define IKS_SHA256_HEX_DIGEST_LENGTH ((SHA256_DIGEST_LENGTH * 2) + 1)
/** /**

View File

@ -63,6 +63,7 @@ extern const char *iks_find_attrib_soft(iks *xml, const char *attrib);
extern const char *iks_find_attrib_default(iks *xml, const char *attrib, const char *def); extern const char *iks_find_attrib_default(iks *xml, const char *attrib, const char *def);
extern int iks_find_bool_attrib(iks *xml, const char *attrib); extern int iks_find_bool_attrib(iks *xml, const char *attrib);
extern int iks_find_int_attrib(iks *xml, const char *attrib); extern int iks_find_int_attrib(iks *xml, const char *attrib);
extern char iks_find_char_attrib(iks *xml, const char *attrib);
extern double iks_find_decimal_attrib(iks *xml, const char *attrib); extern double iks_find_decimal_attrib(iks *xml, const char *attrib);
extern const char *iks_node_type_to_string(int type); extern const char *iks_node_type_to_string(int type);
extern const char *iks_net_error_to_string(int err); extern const char *iks_net_error_to_string(int err);
@ -73,9 +74,12 @@ extern char *iks_server_dialback_key(const char *secret, const char *receiving_s
/** A function to validate attribute value */ /** A function to validate attribute value */
typedef int (*iks_attrib_validation_function)(const char *); typedef int (*iks_attrib_validation_function)(const char *);
extern int validate_optional_attrib(iks_attrib_validation_function fn, const char *attrib);
#define ELEMENT_DECL(name) extern int VALIDATE_##name(iks *node); #define ELEMENT_DECL(name) extern int VALIDATE_##name(iks *node);
#define ELEMENT(name) int VALIDATE_##name(iks *node) { int result = 1; if (!node) return 0; #define ELEMENT(name) int VALIDATE_##name(iks *node) { int result = 1; if (!node) return 0;
#define ATTRIB(name, def, rule) result &= iks_attrib_is_##rule(iks_find_attrib_default(node, #name, #def)); #define ATTRIB(name, def, rule) result &= iks_attrib_is_##rule(iks_find_attrib_default(node, #name, #def));
#define OPTIONAL_ATTRIB(name, def, rule) result &= validate_optional_attrib(iks_attrib_is_##rule, iks_find_attrib_default(node, #name, #def));
#define STRING_ATTRIB(name, def, rule) result &= value_matches(iks_find_attrib_default(node, #name, #def), rule); #define STRING_ATTRIB(name, def, rule) result &= value_matches(iks_find_attrib_default(node, #name, #def), rule);
#define ELEMENT_END return result; } #define ELEMENT_END return result; }
@ -87,6 +91,7 @@ extern int iks_attrib_is_positive(const char *value);
extern int iks_attrib_is_positive_or_neg_one(const char *value); extern int iks_attrib_is_positive_or_neg_one(const char *value);
extern int iks_attrib_is_any(const char *value); extern int iks_attrib_is_any(const char *value);
extern int iks_attrib_is_decimal_between_zero_and_one(const char *value); extern int iks_attrib_is_decimal_between_zero_and_one(const char *value);
extern int iks_attrib_is_dtmf_digit(const char *value);
#endif #endif

View File

@ -239,6 +239,12 @@ static void rayo_console_client_send(struct rayo_actor *client, struct rayo_mess
static void on_client_presence(struct rayo_client *rclient, iks *node); static void on_client_presence(struct rayo_client *rclient, iks *node);
typedef switch_bool_t (* rayo_actor_match_fn)(struct rayo_actor *);
static switch_bool_t is_call_actor(struct rayo_actor *actor);
/** /**
* @param msg to check * @param msg to check
* @return true if message was sent by admin client (console) * @return true if message was sent by admin client (console)
@ -712,6 +718,9 @@ struct rayo_actor *rayo_actor_locate(const char *jid, const char *file, int line
{ {
struct rayo_actor *actor = NULL; struct rayo_actor *actor = NULL;
switch_mutex_lock(globals.actors_mutex); switch_mutex_lock(globals.actors_mutex);
if (!strncmp("xmpp:", jid, 5)) {
jid = jid + 5;
}
actor = (struct rayo_actor *)switch_core_hash_find(globals.actors, jid); actor = (struct rayo_actor *)switch_core_hash_find(globals.actors, jid);
if (actor) { if (actor) {
if (!actor->destroy) { if (!actor->destroy) {
@ -818,16 +827,33 @@ int rayo_actor_seq_next(struct rayo_actor *actor)
return seq; return seq;
} }
#define RAYO_CALL_LOCATE(call_uuid) rayo_call_locate(call_uuid, __FILE__, __LINE__) #define RAYO_CALL_LOCATE(call_uri) rayo_call_locate(call_uri, __FILE__, __LINE__)
/** /**
* Get exclusive access to Rayo call data. Use to access call data outside channel thread. * Get access to Rayo call data. Use to access call data outside channel thread.
* @param call_uri the Rayo XMPP URI
* @return the call or NULL.
*/
static struct rayo_call *rayo_call_locate(const char *call_uri, const char *file, int line)
{
struct rayo_actor *actor = rayo_actor_locate(call_uri, file, line);
if (actor && is_call_actor(actor)) {
return RAYO_CALL(actor);
} else if (actor) {
RAYO_UNLOCK(actor);
}
return NULL;
}
#define RAYO_CALL_LOCATE_BY_ID(call_uuid) rayo_call_locate_by_id(call_uuid, __FILE__, __LINE__)
/**
* Get access to Rayo call data. Use to access call data outside channel thread.
* @param call_uuid the FreeSWITCH call UUID * @param call_uuid the FreeSWITCH call UUID
* @return the call or NULL. * @return the call or NULL.
*/ */
static struct rayo_call *rayo_call_locate(const char *call_uuid, const char *file, int line) static struct rayo_call *rayo_call_locate_by_id(const char *call_uuid, const char *file, int line)
{ {
struct rayo_actor *actor = rayo_actor_locate_by_id(call_uuid, file, line); struct rayo_actor *actor = rayo_actor_locate_by_id(call_uuid, file, line);
if (actor && !strcmp(RAT_CALL, actor->type)) { if (actor && is_call_actor(actor)) {
return RAYO_CALL(actor); return RAYO_CALL(actor);
} else if (actor) { } else if (actor) {
RAYO_UNLOCK(actor); RAYO_UNLOCK(actor);
@ -1686,18 +1712,18 @@ static iks *on_rayo_hangup(struct rayo_actor *call, struct rayo_message *msg, vo
* @param call the call that joins * @param call the call that joins
* @param session the session * @param session the session
* @param node the join request * @param node the join request
* @param call_id to join * @param call_uri to join
* @param media mode (direct/bridge) * @param media mode (direct/bridge)
* @return the response * @return the response
*/ */
static iks *join_call(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *call_id, const char *media) static iks *join_call(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *call_uri, const char *media)
{ {
iks *response = NULL; iks *response = NULL;
/* take call out of media path if media = "direct" */ /* take call out of media path if media = "direct" */
const char *bypass = !strcmp("direct", media) ? "true" : "false"; const char *bypass = !strcmp("direct", media) ? "true" : "false";
/* check if joining to rayo call */ /* check if joining to rayo call */
struct rayo_call *b_call = RAYO_CALL_LOCATE(call_id); struct rayo_call *b_call = RAYO_CALL_LOCATE(call_uri);
if (!b_call) { if (!b_call) {
/* not a rayo call */ /* not a rayo call */
response = iks_new_error_detailed(node, STANZA_ERROR_SERVICE_UNAVAILABLE, "b-leg is not a rayo call"); response = iks_new_error_detailed(node, STANZA_ERROR_SERVICE_UNAVAILABLE, "b-leg is not a rayo call");
@ -1706,18 +1732,17 @@ static iks *join_call(struct rayo_call *call, switch_core_session_t *session, ik
response = iks_new_error_detailed(node, STANZA_ERROR_CONFLICT, "multiple joined calls not supported"); response = iks_new_error_detailed(node, STANZA_ERROR_CONFLICT, "multiple joined calls not supported");
RAYO_UNLOCK(b_call); RAYO_UNLOCK(b_call);
} else { } else {
RAYO_UNLOCK(b_call);
/* bridge this call to call-uri */ /* bridge this call to call-uri */
switch_channel_set_variable(switch_core_session_get_channel(session), "bypass_media", bypass); switch_channel_set_variable(switch_core_session_get_channel(session), "bypass_media", bypass);
if (switch_false(bypass)) { if (switch_false(bypass)) {
switch_channel_pre_answer(switch_core_session_get_channel(session)); switch_channel_pre_answer(switch_core_session_get_channel(session));
} }
if (switch_ivr_uuid_bridge(rayo_call_get_uuid(call), call_id) == SWITCH_STATUS_SUCCESS) { if (switch_ivr_uuid_bridge(rayo_call_get_uuid(call), rayo_call_get_uuid(b_call)) == SWITCH_STATUS_SUCCESS) {
response = iks_new_iq_result(node); response = iks_new_iq_result(node);
} else { } else {
response = iks_new_error_detailed(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to bridge call"); response = iks_new_error_detailed(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to bridge call");
} }
RAYO_UNLOCK(b_call);
} }
return response; return response;
} }
@ -1791,7 +1816,7 @@ static iks *on_rayo_join(struct rayo_actor *call, struct rayo_message *msg, void
iks *join = iks_find(node, "join"); iks *join = iks_find(node, "join");
const char *join_id; const char *join_id;
const char *mixer_name; const char *mixer_name;
const char *call_id; const char *call_uri;
/* validate input attributes */ /* validate input attributes */
if (!VALIDATE_RAYO_JOIN(join)) { if (!VALIDATE_RAYO_JOIN(join)) {
@ -1800,22 +1825,22 @@ static iks *on_rayo_join(struct rayo_actor *call, struct rayo_message *msg, void
goto done; goto done;
} }
mixer_name = iks_find_attrib(join, "mixer-name"); mixer_name = iks_find_attrib(join, "mixer-name");
call_id = iks_find_attrib(join, "call-uri"); call_uri = iks_find_attrib(join, "call-uri");
if (!zstr(mixer_name)) { if (!zstr(mixer_name)) {
join_id = mixer_name; join_id = mixer_name;
} else { } else {
join_id = call_id; join_id = call_uri;
} }
/* can't join both mixer and call */ /* can't join both mixer and call */
if (!zstr(mixer_name) && !zstr(call_id)) { if (!zstr(mixer_name) && !zstr(call_uri)) {
response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "mixer-name and call-uri are mutually exclusive"); response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "mixer-name and call-uri are mutually exclusive");
goto done; goto done;
} }
/* need to join *something* */ /* need to join *something* */
if (zstr(mixer_name) && zstr(call_id)) { if (zstr(mixer_name) && zstr(call_uri)) {
response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "mixer-name or call-uri is required"); response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "mixer-name or call-uri is required");
goto done; goto done;
} }
@ -1832,7 +1857,7 @@ static iks *on_rayo_join(struct rayo_actor *call, struct rayo_message *msg, void
response = join_mixer(RAYO_CALL(call), session, node, mixer_name, iks_find_attrib(join, "direction")); response = join_mixer(RAYO_CALL(call), session, node, mixer_name, iks_find_attrib(join, "direction"));
} else { } else {
/* bridge calls */ /* bridge calls */
response = join_call(RAYO_CALL(call), session, node, call_id, iks_find_attrib(join, "media")); response = join_call(RAYO_CALL(call), session, node, call_uri, iks_find_attrib(join, "media"));
} }
done: done:
@ -1844,21 +1869,22 @@ done:
* @param call the call that unjoined * @param call the call that unjoined
* @param session the session * @param session the session
* @param node the unjoin request * @param node the unjoin request
* @param call_id the b-leg uuid * @param call_uri the b-leg xmpp URI
* @return the response * @return the response
*/ */
static iks *unjoin_call(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *call_id) static iks *unjoin_call(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *call_uri)
{ {
iks *response = NULL; iks *response = NULL;
const char *bleg = switch_channel_get_variable(switch_core_session_get_channel(session), SWITCH_BRIDGE_UUID_VARIABLE); const char *bleg_uuid = switch_channel_get_variable(switch_core_session_get_channel(session), SWITCH_BRIDGE_UUID_VARIABLE);
const char *bleg_uri = switch_core_session_sprintf(session, "xmpp:%s@%s", bleg_uuid ? bleg_uuid : "", RAYO_JID(globals.server));
/* bleg must match call_id */ /* bleg must match call_uri */
if (!zstr(bleg) && !strcmp(bleg, call_id)) { if (!zstr(bleg_uri) && !strcmp(bleg_uri, call_uri)) {
/* unbridge call */ /* unbridge call */
response = iks_new_iq_result(node); response = iks_new_iq_result(node);
switch_ivr_park_session(session); switch_ivr_park_session(session);
} else { } else {
/* not bridged or wrong b-leg UUID */ /* not bridged or wrong b-leg URI */
response = iks_new_error(node, STANZA_ERROR_SERVICE_UNAVAILABLE); response = iks_new_error(node, STANZA_ERROR_SERVICE_UNAVAILABLE);
} }
@ -1913,16 +1939,16 @@ static iks *on_rayo_unjoin(struct rayo_actor *call, struct rayo_message *msg, vo
switch_core_session_t *session = (switch_core_session_t *)session_data; switch_core_session_t *session = (switch_core_session_t *)session_data;
iks *response = NULL; iks *response = NULL;
iks *unjoin = iks_find(node, "unjoin"); iks *unjoin = iks_find(node, "unjoin");
const char *call_id = iks_find_attrib(unjoin, "call-uri"); const char *call_uri = iks_find_attrib(unjoin, "call-uri");
const char *mixer_name = iks_find_attrib(unjoin, "mixer-name"); const char *mixer_name = iks_find_attrib(unjoin, "mixer-name");
if (!zstr(call_id) && !zstr(mixer_name)) { if (!zstr(call_uri) && !zstr(mixer_name)) {
response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST); response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST);
} else if (!RAYO_CALL(call)->joined) { } else if (!RAYO_CALL(call)->joined) {
/* not joined to anything */ /* not joined to anything */
response = iks_new_error(node, STANZA_ERROR_SERVICE_UNAVAILABLE); response = iks_new_error(node, STANZA_ERROR_SERVICE_UNAVAILABLE);
} else if (!zstr(call_id)) { } else if (!zstr(call_uri)) {
response = unjoin_call(RAYO_CALL(call), session, node, call_id); response = unjoin_call(RAYO_CALL(call), session, node, call_uri);
} else if (!zstr(mixer_name)) { } else if (!zstr(mixer_name)) {
response = unjoin_mixer(RAYO_CALL(call), session, node, mixer_name); response = unjoin_mixer(RAYO_CALL(call), session, node, mixer_name);
} else { } else {
@ -2037,20 +2063,20 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void *
if (join) { if (join) {
/* check join args */ /* check join args */
const char *call_id = iks_find_attrib(join, "call-uri"); const char *call_uri = iks_find_attrib(join, "call-uri");
const char *mixer_name = iks_find_attrib(join, "mixer-name"); const char *mixer_name = iks_find_attrib(join, "mixer-name");
if (!zstr(call_id) && !zstr(mixer_name)) { if (!zstr(call_uri) && !zstr(mixer_name)) {
/* can't join both */ /* can't join both */
response = iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); response = iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
goto done; goto done;
} else if (zstr(call_id) && zstr(mixer_name)) { } else if (zstr(call_uri) && zstr(mixer_name)) {
/* nobody to join to? */ /* nobody to join to? */
response = iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); response = iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
goto done; goto done;
} else if (!zstr(call_id)) { } else if (!zstr(call_uri)) {
/* bridge */ /* bridge */
struct rayo_call *b_call = RAYO_CALL_LOCATE(call_id); struct rayo_call *b_call = RAYO_CALL_LOCATE(call_uri);
/* is b-leg available? */ /* is b-leg available? */
if (!b_call) { if (!b_call) {
response = iks_new_error_detailed(iq, STANZA_ERROR_SERVICE_UNAVAILABLE, "b-leg not found"); response = iks_new_error_detailed(iq, STANZA_ERROR_SERVICE_UNAVAILABLE, "b-leg not found");
@ -2060,8 +2086,8 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void *
RAYO_UNLOCK(b_call); RAYO_UNLOCK(b_call);
goto done; goto done;
} }
stream.write_function(&stream, "%s%s &rayo(bridge %s)", gateway->dial_prefix, dial_to_stripped, rayo_call_get_uuid(b_call));
RAYO_UNLOCK(b_call); RAYO_UNLOCK(b_call);
stream.write_function(&stream, "%s%s &rayo(bridge %s)", gateway->dial_prefix, dial_to_stripped, call_id);
} else { } else {
/* conference */ /* conference */
stream.write_function(&stream, "%s%s &rayo(conference %s@%s)", gateway->dial_prefix, dial_to_stripped, mixer_name, globals.mixer_conf_profile); stream.write_function(&stream, "%s%s &rayo(conference %s@%s)", gateway->dial_prefix, dial_to_stripped, mixer_name, globals.mixer_conf_profile);
@ -2393,7 +2419,7 @@ static void on_mixer_delete_member_event(struct rayo_mixer *mixer, switch_event_
switch_core_hash_delete(mixer->members, uuid); switch_core_hash_delete(mixer->members, uuid);
/* flag call as available to join another mixer */ /* flag call as available to join another mixer */
call = RAYO_CALL_LOCATE(uuid); call = RAYO_CALL_LOCATE_BY_ID(uuid);
if (call) { if (call) {
call->joined = 0; call->joined = 0;
call->joined_id = NULL; call->joined_id = NULL;
@ -2409,7 +2435,7 @@ static void on_mixer_delete_member_event(struct rayo_mixer *mixer, switch_event_
/* broadcast member unjoined event to subscribers */ /* broadcast member unjoined event to subscribers */
delete_member_event = iks_new_presence("unjoined", RAYO_NS, RAYO_JID(mixer), ""); delete_member_event = iks_new_presence("unjoined", RAYO_NS, RAYO_JID(mixer), "");
x = iks_find(delete_member_event, "unjoined"); x = iks_find(delete_member_event, "unjoined");
iks_insert_attrib(x, "call-uri", uuid); iks_insert_attrib_printf(x, "call-uri", "xmpp:%s@%s", uuid, RAYO_JID(globals.server));
broadcast_mixer_event(mixer, delete_member_event); broadcast_mixer_event(mixer, delete_member_event);
iks_delete(delete_member_event); iks_delete(delete_member_event);
@ -2445,7 +2471,7 @@ static void on_mixer_add_member_event(struct rayo_mixer *mixer, switch_event_t *
{ {
iks *add_member_event = NULL, *x; iks *add_member_event = NULL, *x;
const char *uuid = switch_event_get_header(event, "Unique-ID"); const char *uuid = switch_event_get_header(event, "Unique-ID");
struct rayo_call *call = RAYO_CALL_LOCATE(uuid); struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(uuid);
if (!mixer) { if (!mixer) {
/* new mixer */ /* new mixer */
@ -2487,7 +2513,7 @@ static void on_mixer_add_member_event(struct rayo_mixer *mixer, switch_event_t *
/* broadcast member joined event to subscribers */ /* broadcast member joined event to subscribers */
add_member_event = iks_new_presence("joined", RAYO_NS, RAYO_JID(mixer), ""); add_member_event = iks_new_presence("joined", RAYO_NS, RAYO_JID(mixer), "");
x = iks_find(add_member_event, "joined"); x = iks_find(add_member_event, "joined");
iks_insert_attrib(x, "call-uri", uuid); iks_insert_attrib_printf(x, "call-uri", "xmpp:%s@%s", uuid, RAYO_JID(globals.server));
broadcast_mixer_event(mixer, add_member_event); broadcast_mixer_event(mixer, add_member_event);
iks_delete(add_member_event); iks_delete(add_member_event);
} }
@ -2533,7 +2559,7 @@ static void on_call_originate_event(struct rayo_client *rclient, switch_event_t
{ {
switch_core_session_t *session = NULL; switch_core_session_t *session = NULL;
const char *uuid = switch_event_get_header(event, "Unique-ID"); const char *uuid = switch_event_get_header(event, "Unique-ID");
struct rayo_call *call = RAYO_CALL_LOCATE(uuid); struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(uuid);
if (call && (session = switch_core_session_locate(uuid))) { if (call && (session = switch_core_session_locate(uuid))) {
iks *response, *ref; iks *response, *ref;
@ -2550,11 +2576,7 @@ static void on_call_originate_event(struct rayo_client *rclient, switch_event_t
ref = iks_insert(response, "ref"); ref = iks_insert(response, "ref");
iks_insert_attrib(ref, "xmlns", RAYO_NS); iks_insert_attrib(ref, "xmlns", RAYO_NS);
#ifdef RAYO_UUID_IN_REF_URI
iks_insert_attrib(ref, "uri", uuid);
#else
iks_insert_attrib_printf(ref, "uri", "xmpp:%s", RAYO_JID(call)); iks_insert_attrib_printf(ref, "uri", "xmpp:%s", RAYO_JID(call));
#endif
RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), response); RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), response);
call->dial_id = NULL; call->dial_id = NULL;
} }
@ -2567,7 +2589,7 @@ static void on_call_originate_event(struct rayo_client *rclient, switch_event_t
*/ */
static void on_call_end_event(switch_event_t *event) static void on_call_end_event(switch_event_t *event)
{ {
struct rayo_call *call = RAYO_CALL_LOCATE(switch_event_get_header(event, "Unique-ID")); struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(switch_event_get_header(event, "Unique-ID"));
if (call) { if (call) {
#if 0 #if 0
@ -2591,7 +2613,7 @@ static void on_call_end_event(switch_event_t *event)
*/ */
static void on_call_answer_event(struct rayo_client *rclient, switch_event_t *event) static void on_call_answer_event(struct rayo_client *rclient, switch_event_t *event)
{ {
struct rayo_call *call = RAYO_CALL_LOCATE(switch_event_get_header(event, "Unique-ID")); struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(switch_event_get_header(event, "Unique-ID"));
if (call) { if (call) {
iks *revent = iks_new_presence("answered", RAYO_NS, iks *revent = iks_new_presence("answered", RAYO_NS,
switch_event_get_header(event, "variable_rayo_call_jid"), switch_event_get_header(event, "variable_rayo_call_jid"),
@ -2610,7 +2632,7 @@ static void on_call_ringing_event(struct rayo_client *rclient, switch_event_t *e
{ {
const char *call_direction = switch_event_get_header(event, "Call-Direction"); const char *call_direction = switch_event_get_header(event, "Call-Direction");
if (call_direction && !strcmp(call_direction, "outbound")) { if (call_direction && !strcmp(call_direction, "outbound")) {
struct rayo_call *call = RAYO_CALL_LOCATE(switch_event_get_header(event, "Unique-ID")); struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(switch_event_get_header(event, "Unique-ID"));
if (call) { if (call) {
switch_mutex_lock(RAYO_ACTOR(call)->mutex); switch_mutex_lock(RAYO_ACTOR(call)->mutex);
if (!call->ringing_sent) { if (!call->ringing_sent) {
@ -2635,7 +2657,7 @@ static void on_call_bridge_event(struct rayo_client *rclient, switch_event_t *ev
{ {
const char *a_uuid = switch_event_get_header(event, "Unique-ID"); const char *a_uuid = switch_event_get_header(event, "Unique-ID");
const char *b_uuid = switch_event_get_header(event, "Bridge-B-Unique-ID"); const char *b_uuid = switch_event_get_header(event, "Bridge-B-Unique-ID");
struct rayo_call *call = RAYO_CALL_LOCATE(a_uuid); struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(a_uuid);
struct rayo_call *b_call; struct rayo_call *b_call;
if (call) { if (call) {
@ -2644,7 +2666,7 @@ static void on_call_bridge_event(struct rayo_client *rclient, switch_event_t *ev
switch_event_get_header(event, "variable_rayo_call_jid"), switch_event_get_header(event, "variable_rayo_call_jid"),
switch_event_get_header(event, "variable_rayo_dcp_jid")); switch_event_get_header(event, "variable_rayo_dcp_jid"));
iks *joined = iks_find(revent, "joined"); iks *joined = iks_find(revent, "joined");
iks_insert_attrib(joined, "call-uri", b_uuid); iks_insert_attrib_printf(joined, "call-uri", "xmpp:%s@%s", b_uuid, RAYO_JID(globals.server));
call->joined = JOINED_CALL; call->joined = JOINED_CALL;
call->joined_id = switch_core_strdup(RAYO_POOL(call), b_uuid); call->joined_id = switch_core_strdup(RAYO_POOL(call), b_uuid);
@ -2652,11 +2674,11 @@ static void on_call_bridge_event(struct rayo_client *rclient, switch_event_t *ev
RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent); RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent);
/* send B-leg event */ /* send B-leg event */
b_call = RAYO_CALL_LOCATE(b_uuid); b_call = RAYO_CALL_LOCATE_BY_ID(b_uuid);
if (b_call) { if (b_call) {
revent = iks_new_presence("joined", RAYO_NS, RAYO_JID(b_call), rayo_call_get_dcp_jid(b_call)); revent = iks_new_presence("joined", RAYO_NS, RAYO_JID(b_call), rayo_call_get_dcp_jid(b_call));
joined = iks_find(revent, "joined"); joined = iks_find(revent, "joined");
iks_insert_attrib(joined, "call-uri", a_uuid); iks_insert_attrib_printf(joined, "call-uri", "xmpp:%s@%s", a_uuid, RAYO_JID(globals.server));
b_call->joined = JOINED_CALL; b_call->joined = JOINED_CALL;
b_call->joined_id = switch_core_strdup(RAYO_POOL(b_call), a_uuid); b_call->joined_id = switch_core_strdup(RAYO_POOL(b_call), a_uuid);
@ -2677,7 +2699,7 @@ static void on_call_unbridge_event(struct rayo_client *rclient, switch_event_t *
{ {
const char *a_uuid = switch_event_get_header(event, "Unique-ID"); const char *a_uuid = switch_event_get_header(event, "Unique-ID");
const char *b_uuid = switch_event_get_header(event, "Bridge-B-Unique-ID"); const char *b_uuid = switch_event_get_header(event, "Bridge-B-Unique-ID");
struct rayo_call *call = RAYO_CALL_LOCATE(a_uuid); struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(a_uuid);
struct rayo_call *b_call; struct rayo_call *b_call;
if (call) { if (call) {
@ -2686,18 +2708,18 @@ static void on_call_unbridge_event(struct rayo_client *rclient, switch_event_t *
switch_event_get_header(event, "variable_rayo_call_jid"), switch_event_get_header(event, "variable_rayo_call_jid"),
switch_event_get_header(event, "variable_rayo_dcp_jid")); switch_event_get_header(event, "variable_rayo_dcp_jid"));
iks *joined = iks_find(revent, "unjoined"); iks *joined = iks_find(revent, "unjoined");
iks_insert_attrib(joined, "call-uri", b_uuid); iks_insert_attrib_printf(joined, "call-uri", "xmpp:%s@%s", b_uuid, RAYO_JID(globals.server));
RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent); RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent);
call->joined = 0; call->joined = 0;
call->joined_id = NULL; call->joined_id = NULL;
/* send B-leg event */ /* send B-leg event */
b_call = RAYO_CALL_LOCATE(b_uuid); b_call = RAYO_CALL_LOCATE_BY_ID(b_uuid);
if (b_call) { if (b_call) {
revent = iks_new_presence("unjoined", RAYO_NS, RAYO_JID(b_call), rayo_call_get_dcp_jid(b_call)); revent = iks_new_presence("unjoined", RAYO_NS, RAYO_JID(b_call), rayo_call_get_dcp_jid(b_call));
joined = iks_find(revent, "unjoined"); joined = iks_find(revent, "unjoined");
iks_insert_attrib(joined, "call-uri", a_uuid); iks_insert_attrib_printf(joined, "call-uri", "xmpp:%s@%s", a_uuid, RAYO_JID(globals.server));
RAYO_SEND_MESSAGE(b_call, rayo_call_get_dcp_jid(b_call), revent); RAYO_SEND_MESSAGE(b_call, rayo_call_get_dcp_jid(b_call), revent);
b_call->joined = 0; b_call->joined = 0;
@ -3077,6 +3099,21 @@ static void on_xmpp_stream_destroy(struct xmpp_stream *stream)
} }
} }
/**
* Add an alias to an API command
* @param alias_name
* @param alias_target
* @param alias_cmd
*/
static void rayo_add_cmd_alias(const char *alias_name, const char *alias_target, const char *alias_cmd)
{
if (zstr(alias_target)) {
alias_target = "all";
}
switch_console_set_complete(switch_core_sprintf(globals.pool, "add rayo %s ::rayo::list_%s", alias_name, alias_target));
switch_core_hash_insert(globals.cmd_aliases, alias_name, alias_cmd);
}
/** /**
* Process module XML configuration * Process module XML configuration
* @param pool memory pool to allocate from * @param pool memory pool to allocate from
@ -3262,6 +3299,22 @@ static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_
} }
} }
/* get aliases */
{
switch_xml_t aliases = switch_xml_child(cfg, "aliases");
if (aliases) {
switch_xml_t alias;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Setting configured aliases\n");
for (alias = switch_xml_child(aliases, "alias"); alias; alias = alias->next) {
const char *alias_name = switch_xml_attr_soft(alias, "name");
const char *alias_target = switch_xml_attr_soft(alias, "target");
if (!zstr(alias_name) && !zstr(alias->txt)) {
rayo_add_cmd_alias(alias_name, alias_target, alias->txt);
}
}
}
}
done: done:
switch_xml_free(xml); switch_xml_free(xml);
@ -3360,12 +3413,6 @@ static void send_console_command(struct rayo_client *client, const char *to, con
iks *command = NULL; iks *command = NULL;
iksparser *p = iks_dom_new(&command); iksparser *p = iks_dom_new(&command);
/* check if aliased */
const char *alias = switch_core_hash_find(globals.cmd_aliases, command_str);
if (!zstr(alias)) {
command_str = alias;
}
if (iks_parse(p, command_str, 0, 1) == IKS_OK && command) { if (iks_parse(p, command_str, 0, 1) == IKS_OK && command) {
char *str; char *str;
iks *iq = NULL; iks *iq = NULL;
@ -3404,14 +3451,15 @@ static void send_console_command(struct rayo_client *client, const char *to, con
/** /**
* Send command to rayo actor * Send command to rayo actor
*/ */
static int command_api(const char *cmd, switch_stream_handle_t *stream) static int command_api(char *cmd, switch_stream_handle_t *stream)
{ {
char *cmd_dup = strdup(cmd);
char *argv[2] = { 0 }; char *argv[2] = { 0 };
int argc = switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0])); if (!zstr(cmd)) {
int argc = switch_separate_string(cmd, ' ', argv, sizeof(argv) / sizeof(argv[0]));
if (argc != 2) { if (argc != 2) {
free(cmd_dup); return 0;
}
} else {
return 0; return 0;
} }
@ -3419,7 +3467,22 @@ static int command_api(const char *cmd, switch_stream_handle_t *stream)
send_console_command(globals.console, argv[0], argv[1]); send_console_command(globals.console, argv[0], argv[1]);
stream->write_function(stream, "+OK\n"); stream->write_function(stream, "+OK\n");
free(cmd_dup); return 1;
}
/**
* Send command to rayo actor
*/
static int alias_api(const char *cmd, char *jid, switch_stream_handle_t *stream)
{
if (zstr(cmd) || zstr(jid)) {
return 0;
}
/* send command */
send_console_command(globals.console, jid, cmd);
stream->write_function(stream, "+OK\n");
return 1; return 1;
} }
@ -3443,14 +3506,15 @@ static void send_console_message(struct rayo_client *client, const char *to, con
/** /**
* Send message to rayo actor * Send message to rayo actor
*/ */
static int message_api(const char *msg, switch_stream_handle_t *stream) static int message_api(char *cmd, switch_stream_handle_t *stream)
{ {
char *msg_dup = strdup(msg);
char *argv[2] = { 0 }; char *argv[2] = { 0 };
int argc = switch_separate_string(msg_dup, ' ', argv, sizeof(argv) / sizeof(argv[0])); if (!zstr(cmd)) {
int argc = switch_separate_string(cmd, ' ', argv, sizeof(argv) / sizeof(argv[0]));
if (argc != 2) { if (argc != 2) {
free(msg_dup); return 0;
}
} else {
return 0; return 0;
} }
@ -3458,7 +3522,6 @@ static int message_api(const char *msg, switch_stream_handle_t *stream)
send_console_message(globals.console, argv[0], argv[1]); send_console_message(globals.console, argv[0], argv[1]);
stream->write_function(stream, "+OK\n"); stream->write_function(stream, "+OK\n");
free(msg_dup);
return 1; return 1;
} }
@ -3484,151 +3547,238 @@ static void send_console_presence(struct rayo_client *client, const char *to, in
/** /**
* Send console presence * Send console presence
*/ */
static int presence_api(const char *cmd, switch_stream_handle_t *stream) static int presence_api(char *cmd, switch_stream_handle_t *stream)
{ {
char *cmd_dup = strdup(cmd);
char *argv[2] = { 0 };
int argc = switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0]));
int is_online = 0; int is_online = 0;
char *argv[2] = { 0 };
if (!zstr(cmd)) {
int argc = switch_separate_string(cmd, ' ', argv, sizeof(argv) / sizeof(argv[0]));
if (argc != 2) { if (argc != 2) {
free(cmd_dup); return 0;
}
} else {
return 0; return 0;
} }
if (!strcmp("online", argv[1])) { if (!strcmp("online", argv[1])) {
is_online = 1; is_online = 1;
} else if (strcmp("offline", argv[1])) { } else if (strcmp("offline", argv[1])) {
free(cmd_dup);
return 0; return 0;
} }
/* send presence */ /* send presence */
send_console_presence(globals.console, argv[0], is_online); send_console_presence(globals.console, argv[0], is_online);
stream->write_function(stream, "+OK\n"); stream->write_function(stream, "+OK\n");
free(cmd_dup);
return 1; return 1;
} }
#define RAYO_API_SYNTAX "status | (cmd <jid> <command>) | (msg <jid> <message text>) | (presence <jid> <online|offline>)" #define RAYO_API_SYNTAX "status | (<alias> <jid>) | (cmd <jid> <command>) | (msg <jid> <message text>) | (presence <jid> <online|offline>)"
SWITCH_STANDARD_API(rayo_api) SWITCH_STANDARD_API(rayo_api)
{ {
const char *alias;
char *cmd_dup = strdup(cmd);
char *argv[2] = { 0 };
int success = 0; int success = 0;
if (!strncmp("status", cmd, 6)) {
success = dump_api(cmd + 6, stream); switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0]));
} else if (!strncmp("cmd", cmd, 3)) {
success = command_api(cmd + 3, stream); /* check if a command alias */
} else if (!strncmp("msg", cmd, 3)) { alias = switch_core_hash_find(globals.cmd_aliases, argv[0]);
success = message_api(cmd + 3, stream);
} else if (!strncmp("presence", cmd, 8)) { if (!zstr(alias)) {
success = presence_api(cmd + 8, stream); success = alias_api(alias, argv[1], stream);
} else if (!strcmp("cmd", argv[0])) {
success = command_api(argv[1], stream);
} else if (!strcmp("status", argv[0])) {
success = dump_api(argv[1], stream);
} else if (!strcmp("msg", argv[0])) {
success = message_api(argv[1], stream);
} else if (!strcmp("presence", argv[0])) {
success = presence_api(argv[1], stream);
} }
if (!success) { if (!success) {
stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_API_SYNTAX); stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_API_SYNTAX);
} }
free(cmd_dup);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
/**
* Console auto-completion for actors given validation function
*/
static switch_status_t list_actors(const char *line, const char *cursor, switch_console_callback_match_t **matches, rayo_actor_match_fn match)
{
switch_hash_index_t *hi;
void *val;
const void *vvar;
switch_console_callback_match_t *my_matches = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
struct rayo_actor *actor;
switch_mutex_lock(globals.actors_mutex);
for (hi = switch_hash_first(NULL, globals.actors); hi; hi = switch_hash_next(hi)) {
switch_hash_this(hi, &vvar, NULL, &val);
actor = (struct rayo_actor *) val;
if (match(actor)) {
switch_console_push_match(&my_matches, (const char *) vvar);
}
}
switch_mutex_unlock(globals.actors_mutex);
if (my_matches) {
*matches = my_matches;
status = SWITCH_STATUS_SUCCESS;
}
return status;
}
/**
* @return true if internal actor
*/
static switch_bool_t is_internal_actor(struct rayo_actor *actor)
{
return strcmp(RAT_CLIENT, actor->type) && strcmp(RAT_PEER_SERVER, actor->type);
}
/** /**
* Console auto-completion for all internal actors * Console auto-completion for all internal actors
*/ */
switch_status_t list_internal(const char *line, const char *cursor, switch_console_callback_match_t **matches) static switch_status_t list_internal(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{ {
switch_hash_index_t *hi; return list_actors(line, cursor, matches, is_internal_actor);
void *val; }
const void *vvar;
switch_console_callback_match_t *my_matches = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
struct rayo_actor *actor;
switch_mutex_lock(globals.actors_mutex); /**
for (hi = switch_hash_first(NULL, globals.actors); hi; hi = switch_hash_next(hi)) { * @return true if external actor
switch_hash_this(hi, &vvar, NULL, &val); */
static switch_bool_t is_external_actor(struct rayo_actor *actor)
actor = (struct rayo_actor *) val; {
if (strcmp(RAT_CLIENT, actor->type) && strcmp(RAT_PEER_SERVER, actor->type)) { return !strcmp(RAT_CLIENT, actor->type) || !strcmp(RAT_PEER_SERVER, actor->type);
switch_console_push_match(&my_matches, (const char *) vvar);
}
}
switch_mutex_unlock(globals.actors_mutex);
if (my_matches) {
*matches = my_matches;
status = SWITCH_STATUS_SUCCESS;
}
return status;
} }
/** /**
* Console auto-completion for all external actors * Console auto-completion for all external actors
*/ */
switch_status_t list_external(const char *line, const char *cursor, switch_console_callback_match_t **matches) static switch_status_t list_external(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{ {
switch_hash_index_t *hi; return list_actors(line, cursor, matches, is_external_actor);
void *val; }
const void *vvar;
switch_console_callback_match_t *my_matches = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
struct rayo_actor *actor;
switch_mutex_lock(globals.actors_mutex); /**
for (hi = switch_hash_first(NULL, globals.actors); hi; hi = switch_hash_next(hi)) { * @return true
switch_hash_this(hi, &vvar, NULL, &val); */
static switch_bool_t is_any_actor(struct rayo_actor *actor)
actor = (struct rayo_actor *) val; {
if (!strcmp(RAT_CLIENT, actor->type) || !strcmp(RAT_PEER_SERVER, actor->type)) { return SWITCH_TRUE;
switch_console_push_match(&my_matches, (const char *) vvar);
}
}
switch_mutex_unlock(globals.actors_mutex);
if (my_matches) {
*matches = my_matches;
status = SWITCH_STATUS_SUCCESS;
}
return status;
} }
/** /**
* Console auto-completion for all actors * Console auto-completion for all actors
*/ */
switch_status_t list_all(const char *line, const char *cursor, switch_console_callback_match_t **matches) static switch_status_t list_all(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{ {
switch_hash_index_t *hi; return list_actors(line, cursor, matches, is_any_actor);
void *val;
const void *vvar;
switch_console_callback_match_t *my_matches = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
switch_mutex_lock(globals.actors_mutex);
for (hi = switch_hash_first(NULL, globals.actors); hi; hi = switch_hash_next(hi)) {
switch_hash_this(hi, &vvar, NULL, &val);
switch_console_push_match(&my_matches, (const char *) vvar);
}
switch_mutex_unlock(globals.actors_mutex);
if (my_matches) {
*matches = my_matches;
status = SWITCH_STATUS_SUCCESS;
}
return status;
} }
/** /**
* Add an alias to an API command * @return true if a server
* @param alias_name
* @param alias_cmd
*/ */
static void rayo_add_cmd_alias(const char *alias_name, const char *alias_cmd) static switch_bool_t is_server_actor(struct rayo_actor *actor)
{ {
char *cmd = switch_core_sprintf(globals.pool, "add rayo cmd ::rayo::list_actors %s", alias_name); return !strcmp(RAT_SERVER, actor->type);
switch_console_set_complete(cmd); }
switch_core_hash_insert(globals.cmd_aliases, alias_name, alias_cmd);
/**
* Console auto-completion for all servers
*/
static switch_status_t list_server(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
return list_actors(line, cursor, matches, is_server_actor);
}
/**
* @return true if a call
*/
static switch_bool_t is_call_actor(struct rayo_actor *actor)
{
return !strcmp(RAT_CALL, actor->type);
}
/**
* Console auto-completion for all calls
*/
static switch_status_t list_call(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
return list_actors(line, cursor, matches, is_call_actor);
}
/**
* @return true if a component
*/
switch_bool_t is_component_actor(struct rayo_actor *actor)
{
return !strncmp(RAT_COMPONENT, actor->type, strlen(RAT_COMPONENT));
}
/**
* Console auto-completion for all components
*/
static switch_status_t list_component(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
return list_actors(line, cursor, matches, is_component_actor);
}
/**
* @return true if a record component
*/
static switch_bool_t is_record_actor(struct rayo_actor *actor)
{
return is_component_actor(actor) && !strcmp(actor->subtype, "record");
}
/**
* Console auto-completion for all components
*/
static switch_status_t list_record(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
return list_actors(line, cursor, matches, is_record_actor);
}
/**
* @return true if an output component
*/
static switch_bool_t is_output_actor(struct rayo_actor *actor)
{
return is_component_actor(actor) && !strcmp(actor->subtype, "output");
}
/**
* Console auto-completion for all components
*/
static switch_status_t list_output(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
return list_actors(line, cursor, matches, is_output_actor);
}
/**
* @return true if an input component
*/
static switch_bool_t is_input_actor(struct rayo_actor *actor)
{
return is_component_actor(actor) && !strcmp(actor->subtype, "input");
}
/**
* Console auto-completion for all components
*/
static switch_status_t list_input(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
return list_actors(line, cursor, matches, is_input_actor);
} }
/** /**
@ -3708,98 +3858,20 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load)
globals.console = rayo_console_client_create(); globals.console = rayo_console_client_create();
switch_console_set_complete("add rayo status"); switch_console_set_complete("add rayo status");
switch_console_set_complete("add rayo cmd ::rayo::list_internal");
switch_console_set_complete("add rayo msg ::rayo::list_external"); switch_console_set_complete("add rayo msg ::rayo::list_external");
switch_console_set_complete("add rayo presence ::rayo::list_all online"); switch_console_set_complete("add rayo cmd ::rayo::list_all");
switch_console_set_complete("add rayo presence ::rayo::list_all offline"); switch_console_set_complete("add rayo presence ::rayo::list_server online");
switch_console_set_complete("add rayo presence ::rayo::list_server offline");
switch_console_add_complete_func("::rayo::list_all", list_all);
switch_console_add_complete_func("::rayo::list_internal", list_internal); switch_console_add_complete_func("::rayo::list_internal", list_internal);
switch_console_add_complete_func("::rayo::list_external", list_external); switch_console_add_complete_func("::rayo::list_external", list_external);
switch_console_add_complete_func("::rayo::list_all", list_all); switch_console_add_complete_func("::rayo::list_server", list_server);
switch_console_add_complete_func("::rayo::list_call", list_call);
switch_console_add_complete_func("::rayo::list_component", list_component);
switch_console_add_complete_func("::rayo::list_record", list_record);
switch_console_add_complete_func("::rayo::list_output", list_output);
switch_console_add_complete_func("::rayo::list_input", list_input);
rayo_add_cmd_alias("ping", "<iq type=\"get\"><ping xmlns=\""IKS_NS_XMPP_PING"\"/></iq>");
rayo_add_cmd_alias("answer", "<answer xmlns=\""RAYO_NS"\"/>");
rayo_add_cmd_alias("hangup", "<hangup xmlns=\""RAYO_NS"\"/>");
rayo_add_cmd_alias("stop", "<stop xmlns=\""RAYO_EXT_NS"\"/>");
rayo_add_cmd_alias("pause", "<pause xmlns=\""RAYO_OUTPUT_NS"\"/>");
rayo_add_cmd_alias("resume", "<resume xmlns=\""RAYO_OUTPUT_NS"\"/>");
rayo_add_cmd_alias("speed-up", "<speed-up xmlns=\""RAYO_OUTPUT_NS"\"/>");
rayo_add_cmd_alias("speed-down", "<speed-down xmlns=\""RAYO_OUTPUT_NS"\"/>");
rayo_add_cmd_alias("volume-up", "<volume-up xmlns=\""RAYO_OUTPUT_NS"\"/>");
rayo_add_cmd_alias("volume-down", "<volume-down xmlns=\""RAYO_OUTPUT_NS"\"/>");
rayo_add_cmd_alias("record", "<record xmlns=\""RAYO_RECORD_NS"\"/>");
rayo_add_cmd_alias("record_pause", "<pause xmlns=\""RAYO_RECORD_NS"\"/>");
rayo_add_cmd_alias("record_resume", "<resume xmlns=\""RAYO_RECORD_NS"\"/>");
rayo_add_cmd_alias("prompt_barge", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"true\">"
"<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-times=\"5\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><p>Please press a digit.</p></speak>]]></document></output>"
"<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
"<grammar content-type=\"application/srgs+xml\">"
"<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digit\" scope=\"public\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]>"
"</grammar></input>"
"</prompt>");
rayo_add_cmd_alias("prompt_no_barge", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"false\">"
"<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-times=\"2\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><p>Please press a digit.</p></speak>]]></document></output>"
"<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
"<grammar content-type=\"application/srgs+xml\">"
"<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digit\" scope=\"public\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]>"
"</grammar></input>"
"</prompt>");
rayo_add_cmd_alias("prompt_long", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"true\">"
"<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-times=\"100\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><audio src=\"http://phono.com/audio/troporocks.mp3\"/></speak>]]></document></output>"
"<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
"<grammar content-type=\"application/srgs+xml\">"
"<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digit\" scope=\"public\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></rule></grammar>]]>"
"</grammar></input>"
"</prompt>");
rayo_add_cmd_alias("prompt_multi_digit", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"true\">"
"<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-times=\"100\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><audio src=\"http://phono.com/audio/troporocks.mp3\"/></speak>]]></document></output>"
"<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
"<grammar content-type=\"application/srgs+xml\">"
"<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digits\" scope=\"public\"><item repeat=\"1-4\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]>"
"</grammar></input>"
"</prompt>");
rayo_add_cmd_alias("prompt_terminator", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"true\">"
"<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-times=\"100\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><audio src=\"http://phono.com/audio/troporocks.mp3\"/></speak>]]></document></output>"
"<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\" terminator=\"#\">"
"<grammar content-type=\"application/srgs+xml\">"
"<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digits\" scope=\"public\"><item repeat=\"1-4\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]>"
"</grammar></input>"
"</prompt>");
rayo_add_cmd_alias("prompt_input_bad", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"true\">"
"<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-times=\"100\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><audio src=\"http://phono.com/audio/troporocks.mp3\"/></speak>]]></document></output>"
"<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
"<grammar content-type=\"application/srgs+xml\">"
"<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digits\" scope=\"public\"><item repeat=\"4\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]>"
"</grammar></input>"
"</prompt>");
rayo_add_cmd_alias("prompt_output_bad", "<prompt xmlns=\""RAYO_PROMPT_NS"\" barge-in=\"true\">"
"<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-time=\"100\"><document content-type=\"application/ssml+xml\"><![CDATA[<speak><audio src=\"http://phono.com/audio/troporocks.mp3\"/></speak>]]></document></output>"
"<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
"<grammar content-type=\"application/srgs+xml\">"
"<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digits\" scope=\"public\"><item repeat=\"4\"><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item></one-of></item></rule></grammar>]]>"
"</grammar></input>"
"</prompt>");
rayo_add_cmd_alias("input", "<input xmlns=\""RAYO_INPUT_NS"\" mode=\"dtmf\" initial-timeout=\"5000\" inter-digit-timeout=\"3000\">"
"<grammar content-type=\"application/srgs+xml\">"
"<![CDATA[<grammar mode=\"dtmf\"><rule id=\"digits\" scope=\"public\"><item><one-of><item>0</item><item>1</item><item>2</item><item>3</item><item>4</item><item>5</item><item>6</item><item>7</item><item>8</item><item>9</item><item>*</item><item>#</item></one-of></item></rule></grammar>]]>"
"</grammar></input>");
rayo_add_cmd_alias("output_bad",
"<output xmlns=\""RAYO_OUTPUT_NS"\" repeat-time=\"100\"></output>");
rayo_add_cmd_alias("join_mixer_duplex",
"<join xmlns=\""RAYO_NS"\" mixer-name=\"test\" direction=\"duplex\"/>");
rayo_add_cmd_alias("join_mixer_send",
"<join xmlns=\""RAYO_NS"\" mixer-name=\"test\" direction=\"send\"/>");
rayo_add_cmd_alias("join_mixer_recv",
"<join xmlns=\""RAYO_NS"\" mixer-name=\"test\" direction=\"recv\"/>");
rayo_add_cmd_alias("unjoin_mixer",
"<unjoin xmlns=\""RAYO_NS"\" mixer-name=\"test\"/>");
rayo_add_cmd_alias("unjoin",
"<unjoin xmlns=\""RAYO_NS"\"/>");
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
@ -3808,9 +3880,15 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load)
*/ */
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_rayo_shutdown) SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_rayo_shutdown)
{ {
switch_console_del_complete_func("::rayo::list_all");
switch_console_del_complete_func("::rayo::list_internal"); switch_console_del_complete_func("::rayo::list_internal");
switch_console_del_complete_func("::rayo::list_external"); switch_console_del_complete_func("::rayo::list_external");
switch_console_del_complete_func("::rayo::list_all"); switch_console_del_complete_func("::rayo::list_server");
switch_console_del_complete_func("::rayo::list_call");
switch_console_del_complete_func("::rayo::list_component");
switch_console_del_complete_func("::rayo::list_record");
switch_console_del_complete_func("::rayo::list_output");
switch_console_del_complete_func("::rayo::list_input");
switch_console_set_complete("del rayo"); switch_console_set_complete("del rayo");
/* stop XMPP streams */ /* stop XMPP streams */

View File

@ -51,9 +51,6 @@
#define RAT_PEER_SERVER "PEER_SERVER" #define RAT_PEER_SERVER "PEER_SERVER"
#define RAT_CLIENT "CLIENT" #define RAT_CLIENT "CLIENT"
/* these are support punchblock.. undefine once punchblock is fixed */
#define RAYO_UUID_IN_REF_URI
struct rayo_actor; struct rayo_actor;
struct rayo_call; struct rayo_call;
struct rayo_mixer; struct rayo_mixer;
@ -162,6 +159,7 @@ extern const char *rayo_call_get_dcp_jid(struct rayo_call *call);
#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__) #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); 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);
extern switch_bool_t is_component_actor(struct rayo_actor *);
typedef iks *(*rayo_actor_xmpp_handler)(struct rayo_actor *, struct rayo_message *, void *); typedef iks *(*rayo_actor_xmpp_handler)(struct rayo_actor *, struct rayo_message *, void *);
extern void rayo_actor_command_handler_add(const char *type, const char *subtype, const char *name, rayo_actor_xmpp_handler fn); extern void rayo_actor_command_handler_add(const char *type, const char *subtype, const char *name, rayo_actor_xmpp_handler fn);

View File

@ -40,7 +40,7 @@
struct rayo_component *rayo_component_locate(const char *id, const char *file, int line) struct rayo_component *rayo_component_locate(const char *id, const char *file, int line)
{ {
struct rayo_actor *actor = rayo_actor_locate_by_id(id, file, line); struct rayo_actor *actor = rayo_actor_locate_by_id(id, file, line);
if (actor && !strncmp(RAT_COMPONENT, actor->type, strlen(RAT_COMPONENT))) { if (actor && is_component_actor(actor)) {
return RAYO_COMPONENT(actor); return RAYO_COMPONENT(actor);
} else if (actor) { } else if (actor) {
RAYO_UNLOCK(actor); RAYO_UNLOCK(actor);
@ -58,11 +58,7 @@ void rayo_component_send_start(struct rayo_component *component, iks *iq)
iks *response = iks_new_iq_result(iq); iks *response = iks_new_iq_result(iq);
iks *ref = iks_insert(response, "ref"); iks *ref = iks_insert(response, "ref");
iks_insert_attrib(ref, "xmlns", RAYO_NS); iks_insert_attrib(ref, "xmlns", RAYO_NS);
#ifdef RAYO_UUID_IN_REF_URI
iks_insert_attrib(ref, "uri", component->ref);
#else
iks_insert_attrib_printf(ref, "uri", "xmpp:%s", RAYO_JID(component)); iks_insert_attrib_printf(ref, "uri", "xmpp:%s", RAYO_JID(component));
#endif
RAYO_SEND_REPLY(component, iks_find_attrib(response, "to"), response); RAYO_SEND_REPLY(component, iks_find_attrib(response, "to"), response);
} }
@ -227,15 +223,10 @@ void rayo_component_api_execute_async(struct rayo_component *component, const ch
*/ */
switch_status_t rayo_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) switch_status_t rayo_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file)
{ {
rayo_input_component_load(); if (rayo_input_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
rayo_output_component_load(module_interface, pool); rayo_output_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
rayo_prompt_component_load(); rayo_prompt_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
rayo_record_component_load(pool, config_file); rayo_record_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS) {
if (rayo_input_component_load() != SWITCH_STATUS_SUCCESS ||
rayo_output_component_load(module_interface, pool) != SWITCH_STATUS_SUCCESS ||
rayo_prompt_component_load() != SWITCH_STATUS_SUCCESS ||
rayo_record_component_load(pool, config_file) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_TERM; return SWITCH_STATUS_TERM;
} }
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;

View File

@ -54,10 +54,10 @@
#define COMPONENT_COMPLETE_HANGUP "hangup", RAYO_EXT_COMPLETE_NS #define COMPONENT_COMPLETE_HANGUP "hangup", RAYO_EXT_COMPLETE_NS
extern switch_status_t rayo_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); extern switch_status_t rayo_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
extern switch_status_t rayo_input_component_load(void); extern switch_status_t rayo_input_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
extern switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool); extern switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
extern switch_status_t rayo_prompt_component_load(void); extern switch_status_t rayo_prompt_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
extern switch_status_t rayo_record_component_load(switch_memory_pool_t *pool, const char *config_file); extern switch_status_t rayo_record_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
extern switch_status_t rayo_components_shutdown(void); extern switch_status_t rayo_components_shutdown(void);
extern switch_status_t rayo_input_component_shutdown(void); extern switch_status_t rayo_input_component_shutdown(void);

View File

@ -33,7 +33,7 @@
*/ */
ELEMENT(RAYO_INPUT) ELEMENT(RAYO_INPUT)
STRING_ATTRIB(mode, any, "any,dtmf,voice") STRING_ATTRIB(mode, any, "any,dtmf,voice")
ATTRIB(terminator,, any) OPTIONAL_ATTRIB(terminator,, dtmf_digit)
ATTRIB(recognizer,, any) ATTRIB(recognizer,, any)
ATTRIB(language, en-US, any) ATTRIB(language, en-US, any)
ATTRIB(initial-timeout, -1, positive_or_neg_one) ATTRIB(initial-timeout, -1, positive_or_neg_one)

View File

@ -45,6 +45,8 @@ struct input_handler;
static struct { static struct {
/** grammar parser */ /** grammar parser */
struct srgs_parser *parser; struct srgs_parser *parser;
/** default recognizer to use if none specified */
const char *default_recognizer;
} globals; } globals;
/** /**
@ -57,8 +59,8 @@ struct input_component {
int speech_mode; int speech_mode;
/** Number of collected digits */ /** Number of collected digits */
int num_digits; int num_digits;
/** Terminating digits */ /** Terminating digit */
int term_digit_mask; char term_digit;
/** The collected digits */ /** The collected digits */
char digits[MAX_DTMF + 1]; char digits[MAX_DTMF + 1];
/** grammar to match */ /** grammar to match */
@ -70,7 +72,9 @@ struct input_component {
/** maximum silence allowed */ /** maximum silence allowed */
int max_silence; int max_silence;
/** minimum speech detection confidence */ /** minimum speech detection confidence */
int min_confidence; double min_confidence;
/** sensitivity to background noise */
double sensitivity;
/** timeout after first digit is received */ /** timeout after first digit is received */
int inter_digit_timeout; int inter_digit_timeout;
/** stop flag */ /** stop flag */
@ -79,6 +83,10 @@ struct input_component {
int start_timers; int start_timers;
/** true if event fired for first digit / start of speech */ /** true if event fired for first digit / start of speech */
int barge_event; int barge_event;
/** optional language to use */
const char *language;
/** optional recognizer to use */
const char *recognizer;
/** global data */ /** global data */
struct input_handler *handler; struct input_handler *handler;
}; };
@ -91,77 +99,24 @@ struct input_component {
struct input_handler { struct input_handler {
/** media bug to monitor frames / control input lifecycle */ /** media bug to monitor frames / control input lifecycle */
switch_media_bug_t *bug; switch_media_bug_t *bug;
/** active input component - TODO multiple inputs */ /** active voice input component */
struct input_component *component; struct input_component *voice_component;
/** active dtmf input component */
struct input_component *dtmf_component;
/** synchronizes media bug and dtmf callbacks */ /** synchronizes media bug and dtmf callbacks */
switch_mutex_t *mutex; switch_mutex_t *mutex;
/** last recognizer used */
const char *last_recognizer;
}; };
/** /**
* @return digit mask * @param digit1 to match
* @param digit2 to match
* @return true if matching
*/ */
static int get_digit_mask(char digit) static int digit_test(char digit1, char digit2)
{ {
switch(digit) { return digit1 && digit2 && tolower(digit1) == tolower(digit2);
case '0': return 1;
case '1': return 1 << 1;
case '2': return 1 << 2;
case '3': return 1 << 3;
case '4': return 1 << 4;
case '5': return 1 << 5;
case '6': return 1 << 6;
case '7': return 1 << 7;
case '8': return 1 << 8;
case '9': return 1 << 9;
case 'A':
case 'a': return 1 << 10;
case 'B':
case 'b': return 1 << 11;
case 'C':
case 'c': return 1 << 12;
case 'D':
case 'd': return 1 << 13;
case '#': return 1 << 14;
case '*': return 1 << 15;
}
return 0;
}
/**
* @param digit_mask to check
* @param digit to look for
* @return true if set
*/
static int digit_mask_test(int digit_mask, char digit)
{
return digit_mask & get_digit_mask(digit);
}
/**
* @param digit_mask to set digit in
* @param digit to set
* @return the digit mask with the set digit
*/
static int digit_mask_set(int digit_mask, char digit)
{
return digit_mask | get_digit_mask(digit);
}
/**
* @param digit_mask to set digits in
* @param digits to add to mask
* @return the digit mask with the set digits
*/
static int digit_mask_set_from_digits(int digit_mask, const char *digits)
{
if (!zstr(digits)) {
int digits_len = strlen(digits);
int i;
for (i = 0; i < digits_len; i++) {
digit_mask = digit_mask_set(digit_mask, digits[i]);
}
}
return digit_mask;
} }
/** /**
@ -205,15 +160,14 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c
switch_mutex_lock(handler->mutex); switch_mutex_lock(handler->mutex);
component = handler->component; component = handler->dtmf_component;
/* additional paranoia check */ /* additional paranoia check */
if (!component) { if (!component) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Received DTMF without active input component\n");
switch_mutex_unlock(handler->mutex); switch_mutex_unlock(handler->mutex);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
is_term_digit = digit_mask_test(component->term_digit_mask, dtmf->digit); is_term_digit = digit_test(component->term_digit, dtmf->digit);
if (!is_term_digit) { if (!is_term_digit) {
component->digits[component->num_digits] = dtmf->digit; component->digits[component->num_digits] = dtmf->digit;
@ -247,7 +201,7 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c
} }
case SMT_NO_MATCH: { case SMT_NO_MATCH: {
/* notify of no-match and remove input component */ /* notify of no-match and remove input component */
handler->component = NULL; handler->dtmf_component = NULL;
switch_core_media_bug_remove(session, &handler->bug); switch_core_media_bug_remove(session, &handler->bug);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "NO MATCH = %s\n", component->digits); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "NO MATCH = %s\n", component->digits);
rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOMATCH); rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOMATCH);
@ -256,7 +210,7 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c
case SMT_MATCH_END: { case SMT_MATCH_END: {
iks *result = nlsml_create_dtmf_match(component->digits); iks *result = nlsml_create_dtmf_match(component->digits);
/* notify of match and remove input component */ /* notify of match and remove input component */
handler->component = NULL; handler->dtmf_component = NULL;
switch_core_media_bug_remove(session, &handler->bug); switch_core_media_bug_remove(session, &handler->bug);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "MATCH = %s\n", component->digits); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "MATCH = %s\n", component->digits);
send_match_event(RAYO_COMPONENT(component), result); send_match_event(RAYO_COMPONENT(component), result);
@ -279,7 +233,7 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void
struct input_component *component; struct input_component *component;
switch_mutex_lock(handler->mutex); switch_mutex_lock(handler->mutex);
component = handler->component; component = handler->dtmf_component;
switch(type) { switch(type) {
case SWITCH_ABC_TYPE_INIT: { case SWITCH_ABC_TYPE_INIT: {
@ -294,7 +248,7 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void
int elapsed_ms = (switch_micro_time_now() - component->last_digit_time) / 1000; int elapsed_ms = (switch_micro_time_now() - component->last_digit_time) / 1000;
if (component->num_digits && component->inter_digit_timeout > 0 && elapsed_ms > component->inter_digit_timeout) { if (component->num_digits && component->inter_digit_timeout > 0 && elapsed_ms > component->inter_digit_timeout) {
enum srgs_match_type match; enum srgs_match_type match;
handler->component = NULL; handler->dtmf_component = NULL;
switch_core_media_bug_set_flag(bug, SMBF_PRUNE); switch_core_media_bug_set_flag(bug, SMBF_PRUNE);
/* we got some input, check for match */ /* we got some input, check for match */
@ -310,7 +264,7 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void
rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOMATCH); rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOMATCH);
} }
} else if (!component->num_digits && component->initial_timeout > 0 && elapsed_ms > component->initial_timeout) { } else if (!component->num_digits && component->initial_timeout > 0 && elapsed_ms > component->initial_timeout) {
handler->component = NULL; handler->dtmf_component = NULL;
switch_core_media_bug_set_flag(bug, SMBF_PRUNE); switch_core_media_bug_set_flag(bug, SMBF_PRUNE);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "initial-timeout\n"); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "initial-timeout\n");
rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOINPUT); rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOINPUT);
@ -323,10 +277,10 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void
/* check for hangup */ /* check for hangup */
if (component) { if (component) {
if (component->stop) { if (component->stop) {
handler->component = NULL; handler->dtmf_component = NULL;
rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP); rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP);
} else { } else {
handler->component = NULL; handler->dtmf_component = NULL;
rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_HANGUP); rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_HANGUP);
} }
} }
@ -396,24 +350,45 @@ static iks *start_call_input(struct input_component *component, switch_core_sess
handler = switch_core_session_alloc(session, sizeof(*handler)); handler = switch_core_session_alloc(session, sizeof(*handler));
switch_mutex_init(&handler->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); switch_mutex_init(&handler->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
switch_channel_set_private(switch_core_session_get_channel(session), RAYO_INPUT_COMPONENT_PRIVATE_VAR, handler); switch_channel_set_private(switch_core_session_get_channel(session), RAYO_INPUT_COMPONENT_PRIVATE_VAR, handler);
handler->last_recognizer = "";
} }
handler->component = component;
/* TODO break up this function by mode... dtmf/voice/fax/etc */
component->speech_mode = strcmp(iks_find_attrib_soft(input, "mode"), "dtmf");
if (component->speech_mode && handler->voice_component) {
/* don't allow multi voice input */
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 */
return iks_new_error_detailed(iq, STANZA_ERROR_CONFLICT, "Multiple dtmf input is not allowed");
}
if (component->speech_mode) {
handler->voice_component = component;
} else {
handler->dtmf_component = component;
}
component->grammar = NULL;
component->num_digits = 0; component->num_digits = 0;
component->digits[0] = '\0'; component->digits[0] = '\0';
component->stop = 0; component->stop = 0;
component->speech_mode = 0;
component->initial_timeout = iks_find_int_attrib(input, "initial-timeout"); component->initial_timeout = iks_find_int_attrib(input, "initial-timeout");
component->inter_digit_timeout = iks_find_int_attrib(input, "inter-digit-timeout"); component->inter_digit_timeout = iks_find_int_attrib(input, "inter-digit-timeout");
component->max_silence = iks_find_int_attrib(input, "max-silence"); component->max_silence = iks_find_int_attrib(input, "max-silence");
component->min_confidence = (int)ceil(iks_find_decimal_attrib(input, "min-confidence") * 100.0); component->min_confidence = iks_find_decimal_attrib(input, "min-confidence");
component->sensitivity = iks_find_decimal_attrib(input, "sensitivity");
component->barge_event = iks_find_bool_attrib(input, "barge-event"); component->barge_event = iks_find_bool_attrib(input, "barge-event");
component->start_timers = iks_find_bool_attrib(input, "start-timers"); component->start_timers = iks_find_bool_attrib(input, "start-timers");
/* TODO this should just be a single digit terminator? */ component->term_digit = iks_find_char_attrib(input, "terminator");
component->term_digit_mask = digit_mask_set_from_digits(0, iks_find_attrib_soft(input, "terminator")); component->recognizer = iks_find_attrib(input, "recognizer");
/* TODO recognizer ignored */ component->language = iks_find_attrib(input, "language");
/* TODO language ignored */
component->handler = handler; component->handler = handler;
/* is this voice or dtmf srgs grammar? */
if (!component->speech_mode) {
/* parse the grammar */ /* parse the grammar */
if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) { if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Failed to parse grammar body\n"); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Failed to parse grammar body\n");
@ -422,8 +397,6 @@ static iks *start_call_input(struct input_component *component, switch_core_sess
return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Failed to parse grammar body"); return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Failed to parse grammar body");
} }
/* is this voice or dtmf srgs grammar? */
if (!strcasecmp("dtmf", iks_find_attrib_soft(input, "mode"))) {
component->last_digit_time = switch_micro_time_now(); component->last_digit_time = switch_micro_time_now();
/* acknowledge command */ /* acknowledge command */
@ -431,38 +404,124 @@ static iks *start_call_input(struct input_component *component, switch_core_sess
/* start dtmf input detection */ /* start dtmf input detection */
if (switch_core_media_bug_add(session, "rayo_input_component", NULL, input_component_bug_callback, handler, 0, SMBF_READ_REPLACE, &handler->bug) != SWITCH_STATUS_SUCCESS) { if (switch_core_media_bug_add(session, "rayo_input_component", NULL, input_component_bug_callback, handler, 0, SMBF_READ_REPLACE, &handler->bug) != SWITCH_STATUS_SUCCESS) {
handler->dtmf_component = NULL;
rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR); rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR);
} }
} else { } else {
char *grammar = NULL; switch_stream_handle_t grammar = { 0 };
const char *jsgf_path; SWITCH_STANDARD_STREAM(grammar);
component->speech_mode = 1;
jsgf_path = srgs_grammar_to_jsgf_file(component->grammar, SWITCH_GLOBAL_dirs.grammar_dir, "gram"); if (zstr(component->recognizer)) {
if (!jsgf_path) { component->recognizer = globals.default_recognizer;
}
/* if recognition engine is different, we can't handle this request */
if (!zstr(handler->last_recognizer) && strcmp(component->recognizer, handler->last_recognizer)) {
handler->voice_component = NULL;
RAYO_UNLOCK(component); RAYO_UNLOCK(component);
RAYO_DESTROY(component); RAYO_DESTROY(component);
return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Grammar error"); return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Must use the same recognizer for the entire call");
}
handler->last_recognizer = switch_core_session_strdup(session, component->recognizer);
if (!strcmp(component->recognizer, "pocketsphinx")) {
const char *jsgf_path;
/* transform SRGS grammar to JSGF */
if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Failed to parse grammar body\n");
handler->voice_component = NULL;
RAYO_UNLOCK(component);
RAYO_DESTROY(component);
return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Failed to parse grammar body");
}
jsgf_path = srgs_grammar_to_jsgf_file(component->grammar, SWITCH_GLOBAL_dirs.grammar_dir, "gram");
if (!jsgf_path) {
handler->voice_component = NULL;
RAYO_UNLOCK(component);
RAYO_DESTROY(component);
return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Grammar conversion to JSGF error");
}
/* build pocketsphinx grammar string */
grammar.write_function(&grammar,
"{start-input-timers=%s,no-input-timeout=%d,speech-timeout=%d,confidence-threshold=%d}%s",
component->start_timers ? "true" : "false",
component->initial_timeout,
component->max_silence,
(int)ceil(component->min_confidence * 100.0),
jsgf_path);
} else if (!strncmp(component->recognizer, "unimrcp", strlen("unimrcp"))) {
/* send inline grammar to unimrcp */
grammar.write_function(&grammar, "{start-input-timers=%s,confidence-threshold=%f,sensitivity-level=%f",
component->start_timers ? "true" : "false",
component->min_confidence,
component->sensitivity);
if (component->initial_timeout > 0) {
grammar.write_function(&grammar, ",no-input-timeout=%d",
component->initial_timeout);
}
if (component->max_silence > 0) {
grammar.write_function(&grammar, ",speech-complete-timeout=%d,speech-incomplete-timeout=%d",
component->max_silence,
component->max_silence);
}
if (!zstr(component->language)) {
grammar.write_function(&grammar, ",speech-language=%s", component->language);
}
if (!strcmp(iks_find_attrib_soft(input, "mode"), "any")) {
/* set dtmf params */
if (component->inter_digit_timeout > 0) {
grammar.write_function(&grammar, ",dtmf-interdigit-timeout=%d", component->inter_digit_timeout);
}
if (component->term_digit) {
grammar.write_function(&grammar, ",dtmf-term-char=%c", component->term_digit);
}
}
grammar.write_function(&grammar, "}inline:%s", iks_find_cdata(input, "grammar"));
} else {
/* passthrough to unknown ASR module */
grammar.write_function(&grammar, "%s", iks_find_cdata(input, "grammar"));
} }
/* acknowledge command */ /* acknowledge command */
rayo_component_send_start(RAYO_COMPONENT(component), iq); rayo_component_send_start(RAYO_COMPONENT(component), iq);
/* TODO configurable speech detection - different engines, grammar passthrough, dtmf handled by recognizer */
grammar = switch_mprintf("{no-input-timeout=%s,speech-timeout=%s,start-input-timers=%s,confidence-threshold=%d}%s",
component->initial_timeout, component->max_silence,
component->start_timers ? "true" : "false",
component->min_confidence, jsgf_path);
/* start speech detection */ /* start speech detection */
switch_channel_set_variable(switch_core_session_get_channel(session), "fire_asr_events", "true"); switch_channel_set_variable(switch_core_session_get_channel(session), "fire_asr_events", "true");
if (switch_ivr_detect_speech(session, "pocketsphinx", grammar, "mod_rayo_grammar", "", NULL) != SWITCH_STATUS_SUCCESS) { if (switch_ivr_detect_speech(session, component->recognizer, grammar.data, "mod_rayo_grammar", "", NULL) != SWITCH_STATUS_SUCCESS) {
handler->voice_component = NULL;
rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR); rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR);
} }
switch_safe_free(grammar); switch_safe_free(grammar.data);
} }
return NULL; return NULL;
} }
/**
* Create input component id for session.
* @param session requesting component
* @param input request
* @return the ID
*/
static char *create_input_component_id(switch_core_session_t *session, iks *input)
{
const char *mode = "unk";
if (input) {
mode = iks_find_attrib_soft(input, "mode");
if (!strcmp(mode, "any")) {
mode = "voice";
}
}
return switch_core_session_sprintf(session, "%s-input-%s", switch_core_session_get_uuid(session), mode);
}
/** /**
* Start execution of input component * Start execution of input component
*/ */
@ -470,10 +529,10 @@ static iks *start_call_input_component(struct rayo_actor *call, struct rayo_mess
{ {
iks *iq = msg->payload; iks *iq = msg->payload;
switch_core_session_t *session = (switch_core_session_t *)session_data; switch_core_session_t *session = (switch_core_session_t *)session_data;
char *component_id = switch_mprintf("%s-input", switch_core_session_get_uuid(session)); iks *input = iks_find(iq, "input");
char *component_id = create_input_component_id(session, input);
switch_memory_pool_t *pool = NULL; switch_memory_pool_t *pool = NULL;
struct input_component *input_component = NULL; struct input_component *input_component = NULL;
iks *input = iks_find(iq, "input");
const char *error = NULL; const char *error = NULL;
if (!validate_call_input(input, &error)) { if (!validate_call_input(input, &error)) {
@ -484,7 +543,6 @@ static iks *start_call_input_component(struct rayo_actor *call, struct rayo_mess
switch_core_new_memory_pool(&pool); switch_core_new_memory_pool(&pool);
input_component = switch_core_alloc(pool, sizeof(*input_component)); 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")); rayo_component_init(RAYO_COMPONENT(input_component), pool, RAT_CALL_COMPONENT, "input", component_id, call, iks_find_attrib(iq, "from"));
switch_safe_free(component_id);
/* start input */ /* start input */
return start_call_input(input_component, session, iks_find(iq, "input"), iq, NULL, 0); return start_call_input(input_component, session, iks_find(iq, "input"), iq, NULL, 0);
@ -530,7 +588,6 @@ static iks *start_timers_call_input_component(struct rayo_actor *component, stru
switch_mutex_lock(input_component->handler->mutex); switch_mutex_lock(input_component->handler->mutex);
if (input_component->speech_mode) { if (input_component->speech_mode) {
switch_ivr_detect_speech_start_input_timers(session); switch_ivr_detect_speech_start_input_timers(session);
rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP);
} else { } else {
input_component->last_digit_time = switch_micro_time_now(); input_component->last_digit_time = switch_micro_time_now();
input_component->start_timers = 1; input_component->start_timers = 1;
@ -549,24 +606,28 @@ static void on_detected_speech_event(switch_event_t *event)
{ {
const char *speech_type = switch_event_get_header(event, "Speech-Type"); const char *speech_type = switch_event_get_header(event, "Speech-Type");
char *event_str = NULL; char *event_str = NULL;
const char *uuid = switch_event_get_header(event, "Unique-ID");
switch_event_serialize(event, &event_str, SWITCH_FALSE); switch_event_serialize(event, &event_str, SWITCH_FALSE);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s\n", event_str); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s\n", event_str);
if (!speech_type) { if (!speech_type || !uuid) {
return; return;
} }
if (!strcasecmp("detected-speech", speech_type)) { if (!strcasecmp("detected-speech", speech_type)) {
const char *uuid = switch_event_get_header(event, "Unique-ID"); char *component_id = switch_mprintf("%s-input-voice", uuid);
char *component_id = switch_mprintf("%s-input", uuid);
struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id); struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id);
switch_safe_free(component_id); switch_safe_free(component_id);
if (component) { if (component) {
const char *result = switch_event_get_body(event); const char *result = switch_event_get_body(event);
switch_mutex_lock(INPUT_COMPONENT(component)->handler->mutex); switch_mutex_lock(INPUT_COMPONENT(component)->handler->mutex);
INPUT_COMPONENT(component)->handler->component = NULL; INPUT_COMPONENT(component)->handler->voice_component = NULL;
switch_mutex_unlock(INPUT_COMPONENT(component)->handler->mutex); switch_mutex_unlock(INPUT_COMPONENT(component)->handler->mutex);
if (zstr(result)) { if (zstr(result)) {
rayo_component_send_complete(component, INPUT_NOMATCH); rayo_component_send_complete(component, INPUT_NOMATCH);
} else { } else {
if (strchr(result, '<')) {
/* got an XML result */
enum nlsml_match_type match_type = nlsml_parse(result, uuid); enum nlsml_match_type match_type = nlsml_parse(result, uuid);
switch (match_type) { switch (match_type) {
case NMT_NOINPUT: case NMT_NOINPUT:
@ -590,12 +651,18 @@ static void on_detected_speech_event(switch_event_t *event)
rayo_component_send_complete(component, INPUT_NOMATCH); rayo_component_send_complete(component, INPUT_NOMATCH);
break; break;
} }
} else if (strstr(result, "002")) {
/* Completion-Cause: 002 no-input-timeout */
rayo_component_send_complete(component, INPUT_NOINPUT);
} else {
/* assume no match */
rayo_component_send_complete(component, INPUT_NOMATCH);
}
} }
RAYO_UNLOCK(component); RAYO_UNLOCK(component);
} }
} else if (!strcasecmp("begin-speaking", speech_type)) { } else if (!strcasecmp("begin-speaking", speech_type)) {
const char *uuid = switch_event_get_header(event, "Unique-ID"); char *component_id = switch_mprintf("%s-input-voice", uuid);
char *component_id = switch_mprintf("%s-input", uuid);
struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id); struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id);
switch_safe_free(component_id); switch_safe_free(component_id);
if (component && INPUT_COMPONENT(component)->barge_event) { if (component && INPUT_COMPONENT(component)->barge_event) {
@ -603,14 +670,13 @@ static void on_detected_speech_event(switch_event_t *event)
} }
RAYO_UNLOCK(component); RAYO_UNLOCK(component);
} else if (!strcasecmp("closed", speech_type)) { } else if (!strcasecmp("closed", speech_type)) {
const char *uuid = switch_event_get_header(event, "Unique-ID"); char *component_id = switch_mprintf("%s-input-voice", uuid);
char *component_id = switch_mprintf("%s-input", uuid);
struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id); struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id);
switch_safe_free(component_id); switch_safe_free(component_id);
if (component) { if (component) {
char *channel_state = switch_event_get_header(event, "Channel-State"); char *channel_state = switch_event_get_header(event, "Channel-State");
switch_mutex_lock(INPUT_COMPONENT(component)->handler->mutex); switch_mutex_lock(INPUT_COMPONENT(component)->handler->mutex);
INPUT_COMPONENT(component)->handler->component = NULL; INPUT_COMPONENT(component)->handler->voice_component = NULL;
switch_mutex_unlock(INPUT_COMPONENT(component)->handler->mutex); switch_mutex_unlock(INPUT_COMPONENT(component)->handler->mutex);
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Recognizer closed\n"); switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Recognizer closed\n");
if (channel_state && !strcmp("CS_HANGUP", channel_state)) { if (channel_state && !strcmp("CS_HANGUP", channel_state)) {
@ -625,12 +691,63 @@ static void on_detected_speech_event(switch_event_t *event)
switch_safe_free(event_str); switch_safe_free(event_str);
} }
/**
* Process module XML configuration
* @param pool memory pool to allocate from
* @param config_file to use
* @return SWITCH_STATUS_SUCCESS on successful configuration
*/
static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_file)
{
switch_xml_t cfg, xml;
/* set defaults */
globals.default_recognizer = "pocketsphinx";
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Configuring module\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;
}
/* get params */
{
switch_xml_t settings = switch_xml_child(cfg, "input");
if (settings) {
switch_xml_t param;
for (param = switch_xml_child(settings, "param"); param; param = param->next) {
const char *var = switch_xml_attr_soft(param, "name");
const char *val = switch_xml_attr_soft(param, "value");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "param: %s = %s\n", var, val);
if (!strcasecmp(var, "default-recognizer")) {
if (!zstr(val)) {
globals.default_recognizer = switch_core_strdup(pool, val);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unsupported param: %s\n", var);
}
}
}
}
switch_xml_free(xml);
return SWITCH_STATUS_SUCCESS;
}
/** /**
* Initialize input component * Initialize input component
* @param module_interface
* @param pool memory pool to allocate from
* @param config_file to use
* @return SWITCH_STATUS_SUCCESS if successful * @return SWITCH_STATUS_SUCCESS if successful
*/ */
switch_status_t rayo_input_component_load(void) switch_status_t rayo_input_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file)
{ {
if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_TERM;
}
srgs_init(); srgs_init();
nlsml_init(); nlsml_init();

View File

@ -1092,9 +1092,12 @@ static char *fileman_supported_formats[] = { "fileman", NULL };
/** /**
* Initialize output component * Initialize output component
* @param module_interface
* @param pool memory pool to allocate from
* @param config_file to use
* @return SWITCH_STATUS_SUCCESS if successful * @return SWITCH_STATUS_SUCCESS if successful
*/ */
switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file)
{ {
switch_api_interface_t *api_interface; switch_api_interface_t *api_interface;
switch_file_interface_t *file_interface; switch_file_interface_t *file_interface;

View File

@ -620,9 +620,12 @@ static iks *forward_output_component_request(struct rayo_actor *prompt, struct r
/** /**
* Initialize prompt component * Initialize prompt component
* @param module_interface
* @param pool memory pool to allocate from
* @param config_file to use
* @return SWITCH_STATUS_SUCCESS if successful * @return SWITCH_STATUS_SUCCESS if successful
*/ */
switch_status_t rayo_prompt_component_load(void) switch_status_t rayo_prompt_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file)
{ {
/* Prompt is a convenience component that wraps <input> and <output> */ /* Prompt is a convenience component that wraps <input> and <output> */
rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_PROMPT_NS":prompt", start_call_prompt_component); rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_PROMPT_NS":prompt", start_call_prompt_component);

View File

@ -479,11 +479,12 @@ static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_
/** /**
* Initialize record component * Initialize record component
* @param module_interface
* @param pool memory pool to allocate from * @param pool memory pool to allocate from
* @param config_file to use * @param config_file to use
* @return SWITCH_STATUS_SUCCESS if successful * @return SWITCH_STATUS_SUCCESS if successful
*/ */
switch_status_t rayo_record_component_load(switch_memory_pool_t *pool, const char *config_file) switch_status_t rayo_record_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file)
{ {
if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) { if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_TERM; return SWITCH_STATUS_TERM;

View File

@ -145,6 +145,27 @@ static void test_dialback_key(void)
ASSERT_NULL(iks_server_dialback_key("s3cr3tf0rd14lb4ck", "xmpp.example.com", "example.org", NULL)); ASSERT_NULL(iks_server_dialback_key("s3cr3tf0rd14lb4ck", "xmpp.example.com", "example.org", NULL));
} }
static void test_validate_dtmf(void)
{
ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("1"));
ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("A"));
ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("a"));
ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("D"));
ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("d"));
ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("*"));
ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("#"));
ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit("E"));
ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit(NULL));
ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit(""));
ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit("11"));
ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "A"));
ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "1"));
ASSERT_EQUALS(SWITCH_FALSE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "Z"));
ASSERT_EQUALS(SWITCH_FALSE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "11"));
ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, NULL));
ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, ""));
}
/** /**
* main program * main program
*/ */
@ -159,5 +180,6 @@ int main(int argc, char **argv)
TEST(test_rayo_test_srgs); TEST(test_rayo_test_srgs);
TEST(test_iks_helper_value_matches); TEST(test_iks_helper_value_matches);
TEST(test_dialback_key); TEST(test_dialback_key);
TEST(test_validate_dtmf);
return 0; return 0;
} }

View File

@ -33,6 +33,7 @@
#include <switch.h> #include <switch.h>
#include <switch_console.h> #include <switch_console.h>
#include <switch_version.h> #include <switch_version.h>
#include <switch_private.h>
#define CMD_BUFLEN 1024 #define CMD_BUFLEN 1024
#ifdef SWITCH_HAVE_LIBEDIT #ifdef SWITCH_HAVE_LIBEDIT
@ -619,6 +620,36 @@ SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_loaded_modules(const
return SWITCH_STATUS_FALSE; return SWITCH_STATUS_FALSE;
} }
#ifdef HAVE_GETIFADDRS
#include <ifaddrs.h>
#include <net/if.h>
SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_interfaces(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
struct match_helper h = { 0 };
struct ifaddrs *addrs, *addr;
getifaddrs(&addrs);
for(addr = addrs; addr; addr = addr->ifa_next) {
if (addr->ifa_flags & IFF_UP) {
switch_console_push_match_unique(&h.my_matches, addr->ifa_name);
}
}
freeifaddrs(addrs);
if (h.my_matches) {
*matches = h.my_matches;
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_FALSE;
}
#else
SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_interfaces(const char *line, const char *cursor, switch_console_callback_match_t **matches)
{
return SWITCH_STATUS_FALSE;
}
#endif
static int uuid_callback(void *pArg, int argc, char **argv, char **columnNames) static int uuid_callback(void *pArg, int argc, char **argv, char **columnNames)
{ {
struct match_helper *h = (struct match_helper *) pArg; struct match_helper *h = (struct match_helper *) pArg;
@ -1631,6 +1662,7 @@ SWITCH_DECLARE(switch_status_t) switch_console_init(switch_memory_pool_t *pool)
switch_core_hash_init(&globals.func_hash, pool); switch_core_hash_init(&globals.func_hash, pool);
switch_console_add_complete_func("::console::list_available_modules", (switch_console_complete_callback_t) switch_console_list_available_modules); switch_console_add_complete_func("::console::list_available_modules", (switch_console_complete_callback_t) switch_console_list_available_modules);
switch_console_add_complete_func("::console::list_loaded_modules", (switch_console_complete_callback_t) switch_console_list_loaded_modules); switch_console_add_complete_func("::console::list_loaded_modules", (switch_console_complete_callback_t) switch_console_list_loaded_modules);
switch_console_add_complete_func("::console::list_interfaces", (switch_console_complete_callback_t) switch_console_list_interfaces);
switch_console_add_complete_func("::console::list_uuid", (switch_console_complete_callback_t) switch_console_list_uuid); switch_console_add_complete_func("::console::list_uuid", (switch_console_complete_callback_t) switch_console_list_uuid);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
@ -1741,6 +1773,20 @@ SWITCH_DECLARE(void) switch_console_sort_matches(switch_console_callback_match_t
} }
} }
SWITCH_DECLARE(void) switch_console_push_match_unique(switch_console_callback_match_t **matches, const char *new_val)
{
/* Ignore the entry if it is already in the list */
if (*matches) {
switch_console_callback_match_node_t *node;
for(node = (*matches)->head; node; node = node->next) {
if (!strcasecmp(node->val, new_val)) return;
}
}
switch_console_push_match(matches, new_val);
}
SWITCH_DECLARE(void) switch_console_push_match(switch_console_callback_match_t **matches, const char *new_val) SWITCH_DECLARE(void) switch_console_push_match(switch_console_callback_match_t **matches, const char *new_val)
{ {
switch_console_callback_match_node_t *match; switch_console_callback_match_node_t *match;

View File

@ -2905,6 +2905,12 @@ SWITCH_DECLARE(void) switch_ivr_delay_echo(switch_core_session_t *session, uint3
interval = read_impl.microseconds_per_packet / 1000; interval = read_impl.microseconds_per_packet / 1000;
//samples = switch_samples_per_packet(read_impl.samples_per_second, interval); //samples = switch_samples_per_packet(read_impl.samples_per_second, interval);
if (delay_ms < interval * 2) {
delay_ms = interval * 2;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Minimum possible delay for this codec (%d) has been chosen\n", delay_ms);
}
qlen = delay_ms / (interval) / 2; qlen = delay_ms / (interval) / 2;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Setting delay to %dms (%d frames)\n", delay_ms, qlen); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Setting delay to %dms (%d frames)\n", delay_ms, qlen);
jb = stfu_n_init(qlen, qlen, read_impl.samples_per_packet, read_impl.samples_per_second, 0); jb = stfu_n_init(qlen, qlen, read_impl.samples_per_packet, read_impl.samples_per_second, 0);

View File

@ -1262,7 +1262,7 @@ static int get_netmask(struct sockaddr_in *me, int *mask)
struct sockaddr_in *s = (struct sockaddr_in *) i->ifa_addr; struct sockaddr_in *s = (struct sockaddr_in *) i->ifa_addr;
struct sockaddr_in *m = (struct sockaddr_in *) i->ifa_netmask; struct sockaddr_in *m = (struct sockaddr_in *) i->ifa_netmask;
if (s && m && s->sin_addr.s_addr == me->sin_addr.s_addr) { if (s && m && s->sin_family == AF_INET && s->sin_addr.s_addr == me->sin_addr.s_addr) {
*mask = m->sin_addr.s_addr; *mask = m->sin_addr.s_addr;
freeifaddrs(ifaddrs); freeifaddrs(ifaddrs);
return 0; return 0;
@ -1569,6 +1569,61 @@ SWITCH_DECLARE(switch_status_t) switch_find_local_ip(char *buf, int len, int *ma
return status; return status;
} }
#ifdef HAVE_GETIFADDRS
# include <ifaddrs.h>
# include <net/if.h>
#endif
SWITCH_DECLARE(switch_status_t) switch_find_interface_ip(char *buf, int len, int *mask, const char *ifname, int family)
{
switch_status_t status = SWITCH_STATUS_FALSE;
#ifdef HAVE_GETIFADDRS
struct ifaddrs *addrs, *addr;
getifaddrs(&addrs);
for(addr = addrs; addr; addr = addr->ifa_next)
{
if (!(addr->ifa_flags & IFF_UP)) continue; // Address is not UP
if (!addr->ifa_addr) continue; // No address set
if (!addr->ifa_netmask) continue; // No netmask set
if (family != AF_UNSPEC && addr->ifa_addr->sa_family != family) continue; // Not the address family we're looking for
if (strcmp(addr->ifa_name, ifname)) continue; // Not the interface we're looking for
switch(addr->ifa_addr->sa_family) {
case AF_INET:
inet_ntop(AF_INET, &( ((struct sockaddr_in*)(addr->ifa_addr))->sin_addr ), buf, len - 1);
break;
case AF_INET6:
inet_ntop(AF_INET6, &( ((struct sockaddr_in6*)(addr->ifa_addr))->sin6_addr ), buf, len - 1);
break;
default:
continue;
}
if (mask && addr->ifa_netmask->sa_family == AF_INET) {
*mask = ((struct sockaddr_in*)(addr->ifa_addr))->sin_addr.s_addr;
}
status = SWITCH_STATUS_SUCCESS;
break;
}
freeifaddrs(addrs);
#elif defined(__linux__)
// TODO Not implemented, contributions welcome.
#elif defined(WIN32)
// TODO Not implemented, contributions welcome.
#endif
return status;
}
SWITCH_DECLARE(switch_time_t) switch_str_time(const char *in) SWITCH_DECLARE(switch_time_t) switch_str_time(const char *in)
{ {
switch_time_exp_t tm = { 0 }; switch_time_exp_t tm = { 0 };