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 @@ + + + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + 0123456789]]]]> + + + ]]> + + + + + yesno + ]]]]> + + + ]]> + + + + + yesno + ]]]]> + + + ]]> + + + + + + yesno + ]]]]> + + + ]]> + + + + + + yesno + ]]]]> + + + ]]> + + +
+ 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 @@ + + + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + +

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + 0123456789]]]]> + + + ]]> + + + + + yesno + ]]]]> + + + ]]> + + + + + yesno + ]]]]> + + + ]]> + + + + + + yesno + ]]]]> + + + ]]> + + + + + + yesno + ]]]]> + + + ]]> + + +
+ 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", "" - "

Please press a digit.

]]>
" - "" - "" - "0123456789]]>" - "" - "
"); - - rayo_add_cmd_alias("prompt_no_barge", "" - "

Please press a digit.

]]>
" - "" - "" - "0123456789]]>" - "" - "
"); - - rayo_add_cmd_alias("prompt_long", "" - "" - "" - "" - "0123456789]]>" - "" - ""); - - rayo_add_cmd_alias("prompt_multi_digit", "" - "" - "" - "" - "0123456789]]>" - "" - ""); - - rayo_add_cmd_alias("prompt_terminator", "" - "" - "" - "" - "0123456789]]>" - "" - ""); - - rayo_add_cmd_alias("prompt_input_bad", "" - "" - "" - "" - "0123456789]]>" - "" - ""); - - rayo_add_cmd_alias("prompt_output_bad", "" - "" - "" - "" - "0123456789]]>" - "" - ""); - rayo_add_cmd_alias("input", "" - "" - "0123456789*#]]>" - ""); - 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 */ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_PROMPT_NS":prompt", start_call_prompt_component); diff --git a/src/mod/event_handlers/mod_rayo/rayo_record_component.c b/src/mod/event_handlers/mod_rayo/rayo_record_component.c index 2db5b30771..601d8cb3aa 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_record_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_record_component.c @@ -479,11 +479,12 @@ static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_ /** * Initialize record 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_record_component_load(switch_memory_pool_t *pool, const char *config_file) +switch_status_t rayo_record_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) { if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_TERM; diff --git a/src/mod/event_handlers/mod_rayo/test_iks/main.c b/src/mod/event_handlers/mod_rayo/test_iks/main.c index 699f40267e..09a368dc4b 100644 --- a/src/mod/event_handlers/mod_rayo/test_iks/main.c +++ b/src/mod/event_handlers/mod_rayo/test_iks/main.c @@ -145,6 +145,27 @@ static void test_dialback_key(void) ASSERT_NULL(iks_server_dialback_key("s3cr3tf0rd14lb4ck", "xmpp.example.com", "example.org", NULL)); } +static void test_validate_dtmf(void) +{ + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("1")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("A")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("a")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("D")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("d")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("*")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("#")); + ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit("E")); + ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit(NULL)); + ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit("")); + ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit("11")); + ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "A")); + ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "1")); + ASSERT_EQUALS(SWITCH_FALSE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "Z")); + ASSERT_EQUALS(SWITCH_FALSE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "11")); + ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, NULL)); + ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "")); +} + /** * main program */ @@ -159,5 +180,6 @@ int main(int argc, char **argv) TEST(test_rayo_test_srgs); TEST(test_iks_helper_value_matches); TEST(test_dialback_key); + TEST(test_validate_dtmf); return 0; } diff --git a/src/switch_console.c b/src/switch_console.c index b1e2c8e271..b2c003a6c6 100644 --- a/src/switch_console.c +++ b/src/switch_console.c @@ -33,6 +33,7 @@ #include #include #include +#include #define CMD_BUFLEN 1024 #ifdef SWITCH_HAVE_LIBEDIT @@ -619,6 +620,36 @@ SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_loaded_modules(const return SWITCH_STATUS_FALSE; } +#ifdef HAVE_GETIFADDRS +#include +#include +SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_interfaces(const char *line, const char *cursor, switch_console_callback_match_t **matches) +{ + struct match_helper h = { 0 }; + struct ifaddrs *addrs, *addr; + + getifaddrs(&addrs); + for(addr = addrs; addr; addr = addr->ifa_next) { + if (addr->ifa_flags & IFF_UP) { + switch_console_push_match_unique(&h.my_matches, addr->ifa_name); + } + } + freeifaddrs(addrs); + + if (h.my_matches) { + *matches = h.my_matches; + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; +} +#else +SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_interfaces(const char *line, const char *cursor, switch_console_callback_match_t **matches) +{ + return SWITCH_STATUS_FALSE; +} +#endif + static int uuid_callback(void *pArg, int argc, char **argv, char **columnNames) { struct match_helper *h = (struct match_helper *) pArg; @@ -1631,6 +1662,7 @@ SWITCH_DECLARE(switch_status_t) switch_console_init(switch_memory_pool_t *pool) switch_core_hash_init(&globals.func_hash, pool); switch_console_add_complete_func("::console::list_available_modules", (switch_console_complete_callback_t) switch_console_list_available_modules); switch_console_add_complete_func("::console::list_loaded_modules", (switch_console_complete_callback_t) switch_console_list_loaded_modules); + switch_console_add_complete_func("::console::list_interfaces", (switch_console_complete_callback_t) switch_console_list_interfaces); switch_console_add_complete_func("::console::list_uuid", (switch_console_complete_callback_t) switch_console_list_uuid); return SWITCH_STATUS_SUCCESS; } @@ -1741,6 +1773,20 @@ SWITCH_DECLARE(void) switch_console_sort_matches(switch_console_callback_match_t } } +SWITCH_DECLARE(void) switch_console_push_match_unique(switch_console_callback_match_t **matches, const char *new_val) +{ + /* Ignore the entry if it is already in the list */ + if (*matches) { + switch_console_callback_match_node_t *node; + + for(node = (*matches)->head; node; node = node->next) { + if (!strcasecmp(node->val, new_val)) return; + } + } + + switch_console_push_match(matches, new_val); +} + SWITCH_DECLARE(void) switch_console_push_match(switch_console_callback_match_t **matches, const char *new_val) { switch_console_callback_match_node_t *match; diff --git a/src/switch_ivr.c b/src/switch_ivr.c index f424a4668b..6cfdca4dad 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -2905,6 +2905,12 @@ SWITCH_DECLARE(void) switch_ivr_delay_echo(switch_core_session_t *session, uint3 interval = read_impl.microseconds_per_packet / 1000; //samples = switch_samples_per_packet(read_impl.samples_per_second, interval); + if (delay_ms < interval * 2) { + delay_ms = interval * 2; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Minimum possible delay for this codec (%d) has been chosen\n", delay_ms); + } + + qlen = delay_ms / (interval) / 2; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Setting delay to %dms (%d frames)\n", delay_ms, qlen); jb = stfu_n_init(qlen, qlen, read_impl.samples_per_packet, read_impl.samples_per_second, 0); diff --git a/src/switch_utils.c b/src/switch_utils.c index da89f0c9c0..36ee3a36ca 100644 --- a/src/switch_utils.c +++ b/src/switch_utils.c @@ -1262,7 +1262,7 @@ static int get_netmask(struct sockaddr_in *me, int *mask) struct sockaddr_in *s = (struct sockaddr_in *) i->ifa_addr; struct sockaddr_in *m = (struct sockaddr_in *) i->ifa_netmask; - if (s && m && s->sin_addr.s_addr == me->sin_addr.s_addr) { + if (s && m && s->sin_family == AF_INET && s->sin_addr.s_addr == me->sin_addr.s_addr) { *mask = m->sin_addr.s_addr; freeifaddrs(ifaddrs); return 0; @@ -1569,6 +1569,61 @@ SWITCH_DECLARE(switch_status_t) switch_find_local_ip(char *buf, int len, int *ma return status; } +#ifdef HAVE_GETIFADDRS +# include +# include +#endif +SWITCH_DECLARE(switch_status_t) switch_find_interface_ip(char *buf, int len, int *mask, const char *ifname, int family) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + +#ifdef HAVE_GETIFADDRS + + struct ifaddrs *addrs, *addr; + + getifaddrs(&addrs); + for(addr = addrs; addr; addr = addr->ifa_next) + { + if (!(addr->ifa_flags & IFF_UP)) continue; // Address is not UP + if (!addr->ifa_addr) continue; // No address set + if (!addr->ifa_netmask) continue; // No netmask set + if (family != AF_UNSPEC && addr->ifa_addr->sa_family != family) continue; // Not the address family we're looking for + if (strcmp(addr->ifa_name, ifname)) continue; // Not the interface we're looking for + + switch(addr->ifa_addr->sa_family) { + case AF_INET: + inet_ntop(AF_INET, &( ((struct sockaddr_in*)(addr->ifa_addr))->sin_addr ), buf, len - 1); + break; + case AF_INET6: + inet_ntop(AF_INET6, &( ((struct sockaddr_in6*)(addr->ifa_addr))->sin6_addr ), buf, len - 1); + break; + default: + continue; + } + + if (mask && addr->ifa_netmask->sa_family == AF_INET) { + *mask = ((struct sockaddr_in*)(addr->ifa_addr))->sin_addr.s_addr; + } + + status = SWITCH_STATUS_SUCCESS; + break; + } + freeifaddrs(addrs); + +#elif defined(__linux__) + + // TODO Not implemented, contributions welcome. + +#elif defined(WIN32) + + // TODO Not implemented, contributions welcome. + +#endif + + return status; +} + + SWITCH_DECLARE(switch_time_t) switch_str_time(const char *in) { switch_time_exp_t tm = { 0 };