Merge branch 'v1.2.stable' of ssh://git.freeswitch.org:222/freeswitch into v1.2.stable
This commit is contained in:
commit
f4b71b1082
|
@ -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>
|
||||||
|
|
|
@ -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>
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
Loading…
Reference in New Issue