diff --git a/conf/rayo/autoload_configs/rayo.conf.xml b/conf/rayo/autoload_configs/rayo.conf.xml
index 0cb46d7992..35419319bc 100644
--- a/conf/rayo/autoload_configs/rayo.conf.xml
+++ b/conf/rayo/autoload_configs/rayo.conf.xml
@@ -11,6 +11,11 @@
+
+
+
+
+
@@ -42,4 +47,211 @@
+
+
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+ ]]>
+
+
+
+
+ - yes
- no
+ ]]]]>
+
+
+ ]]>
+
+
+
+
+ - yes
- no
+ ]]]]>
+
+
+ ]]>
+
+
+
+
+
+ - yes
- no
+ ]]]]>
+
+
+ ]]>
+
+
+
+
+
+ - yes
- no
+ ]]]]>
+
+
+ ]]>
+
+
+
+
diff --git a/conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml b/conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml
new file mode 100644
index 0000000000..cbde87ca5a
--- /dev/null
+++ b/conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c
index 316f5607c2..9f56d9f454 100755
--- a/libs/freetdm/mod_freetdm/mod_freetdm.c
+++ b/libs/freetdm/mod_freetdm/mod_freetdm.c
@@ -2241,7 +2241,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal)
{
switch_core_session_t *session = NULL;
switch_channel_t *channel = NULL;
- ftdm_status_t status;
+ ftdm_status_t status = FTDM_SUCCESS;
uint32_t spanid;
uint32_t chanid;
ftdm_caller_data_t *caller_data;
@@ -2296,6 +2296,45 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal)
break;
case FTDM_SIGEVENT_SIGSTATUS_CHANGED:
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;
default:
{
@@ -2305,7 +2344,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal)
break;
}
- return FTDM_SUCCESS;
+ return status;
}
static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal)
@@ -3785,7 +3824,10 @@ static switch_status_t load_config(void)
char *hold_music = NULL;
char *fail_dial_regex = NULL;
char str_false[] = "false";
+ char str_empty[] = "";
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;
ftdm_span_t *span = NULL;
analog_option_t analog_options = ANALOG_OPTION_NONE;
@@ -3814,6 +3856,10 @@ static switch_status_t load_config(void)
max_digits = val;
} else if (!strcasecmp(var, "answer-supervision")) {
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")) {
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,
"tonemap", tonegroup,
"answer_supervision", answer_supervision,
+ "ringback_during_collect", ringback_during_collect,
+ "ringback_file", ringback_file,
"digit_timeout", &to,
"dial_timeout", &dial_timeout_int,
"max_dialstr", &max,
diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c
index a2a48b5b50..1f3e734df0 100644
--- a/libs/freetdm/src/ftdm_io.c
+++ b/libs/freetdm/src/ftdm_io.c
@@ -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)
{
ftdm_channel_t *fchan = NULL;
+ ftdm_status_t status = FTDM_SUCCESS;
if (sigmsg->channel) {
fchan = sigmsg->channel;
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)) {
ftdm_span_queue_signal(span, sigmsg);
} else {
- ftdm_span_trigger_signal(span, sigmsg);
+ status = ftdm_span_trigger_signal(span, sigmsg);
}
done:
@@ -6185,7 +6186,7 @@ done:
ftdm_channel_unlock(fchan);
}
- return FTDM_SUCCESS;
+ return status;
}
static void *ftdm_cpu_monitor_run(ftdm_thread_t *me, void *obj)
diff --git a/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h b/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h
index 37c90c3b4f..fb9959d061 100644
--- a/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h
+++ b/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h
@@ -33,6 +33,7 @@
* Contributor(s):
*
* John Wehle (john@feith.com)
+ * Moises Silva (moy@sangoma.com)
*
*/
@@ -53,6 +54,8 @@ struct ftdm_analog_data {
uint32_t digit_timeout;
uint32_t dial_timeout;
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);
diff --git a/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c
index b8ae4c80e6..7632932eef 100644
--- a/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c
+++ b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c
@@ -33,6 +33,7 @@
* Contributor(s):
*
* 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);
#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);
/**
@@ -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);
}
+/**
+ * \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
* \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)
//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 *ringback_file = "";
+ ftdm_bool_t ringback_during_collect = FTDM_FALSE;
uint32_t digit_timeout = 2000;
uint32_t max_dialstr = 11;
uint32_t dial_timeout = 0;
@@ -134,9 +250,8 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span)
return FTDM_FAIL;
}
- analog_data = ftdm_malloc(sizeof(*analog_data));
+ analog_data = ftdm_calloc(1, sizeof(*analog_data));
assert(analog_data != NULL);
- memset(analog_data, 0, sizeof(*analog_data));
while((var = va_arg(ap, char *))) {
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;
}
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")) {
if (!(val = va_arg(ap, char *))) {
break;
@@ -182,6 +307,7 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span)
}
span->start = ftdm_analog_em_start;
+ span->stop = ftdm_analog_em_stop;
analog_data->digit_timeout = digit_timeout;
analog_data->max_dialstr = max_dialstr;
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_span_sig_status = analog_em_get_span_sig_status;
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;
@@ -239,6 +369,7 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
int cas_bits = 0;
uint32_t cas_answer = 0;
int cas_answer_ms = 500;
+ FILE *ringback_f = NULL;
ftdm_bool_t digits_sent = FTDM_FALSE;
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);
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)) {
ftdm_wait_flag_t flags = FTDM_READ;
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))) {
ftdm_log(FTDM_LOG_DEBUG, "Number obtained [%s]\n", dtmf);
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RING);
@@ -573,6 +715,16 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
ftdm_channel_write(ftdmchan, frame, sizeof(frame), &rlen);
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) {
continue;
@@ -582,7 +734,25 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
len *= 2;
}
- rlen = ftdm_buffer_read_loop(dt_buffer, frame, len);
+ 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);
+ }
if (ftdmchan->effective_codec != FTDM_CODEC_SLIN) {
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) {
codec_func(frame, sizeof(frame), &rlen);
} 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;
}
}
@@ -621,6 +791,10 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
ftdm_buffer_destroy(&dt_buffer);
}
+ if (ringback_f) {
+ fclose(ringback_f);
+ }
+
ftdm_clear_flag(closed_chan, FTDM_CHANNEL_INTHREAD);
ftdm_log(FTDM_LOG_DEBUG, "ANALOG EM CHANNEL thread ended.\n");
diff --git a/scripts/lua/zrtp_sas_proxy.lua b/scripts/lua/zrtp_sas_proxy.lua
new file mode 100644
index 0000000000..503b8d8c72
--- /dev/null
+++ b/scripts/lua/zrtp_sas_proxy.lua
@@ -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.:
+--
+--
+--
+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
diff --git a/src/include/switch_console.h b/src/include/switch_console.h
index e6f6cf476e..73410bd248 100644
--- a/src/include/switch_console.h
+++ b/src/include/switch_console.h
@@ -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_run_complete_func(const char *func, const char *line,
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_free_matches(switch_console_callback_match_t **matches);
SWITCH_DECLARE(unsigned char) switch_console_complete(const char *line, const char *last_word,
diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h
index 5deb8ee313..24b8765faf 100644
--- a/src/include/switch_utils.h
+++ b/src/include/switch_utils.h
@@ -464,14 +464,27 @@ SWITCH_DECLARE(switch_status_t) switch_resolve_host(const char *host, char *buf,
/*!
\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 mask the CIDR found (AF_INET only)
\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_local_ip(_Out_opt_bytecapcount_(len)
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
\param buf the buffer to write the ip adress found into
diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c
index fc74c3dd65..abc0aace9a 100644
--- a/src/mod/applications/mod_commands/mod_commands.c
+++ b/src/mod/applications/mod_commands/mod_commands.c
@@ -6028,6 +6028,49 @@ SWITCH_STANDARD_API(file_exists_function)
return SWITCH_STATUS_SUCCESS;
}
+#define INTERFACE_IP_SYNTAX "[auto|ipv4|ipv6] "
+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_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, "host_lookup", "Lookup host", host_lookup_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, "hupall", "hupall", hupall_api_function, " [ ]");
SWITCH_ADD_API(commands_api_interface, "in_group", "Determine if a user is in a group", in_group_function, "[@] ");
@@ -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 min_idle_cpu");
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 nat_map reinit");
switch_console_set_complete("add nat_map republish");
diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c
index 8778e93f5d..7ea86fa973 100644
--- a/src/mod/applications/mod_conference/mod_conference.c
+++ b/src/mod/applications/mod_conference/mod_conference.c
@@ -7670,7 +7670,9 @@ SWITCH_STANDARD_APP(conference_function)
}
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;
/* be friendly */
diff --git a/src/mod/applications/mod_voicemail/mod_voicemail.c b/src/mod/applications/mod_voicemail/mod_voicemail.c
index 41f7cef5d6..e7d4da79ca 100644
--- a/src/mod/applications/mod_voicemail/mod_voicemail.c
+++ b/src/mod/applications/mod_voicemail/mod_voicemail.c
@@ -2876,7 +2876,7 @@ static switch_status_t deliver_vm(vm_profile_t *profile,
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;
char *from;
char *body;
diff --git a/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c b/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c
index 1934b0a90f..2aab3e5a5c 100644
--- a/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c
+++ b/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c
@@ -894,6 +894,7 @@ SWITCH_STANDARD_API(sangoma_function)
char *argv[10] = { 0 };
int argc = 0;
char *mycmd = NULL;
+ switch_bool_t locked = SWITCH_FALSE;
if (zstr(cmd)) {
stream->write_function(stream, "%s", SANGOMA_SYNTAX);
@@ -910,6 +911,10 @@ SWITCH_STANDARD_API(sangoma_function)
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")) {
char addrbuff[50];
int addr;
@@ -922,7 +927,6 @@ SWITCH_STANDARD_API(sangoma_function)
const void *var;
void *val;
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"
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");
@@ -967,7 +971,6 @@ SWITCH_STANDARD_API(sangoma_function)
decoder_avgrxus_str);
totalsess++;
}
- switch_mutex_unlock(g_sessions_lock);
stream->write_function(stream, "Total sessions: %d\n", totalsess);
} else if (!strcasecmp(argv[0], "stats")) {
struct sangoma_transcoding_session *sess;
@@ -983,6 +986,7 @@ SWITCH_STANDARD_API(sangoma_function)
stream->write_function(stream, "%s", SANGOMA_SYNTAX);
goto done;
}
+
sess = sangoma_find_session(sessid);
if (!sess) {
stream->write_function(stream, "Failed to find session %lu\n", sessid);
@@ -1076,6 +1080,9 @@ SWITCH_STANDARD_API(sangoma_function)
}
done:
+ if (locked) {
+ switch_mutex_unlock(g_sessions_lock);
+ }
switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS;
diff --git a/src/mod/endpoints/mod_loopback/mod_loopback.c b/src/mod/endpoints/mod_loopback/mod_loopback.c
index 105b96564a..41f5849655 100644
--- a/src/mod/endpoints/mod_loopback/mod_loopback.c
+++ b/src/mod/endpoints/mod_loopback/mod_loopback.c
@@ -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)) {
switch_channel_t *other_channel = switch_core_session_get_channel(other_session);
- if (switch_channel_test_flag(other_channel, CF_BRIDGED)) {
- /* Wait for real channel to be exchanging media */
- switch_channel_wait_for_state(other_channel, channel, CS_EXCHANGE_MEDIA);
- }
+ switch_channel_wait_for_state_timeout(other_channel, CS_EXCHANGE_MEDIA, 5000);
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));
diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c
index dff09844e7..9040c2c141 100644
--- a/src/mod/endpoints/mod_skinny/skinny_server.c
+++ b/src/mod/endpoints/mod_skinny/skinny_server.c
@@ -438,6 +438,7 @@ switch_status_t skinny_session_send_call_info_all(switch_core_session_t *session
struct skinny_session_set_variables_helper {
private_t *tech_pvt;
switch_channel_t *channel;
+ listener_t *listener;
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;
char *tmp;
+ listener_t *listener;
+
+ switch_xml_t xroot, xdomain, xuser, xvariables, xvariable;
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, 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;
}
@@ -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.channel = switch_core_session_get_channel(session);
+ helper.listener = listener;
helper.count = 0;
switch_channel_set_variable(helper.channel, "skinny_profile_name", helper.tech_pvt->profile->name);
diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c
index 907de17a20..e9297c0c77 100644
--- a/src/mod/endpoints/mod_sofia/mod_sofia.c
+++ b/src/mod/endpoints/mod_sofia/mod_sofia.c
@@ -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"
"X-FS-Display-Name: %s\nX-FS-Display-Number: %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" : "",
callee_name, callee_number);
} else {
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" : "");
}
}
diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c
index 4faea7ac42..c3de108ac9 100644
--- a/src/mod/endpoints/mod_sofia/sofia.c
+++ b/src/mod/endpoints/mod_sofia/sofia.c
@@ -4052,9 +4052,22 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name)
}
} else if (!strcasecmp(var, "rtp-ip")) {
char *ip = mod_sofia_globals.guess_ip;
+ char buf[64];
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);
+ } 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 {
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")) {
char *ip = mod_sofia_globals.guess_ip;
+ char buf[64];
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);
+ } 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 {
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(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(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(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);
diff --git a/src/mod/endpoints/mod_sofia/sofia_presence.c b/src/mod/endpoints/mod_sofia/sofia_presence.c
index dd168aa4fe..cbad6ea20e 100644
--- a/src/mod/endpoints/mod_sofia/sofia_presence.c
+++ b/src/mod/endpoints/mod_sofia/sofia_presence.c
@@ -154,6 +154,11 @@ switch_status_t sofia_presence_chat_send(switch_event_t *message_event)
goto end;
}
+ if (!from) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing From: header.\n");
+ goto end;
+ }
+
if (!zstr(type)) {
ct = type;
}
diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c
index e195a1cbae..ed8001bc32 100644
--- a/src/mod/endpoints/mod_sofia/sofia_reg.c
+++ b/src/mod/endpoints/mod_sofia/sofia_reg.c
@@ -2745,7 +2745,7 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile,
skip_auth:
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);
}
diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml
index 0cb46d7992..35419319bc 100644
--- a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml
+++ b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml
@@ -11,6 +11,11 @@
+
+
+
+
+
@@ -42,4 +47,211 @@
+
+
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+ ]]>
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+
+ ]]>
+
+
+
+
+ - 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]]]>
+
+
+ ]]>
+
+
+
+
+ - yes
- no
+ ]]]]>
+
+
+ ]]>
+
+
+
+
+ - yes
- no
+ ]]]]>
+
+
+ ]]>
+
+
+
+
+
+ - yes
- no
+ ]]]]>
+
+
+ ]]>
+
+
+
+
+
+ - yes
- no
+ ]]]]>
+
+
+ ]]>
+
+
+
+
diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.c b/src/mod/event_handlers/mod_rayo/iks_helpers.c
index 0b5616a1f3..30d9d215a5 100644
--- a/src/mod/event_handlers/mod_rayo/iks_helpers.c
+++ b/src/mod/event_handlers/mod_rayo/iks_helpers.c
@@ -216,6 +216,17 @@ double iks_find_decimal_attrib(iks *xml, const char *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
* @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;
}
+/**
+ * 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)
/**
diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.h b/src/mod/event_handlers/mod_rayo/iks_helpers.h
index 90a5ca688d..3a7bae02e7 100644
--- a/src/mod/event_handlers/mod_rayo/iks_helpers.h
+++ b/src/mod/event_handlers/mod_rayo/iks_helpers.h
@@ -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 int iks_find_bool_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 const char *iks_node_type_to_string(int type);
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 */
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(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 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 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_any(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
diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c
index fffffe31eb..3004601661 100644
--- a/src/mod/event_handlers/mod_rayo/mod_rayo.c
+++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c
@@ -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);
+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
* @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;
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);
if (actor) {
if (!actor->destroy) {
@@ -818,16 +827,33 @@ int rayo_actor_seq_next(struct rayo_actor *actor)
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
* @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);
- if (actor && !strcmp(RAT_CALL, actor->type)) {
+ if (actor && is_call_actor(actor)) {
return RAYO_CALL(actor);
} else if (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 session the session
* @param node the join request
- * @param call_id to join
+ * @param call_uri to join
* @param media mode (direct/bridge)
* @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;
/* take call out of media path if media = "direct" */
const char *bypass = !strcmp("direct", media) ? "true" : "false";
/* 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) {
/* 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");
RAYO_UNLOCK(b_call);
} else {
- RAYO_UNLOCK(b_call);
-
/* bridge this call to call-uri */
switch_channel_set_variable(switch_core_session_get_channel(session), "bypass_media", bypass);
if (switch_false(bypass)) {
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);
} else {
response = iks_new_error_detailed(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to bridge call");
}
+ RAYO_UNLOCK(b_call);
}
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");
const char *join_id;
const char *mixer_name;
- const char *call_id;
+ const char *call_uri;
/* validate input attributes */
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;
}
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)) {
join_id = mixer_name;
} else {
- join_id = call_id;
+ join_id = call_uri;
}
/* 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");
goto done;
}
/* 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");
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"));
} else {
/* 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:
@@ -1844,21 +1869,22 @@ done:
* @param call the call that unjoined
* @param session the session
* @param node the unjoin request
- * @param call_id the b-leg uuid
+ * @param call_uri the b-leg xmpp URI
* @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;
- 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 */
- if (!zstr(bleg) && !strcmp(bleg, call_id)) {
+ /* bleg must match call_uri */
+ if (!zstr(bleg_uri) && !strcmp(bleg_uri, call_uri)) {
/* unbridge call */
response = iks_new_iq_result(node);
switch_ivr_park_session(session);
} else {
- /* not bridged or wrong b-leg UUID */
+ /* not bridged or wrong b-leg URI */
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;
iks *response = NULL;
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");
- if (!zstr(call_id) && !zstr(mixer_name)) {
+ if (!zstr(call_uri) && !zstr(mixer_name)) {
response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST);
} else if (!RAYO_CALL(call)->joined) {
/* not joined to anything */
response = iks_new_error(node, STANZA_ERROR_SERVICE_UNAVAILABLE);
- } else if (!zstr(call_id)) {
- response = unjoin_call(RAYO_CALL(call), session, node, call_id);
+ } else if (!zstr(call_uri)) {
+ response = unjoin_call(RAYO_CALL(call), session, node, call_uri);
} else if (!zstr(mixer_name)) {
response = unjoin_mixer(RAYO_CALL(call), session, node, mixer_name);
} else {
@@ -2037,20 +2063,20 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void *
if (join) {
/* 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");
- if (!zstr(call_id) && !zstr(mixer_name)) {
+ if (!zstr(call_uri) && !zstr(mixer_name)) {
/* can't join both */
response = iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
goto done;
- } else if (zstr(call_id) && zstr(mixer_name)) {
+ } else if (zstr(call_uri) && zstr(mixer_name)) {
/* nobody to join to? */
response = iks_new_error(iq, STANZA_ERROR_BAD_REQUEST);
goto done;
- } else if (!zstr(call_id)) {
+ } else if (!zstr(call_uri)) {
/* 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? */
if (!b_call) {
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);
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);
- stream.write_function(&stream, "%s%s &rayo(bridge %s)", gateway->dial_prefix, dial_to_stripped, call_id);
} else {
/* conference */
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);
/* flag call as available to join another mixer */
- call = RAYO_CALL_LOCATE(uuid);
+ call = RAYO_CALL_LOCATE_BY_ID(uuid);
if (call) {
call->joined = 0;
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 */
delete_member_event = iks_new_presence("unjoined", RAYO_NS, RAYO_JID(mixer), "");
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);
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;
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) {
/* 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 */
add_member_event = iks_new_presence("joined", RAYO_NS, RAYO_JID(mixer), "");
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);
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;
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))) {
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");
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));
-#endif
RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), response);
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)
{
- 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 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)
{
- 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) {
iks *revent = iks_new_presence("answered", RAYO_NS,
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");
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) {
switch_mutex_lock(RAYO_ACTOR(call)->mutex);
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 *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;
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_dcp_jid"));
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_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);
/* send B-leg event */
- b_call = RAYO_CALL_LOCATE(b_uuid);
+ b_call = RAYO_CALL_LOCATE_BY_ID(b_uuid);
if (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");
- 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_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 *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;
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_dcp_jid"));
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);
call->joined = 0;
call->joined_id = NULL;
/* send B-leg event */
- b_call = RAYO_CALL_LOCATE(b_uuid);
+ b_call = RAYO_CALL_LOCATE_BY_ID(b_uuid);
if (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");
- 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);
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
* @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:
switch_xml_free(xml);
@@ -3360,12 +3413,6 @@ static void send_console_command(struct rayo_client *client, const char *to, con
iks *command = NULL;
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) {
char *str;
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
*/
-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 };
- int argc = switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0]));
-
- if (argc != 2) {
- free(cmd_dup);
+ if (!zstr(cmd)) {
+ int argc = switch_separate_string(cmd, ' ', argv, sizeof(argv) / sizeof(argv[0]));
+ if (argc != 2) {
+ return 0;
+ }
+ } else {
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]);
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;
}
@@ -3443,14 +3506,15 @@ static void send_console_message(struct rayo_client *client, const char *to, con
/**
* 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 };
- int argc = switch_separate_string(msg_dup, ' ', argv, sizeof(argv) / sizeof(argv[0]));
-
- if (argc != 2) {
- free(msg_dup);
+ if (!zstr(cmd)) {
+ int argc = switch_separate_string(cmd, ' ', argv, sizeof(argv) / sizeof(argv[0]));
+ if (argc != 2) {
+ return 0;
+ }
+ } else {
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]);
stream->write_function(stream, "+OK\n");
- free(msg_dup);
return 1;
}
@@ -3484,151 +3547,238 @@ static void send_console_presence(struct rayo_client *client, const char *to, in
/**
* 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;
-
- if (argc != 2) {
- free(cmd_dup);
+ char *argv[2] = { 0 };
+ if (!zstr(cmd)) {
+ int argc = switch_separate_string(cmd, ' ', argv, sizeof(argv) / sizeof(argv[0]));
+ if (argc != 2) {
+ return 0;
+ }
+ } else {
return 0;
}
if (!strcmp("online", argv[1])) {
is_online = 1;
} else if (strcmp("offline", argv[1])) {
- free(cmd_dup);
return 0;
}
/* send presence */
send_console_presence(globals.console, argv[0], is_online);
stream->write_function(stream, "+OK\n");
- free(cmd_dup);
return 1;
}
-#define RAYO_API_SYNTAX "status | (cmd ) | (msg ) | (presence )"
+#define RAYO_API_SYNTAX "status | ( ) | (cmd ) | (msg ) | (presence )"
SWITCH_STANDARD_API(rayo_api)
{
+ const char *alias;
+ char *cmd_dup = strdup(cmd);
+ char *argv[2] = { 0 };
int success = 0;
- if (!strncmp("status", cmd, 6)) {
- success = dump_api(cmd + 6, stream);
- } else if (!strncmp("cmd", cmd, 3)) {
- success = command_api(cmd + 3, stream);
- } else if (!strncmp("msg", cmd, 3)) {
- success = message_api(cmd + 3, stream);
- } else if (!strncmp("presence", cmd, 8)) {
- success = presence_api(cmd + 8, stream);
+
+ switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0]));
+
+ /* check if a command alias */
+ alias = switch_core_hash_find(globals.cmd_aliases, argv[0]);
+
+ if (!zstr(alias)) {
+ 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) {
stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_API_SYNTAX);
}
+ free(cmd_dup);
+
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
*/
-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;
- void *val;
- const void *vvar;
- switch_console_callback_match_t *my_matches = NULL;
- switch_status_t status = SWITCH_STATUS_FALSE;
- struct rayo_actor *actor;
+ return list_actors(line, cursor, matches, is_internal_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 (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;
+/**
+ * @return true if external actor
+ */
+static switch_bool_t is_external_actor(struct rayo_actor *actor)
+{
+ return !strcmp(RAT_CLIENT, actor->type) || !strcmp(RAT_PEER_SERVER, actor->type);
}
/**
* 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;
- void *val;
- const void *vvar;
- switch_console_callback_match_t *my_matches = NULL;
- switch_status_t status = SWITCH_STATUS_FALSE;
- struct rayo_actor *actor;
+ return list_actors(line, cursor, matches, is_external_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 (!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;
+/**
+ * @return true
+ */
+static switch_bool_t is_any_actor(struct rayo_actor *actor)
+{
+ return SWITCH_TRUE;
}
/**
* 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;
- 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;
+ return list_actors(line, cursor, matches, is_any_actor);
}
/**
- * Add an alias to an API command
- * @param alias_name
- * @param alias_cmd
+ * @return true if a server
*/
-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);
- switch_console_set_complete(cmd);
- switch_core_hash_insert(globals.cmd_aliases, alias_name, alias_cmd);
+ return !strcmp(RAT_SERVER, actor->type);
+}
+
+/**
+ * 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();
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 presence ::rayo::list_all online");
- switch_console_set_complete("add rayo presence ::rayo::list_all offline");
+ switch_console_set_complete("add rayo cmd ::rayo::list_all");
+ 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_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", "");
- rayo_add_cmd_alias("answer", "");
- rayo_add_cmd_alias("hangup", "");
- rayo_add_cmd_alias("stop", "");
- rayo_add_cmd_alias("pause", "");
- rayo_add_cmd_alias("resume", "");
- rayo_add_cmd_alias("speed-up", "");
- rayo_add_cmd_alias("speed-down", "");
- rayo_add_cmd_alias("volume-up", "");
- rayo_add_cmd_alias("volume-down", "");
- rayo_add_cmd_alias("record", "");
- rayo_add_cmd_alias("record_pause", "");
- rayo_add_cmd_alias("record_resume", "");
- rayo_add_cmd_alias("prompt_barge", ""
- ""
- ""
- ""
- "- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]>"
- ""
- "");
-
- rayo_add_cmd_alias("prompt_no_barge", ""
- ""
- ""
- ""
- "- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]>"
- ""
- "");
-
- rayo_add_cmd_alias("prompt_long", ""
- ""
- ""
- ""
- "- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]>"
- ""
- "");
-
- rayo_add_cmd_alias("prompt_multi_digit", ""
- ""
- ""
- ""
- "- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]>"
- ""
- "");
-
- rayo_add_cmd_alias("prompt_terminator", ""
- ""
- ""
- ""
- "- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]>"
- ""
- "");
-
- rayo_add_cmd_alias("prompt_input_bad", ""
- ""
- ""
- ""
- "- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]>"
- ""
- "");
-
- rayo_add_cmd_alias("prompt_output_bad", ""
- ""
- ""
- ""
- "- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
]]>"
- ""
- "");
- rayo_add_cmd_alias("input", ""
- ""
- "- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- *
- #
]]>"
- "");
- rayo_add_cmd_alias("output_bad",
- "");
- rayo_add_cmd_alias("join_mixer_duplex",
- "");
- rayo_add_cmd_alias("join_mixer_send",
- "");
- rayo_add_cmd_alias("join_mixer_recv",
- "");
- rayo_add_cmd_alias("unjoin_mixer",
- "");
- rayo_add_cmd_alias("unjoin",
- "");
return SWITCH_STATUS_SUCCESS;
}
@@ -3808,9 +3880,15 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load)
*/
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_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");
/* stop XMPP streams */
diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.h b/src/mod/event_handlers/mod_rayo/mod_rayo.h
index f4602cc6b9..dd0adb3192 100644
--- a/src/mod/event_handlers/mod_rayo/mod_rayo.h
+++ b/src/mod/event_handlers/mod_rayo/mod_rayo.h
@@ -51,9 +51,6 @@
#define RAT_PEER_SERVER "PEER_SERVER"
#define RAT_CLIENT "CLIENT"
-/* these are support punchblock.. undefine once punchblock is fixed */
-#define RAYO_UUID_IN_REF_URI
-
struct rayo_actor;
struct rayo_call;
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__)
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 *);
extern void rayo_actor_command_handler_add(const char *type, const char *subtype, const char *name, rayo_actor_xmpp_handler fn);
diff --git a/src/mod/event_handlers/mod_rayo/rayo_components.c b/src/mod/event_handlers/mod_rayo/rayo_components.c
index 54e241d036..b5137d9459 100644
--- a/src/mod/event_handlers/mod_rayo/rayo_components.c
+++ b/src/mod/event_handlers/mod_rayo/rayo_components.c
@@ -40,7 +40,7 @@
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);
- if (actor && !strncmp(RAT_COMPONENT, actor->type, strlen(RAT_COMPONENT))) {
+ if (actor && is_component_actor(actor)) {
return RAYO_COMPONENT(actor);
} else if (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 *ref = iks_insert(response, "ref");
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));
-#endif
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)
{
- rayo_input_component_load();
- rayo_output_component_load(module_interface, pool);
- rayo_prompt_component_load();
- rayo_record_component_load(pool, config_file);
-
- 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) {
+ if (rayo_input_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
+ rayo_output_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
+ rayo_prompt_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS ||
+ rayo_record_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_TERM;
}
return SWITCH_STATUS_SUCCESS;
diff --git a/src/mod/event_handlers/mod_rayo/rayo_components.h b/src/mod/event_handlers/mod_rayo/rayo_components.h
index 71891ea3d6..6e93dfbc43 100644
--- a/src/mod/event_handlers/mod_rayo/rayo_components.h
+++ b/src/mod/event_handlers/mod_rayo/rayo_components.h
@@ -54,10 +54,10 @@
#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_input_component_load(void);
-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_prompt_component_load(void);
-extern switch_status_t rayo_record_component_load(switch_memory_pool_t *pool, const char *config_file);
+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, const char *config_file);
+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_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_input_component_shutdown(void);
diff --git a/src/mod/event_handlers/mod_rayo/rayo_elements.c b/src/mod/event_handlers/mod_rayo/rayo_elements.c
index 89e31c7d66..34577a9492 100644
--- a/src/mod/event_handlers/mod_rayo/rayo_elements.c
+++ b/src/mod/event_handlers/mod_rayo/rayo_elements.c
@@ -33,7 +33,7 @@
*/
ELEMENT(RAYO_INPUT)
STRING_ATTRIB(mode, any, "any,dtmf,voice")
- ATTRIB(terminator,, any)
+ OPTIONAL_ATTRIB(terminator,, dtmf_digit)
ATTRIB(recognizer,, any)
ATTRIB(language, en-US, any)
ATTRIB(initial-timeout, -1, positive_or_neg_one)
diff --git a/src/mod/event_handlers/mod_rayo/rayo_input_component.c b/src/mod/event_handlers/mod_rayo/rayo_input_component.c
index 9e10d1b7a5..3bf3b6dfb6 100644
--- a/src/mod/event_handlers/mod_rayo/rayo_input_component.c
+++ b/src/mod/event_handlers/mod_rayo/rayo_input_component.c
@@ -45,6 +45,8 @@ struct input_handler;
static struct {
/** grammar parser */
struct srgs_parser *parser;
+ /** default recognizer to use if none specified */
+ const char *default_recognizer;
} globals;
/**
@@ -57,8 +59,8 @@ struct input_component {
int speech_mode;
/** Number of collected digits */
int num_digits;
- /** Terminating digits */
- int term_digit_mask;
+ /** Terminating digit */
+ char term_digit;
/** The collected digits */
char digits[MAX_DTMF + 1];
/** grammar to match */
@@ -70,7 +72,9 @@ struct input_component {
/** maximum silence allowed */
int max_silence;
/** minimum speech detection confidence */
- int min_confidence;
+ double min_confidence;
+ /** sensitivity to background noise */
+ double sensitivity;
/** timeout after first digit is received */
int inter_digit_timeout;
/** stop flag */
@@ -79,6 +83,10 @@ struct input_component {
int start_timers;
/** true if event fired for first digit / start of speech */
int barge_event;
+ /** optional language to use */
+ const char *language;
+ /** optional recognizer to use */
+ const char *recognizer;
/** global data */
struct input_handler *handler;
};
@@ -91,77 +99,24 @@ struct input_component {
struct input_handler {
/** media bug to monitor frames / control input lifecycle */
switch_media_bug_t *bug;
- /** active input component - TODO multiple inputs */
- struct input_component *component;
+ /** active voice input component */
+ struct input_component *voice_component;
+ /** active dtmf input component */
+ struct input_component *dtmf_component;
/** synchronizes media bug and dtmf callbacks */
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) {
- 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;
+ return digit1 && digit2 && tolower(digit1) == tolower(digit2);
}
/**
@@ -205,15 +160,14 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c
switch_mutex_lock(handler->mutex);
- component = handler->component;
+ component = handler->dtmf_component;
/* additional paranoia check */
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);
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) {
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: {
/* notify of no-match and remove input component */
- handler->component = NULL;
+ handler->dtmf_component = NULL;
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);
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: {
iks *result = nlsml_create_dtmf_match(component->digits);
/* notify of match and remove input component */
- handler->component = NULL;
+ handler->dtmf_component = NULL;
switch_core_media_bug_remove(session, &handler->bug);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "MATCH = %s\n", component->digits);
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;
switch_mutex_lock(handler->mutex);
- component = handler->component;
+ component = handler->dtmf_component;
switch(type) {
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;
if (component->num_digits && component->inter_digit_timeout > 0 && elapsed_ms > component->inter_digit_timeout) {
enum srgs_match_type match;
- handler->component = NULL;
+ handler->dtmf_component = NULL;
switch_core_media_bug_set_flag(bug, SMBF_PRUNE);
/* 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);
}
} 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_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "initial-timeout\n");
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 */
if (component) {
if (component->stop) {
- handler->component = NULL;
+ handler->dtmf_component = NULL;
rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP);
} else {
- handler->component = NULL;
+ handler->dtmf_component = NULL;
rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_HANGUP);
}
}
@@ -396,34 +350,53 @@ static iks *start_call_input(struct input_component *component, switch_core_sess
handler = switch_core_session_alloc(session, sizeof(*handler));
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);
+ 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->digits[0] = '\0';
component->stop = 0;
- component->speech_mode = 0;
component->initial_timeout = iks_find_int_attrib(input, "initial-timeout");
component->inter_digit_timeout = iks_find_int_attrib(input, "inter-digit-timeout");
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->start_timers = iks_find_bool_attrib(input, "start-timers");
- /* TODO this should just be a single digit terminator? */
- component->term_digit_mask = digit_mask_set_from_digits(0, iks_find_attrib_soft(input, "terminator"));
- /* TODO recognizer ignored */
- /* TODO language ignored */
+ component->term_digit = iks_find_char_attrib(input, "terminator");
+ component->recognizer = iks_find_attrib(input, "recognizer");
+ component->language = iks_find_attrib(input, "language");
component->handler = handler;
- /* parse the 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");
- RAYO_UNLOCK(component);
- RAYO_DESTROY(component);
- 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"))) {
+ if (!component->speech_mode) {
+
+ /* parse the 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");
+ RAYO_UNLOCK(component);
+ RAYO_DESTROY(component);
+ return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Failed to parse grammar body");
+ }
+
component->last_digit_time = switch_micro_time_now();
/* acknowledge command */
@@ -431,38 +404,124 @@ static iks *start_call_input(struct input_component *component, switch_core_sess
/* 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) {
+ handler->dtmf_component = NULL;
rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR);
}
} else {
- char *grammar = NULL;
- const char *jsgf_path;
- component->speech_mode = 1;
- jsgf_path = srgs_grammar_to_jsgf_file(component->grammar, SWITCH_GLOBAL_dirs.grammar_dir, "gram");
- if (!jsgf_path) {
+ switch_stream_handle_t grammar = { 0 };
+ SWITCH_STANDARD_STREAM(grammar);
+
+ if (zstr(component->recognizer)) {
+ 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_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 */
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 */
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);
}
- switch_safe_free(grammar);
+ switch_safe_free(grammar.data);
}
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
*/
@@ -470,10 +529,10 @@ static iks *start_call_input_component(struct rayo_actor *call, struct rayo_mess
{
iks *iq = msg->payload;
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;
struct input_component *input_component = NULL;
- iks *input = iks_find(iq, "input");
const char *error = NULL;
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);
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"));
- switch_safe_free(component_id);
/* start input */
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);
if (input_component->speech_mode) {
switch_ivr_detect_speech_start_input_timers(session);
- rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP);
} else {
input_component->last_digit_time = switch_micro_time_now();
input_component->start_timers = 1;
@@ -549,53 +606,63 @@ static void on_detected_speech_event(switch_event_t *event)
{
const char *speech_type = switch_event_get_header(event, "Speech-Type");
char *event_str = NULL;
+ const char *uuid = switch_event_get_header(event, "Unique-ID");
switch_event_serialize(event, &event_str, SWITCH_FALSE);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s\n", event_str);
- if (!speech_type) {
+ if (!speech_type || !uuid) {
return;
}
+
if (!strcasecmp("detected-speech", speech_type)) {
- const char *uuid = switch_event_get_header(event, "Unique-ID");
- char *component_id = switch_mprintf("%s-input", uuid);
+ char *component_id = switch_mprintf("%s-input-voice", uuid);
struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id);
+
switch_safe_free(component_id);
if (component) {
const char *result = switch_event_get_body(event);
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);
if (zstr(result)) {
rayo_component_send_complete(component, INPUT_NOMATCH);
} else {
- enum nlsml_match_type match_type = nlsml_parse(result, uuid);
- switch (match_type) {
- case NMT_NOINPUT:
+ if (strchr(result, '<')) {
+ /* got an XML result */
+ enum nlsml_match_type match_type = nlsml_parse(result, uuid);
+ switch (match_type) {
+ case NMT_NOINPUT:
+ rayo_component_send_complete(component, INPUT_NOINPUT);
+ break;
+ case NMT_MATCH: {
+ iks *result_xml = nlsml_normalize(result);
+ send_match_event(RAYO_COMPONENT(component), result_xml);
+ iks_delete(result_xml);
+ break;
+ }
+ case NMT_BAD_XML:
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_WARNING, "Failed to parse NLSML result: %s!\n", result);
+ rayo_component_send_complete(component, INPUT_NOMATCH);
+ break;
+ case NMT_NOMATCH:
+ rayo_component_send_complete(component, INPUT_NOMATCH);
+ break;
+ default:
+ switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_CRIT, "Unknown NLSML match type: %i, %s!\n", match_type, result);
+ rayo_component_send_complete(component, INPUT_NOMATCH);
+ break;
+ }
+ } else if (strstr(result, "002")) {
+ /* Completion-Cause: 002 no-input-timeout */
rayo_component_send_complete(component, INPUT_NOINPUT);
- break;
- case NMT_MATCH: {
- iks *result_xml = nlsml_normalize(result);
- send_match_event(RAYO_COMPONENT(component), result_xml);
- iks_delete(result_xml);
- break;
- }
- case NMT_BAD_XML:
- switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_WARNING, "Failed to parse NLSML result: %s!\n", result);
+ } else {
+ /* assume no match */
rayo_component_send_complete(component, INPUT_NOMATCH);
- break;
- case NMT_NOMATCH:
- rayo_component_send_complete(component, INPUT_NOMATCH);
- break;
- default:
- switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_CRIT, "Unknown NLSML match type: %i, %s!\n", match_type, result);
- rayo_component_send_complete(component, INPUT_NOMATCH);
- break;
}
}
RAYO_UNLOCK(component);
}
} else if (!strcasecmp("begin-speaking", speech_type)) {
- const char *uuid = switch_event_get_header(event, "Unique-ID");
- char *component_id = switch_mprintf("%s-input", uuid);
+ char *component_id = switch_mprintf("%s-input-voice", uuid);
struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id);
switch_safe_free(component_id);
if (component && INPUT_COMPONENT(component)->barge_event) {
@@ -603,14 +670,13 @@ static void on_detected_speech_event(switch_event_t *event)
}
RAYO_UNLOCK(component);
} else if (!strcasecmp("closed", speech_type)) {
- const char *uuid = switch_event_get_header(event, "Unique-ID");
- char *component_id = switch_mprintf("%s-input", uuid);
+ char *component_id = switch_mprintf("%s-input-voice", uuid);
struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id);
switch_safe_free(component_id);
if (component) {
char *channel_state = switch_event_get_header(event, "Channel-State");
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_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Recognizer closed\n");
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);
}
+/**
+ * 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
+ * @param module_interface
+ * @param pool memory pool to allocate from
+ * @param config_file to use
* @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();
nlsml_init();
diff --git a/src/mod/event_handlers/mod_rayo/rayo_output_component.c b/src/mod/event_handlers/mod_rayo/rayo_output_component.c
index f92d994a64..e85a12cece 100644
--- a/src/mod/event_handlers/mod_rayo/rayo_output_component.c
+++ b/src/mod/event_handlers/mod_rayo/rayo_output_component.c
@@ -1092,9 +1092,12 @@ static char *fileman_supported_formats[] = { "fileman", NULL };
/**
* Initialize output component
+ * @param module_interface
+ * @param pool memory pool to allocate from
+ * @param config_file to use
* @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_file_interface_t *file_interface;
diff --git a/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c b/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c
index 47dbc46df3..f48e7d02d2 100644
--- a/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c
+++ b/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c
@@ -620,9 +620,12 @@ static iks *forward_output_component_request(struct rayo_actor *prompt, struct r
/**
* Initialize prompt component
+ * @param module_interface
+ * @param pool memory pool to allocate from
+ * @param config_file to use
* @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 and