From 95a2c11b0da7ba890782652b934072a332f75f4b Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Tue, 25 Jun 2013 22:19:24 -0400 Subject: [PATCH 01/26] Added ability to play a wav file as ringback tone during the COLLECT state of E&M signaling module This is configured through 2 new parameters: ringback-during-collect=yes|no ringback-file= You may not want to use this if your E&M lines are connected to traditional phones, otherwise you will hear ringback tone while pressing digits. This is mostly useful with old switches that do not provide ringback tone but the user is already done dialing (perhaps the signaling was converted from ISDN to E&M and the full number was received in a single SETUP message) --- .../ftmod/ftmod_analog_em/ftdm_analog_em.h | 3 + .../ftmod/ftmod_analog_em/ftmod_analog_em.c | 184 +++++++++++++++++- 2 files changed, 181 insertions(+), 6 deletions(-) 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..cace914243 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,14 @@ 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 + )) { + indicate = 1; + } if (!indicate) { continue; @@ -582,7 +732,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"); + } + 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 +764,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 +789,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"); From a55b5347ba62420e1c705f05723d421876367ee3 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Tue, 25 Jun 2013 23:41:30 -0400 Subject: [PATCH 02/26] mod_freetdm: Expose new ringback E&M parameters in the XML config --- libs/freetdm/mod_freetdm/mod_freetdm.c | 9 +++++++++ libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index 316f5607c2..f980930f94 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -3785,7 +3785,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 +3817,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 +3874,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/ftmod/ftmod_analog_em/ftmod_analog_em.c b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c index cace914243..c3f7c20bbe 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 @@ -738,7 +738,7 @@ 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"); + ftdm_log(FTDM_LOG_ERROR, "Error reading from ringback file: %zd != %zd\n", rlen, len); } if (failed_read) { continue; From c0fe9c9cd3d0d4ee3e66c6343040c4521b28f34c Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Wed, 26 Jun 2013 00:10:04 -0400 Subject: [PATCH 03/26] freetdm: Override regular media with ringback in E&M when a ringback file is specified --- libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 c3f7c20bbe..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 @@ -719,7 +719,9 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj) 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_RINGING || + ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS || + ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA )) { indicate = 1; } From f49dc139ad4bb86067aac4a185588f69355bc699 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Wed, 26 Jun 2013 00:16:11 -0400 Subject: [PATCH 04/26] freetdm: Added E & M logic for routing success and fail regex parameters --- libs/freetdm/mod_freetdm/mod_freetdm.c | 43 ++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index f980930f94..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) From 7ff70bd5cd7a49094aebc24ff2aadf7687d9023d Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Wed, 3 Jul 2013 02:07:05 -0400 Subject: [PATCH 05/26] freetdm: Fix longstanding minor bug in ftdm_span_send_signal causing dial-regex in Analog modules to not work The return status of the signal callback was not being passed to the signaling module delivering the signal --- libs/freetdm/src/ftdm_io.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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) From e84e92dc8530792d375f8f1a4900e093113a0935 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Tue, 13 Aug 2013 20:28:47 +0000 Subject: [PATCH 06/26] Add lua script for proxying ZRTP SAS values to legacy phones Ken is creating a bump-in-the-wire box for legacy IP phones and might find this script useful. --- scripts/lua/zrtp_sas_proxy.lua | 103 +++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 scripts/lua/zrtp_sas_proxy.lua 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 From bdc2d307c485a582aa0abd67b4d0db023f5dccd7 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 14 Aug 2013 02:08:54 +0500 Subject: [PATCH 07/26] FS-5698 --resolve --- src/mod/endpoints/mod_sofia/sofia_presence.c | 5 +++++ 1 file changed, 5 insertions(+) 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; } From 5bf89d1d872415a3934a8b5512f7c13d14836327 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 14 Aug 2013 02:13:49 +0500 Subject: [PATCH 08/26] FS-5453 --resolve --- src/mod/endpoints/mod_loopback/mod_loopback.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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)); From 2131cd6cf2861e3d5fc7aeb0feb81f524e9f4a5c Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 14 Aug 2013 02:24:53 +0500 Subject: [PATCH 09/26] FS-5683 --resolve --- src/mod/endpoints/mod_sofia/sofia_reg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); } From 217a7c5ff846ee4b45e0f4fd2c6caf05b6236c25 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Wed, 14 Aug 2013 09:41:11 -0400 Subject: [PATCH 10/26] mod_rayo: add support for speech recognizers other than pocketsphinx, fixed some input component bugs, allow simultaneous dtmf and voice input. --- conf/rayo/autoload_configs/rayo.conf.xml | 5 + .../conf/autoload_configs/rayo.conf.xml | 5 + src/mod/event_handlers/mod_rayo/iks_helpers.c | 59 +++ src/mod/event_handlers/mod_rayo/iks_helpers.h | 5 + src/mod/event_handlers/mod_rayo/mod_rayo.c | 36 ++ .../event_handlers/mod_rayo/rayo_components.c | 13 +- .../event_handlers/mod_rayo/rayo_components.h | 8 +- .../event_handlers/mod_rayo/rayo_elements.c | 2 +- .../mod_rayo/rayo_input_component.c | 397 ++++++++++++------ .../mod_rayo/rayo_output_component.c | 5 +- .../mod_rayo/rayo_prompt_component.c | 5 +- .../mod_rayo/rayo_record_component.c | 3 +- .../event_handlers/mod_rayo/test_iks/main.c | 22 + 13 files changed, 408 insertions(+), 157 deletions(-) diff --git a/conf/rayo/autoload_configs/rayo.conf.xml b/conf/rayo/autoload_configs/rayo.conf.xml index 0cb46d7992..248fd47ccf 100644 --- a/conf/rayo/autoload_configs/rayo.conf.xml +++ b/conf/rayo/autoload_configs/rayo.conf.xml @@ -11,6 +11,11 @@ + + + + + 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..248fd47ccf 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 @@ + + + + + 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..69f8b2271b 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -3737,6 +3737,14 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load) "" ""); + rayo_add_cmd_alias("prompt_barge_mrcp", "" + "

Please press a digit.

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

Please press a digit.

]]>
" "" @@ -3800,6 +3808,34 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load) ""); rayo_add_cmd_alias("unjoin", ""); + rayo_add_cmd_alias("input_voice_yesno_unimrcp", + "" + "" + "yesno]]>"); +rayo_add_cmd_alias("input_voice_yesno_unimrcp_timeout", + "" + "" + "yesno]]>"); + rayo_add_cmd_alias("input_voice_yesno_pocketsphinx", + "" + "" + "yesno]]>"); + rayo_add_cmd_alias("input_voice_yesno_default", + "" + "" + "yesno]]>"); return SWITCH_STATUS_SUCCESS; } diff --git a/src/mod/event_handlers/mod_rayo/rayo_components.c b/src/mod/event_handlers/mod_rayo/rayo_components.c index 54e241d036..d8a8854f52 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_components.c +++ b/src/mod/event_handlers/mod_rayo/rayo_components.c @@ -227,15 +227,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; } From a8f2e684670c9d3b8139930abcb0509f5c4589fd Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Wed, 14 Aug 2013 10:06:18 -0400 Subject: [PATCH 11/26] mod_unimrcp: add example config for Vestec --- conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + From 6726059e472a74aac0c7280558a1a56e9446d1d7 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 14 Aug 2013 09:05:14 -0500 Subject: [PATCH 12/26] FS-5694 --resolve --- src/mod/applications/mod_voicemail/mod_voicemail.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From a29b2c2f7f9e47685ed485dc95c0c949f24cc9e8 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Wed, 14 Aug 2013 09:49:48 -0500 Subject: [PATCH 13/26] FS-5648 --resolve --- src/mod/endpoints/mod_sofia/sofia.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 4faea7ac42..c98417f374 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -8582,6 +8582,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); From b0371ee702751970d649f7b73b4b526289148404 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 14 Aug 2013 21:00:08 +0500 Subject: [PATCH 14/26] FS-5701 --resolve --- src/switch_ivr.c | 6 ++++++ 1 file changed, 6 insertions(+) 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); From f7d4ff390a5f03b7082dbe375d177249ae15f992 Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Wed, 14 Aug 2013 18:00:30 -0400 Subject: [PATCH 15/26] mod_rayo: move alias definition to config file, tweak console command completion to make testing easier --- conf/rayo/autoload_configs/rayo.conf.xml | 207 +++++++ .../conf/autoload_configs/rayo.conf.xml | 207 +++++++ src/mod/event_handlers/mod_rayo/mod_rayo.c | 504 +++++++++--------- src/mod/event_handlers/mod_rayo/mod_rayo.h | 1 + .../event_handlers/mod_rayo/rayo_components.c | 2 +- 5 files changed, 681 insertions(+), 240 deletions(-) diff --git a/conf/rayo/autoload_configs/rayo.conf.xml b/conf/rayo/autoload_configs/rayo.conf.xml index 248fd47ccf..35419319bc 100644 --- a/conf/rayo/autoload_configs/rayo.conf.xml +++ b/conf/rayo/autoload_configs/rayo.conf.xml @@ -47,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/conf/autoload_configs/rayo.conf.xml b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml index 248fd47ccf..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 @@ -47,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/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index 69f8b2271b..be66fe877c 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) @@ -827,7 +833,7 @@ int rayo_actor_seq_next(struct rayo_actor *actor) static struct rayo_call *rayo_call_locate(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); @@ -3077,6 +3083,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 +3283,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 +3397,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 +3435,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 +3451,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 +3490,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 +3506,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 +3531,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,134 +3842,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_barge_mrcp", "" - "

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", - ""); - rayo_add_cmd_alias("input_voice_yesno_unimrcp", - "" - "" - "yesno]]>"); -rayo_add_cmd_alias("input_voice_yesno_unimrcp_timeout", - "" - "" - "yesno]]>"); - rayo_add_cmd_alias("input_voice_yesno_pocketsphinx", - "" - "" - "yesno]]>"); - rayo_add_cmd_alias("input_voice_yesno_default", - "" - "" - "yesno]]>"); return SWITCH_STATUS_SUCCESS; } @@ -3844,9 +3864,15 @@ rayo_add_cmd_alias("input_voice_yesno_unimrcp_timeout", */ 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..260c811a45 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.h +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.h @@ -162,6 +162,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 d8a8854f52..b376e97770 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); From 43a11b00aadc22293730aab903990e1f2abd7d2b Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 16 Jul 2013 19:28:02 -0500 Subject: [PATCH 16/26] Fix segfault in mod_conference This was introduced by commit a4408e62a6ca1bc36e26109396af332d05fae812. FS-5612 --resolve --- src/mod/applications/mod_conference/mod_conference.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 */ From ed6a090483814d38ea83c3e93bff929b2e8021ef Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 15 Aug 2013 21:19:51 +0500 Subject: [PATCH 17/26] FS-5700 --resolve --- src/mod/endpoints/mod_sofia/mod_sofia.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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" : ""); } } From 64ade54a73723caca2e8df9e2260b89bf6b7e4cd Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Fri, 16 Aug 2013 09:52:14 -0400 Subject: [PATCH 18/26] FS-5528 --resolve --- src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) 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; From 77d0ee21b2bf64d16e665ef43acfdda98f3514b3 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Sat, 17 Aug 2013 02:13:49 +0500 Subject: [PATCH 19/26] FS-5708 --resolve --- src/switch_utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_utils.c b/src/switch_utils.c index da89f0c9c0..d49e8b03b5 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 && addr->ifa_addr->sa_family == AF_INET && s->sin_addr && s->sin_addr.s_addr == me->sin_addr.s_addr) { *mask = m->sin_addr.s_addr; freeifaddrs(ifaddrs); return 0; From 24493713295b890ab5e818a16bb0a1784c76d2a7 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Sat, 17 Aug 2013 02:16:28 +0500 Subject: [PATCH 20/26] FS-5709 --resolve --- src/include/switch_console.h | 1 + src/include/switch_utils.h | 15 ++++- .../applications/mod_commands/mod_commands.c | 47 ++++++++++++++++ src/mod/endpoints/mod_sofia/sofia.c | 26 +++++++++ src/switch_console.c | 46 ++++++++++++++++ src/switch_utils.c | 55 +++++++++++++++++++ 6 files changed, 189 insertions(+), 1 deletion(-) 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/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index c98417f374..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; } 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_utils.c b/src/switch_utils.c index d49e8b03b5..2c723a73ae 100644 --- a/src/switch_utils.c +++ b/src/switch_utils.c @@ -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 }; From 8566ffa82a26202cc2aae5ec031c500f0bffff91 Mon Sep 17 00:00:00 2001 From: Ken Rice Date: Fri, 16 Aug 2013 17:54:03 -0500 Subject: [PATCH 21/26] Revert FS-5708 ; build issues This reverts commit 77d0ee21b2bf64d16e665ef43acfdda98f3514b3. --- src/switch_utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_utils.c b/src/switch_utils.c index 2c723a73ae..08e4418b01 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 && addr->ifa_addr->sa_family == AF_INET && s->sin_addr && s->sin_addr.s_addr == me->sin_addr.s_addr) { + if (s && m && s->sin_addr.s_addr == me->sin_addr.s_addr) { *mask = m->sin_addr.s_addr; freeifaddrs(ifaddrs); return 0; From 93c7e496c74b29ddf420255eaa518d669e3e2134 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 19 Aug 2013 23:48:09 +0500 Subject: [PATCH 22/26] FS-5708 --resolve --- src/switch_utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/switch_utils.c b/src/switch_utils.c index 08e4418b01..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; From cad11edb76475e5308ffdeaec3b21ce4b327df83 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Tue, 20 Aug 2013 09:24:43 -0500 Subject: [PATCH 23/26] FS-5164 --resolve add support for setting user variables w/ skinny --- src/mod/endpoints/mod_skinny/skinny_server.c | 43 ++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index dff09844e7..3eb4e7b514 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,44 @@ 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; + + /* 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 +534,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); From 1d30a502b837c987c4aa63b41105866b5743ab55 Mon Sep 17 00:00:00 2001 From: Nathan Neulinger Date: Tue, 20 Aug 2013 13:01:40 -0500 Subject: [PATCH 24/26] FS-5164 - fix segv on ring handling due to listener not being defined --- src/mod/endpoints/mod_skinny/skinny_server.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index 3eb4e7b514..9040c2c141 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -488,6 +488,12 @@ int skinny_session_set_variables_callback(void *pArg, int argc, char **argv, cha 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", From 93fec8dd75be26b1f262e4be48990f0dc5692b10 Mon Sep 17 00:00:00 2001 From: Ben Langfeld Date: Tue, 20 Aug 2013 17:11:27 -0300 Subject: [PATCH 25/26] mod_rayo: Remove compensation for Punchblock Rayo spec incompatibility --- src/mod/event_handlers/mod_rayo/mod_rayo.c | 4 ---- src/mod/event_handlers/mod_rayo/mod_rayo.h | 3 --- src/mod/event_handlers/mod_rayo/rayo_components.c | 4 ---- 3 files changed, 11 deletions(-) diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index be66fe877c..dc2e4aafce 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -2556,11 +2556,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; } diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.h b/src/mod/event_handlers/mod_rayo/mod_rayo.h index 260c811a45..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; diff --git a/src/mod/event_handlers/mod_rayo/rayo_components.c b/src/mod/event_handlers/mod_rayo/rayo_components.c index b376e97770..b5137d9459 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_components.c +++ b/src/mod/event_handlers/mod_rayo/rayo_components.c @@ -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); } From 83e33dd150c320424f73b77511c2de9609b76ace Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Tue, 20 Aug 2013 17:26:07 -0400 Subject: [PATCH 26/26] mod_rayo: use XMPP URI instead of FS UUID for join/unjoin --- src/mod/event_handlers/mod_rayo/mod_rayo.c | 114 ++++++++++++--------- 1 file changed, 67 insertions(+), 47 deletions(-) diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index dc2e4aafce..3004601661 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -718,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) { @@ -824,13 +827,30 @@ 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 && is_call_actor(actor)) { @@ -1692,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"); @@ -1712,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; } @@ -1797,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)) { @@ -1806,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; } @@ -1838,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: @@ -1850,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); } @@ -1919,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 { @@ -2043,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"); @@ -2066,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); @@ -2399,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; @@ -2415,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); @@ -2451,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 */ @@ -2493,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); } @@ -2539,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; @@ -2569,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 @@ -2593,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"), @@ -2612,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) { @@ -2637,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) { @@ -2646,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); @@ -2654,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); @@ -2679,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) { @@ -2688,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;