diff --git a/conf/rayo/autoload_configs/rayo.conf.xml b/conf/rayo/autoload_configs/rayo.conf.xml index 0cb46d7992..35419319bc 100644 --- a/conf/rayo/autoload_configs/rayo.conf.xml +++ b/conf/rayo/autoload_configs/rayo.conf.xml @@ -11,6 +11,11 @@ + + + + + @@ -42,4 +47,211 @@ + + + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + + + + +

Please press a digit.

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

Please press a digit.

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

Please press a digit.

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

Please press a digit.

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

Please press a digit.

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

Please press a digit.

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

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + 0123456789]]]]> + + + ]]> + + + + + yesno + ]]]]> + + + ]]> + + + + + yesno + ]]]]> + + + ]]> + + + + + + yesno + ]]]]> + + + ]]> + + + + + + yesno + ]]]]> + + + ]]> + + +
+ diff --git a/conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml b/conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml new file mode 100644 index 0000000000..cbde87ca5a --- /dev/null +++ b/conf/vanilla/mrcp_profiles/vestec-mrcp-v1.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conf/vanilla/vars.xml b/conf/vanilla/vars.xml index c3f235bfe3..1abbb240f7 100644 --- a/conf/vanilla/vars.xml +++ b/conf/vanilla/vars.xml @@ -14,6 +14,28 @@ --> + + diff --git a/libs/apr/.update b/libs/apr/.update index c106d5b06f..1aeaab4078 100644 --- a/libs/apr/.update +++ b/libs/apr/.update @@ -1 +1 @@ -Wed Nov 7 10:37:54 CST 2012 +Tue Aug 27 13:58:18 EDT 2013 diff --git a/libs/apr/network_io/unix/sockets.c b/libs/apr/network_io/unix/sockets.c index 6d24cdcf00..4cff3bbca1 100644 --- a/libs/apr/network_io/unix/sockets.c +++ b/libs/apr/network_io/unix/sockets.c @@ -32,6 +32,10 @@ static apr_status_t socket_cleanup(void *sock) { apr_socket_t *thesocket = sock; + if (thesocket && thesocket->socketdes == -1) { + return APR_SUCCESS; + } + if (close(thesocket->socketdes) == 0) { thesocket->socketdes = -1; return APR_SUCCESS; diff --git a/libs/esl/src/esl_event.c b/libs/esl/src/esl_event.c index 910180f47e..163d35cc6c 100644 --- a/libs/esl/src/esl_event.c +++ b/libs/esl/src/esl_event.c @@ -98,6 +98,8 @@ static const char *EVENT_NAMES[] = { "MESSAGE", "PRESENCE_IN", "NOTIFY_IN", + "PHONE_FEATURE", + "PHONE_FEATURE_SUBSCRIBE", "PRESENCE_OUT", "PRESENCE_PROBE", "MESSAGE_WAITING", diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index 316f5607c2..63d4cd9644 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -2241,7 +2241,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal) { switch_core_session_t *session = NULL; switch_channel_t *channel = NULL; - ftdm_status_t status; + ftdm_status_t status = FTDM_SUCCESS; uint32_t spanid; uint32_t chanid; ftdm_caller_data_t *caller_data; @@ -2296,6 +2296,45 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal) break; case FTDM_SIGEVENT_SIGSTATUS_CHANGED: case FTDM_SIGEVENT_COLLECTED_DIGIT: /* Analog E&M */ + { + int span_id = ftdm_channel_get_span_id(sigmsg->channel); + char *dtmf = sigmsg->ev_data.collected.digits; + char *regex = SPAN_CONFIG[span_id].dial_regex; + char *fail_regex = SPAN_CONFIG[span_id].fail_dial_regex; + ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(sigmsg->channel); + + if (zstr(regex)) { + regex = NULL; + } + + if (zstr(fail_regex)) { + fail_regex = NULL; + } + + ftdm_log(FTDM_LOG_DEBUG, "got DTMF sig [%s]\n", dtmf); + switch_set_string(caller_data->collected, dtmf); + + if ((regex || fail_regex) && !zstr(dtmf)) { + switch_regex_t *re = NULL; + int ovector[30]; + int match = 0; + + if (fail_regex) { + match = switch_regex_perform(dtmf, fail_regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); + status = match ? FTDM_SUCCESS : FTDM_BREAK; + switch_regex_safe_free(re); + ftdm_log(FTDM_LOG_DEBUG, "DTMF [%s] vs fail regex %s %s\n", dtmf, fail_regex, match ? "matched" : "did not match"); + } + + if (status == FTDM_SUCCESS && regex) { + match = switch_regex_perform(dtmf, regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); + status = match ? FTDM_BREAK : FTDM_SUCCESS; + switch_regex_safe_free(re); + ftdm_log(FTDM_LOG_DEBUG, "DTMF [%s] vs dial regex %s %s\n", dtmf, regex, match ? "matched" : "did not match"); + } + ftdm_log(FTDM_LOG_DEBUG, "returning %s to COLLECT event with DTMF %s\n", status == FTDM_SUCCESS ? "success" : "break", dtmf); + } + } break; default: { @@ -2305,7 +2344,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal) break; } - return FTDM_SUCCESS; + return status; } static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal) @@ -3785,7 +3824,10 @@ static switch_status_t load_config(void) char *hold_music = NULL; char *fail_dial_regex = NULL; char str_false[] = "false"; + char str_empty[] = ""; char *answer_supervision = str_false; + char *immediate_ringback = str_false; + char *ringback_file = str_empty; uint32_t span_id = 0, to = 0, max = 0, dial_timeout_int = 0; ftdm_span_t *span = NULL; analog_option_t analog_options = ANALOG_OPTION_NONE; @@ -3814,6 +3856,10 @@ static switch_status_t load_config(void) max_digits = val; } else if (!strcasecmp(var, "answer-supervision")) { answer_supervision = val; + } else if (!strcasecmp(var, "immediate-ringback")) { + immediate_ringback = val; + } else if (!strcasecmp(var, "ringback-file")) { + ringback_file = val; } else if (!strcasecmp(var, "enable-analog-option")) { analog_options = enable_analog_option(val, analog_options); } @@ -3867,6 +3913,8 @@ static switch_status_t load_config(void) if (ftdm_configure_span(span, "analog_em", on_analog_signal, "tonemap", tonegroup, "answer_supervision", answer_supervision, + "immediate_ringback", immediate_ringback, + "ringback_file", ringback_file, "digit_timeout", &to, "dial_timeout", &dial_timeout_int, "max_dialstr", &max, diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 9ea22fbbfe..57d5af1b85 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -6089,6 +6089,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); @@ -6193,7 +6194,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: @@ -6202,7 +6203,7 @@ done: ftdm_channel_unlock(fchan); } - return FTDM_SUCCESS; + return status; } static void *ftdm_cpu_monitor_run(ftdm_thread_t *me, void *obj) diff --git a/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h b/libs/freetdm/src/ftmod/ftmod_analog_em/ftdm_analog_em.h index 37c90c3b4f..121381d522 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 immediate_ringback; + 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 598a8cd302..c387d5a528 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); /** @@ -70,6 +171,19 @@ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(analog_em_outgoing_call) return FTDM_FAIL; } +static ftdm_status_t ftdm_analog_em_sig_write(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size) +{ + ftdm_analog_em_data_t *analog_data = ftdmchan->span->signal_data; + if (ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA + && analog_data->immediate_ringback + && ftdmchan->call_data) { + /* DO NOT USE ftdmchan->call_data, as is a dummy non-null pointer */ + /* ringback is being played in the analog thread, ignore user data for now */ + return FTDM_BREAK; + } + return FTDM_SUCCESS; +} + /** * \brief Starts an EM span thread (monitor) * \param span Span to monitor @@ -82,6 +196,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 @@ -120,8 +247,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 immediate_ringback = FTDM_FALSE; uint32_t digit_timeout = 2000; uint32_t max_dialstr = 11; uint32_t dial_timeout = 0; @@ -136,9 +265,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); @@ -147,6 +275,16 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span) break; } tonemap = val; + } else if (!strcasecmp(var, "immediate_ringback")) { + if (!(val = va_arg(ap, char *))) { + break; + } + immediate_ringback = 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; @@ -184,6 +322,8 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_analog_em_configure_span) } span->start = ftdm_analog_em_start; + span->stop = ftdm_analog_em_stop; + span->sig_write = ftdm_analog_em_sig_write; analog_data->digit_timeout = digit_timeout; analog_data->max_dialstr = max_dialstr; analog_data->dial_timeout = dial_timeout; @@ -195,6 +335,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 (immediate_ringback || !ftdm_strlen_zero(ringback_file)) { + analog_data->immediate_ringback = FTDM_TRUE; + ftdm_set_string(analog_data->ringback_file, ringback_file); + } return FTDM_SUCCESS; @@ -241,6 +385,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_unused_arg(me); @@ -283,6 +428,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->immediate_ringback && !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; @@ -464,9 +621,11 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj) break; case FTDM_CHANNEL_STATE_RINGING: { - ftdm_buffer_zero(dt_buffer); - teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_RING]); - indicate = 1; + if (!analog_data->immediate_ringback) { + ftdm_buffer_zero(dt_buffer); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_RING]); + indicate = 1; + } } break; case FTDM_CHANNEL_STATE_BUSY: @@ -515,7 +674,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); @@ -531,7 +689,8 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj) continue; } - len = sizeof(frame); + /* Do not try to read more than the proper interval size */ + len = ftdmchan->packet_len * 2; if (ftdm_channel_read(ftdmchan, frame, &len) != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_ERROR, "READ ERROR [%s]\n", ftdmchan->last_error); goto done; @@ -542,6 +701,11 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj) continue; } + if (len >= (sizeof(frame)/2)) { + ftdm_log(FTDM_LOG_CRIT, "Ignoring big read of %zd bytes!\n", len); + continue; + } + if (ftdmchan->detected_tones[0]) { int i; @@ -576,6 +740,20 @@ 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->immediate_ringback && + (ftdmchan->state == FTDM_CHANNEL_STATE_COLLECT || + ftdmchan->state == FTDM_CHANNEL_STATE_RING || + ftdmchan->state == FTDM_CHANNEL_STATE_RINGING || + ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS || + ftdmchan->state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA + )) { + indicate = 1; + if (!ringback_f) { + ftdm_buffer_zero(dt_buffer); + teletone_run(&ts, ftdmchan->span->tone_map[FTDM_TONEMAP_RING]); + } + } if (!indicate) { continue; @@ -585,7 +763,25 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj) len *= 2; } - rlen = ftdm_buffer_read_loop(dt_buffer, frame, len); + if (ringback_f) { + uint8_t failed_read = 0; +read_try: + rlen = fread(frame, 1, len, ringback_f); + if (rlen != len) { + if (!feof(ringback_f)) { + ftdm_log(FTDM_LOG_ERROR, "Error reading from ringback file: %zd != %zd\n", rlen, len); + } + if (failed_read) { + continue; + } + /* return cursor to start of wav file */ + fseek(ringback_f, WAVE_HEADER_LEN, SEEK_SET); + failed_read++; + goto read_try; + } + } else { + rlen = ftdm_buffer_read_loop(dt_buffer, frame, len); + } if (ftdmchan->effective_codec != FTDM_CODEC_SLIN) { fio_codec_t codec_func = NULL; @@ -599,12 +795,17 @@ 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; } } + /* we must lock the channel and make sure we let our own generated audio thru (ftdmchan->call_data is tested in the ftdm_analog_em_sig_write handler)*/ + ftdm_channel_lock(ftdmchan); + ftdmchan->call_data = (void *)0xFF; /* ugh! */ ftdm_channel_write(ftdmchan, frame, sizeof(frame), &rlen); + ftdmchan->call_data = NULL; + ftdm_channel_unlock(ftdmchan); } done: @@ -624,6 +825,10 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj) ftdm_buffer_destroy(&dt_buffer); } + if (ringback_f) { + fclose(ringback_f); + } + ftdm_clear_flag(closed_chan, FTDM_CHANNEL_INTHREAD); ftdm_log(FTDM_LOG_DEBUG, "ANALOG EM CHANNEL thread ended.\n"); diff --git a/libs/libsndfile/.update b/libs/libsndfile/.update index 31cb06ca73..1103eb6bc0 100644 --- a/libs/libsndfile/.update +++ b/libs/libsndfile/.update @@ -1 +1 @@ -Mon Apr 16 11:52:47 CDT 2012 +Wed Aug 28 03:22:32 CDT 2013 diff --git a/libs/libsndfile/src/file_io.c b/libs/libsndfile/src/file_io.c index 4e5ee3eed6..b5ec7c39ce 100644 --- a/libs/libsndfile/src/file_io.c +++ b/libs/libsndfile/src/file_io.c @@ -100,6 +100,9 @@ int psf_fclose (SF_PRIVATE *psf) { int retval ; + if (psf->filedes == -1) + return -1; /* already closed */ + if (psf->virtual_io) return 0 ; diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index eba02f5caf..63ebb2503b 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Mon Jul 29 16:07:25 CDT 2013 +Fri Aug 23 04:09:44 CDT 2013 diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c index 67f78148fb..693c14e194 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_ws.c @@ -228,8 +228,8 @@ int tport_recv_stream_ws(tport_t *self) } if (N < 0) { err = errno = EHOSTDOWN; - SU_DEBUG_1(("%s(%p): su_getmsgsize(): %s (%d)\n", __func__, (void *)self, - su_strerror(err), err)); + SU_DEBUG_1(("%s(%p): su_getmsgsize(): %s (%d) N=%ld\n", __func__, (void *)self, + su_strerror(err), err, N)); return 0; } @@ -459,7 +459,6 @@ int tport_ws_init_secondary(tport_t *self, int socket, int accepted, if (ws_init(&wstp->ws, socket, wstp->ws_secure ? wspri->ssl_ctx : NULL, 0) < 0) { ws_destroy(&wstp->ws); - su_close(socket); wstp->ws_initialized = -1; return *return_reason = "WS_INIT", -1; } diff --git a/libs/spandsp/spandsp/global-tones.xml b/libs/spandsp/spandsp/global-tones.xml index 466dae5d2f..5b376d1e86 100644 --- a/libs/spandsp/spandsp/global-tones.xml +++ b/libs/spandsp/spandsp/global-tones.xml @@ -5,12 +5,12 @@ - + - + @@ -25,12 +25,12 @@ - + - + @@ -42,12 +42,12 @@ - + - + @@ -82,12 +82,12 @@ - + - + @@ -142,12 +142,12 @@ - + - + @@ -180,12 +180,12 @@ - + - + @@ -230,12 +230,12 @@ - + - + @@ -273,12 +273,12 @@ - + - + @@ -306,14 +306,14 @@ - + - + @@ -377,12 +377,12 @@ - + - + @@ -448,12 +448,12 @@ - + - + @@ -469,14 +469,14 @@ - + - + @@ -533,12 +533,12 @@ - + - + @@ -553,12 +553,12 @@ - + - + @@ -575,12 +575,12 @@ - + - + @@ -601,12 +601,12 @@ - + - + @@ -618,12 +618,12 @@ - + - + @@ -655,14 +655,14 @@ - + - + @@ -689,14 +689,14 @@ - + - + @@ -735,12 +735,12 @@ - + - + @@ -784,12 +784,12 @@ - + - + @@ -841,14 +841,14 @@ - + - + @@ -907,24 +907,24 @@ - + - - + + - - + + - + @@ -995,18 +995,18 @@ - + - - + + - + @@ -1038,12 +1038,12 @@ - + - + @@ -1067,12 +1067,12 @@ - + - + @@ -1096,18 +1096,18 @@ - + - - + + - + @@ -1141,12 +1141,12 @@ - + - + @@ -1173,12 +1173,12 @@ - + - + @@ -1211,14 +1211,14 @@ - + - + @@ -1255,12 +1255,12 @@ - + - + @@ -1310,12 +1310,12 @@ - + - + @@ -1364,12 +1364,12 @@ - + - + @@ -1385,12 +1385,12 @@ - + - + @@ -1409,12 +1409,12 @@ - + - + @@ -1483,24 +1483,24 @@ - + - - + + - - + + - + @@ -1537,20 +1537,20 @@ - + - - + + - + @@ -1604,12 +1604,12 @@ - + - + @@ -1658,12 +1658,12 @@ - + - + @@ -1717,12 +1717,12 @@ - + - + @@ -1733,12 +1733,12 @@ - + - + @@ -1761,12 +1761,12 @@ - + - + @@ -1818,14 +1818,14 @@ - + - + @@ -1860,12 +1860,12 @@ - + - + @@ -1884,12 +1884,12 @@ - + - + @@ -1922,12 +1922,12 @@ - + - + @@ -1939,12 +1939,12 @@ - + - + @@ -1980,12 +1980,12 @@ - + - + @@ -2011,12 +2011,12 @@ - + - + @@ -2050,12 +2050,12 @@ - + - + @@ -2083,22 +2083,22 @@ - + - - + + - + @@ -2146,12 +2146,12 @@ - + - + @@ -2195,12 +2195,12 @@ - + - + @@ -2225,12 +2225,12 @@ - + - + @@ -2260,18 +2260,18 @@ - + - - + + - + @@ -2303,12 +2303,12 @@ - + - + @@ -2323,7 +2323,7 @@ - + @@ -2332,8 +2332,8 @@ - - + + @@ -2342,19 +2342,19 @@ - - + + - - + + - + @@ -2435,12 +2435,12 @@ - + - + @@ -2482,14 +2482,14 @@ - + - + @@ -2531,12 +2531,12 @@ - + - + @@ -2591,12 +2591,12 @@ - + - + @@ -2630,12 +2630,12 @@ - + - + @@ -2652,12 +2652,12 @@ - + - + @@ -2681,12 +2681,12 @@ - + - + @@ -2707,12 +2707,12 @@ - + - + @@ -2727,12 +2727,12 @@ - + - + @@ -2758,14 +2758,14 @@ - + - + @@ -2796,14 +2796,14 @@ - + - + @@ -2839,12 +2839,12 @@ - + - + @@ -2898,12 +2898,12 @@ - + - + @@ -2938,22 +2938,22 @@ - + - - + + - + @@ -2982,12 +2982,12 @@ - + - + @@ -3037,12 +3037,12 @@ - + - + @@ -3096,11 +3096,11 @@ - + - + @@ -3112,14 +3112,14 @@ - + - + @@ -3172,12 +3172,12 @@ - + - + @@ -3249,12 +3249,12 @@ - + - + @@ -3267,12 +3267,12 @@ - + - + @@ -3289,32 +3289,32 @@ - + - - + + - - + + - - + + - + @@ -3381,18 +3381,18 @@ - + - - + + - + @@ -3450,20 +3450,20 @@ - + - - + + - + @@ -3526,7 +3526,7 @@ - + @@ -3537,7 +3537,7 @@ - + @@ -3583,14 +3583,14 @@ - + - + @@ -3628,12 +3628,12 @@ - + - + @@ -3682,18 +3682,18 @@ - + - - + + - + @@ -3742,12 +3742,12 @@ - + - + @@ -3775,12 +3775,12 @@ - + - + @@ -3808,12 +3808,12 @@ - + - + @@ -3825,12 +3825,12 @@ - + - + @@ -3852,12 +3852,12 @@ - + - + @@ -3875,12 +3875,12 @@ - + - + @@ -3924,12 +3924,12 @@ - + - + @@ -3960,12 +3960,12 @@ - + - + @@ -3981,20 +3981,20 @@ - + - - + + - + @@ -4034,14 +4034,14 @@ - + - + @@ -4098,14 +4098,14 @@ - + - + @@ -4139,12 +4139,12 @@ - + - + @@ -4166,14 +4166,14 @@ - + - + @@ -4203,12 +4203,12 @@ - + - + @@ -4232,12 +4232,12 @@ - + - + @@ -4253,12 +4253,12 @@ - + - + @@ -4274,12 +4274,12 @@ - + - + @@ -4303,12 +4303,12 @@ - + - + @@ -4329,12 +4329,12 @@ - + - + @@ -4359,12 +4359,12 @@ - + - + @@ -4419,12 +4419,12 @@ - + - + @@ -4437,12 +4437,12 @@ - + - + @@ -4460,14 +4460,14 @@ - + - + @@ -4517,20 +4517,20 @@ - + - - + + - + @@ -4562,12 +4562,12 @@ - + - + @@ -4595,12 +4595,12 @@ - + - + @@ -4645,12 +4645,12 @@ - + - + @@ -4674,14 +4674,14 @@ - + - + @@ -4752,12 +4752,12 @@ - + - + @@ -4785,12 +4785,12 @@ - + - + @@ -4832,18 +4832,18 @@ - + - - + + - + @@ -4885,14 +4885,14 @@ - + - + @@ -4927,20 +4927,20 @@ - + - - + + - + @@ -4960,12 +4960,12 @@ - + - + @@ -5002,14 +5002,14 @@ - + - + @@ -5074,12 +5074,12 @@ - + - + @@ -5123,18 +5123,18 @@ - + - - + + - + @@ -5167,12 +5167,12 @@ - + - + @@ -5213,18 +5213,18 @@ - + - - + + - + @@ -5281,22 +5281,22 @@ - + - - + + - + @@ -5327,12 +5327,12 @@ - + - + @@ -5356,12 +5356,12 @@ - + - + @@ -5380,12 +5380,12 @@ - + - + @@ -5408,12 +5408,12 @@ - + - + @@ -5438,12 +5438,12 @@ - + - + @@ -5477,12 +5477,12 @@ - + - + @@ -5531,14 +5531,14 @@ - + - + @@ -5576,12 +5576,12 @@ - + - + @@ -5605,12 +5605,12 @@ - + - + @@ -5633,12 +5633,12 @@ - + - + @@ -5650,12 +5650,12 @@ - + - + @@ -5679,12 +5679,12 @@ - + - + @@ -5724,12 +5724,12 @@ - + - + @@ -5745,14 +5745,14 @@ - + - + @@ -5777,12 +5777,12 @@ - + - + @@ -5809,14 +5809,14 @@ - + - + @@ -5874,12 +5874,12 @@ - + - + @@ -5932,12 +5932,12 @@ - + - + @@ -5961,12 +5961,12 @@ - + - + @@ -6005,14 +6005,14 @@ - + - + @@ -6090,12 +6090,12 @@ - + - + @@ -6176,12 +6176,12 @@ - + - + @@ -6214,12 +6214,12 @@ - + - + @@ -6242,14 +6242,14 @@ - + - + @@ -6297,18 +6297,18 @@ - + - - + + - + @@ -6389,12 +6389,12 @@ - + - + @@ -6450,18 +6450,18 @@ - + - - + + - + @@ -6504,12 +6504,12 @@ - + - + @@ -6527,15 +6527,15 @@ - + - - + + - + @@ -6559,12 +6559,12 @@ - + - + @@ -6603,12 +6603,12 @@ - + - + @@ -6619,14 +6619,14 @@ - + - - + + - + @@ -6636,12 +6636,12 @@ - + - + @@ -6672,12 +6672,12 @@ - + - + @@ -6736,12 +6736,12 @@ - + - + @@ -6776,12 +6776,12 @@ - + - + @@ -6829,14 +6829,14 @@ - + - + @@ -6865,14 +6865,14 @@ - + - + @@ -6897,14 +6897,14 @@ - + - + @@ -6957,18 +6957,18 @@ - + - - + + - + @@ -7069,12 +7069,12 @@ - + - + @@ -7126,20 +7126,20 @@ - + - - + + - + @@ -7187,12 +7187,12 @@ - + - + @@ -7216,14 +7216,14 @@ - + - + @@ -7244,18 +7244,18 @@ - + - - + + - + @@ -7280,12 +7280,12 @@ - + - + @@ -7329,14 +7329,14 @@ - + - + diff --git a/libs/spandsp/spandsp/tones.dtd b/libs/spandsp/spandsp/tones.dtd index 023f8d0783..b751d2387d 100644 --- a/libs/spandsp/spandsp/tones.dtd +++ b/libs/spandsp/spandsp/tones.dtd @@ -1,21 +1,64 @@ - - + + - - + + + + + + + + - - - - - - - - - - - - - + + + + - + @@ -6123,7 +6123,7 @@ - + diff --git a/libs/spandsp/src/Makefile.am b/libs/spandsp/src/Makefile.am index 812100f9cb..a3b7a48ecd 100644 --- a/libs/spandsp/src/Makefile.am +++ b/libs/spandsp/src/Makefile.am @@ -294,6 +294,9 @@ nobase_include_HEADERS = spandsp/ademco_contactid.h \ spandsp/private/modem_echo.h \ spandsp/private/noise.h \ spandsp/private/oki_adpcm.h \ + spandsp/private/playout.h \ + spandsp/private/plc.h \ + spandsp/private/power_meter.h \ spandsp/private/queue.h \ spandsp/private/schedule.h \ spandsp/private/sig_tone.h \ diff --git a/libs/spandsp/src/image_translate.c b/libs/spandsp/src/image_translate.c index 442d188be5..4c6a5affb8 100644 --- a/libs/spandsp/src/image_translate.c +++ b/libs/spandsp/src/image_translate.c @@ -337,6 +337,7 @@ static int image_resize_row(image_translate_state_t *s, uint8_t buf[]) double int_part; double frac_row; double frac_col; + double width_scaling; #endif uint8_t *row8[2]; uint16_t *row16[2]; @@ -352,17 +353,13 @@ static int image_resize_row(image_translate_state_t *s, uint8_t buf[]) input_width = s->input_width - 1; input_length = s->input_length - 1; - skip = s->raw_output_row*input_length/output_length; + skip = s->raw_output_row*input_length/output_length + 1; if (skip >= s->raw_input_row) { - skip++; while (skip >= s->raw_input_row) { if (s->raw_input_row >= s->input_length) - { - s->raw_output_row = -1; break; - } row_len = get_and_scrunch_row(s, s->raw_pixel_row[0]); if (row_len != s->output_width) { @@ -380,6 +377,7 @@ static int image_resize_row(image_translate_state_t *s, uint8_t buf[]) frac_row = ((s->raw_output_row*256*input_length)/output_length) & 0xFF; #else frac_row = modf((double) s->raw_output_row*input_length/output_length, &int_part); + width_scaling = (double) input_width/output_width; #endif switch (s->output_format) @@ -402,7 +400,7 @@ static int image_resize_row(image_translate_state_t *s, uint8_t buf[]) buf[3*i + j] = saturateu8(c1 + (((c2 - c1)*frac_row) >> 8)); } #else - frac_col = modf((double) i*input_width/output_width, &int_part); + frac_col = modf(width_scaling*i, &int_part); x = 3*int_part; for (j = 0; j < 3; j++) { @@ -431,7 +429,7 @@ static int image_resize_row(image_translate_state_t *s, uint8_t buf[]) buf16[3*i + j] = saturateu16(c1 + (((c2 - c1)*frac_row) >> 8)); } #else - frac_col = modf((double) i*input_width/output_width, &int_part); + frac_col = modf(width_scaling*i, &int_part); x = 3*int_part; for (j = 0; j < 3; j++) { @@ -456,7 +454,7 @@ static int image_resize_row(image_translate_state_t *s, uint8_t buf[]) c2 = row8[1][x] + (((row8[1][x + 1] - row8[1][x])*frac_col) >> 8); buf[i] = saturateu8(c1 + (((c2 - c1)*frac_row) >> 8)); #else - frac_col = modf((double) i*input_width/output_width, &int_part); + frac_col = modf(width_scaling*i, &int_part); x = int_part; c1 = row8[0][x] + (row8[0][x + 1] - row8[0][x])*frac_col; c2 = row8[1][x] + (row8[1][x + 1] - row8[1][x])*frac_col; @@ -478,7 +476,7 @@ static int image_resize_row(image_translate_state_t *s, uint8_t buf[]) c2 = row16[1][x] + (((row16[1][x + 1] - row16[1][x])*frac_col) >> 8); buf[i] = saturateu8(c1 + (((c2 - c1)*frac_row) >> 8)); #else - frac_col = modf((double) i*input_width/output_width, &int_part); + frac_col = modf(width_scaling*i, &int_part); x = int_part; c1 = row16[0][x] + (row16[0][x + 1] - row16[0][x])*frac_col; c2 = row16[1][x] + (row16[1][x + 1] - row16[1][x])*frac_col; diff --git a/libs/spandsp/src/msvc/getopt.c b/libs/spandsp/src/msvc/getopt.c index 2fa9318def..a19bd4b78b 100644 --- a/libs/spandsp/src/msvc/getopt.c +++ b/libs/spandsp/src/msvc/getopt.c @@ -1,177 +1,177 @@ -/***************************************************************************** - * - * MODULE NAME : GETOPT.C - * - * COPYRIGHTS: - * This module contains code made available by IBM - * Corporation on an AS IS basis. Any one receiving the - * module is considered to be licensed under IBM copyrights - * to use the IBM-provided source code in any way he or she - * deems fit, including copying it, compiling it, modifying - * it, and redistributing it, with or without - * modifications. No license under any IBM patents or - * patent applications is to be implied from this copyright - * license. - * - * A user of the module should understand that IBM cannot - * provide technical support for the module and will not be - * responsible for any consequences of use of the program. - * - * Any notices, including this one, are not to be removed - * from the module without the prior written consent of - * IBM. - * - * AUTHOR: Original author: - * G. R. Blair (BOBBLAIR at AUSVM1) - * Internet: bobblair@bobblair.austin.ibm.com - * - * Extensively revised by: - * John Q. Walker II, Ph.D. (JOHHQ at RALVM6) - * Internet: johnq@ralvm6.vnet.ibm.com - * - *****************************************************************************/ - -/****************************************************************************** - * getopt() - * - * The getopt() function is a command line parser. It returns the next - * option character in argv that matches an option character in opstring. - * - * The argv argument points to an array of argc+1 elements containing argc - * pointers to character strings followed by a null pointer. - * - * The opstring argument points to a string of option characters; if an - * option character is followed by a colon, the option is expected to have - * an argument that may or may not be separated from it by white space. - * The external variable optarg is set to point to the start of the option - * argument on return from getopt(). - * - * The getopt() function places in optind the argv index of the next argument - * to be processed. The system initializes the external variable optind to - * 1 before the first call to getopt(). - * - * When all options have been processed (that is, up to the first nonoption - * argument), getopt() returns EOF. The special option "--" may be used to - * delimit the end of the options; EOF will be returned, and "--" will be - * skipped. - * - * The getopt() function returns a question mark (?) when it encounters an - * option character not included in opstring. This error message can be - * disabled by setting opterr to zero. Otherwise, it returns the option - * character that was detected. - * - * If the special option "--" is detected, or all options have been - * processed, EOF is returned. - * - * Options are marked by either a minus sign (-) or a slash (/). - * - * No errors are defined. - *****************************************************************************/ - -#include /* for EOF */ -#include /* for strchr() */ - - -/* static (global) variables that are specified as exported by getopt() */ -char *optarg = NULL; /* pointer to the start of the option argument */ -int optind = 1; /* number of the next argv[] to be evaluated */ -int opterr = 1; /* non-zero if a question mark should be returned - when a non-valid option character is detected */ - -/* handle possible future character set concerns by putting this in a macro */ -#define _next_char(string) (char)(*(string+1)) - -int getopt(int argc, char *argv[], char *opstring) -{ - static char *pIndexPosition = NULL; /* place inside current argv string */ - char *pArgString = NULL; /* where to start from next */ - char *pOptString; /* the string in our program */ - - - if (pIndexPosition != NULL) { - /* we last left off inside an argv string */ - if (*(++pIndexPosition)) { - /* there is more to come in the most recent argv */ - pArgString = pIndexPosition; - } - } - - if (pArgString == NULL) { - /* we didn't leave off in the middle of an argv string */ - if (optind >= argc) { - /* more command-line arguments than the argument count */ - pIndexPosition = NULL; /* not in the middle of anything */ - return EOF; /* used up all command-line arguments */ - } - - /*--------------------------------------------------------------------- - * If the next argv[] is not an option, there can be no more options. - *-------------------------------------------------------------------*/ - pArgString = argv[optind++]; /* set this to the next argument ptr */ - - if (('/' != *pArgString) && /* doesn't start with a slash or a dash? */ - ('-' != *pArgString)) { - --optind; /* point to current arg once we're done */ - optarg = NULL; /* no argument follows the option */ - pIndexPosition = NULL; /* not in the middle of anything */ - return EOF; /* used up all the command-line flags */ - } - - /* check for special end-of-flags markers */ - if ((strcmp(pArgString, "-") == 0) || - (strcmp(pArgString, "--") == 0)) { - optarg = NULL; /* no argument follows the option */ - pIndexPosition = NULL; /* not in the middle of anything */ - return EOF; /* encountered the special flag */ - } - - pArgString++; /* look past the / or - */ - } - - if (':' == *pArgString) { /* is it a colon? */ - /*--------------------------------------------------------------------- - * Rare case: if opterr is non-zero, return a question mark; - * otherwise, just return the colon we're on. - *-------------------------------------------------------------------*/ - return (opterr ? (int)'?' : (int)':'); - } - else if ((pOptString = strchr(opstring, *pArgString)) == 0) { - /*--------------------------------------------------------------------- - * The letter on the command-line wasn't any good. - *-------------------------------------------------------------------*/ - optarg = NULL; /* no argument follows the option */ - pIndexPosition = NULL; /* not in the middle of anything */ - return (opterr ? (int)'?' : (int)*pArgString); - } - else { - /*--------------------------------------------------------------------- - * The letter on the command-line matches one we expect to see - *-------------------------------------------------------------------*/ - if (':' == _next_char(pOptString)) { /* is the next letter a colon? */ - /* It is a colon. Look for an argument string. */ - if ('\0' != _next_char(pArgString)) { /* argument in this argv? */ - optarg = &pArgString[1]; /* Yes, it is */ - } - else { - /*------------------------------------------------------------- - * The argument string must be in the next argv. - * But, what if there is none (bad input from the user)? - * In that case, return the letter, and optarg as NULL. - *-----------------------------------------------------------*/ - if (optind < argc) - optarg = argv[optind++]; - else { - optarg = NULL; - return (opterr ? (int)'?' : (int)*pArgString); - } - } - pIndexPosition = NULL; /* not in the middle of anything */ - } - else { - /* it's not a colon, so just return the letter */ - optarg = NULL; /* no argument follows the option */ - pIndexPosition = pArgString; /* point to the letter we're on */ - } - return (int)*pArgString; /* return the letter that matched */ - } +/***************************************************************************** + * + * MODULE NAME : GETOPT.C + * + * COPYRIGHTS: + * This module contains code made available by IBM + * Corporation on an AS IS basis. Any one receiving the + * module is considered to be licensed under IBM copyrights + * to use the IBM-provided source code in any way he or she + * deems fit, including copying it, compiling it, modifying + * it, and redistributing it, with or without + * modifications. No license under any IBM patents or + * patent applications is to be implied from this copyright + * license. + * + * A user of the module should understand that IBM cannot + * provide technical support for the module and will not be + * responsible for any consequences of use of the program. + * + * Any notices, including this one, are not to be removed + * from the module without the prior written consent of + * IBM. + * + * AUTHOR: Original author: + * G. R. Blair (BOBBLAIR at AUSVM1) + * Internet: bobblair@bobblair.austin.ibm.com + * + * Extensively revised by: + * John Q. Walker II, Ph.D. (JOHHQ at RALVM6) + * Internet: johnq@ralvm6.vnet.ibm.com + * + *****************************************************************************/ + +/****************************************************************************** + * getopt() + * + * The getopt() function is a command line parser. It returns the next + * option character in argv that matches an option character in opstring. + * + * The argv argument points to an array of argc+1 elements containing argc + * pointers to character strings followed by a null pointer. + * + * The opstring argument points to a string of option characters; if an + * option character is followed by a colon, the option is expected to have + * an argument that may or may not be separated from it by white space. + * The external variable optarg is set to point to the start of the option + * argument on return from getopt(). + * + * The getopt() function places in optind the argv index of the next argument + * to be processed. The system initializes the external variable optind to + * 1 before the first call to getopt(). + * + * When all options have been processed (that is, up to the first nonoption + * argument), getopt() returns EOF. The special option "--" may be used to + * delimit the end of the options; EOF will be returned, and "--" will be + * skipped. + * + * The getopt() function returns a question mark (?) when it encounters an + * option character not included in opstring. This error message can be + * disabled by setting opterr to zero. Otherwise, it returns the option + * character that was detected. + * + * If the special option "--" is detected, or all options have been + * processed, EOF is returned. + * + * Options are marked by either a minus sign (-) or a slash (/). + * + * No errors are defined. + *****************************************************************************/ + +#include /* for EOF */ +#include /* for strchr() */ + + +/* static (global) variables that are specified as exported by getopt() */ +char *optarg = NULL; /* pointer to the start of the option argument */ +int optind = 1; /* number of the next argv[] to be evaluated */ +int opterr = 1; /* non-zero if a question mark should be returned + when a non-valid option character is detected */ + +/* handle possible future character set concerns by putting this in a macro */ +#define _next_char(string) (char)(*(string+1)) + +int getopt(int argc, char *argv[], char *opstring) +{ + static char *pIndexPosition = NULL; /* place inside current argv string */ + char *pArgString = NULL; /* where to start from next */ + char *pOptString; /* the string in our program */ + + + if (pIndexPosition != NULL) { + /* we last left off inside an argv string */ + if (*(++pIndexPosition)) { + /* there is more to come in the most recent argv */ + pArgString = pIndexPosition; + } + } + + if (pArgString == NULL) { + /* we didn't leave off in the middle of an argv string */ + if (optind >= argc) { + /* more command-line arguments than the argument count */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all command-line arguments */ + } + + /*--------------------------------------------------------------------- + * If the next argv[] is not an option, there can be no more options. + *-------------------------------------------------------------------*/ + pArgString = argv[optind++]; /* set this to the next argument ptr */ + + if (('/' != *pArgString) && /* doesn't start with a slash or a dash? */ + ('-' != *pArgString)) { + --optind; /* point to current arg once we're done */ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all the command-line flags */ + } + + /* check for special end-of-flags markers */ + if ((strcmp(pArgString, "-") == 0) || + (strcmp(pArgString, "--") == 0)) { + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* encountered the special flag */ + } + + pArgString++; /* look past the / or - */ + } + + if (':' == *pArgString) { /* is it a colon? */ + /*--------------------------------------------------------------------- + * Rare case: if opterr is non-zero, return a question mark; + * otherwise, just return the colon we're on. + *-------------------------------------------------------------------*/ + return (opterr ? (int)'?' : (int)':'); + } + else if ((pOptString = strchr(opstring, *pArgString)) == 0) { + /*--------------------------------------------------------------------- + * The letter on the command-line wasn't any good. + *-------------------------------------------------------------------*/ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return (opterr ? (int)'?' : (int)*pArgString); + } + else { + /*--------------------------------------------------------------------- + * The letter on the command-line matches one we expect to see + *-------------------------------------------------------------------*/ + if (':' == _next_char(pOptString)) { /* is the next letter a colon? */ + /* It is a colon. Look for an argument string. */ + if ('\0' != _next_char(pArgString)) { /* argument in this argv? */ + optarg = &pArgString[1]; /* Yes, it is */ + } + else { + /*------------------------------------------------------------- + * The argument string must be in the next argv. + * But, what if there is none (bad input from the user)? + * In that case, return the letter, and optarg as NULL. + *-----------------------------------------------------------*/ + if (optind < argc) + optarg = argv[optind++]; + else { + optarg = NULL; + return (opterr ? (int)'?' : (int)*pArgString); + } + } + pIndexPosition = NULL; /* not in the middle of anything */ + } + else { + /* it's not a colon, so just return the letter */ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = pArgString; /* point to the letter we're on */ + } + return (int)*pArgString; /* return the letter that matched */ + } } diff --git a/libs/spandsp/src/msvc/spandsp.h b/libs/spandsp/src/msvc/spandsp.h index fbdfac8e49..ec0b629d27 100644 --- a/libs/spandsp/src/msvc/spandsp.h +++ b/libs/spandsp/src/msvc/spandsp.h @@ -48,6 +48,9 @@ #include #include +#if !defined(__cplusplus) +#include +#endif #include #include #include diff --git a/libs/spandsp/src/spandsp/private/t42.h b/libs/spandsp/src/spandsp/private/t42.h index 4c1de58ffa..e151afc6fc 100644 --- a/libs/spandsp/src/spandsp/private/t42.h +++ b/libs/spandsp/src/spandsp/private/t42.h @@ -40,10 +40,13 @@ struct lab_params_s float offset_b; int ab_are_signed; - /* Illuminant */ + /* Illuminant, forward and reverse */ float x_n; float y_n; float z_n; + float x_rn; + float y_rn; + float z_rn; }; /* State of a working instance of the T.42 JPEG FAX encoder */ diff --git a/libs/spandsp/src/spandsp/t4_rx.h b/libs/spandsp/src/spandsp/t4_rx.h index 0401d10823..de6698517e 100644 --- a/libs/spandsp/src/spandsp/t4_rx.h +++ b/libs/spandsp/src/spandsp/t4_rx.h @@ -70,6 +70,12 @@ typedef enum T4_COMPRESSION_SYCC_T81 = 0x200, /*! T.88 monochrome JBIG2 compression */ T4_COMPRESSION_T88 = 0x400, + /*! Uncompressed image. This compression cannot be used for FAXes. It is provided for specifying + output formats for received images. */ + T4_COMPRESSION_UNCOMPRESSED = 0x1000, + /*! Conventional JPEG. This compression cannot be used for FAXes. It is provided for specifying + output formats for received images. */ + T4_COMPRESSION_JPEG = 0x2000, /*! Support solour compression without sub-sampling */ T4_COMPRESSION_NO_SUBSAMPLING = 0x800000, /*! Gray-scale support by multi-level codecs */ diff --git a/libs/spandsp/src/t30.c b/libs/spandsp/src/t30.c index 1d3555a97d..110b5b2e5d 100644 --- a/libs/spandsp/src/t30.c +++ b/libs/spandsp/src/t30.c @@ -1804,8 +1804,8 @@ static int analyze_rx_dis_dtc(t30_state_t *s, const uint8_t *msg, int len) } else { - if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_INCH_RESOLUTION_PREFERRED)) - s->mutual_colour_resolutions &= ~T4_RESOLUTION_200_200; + //if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_INCH_RESOLUTION_PREFERRED)) + // s->mutual_colour_resolutions &= ~T4_RESOLUTION_200_200; } if (!test_ctrl_bit(s->far_dis_dtc_frame, T30_DIS_BIT_INCH_RESOLUTION_PREFERRED)) s->mutual_bilevel_resolutions &= ~T4_RESOLUTION_200_100; @@ -2589,7 +2589,7 @@ static int start_sending_document(t30_state_t *s) t30_set_status(s, T30_ERR_NORESSUPPORT); break; default: - span_log(&s->logging, SPAN_LOG_WARNING, "Cannot negotiate an image format\n"); + span_log(&s->logging, SPAN_LOG_WARNING, "Cannot negotiate an image mode\n"); t30_set_status(s, T30_ERR_BADTIFF); break; } @@ -6706,9 +6706,9 @@ SPAN_DECLARE(t30_state_t *) t30_init(t30_state_t *s, | T4_SUPPORT_LENGTH_A4 | T4_SUPPORT_LENGTH_B4 | T4_SUPPORT_LENGTH_UNLIMITED; - /* Set the output encoding to something safe. Most things get 1D and 2D - encoding right. Quite a lot get other things wrong. */ - s->supported_output_compressions = T4_COMPRESSION_T4_2D | T4_COMPRESSION_T42_T81; + /* Set the output encoding to something safe. For bi-level images ost things get + 1D and 2D encoding right. Quite a lot get other things wrong. */ + s->supported_output_compressions = T4_COMPRESSION_T4_2D | T4_COMPRESSION_JPEG; s->local_min_scan_time_code = T30_MIN_SCAN_0MS; span_log_init(&s->logging, SPAN_LOG_NONE, NULL); span_log_set_protocol(&s->logging, "T.30"); diff --git a/libs/spandsp/src/t42.c b/libs/spandsp/src/t42.c index 3e7ea3070b..1469c389be 100644 --- a/libs/spandsp/src/t42.c +++ b/libs/spandsp/src/t42.c @@ -72,6 +72,11 @@ #include "spandsp/private/t85.h" #include "spandsp/private/t42.h" +/* The open_memstream() and fmemopen() in older versions of glibc seems quirky */ +#if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 12)) +#undef OPEN_MEMSTREAM +#endif + #define T42_USE_LUTS #include "t42_t43_local.h" @@ -97,23 +102,23 @@ typedef struct static const illuminant_t illuminants[] = { - {"\0D50", "CIE D50/2°", 96.422f, 100.000f, 82.521f}, + {"\0D50", "CIE D50/2°", 96.422f, 100.000f, 82.521f}, /* Horizon Light. ICC profile PCS */ {"", "CIE D50/10°", 96.720f, 100.000f, 81.427f}, - {"", "CIE D55/2°", 95.682f, 100.000f, 92.149f}, + {"", "CIE D55/2°", 95.682f, 100.000f, 92.149f}, /* Mid-morning/mid-afternoon daylight */ {"", "CIE D55/10°", 95.799f, 100.000f, 90.926f}, - {"\0D65", "CIE D65/2°", 95.047f, 100.000f, 108.883f}, + {"\0D65", "CIE D65/2°", 95.047f, 100.000f, 108.883f}, /* Noon daylight, television, sRGB color space */ {"", "CIE D65/10°", 94.811f, 100.000f, 107.304f}, - {"\0D75", "CIE D75/2°", 94.972f, 100.000f, 122.638f}, + {"\0D75", "CIE D75/2°", 94.972f, 100.000f, 122.638f}, /* North sky daylight */ {"", "CIE D75/10°", 94.416f, 100.000f, 120.641f}, - {"\0\0F2", "F02/2°", 99.186f, 100.000f, 67.393f}, + {"\0\0F2", "F02/2°", 99.186f, 100.000f, 67.393f}, /* Cool white fluorescent */ {"", "F02/10°", 103.279f, 100.000f, 69.027f}, - {"\0\0F7", "F07/2°", 95.041f, 100.000f, 108.747f}, + {"\0\0F7", "F07/2°", 95.041f, 100.000f, 108.747f}, /* D65 simulator, daylight simulator */ {"", "F07/10°", 95.792f, 100.000f, 107.686f}, - {"\0F11", "F11/2°", 100.962f, 100.000f, 64.350f}, + {"\0F11", "F11/2°", 100.962f, 100.000f, 64.350f}, /* Philips TL84, Ultralume 40 */ {"", "F11/10°", 103.863f, 100.000f, 65.607f}, - {"\0\0SA", "A/2°", 109.850f, 100.000f, 35.585f}, + {"\0\0SA", "A/2°", 109.850f, 100.000f, 35.585f}, /* Incandescent/tungsten */ {"", "A/10°", 111.144f, 100.000f, 35.200f}, - {"\0\0SC", "C/2°", 98.074f, 100.000f, 118.232f}, + {"\0\0SC", "C/2°", 98.074f, 100.000f, 118.232f}, /* {obsolete} average/north sky daylight */ {"", "C/10°", 97.285f, 100.000f, 116.145f}, {"", "", 0.000f, 0.000f, 0.000f} }; @@ -304,6 +309,9 @@ SPAN_DECLARE(void) set_lab_illuminant(lab_params_t *lab, float new_xn, float new lab->y_n = new_yn; lab->z_n = new_zn; } + lab->x_rn = 1.0f/lab->x_n; + lab->y_rn = 1.0f/lab->y_n; + lab->z_rn = 1.0f/lab->z_n; } /*- End of function --------------------------------------------------------*/ @@ -470,9 +478,9 @@ SPAN_DECLARE(void) srgb_to_lab(lab_params_t *s, uint8_t lab[], const uint8_t srg z = 0.0193f*r + 0.1192f*g + 0.9505f*b; /* Normalise for the illuminant */ - x /= s->x_n; - y /= s->y_n; - z /= s->z_n; + x *= s->x_rn; + y *= s->y_rn; + z *= s->z_rn; /* XYZ to Lab */ xx = (x <= 0.008856f) ? (7.787f*x + 0.1379f) : cbrtf(x); @@ -941,14 +949,16 @@ SPAN_DECLARE(int) t42_encode_restart(t42_encode_state_t *s, uint32_t image_width { /* ITU-YCC */ /* Illuminant D65 */ - set_lab_illuminant(&s->lab, 95.047f, 100.000f, 108.883f); + //set_lab_illuminant(&s->lab, 95.047f, 100.000f, 108.883f); + set_lab_illuminant(&s->lab, 100.0f, 100.0f, 100.0f); set_lab_gamut(&s->lab, 0, 100, -127, 127, -127, 127, false); } else { /* ITULAB */ /* Illuminant D50 */ - set_lab_illuminant(&s->lab, 96.422f, 100.000f, 82.521f); + //set_lab_illuminant(&s->lab, 96.422f, 100.000f, 82.521f); + set_lab_illuminant(&s->lab, 100.0f, 100.0f, 100.0f); set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, false); } s->compressed_image_size = 0; @@ -965,12 +975,6 @@ SPAN_DECLARE(int) t42_encode_restart(t42_encode_state_t *s, uint32_t image_width span_log(&s->logging, SPAN_LOG_FLOW, "Failed to open_memstream().\n"); return -1; } - if (fseek(s->out, 0, SEEK_SET) != 0) - { - fclose(s->out); - s->out = NULL; - return -1; - } #else if ((s->out = tmpfile()) == NULL) { @@ -1114,14 +1118,6 @@ static int t42_itulab_jpeg_to_srgb(t42_decode_state_t *s) for (i = 0; i < 16; i++) jpeg_save_markers(&s->decompressor, JPEG_APP0 + i, 0xFFFF); - /* Rewind the file */ - if (fseek(s->in, 0, SEEK_SET) != 0) - { - fclose(s->in); - s->in = NULL; - return -1; - } - /* Take the header */ jpeg_read_header(&s->decompressor, false); /* Sanity check and parameter check */ @@ -1222,7 +1218,7 @@ SPAN_DECLARE(void) t42_decode_rx_status(t42_decode_state_t *s, int status) { if (t42_itulab_jpeg_to_srgb(s)) span_log(&s->logging, SPAN_LOG_FLOW, "Failed to convert from ITULAB.\n"); - s->end_of_data = 1; + s->end_of_data = true; } break; default: @@ -1242,7 +1238,7 @@ SPAN_DECLARE(int) t42_decode_put(t42_decode_state_t *s, const uint8_t data[], si { if (t42_itulab_jpeg_to_srgb(s)) span_log(&s->logging, SPAN_LOG_FLOW, "Failed to convert from ITULAB.\n"); - s->end_of_data = 1; + s->end_of_data = true; } return T4_DECODE_OK; } @@ -1320,18 +1316,20 @@ SPAN_DECLARE(int) t42_decode_restart(t42_decode_state_t *s) { /* ITU-YCC */ /* Illuminant D65 */ - set_lab_illuminant(&s->lab, 95.047f, 100.000f, 108.883f); + //set_lab_illuminant(&s->lab, 95.047f, 100.000f, 108.883f); + set_lab_illuminant(&s->lab, 100.0f, 100.0f, 100.0f); set_lab_gamut(&s->lab, 0, 100, -127, 127, -127, 127, false); } else { /* ITULAB */ /* Illuminant D50 */ - set_lab_illuminant(&s->lab, 96.422f, 100.000f, 82.521f); + //set_lab_illuminant(&s->lab, 96.422f, 100.000f, 82.521f); + set_lab_illuminant(&s->lab, 100.0f, 100.0f, 100.0f); set_lab_gamut(&s->lab, 0, 100, -85, 85, -75, 125, false); } - s->end_of_data = 0; + s->end_of_data = false; s->compressed_image_size = 0; s->error_message[0] = '\0'; diff --git a/libs/spandsp/src/t4_rx.c b/libs/spandsp/src/t4_rx.c index 256e8fcdfc..141a6529e9 100644 --- a/libs/spandsp/src/t4_rx.c +++ b/libs/spandsp/src/t4_rx.c @@ -123,6 +123,11 @@ SPAN_DECLARE(const char *) t4_compression_to_str(int compression) return "T.43"; case T4_COMPRESSION_T45: return "T.45"; + /* Compressions which can only be used in TIFF files */ + case T4_COMPRESSION_UNCOMPRESSED: + return "Uncompressed"; + case T4_COMPRESSION_JPEG: + return "JPEG"; } return "???"; } @@ -238,13 +243,27 @@ static int set_tiff_directory_info(t4_rx_state_t *s) break; #endif #if defined(SPANDSP_SUPPORT_T42) + case T4_COMPRESSION_JPEG: + output_compression = COMPRESSION_JPEG; + bits_per_sample = 8; + if (t->image_type == T4_IMAGE_TYPE_COLOUR_8BIT) + { + samples_per_pixel = 3; + photometric = PHOTOMETRIC_YCBCR; + } + else + { + samples_per_pixel = 1; + photometric = PHOTOMETRIC_MINISBLACK; + } + break; case T4_COMPRESSION_T42_T81: output_compression = COMPRESSION_JPEG; bits_per_sample = 8; if (t->image_type == T4_IMAGE_TYPE_COLOUR_8BIT) { samples_per_pixel = 3; - photometric = PHOTOMETRIC_YCBCR; //PHOTOMETRIC_ITULAB; + photometric = PHOTOMETRIC_ITULAB; } else { @@ -306,12 +325,20 @@ static int set_tiff_directory_info(t4_rx_state_t *s) TIFFSetField(t->tiff_file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(t->tiff_file, TIFFTAG_PHOTOMETRIC, photometric); TIFFSetField(t->tiff_file, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB); - if (t->compression == T4_COMPRESSION_T42_T81) + switch (t->compression) { + case T4_COMPRESSION_JPEG: TIFFSetField(t->tiff_file, TIFFTAG_YCBCRSUBSAMPLING, 2, 2); //TIFFSetField(t->tiff_file, TIFFTAG_YCBCRSUBSAMPLING, 1, 1); TIFFSetField(t->tiff_file, TIFFTAG_JPEGQUALITY, 75); TIFFSetField(t->tiff_file, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + break; + case T4_COMPRESSION_T42_T81: + TIFFSetField(t->tiff_file, TIFFTAG_YCBCRSUBSAMPLING, 2, 2); + //TIFFSetField(t->tiff_file, TIFFTAG_YCBCRSUBSAMPLING, 1, 1); + TIFFSetField(t->tiff_file, TIFFTAG_JPEGQUALITY, 75); + TIFFSetField(t->tiff_file, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + break; } /* TIFFTAG_STRIPBYTECOUNTS and TIFFTAG_STRIPOFFSETS are added automatically */ @@ -815,10 +842,14 @@ static void select_tiff_compression(t4_rx_state_t *s, int output_image_type) } else { - if ((s->supported_tiff_compressions & T4_COMPRESSION_T42_T81)) + if ((s->supported_tiff_compressions & T4_COMPRESSION_JPEG)) + s->tiff.compression = T4_COMPRESSION_JPEG; + else if ((s->supported_tiff_compressions & T4_COMPRESSION_T42_T81)) s->tiff.compression = T4_COMPRESSION_T42_T81; else if ((s->supported_tiff_compressions & T4_COMPRESSION_T43)) s->tiff.compression = T4_COMPRESSION_T43; + else if ((s->supported_tiff_compressions & T4_COMPRESSION_UNCOMPRESSED)) + s->tiff.compression = T4_COMPRESSION_UNCOMPRESSED; } s->tiff.image_type = output_image_type; } @@ -1020,7 +1051,7 @@ SPAN_DECLARE(void) t4_rx_get_transfer_statistics(t4_rx_state_t *s, t4_stats_t *t break; #endif case T4_COMPRESSION_T42_T81: - t->type = T4_IMAGE_TYPE_GRAY_8BIT; //T4_IMAGE_TYPE_COLOUR_8BIT; + t->type = T4_IMAGE_TYPE_COLOUR_8BIT; //T4_IMAGE_TYPE_GRAY_8BIT; t->width = t42_decode_get_image_width(&s->decoder.t42); t->length = t42_decode_get_image_length(&s->decoder.t42); t->image_type = t->type; diff --git a/libs/spandsp/src/t4_tx.c b/libs/spandsp/src/t4_tx.c index 5c35fdb0b4..0c17b585f2 100644 --- a/libs/spandsp/src/t4_tx.c +++ b/libs/spandsp/src/t4_tx.c @@ -2009,6 +2009,8 @@ SPAN_DECLARE(int) t4_tx_set_tx_image_format(t4_tx_state_t *s, /* We can't rework a bilevel image that fits none of the patterns */ if (s->tiff.image_type == T4_IMAGE_TYPE_BILEVEL) return T4_IMAGE_FORMAT_NORESSUPPORT; + if (!(supported_compressions & T4_COMPRESSION_RESCALING)) + return T4_IMAGE_FORMAT_NORESSUPPORT; res = T4_IMAGE_FORMAT_OK; /* Any other kind of image might be resizable */ s->metadata.image_width = T4_WIDTH_200_A4; @@ -2022,8 +2024,18 @@ SPAN_DECLARE(int) t4_tx_set_tx_image_format(t4_tx_state_t *s, if (s->metadata.image_type != s->tiff.image_type || s->metadata.image_width != s->tiff.image_width) { - if (image_translate_init(&s->translator, s->metadata.image_type, s->metadata.image_width, -1, s->tiff.image_type, s->tiff.image_width, s->tiff.image_length, translate_row_read2, s) == NULL) + if (image_translate_init(&s->translator, + s->metadata.image_type, + s->metadata.image_width, + -1, + s->tiff.image_type, + s->tiff.image_width, + s->tiff.image_length, + translate_row_read2, + s) == NULL) + { return T4_IMAGE_FORMAT_INCOMPATIBLE; + } s->metadata.image_length = image_translate_get_output_length(&s->translator); } diff --git a/libs/spandsp/tests/fax_tests.c b/libs/spandsp/tests/fax_tests.c index 8e417adf9b..7cbe61fcfa 100644 --- a/libs/spandsp/tests/fax_tests.c +++ b/libs/spandsp/tests/fax_tests.c @@ -926,7 +926,7 @@ int main(int argc, char *argv[]) { t30_set_supported_colour_resolutions(t30_state[i], 0); } - //t30_set_supported_output_compressions(t30_state[i], T4_COMPRESSION_T85); + t30_set_supported_output_compressions(t30_state[i], T4_COMPRESSION_T6 | T4_COMPRESSION_JPEG); t30_set_ecm_capability(t30_state[i], use_ecm); t30_set_supported_compressions(t30_state[i], T4_COMPRESSION_T4_1D diff --git a/libs/srtp/include/srtp_priv.h b/libs/srtp/include/srtp_priv.h index 180a51b9e8..8425b351ce 100644 --- a/libs/srtp/include/srtp_priv.h +++ b/libs/srtp/include/srtp_priv.h @@ -83,11 +83,11 @@ typedef struct { #else /* BIG_ENDIAN */ typedef struct { - unsigned char version:2; /* protocol version */ - unsigned char p:1; /* padding flag */ - unsigned char x:1; /* header extension flag */ - unsigned char cc:4; /* CSRC count */ - unsigned char m:1; /* marker bit */ + unsigned version:2; /* protocol version */ + unsigned p:1; /* padding flag */ + unsigned x:1; /* header extension flag */ + unsigned cc:4; /* CSRC count */ + unsigned m:1; /* marker bit */ unsigned pt:7; /* payload type */ unsigned seq:16; /* sequence number */ unsigned ts:32; /* timestamp */ @@ -131,10 +131,10 @@ typedef struct { #else /* BIG_ENDIAN */ typedef struct { - unsigned char version:2; /* protocol version */ - unsigned char p:1; /* padding flag */ - unsigned char rc:5; /* reception report count */ - unsigned char pt:8; /* payload type */ + unsigned version:2; /* protocol version */ + unsigned p:1; /* padding flag */ + unsigned rc:5; /* reception report count */ + unsigned pt:8; /* payload type */ uint16_t len; /* length */ uint32_t ssrc; /* synchronization source */ } srtcp_hdr_t; diff --git a/libs/stfu/stfu.c b/libs/stfu/stfu.c index 5ab14a31d6..8f09145e71 100644 --- a/libs/stfu/stfu.c +++ b/libs/stfu/stfu.c @@ -447,6 +447,10 @@ stfu_status_t stfu_n_add_data(stfu_instance_t *i, uint32_t ts, uint16_t seq, uin if (i->max_drift) { + if (i->drift_dropped_packets > 500) { + stfu_n_reset(i); + } + if (i->ts_drift < i->max_drift) { if (++i->drift_dropped_packets < i->drift_max_dropped) { stfu_log(STFU_LOG_EMERG, "%s TOO LATE !!! %u \n\n\n", i->name, ts); diff --git a/scripts/gentls_cert.in b/scripts/gentls_cert.in index 26da60c7f8..f2e4cd5a99 100644 --- a/scripts/gentls_cert.in +++ b/scripts/gentls_cert.in @@ -7,7 +7,7 @@ export KEY_SIZE=${KEY_SIZE} TMPFILE="/tmp/fs-ca-$$-$(date +%Y%m%d%H%M%S)" -COMMON_NAME="FreesSWITCH CA" +COMMON_NAME="FreeSWITCH CA" ALT_NAME="DNS:test.freeswitch.org" ORG_NAME="FreeSWITCH" OUTFILE="agent.pem" @@ -47,6 +47,7 @@ setup_ca() { default_bits = \$ENV::KEY_SIZE prompt = no distinguished_name = req_dn + x509_extensions = v3_ca [ req_dn ] commonName = %CN% @@ -69,6 +70,12 @@ setup_ca() { subjectAltName=%ALTNAME% nsCertType=client extendedKeyUsage=clientAuth + + [ v3_ca ] + subjectKeyIdentifier=hash + authorityKeyIdentifier=keyid:always,issuer + basicConstraints=CA:TRUE + EOF fi @@ -80,10 +87,12 @@ setup_ca() { "${CONFDIR}/CA/config.tpl" \ > "${TMPFILE}.cfg" || exit 1 + openssl ecparam -name secp160r2 -out CA_CURVE.pem openssl req -out "${CONFDIR}/CA/cacert.pem" \ - -new -x509 -keyout "${CONFDIR}/CA/cakey.pem" \ + -new -x509 -keyout "${CONFDIR}/CA/cakey.pem" -newkey ec:CA_CURVE.pem \ -config "${TMPFILE}.cfg" -nodes -days ${DAYS} -sha1 >/dev/null || exit 1 cat "${CONFDIR}/CA/cacert.pem" > "${CONFDIR}/cafile.pem" + cp $TMPFILE.cfg /tmp/ssl.cfg rm "${TMPFILE}.cfg" echo "DONE" @@ -122,7 +131,7 @@ generate_cert() { > "${TMPFILE}.cfg" || exit 1 openssl req -new -out "${TMPFILE}.req" \ - -newkey rsa:${KEY_SIZE} -keyout "${TMPFILE}.key" \ + -newkey ec:CA_CURVE.pem -keyout "${TMPFILE}.key" \ -config "${TMPFILE}.cfg" -nodes -sha1 >/dev/null || exit 1 openssl x509 -req -CAkey "${CONFDIR}/CA/cakey.pem" -CA "${CONFDIR}/CA/cacert.pem" -CAcreateserial \ @@ -148,7 +157,6 @@ remove_ca() { } OUTFILESET="0" command="$1" -shift while [ $# -gt 0 ]; do case $1 in diff --git a/scripts/lua/zrtp_sas_proxy.lua b/scripts/lua/zrtp_sas_proxy.lua new file mode 100644 index 0000000000..503b8d8c72 --- /dev/null +++ b/scripts/lua/zrtp_sas_proxy.lua @@ -0,0 +1,103 @@ +-- zrtp_sas_proxy.lua +-- +-- Copyright (c) 2011-2013 Travis Cross +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +-- +-- +-- When we're acting as a ZRTP man-in-the-middle, proxy the SAS (Short +-- Authentication String) from one leg of the call to the other. +-- +-- This script should be called asynchonously with luarun. e.g.: +-- +-- +-- +aleg=argv[1] +api=freeswitch.API() + +function log(level,msg) return freeswitch.consoleLog(level,"zrtp_sas: "..msg.."\n") end +function sleep(sec) return freeswitch.msleep(sec*1000) end +function ready() return api:execute("uuid_exists",aleg)=="true" end +function getvar(uuid,var) + local x=api:execute("uuid_getvar",uuid.." "..var) + if x=="_undef_" then return nil end + return x +end +function getvarp(uuid,var) return getvar(uuid,var)=="true" end +function display(uuid,msg) + local cidn=getvar(uuid,"caller_id_name") + return api:execute("uuid_display",uuid.." "..msg.." "..cidn) +end + +function mk_sas(sas1,sas2) + if sas1 and sas2 then return sas1.." "..sas2 + else return sas1 or sas2 or "" end +end + +function get_sas(uuid) + return mk_sas(getvar(uuid,"zrtp_sas1_string_audio"), + getvar(uuid,"zrtp_sas2_string")) +end + +function log_sas(leg,uuid) + return log("notice",leg..": "..uuid.." sas: "..get_sas(uuid)) +end + +function display_sas(to,from) + return display(to," ("..get_sas(from)..")") +end + +function get_bleg(aleg) + local retries=15 bleg=nil + while ready() do + if retries<1 then return nil end + local bleg=getvar(aleg,"signal_bond") + if bleg then return bleg end + log("debug","waiting for bleg uuid...") + sleep(1) + retries=retries-1 + end +end + +function handle_sas(aleg,bleg) + local retries=45 af=false bf=false + while ready() do + if retries<1 then return nil end + if not af and getvarp(aleg,"zrtp_secure_media_confirmed_audio") then + af=true + log_sas("aleg",aleg) + display_sas(bleg,aleg) + end + if not bf and getvarp(bleg,"zrtp_secure_media_confirmed_audio") then + bf=true + log_sas("bleg",bleg) + display_sas(aleg,bleg) + end + if (af and bf) then break + elseif af then log("debug","waiting on bleg zrtp...") + elseif bf then log("debug","waiting on aleg zrtp...") + else log("debug","waiting for zrtp...") end + sleep(1) + retries=retries-1 + end +end + +if not (getvarp(aleg,"zrtp_passthru") or getvarp(aleg,"proxy_media")) then + handle_sas(aleg,get_bleg(aleg)) +end diff --git a/src/include/switch_console.h b/src/include/switch_console.h index e6f6cf476e..73410bd248 100644 --- a/src/include/switch_console.h +++ b/src/include/switch_console.h @@ -83,6 +83,7 @@ SWITCH_DECLARE(switch_status_t) switch_console_add_complete_func(const char *nam SWITCH_DECLARE(switch_status_t) switch_console_del_complete_func(const char *name); SWITCH_DECLARE(switch_status_t) switch_console_run_complete_func(const char *func, const char *line, const char *last_word, switch_console_callback_match_t **matches); +SWITCH_DECLARE(void) switch_console_push_match_unique(switch_console_callback_match_t **matches, const char *new_val); SWITCH_DECLARE(void) switch_console_push_match(switch_console_callback_match_t **matches, const char *new_val); SWITCH_DECLARE(void) switch_console_free_matches(switch_console_callback_match_t **matches); SWITCH_DECLARE(unsigned char) switch_console_complete(const char *line, const char *last_word, diff --git a/src/include/switch_core.h b/src/include/switch_core.h index dfa1d39d7c..1a12da7129 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -878,6 +878,8 @@ SWITCH_DECLARE(char *) switch_core_get_variable_pdup(_In_z_ const char *varname, SWITCH_DECLARE(const char *) switch_core_get_hostname(void); SWITCH_DECLARE(const char *) switch_core_get_switchname(void); +SWITCH_DECLARE(char *) switch_core_get_domain(switch_bool_t dup); + /*! \brief Add a global variable to the core \param varname the name of the variable @@ -1015,6 +1017,8 @@ SWITCH_DECLARE(switch_app_log_t *) switch_core_session_get_app_log(_In_ switch_c */ SWITCH_DECLARE(switch_status_t) switch_core_session_exec(_In_ switch_core_session_t *session, _In_ const switch_application_interface_t *application_interface, _In_opt_z_ const char *arg); + +SWITCH_DECLARE(void) switch_core_session_video_reset(switch_core_session_t *session); /*! \brief Execute an application on a session \param session the current session diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 7f3aade715..04bec8c27a 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -652,6 +652,7 @@ typedef enum { SWITCH_RTP_FLAG_VIDEO, SWITCH_RTP_FLAG_ENABLE_RTCP, SWITCH_RTP_FLAG_RTCP_MUX, + SWITCH_RTP_FLAG_KILL_JB, SWITCH_RTP_FLAG_INVALID } switch_rtp_flag_t; @@ -1680,6 +1681,8 @@ typedef uint32_t switch_io_flag_t; SWITCH_EVENT_RE_SCHEDULE - Something scheduled has been rescheduled SWITCH_EVENT_RELOADXML - XML registry has been reloaded SWITCH_EVENT_NOTIFY - Notification + SWITCH_EVENT_PHONE_FEATURE - Notification (DND/CFWD/etc) + SWITCH_EVENT_PHONE_FEATURE_SUBSCRIBE - Phone feature subscription SWITCH_EVENT_SEND_MESSAGE - Message SWITCH_EVENT_RECV_MESSAGE - Message SWITCH_EVENT_NAT - NAT Management (new/del/status) @@ -1747,6 +1750,8 @@ typedef enum { SWITCH_EVENT_RE_SCHEDULE, SWITCH_EVENT_RELOADXML, SWITCH_EVENT_NOTIFY, + SWITCH_EVENT_PHONE_FEATURE, + SWITCH_EVENT_PHONE_FEATURE_SUBSCRIBE, SWITCH_EVENT_SEND_MESSAGE, SWITCH_EVENT_RECV_MESSAGE, SWITCH_EVENT_REQUEST_PARAMS, diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index 40630d8c9f..f4aa3d7f28 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -467,14 +467,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..2b7804dd31 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -467,7 +467,7 @@ SWITCH_STANDARD_API(reg_url_function) } if (zstr(domain)) { - dup_domain = switch_core_get_variable_dup("domain"); + dup_domain = switch_core_get_domain(SWITCH_TRUE); domain = dup_domain; } @@ -886,7 +886,7 @@ SWITCH_STANDARD_API(group_call_function) if (domain) { *domain++ = '\0'; } else { - if ((dup_domain = switch_core_get_variable_dup("domain"))) { + if ((dup_domain = switch_core_get_domain(SWITCH_TRUE))) { domain = dup_domain; } } @@ -1076,7 +1076,7 @@ SWITCH_STANDARD_API(in_group_function) if ((domain = strchr(user, '@'))) { *domain++ = '\0'; } else { - if ((dup_domain = switch_core_get_variable_dup("domain"))) { + if ((dup_domain = switch_core_get_domain(SWITCH_TRUE))) { domain = dup_domain; } } @@ -1131,7 +1131,7 @@ SWITCH_STANDARD_API(user_data_function) if ((domain = strchr(user, '@'))) { *domain++ = '\0'; } else { - if ((dup_domain = switch_core_get_variable_dup("domain"))) { + if ((dup_domain = switch_core_get_domain(SWITCH_TRUE))) { domain = dup_domain; } else { domain = "cluecon.com"; @@ -6028,6 +6028,49 @@ SWITCH_STANDARD_API(file_exists_function) return SWITCH_STATUS_SUCCESS; } +#define INTERFACE_IP_SYNTAX "[auto|ipv4|ipv6] " +SWITCH_STANDARD_API(interface_ip_function) +{ + char *mydata = NULL, *argv[3] = { 0 }; + int argc = 0; + char addr[INET6_ADDRSTRLEN]; + + if (!zstr(cmd)) { + mydata = strdup(cmd); + switch_assert(mydata); + argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + if (argc < 2) { + stream->write_function(stream, "USAGE: interface_ip %s\n", INTERFACE_IP_SYNTAX); + goto end; + } + + if (!strcasecmp(argv[0], "ipv4")) { + if (switch_find_interface_ip(addr, sizeof(addr), NULL, argv[1], AF_INET) == SWITCH_STATUS_SUCCESS) { + stream->write_function(stream, "%s", addr); + } + } + else if (!strcasecmp(argv[0], "ipv6")) { + if (switch_find_interface_ip(addr, sizeof(addr), NULL, argv[1], AF_INET6) == SWITCH_STATUS_SUCCESS) { + stream->write_function(stream, "%s", addr); + } + } + else if (!strcasecmp(argv[0], "auto")) { + if (switch_find_interface_ip(addr, sizeof(addr), NULL, argv[1], AF_UNSPEC) == SWITCH_STATUS_SUCCESS) { + stream->write_function(stream, "%s", addr); + } + } + else { + stream->write_function(stream, "USAGE: interface_ip %s\n", INTERFACE_IP_SYNTAX); + } + +end: + switch_safe_free(mydata); + + return SWITCH_STATUS_SUCCESS; +} + SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) { switch_api_interface_t *commands_api_interface; @@ -6065,6 +6108,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) SWITCH_ADD_API(commands_api_interface, "help", "Show help for all the api commands", help_function, ""); SWITCH_ADD_API(commands_api_interface, "host_lookup", "Lookup host", host_lookup_function, ""); SWITCH_ADD_API(commands_api_interface, "hostname", "Return the system hostname", hostname_api_function, ""); + SWITCH_ADD_API(commands_api_interface, "interface_ip", "Return the primary IP of an interface", interface_ip_function, INTERFACE_IP_SYNTAX); SWITCH_ADD_API(commands_api_interface, "switchname", "Return the switch name", switchname_api_function, ""); SWITCH_ADD_API(commands_api_interface, "hupall", "hupall", hupall_api_function, " [ ]"); SWITCH_ADD_API(commands_api_interface, "in_group", "Determine if a user is in a group", in_group_function, "[@] "); @@ -6217,6 +6261,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) switch_console_set_complete("add fsctl flush_db_handles"); switch_console_set_complete("add fsctl min_idle_cpu"); switch_console_set_complete("add fsctl send_sighup"); + switch_console_set_complete("add interface_ip auto ::console::list_interfaces"); + switch_console_set_complete("add interface_ip ipv4 ::console::list_interfaces"); + switch_console_set_complete("add interface_ip ipv6 ::console::list_interfaces"); switch_console_set_complete("add load ::console::list_available_modules"); switch_console_set_complete("add nat_map reinit"); switch_console_set_complete("add nat_map republish"); diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 99b992befd..0ad4c1bc67 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -451,6 +451,7 @@ struct conference_member { conference_cdr_node_t *cdr_node; char *kicked_sound; switch_queue_t *dtmf_queue; + switch_thread_t *input_thread; }; /* Record Node */ @@ -642,7 +643,7 @@ static char *conference_rfc4579_render(conference_obj_t *conference, switch_even if (!event || !(domain = switch_event_get_header(event, "conference-domain"))) { if (!(domain = conference->domain)) { - dup_domain = switch_core_get_variable_dup("domain"); + dup_domain = switch_core_get_domain(SWITCH_TRUE); if (!(domain = dup_domain)) { domain = "cluecon.com"; } @@ -1337,7 +1338,7 @@ static void send_rfc_event(conference_obj_t *conference) } if (!(domain = conference->domain)) { - dup_domain = switch_core_get_variable_dup("domain"); + dup_domain = switch_core_get_domain(SWITCH_TRUE); if (!(domain = dup_domain)) { domain = "cluecon.com"; } @@ -1376,7 +1377,7 @@ static void send_conference_notify(conference_obj_t *conference, const char *sta } if (!(domain = conference->domain)) { - dup_domain = switch_core_get_variable_dup("domain"); + dup_domain = switch_core_get_domain(SWITCH_TRUE); if (!(domain = dup_domain)) { domain = "cluecon.com"; } @@ -1607,7 +1608,7 @@ static void conference_set_video_floor_holder(conference_obj_t *conference, conf conference_member_t *imember; for (imember = conference->members; imember; imember = imember->next) { - if (imember != conference->video_floor_holder && switch_channel_test_flag(imember->channel, CF_VIDEO)) { + if (imember != conference->video_floor_holder && imember->channel && switch_channel_test_flag(imember->channel, CF_VIDEO)) { member = imember; break; } @@ -1658,7 +1659,8 @@ static void conference_set_floor_holder(conference_obj_t *conference, conference int old_id = 0; if (!switch_test_flag(conference, CFLAG_VIDEO_BRIDGE) && - ((conference->video_floor_holder && !member) || (member && switch_channel_test_flag(member->channel, CF_VIDEO)))) { + ((conference->video_floor_holder && !member) || + (member && member->channel && switch_channel_test_flag(member->channel, CF_VIDEO)))) { conference_set_video_floor_holder(conference, member, SWITCH_FALSE); } @@ -3184,6 +3186,11 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v uint32_t hangover = 40, hangunder = 5, hangover_hits = 0, hangunder_hits = 0, diff_level = 400; switch_core_session_t *session = member->session; + + if (switch_core_session_read_lock(session) != SWITCH_STATUS_SUCCESS) { + return NULL; + } + switch_assert(member != NULL); switch_clear_flag_locked(member, MFLAG_TALKING); @@ -3480,6 +3487,8 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v switch_resample_destroy(&member->read_resampler); switch_clear_flag_locked(member, MFLAG_ITHREAD); + switch_core_session_rwunlock(session); + return NULL; } @@ -3569,17 +3578,15 @@ static void member_add_file_data(conference_member_t *member, int16_t *data, swi /* launch an input thread for the call leg */ static void launch_conference_loop_input(conference_member_t *member, switch_memory_pool_t *pool) { - switch_thread_t *thread; switch_threadattr_t *thd_attr = NULL; if (member == NULL) return; switch_threadattr_create(&thd_attr, pool); - switch_threadattr_detach_set(thd_attr, 1); switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); switch_set_flag_locked(member, MFLAG_ITHREAD); - switch_thread_create(&thread, thd_attr, conference_loop_input, member, pool); + switch_thread_create(&member->input_thread, thd_attr, conference_loop_input, member, pool); } /* marshall frames from the conference (or file or tts output) to the call leg */ @@ -3599,6 +3606,7 @@ static void conference_loop_output(conference_member_t *member) call_list_t *call_list, *cp; switch_codec_implementation_t read_impl = { 0 }; int sanity; + switch_status_t st; switch_core_session_get_read_impl(member->session, &read_impl); @@ -3913,6 +3921,11 @@ static void conference_loop_output(conference_member_t *member) end: switch_clear_flag_locked(member, MFLAG_RUNNING); + + if (member->input_thread) { + switch_thread_join(&st, member->input_thread); + } + switch_core_timer_destroy(&timer); switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "Channel leaving conference, cause: %s\n", @@ -7472,6 +7485,8 @@ SWITCH_STANDARD_APP(conference_function) switch_channel_set_app_flag_key("conf_silent", channel, CONF_SILENT_REQ); } + switch_core_session_video_reset(session); + switch_channel_set_flag(channel, CF_CONFERENCE); if (switch_channel_answer(channel) != SWITCH_STATUS_SUCCESS) { @@ -8028,6 +8043,7 @@ SWITCH_STANDARD_APP(conference_function) switch_channel_clear_flag(channel, CF_CONFERENCE); + switch_core_session_video_reset(session); } /* Create a thread for the conference and launch it */ @@ -8908,7 +8924,8 @@ static void call_setup_event_handler(switch_event_t *event) switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "conference_track_status", "true"); switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "conference_track_call_id", call_id); - switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "sip_invite_domain", domain); + switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "sip_invite_domain", domain); + switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "sip_invite_contact_params", "~isfocus"); if (!strncasecmp(ostr, "url+", 4)) { ostr += 4; diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 1f910c70ab..a05f880a4a 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -3288,7 +3288,7 @@ static void pickup_send_presence(const char *key_name) } if (zstr(domain_name)) { - dup_domain_name = switch_core_get_variable_dup("domain"); + dup_domain_name = switch_core_get_domain(SWITCH_TRUE); domain_name = dup_domain_name; } @@ -3363,7 +3363,7 @@ static void pickup_pres_event_handler(switch_event_t *event) if ((domain_name = strchr(key_name, '@'))) { *domain_name++ = '\0'; } else { - dup_domain_name = switch_core_get_variable_dup("domain"); + dup_domain_name = switch_core_get_domain(SWITCH_TRUE); domain_name = dup_domain_name; } @@ -3422,7 +3422,7 @@ static void pickup_add_session(switch_core_session_t *session, const char *key) char *dup_key = NULL; if (!strchr(key, '@')) { - dup_key = switch_mprintf("%s@%s", key, switch_core_get_variable("domain")); + dup_key = switch_mprintf("%s@%s", key, switch_core_get_domain(SWITCH_FALSE)); key = dup_key; } @@ -3456,7 +3456,7 @@ static char *pickup_pop_uuid(const char *key, const char *uuid) char *dup_key = NULL; if (!strchr(key, '@')) { - dup_key = switch_mprintf("%s@%s", key, switch_core_get_variable("domain")); + dup_key = switch_mprintf("%s@%s", key, switch_core_get_domain(SWITCH_FALSE)); key = dup_key; } @@ -3781,7 +3781,7 @@ static switch_call_cause_t group_outgoing_channel(switch_core_session_t *session if ((domain = strchr(group, '@'))) { *domain++ = '\0'; } else { - domain = switch_core_get_variable_dup("domain"); + domain = switch_core_get_domain(SWITCH_TRUE); dup_domain = domain; } @@ -3904,7 +3904,7 @@ static switch_call_cause_t user_outgoing_channel(switch_core_session_t *session, if ((domain = strchr(user, '@'))) { *domain++ = '\0'; } else { - domain = switch_core_get_variable_dup("domain"); + domain = switch_core_get_domain(SWITCH_TRUE); dup_domain = domain; } diff --git a/src/mod/applications/mod_fifo/mod_fifo.c b/src/mod/applications/mod_fifo/mod_fifo.c index 05652d2c23..d2cefcfea4 100644 --- a/src/mod/applications/mod_fifo/mod_fifo.c +++ b/src/mod/applications/mod_fifo/mod_fifo.c @@ -863,7 +863,7 @@ static fifo_node_t *create_node(const char *name, uint32_t importance, switch_mu node->name = switch_core_strdup(node->pool, name); if (!strchr(name, '@')) { - domain_name = switch_core_get_variable_dup("domain"); + domain_name = switch_core_get_domain(SWITCH_TRUE); node->domain_name = switch_core_strdup(node->pool, domain_name); } diff --git a/src/mod/applications/mod_httapi/mod_httapi.c b/src/mod/applications/mod_httapi/mod_httapi.c index 52e4a21a6b..e5bfac12ae 100644 --- a/src/mod/applications/mod_httapi/mod_httapi.c +++ b/src/mod/applications/mod_httapi/mod_httapi.c @@ -263,7 +263,7 @@ static switch_status_t parse_voicemail(const char *tag_name, client_t *client, s if (zstr(profile)) profile = "default"; if (zstr(domain)) { - if ((ddom = switch_core_get_variable_dup("domain"))) { + if ((ddom = switch_core_get_domain(SWITCH_TRUE))) { domain = ddom; } } diff --git a/src/mod/applications/mod_oreka/mod_oreka.c b/src/mod/applications/mod_oreka/mod_oreka.c index f954341b4c..a9c8c927e1 100644 --- a/src/mod/applications/mod_oreka/mod_oreka.c +++ b/src/mod/applications/mod_oreka/mod_oreka.c @@ -274,17 +274,17 @@ static int oreka_send_sip_message(oreka_session_t *oreka, oreka_recording_status caller_id_number = switch_caller_get_field_by_name(caller_profile, "caller_id_number"); caller_id_name = switch_caller_get_field_by_name(caller_profile, "caller_id_name"); - if (!caller_id_name) { + if (zstr(caller_id_name)) { caller_id_name = caller_id_number; } callee_id_number = switch_caller_get_field_by_name(caller_profile, "callee_id_number"); - if (!callee_id_number) { + if (zstr(callee_id_number)) { callee_id_number = switch_caller_get_field_by_name(caller_profile, "destination_number"); } callee_id_name = switch_caller_get_field_by_name(caller_profile, "callee_id_name"); - if (!callee_id_name) { + if (zstr(callee_id_name)) { callee_id_name = callee_id_number; } diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.c b/src/mod/applications/mod_spandsp/mod_spandsp.c index 51e2505758..b6ff1771de 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp.c @@ -577,6 +577,26 @@ switch_status_t load_configuration(switch_bool_t reload) spandsp_globals.disable_v17 = 1; else spandsp_globals.disable_v17 = 0; + } else if (!strcmp(name, "enable-colour")) { + if (switch_true(value)) + spandsp_globals.enable_colour_fax = 1; + else + spandsp_globals.enable_colour_fax = 0; + } else if (!strcmp(name, "enable-image-resizing")) { + if (switch_true(value)) + spandsp_globals.enable_image_resizing = 1; + else + spandsp_globals.enable_image_resizing = 0; + } else if (!strcmp(name, "enable-colour-to-bilevel")) { + if (switch_true(value)) + spandsp_globals.enable_colour_to_bilevel = 1; + else + spandsp_globals.enable_colour_to_bilevel = 0; + } else if (!strcmp(name, "enable-grayscale-to-bilevel")) { + if (switch_true(value)) + spandsp_globals.enable_grayscale_to_bilevel = 1; + else + spandsp_globals.enable_grayscale_to_bilevel = 0; } else if (!strcmp(name, "enable-t38")) { if (switch_true(value)) { spandsp_globals.enable_t38= 1; diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.h b/src/mod/applications/mod_spandsp/mod_spandsp.h index 323ff07a45..5f5a038c43 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp.h +++ b/src/mod/applications/mod_spandsp/mod_spandsp.h @@ -60,6 +60,10 @@ struct spandsp_globals { short int use_ecm; short int verbose; short int disable_v17; + short int enable_colour_fax; + short int enable_image_resizing; + short int enable_colour_to_bilevel; + short int enable_grayscale_to_bilevel; short int enable_t38; short int enable_t38_request; short int enable_t38_insist; diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c index 21a9cc51dc..db9d8c1f2a 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c @@ -89,6 +89,10 @@ struct pvt_s { int use_ecm; int disable_v17; + int enable_colour_fax; + int enable_image_resizing; + int enable_colour_to_bilevel; + int enable_grayscale_to_bilevel; int verbose; int caller; @@ -438,7 +442,7 @@ static int phase_d_handler(t30_state_t *s, void *user_data, int msg) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "==== Page %s===========================================================\n", pvt->app_mode == FUNCTION_TX ? "Sent ====": "Received "); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Page no = %d\n", (pvt->app_mode == FUNCTION_TX) ? t30_stats.pages_tx : t30_stats.pages_rx); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Image type = %s (%s in the file)\n", t4_image_type_to_str(t30_stats.type), t4_image_type_to_str(t30_stats.image_type)); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Image type = %s (%s in the file)\n", t4_image_type_to_str(t30_stats.type), t4_image_type_to_str(t30_stats.image_type)); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Image size = %d x %d pixels (%d x %d pixels in the file)\n", t30_stats.width, t30_stats.length, t30_stats.image_width, t30_stats.image_length); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Image resolution = %d/m x %d/m (%d/m x %d/m in the file)\n", t30_stats.x_resolution, t30_stats.y_resolution, t30_stats.image_x_resolution, t30_stats.image_y_resolution); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Compression = %s (%d)\n", t4_compression_to_str(t30_stats.compression), t30_stats.compression); @@ -693,6 +697,7 @@ static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode) const char *tz; int fec_entries = DEFAULT_FEC_ENTRIES; int fec_span = DEFAULT_FEC_SPAN; + int compressions; session = (switch_core_session_t *) pvt->session; @@ -872,8 +877,12 @@ static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode) t30_set_phase_b_handler(t30, phase_b_handler, pvt); t30_set_supported_image_sizes(t30, - T4_SUPPORT_LENGTH_US_LETTER | T4_SUPPORT_LENGTH_US_LEGAL | T4_SUPPORT_LENGTH_UNLIMITED - | T4_SUPPORT_WIDTH_215MM | T4_SUPPORT_WIDTH_255MM | T4_SUPPORT_WIDTH_303MM); + T4_SUPPORT_LENGTH_US_LETTER + | T4_SUPPORT_LENGTH_US_LEGAL + | T4_SUPPORT_LENGTH_UNLIMITED + | T4_SUPPORT_WIDTH_215MM + | T4_SUPPORT_WIDTH_255MM + | T4_SUPPORT_WIDTH_303MM); t30_set_supported_bilevel_resolutions(t30, T4_RESOLUTION_R8_STANDARD | T4_RESOLUTION_R8_FINE @@ -883,7 +892,28 @@ static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode) | T4_RESOLUTION_200_200 | T4_RESOLUTION_200_400 | T4_RESOLUTION_400_400); - t30_set_supported_colour_resolutions(t30, 0); + compressions = T4_COMPRESSION_T4_1D + | T4_COMPRESSION_T4_2D + | T4_COMPRESSION_T6 + | T4_COMPRESSION_T85 + | T4_COMPRESSION_T85_L0; + if (pvt->enable_colour_fax) { + t30_set_supported_colour_resolutions(t30, T4_RESOLUTION_100_100 + | T4_RESOLUTION_200_200 + | T4_RESOLUTION_300_300 + | T4_RESOLUTION_400_400); + compressions |= (T4_COMPRESSION_COLOUR | T4_COMPRESSION_T42_T81); + } else { + t30_set_supported_colour_resolutions(t30, 0); + } + if (pvt->enable_image_resizing) + compressions |= T4_COMPRESSION_RESCALING; + if (pvt->enable_colour_to_bilevel) + compressions |= T4_COMPRESSION_COLOUR_TO_BILEVEL; + if (pvt->enable_grayscale_to_bilevel) + compressions |= T4_COMPRESSION_GRAY_TO_BILEVEL; + + t30_set_supported_compressions(t30, compressions); if (pvt->disable_v17) { t30_set_supported_modems(t30, T30_SUPPORT_V29 | T30_SUPPORT_V27TER); @@ -894,11 +924,10 @@ static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode) } if (pvt->use_ecm) { - t30_set_supported_compressions(t30, T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D | T4_COMPRESSION_T6 | T4_COMPRESSION_T85 | T4_COMPRESSION_T85_L0); t30_set_ecm_capability(t30, TRUE); switch_channel_set_variable(channel, "fax_ecm_requested", "1"); } else { - t30_set_supported_compressions(t30, T4_COMPRESSION_T4_1D | T4_COMPRESSION_T4_2D); + t30_set_ecm_capability(t30, FALSE); switch_channel_set_variable(channel, "fax_ecm_requested", "0"); } @@ -1217,6 +1246,30 @@ static pvt_t *pvt_init(switch_core_session_t *session, mod_spandsp_fax_applicati pvt->disable_v17 = spandsp_globals.disable_v17; } + if ((tmp = switch_channel_get_variable(channel, "fax_enable_colour"))) { + pvt->enable_colour_fax = switch_true(tmp); + } else { + pvt->enable_colour_fax = spandsp_globals.enable_colour_fax; + } + + if ((tmp = switch_channel_get_variable(channel, "fax_enable_image_resizing"))) { + pvt->enable_image_resizing = switch_true(tmp); + } else { + pvt->enable_image_resizing = spandsp_globals.enable_image_resizing; + } + + if ((tmp = switch_channel_get_variable(channel, "fax_enable_colour_to_bilevel"))) { + pvt->enable_colour_to_bilevel = switch_true(tmp); + } else { + pvt->enable_colour_to_bilevel = spandsp_globals.enable_colour_to_bilevel; + } + + if ((tmp = switch_channel_get_variable(channel, "fax_enable_grayscale_to_bilevel"))) { + pvt->enable_grayscale_to_bilevel = switch_true(tmp); + } else { + pvt->enable_grayscale_to_bilevel = spandsp_globals.enable_grayscale_to_bilevel; + } + if ((tmp = switch_channel_get_variable(channel, "fax_verbose"))) { pvt->verbose = switch_true(tmp); } else { diff --git a/src/mod/applications/mod_valet_parking/mod_valet_parking.c b/src/mod/applications/mod_valet_parking/mod_valet_parking.c index a428f82684..f14c81c3bf 100644 --- a/src/mod/applications/mod_valet_parking/mod_valet_parking.c +++ b/src/mod/applications/mod_valet_parking/mod_valet_parking.c @@ -287,7 +287,7 @@ static void valet_send_presence(const char *lot_name, valet_lot_t *lot, valet_to } if (zstr(domain_name)) { - dup_domain_name = switch_core_get_variable_dup("domain"); + dup_domain_name = switch_core_get_domain(SWITCH_TRUE); domain_name = dup_domain_name; } diff --git a/src/mod/applications/mod_voicemail/mod_voicemail.c b/src/mod/applications/mod_voicemail/mod_voicemail.c index 41f7cef5d6..bf355d043b 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; @@ -3167,7 +3167,7 @@ static switch_status_t voicemail_inject(const char *data, switch_core_session_t } if (zstr(domain)) { - if ((dup_domain = switch_core_get_variable_dup("domain"))) { + if ((dup_domain = switch_core_get_domain(SWITCH_TRUE))) { domain = dup_domain; } profile_name = domain; 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 cd09bf3a26..416eb17b82 100644 --- a/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c +++ b/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c @@ -894,6 +894,7 @@ SWITCH_STANDARD_API(sangoma_function) char *argv[10] = { 0 }; int argc = 0; char *mycmd = NULL; + switch_bool_t locked = SWITCH_FALSE; if (zstr(cmd)) { stream->write_function(stream, "%s", SANGOMA_SYNTAX); @@ -910,6 +911,10 @@ SWITCH_STANDARD_API(sangoma_function) return SWITCH_STATUS_SUCCESS; } + /* Most operations in this API require the global session lock anyways since sessions can disappear at any moment ... */ + switch_mutex_lock(g_sessions_lock); + locked = SWITCH_TRUE; + if (!strcasecmp(argv[0], "settings")) { char addrbuff[50]; int addr; @@ -922,7 +927,6 @@ SWITCH_STANDARD_API(sangoma_function) const void *var; void *val; unsigned totalsess = 0; - switch_mutex_lock(g_sessions_lock); #define STATS_FORMAT "%-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %-15.15s %-15.15s\n" stream->write_function(stream, STATS_FORMAT, "Session", "Codec", "Enc", "Dec", "Enc Tx", "Enc Rx", "Dec Tx", "Dec Rx", "Enc Lost", "Dec Lost", "Enc AvgRxMs", "Dec AvgRxMs"); @@ -967,7 +971,6 @@ SWITCH_STANDARD_API(sangoma_function) decoder_avgrxus_str); totalsess++; } - switch_mutex_unlock(g_sessions_lock); stream->write_function(stream, "Total sessions: %d\n", totalsess); } else if (!strcasecmp(argv[0], "stats")) { struct sangoma_transcoding_session *sess; @@ -983,6 +986,7 @@ SWITCH_STANDARD_API(sangoma_function) stream->write_function(stream, "%s", SANGOMA_SYNTAX); goto done; } + sess = sangoma_find_session(sessid); if (!sess) { stream->write_function(stream, "Failed to find session %lu\n", sessid); @@ -1076,6 +1080,9 @@ SWITCH_STANDARD_API(sangoma_function) } done: + if (locked) { + switch_mutex_unlock(g_sessions_lock); + } switch_safe_free(mycmd); return SWITCH_STATUS_SUCCESS; diff --git a/src/mod/endpoints/mod_loopback/mod_loopback.c b/src/mod/endpoints/mod_loopback/mod_loopback.c index 105b96564a..41f5849655 100644 --- a/src/mod/endpoints/mod_loopback/mod_loopback.c +++ b/src/mod/endpoints/mod_loopback/mod_loopback.c @@ -458,10 +458,7 @@ static switch_status_t channel_on_execute(switch_core_session_t *session) if ((find_non_loopback_bridge(tech_pvt->other_session, &other_session, &other_uuid) == SWITCH_STATUS_SUCCESS)) { switch_channel_t *other_channel = switch_core_session_get_channel(other_session); - if (switch_channel_test_flag(other_channel, CF_BRIDGED)) { - /* Wait for real channel to be exchanging media */ - switch_channel_wait_for_state(other_channel, channel, CS_EXCHANGE_MEDIA); - } + switch_channel_wait_for_state_timeout(other_channel, CS_EXCHANGE_MEDIA, 5000); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_INFO, "BOWOUT Replacing loopback channel with real channel: %s\n", switch_channel_get_name(other_channel)); diff --git a/src/mod/endpoints/mod_rtmp/rtmp_sig.c b/src/mod/endpoints/mod_rtmp/rtmp_sig.c index fb82b180ea..a3160eee5b 100644 --- a/src/mod/endpoints/mod_rtmp/rtmp_sig.c +++ b/src/mod/endpoints/mod_rtmp/rtmp_sig.c @@ -376,7 +376,7 @@ RTMP_INVOKE_FUNCTION(rtmp_i_login) } if (zstr(domain)) { - ddomain = switch_core_get_variable_dup("domain"); + ddomain = switch_core_get_domain(SWITCH_TRUE); domain = ddomain; } diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index f3dda49f1b..1dbf66b7b2 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -438,6 +438,7 @@ switch_status_t skinny_session_send_call_info_all(switch_core_session_t *session struct skinny_session_set_variables_helper { private_t *tech_pvt; switch_channel_t *channel; + listener_t *listener; uint32_t count; }; @@ -463,6 +464,9 @@ int skinny_session_set_variables_callback(void *pArg, int argc, char **argv, cha struct skinny_session_set_variables_helper *helper = pArg; char *tmp; + listener_t *listener; + + switch_xml_t xroot, xdomain, xuser, xvariables, xvariable; helper->count++; switch_channel_set_variable_name_printf(helper->channel, device_name, "skinny_device_name_%d", helper->count); @@ -482,6 +486,50 @@ int skinny_session_set_variables_callback(void *pArg, int argc, char **argv, cha switch_channel_set_variable_name_printf(helper->channel, value, "skinny_line_value_%d", helper->count); switch_channel_set_variable_name_printf(helper->channel, caller_name, "skinny_line_caller_name_%d", helper->count); + listener = helper->listener; + + if ( ! listener ) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(helper->tech_pvt->session), SWITCH_LOG_DEBUG, + "no defined listener on channel var setup, will not attempt to set variables\n"); + return(0); + } + + /* Process through and extract any variables from the user and set in the channel */ + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(helper->tech_pvt->session), SWITCH_LOG_DEBUG, + "searching for user (id=%s) in profile %s in channel var setup\n", + listener->device_name, listener->profile->domain); + + if (switch_xml_locate_user("id", listener->device_name, listener->profile->domain, "", + &xroot, &xdomain, &xuser, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(helper->tech_pvt->session), SWITCH_LOG_WARNING, + "unable to find user (id=%s) in channel var setup\n", listener->device_name); + } + + if ( xuser ) { + char *uid = (char *) switch_xml_attr_soft(xuser, "id"); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(helper->tech_pvt->session), SWITCH_LOG_DEBUG, + "found user (id=%s) in channel var setup\n", uid); + + if ((xvariables = switch_xml_child(xuser, "variables"))) { + + for (xvariable = switch_xml_child(xvariables, "variable"); xvariable; xvariable = xvariable->next) { + char *name = (char *) switch_xml_attr_soft(xvariable, "name"); + char *value = (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", name, value, listener->device_name); + + switch_channel_set_variable_name_printf(helper->channel, value, "%s", name); + } + } + } + + if ( xroot ) { + switch_xml_free(xroot); + } + return 0; } @@ -492,6 +540,7 @@ switch_status_t skinny_session_set_variables(switch_core_session_t *session, lis helper.tech_pvt = switch_core_session_get_private(session); helper.channel = switch_core_session_get_channel(session); + helper.listener = listener; helper.count = 0; switch_channel_set_variable(helper.channel, "skinny_profile_name", helper.tech_pvt->profile->name); diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 2f193c051a..e3ffae4778 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -284,11 +284,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" : ""); } } @@ -3536,7 +3538,7 @@ SWITCH_STANDARD_API(sofia_contact_function) } if (zstr(domain)) { - dup_domain = switch_core_get_variable_dup("domain"); + dup_domain = switch_core_get_domain(SWITCH_TRUE); domain = dup_domain; } @@ -3726,7 +3728,7 @@ SWITCH_STANDARD_API(sofia_presence_data_function) } if (zstr(domain)) { - dup_domain = switch_core_get_variable_dup("domain"); + dup_domain = switch_core_get_domain(SWITCH_TRUE); domain = dup_domain; } @@ -4580,6 +4582,70 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session return cause; } +static int notify_csta_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + nua_handle_t *nh; + sofia_profile_t *ext_profile = NULL, *profile = (sofia_profile_t *) pArg; + int i = 0; + char *user = argv[i++]; + char *host = argv[i++]; + char *contact_in = argv[i++]; + char *profile_name = argv[i++]; + char *call_id = argv[i++]; + char *full_from = argv[i++]; + char *full_to = argv[i++]; + int expires = atoi(argv[i++]); + char *body = argv[i++]; + char *ct = argv[i++]; + char *id = NULL; + char *contact; + sofia_destination_t *dst = NULL; + char *route_uri = NULL; + + time_t epoch_now = switch_epoch_time_now(NULL); + time_t expires_in = (expires - epoch_now); + char *extra_headers = switch_mprintf("Subscription-State: active, %d\r\n", expires_in); + + if (profile_name && strcasecmp(profile_name, profile->name)) { + if ((ext_profile = sofia_glue_find_profile(profile_name))) { + profile = ext_profile; + } + } + + id = switch_mprintf("sip:%s@%s", user, host); + switch_assert(id); + contact = sofia_glue_get_url_from_contact(contact_in, 1); + + + dst = sofia_glue_get_destination((char *) contact); + + if (dst->route_uri) { + route_uri = sofia_glue_strip_uri(dst->route_uri); + } + + //nh = nua_handle(profile->nua, NULL, NUTAG_URL(dst->contact), SIPTAG_FROM_STR(id), SIPTAG_TO_STR(id), SIPTAG_CONTACT_STR(profile->url), TAG_END()); + nh = nua_handle(profile->nua, NULL, NUTAG_URL(dst->contact), SIPTAG_FROM_STR(full_from), SIPTAG_TO_STR(full_to), SIPTAG_CONTACT_STR(profile->url), TAG_END()); + + nua_handle_bind(nh, &mod_sofia_globals.destroy_private); + + nua_notify(nh, NUTAG_NEWSUB(1), + TAG_IF(dst->route_uri, NUTAG_PROXY(route_uri)), TAG_IF(dst->route, SIPTAG_ROUTE_STR(dst->route)), TAG_IF(call_id, SIPTAG_CALL_ID_STR(call_id)), + SIPTAG_EVENT_STR("as-feature-event"), SIPTAG_CONTENT_TYPE_STR(ct), TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)), TAG_IF(!zstr(body), SIPTAG_PAYLOAD_STR(body)), TAG_END()); + + + + switch_safe_free(route_uri); + sofia_glue_free_destination(dst); + + free(id); + free(contact); + + if (ext_profile) { + sofia_glue_release_profile(ext_profile); + } + + return 0; +} static int notify_callback(void *pArg, int argc, char **argv, char **columnNames) { @@ -4653,22 +4719,69 @@ static void general_event_handler(switch_event_t *event) const char *to_uri = switch_event_get_header(event, "to-uri"); const char *from_uri = switch_event_get_header(event, "from-uri"); const char *extra_headers = switch_event_get_header(event, "extra-headers"); + const char *contact_uri = switch_event_get_header(event, "contact-uri"); + const char *no_sub_state = switch_event_get_header(event, "no-sub-state"); sofia_profile_t *profile; + if (contact_uri) { + if (!es) { + es = "message-summary"; + } - if (to_uri || from_uri) { + if (!ct) { + ct = "application/simple-message-summary"; + } - if (!to_uri) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing To-URI header\n"); + if (!profile_name) { + profile_name = "default"; + } + + if (!(profile = sofia_glue_find_profile(profile_name))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't find profile %s\n", profile_name); return; } - if (!from_uri) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing From-URI header\n"); - return; + if (to_uri && from_uri) { + sofia_destination_t *dst = NULL; + nua_handle_t *nh; + char *route_uri = NULL; + char *sip_sub_st = NULL; + + dst = sofia_glue_get_destination((char *) contact_uri); + + if (dst->route_uri) { + route_uri = sofia_glue_strip_uri(dst->route_uri); + } + + nh = nua_handle(profile->nua, + NULL, + NUTAG_URL(dst->contact), + SIPTAG_FROM_STR(from_uri), + SIPTAG_TO_STR(to_uri), + SIPTAG_CONTACT_STR(profile->url), + TAG_END()); + + nua_handle_bind(nh, &mod_sofia_globals.destroy_private); + + if (!switch_true(no_sub_state)) { + sip_sub_st = "terminated;reason=noresource"; + } + + nua_notify(nh, + NUTAG_NEWSUB(1), TAG_IF(sip_sub_st, SIPTAG_SUBSCRIPTION_STATE_STR(sip_sub_st)), + TAG_IF(dst->route_uri, NUTAG_PROXY(dst->route_uri)), TAG_IF(dst->route, SIPTAG_ROUTE_STR(dst->route)), TAG_IF(call_id, SIPTAG_CALL_ID_STR(call_id)), + SIPTAG_EVENT_STR(es), TAG_IF(ct, SIPTAG_CONTENT_TYPE_STR(ct)), TAG_IF(!zstr(body), SIPTAG_PAYLOAD_STR(body)), + TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)), TAG_END()); + + switch_safe_free(route_uri); + sofia_glue_free_destination(dst); + + sofia_glue_release_profile(profile); } + return; + } else if (to_uri || from_uri) { if (!es) { es = "message-summary"; } @@ -4768,6 +4881,96 @@ static void general_event_handler(switch_event_t *event) } break; + case SWITCH_EVENT_PHONE_FEATURE: + { + const char *profile_name = switch_event_get_header(event, "profile"); + const char *user = switch_event_get_header(event, "user"); + const char *host = switch_event_get_header(event, "host"); + const char *call_id = switch_event_get_header(event, "call-id"); + const char *csta_event = switch_event_get_header(event, "csta-event"); + + char *ct = "application/x-as-feature-event+xml"; + + sofia_profile_t *profile; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Phone Feature NOTIFY\n"); + if (profile_name && user && host && (profile = sofia_glue_find_profile(profile_name))) { + char *sql; + switch_stream_handle_t stream = { 0 }; + SWITCH_STANDARD_STREAM(stream); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "we have all required vars\n"); + + if (csta_event) { + if (!strcmp(csta_event, "init")) { + char *boundary_string = "UniqueFreeSWITCHBoundary"; + switch_stream_handle_t dnd_stream = { 0 }; + char *header_name = NULL; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sending multipart with DND and CFWD\n"); + + if ((header_name = switch_event_get_header(event, "forward_immediate"))) { + switch_stream_handle_t fwdi_stream = { 0 }; + SWITCH_STANDARD_STREAM(fwdi_stream); + write_csta_xml_chunk(event, fwdi_stream, "ForwardingEvent", "forwardImmediate"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[%s] is %d bytes long\n", (char *)fwdi_stream.data, (int)strlen(fwdi_stream.data)); + stream.write_function(&stream, "--%s\nContent-Type: application/x-as-feature-event+xml\nContent-Length:%d\nContent-ID:<%s@%s>\n\n%s", boundary_string, strlen(fwdi_stream.data), user, host, fwdi_stream.data); + switch_safe_free(fwdi_stream.data); + } + if ((header_name = switch_event_get_header(event, "forward_busy"))) { + switch_stream_handle_t fwdb_stream = { 0 }; + SWITCH_STANDARD_STREAM(fwdb_stream); + write_csta_xml_chunk(event, fwdb_stream, "ForwardingEvent", "forwardBusy"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[%s] is %d bytes long\n", (char *)fwdb_stream.data, (int)strlen(fwdb_stream.data)); + stream.write_function(&stream, "--%s\nContent-Type: application/x-as-feature-event+xml\nContent-Length:%d\nContent-ID:<%s@%s>\n\n%s", boundary_string, strlen(fwdb_stream.data), user, host, fwdb_stream.data); + switch_safe_free(fwdb_stream.data); + } + if ((header_name = switch_event_get_header(event, "forward_no_answer"))) { + switch_stream_handle_t fwdna_stream = { 0 }; + SWITCH_STANDARD_STREAM(fwdna_stream); + write_csta_xml_chunk(event, fwdna_stream, "ForwardingEvent", "forwardNoAns"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[%s] is %d bytes long\n", (char *)fwdna_stream.data, (int)strlen(fwdna_stream.data)); + stream.write_function(&stream, "--%s\nContent-Type: application/x-as-feature-event+xml\nContent-Length:%d\nContent-ID:<%s@%s>\n\n%s", boundary_string, strlen(fwdna_stream.data), user, host, fwdna_stream.data); + switch_safe_free(fwdna_stream.data); + } + + SWITCH_STANDARD_STREAM(dnd_stream); + write_csta_xml_chunk(event, dnd_stream, "DoNotDisturbEvent", NULL); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[%s] is %d bytes long\n", (char *)dnd_stream.data, (int)strlen(dnd_stream.data)); + stream.write_function(&stream, "--%s\nContent-Type:application/x-as-feature-event+xml\nContent-Length:%d\nContent-ID:<%s@%s>\n\n%s", boundary_string, strlen(dnd_stream.data), user, host, dnd_stream.data); + switch_safe_free(dnd_stream.data); + + stream.write_function(&stream, "--%s--\n", boundary_string); + + ct = switch_mprintf("multipart/mixed; boundary=\"%s\"", boundary_string); + } else { + // this will need some work to handle the different types of forwarding events + write_csta_xml_chunk(event, stream, csta_event, NULL); + } + } + + if (call_id) { + sql = switch_mprintf("select sip_user,sip_host,contact,profile_name,call_id,full_from,full_to,expires,'%q', '%q' " + "from sip_subscriptions where event='as-feature-event' and call_id='%q'", stream.data, ct, call_id); + } else { + sql = switch_mprintf("select sip_user,sip_host,contact,profile_name,call_id,full_from,full_to,expires,'%q', '%q' " + "from sip_subscriptions where event='as-feature-event' and sip_user='%s' and sip_host='%q'", stream.data, ct, switch_str_nil(user), switch_str_nil(host) + ); + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query: %s\n", sql); + switch_safe_free(stream.data); + switch_mutex_lock(profile->ireg_mutex); + sofia_glue_execute_sql_callback(profile, NULL, sql, notify_csta_callback, profile); + switch_mutex_unlock(profile->ireg_mutex); + sofia_glue_release_profile(profile); + + free(sql); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "missing something\n"); + } + } + break; case SWITCH_EVENT_SEND_MESSAGE: { const char *profile_name = switch_event_get_header(event, "profile"); @@ -5022,6 +5225,57 @@ static void general_event_handler(switch_event_t *event) } } +void write_csta_xml_chunk(switch_event_t *event, switch_stream_handle_t stream, const char *csta_event, char *fwdtype) +{ + const char *device = switch_event_get_header(event, "device"); + + if (csta_event) { + stream.write_function(&stream, "\n<%s xmlns=\"http://www.ecma-international.org/standards/ecma-323/csta/ed3\">\n", csta_event); + } + + if (device) { + stream.write_function(&stream, " %s\n", device); + } + + if (!strcmp(csta_event, "DoNotDisturbEvent")) { + const char *dndstatus = switch_event_get_header(event, "doNotDisturbOn"); + + if (dndstatus) { + stream.write_function(&stream, " %s\n", dndstatus); + } + } else if(!strcmp(csta_event, "ForwardingEvent")) { + const char *fwdstatus = switch_event_get_header(event, "forwardStatus"); + const char *fwdto = NULL; + const char *ringcount = NULL; + + if (strcmp("forwardImmediate", fwdtype)) { + fwdto = switch_event_get_header(event, "forward_immediate"); + } else if (strcmp("forwardBusy", fwdtype)) { + fwdto = switch_event_get_header(event, "forward_busy"); + } else if (strcmp("fowardNoAns", fwdtype)) { + fwdto = switch_event_get_header(event, "forward_no_answer"); + ringcount = switch_event_get_header(event, "ringCount"); + } + + if (fwdtype) { + stream.write_function(&stream, " %s\n", fwdtype); + } + if (fwdstatus) { + stream.write_function(&stream, " %s\n", fwdstatus); + } + if (fwdto) { + stream.write_function(&stream, " %s\n", fwdto); + } + if (ringcount) { + stream.write_function(&stream, " %s\n", ringcount); + } + } + + if (csta_event) { + stream.write_function(&stream, "\n", csta_event); + } +} + switch_status_t list_profiles_full(const char *line, const char *cursor, switch_console_callback_match_t **matches, switch_bool_t show_aliases) { sofia_profile_t *profile = NULL; @@ -5285,6 +5539,11 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_sofia_load) return SWITCH_STATUS_GENERR; } + if (switch_event_bind(modname, SWITCH_EVENT_PHONE_FEATURE, SWITCH_EVENT_SUBCLASS_ANY, general_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n"); + return SWITCH_STATUS_GENERR; + } + if (switch_event_bind(modname, SWITCH_EVENT_SEND_MESSAGE, SWITCH_EVENT_SUBCLASS_ANY, general_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n"); return SWITCH_STATUS_GENERR; diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index a2f85806b4..c27fcb55a3 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -1,4 +1,4 @@ -/* +/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2012, Anthony Minessale II * @@ -22,14 +22,14 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): - * + * * Anthony Minessale II * Ken Rice * Paul D. Tinsley * Bret McDanel * Marcel Barbulescu * Raymond Chandler - * Emmanuel Schmidbauer + * Emmanuel Schmidbauer * * * mod_sofia.h -- SOFIA SIP Endpoint @@ -149,9 +149,9 @@ typedef enum { typedef struct sofia_dispatch_event_s { nua_saved_event_t event[1]; - nua_handle_t *nh; - nua_event_data_t const *data; - su_time_t when; + nua_handle_t *nh; + nua_event_data_t const *data; + su_time_t when; sip_t *sip; nua_t *nua; sofia_profile_t *profile; @@ -353,7 +353,7 @@ struct mod_sofia_globals { int reg_deny_binding_fetch_and_no_lookup; /* backwards compatibility */ int auto_nat; int tracelevel; - char *capture_server; + char *capture_server; int rewrite_multicasted_fs_path; int presence_flush; switch_thread_t *presence_thread; @@ -511,10 +511,10 @@ typedef enum { } sofia_media_options_t; typedef enum { - PAID_DEFAULT = 0, - PAID_USER, - PAID_USER_DOMAIN, - PAID_VERBATIM + PAID_DEFAULT = 0, + PAID_USER, + PAID_USER_DOMAIN, + PAID_VERBATIM } sofia_paid_type_t; #define MAX_RTPIP 50 @@ -619,7 +619,7 @@ struct sofia_profile { char *pre_trans_execute; char *post_trans_execute; char *inner_pre_trans_execute; - char *inner_post_trans_execute; + char *inner_post_trans_execute; switch_sql_queue_manager_t *qm; char *acl[SOFIA_MAX_ACL]; char *acl_pass_context[SOFIA_MAX_ACL]; @@ -678,7 +678,7 @@ struct sofia_profile { sofia_paid_type_t paid_type; uint32_t rtp_digit_delay; switch_queue_t *event_queue; - switch_thread_t *thread; + switch_thread_t *thread; switch_core_media_vflag_t vflags; char *ws_ip; switch_port_t ws_port; @@ -851,7 +851,7 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t sofia_dispatch_event_t *de, tagi_t tags[]); void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip, sofia_dispatch_event_t *de, tagi_t tags[]); - + void sofia_reg_handle_sip_i_register(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t **sofia_private, sip_t const *sip, sofia_dispatch_event_t *de, @@ -868,9 +868,9 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void void launch_sofia_profile_thread(sofia_profile_t *profile); switch_status_t sofia_presence_chat_send(switch_event_t *message_event); - + /* - * \brief Sets the "ep_codec_string" channel variable, parsing r_sdp and taing codec_string in consideration + * \brief Sets the "ep_codec_string" channel variable, parsing r_sdp and taing codec_string in consideration * \param channel Current channel * \param codec_string The profile's codec string or NULL if inexistant * \param sdp The parsed SDP content @@ -935,7 +935,7 @@ switch_call_cause_t sofia_glue_sip_cause_to_freeswitch(int status); void sofia_glue_do_xfer_invite(switch_core_session_t *session); uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sip_t const *sip, sofia_dispatch_event_t *de, - sofia_regtype_t regtype, char *key, + sofia_regtype_t regtype, char *key, uint32_t keylen, switch_event_t **v_event, const char *is_nat, sofia_private_t **sofia_private_p, switch_xml_t *user_xml); extern switch_endpoint_interface_t *sofia_endpoint_interface; void sofia_presence_set_chat_hash(private_object_t *tech_pvt, sip_t const *sip); @@ -1044,7 +1044,7 @@ void sofia_glue_restart_all_profiles(void); const char *sofia_state_string(int state); void sofia_wait_for_reply(struct private_object *tech_pvt, nua_event_t event, uint32_t timeout); -/* +/* * Logging control functions */ @@ -1117,6 +1117,7 @@ switch_status_t sofia_glue_ext_address_lookup(sofia_profile_t *profile, char **i void sofia_reg_check_socket(sofia_profile_t *profile, const char *call_id, const char *network_addr, const char *network_ip); void sofia_reg_close_handles(sofia_profile_t *profile); +void write_csta_xml_chunk(switch_event_t *event, switch_stream_handle_t stream, const char *csta_event, char *fwd_type); /* For Emacs: * Local Variables: * mode:c diff --git a/src/mod/endpoints/mod_sofia/rtp.c b/src/mod/endpoints/mod_sofia/rtp.c index e4fffa6f78..ea4921052e 100644 --- a/src/mod/endpoints/mod_sofia/rtp.c +++ b/src/mod/endpoints/mod_sofia/rtp.c @@ -153,6 +153,7 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi char name[128]; crtp_private_t *tech_pvt = NULL; switch_caller_profile_t *caller_profile; + switch_rtp_flag_t rtp_flags[SWITCH_RTP_FLAG_INVALID] = {0}; const char *err; @@ -261,7 +262,7 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi if (!(tech_pvt->rtp_session = switch_rtp_new(local_addr, local_port, remote_addr, remote_port, tech_pvt->agreed_pt, tech_pvt->read_codec.implementation->samples_per_packet, ptime * 1000, - 0, "soft", &err, switch_core_session_get_pool(*new_session)))) { + rtp_flags, "soft", &err, switch_core_session_get_pool(*new_session)))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't setup RTP session: [%s]\n", err); goto fail; } diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index db11faa40e..c0fb9e720e 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -1,4 +1,4 @@ -/* +/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2012, Anthony Minessale II * @@ -22,7 +22,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): - * + * * Anthony Minessale II * Ken Rice * Paul D. Tinsley @@ -91,7 +91,7 @@ void sofia_handle_sip_r_notify(switch_core_session_t *session, int status, if (status == 481 && sip && !sip->sip_retry_after && sip->sip_call_id && (!sofia_private || !sofia_private->is_call)) { char *sql; - + sql = switch_mprintf("delete from sip_subscriptions where call_id='%q'", sip->sip_call_id->i_id); switch_assert(sql != NULL); sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); @@ -179,7 +179,7 @@ static void extract_header_vars(sofia_profile_t *profile, sip_t const *sip, if (sip->sip_route) { if ((full = sip_header_as_string(nh->nh_home, (void *) sip->sip_route))) { const char *v = switch_channel_get_variable(channel, "sip_full_route"); - if (!v) { + if (!v) { switch_channel_set_variable(channel, "sip_full_route", full); } su_free(nh->nh_home, full); @@ -204,13 +204,13 @@ static void extract_header_vars(sofia_profile_t *profile, sip_t const *sip, if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { char *tmp[128] = { 0 }; int y = 0; - + for(rrp = sip->sip_record_route; rrp; rrp = rrp->r_next) { char *rr = sip_header_as_string(nh->nh_home, (void *) rrp); tmp[y++] = rr; if (y == 127) break; } - + y--; while(y >= 0) { @@ -223,14 +223,14 @@ static void extract_header_vars(sofia_profile_t *profile, sip_t const *sip, } else { for(rrp = sip->sip_record_route; rrp; rrp = rrp->r_next) { char *rr = sip_header_as_string(nh->nh_home, (void *) rrp); - + stream.write_function(&stream, x == 0 ? "%s" : ",%s", rr); su_free(nh->nh_home, rr); - + x++; } } - + switch_channel_set_variable(channel, "sip_invite_record_route", (char *)stream.data); free(stream.data); } @@ -241,16 +241,16 @@ static void extract_header_vars(sofia_profile_t *profile, sip_t const *sip, int x = 0; SWITCH_STANDARD_STREAM(stream); - + for(vp = sip->sip_via; vp; vp = vp->v_next) { char *v = sip_header_as_string(nh->nh_home, (void *) vp); - + stream.write_function(&stream, x == 0 ? "%s" : ",%s", v); su_free(nh->nh_home, v); - + x++; } - + switch_channel_set_variable(channel, "sip_full_via", (char *)stream.data); if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND || switch_stristr("TCP", (char *)stream.data)) { @@ -258,9 +258,9 @@ static void extract_header_vars(sofia_profile_t *profile, sip_t const *sip, } free(stream.data); - + } - + if (sip->sip_from) { char *p = strip_quotes(sip->sip_from->a_display); @@ -451,7 +451,7 @@ void sofia_handle_sip_i_notify(switch_core_session_t *session, int status, sip->sip_event->o_type); } goto error; - } + } /* find the corresponding gateway subscription (if any) */ if (!(gw_sub_ptr = sofia_find_gateway_subscription(sofia_private->gateway, sip->sip_event->o_type))) { @@ -523,7 +523,7 @@ void sofia_handle_sip_i_notify(switch_core_session_t *session, int status, if (sip->sip_to && sip->sip_to->a_url && sip->sip_to->a_url->url_user && sip->sip_to->a_url->url_host && sip->sip_payload && sip->sip_payload->pl_data ) { - sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), NULL); + sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), NULL); for (x = 0; x < profile->acl_count; x++) { last_acl = profile->acl[x]; if (!(acl_ok = switch_check_network_list_ip(network_ip, last_acl))) { @@ -623,7 +623,7 @@ void sofia_handle_sip_i_bye(switch_core_session_t *session, int status, if (!zstr(other_uuid) && (other_session = switch_core_session_locate(other_uuid))) { switch_channel_t *other_channel = switch_core_session_get_channel(other_session); - + switch_mutex_lock(profile->ireg_mutex); if (switch_ivr_eavesdrop_pop_eavesdropper(session, &new_session) == SWITCH_STATUS_SUCCESS) { switch_channel_t *new_channel = switch_core_session_get_channel(new_session); @@ -637,18 +637,18 @@ void sofia_handle_sip_i_bye(switch_core_session_t *session, int status, switch_channel_set_variable(new_channel, SWITCH_SIGNAL_BOND_VARIABLE, NULL); switch_channel_set_flag(other_channel, CF_REDIRECT); - + switch_channel_set_state(new_channel, CS_RESET); - + switch_ivr_uuid_bridge(new_uuid, other_uuid); cmd = switch_core_session_sprintf(session, "sleep:500,sofia_sla:%s inline", new_uuid); - - switch_channel_clear_flag(other_channel, CF_REDIRECT); + + switch_channel_clear_flag(other_channel, CF_REDIRECT); switch_core_session_rwunlock(new_session); } switch_mutex_unlock(profile->ireg_mutex); - + switch_core_session_rwunlock(other_session); } @@ -700,16 +700,16 @@ void sofia_handle_sip_i_bye(switch_core_session_t *session, int status, extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_BYE_HEADER_PREFIX); sofia_glue_set_extra_headers(session, sip, SOFIA_SIP_BYE_HEADER_PREFIX); - if (!(vval = switch_channel_get_variable(channel, "sip_copy_custom_headers")) || switch_true(vval)) { - switch_core_session_t *nsession = NULL; - - switch_core_session_get_partner(session, &nsession); - - if (nsession) { - switch_ivr_transfer_variable(session, nsession, SOFIA_SIP_BYE_HEADER_PREFIX_T); - switch_core_session_rwunlock(nsession); - } - } + if (!(vval = switch_channel_get_variable(channel, "sip_copy_custom_headers")) || switch_true(vval)) { + switch_core_session_t *nsession = NULL; + + switch_core_session_get_partner(session, &nsession); + + if (nsession) { + switch_ivr_transfer_variable(session, nsession, SOFIA_SIP_BYE_HEADER_PREFIX_T); + switch_core_session_rwunlock(nsession); + } + } switch_channel_hangup(channel, cause); @@ -795,15 +795,15 @@ void sofia_send_callee_id(switch_core_session_t *session, const char *name, cons if (zstr(name)) { name = caller_profile->caller_id_name; } - + if (zstr(number)) { number = caller_profile->caller_id_number; } - + if (zstr(name)) { name = number; } - + if (zstr(number)) { name = number = "UNKNOWN"; } @@ -811,15 +811,15 @@ void sofia_send_callee_id(switch_core_session_t *session, const char *name, cons if (zstr(name)) { name = caller_profile->callee_id_name; } - + if (zstr(number)) { number = caller_profile->callee_id_number; } - + if (zstr(name)) { name = number; } - + if (zstr(number)) { number = caller_profile->destination_number; } @@ -872,7 +872,7 @@ void sofia_update_callee_id(switch_core_session_t *session, sofia_profile_t *pro number = (char *) switch_channel_get_variable(channel, num_var); name = (char *) switch_channel_get_variable(channel, name_var); - + if (zstr(number) && sip->sip_to) { number = sip->sip_to->a_url->url_user; } @@ -887,7 +887,7 @@ void sofia_update_callee_id(switch_core_session_t *session, sofia_profile_t *pro check_decode(name, session); fs++; } - + if ((val = sofia_glue_get_unknown_header(sip, "X-FS-Lazy-Attended-Transfer"))) { lazy = switch_true(val); fs++; @@ -916,7 +916,7 @@ void sofia_update_callee_id(switch_core_session_t *session, sofia_profile_t *pro } } } - + if (zstr(number)) { if ((tmp = switch_channel_get_variable(channel, num_var)) && !zstr(tmp)) { @@ -956,7 +956,7 @@ void sofia_update_callee_id(switch_core_session_t *session, sofia_profile_t *pro } } else { - + if (!strcmp(caller_profile->callee_id_name, name) && !strcmp(caller_profile->callee_id_number, number)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "%s Same Callee ID \"%s\" <%s>\n", switch_channel_get_name(channel), name, number); send = 0; @@ -964,7 +964,7 @@ void sofia_update_callee_id(switch_core_session_t *session, sofia_profile_t *pro caller_profile->callee_id_name = switch_sanitize_number(switch_core_strdup(caller_profile->pool, name)); caller_profile->callee_id_number = switch_sanitize_number(switch_core_strdup(caller_profile->pool, number)); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Update Callee ID to \"%s\" <%s>\n", switch_channel_get_name(channel), name, number); - + if (lazy || (att && !switch_channel_get_partner_uuid(channel))) { switch_channel_flip_cid(channel); } @@ -998,22 +998,22 @@ static void tech_send_ack(nua_handle_t *nh, private_object_t *tech_pvt) if (sofia_test_pflag(tech_pvt->profile, PFLAG_TRACK_CALLS)) { const char *invite_full_via = switch_channel_get_variable(tech_pvt->channel, "sip_invite_full_via"); - const char *invite_route_uri = switch_channel_get_variable(tech_pvt->channel, "sip_invite_route_uri"); - - nua_ack(nh, + const char *invite_route_uri = switch_channel_get_variable(tech_pvt->channel, "sip_invite_route_uri"); + + nua_ack(nh, TAG_IF(invite_full_from, SIPTAG_FROM_STR(invite_full_from)), TAG_IF(invite_full_to, SIPTAG_TO_STR(invite_full_to)), TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), TAG_IF((zstr(tech_pvt->user_via) && !zstr(invite_full_via)), SIPTAG_VIA_STR(invite_full_via)), TAG_IF(!zstr(invite_route_uri), SIPTAG_ROUTE_STR(invite_route_uri)), TAG_END()); - - + + } else { - nua_ack(nh, + nua_ack(nh, TAG_IF(invite_full_from, SIPTAG_FROM_STR(invite_full_from)), TAG_IF(invite_full_to, SIPTAG_TO_STR(invite_full_to)), - TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), + TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), TAG_END()); } @@ -1037,7 +1037,7 @@ static void our_sofia_event_callback(nua_event_t event, profile->last_sip_event = switch_time_now(); - /* sofia_private will be == &mod_sofia_globals.keep_private whenever a request is done with a new handle that has to be + /* sofia_private will be == &mod_sofia_globals.keep_private whenever a request is done with a new handle that has to be freed whenever the request is done */ if (nh && sofia_private == &mod_sofia_globals.keep_private) { if (status >= 300) { @@ -1046,7 +1046,7 @@ static void our_sofia_event_callback(nua_event_t event, return; } } - + if (sofia_private && sofia_private != &mod_sofia_globals.destroy_private && sofia_private != &mod_sofia_globals.keep_private) { if ((gateway = sofia_private->gateway)) { @@ -1093,7 +1093,7 @@ static void our_sofia_event_callback(nua_event_t event, } } } - + if (sofia_test_pflag(profile, PFLAG_AUTH_ALL) && tech_pvt && tech_pvt->key && sip && (event < nua_r_set_params || event > nua_r_authenticate)) { sip_authorization_t const *authorization = NULL; @@ -1151,7 +1151,7 @@ static void our_sofia_event_callback(nua_event_t event, if (sip->sip_reason) { char *reason_header = sip_header_as_string(nh->nh_home, (void *) sip->sip_reason); - + if (!zstr(reason_header)) { switch_channel_set_variable_partner(channel, "sip_reason", reason_header); } @@ -1193,7 +1193,7 @@ static void our_sofia_event_callback(nua_event_t event, if (sofia_test_flag(tech_pvt, TFLAG_PASS_ACK)) { switch_core_session_t *other_session; - + sofia_clear_flag(tech_pvt, TFLAG_PASS_ACK); @@ -1203,8 +1203,8 @@ static void our_sofia_event_callback(nua_event_t event, tech_send_ack(other_tech_pvt->nh, other_tech_pvt); } switch_core_session_rwunlock(other_session); - } - + } + } @@ -1299,19 +1299,18 @@ static void our_sofia_event_callback(nua_event_t event, } - if ((refer_to = sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_refer_to))) { - - if ((tmp = sofia_glue_get_url_from_contact(refer_to, 0))) { - refer_to = tmp; - } - - if ((iparams = strrchr(refer_to, ';'))) { + if ((refer_to = sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_refer_to))) { + if ((iparams = strchr(refer_to, ';'))) { *iparams++ = '\0'; - if (!switch_stristr("method=", iparams)) { + if (!params || !switch_stristr("method=", params)) { params = iparams; } } + + if ((tmp = sofia_glue_get_url_from_contact(refer_to, 0))) { + refer_to = tmp; + } } if (params) { @@ -1321,7 +1320,7 @@ static void our_sofia_event_callback(nua_event_t event, } - + if (!method) { method = strdup("INVITE"); } @@ -1338,11 +1337,11 @@ static void our_sofia_event_callback(nua_event_t event, referred_by = sofia_glue_get_url_from_contact(sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_referred_by), 0); ref_by_user = sip->sip_referred_by->b_url->url_user; } - else if(sip->sip_to && sip->sip_to->a_url) - { + else if(sip->sip_to && sip->sip_to->a_url) + { referred_by = sofia_glue_get_url_from_contact(sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_to), 0); - ref_by_user = sip->sip_to->a_url->url_user; - } + ref_by_user = sip->sip_to->a_url->url_user; + } if (sip->sip_to && sip->sip_to->a_url) { req_user = sip->sip_to->a_url->url_user; @@ -1365,7 +1364,7 @@ static void our_sofia_event_callback(nua_event_t event, if (sip->sip_call_id && sip->sip_call_id->i_id) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Request-Call-ID", sip->sip_call_id->i_id); } - + if (!zstr(referred_by)) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Request-Sender", "sofia/%s/%s", profile->name, referred_by); } @@ -1388,14 +1387,14 @@ static void our_sofia_event_callback(nua_event_t event, np.fs_path = 1; contact_str = sofia_glue_gen_contact_str(profile, sip, nh, de, &np); - + call_id = sip->sip_call_id->i_id; full_from = sip_header_as_string(nh->nh_home, (void *) sip->sip_from); full_to = sip_header_as_string(nh->nh_home, (void *) sip->sip_to); full_via = sip_header_as_string(nh->nh_home, (void *) sip->sip_via); full_agent = sip_header_as_string(nh->nh_home, (void *) sip->sip_user_agent); - + switch_stun_random_string(to_tag, 12, NULL); if (sip->sip_from) { @@ -1413,7 +1412,7 @@ static void our_sofia_event_callback(nua_event_t event, to_user = "n/a"; to_host = "n/a"; } - + sql = switch_mprintf("insert into sip_subscriptions " "(proto,sip_user,sip_host,sub_to_user,sub_to_host,presence_hosts,event,contact,call_id,full_from," "full_via,expires,user_agent,accept,profile_name,hostname,network_port,network_ip,version,orig_proto, full_to) " @@ -1421,23 +1420,23 @@ static void our_sofia_event_callback(nua_event_t event, proto, from_user, from_host, to_user, to_host, profile->presence_hosts ? profile->presence_hosts : "", event_str, contact_str, call_id, full_from, full_via, (long) switch_epoch_time_now(NULL) + 60, - full_agent, accept, profile->name, mod_sofia_globals.hostname, + full_agent, accept, profile->name, mod_sofia_globals.hostname, np.network_port, np.network_ip, orig_proto, full_to, to_tag); - + switch_assert(sql != NULL); - - + + if (mod_sofia_globals.debug_presence > 0 || mod_sofia_globals.debug_sla > 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "%s REFER SUBSCRIBE %s@%s %s@%s\n%s\n", profile->name, from_user, from_host, to_user, to_host, sql); } - - + + sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE); sip_to_tag(nh->nh_home, sip->sip_to, to_tag); } - + nua_respond(nh, SIP_202_ACCEPTED, SIPTAG_TO(sip->sip_to), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); switch_safe_free(method); switch_safe_free(full_url); @@ -1458,7 +1457,7 @@ static void our_sofia_event_callback(nua_event_t event, sql = switch_mprintf("delete from sip_registrations where call_id='%q' and network_ip='%q' and network_port='%q'", sofia_private->call_id, sofia_private->network_ip, sofia_private->network_port); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "SOCKET DISCONNECT: %s %s:%s\n", + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "SOCKET DISCONNECT: %s %s:%s\n", sofia_private->call_id, sofia_private->network_ip, sofia_private->network_port); sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); @@ -1478,7 +1477,7 @@ static void our_sofia_event_callback(nua_event_t event, nua_handle_destroy(nh); } } - + break; default: if (status > 100) { @@ -1573,7 +1572,7 @@ void *SWITCH_THREAD_FUNC sofia_msg_thread_run_once(switch_thread_t *thread, void switch_mutex_lock(mod_sofia_globals.mutex); DE_THREAD_CNT++; - switch_mutex_unlock(mod_sofia_globals.mutex); + switch_mutex_unlock(mod_sofia_globals.mutex); if (de) { pool = de->pool; @@ -1587,14 +1586,14 @@ void *SWITCH_THREAD_FUNC sofia_msg_thread_run_once(switch_thread_t *thread, void switch_mutex_lock(mod_sofia_globals.mutex); DE_THREAD_CNT--; - switch_mutex_unlock(mod_sofia_globals.mutex); + switch_mutex_unlock(mod_sofia_globals.mutex); return NULL; } void sofia_process_dispatch_event_in_thread(sofia_dispatch_event_t **dep) { - sofia_dispatch_event_t *de = *dep; + sofia_dispatch_event_t *de = *dep; switch_memory_pool_t *pool; //sofia_profile_t *profile = (*dep)->profile; switch_thread_data_t *td; @@ -1621,10 +1620,10 @@ void sofia_process_dispatch_event(sofia_dispatch_event_t **dep) sofia_private_t *sofia_private = nua_handle_magic(de->nh); *dep = NULL; - our_sofia_event_callback(de->data->e_event, de->data->e_status, de->data->e_phrase, de->nua, de->profile, + our_sofia_event_callback(de->data->e_event, de->data->e_status, de->data->e_phrase, de->nua, de->profile, de->nh, sofia_private, de->sip, de, (tagi_t *) de->data->e_tags); - nua_destroy_event(de->event); + nua_destroy_event(de->event); su_free(nh->nh_home, de); switch_mutex_lock(profile->flag_mutex); @@ -1652,10 +1651,10 @@ void *SWITCH_THREAD_FUNC sofia_msg_thread_run(switch_thread_t *thread, void *obj break; } } - - switch_mutex_lock(mod_sofia_globals.mutex); + + switch_mutex_lock(mod_sofia_globals.mutex); msg_queue_threads++; - switch_mutex_unlock(mod_sofia_globals.mutex); + switch_mutex_unlock(mod_sofia_globals.mutex); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MSG Thread %d Started\n", my_id); @@ -1677,23 +1676,23 @@ void *SWITCH_THREAD_FUNC sofia_msg_thread_run(switch_thread_t *thread, void *obj switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MSG Thread Ended\n"); - switch_mutex_lock(mod_sofia_globals.mutex); + switch_mutex_lock(mod_sofia_globals.mutex); msg_queue_threads--; - switch_mutex_unlock(mod_sofia_globals.mutex); + switch_mutex_unlock(mod_sofia_globals.mutex); - return NULL; + return NULL; } void sofia_msg_thread_start(int idx) { - if (idx >= mod_sofia_globals.max_msg_queues || + if (idx >= mod_sofia_globals.max_msg_queues || idx >= SOFIA_MAX_MSG_QUEUE || (idx < mod_sofia_globals.msg_queue_len && mod_sofia_globals.msg_queue_thread[idx])) { return; } switch_mutex_lock(mod_sofia_globals.mutex); - + if (idx >= mod_sofia_globals.msg_queue_len) { int i; mod_sofia_globals.msg_queue_len = idx + 1; @@ -1705,10 +1704,10 @@ void sofia_msg_thread_start(int idx) switch_threadattr_create(&thd_attr, mod_sofia_globals.pool); switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); //switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME); - switch_thread_create(&mod_sofia_globals.msg_queue_thread[i], - thd_attr, - sofia_msg_thread_run, - mod_sofia_globals.msg_queue, + switch_thread_create(&mod_sofia_globals.msg_queue_thread[i], + thd_attr, + sofia_msg_thread_run, + mod_sofia_globals.msg_queue, mod_sofia_globals.pool); } } @@ -1728,7 +1727,7 @@ void sofia_queue_message(sofia_dispatch_event_t *de) } - if (de->profile && sofia_test_pflag(de->profile, PFLAG_THREAD_PER_REG) && + if (de->profile && sofia_test_pflag(de->profile, PFLAG_THREAD_PER_REG) && de->data->e_event == nua_i_register && DE_THREAD_CNT < mod_sofia_globals.max_reg_threads) { sofia_process_dispatch_event_in_thread(&de); return; @@ -1771,13 +1770,13 @@ void sofia_event_callback(nua_event_t event, switch(event) { case nua_i_terminated: - if ((status == 401 || status == 407 || status == 403) && sofia_private && sofia_private->uuid) { + if ((status == 401 || status == 407 || status == 403) && sofia_private && sofia_private->uuid) { switch_core_session_t *session; if ((session = switch_core_session_locate(sofia_private->uuid))) { switch_channel_t *channel = switch_core_session_get_channel(session); int end = 0; - + if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND && !switch_channel_test_flag(channel, CF_ANSWERED)) { private_object_t *tech_pvt = switch_core_session_get_private(session); @@ -1786,7 +1785,7 @@ void sofia_event_callback(nua_event_t event, switch_channel_hangup(channel, SWITCH_CAUSE_CALL_REJECTED); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "detaching session %s\n", sofia_private->uuid); - + if (!zstr(tech_pvt->call_id)) { tech_pvt->sofia_private = NULL; tech_pvt->nh = NULL; @@ -1814,18 +1813,18 @@ void sofia_event_callback(nua_event_t event, case nua_i_options: case nua_i_notify: case nua_i_info: - + if (sess_count >= sess_max || !sofia_test_pflag(profile, PFLAG_RUNNING) || !switch_core_ready_inbound()) { nua_respond(nh, 503, "Maximum Calls In Progress", SIPTAG_RETRY_AFTER_STR("300"), NUTAG_WITH_THIS(nua), TAG_END()); goto end; } - + if (switch_queue_size(mod_sofia_globals.msg_queue) > critical) { nua_respond(nh, 503, "System Busy", SIPTAG_RETRY_AFTER_STR("300"), NUTAG_WITH_THIS(nua), TAG_END()); goto end; } - + if (sofia_test_pflag(profile, PFLAG_STANDBY)) { nua_respond(nh, 503, "System Paused", NUTAG_WITH_THIS(nua), TAG_END()); goto end; @@ -1834,7 +1833,7 @@ void sofia_event_callback(nua_event_t event, default: break; - + } switch_mutex_lock(profile->flag_mutex); @@ -1895,19 +1894,19 @@ void sofia_event_callback(nua_event_t event, } } } - + if (!sip || !sip->sip_call_id || zstr(sip->sip_call_id->i_id)) { nua_respond(nh, 503, "INVALID INVITE", TAG_END()); - nua_destroy_event(de->event); + nua_destroy_event(de->event); su_free(nh->nh_home, de); - + switch_mutex_lock(profile->flag_mutex); profile->queued_events--; switch_mutex_unlock(profile->flag_mutex); - + nua_handle_unref(nh); nua_stack_unref(nua); - + goto end; } @@ -1930,26 +1929,26 @@ void sofia_event_callback(nua_event_t event, if (sip->sip_referred_by) { channel_name = url_set_chanvars(session, sip->sip_referred_by->b_url, sip_referred_by); } - + sofia_glue_attach_private(session, profile, tech_pvt, channel_name); set_call_id(tech_pvt, sip); } else { nua_respond(nh, 503, "Maximum Calls In Progress", SIPTAG_RETRY_AFTER_STR("300"), TAG_END()); - nua_destroy_event(de->event); + nua_destroy_event(de->event); su_free(nh->nh_home, de); - + switch_mutex_lock(profile->flag_mutex); profile->queued_events--; switch_mutex_unlock(profile->flag_mutex); - + nua_handle_unref(nh); nua_stack_unref(nua); goto end; } - + if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) { char *uuid; @@ -1976,7 +1975,7 @@ void sofia_event_callback(nua_event_t event, switch_core_session_queue_signal_data(session, de); goto end; } - + if (sofia_private && sofia_private != &mod_sofia_globals.destroy_private && sofia_private != &mod_sofia_globals.keep_private) { switch_core_session_t *session; @@ -1986,7 +1985,7 @@ void sofia_event_callback(nua_event_t event, goto end; } } - + sofia_queue_message(de); end: @@ -2038,7 +2037,7 @@ void event_handler(switch_event_t *event) /* we cannot use switch_event_fire, or otherwise we'll start an endless loop */ sofia_presence_event_handler(pevent); return; - } else { + } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "\nCannot inject PRESENCE_IN event\n"); return; } @@ -2076,7 +2075,7 @@ void event_handler(switch_event_t *event) } sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Expired propagated registration for %s@%s->%s\n", from_user, from_host, contact_str); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Expired propagated registration for %s@%s->%s\n", from_user, from_host, contact_str); if (profile) { sofia_glue_release_profile(profile); @@ -2182,7 +2181,7 @@ void event_handler(switch_event_t *event) switch_find_local_ip(guess_ip4, sizeof(guess_ip4), NULL, AF_INET); sql = switch_mprintf("insert into sip_registrations " "(call_id, sip_user, sip_host, presence_hosts, contact, status, rpid, expires," - "user_agent, server_user, server_host, profile_name, hostname, network_ip, network_port, sip_username, sip_realm," + "user_agent, server_user, server_host, profile_name, hostname, network_ip, network_port, sip_username, sip_realm," "mwi_user, mwi_host, orig_server_host, orig_hostname) " "values ('%q','%q','%q','%q','%q','Registered','%q',%ld, '%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q')", call_id, from_user, from_host, presence_hosts, contact_str, rpid, expires, user_agent, to_user, guess_ip4, @@ -2255,30 +2254,30 @@ void *SWITCH_THREAD_FUNC sofia_profile_worker_thread_run(switch_thread_t *thread sofia_set_pflag_locked(profile, PFLAG_WORKER_RUNNING); while ((mod_sofia_globals.running == 1 && sofia_test_pflag(profile, PFLAG_RUNNING))) { - + if (profile->watchdog_enabled) { uint32_t event_diff = 0, step_diff = 0, event_fail = 0, step_fail = 0; - + if (profile->step_timeout) { step_diff = (uint32_t) ((switch_time_now() - profile->last_root_step) / 1000); - + if (step_diff > profile->step_timeout) { step_fail = 1; } } - + if (profile->event_timeout) { event_diff = (uint32_t) ((switch_time_now() - profile->last_sip_event) / 1000); - + if (event_diff > profile->event_timeout) { event_fail = 1; } } - + if (step_fail && profile->event_timeout && !event_fail) { step_fail = 0; } - + if (event_fail || step_fail) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile %s: SIP STACK FAILURE DETECTED BY WATCHDOG!\n" "GOODBYE CRUEL WORLD, I'M LEAVING YOU TODAY....GOODBYE, GOODBYE, GOOD BYE\n", profile->name); @@ -2294,17 +2293,17 @@ void *SWITCH_THREAD_FUNC sofia_profile_worker_thread_run(switch_thread_t *thread sofia_reg_check_expire(profile, now, 0); ireg_loops = 0; } - + if (++gateway_loops >= GATEWAY_SECONDS) { sofia_reg_check_gateway(profile, switch_epoch_time_now(NULL)); gateway_loops = 0; } - + sofia_sub_check_gateway(profile, time(NULL)); } switch_yield(1000000); - + } sofia_clear_pflag_locked(profile, PFLAG_WORKER_RUNNING); @@ -2327,7 +2326,7 @@ switch_thread_t *launch_sofia_worker_thread(sofia_profile_t *profile) switch_event_create(¶ms, SWITCH_EVENT_REQUEST_PARAMS); switch_assert(params); switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "profile", profile->name); - + if (!(xml = switch_xml_open_cfg(cf, &cfg, params))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf); goto end; @@ -2352,7 +2351,7 @@ switch_thread_t *launch_sofia_worker_thread(sofia_profile_t *profile) const char *dname = switch_xml_attr_soft(domain_tag, "name"); const char *parse = switch_xml_attr_soft(domain_tag, "parse"); const char *alias = switch_xml_attr_soft(domain_tag, "alias"); - + if (!zstr(dname)) { if (!strcasecmp(dname, "all")) { switch_xml_t xml_root, x_domains; @@ -2370,7 +2369,7 @@ switch_thread_t *launch_sofia_worker_thread(sofia_profile_t *profile) } } } - + switch_event_destroy(&xml_params); } @@ -2448,7 +2447,7 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void /* We have to init the verify_subjects here as during config stage profile->home isn't setup, it should be freed when profile->home is freed */ if ( (profile->tls_verify_policy & TPTLS_VERIFY_SUBJECTS_IN) && profile->tls_verify_in_subjects_str && ! profile->tls_verify_in_subjects) { profile->tls_verify_in_subjects = su_strlst_dup_split((su_home_t *)profile->nua, profile->tls_verify_in_subjects_str, "|"); - } + } profile->nua = nua_create(profile->s_root, /* Event loop */ sofia_event_callback, /* Callback for processing events */ @@ -2494,9 +2493,9 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void TAG_IF(sofia_test_pflag(profile, PFLAG_TCP_PING2PONG), TPTAG_PINGPONG(profile->tcp_ping2pong)), TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_SRV503), - NTATAG_SRV_503(0)), + NTATAG_SRV_503(0)), TAG_IF(sofia_test_pflag(profile, PFLAG_TCP_KEEPALIVE), - TPTAG_KEEPALIVE(profile->tcp_keepalive)), + TPTAG_KEEPALIVE(profile->tcp_keepalive)), NTATAG_DEFAULT_PROXY(profile->outbound_proxy), NTATAG_SERVER_RPORT(profile->server_rport_level), NTATAG_CLIENT_RPORT(profile->client_rport_level), @@ -2512,7 +2511,7 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void TAG_IF(sofia_test_pflag(profile, PFLAG_NO_CONNECTION_REUSE), TPTAG_REUSE(0)), TAG_END()); /* Last tag should always finish the sequence */ - + if (!profile->nua) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Creating SIP UA for profile: %s (%s)\n" "The likely causes for this are:\n" "1) Another application is already listening on the specified address.\n" @@ -2523,7 +2522,7 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created agent for %s\n", profile->name); - + nua_set_params(profile->nua, SIPTAG_ALLOW_STR("INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO"), NUTAG_AUTOANSWER(0), @@ -2555,6 +2554,7 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void TAG_IF(profile->pres_type, NUTAG_ALLOW("SUBSCRIBE")), TAG_IF(profile->pres_type, NUTAG_ENABLEMESSAGE(1)), TAG_IF(profile->pres_type, NUTAG_ALLOW_EVENTS("presence")), + TAG_IF(profile->pres_type, NUTAG_ALLOW_EVENTS("as-feature-event")), TAG_IF((profile->pres_type || sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE)), NUTAG_ALLOW_EVENTS("dialog")), TAG_IF((profile->pres_type || sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE)), NUTAG_ALLOW_EVENTS("line-seize")), TAG_IF(profile->pres_type, NUTAG_ALLOW_EVENTS("call-info")), @@ -2584,7 +2584,7 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void } } - + config_sofia_profile_urls(profile); } @@ -2688,7 +2688,7 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void break; } } - + sofia_clear_pflag_locked(profile, PFLAG_RUNNING); sofia_clear_pflag_locked(profile, PFLAG_SHUTDOWN); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Waiting for worker thread\n"); @@ -2757,14 +2757,14 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void switch_core_hash_destroy(&profile->chat_hash); switch_core_hash_destroy(&profile->reg_nh_hash); switch_core_hash_destroy(&profile->mwi_debounce_hash); - + switch_thread_rwlock_unlock(profile->rwlock); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write unlock %s\n", profile->name); if (sofia_test_pflag(profile, PFLAG_RESPAWN)) { config_sofia(SOFIA_CONFIG_RESPAWN, profile->name); } - + sofia_profile_destroy(profile); end: @@ -2775,7 +2775,7 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void return NULL; } -void sofia_profile_destroy(sofia_profile_t *profile) +void sofia_profile_destroy(sofia_profile_t *profile) { if (!profile->inuse) { switch_memory_pool_t *pool = profile->pool; @@ -3131,7 +3131,7 @@ static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag) su_guid_generate(guid); su_guid_sprintf(str_guid, su_guid_strlen + 1, guid); str_rfc_5626 = switch_core_sprintf(gateway->pool, ";reg-id=%s;+sip.instance=\"\"",reg_id,str_guid); - } + } if (zstr(realm)) { if (zstr(proxy)) { @@ -3195,7 +3195,7 @@ static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag) } gateway->retry_seconds = atoi(retry_seconds); - + if (gateway->retry_seconds < 5) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid retry-seconds of %d on gateway %s, using the value of 30 instead.\n", gateway->retry_seconds, name); @@ -3239,7 +3239,7 @@ static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag) if (!zstr(from_domain)) { gateway->from_domain = switch_core_strdup(gateway->pool, from_domain); } - + if (!zstr(register_transport) && !switch_stristr("transport=", proxy)) { gateway->register_url = switch_core_sprintf(gateway->pool, "sip:%s;transport=%s", proxy, register_transport); } else { @@ -3348,9 +3348,9 @@ static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag) if ((gw_subs_tag = switch_xml_child(gateway_tag, "subscriptions"))) { parse_gateway_subscriptions(profile, gateway, gw_subs_tag); } - + sofia_reg_add_gateway(profile, gateway->name, gateway); - + } skip: @@ -3415,13 +3415,13 @@ static void config_sofia_profile_urls(sofia_profile_t * profile) profile->contact_user, ipv6 ? "[" : "", profile->sipip, ipv6 ? "]" : "", profile->sip_port); profile->bindurl = profile->url; } - + profile->tcp_contact = switch_core_sprintf(profile->pool, "<%s;transport=tcp>", profile->url); - + if (profile->public_url) { profile->tcp_public_contact = switch_core_sprintf(profile->pool, "<%s;transport=tcp>", profile->public_url); } - + if (profile->bind_params) { char *bindurl = profile->bindurl; if (!switch_stristr("transport=", profile->bind_params)) { @@ -3430,7 +3430,7 @@ static void config_sofia_profile_urls(sofia_profile_t * profile) profile->bindurl = switch_core_sprintf(profile->pool, "%s;%s", bindurl, profile->bind_params); } else { char *bindurl = profile->bindurl; - profile->bindurl = switch_core_sprintf(profile->pool, "%s;transport=udp,tcp", bindurl); + profile->bindurl = switch_core_sprintf(profile->pool, "%s;transport=udp,tcp", bindurl); } @@ -3461,7 +3461,7 @@ static void config_sofia_profile_urls(sofia_profile_t * profile) if (!profile->tls_sip_port && !sofia_test_pflag(profile, PFLAG_AUTO_ASSIGN_TLS_PORT)) { profile->tls_sip_port = (switch_port_t) atoi(SOFIA_DEFAULT_TLS_PORT); } - + if (profile->extsipip) { char *ipv6 = strchr(profile->extsipip, ':'); profile->tls_public_url = switch_core_sprintf(profile->pool, @@ -3469,7 +3469,7 @@ static void config_sofia_profile_urls(sofia_profile_t * profile) profile->contact_user, ipv6 ? "[" : "", profile->extsipip, ipv6 ? "]" : "", profile->tls_sip_port); } - + if (profile->extsipip && !sofia_test_pflag(profile, PFLAG_AUTO_NAT)) { char *ipv6 = strchr(profile->extsipip, ':'); profile->tls_url = @@ -3492,7 +3492,7 @@ static void config_sofia_profile_urls(sofia_profile_t * profile) "sips:%s@%s%s%s:%d", profile->contact_user, ipv6 ? "[" : "", profile->sipip, ipv6 ? "]" : "", profile->tls_sip_port); } - + if (profile->tls_bind_params) { char *tls_bindurl = profile->tls_bindurl; profile->tls_bindurl = switch_core_sprintf(profile->pool, "%s;%s", tls_bindurl, profile->tls_bind_params); @@ -3502,8 +3502,8 @@ static void config_sofia_profile_urls(sofia_profile_t * profile) if (profile->tls_public_url) { profile->tls_public_contact = switch_core_sprintf(profile->pool, "<%s;transport=tls>", profile->tls_public_url); } - - + + } } @@ -3518,7 +3518,7 @@ static void sofia_time(su_time_t *tv) tv->tv_sec = ((uint32_t) (now / 1000000)) + 2208988800UL; tv->tv_usec = (uint32_t) (now % 1000000); } - + } #endif @@ -3548,7 +3548,7 @@ switch_status_t sofia_init(void) #ifdef HAVE_SOFIA_STUN su_log_redirect(stun_log, logger, NULL); #endif - + return SWITCH_STATUS_SUCCESS; } @@ -3610,23 +3610,23 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) if (x > 0) { mod_sofia_globals.max_reg_threads = x; } - + } else if (!strcasecmp(var, "auto-restart")) { mod_sofia_globals.auto_restart = switch_true(val); } else if (!strcasecmp(var, "reg-deny-binding-fetch-and-no-lookup")) { /* backwards compatibility */ mod_sofia_globals.reg_deny_binding_fetch_and_no_lookup = switch_true(val); /* remove when noone complains about the extra lookup */ if (switch_true(val)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Enabling reg-deny-binding-fetch-and-no-lookup - this functionality is " - "deprecated and will be removed - let FS devs know if you think it should stay\n"); + "deprecated and will be removed - let FS devs know if you think it should stay\n"); } } else if (!strcasecmp(var, "rewrite-multicasted-fs-path")) { if( (!strcasecmp(val, "to_host")) || (!strcasecmp(val, "1")) ) { /* old behaviour */ - mod_sofia_globals.rewrite_multicasted_fs_path = 1; + mod_sofia_globals.rewrite_multicasted_fs_path = 1; } else if (!strcasecmp(val, "original_server_host")) { - mod_sofia_globals.rewrite_multicasted_fs_path = 2; + mod_sofia_globals.rewrite_multicasted_fs_path = 2; } else if (!strcasecmp(val, "original_hostname")) { - mod_sofia_globals.rewrite_multicasted_fs_path = 3; + mod_sofia_globals.rewrite_multicasted_fs_path = 3; } else { mod_sofia_globals.rewrite_multicasted_fs_path = SWITCH_FALSE; } @@ -3657,7 +3657,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) profile_found = 1; } } - + if (!profile_already_started) { /* Setup the pool */ @@ -3666,13 +3666,13 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) sofia_profile_start_failure(NULL, xprofilename); goto done; } - + if (!(profile = (sofia_profile_t *) switch_core_alloc(pool, sizeof(*profile)))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); sofia_profile_start_failure(NULL, xprofilename); goto done; } - + profile->tls_verify_policy = TPTLS_VERIFY_NONE; /* lib default */ profile->tls_verify_depth = 2; @@ -3720,7 +3720,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) sofia_set_pflag(profile, PFLAG_PASS_CALLEE_ID); sofia_set_pflag(profile, PFLAG_SEND_DISPLAY_UPDATE); sofia_set_pflag(profile, PFLAG_MESSAGE_QUERY_ON_FIRST_REGISTER); - //sofia_set_pflag(profile, PFLAG_PRESENCE_ON_FIRST_REGISTER); + //sofia_set_pflag(profile, PFLAG_PRESENCE_ON_FIRST_REGISTER); profile->shutdown_type = "false"; profile->local_network = "localnet.auto"; @@ -3770,7 +3770,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) sofia_set_flag(profile, TFLAG_TPORT_LOG); } else if (!strcasecmp(var, "sip-capture") && switch_true(val)) { sofia_set_flag(profile, TFLAG_CAPTURE); - nua_set_params(profile->nua, TPTAG_CAPT(mod_sofia_globals.capture_server), TAG_END()); + nua_set_params(profile->nua, TPTAG_CAPT(mod_sofia_globals.capture_server), TAG_END()); } else if (!strcasecmp(var, "tcp-keepalive") && !zstr(val)) { profile->tcp_keepalive = atoi(val); sofia_set_pflag(profile, PFLAG_TCP_KEEPALIVE); @@ -3924,7 +3924,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) } else { sofia_clear_pflag(profile, PFLAG_CID_IN_1XX); } - + } else if (!strcasecmp(var, "disable-hold")) { if (switch_true(val)) { sofia_set_media_flag(profile, SCMF_DISABLE_HOLD); @@ -4104,9 +4104,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; } @@ -4117,9 +4130,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; } @@ -4243,7 +4269,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) sofia_set_pflag(profile, PFLAG_MULTIREG); } else if (!strcasecmp(val, "sylantro")) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Sylantro support has been removed.\n" "It was incomplete anyway, and we fully support the broadsoft SCA shared line spec."); } @@ -4258,7 +4284,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) } else if (!strcasecmp(var, "disable-srv503")) { if (switch_true(val)) { sofia_set_pflag(profile, PFLAG_DISABLE_SRV503); - } + } } else if (!strcasecmp(var, "unregister-on-options-fail")) { if (switch_true(val)) { sofia_set_pflag(profile, PFLAG_UNREG_OPTIONS_FAIL); @@ -4343,7 +4369,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) sofia_clear_pflag(profile, PFLAG_NAT_OPTIONS_PING); sofia_clear_pflag(profile, PFLAG_UDP_NAT_OPTIONS_PING); } - } else if (!strcasecmp(var, "all-reg-options-ping")) { + } else if (!strcasecmp(var, "all-reg-options-ping")) { if (switch_true(val)) { sofia_set_pflag(profile, PFLAG_ALL_REG_OPTIONS_PING); } else { @@ -4645,12 +4671,12 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) } else { profile->timer_t4 = 4000; } - } else if (!strcasecmp(var, "sip-options-respond-503-on-busy")) { - if (switch_true(val)) { - sofia_set_pflag(profile, PFLAG_OPTIONS_RESPOND_503_ON_BUSY); - } else { - sofia_clear_pflag(profile, PFLAG_OPTIONS_RESPOND_503_ON_BUSY); - } + } else if (!strcasecmp(var, "sip-options-respond-503-on-busy")) { + if (switch_true(val)) { + sofia_set_pflag(profile, PFLAG_OPTIONS_RESPOND_503_ON_BUSY); + } else { + sofia_clear_pflag(profile, PFLAG_OPTIONS_RESPOND_503_ON_BUSY); + } } else if (!strcasecmp(var, "sip-force-expires")) { int32_t sip_force_expires = atoi(val); if (sip_force_expires >= 0) { @@ -4710,7 +4736,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) if (!profile->rtpip[0]) { profile->rtpip[profile->rtpip_index++] = switch_core_strdup(profile->pool, mod_sofia_globals.guess_ip); } - + if (switch_nat_get_type()) { char *ip = switch_core_get_variable_dup("nat_public_addr"); if (ip && !strchr(profile->sipip, ':')) { @@ -4770,7 +4796,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) profile->extsipip = switch_core_strdup(profile->pool, mod_sofia_globals.guess_ip); } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "we're configured to provision to [%s] on profile [%s]\n", + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "we're configured to provision to [%s] on profile [%s]\n", profile->pnp_prov_url, profile->pnp_notify_profile); } @@ -4866,7 +4892,7 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) } } } - + } else { switch_xml_t aliases_tag, alias_tag; @@ -5032,7 +5058,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status switch_channel_clear_flag(channel, CF_REQ_MEDIA); if (status >= 900) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s status %d received.\n", + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s status %d received.\n", switch_channel_get_name(channel), status); return; } @@ -5051,9 +5077,9 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status if (status >= 400 && sip->sip_reason && sip->sip_reason->re_protocol && (!strcasecmp(sip->sip_reason->re_protocol, "Q.850") || !strcasecmp(sip->sip_reason->re_protocol, "FreeSWITCH") || !strcasecmp(sip->sip_reason->re_protocol, profile->sdp_username)) && sip->sip_reason->re_cause) { - tech_pvt->q850_cause = atoi(sip->sip_reason->re_cause); + tech_pvt->q850_cause = atoi(sip->sip_reason->re_cause); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote Reason: %d\n", tech_pvt->q850_cause); - } + } sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port); @@ -5124,25 +5150,25 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status if (mod_sofia_globals.debug_sla > 1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "QUERY SQL %s\n", sql); } - + sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Auto-Fixing Broken SLA [;%s]\n", + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Auto-Fixing Broken SLA [;%s]\n", sip->sip_from->a_url->url_host, buf); switch_channel_set_variable_printf(channel, "presence_call_info_full", ";%s", sip->sip_from->a_url->url_host, buf); switch_channel_set_variable(channel, "presence_call_info", buf); } - } + } } } #if 0 - if (status == 200 && switch_channel_test_flag(channel, CF_PROXY_MEDIA) && + if (status == 200 && switch_channel_test_flag(channel, CF_PROXY_MEDIA) && sip->sip_payload && sip->sip_payload->pl_data && !strcasecmp(tech_pvt->iananame, "PROXY")) { switch_core_session_t *other_session; - + switch_core_media_proxy_codec(session, sip->sip_payload->pl_data); - + if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) { if (switch_core_session_compare(session, other_session)) { switch_core_media_proxy_codec(other_session, sip->sip_payload->pl_data); @@ -5166,7 +5192,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status if (!(vval = switch_channel_get_variable(channel, "sip_copy_custom_headers")) || switch_true(vval)) { switch_core_session_t *other_session; - + if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) { if (status > 199) { switch_ivr_transfer_variable(session, other_session, SOFIA_SIP_RESPONSE_HEADER_PREFIX_T); @@ -5192,7 +5218,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status } else if (sip->sip_server && sip->sip_server->g_string) { switch_channel_set_variable(channel, "sip_user_agent", sip->sip_server->g_string); } - + sofia_update_callee_id(session, profile, sip, SWITCH_FALSE); if (sofia_test_media_flag(tech_pvt->profile, SCMF_AUTOFIX_TIMING) || sofia_test_media_flag(tech_pvt->profile, SCMF_AUTOFIX_PT)) { @@ -5210,7 +5236,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status char *invite_contact; const char *br; const char *v; - + if ((v = switch_channel_get_variable(channel, "outbound_redirect_fatal")) && switch_true(v)) { switch_channel_hangup(channel, SWITCH_CAUSE_REQUESTED_CHAN_UNAVAIL); goto end; @@ -5292,8 +5318,8 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status } redirect_dialstring = stream.data; - - switch_channel_set_variable_printf(channel, "sip_redirect_count", "%d", i); + + switch_channel_set_variable_printf(channel, "sip_redirect_count", "%d", i); switch_channel_set_variable(channel, "sip_redirect_dialstring", redirect_dialstring); switch_channel_set_variable(a_channel, "sip_redirect_dialstring", redirect_dialstring); @@ -5367,7 +5393,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status if (sip->sip_payload && sip->sip_payload->pl_data && switch_stristr("m=image", sip->sip_payload->pl_data)) { has_t38 = 1; } - + if (switch_channel_test_flag(channel, CF_PROXY_MODE)) { switch_channel_clear_flag(tech_pvt->channel, CF_T38_PASSTHRU); has_t38 = 0; @@ -5376,7 +5402,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status if (switch_channel_test_flag(channel, CF_PROXY_MEDIA) && has_t38) { if (switch_core_media_ready(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO)) { switch_core_media_udptl_mode(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO); - + if ((uuid = switch_channel_get_partner_uuid(channel)) && (other_session = switch_core_session_locate(uuid))) { if (switch_core_session_compare(session, other_session)) { private_object_t *other_tech_pvt = switch_core_session_get_private(other_session); @@ -5387,12 +5413,12 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status switch_core_session_rwunlock(other_session); } } - + has_t38 = 0; } - if (status > 199 && (switch_channel_test_flag(channel, CF_PROXY_MODE) || - switch_channel_test_flag(channel, CF_PROXY_MEDIA) || + if (status > 199 && (switch_channel_test_flag(channel, CF_PROXY_MODE) || + switch_channel_test_flag(channel, CF_PROXY_MEDIA) || (switch_channel_test_flag(tech_pvt->channel, CF_T38_PASSTHRU) && (has_t38 || status > 299)))) { if (sofia_test_flag(tech_pvt, TFLAG_SENT_UPDATE)) { @@ -5410,7 +5436,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status r_sdp = tech_pvt->mparams.remote_sdp_str; switch_core_media_proxy_remote_addr(session, NULL); } - + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Passing %d %s to other leg\n", status, phrase); if (status == 491 && (switch_channel_test_flag(tech_pvt->channel, CF_T38_PASSTHRU) || @@ -5428,7 +5454,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status switch_channel_set_app_flag_key("T38", tech_pvt->channel, CF_APP_T38_FAIL); } else if (status == 200 && switch_channel_test_flag(tech_pvt->channel, CF_T38_PASSTHRU) && has_t38 && sip->sip_payload && sip->sip_payload->pl_data) { switch_t38_options_t *t38_options = switch_core_media_extract_t38_options(session, sip->sip_payload->pl_data); - + if (!t38_options) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Could not parse T.38 options from sdp.\n"); switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "T.38 NEGOTIATION ERROR"); @@ -5453,9 +5479,9 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status msg->pointer_arg = switch_core_session_strdup(other_session, r_sdp); msg->pointer_arg_size = strlen(r_sdp); } - + if (status == 200 && switch_channel_test_flag(tech_pvt->channel, CF_T38_PASSTHRU) && has_t38) { - if (switch_core_media_ready(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO) && + if (switch_core_media_ready(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO) && switch_core_media_ready(other_tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO)) { switch_channel_clear_flag(tech_pvt->channel, CF_NOTIMER_DURING_BRIDGE); switch_core_media_udptl_mode(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO); @@ -5528,7 +5554,7 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status char *full_contact = NULL; char *p = NULL; time_t now; - + if (sip->sip_contact) { full_contact = sip_header_as_string(nua_handle_home(tech_pvt->nh), (void *) sip->sip_contact); } @@ -5536,9 +5562,9 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status if (call_info && (p = strchr(call_info, ';'))) { p++; } - + now = switch_epoch_time_now(NULL); - + sql = switch_mprintf("insert into sip_dialogs " "(call_id,uuid,sip_to_user,sip_to_host,sip_to_tag,sip_from_user,sip_from_host,sip_from_tag,contact_user," "contact_host,state,direction,user_agent,profile_name,hostname,contact,presence_id,presence_data," @@ -5609,7 +5635,7 @@ void *SWITCH_THREAD_FUNC media_on_hold_thread_run(switch_thread_t *thread, void switch_yield(250000); switch_channel_wait_for_flag(channel, CF_MEDIA_ACK, SWITCH_TRUE, 10000, NULL); switch_channel_wait_for_flag(other_channel, CF_MEDIA_ACK, SWITCH_TRUE, 10000, NULL); - + switch_ivr_media(switch_core_session_get_uuid(other_session), SMF_REBRIDGE); if (switch_core_media_ready(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO)) { @@ -5647,7 +5673,7 @@ static void mark_transfer_record(switch_core_session_t *session, const char *br_ const char *uvar1, *dvar1, *uvar2, *dvar2; channel = switch_core_session_get_channel(session); - + if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) { uvar1 = "sip_from_user"; dvar1 = "sip_from_host"; @@ -5656,7 +5682,7 @@ static void mark_transfer_record(switch_core_session_t *session, const char *br_ dvar1 = "sip_to_host"; } - + if ((br_b_session = switch_core_session_locate(br_b)) ) { switch_channel_t *br_b_channel = switch_core_session_get_channel(br_b_session); switch_caller_profile_t *cp = switch_channel_get_caller_profile(br_b_channel); @@ -5706,14 +5732,14 @@ static void mark_transfer_record(switch_core_session_t *session, const char *br_ switch_channel_get_variable(channel, dvar1), switch_channel_get_variable(br_a_channel, uvar2), switch_channel_get_variable(br_a_channel, dvar2)); - + switch_channel_add_variable_var_check(br_a_channel, SWITCH_TRANSFER_HISTORY_VARIABLE, cp->transfer_source, SWITCH_FALSE, SWITCH_STACK_PUSH); switch_channel_set_variable(br_a_channel, SWITCH_TRANSFER_SOURCE_VARIABLE, cp->transfer_source); switch_core_session_rwunlock(br_a_session); } - - + + } @@ -5821,10 +5847,10 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, if ((sofia_test_flag(tech_pvt, TFLAG_LATE_NEGOTIATION) || switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND)) { switch_core_media_set_sdp_codec_string(session, r_sdp); } - + sofia_glue_pass_sdp(tech_pvt, (char *) r_sdp); sofia_set_flag(tech_pvt, TFLAG_NEW_SDP); - + } } } @@ -5861,7 +5887,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, if (channel && (status == 180 || status == 183) && switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { const char *val; if ((val = switch_channel_get_variable(channel, "sip_auto_answer")) && switch_true(val)) { - nua_notify(nh, NUTAG_NEWSUB(1), NUTAG_WITH_THIS_MSG(de->data->e_msg), + nua_notify(nh, NUTAG_NEWSUB(1), NUTAG_WITH_THIS_MSG(de->data->e_msg), NUTAG_SUBSTATE(nua_substate_terminated),SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"), SIPTAG_EVENT_STR("talk"), TAG_END()); } } @@ -5947,10 +5973,10 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, if (!switch_channel_test_flag(channel, CF_ANSWERED)) { const char *wait_for_ack = switch_channel_get_variable(channel, "sip_wait_for_aleg_ack"); - + if (switch_true(wait_for_ack)) { switch_core_session_t *other_session; - + if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) { if (switch_core_session_compare(session, other_session)) { private_object_t *other_tech_pvt = switch_core_session_get_private(other_session); @@ -5984,7 +6010,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, ss_state = nua_callstate_ready; goto state_process; } - + } goto done; case nua_callstate_received: @@ -5999,7 +6025,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, an earlier call to nua_invite silently failed. So we reject the incoming invite with a 491 and redo the failed outgoing invite. */ - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Other leg already handling a reinvite, so responding with 491\n"); nua_respond(tech_pvt->nh, SIP_491_REQUEST_PENDING, TAG_END()); @@ -6068,10 +6094,10 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, const char *br_b = switch_channel_get_partner_uuid(channel); char *br_a = b_private->uuid; - + if (br_b) { - switch_core_session_t *tmp; - + switch_core_session_t *tmp; + if (switch_true(switch_channel_get_variable(channel, "recording_follow_transfer")) && (tmp = switch_core_session_locate(br_a))) { switch_core_media_bug_transfer_recordings(session, tmp); @@ -6196,7 +6222,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, if (switch_stristr("m=image", r_sdp)) { is_t38 = 1; } - + if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) { switch_core_session_message_t *msg; @@ -6223,7 +6249,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, } } } - + if (!switch_core_media_ready(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO)) { switch_core_media_prepare_codecs(tech_pvt->session, SWITCH_FALSE); @@ -6267,7 +6293,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, } } - other_tech_pvt = switch_core_session_get_private(other_session); + other_tech_pvt = switch_core_session_get_private(other_session); if(sofia_test_flag(other_tech_pvt, TFLAG_REINVITED)) { /* The other leg won the reinvite race */ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Other leg already handling reinvite, so responding with 491\n"); @@ -6328,7 +6354,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, if (tech_pvt->mparams.num_codecs) { match = sofia_media_negotiate_sdp(session, r_sdp, SDP_TYPE_REQUEST); } - + if (match && sofia_test_flag(tech_pvt, TFLAG_NOREPLY)) { sofia_clear_flag(tech_pvt, TFLAG_NOREPLY); goto done; @@ -6338,7 +6364,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, if (switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0) != SWITCH_STATUS_SUCCESS) { goto done; } - + switch_core_media_gen_local_sdp(session, NULL, 0, NULL, 0); if (sofia_media_activate_rtp(tech_pvt) != SWITCH_STATUS_SUCCESS) { @@ -6383,7 +6409,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, } break; case nua_callstate_ready: - if (!switch_channel_test_flag(channel, CF_PROXY_MODE) && !switch_channel_test_flag(channel, CF_PROXY_MEDIA) && + if (!switch_channel_test_flag(channel, CF_PROXY_MODE) && !switch_channel_test_flag(channel, CF_PROXY_MEDIA) && r_sdp && (!is_dup_sdp || sofia_test_flag(tech_pvt, TFLAG_NEW_SDP)) && switch_core_media_ready(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO) && !sofia_test_flag(tech_pvt, TFLAG_NOSDP_REINVITE)) { /* sdp changed since 18X w sdp, we're supposed to ignore it but we, of course, were pressured into supporting it */ uint8_t match = 0; @@ -6430,7 +6456,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, r_sdp); } - + if (sofia_test_flag(tech_pvt, TFLAG_3PCC) && sofia_test_pflag(profile, PFLAG_3PCC_PROXY)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "3PCC-PROXY, Got my ACK\n"); sofia_set_flag(tech_pvt, TFLAG_3PCC_HAS_ACK); @@ -6531,7 +6557,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, r_sdp); } - + //switch_channel_answer(other_channel); switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_ANSWER); @@ -6622,7 +6648,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, if (tech_pvt->sofia_private) { tech_pvt->sofia_private = NULL; } - + tech_pvt->nh = NULL; if (nh) { @@ -6635,7 +6661,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, } done: - + if ((enum nua_callstate) ss_state == nua_callstate_ready && channel && session && tech_pvt) { sofia_set_flag(tech_pvt, TFLAG_SIMPLIFY); @@ -6675,11 +6701,11 @@ void *SWITCH_THREAD_FUNC nightmare_xfer_thread_run(switch_thread_t *thread, void if ((status = switch_ivr_originate(NULL, &tsession, &cause, nhelper->exten_with_params, timeout, NULL, NULL, NULL, switch_channel_get_caller_profile(channel_a), nhelper->vars, SOF_NONE, NULL)) == SWITCH_STATUS_SUCCESS) { if (switch_channel_up(channel_a)) { - + if (switch_true(switch_channel_get_variable(channel_a, "recording_follow_transfer"))) { switch_core_media_bug_transfer_recordings(session, a_session); } - + tuuid_str = switch_core_session_get_uuid(tsession); switch_channel_set_variable_printf(channel_a, "transfer_to", "att:%s", tuuid_str); @@ -6841,7 +6867,7 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Process REFER to [%s@%s]\n", exten, (char *) refer_to->r_url->url_host); switch_channel_set_variable(tech_pvt->channel, "transfer_disposition", "recv_replace"); - + if (refer_to->r_url && refer_to->r_url->url_headers) { rep = (char *) switch_stristr("Replaces=", refer_to->r_url->url_headers); @@ -7036,7 +7062,7 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t if ((tmp = switch_core_session_locate(br_b))) { switch_channel_t *tchannel = switch_core_session_get_channel(tmp); - + if ((profile->media_options & MEDIA_OPT_BYPASS_AFTER_ATT_XFER)) { switch_channel_set_flag(tchannel, CF_BYPASS_MEDIA_AFTER_BRIDGE); } @@ -7054,7 +7080,7 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t } - if (switch_true(switch_channel_get_variable(channel_a, "recording_follow_transfer")) && + if (switch_true(switch_channel_get_variable(channel_a, "recording_follow_transfer")) && (tmp = switch_core_session_locate(br_a))) { switch_channel_set_variable(switch_core_session_get_channel(tmp), "transfer_disposition", "bridge"); switch_core_media_bug_transfer_recordings(session, tmp); @@ -7062,16 +7088,16 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t } - if (switch_true(switch_channel_get_variable(channel_b, "recording_follow_transfer")) && + if (switch_true(switch_channel_get_variable(channel_b, "recording_follow_transfer")) && (tmp = switch_core_session_locate(br_b))) { switch_core_media_bug_transfer_recordings(b_session, tmp); switch_core_session_rwunlock(tmp); } switch_channel_set_variable_printf(channel_a, "transfer_to", "att:%s", br_b); - + mark_transfer_record(session, br_a, br_b); - + switch_ivr_uuid_bridge(br_a, br_b); switch_channel_set_variable(channel_b, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"), @@ -7125,7 +7151,7 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t switch_channel_set_variable(t_channel, SOFIA_REFER_TO_VARIABLE, full_ref_to); } - + if (switch_true(switch_channel_get_variable(hup_channel, "recording_follow_transfer"))) { switch_core_media_bug_transfer_recordings(hup_session, t_session); } @@ -7281,7 +7307,7 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t switch_core_session_t *b_session; switch_channel_set_variable_printf(channel, "transfer_to", "blind:%s", br ? br : exten); - + if (!zstr(br) && (b_session = switch_core_session_locate(br))) { const char *var; switch_channel_t *b_channel = switch_core_session_get_channel(b_session); @@ -7294,14 +7320,14 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t if (!zstr(full_ref_to)) { switch_channel_set_variable(b_channel, SOFIA_REFER_TO_VARIABLE, full_ref_to); } - + if (switch_true(switch_channel_get_variable(channel, "recording_follow_transfer"))) { switch_core_media_bug_transfer_recordings(session, b_session); } switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "BLIND_TRANSFER"); - - if (((var = switch_channel_get_variable(channel, "confirm_blind_transfer")) && switch_true(var)) || + + if (((var = switch_channel_get_variable(channel, "confirm_blind_transfer")) && switch_true(var)) || sofia_test_pflag(profile, PFLAG_CONFIRM_BLIND_TRANSFER)) { switch_channel_set_state_flag(b_channel, CF_CONFIRM_BLIND_TRANSFER); @@ -7314,10 +7340,10 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t } else { nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"), NUTAG_SUBSTATE(nua_substate_terminated), - SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"), + SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK\r\n"), SIPTAG_EVENT_STR(etmp), TAG_END()); } - + switch_ivr_session_transfer(b_session, exten, NULL, NULL); switch_core_session_rwunlock(b_session); } else { @@ -7335,7 +7361,7 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t switch_channel_set_variable(channel_a, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER_ERROR"); nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"), NUTAG_SUBSTATE(nua_substate_terminated), - SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"), + SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"), SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden\r\n"), SIPTAG_EVENT_STR(etmp), TAG_END()); } } @@ -7353,7 +7379,7 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t static switch_status_t create_info_event(sip_t const *sip, - nua_handle_t *nh, switch_event_t **revent) + nua_handle_t *nh, switch_event_t **revent) { sip_alert_info_t *alert_info = sip_alert_info(sip); switch_event_t *event; @@ -7361,11 +7387,11 @@ static switch_status_t create_info_event(sip_t const *sip, if (!(sip && switch_event_create(&event, SWITCH_EVENT_RECV_INFO) == SWITCH_STATUS_SUCCESS)) { return SWITCH_STATUS_FALSE; } - + if (sip && sip->sip_content_type) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SIP-Content-Type", sip->sip_content_type->c_type); } - + if (sip->sip_from && sip->sip_from->a_url) { if (sip->sip_from->a_url->url_user) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SIP-From-User", sip->sip_from->a_url->url_user); @@ -7436,28 +7462,28 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t if (sofia_test_pflag(profile, PFLAG_EXTENDED_INFO_PARSING)) { if (sip && sip->sip_content_type && sip->sip_content_type->c_type && sip->sip_content_type->c_subtype && - sip->sip_payload && sip->sip_payload->pl_data) { - + sip->sip_payload && sip->sip_payload->pl_data) { + if (!strncasecmp(sip->sip_content_type->c_type, "freeswitch", 10)) { if (!strcasecmp(sip->sip_content_type->c_subtype, "session-event")) { if (session) { - if (create_info_event(sip, nh, &event) == SWITCH_STATUS_SUCCESS) { + if (create_info_event(sip, nh, &event) == SWITCH_STATUS_SUCCESS) { if (switch_core_session_queue_event(session, &event) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "queued freeswitch event for INFO\n"); nua_respond(nh, SIP_200_OK, SIPTAG_CONTENT_TYPE_STR("freeswitch/session-event-response"), - SIPTAG_PAYLOAD_STR("+OK MESSAGE QUEUED"), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); + SIPTAG_PAYLOAD_STR("+OK MESSAGE QUEUED"), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); } else { switch_event_destroy(&event); nua_respond(nh, SIP_200_OK, SIPTAG_CONTENT_TYPE_STR("freeswitch/session-event-response"), - SIPTAG_PAYLOAD_STR("-ERR MESSAGE NOT QUEUED"), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); + SIPTAG_PAYLOAD_STR("-ERR MESSAGE NOT QUEUED"), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); } } - + } else { nua_respond(nh, SIP_200_OK, SIPTAG_CONTENT_TYPE_STR("freeswitch/session-event-response"), - SIPTAG_PAYLOAD_STR("-ERR INVALID SESSION"), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); - + SIPTAG_PAYLOAD_STR("-ERR INVALID SESSION"), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); + } return; @@ -7467,28 +7493,28 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t char *arg; switch_stream_handle_t stream = { 0 }; switch_status_t status; - + SWITCH_STANDARD_STREAM(stream); switch_assert(stream.data); - + if ((arg = strchr(cmd, ':'))) { *arg++ = '\0'; } if ((status = switch_api_execute(cmd, arg, NULL, &stream)) == SWITCH_STATUS_SUCCESS) { - nua_respond(nh, SIP_200_OK, SIPTAG_CONTENT_TYPE_STR("freeswitch/api-response"), - SIPTAG_PAYLOAD_STR(stream.data), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); + nua_respond(nh, SIP_200_OK, SIPTAG_CONTENT_TYPE_STR("freeswitch/api-response"), + SIPTAG_PAYLOAD_STR(stream.data), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); } else { nua_respond(nh, SIP_200_OK, SIPTAG_CONTENT_TYPE_STR("freeswitch/api-response"), - SIPTAG_PAYLOAD_STR("-ERR INVALID COMMAND"), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); + SIPTAG_PAYLOAD_STR("-ERR INVALID COMMAND"), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); } - + switch_safe_free(stream.data); switch_safe_free(cmd); return; } - nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); + nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); return; } @@ -7507,46 +7533,46 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t if (sip && sip->sip_content_type && sip->sip_content_type->c_type && !strcasecmp(sip->sip_content_type->c_type, "freeswitch/data")) { char *data = NULL; - + if (sip->sip_payload && sip->sip_payload->pl_data) { data = sip->sip_payload->pl_data; } - if ((vval = switch_channel_get_variable(channel, "sip_copy_custom_headers")) && switch_true(vval)) { - switch_core_session_t *nsession = NULL; - - switch_core_session_get_partner(session, &nsession); - - if (nsession) { + if ((vval = switch_channel_get_variable(channel, "sip_copy_custom_headers")) && switch_true(vval)) { + switch_core_session_t *nsession = NULL; + + switch_core_session_get_partner(session, &nsession); + + if (nsession) { switch_core_session_message_t *msg; - - switch_ivr_transfer_variable(session, nsession, SOFIA_SIP_INFO_HEADER_PREFIX_T); + + switch_ivr_transfer_variable(session, nsession, SOFIA_SIP_INFO_HEADER_PREFIX_T); msg = switch_core_session_alloc(nsession, sizeof(*msg)); MESSAGE_STAMP_FFL(msg); msg->message_id = SWITCH_MESSAGE_INDICATE_INFO; - + msg->string_array_arg[2] = switch_core_session_strdup(nsession, data); - + msg->from = __FILE__; switch_core_session_queue_message(nsession, msg); - + switch_core_session_rwunlock(nsession); - } - } + } + } } - + if (sip && sip->sip_content_type && sip->sip_content_type->c_subtype && sip->sip_content_type->c_type && !strncasecmp(sip->sip_content_type->c_type, "message", 7) && !strcasecmp(sip->sip_content_type->c_subtype, "update_display")) { sofia_update_callee_id(session, profile, sip, SWITCH_TRUE); goto end; } - + if (sip && sip->sip_content_type && sip->sip_content_type->c_type && sip->sip_content_type->c_subtype && sip->sip_payload && sip->sip_payload->pl_data) { if (!strncasecmp(sip->sip_content_type->c_type, "application", 11) && !strcasecmp(sip->sip_content_type->c_subtype, "media_control+xml")) { switch_core_session_t *other_session; - + if (switch_channel_test_flag(channel, CF_VIDEO)) { if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) { sofia_glue_build_vid_refresh_message(other_session, sip->sip_payload->pl_data); @@ -7601,7 +7627,7 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t } if (dtmf.digit) { - if (tech_pvt->mparams.dtmf_type == DTMF_INFO || + if (tech_pvt->mparams.dtmf_type == DTMF_INFO || sofia_test_pflag(tech_pvt->profile, PFLAG_LIBERAL_DTMF) || switch_channel_test_flag(tech_pvt->channel, CF_LIBERAL_DTMF)) { /* queue it up */ switch_channel_queue_dtmf(channel, &dtmf); @@ -7628,7 +7654,7 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t /* Send 200 OK response */ nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "IGNORE INFO DTMF(%c) (This channel was not configured to use INFO DTMF!)\n", dtmf.digit); } } @@ -7683,7 +7709,7 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t end: - if (create_info_event(sip, nh, &event) == SWITCH_STATUS_SUCCESS) { + if (create_info_event(sip, nh, &event) == SWITCH_STATUS_SUCCESS) { if (channel) { switch_channel_event_set_data(channel, event); } @@ -7727,7 +7753,7 @@ void sofia_handle_sip_i_reinvite(switch_core_session_t *session, switch_channel_set_variable_printf(channel, "sip_network_port", "%d", network_port); switch_channel_set_variable_printf(channel, "sip_recieved_port", "%d", network_port); switch_channel_set_variable_printf(channel, "sip_via_rport", "%d", network_port); - + switch_core_recovery_track(session); } @@ -7820,7 +7846,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia nua_respond(nh, 400, "Missing Contact Header", TAG_END()); goto fail; } - + sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port); if (sip && sip->sip_via && sip->sip_via->v_protocol && switch_stristr("sip/2.0/ws", sip->sip_via->v_protocol)) { @@ -7856,14 +7882,14 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia if (!zstr(contact_host)) { /* NAT mode double check logic and examples. - Example 1: the contact_host is 192.168.1.100 and the network_ip is also 192.168.1.100 the end point + Example 1: the contact_host is 192.168.1.100 and the network_ip is also 192.168.1.100 the end point is most likely behind nat with us so we need to veto that decision to turn on nat processing. Example 2: the contact_host is 192.168.1.100 and the network_ip is 192.0.2.100 which is a public internet ip - the remote endpoint is likely behind a remote nat traversing the public internet. + the remote endpoint is likely behind a remote nat traversing the public internet. This secondary check is here to double check the conclusion of nat settigs to ensure we don't set net - in cases where we don't really need to be doing this. + in cases where we don't really need to be doing this. Why would you want to do this? Well if your FreeSWITCH is behind nat and you want to talk to endpoints behind remote NAT over the public internet in addition to endpoints behind nat with you. This simplifies that process. @@ -7922,7 +7948,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "IP %s Approved by acl \"%s[%s]\". Access Granted.\n", network_ip, switch_str_nil(last_acl), acl_token); switch_set_string(sip_acl_authed_by, last_acl); - switch_set_string(sip_acl_token, acl_token); + switch_set_string(sip_acl_token, acl_token); is_auth = 1; } } else { @@ -7986,7 +8012,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia proxied_client_ip, switch_str_nil(last_acl), acl_token); switch_set_string(sip_acl_authed_by, last_acl); switch_set_string(sip_acl_token, acl_token); - + is_auth = 1; } @@ -8066,12 +8092,12 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia } else { transport = sofia_glue_url2transport(sip->sip_contact->m_url); } - + tech_pvt->record_route = switch_core_session_sprintf(session, "sip:%s%s%s:%d;transport=%s", - ipv6 ? "[" : "", tech_pvt->mparams.remote_ip, ipv6 ? "]" : "", + ipv6 ? "[" : "", tech_pvt->mparams.remote_ip, ipv6 ? "]" : "", tech_pvt->mparams.remote_port, sofia_glue_transport2str(transport)); switch_channel_set_variable(channel, "sip_received_ip", tech_pvt->mparams.remote_ip); @@ -8121,14 +8147,14 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia ruser = switch_event_get_header(v_event, "username"); rdomain = switch_event_get_header(v_event, "domain_name"); - + switch_channel_set_variable(channel, "requested_user_name", ruser); switch_channel_set_variable(channel, "requested_domain_name", rdomain); } if (!user) user = ruser; if (!domain) domain = rdomain; - + switch_ivr_set_user_xml(session, NULL, user, domain, x_user); switch_xml_free(x_user); x_user = NULL; @@ -8433,7 +8459,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia if (sip->sip_multipart) { msg_multipart_t *mp; - + for (mp = sip->sip_multipart; mp; mp = mp->mp_next) { if (mp->mp_payload && mp->mp_payload->pl_data && mp->mp_content_type && mp->mp_content_type->c_type) { char *val = switch_core_session_sprintf(session, "%s:%s", mp->mp_content_type->c_type, mp->mp_payload->pl_data); @@ -8474,7 +8500,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia if ((call_info = sip_call_info(sip))) { call_info_str = sip_header_as_string(nh->nh_home, (void *) call_info); - + if (sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE) && switch_stristr("appearance", call_info_str)) { char *p; @@ -8484,7 +8510,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia switch_channel_set_variable(channel, "presence_call_info", p); } } - + if (call_info->ci_params && (msg_params_find(call_info->ci_params, "answer-after=0"))) { switch_channel_set_variable(channel, "sip_auto_answer_detected", "true"); } @@ -8504,7 +8530,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia char buf[128] = ""; char *sql; char *state = "progressing"; - + if (sip && sip->sip_from && sip->sip_from->a_url && sip->sip_from->a_url->url_user && sip->sip_from->a_url->url_host && sip->sip_to && sip->sip_to->a_url && sip->sip_to->a_url->url_user && sip->sip_to->a_url->url_host) { @@ -8513,12 +8539,12 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia "sub_to_user='%q' and sub_to_host='%q'", mod_sofia_globals.hostname, sip->sip_to->a_url->url_user, sip->sip_from->a_url->url_host); sofia_glue_execute_sql2str(profile, profile->dbh_mutex, sql, buf, sizeof(buf)); - + if (mod_sofia_globals.debug_sla > 1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "QUERY SQL %s [%s]\n", sql, buf); } free(sql); - + if (!zstr(buf)) { sql = switch_mprintf("update sip_dialogs set call_info='%q',call_info_state='%q' " "where uuid='%q'", buf, state, switch_core_session_get_uuid(session)); @@ -8526,18 +8552,18 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia if (mod_sofia_globals.debug_sla > 1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "QUERY SQL %s\n", sql); } - + sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Auto-Fixing Broken SLA [;%s]\n", + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Auto-Fixing Broken SLA [;%s]\n", sip->sip_from->a_url->url_host, buf); switch_channel_set_variable_printf(channel, "presence_call_info_full", ";%s", sip->sip_from->a_url->url_host, buf); switch_channel_set_variable(channel, "presence_call_info", buf); call_info_str = switch_core_session_sprintf(session, ";%s", sip->sip_from->a_url->url_host, buf); } - } + } } @@ -8659,7 +8685,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia free(sql); } - } + } check_decode(displayname, session); @@ -8669,6 +8695,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); @@ -8698,7 +8725,7 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia tech_pvt->caller_profile->caller_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->caller_id_number); -#if 0 +#if 0 sent_name = switch_channel_get_variable(b_channel, "last_sent_callee_id_name"); sent_number = switch_channel_get_variable(b_channel, "last_sent_callee_id_number"); @@ -8735,34 +8762,34 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia } else { olu = b_private->uuid; } - + if ((os = switch_core_session_locate(olu))) { switch_core_session_get_real_read_impl(os, &read_impl); switch_core_session_rwunlock(os); - codec_str = switch_core_session_sprintf(session, "set:absolute_codec_string=%s@%di,", read_impl.iananame, + codec_str = switch_core_session_sprintf(session, "set:absolute_codec_string=%s@%di,", read_impl.iananame, read_impl.microseconds_per_packet / 1000); } if (!zstr(bridge_uuid) && switch_channel_test_flag(b_channel, CF_LEG_HOLDING)) { - tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, + tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, "%sanswer,intercept:%s", codec_str, bridge_uuid); } else { const char *name = NULL, *num = NULL; switch_caller_profile_t *bcp = switch_channel_get_caller_profile(b_channel); - + if (switch_channel_test_flag(b_channel, CF_BRIDGE_ORIGINATOR) || !switch_channel_test_flag(b_channel, CF_BRIDGED)) { name = bcp->callee_id_name; num = bcp->callee_id_number; } else { name = bcp->caller_id_name; num = bcp->caller_id_number; - } - + } + tech_pvt->caller_profile->callee_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, name); tech_pvt->caller_profile->callee_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, num); - tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, + tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, "%sanswer,sofia_sla:%s", codec_str, b_private->uuid); } } else { @@ -8772,18 +8799,18 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia } else { const char *b_app = switch_channel_get_variable(b_channel, SWITCH_CURRENT_APPLICATION_VARIABLE); const char *b_data = switch_channel_get_variable(b_channel, SWITCH_CURRENT_APPLICATION_DATA_VARIABLE); - + if (b_data && b_app) { tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, "answer,%s:%s", b_app, b_data); } else if (b_app) { tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, "answer,%s", b_app); } - + switch_channel_hangup(b_channel, SWITCH_CAUSE_ATTENDED_TRANSFER); } } - + switch_core_session_rwunlock(b_session); } } @@ -8999,7 +9026,7 @@ void sofia_handle_sip_i_options(int status, (sess_count >= sess_max || !sofia_test_pflag(profile, PFLAG_RUNNING) || !switch_core_ready_inbound())) { nua_respond(nh, 503, "Maximum Calls In Progress", NUTAG_WITH_THIS_MSG(de->data->e_msg), SIPTAG_RETRY_AFTER_STR("300"), TAG_END()); } else { - nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), + nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_IF(sip->sip_record_route, SIPTAG_RECORD_ROUTE(sip->sip_record_route)), TAG_END()); } @@ -9019,14 +9046,14 @@ void sofia_handle_sip_i_options(int status, * mbid=201 * * The a_params list is parsed and the lvalue is used to create the channel variable name while the - * rvalue is used to create the channel variable value. + * rvalue is used to create the channel variable value. * * If no equal (=) sign is found during parsing, a channel variable name is created with the param and * the value is set to NULL. * * Pointers are used for copying the sip_header_name for performance reasons. There are no calls to * any string functions and no memory is allocated/dealocated. The only limiter is the size of the - * sip_header_name array. + * sip_header_name array. */ static void set_variable_sip_param(switch_channel_t *channel, char *header_type, sip_param_t const *params) { diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index 620d4e5d0b..f569a12dd2 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -497,7 +497,12 @@ char *sofia_overcome_sip_uri_weakness(switch_core_session_t *session, const char char *stripped = switch_core_session_strdup(session, uri); char *new_uri = NULL; char *p; + const char *url_params = NULL; + if (!zstr(params) && *params == '~') { + url_params = params + 1; + params = NULL; + } stripped = sofia_glue_get_url_from_contact(stripped, 0); @@ -542,7 +547,9 @@ char *sofia_overcome_sip_uri_weakness(switch_core_session_t *session, const char } } - + if (url_params && !uri_only) { + new_uri = switch_core_session_sprintf(session, "%s;%s", new_uri, url_params); + } if (!zstr(invite_tel_params)) { char *lhs, *rhs = strchr(new_uri, '@'); @@ -913,7 +920,6 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session) switch_channel_set_variable(channel, "sip_outgoing_contact_uri", invite_contact); - /* Does the "genius" who wanted SIP to be "text-based" so it was "easier to read" even use it now, or did he just suggest it to make our lives miserable? diff --git a/src/mod/endpoints/mod_sofia/sofia_presence.c b/src/mod/endpoints/mod_sofia/sofia_presence.c index 5136eee8fc..465455568b 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; } @@ -1032,7 +1037,7 @@ static void conference_data_event_handler(switch_event_t *event) sofia_profile_t *profile = NULL; if (zstr(host)) { - dup_domain = switch_core_get_variable_dup("domain"); + dup_domain = switch_core_get_domain(SWITCH_TRUE); host = dup_domain; } @@ -3653,6 +3658,39 @@ void sofia_presence_handle_sip_i_subscribe(int status, switch_snprintf(exp_delta_str, sizeof(exp_delta_str), "%ld", exp_delta); + if (!strcmp("as-feature-event", event)) { + sip_authorization_t const *authorization = NULL; + auth_res_t auth_res = AUTH_FORBIDDEN; + char key[128] = ""; + switch_event_t *v_event = NULL; + + + if (sip->sip_authorization) { + authorization = sip->sip_authorization; + } else if (sip->sip_proxy_authorization) { + authorization = sip->sip_proxy_authorization; + } + + if (authorization) { + char network_ip[80]; + sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), NULL); + auth_res = sofia_reg_parse_auth(profile, authorization, sip, de, + (char *) sip->sip_request->rq_method_name, key, sizeof(key), network_ip, &v_event, 0, + REG_REGISTER, to_user, NULL, NULL, NULL); + } else if ( sofia_reg_handle_register(nua, profile, nh, sip, de, REG_REGISTER, key, sizeof(key), &v_event, NULL, NULL, NULL)) { + if (v_event) { + switch_event_destroy(&v_event); + } + + goto end; + } + + if ((auth_res != AUTH_OK && auth_res != AUTH_RENEWED)) { + nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); + goto end; + } + } + if (to_user && strchr(to_user, '+')) { char *h; if ((proto = (d_user = strdup(to_user)))) { @@ -3986,7 +4024,24 @@ void sofia_presence_handle_sip_i_subscribe(int status, switch_safe_free(sstr); - if (!strcasecmp(event, "message-summary")) { + if (!strcasecmp(event, "as-feature-event")) { + switch_event_t *event; + char sip_cseq[40] = ""; + switch_snprintf(sip_cseq, sizeof(sip_cseq), "%d", sip->sip_cseq->cs_seq); + switch_event_create(&event, SWITCH_EVENT_PHONE_FEATURE_SUBSCRIBE); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "user", from_user); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "host", from_host); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "contact", contact_str); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-id", call_id); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "expires", exp_delta_str); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "cseq", sip_cseq); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "profile_name", profile->name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hostname", mod_sofia_globals.hostname); + if (sip->sip_payload) { + switch_event_add_body(event, "%s", sip->sip_payload->pl_data); + } + switch_event_fire(&event); + } else if (!strcasecmp(event, "message-summary")) { if ((sql = switch_mprintf("select proto,sip_user,'%q',sub_to_user,sub_to_host,event,contact,call_id,full_from," "full_via,expires,user_agent,accept,profile_name,network_ip" " from sip_subscriptions where hostname='%q' and profile_name='%q' and " diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c index 23f211f53f..d0f7dec837 100644 --- a/src/mod/endpoints/mod_sofia/sofia_reg.c +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -1240,10 +1240,12 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand to_host = to->a_url->url_host; } - if (!to_user) + if (!to_user) { to_user = from_user; - if (!to_host) + } + if (!to_host) { to_host = from_host; + } if (!to_user || !to_host) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can not do authorization without a complete header in REGISTER request from %s:%d\n", @@ -2891,7 +2893,7 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, skip_auth: if (first && (ret == AUTH_OK || ret == AUTH_RENEWED)) { - if (!v_event) { + if (v_event && !*v_event) { switch_event_create_plain(v_event, SWITCH_EVENT_REQUEST_PARAMS); } diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml index 0cb46d7992..35419319bc 100644 --- a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml +++ b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml @@ -11,6 +11,11 @@ + + + + + @@ -42,4 +47,211 @@ + + + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + ]]> + + + + +

Please press a digit.

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

Please press a digit.

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

Please press a digit.

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

Please press a digit.

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

Please press a digit.

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

Please press a digit.

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

Please press a digit.

]]]]> +
+
+ + + 0123456789]]]]> + + + + ]]> +
+ + + + 0123456789]]]]> + + + ]]> + + + + + yesno + ]]]]> + + + ]]> + + + + + yesno + ]]]]> + + + ]]> + + + + + + yesno + ]]]]> + + + ]]> + + + + + + yesno + ]]]]> + + + ]]> + + +
+ diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.c b/src/mod/event_handlers/mod_rayo/iks_helpers.c index 0b5616a1f3..30d9d215a5 100644 --- a/src/mod/event_handlers/mod_rayo/iks_helpers.c +++ b/src/mod/event_handlers/mod_rayo/iks_helpers.c @@ -216,6 +216,17 @@ double iks_find_decimal_attrib(iks *xml, const char *attrib) return atof(iks_find_attrib_soft(xml, attrib)); } +/** + * Get attribute character value of node + * @param xml the XML node to search + * @param attrib the Attribute name + * @return the attribute value + */ +char iks_find_char_attrib(iks *xml, const char *attrib) +{ + return iks_find_attrib_soft(xml, attrib)[0]; +} + /** * Convert iksemel XML node type to string * @param type the XML node type @@ -392,6 +403,54 @@ int iks_attrib_is_decimal_between_zero_and_one(const char *value) return SWITCH_FALSE; } +/** + * Validate dtmf digit + * @param value + * @return SWITCH_TRUE if 0-9,a,b,c,d,A,B,C,D,*,# + */ +int iks_attrib_is_dtmf_digit(const char *value) +{ + if (value && *value && strlen(value) == 1) { + switch (*value) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'a': + case 'B': + case 'b': + case 'C': + case 'c': + case 'D': + case 'd': + case '*': + case '#': + return SWITCH_TRUE; + } + } + return SWITCH_FALSE; +} + +/** + * @param fn to evaluate attribute + * @param attrib to evaluate + * @return true if not set or is valid + */ +int validate_optional_attrib(iks_attrib_validation_function fn, const char *attrib) +{ + if (!attrib || !*attrib) { + return SWITCH_TRUE; + } + return fn(attrib); +} + #define IKS_SHA256_HEX_DIGEST_LENGTH ((SHA256_DIGEST_LENGTH * 2) + 1) /** diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.h b/src/mod/event_handlers/mod_rayo/iks_helpers.h index 90a5ca688d..3a7bae02e7 100644 --- a/src/mod/event_handlers/mod_rayo/iks_helpers.h +++ b/src/mod/event_handlers/mod_rayo/iks_helpers.h @@ -63,6 +63,7 @@ extern const char *iks_find_attrib_soft(iks *xml, const char *attrib); extern const char *iks_find_attrib_default(iks *xml, const char *attrib, const char *def); extern int iks_find_bool_attrib(iks *xml, const char *attrib); extern int iks_find_int_attrib(iks *xml, const char *attrib); +extern char iks_find_char_attrib(iks *xml, const char *attrib); extern double iks_find_decimal_attrib(iks *xml, const char *attrib); extern const char *iks_node_type_to_string(int type); extern const char *iks_net_error_to_string(int err); @@ -73,9 +74,12 @@ extern char *iks_server_dialback_key(const char *secret, const char *receiving_s /** A function to validate attribute value */ typedef int (*iks_attrib_validation_function)(const char *); +extern int validate_optional_attrib(iks_attrib_validation_function fn, const char *attrib); + #define ELEMENT_DECL(name) extern int VALIDATE_##name(iks *node); #define ELEMENT(name) int VALIDATE_##name(iks *node) { int result = 1; if (!node) return 0; #define ATTRIB(name, def, rule) result &= iks_attrib_is_##rule(iks_find_attrib_default(node, #name, #def)); +#define OPTIONAL_ATTRIB(name, def, rule) result &= validate_optional_attrib(iks_attrib_is_##rule, iks_find_attrib_default(node, #name, #def)); #define STRING_ATTRIB(name, def, rule) result &= value_matches(iks_find_attrib_default(node, #name, #def), rule); #define ELEMENT_END return result; } @@ -87,6 +91,7 @@ extern int iks_attrib_is_positive(const char *value); extern int iks_attrib_is_positive_or_neg_one(const char *value); extern int iks_attrib_is_any(const char *value); extern int iks_attrib_is_decimal_between_zero_and_one(const char *value); +extern int iks_attrib_is_dtmf_digit(const char *value); #endif diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index fffffe31eb..3004601661 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -239,6 +239,12 @@ static void rayo_console_client_send(struct rayo_actor *client, struct rayo_mess static void on_client_presence(struct rayo_client *rclient, iks *node); +typedef switch_bool_t (* rayo_actor_match_fn)(struct rayo_actor *); + +static switch_bool_t is_call_actor(struct rayo_actor *actor); + + + /** * @param msg to check * @return true if message was sent by admin client (console) @@ -712,6 +718,9 @@ struct rayo_actor *rayo_actor_locate(const char *jid, const char *file, int line { struct rayo_actor *actor = NULL; switch_mutex_lock(globals.actors_mutex); + if (!strncmp("xmpp:", jid, 5)) { + jid = jid + 5; + } actor = (struct rayo_actor *)switch_core_hash_find(globals.actors, jid); if (actor) { if (!actor->destroy) { @@ -818,16 +827,33 @@ int rayo_actor_seq_next(struct rayo_actor *actor) return seq; } -#define RAYO_CALL_LOCATE(call_uuid) rayo_call_locate(call_uuid, __FILE__, __LINE__) +#define RAYO_CALL_LOCATE(call_uri) rayo_call_locate(call_uri, __FILE__, __LINE__) /** - * Get exclusive access to Rayo call data. Use to access call data outside channel thread. + * Get access to Rayo call data. Use to access call data outside channel thread. + * @param call_uri the Rayo XMPP URI + * @return the call or NULL. + */ +static struct rayo_call *rayo_call_locate(const char *call_uri, const char *file, int line) +{ + struct rayo_actor *actor = rayo_actor_locate(call_uri, file, line); + if (actor && is_call_actor(actor)) { + return RAYO_CALL(actor); + } else if (actor) { + RAYO_UNLOCK(actor); + } + return NULL; +} + +#define RAYO_CALL_LOCATE_BY_ID(call_uuid) rayo_call_locate_by_id(call_uuid, __FILE__, __LINE__) +/** + * Get access to Rayo call data. Use to access call data outside channel thread. * @param call_uuid the FreeSWITCH call UUID * @return the call or NULL. */ -static struct rayo_call *rayo_call_locate(const char *call_uuid, const char *file, int line) +static struct rayo_call *rayo_call_locate_by_id(const char *call_uuid, const char *file, int line) { struct rayo_actor *actor = rayo_actor_locate_by_id(call_uuid, file, line); - if (actor && !strcmp(RAT_CALL, actor->type)) { + if (actor && is_call_actor(actor)) { return RAYO_CALL(actor); } else if (actor) { RAYO_UNLOCK(actor); @@ -1686,18 +1712,18 @@ static iks *on_rayo_hangup(struct rayo_actor *call, struct rayo_message *msg, vo * @param call the call that joins * @param session the session * @param node the join request - * @param call_id to join + * @param call_uri to join * @param media mode (direct/bridge) * @return the response */ -static iks *join_call(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *call_id, const char *media) +static iks *join_call(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *call_uri, const char *media) { iks *response = NULL; /* take call out of media path if media = "direct" */ const char *bypass = !strcmp("direct", media) ? "true" : "false"; /* check if joining to rayo call */ - struct rayo_call *b_call = RAYO_CALL_LOCATE(call_id); + struct rayo_call *b_call = RAYO_CALL_LOCATE(call_uri); if (!b_call) { /* not a rayo call */ response = iks_new_error_detailed(node, STANZA_ERROR_SERVICE_UNAVAILABLE, "b-leg is not a rayo call"); @@ -1706,18 +1732,17 @@ static iks *join_call(struct rayo_call *call, switch_core_session_t *session, ik response = iks_new_error_detailed(node, STANZA_ERROR_CONFLICT, "multiple joined calls not supported"); RAYO_UNLOCK(b_call); } else { - RAYO_UNLOCK(b_call); - /* bridge this call to call-uri */ switch_channel_set_variable(switch_core_session_get_channel(session), "bypass_media", bypass); if (switch_false(bypass)) { switch_channel_pre_answer(switch_core_session_get_channel(session)); } - if (switch_ivr_uuid_bridge(rayo_call_get_uuid(call), call_id) == SWITCH_STATUS_SUCCESS) { + if (switch_ivr_uuid_bridge(rayo_call_get_uuid(call), rayo_call_get_uuid(b_call)) == SWITCH_STATUS_SUCCESS) { response = iks_new_iq_result(node); } else { response = iks_new_error_detailed(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to bridge call"); } + RAYO_UNLOCK(b_call); } return response; } @@ -1791,7 +1816,7 @@ static iks *on_rayo_join(struct rayo_actor *call, struct rayo_message *msg, void iks *join = iks_find(node, "join"); const char *join_id; const char *mixer_name; - const char *call_id; + const char *call_uri; /* validate input attributes */ if (!VALIDATE_RAYO_JOIN(join)) { @@ -1800,22 +1825,22 @@ static iks *on_rayo_join(struct rayo_actor *call, struct rayo_message *msg, void goto done; } mixer_name = iks_find_attrib(join, "mixer-name"); - call_id = iks_find_attrib(join, "call-uri"); + call_uri = iks_find_attrib(join, "call-uri"); if (!zstr(mixer_name)) { join_id = mixer_name; } else { - join_id = call_id; + join_id = call_uri; } /* can't join both mixer and call */ - if (!zstr(mixer_name) && !zstr(call_id)) { + if (!zstr(mixer_name) && !zstr(call_uri)) { response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "mixer-name and call-uri are mutually exclusive"); goto done; } /* need to join *something* */ - if (zstr(mixer_name) && zstr(call_id)) { + if (zstr(mixer_name) && zstr(call_uri)) { response = iks_new_error_detailed(node, STANZA_ERROR_BAD_REQUEST, "mixer-name or call-uri is required"); goto done; } @@ -1832,7 +1857,7 @@ static iks *on_rayo_join(struct rayo_actor *call, struct rayo_message *msg, void response = join_mixer(RAYO_CALL(call), session, node, mixer_name, iks_find_attrib(join, "direction")); } else { /* bridge calls */ - response = join_call(RAYO_CALL(call), session, node, call_id, iks_find_attrib(join, "media")); + response = join_call(RAYO_CALL(call), session, node, call_uri, iks_find_attrib(join, "media")); } done: @@ -1844,21 +1869,22 @@ done: * @param call the call that unjoined * @param session the session * @param node the unjoin request - * @param call_id the b-leg uuid + * @param call_uri the b-leg xmpp URI * @return the response */ -static iks *unjoin_call(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *call_id) +static iks *unjoin_call(struct rayo_call *call, switch_core_session_t *session, iks *node, const char *call_uri) { iks *response = NULL; - const char *bleg = switch_channel_get_variable(switch_core_session_get_channel(session), SWITCH_BRIDGE_UUID_VARIABLE); + const char *bleg_uuid = switch_channel_get_variable(switch_core_session_get_channel(session), SWITCH_BRIDGE_UUID_VARIABLE); + const char *bleg_uri = switch_core_session_sprintf(session, "xmpp:%s@%s", bleg_uuid ? bleg_uuid : "", RAYO_JID(globals.server)); - /* bleg must match call_id */ - if (!zstr(bleg) && !strcmp(bleg, call_id)) { + /* bleg must match call_uri */ + if (!zstr(bleg_uri) && !strcmp(bleg_uri, call_uri)) { /* unbridge call */ response = iks_new_iq_result(node); switch_ivr_park_session(session); } else { - /* not bridged or wrong b-leg UUID */ + /* not bridged or wrong b-leg URI */ response = iks_new_error(node, STANZA_ERROR_SERVICE_UNAVAILABLE); } @@ -1913,16 +1939,16 @@ static iks *on_rayo_unjoin(struct rayo_actor *call, struct rayo_message *msg, vo switch_core_session_t *session = (switch_core_session_t *)session_data; iks *response = NULL; iks *unjoin = iks_find(node, "unjoin"); - const char *call_id = iks_find_attrib(unjoin, "call-uri"); + const char *call_uri = iks_find_attrib(unjoin, "call-uri"); const char *mixer_name = iks_find_attrib(unjoin, "mixer-name"); - if (!zstr(call_id) && !zstr(mixer_name)) { + if (!zstr(call_uri) && !zstr(mixer_name)) { response = iks_new_error(node, STANZA_ERROR_BAD_REQUEST); } else if (!RAYO_CALL(call)->joined) { /* not joined to anything */ response = iks_new_error(node, STANZA_ERROR_SERVICE_UNAVAILABLE); - } else if (!zstr(call_id)) { - response = unjoin_call(RAYO_CALL(call), session, node, call_id); + } else if (!zstr(call_uri)) { + response = unjoin_call(RAYO_CALL(call), session, node, call_uri); } else if (!zstr(mixer_name)) { response = unjoin_mixer(RAYO_CALL(call), session, node, mixer_name); } else { @@ -2037,20 +2063,20 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void * if (join) { /* check join args */ - const char *call_id = iks_find_attrib(join, "call-uri"); + const char *call_uri = iks_find_attrib(join, "call-uri"); const char *mixer_name = iks_find_attrib(join, "mixer-name"); - if (!zstr(call_id) && !zstr(mixer_name)) { + if (!zstr(call_uri) && !zstr(mixer_name)) { /* can't join both */ response = iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); goto done; - } else if (zstr(call_id) && zstr(mixer_name)) { + } else if (zstr(call_uri) && zstr(mixer_name)) { /* nobody to join to? */ response = iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); goto done; - } else if (!zstr(call_id)) { + } else if (!zstr(call_uri)) { /* bridge */ - struct rayo_call *b_call = RAYO_CALL_LOCATE(call_id); + struct rayo_call *b_call = RAYO_CALL_LOCATE(call_uri); /* is b-leg available? */ if (!b_call) { response = iks_new_error_detailed(iq, STANZA_ERROR_SERVICE_UNAVAILABLE, "b-leg not found"); @@ -2060,8 +2086,8 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void * RAYO_UNLOCK(b_call); goto done; } + stream.write_function(&stream, "%s%s &rayo(bridge %s)", gateway->dial_prefix, dial_to_stripped, rayo_call_get_uuid(b_call)); RAYO_UNLOCK(b_call); - stream.write_function(&stream, "%s%s &rayo(bridge %s)", gateway->dial_prefix, dial_to_stripped, call_id); } else { /* conference */ stream.write_function(&stream, "%s%s &rayo(conference %s@%s)", gateway->dial_prefix, dial_to_stripped, mixer_name, globals.mixer_conf_profile); @@ -2393,7 +2419,7 @@ static void on_mixer_delete_member_event(struct rayo_mixer *mixer, switch_event_ switch_core_hash_delete(mixer->members, uuid); /* flag call as available to join another mixer */ - call = RAYO_CALL_LOCATE(uuid); + call = RAYO_CALL_LOCATE_BY_ID(uuid); if (call) { call->joined = 0; call->joined_id = NULL; @@ -2409,7 +2435,7 @@ static void on_mixer_delete_member_event(struct rayo_mixer *mixer, switch_event_ /* broadcast member unjoined event to subscribers */ delete_member_event = iks_new_presence("unjoined", RAYO_NS, RAYO_JID(mixer), ""); x = iks_find(delete_member_event, "unjoined"); - iks_insert_attrib(x, "call-uri", uuid); + iks_insert_attrib_printf(x, "call-uri", "xmpp:%s@%s", uuid, RAYO_JID(globals.server)); broadcast_mixer_event(mixer, delete_member_event); iks_delete(delete_member_event); @@ -2445,7 +2471,7 @@ static void on_mixer_add_member_event(struct rayo_mixer *mixer, switch_event_t * { iks *add_member_event = NULL, *x; const char *uuid = switch_event_get_header(event, "Unique-ID"); - struct rayo_call *call = RAYO_CALL_LOCATE(uuid); + struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(uuid); if (!mixer) { /* new mixer */ @@ -2487,7 +2513,7 @@ static void on_mixer_add_member_event(struct rayo_mixer *mixer, switch_event_t * /* broadcast member joined event to subscribers */ add_member_event = iks_new_presence("joined", RAYO_NS, RAYO_JID(mixer), ""); x = iks_find(add_member_event, "joined"); - iks_insert_attrib(x, "call-uri", uuid); + iks_insert_attrib_printf(x, "call-uri", "xmpp:%s@%s", uuid, RAYO_JID(globals.server)); broadcast_mixer_event(mixer, add_member_event); iks_delete(add_member_event); } @@ -2533,7 +2559,7 @@ static void on_call_originate_event(struct rayo_client *rclient, switch_event_t { switch_core_session_t *session = NULL; const char *uuid = switch_event_get_header(event, "Unique-ID"); - struct rayo_call *call = RAYO_CALL_LOCATE(uuid); + struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(uuid); if (call && (session = switch_core_session_locate(uuid))) { iks *response, *ref; @@ -2550,11 +2576,7 @@ static void on_call_originate_event(struct rayo_client *rclient, switch_event_t ref = iks_insert(response, "ref"); iks_insert_attrib(ref, "xmlns", RAYO_NS); -#ifdef RAYO_UUID_IN_REF_URI - iks_insert_attrib(ref, "uri", uuid); -#else iks_insert_attrib_printf(ref, "uri", "xmpp:%s", RAYO_JID(call)); -#endif RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), response); call->dial_id = NULL; } @@ -2567,7 +2589,7 @@ static void on_call_originate_event(struct rayo_client *rclient, switch_event_t */ static void on_call_end_event(switch_event_t *event) { - struct rayo_call *call = RAYO_CALL_LOCATE(switch_event_get_header(event, "Unique-ID")); + struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(switch_event_get_header(event, "Unique-ID")); if (call) { #if 0 @@ -2591,7 +2613,7 @@ static void on_call_end_event(switch_event_t *event) */ static void on_call_answer_event(struct rayo_client *rclient, switch_event_t *event) { - struct rayo_call *call = RAYO_CALL_LOCATE(switch_event_get_header(event, "Unique-ID")); + struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(switch_event_get_header(event, "Unique-ID")); if (call) { iks *revent = iks_new_presence("answered", RAYO_NS, switch_event_get_header(event, "variable_rayo_call_jid"), @@ -2610,7 +2632,7 @@ static void on_call_ringing_event(struct rayo_client *rclient, switch_event_t *e { const char *call_direction = switch_event_get_header(event, "Call-Direction"); if (call_direction && !strcmp(call_direction, "outbound")) { - struct rayo_call *call = RAYO_CALL_LOCATE(switch_event_get_header(event, "Unique-ID")); + struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(switch_event_get_header(event, "Unique-ID")); if (call) { switch_mutex_lock(RAYO_ACTOR(call)->mutex); if (!call->ringing_sent) { @@ -2635,7 +2657,7 @@ static void on_call_bridge_event(struct rayo_client *rclient, switch_event_t *ev { const char *a_uuid = switch_event_get_header(event, "Unique-ID"); const char *b_uuid = switch_event_get_header(event, "Bridge-B-Unique-ID"); - struct rayo_call *call = RAYO_CALL_LOCATE(a_uuid); + struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(a_uuid); struct rayo_call *b_call; if (call) { @@ -2644,7 +2666,7 @@ static void on_call_bridge_event(struct rayo_client *rclient, switch_event_t *ev switch_event_get_header(event, "variable_rayo_call_jid"), switch_event_get_header(event, "variable_rayo_dcp_jid")); iks *joined = iks_find(revent, "joined"); - iks_insert_attrib(joined, "call-uri", b_uuid); + iks_insert_attrib_printf(joined, "call-uri", "xmpp:%s@%s", b_uuid, RAYO_JID(globals.server)); call->joined = JOINED_CALL; call->joined_id = switch_core_strdup(RAYO_POOL(call), b_uuid); @@ -2652,11 +2674,11 @@ static void on_call_bridge_event(struct rayo_client *rclient, switch_event_t *ev RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent); /* send B-leg event */ - b_call = RAYO_CALL_LOCATE(b_uuid); + b_call = RAYO_CALL_LOCATE_BY_ID(b_uuid); if (b_call) { revent = iks_new_presence("joined", RAYO_NS, RAYO_JID(b_call), rayo_call_get_dcp_jid(b_call)); joined = iks_find(revent, "joined"); - iks_insert_attrib(joined, "call-uri", a_uuid); + iks_insert_attrib_printf(joined, "call-uri", "xmpp:%s@%s", a_uuid, RAYO_JID(globals.server)); b_call->joined = JOINED_CALL; b_call->joined_id = switch_core_strdup(RAYO_POOL(b_call), a_uuid); @@ -2677,7 +2699,7 @@ static void on_call_unbridge_event(struct rayo_client *rclient, switch_event_t * { const char *a_uuid = switch_event_get_header(event, "Unique-ID"); const char *b_uuid = switch_event_get_header(event, "Bridge-B-Unique-ID"); - struct rayo_call *call = RAYO_CALL_LOCATE(a_uuid); + struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(a_uuid); struct rayo_call *b_call; if (call) { @@ -2686,18 +2708,18 @@ static void on_call_unbridge_event(struct rayo_client *rclient, switch_event_t * switch_event_get_header(event, "variable_rayo_call_jid"), switch_event_get_header(event, "variable_rayo_dcp_jid")); iks *joined = iks_find(revent, "unjoined"); - iks_insert_attrib(joined, "call-uri", b_uuid); + iks_insert_attrib_printf(joined, "call-uri", "xmpp:%s@%s", b_uuid, RAYO_JID(globals.server)); RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent); call->joined = 0; call->joined_id = NULL; /* send B-leg event */ - b_call = RAYO_CALL_LOCATE(b_uuid); + b_call = RAYO_CALL_LOCATE_BY_ID(b_uuid); if (b_call) { revent = iks_new_presence("unjoined", RAYO_NS, RAYO_JID(b_call), rayo_call_get_dcp_jid(b_call)); joined = iks_find(revent, "unjoined"); - iks_insert_attrib(joined, "call-uri", a_uuid); + iks_insert_attrib_printf(joined, "call-uri", "xmpp:%s@%s", a_uuid, RAYO_JID(globals.server)); RAYO_SEND_MESSAGE(b_call, rayo_call_get_dcp_jid(b_call), revent); b_call->joined = 0; @@ -3077,6 +3099,21 @@ static void on_xmpp_stream_destroy(struct xmpp_stream *stream) } } +/** + * Add an alias to an API command + * @param alias_name + * @param alias_target + * @param alias_cmd + */ +static void rayo_add_cmd_alias(const char *alias_name, const char *alias_target, const char *alias_cmd) +{ + if (zstr(alias_target)) { + alias_target = "all"; + } + switch_console_set_complete(switch_core_sprintf(globals.pool, "add rayo %s ::rayo::list_%s", alias_name, alias_target)); + switch_core_hash_insert(globals.cmd_aliases, alias_name, alias_cmd); +} + /** * Process module XML configuration * @param pool memory pool to allocate from @@ -3262,6 +3299,22 @@ static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_ } } + /* get aliases */ + { + switch_xml_t aliases = switch_xml_child(cfg, "aliases"); + if (aliases) { + switch_xml_t alias; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Setting configured aliases\n"); + for (alias = switch_xml_child(aliases, "alias"); alias; alias = alias->next) { + const char *alias_name = switch_xml_attr_soft(alias, "name"); + const char *alias_target = switch_xml_attr_soft(alias, "target"); + if (!zstr(alias_name) && !zstr(alias->txt)) { + rayo_add_cmd_alias(alias_name, alias_target, alias->txt); + } + } + } + } + done: switch_xml_free(xml); @@ -3360,12 +3413,6 @@ static void send_console_command(struct rayo_client *client, const char *to, con iks *command = NULL; iksparser *p = iks_dom_new(&command); - /* check if aliased */ - const char *alias = switch_core_hash_find(globals.cmd_aliases, command_str); - if (!zstr(alias)) { - command_str = alias; - } - if (iks_parse(p, command_str, 0, 1) == IKS_OK && command) { char *str; iks *iq = NULL; @@ -3404,14 +3451,15 @@ static void send_console_command(struct rayo_client *client, const char *to, con /** * Send command to rayo actor */ -static int command_api(const char *cmd, switch_stream_handle_t *stream) +static int command_api(char *cmd, switch_stream_handle_t *stream) { - char *cmd_dup = strdup(cmd); char *argv[2] = { 0 }; - int argc = switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0])); - - if (argc != 2) { - free(cmd_dup); + if (!zstr(cmd)) { + int argc = switch_separate_string(cmd, ' ', argv, sizeof(argv) / sizeof(argv[0])); + if (argc != 2) { + return 0; + } + } else { return 0; } @@ -3419,7 +3467,22 @@ static int command_api(const char *cmd, switch_stream_handle_t *stream) send_console_command(globals.console, argv[0], argv[1]); stream->write_function(stream, "+OK\n"); - free(cmd_dup); + return 1; +} + +/** + * Send command to rayo actor + */ +static int alias_api(const char *cmd, char *jid, switch_stream_handle_t *stream) +{ + if (zstr(cmd) || zstr(jid)) { + return 0; + } + + /* send command */ + send_console_command(globals.console, jid, cmd); + stream->write_function(stream, "+OK\n"); + return 1; } @@ -3443,14 +3506,15 @@ static void send_console_message(struct rayo_client *client, const char *to, con /** * Send message to rayo actor */ -static int message_api(const char *msg, switch_stream_handle_t *stream) +static int message_api(char *cmd, switch_stream_handle_t *stream) { - char *msg_dup = strdup(msg); char *argv[2] = { 0 }; - int argc = switch_separate_string(msg_dup, ' ', argv, sizeof(argv) / sizeof(argv[0])); - - if (argc != 2) { - free(msg_dup); + if (!zstr(cmd)) { + int argc = switch_separate_string(cmd, ' ', argv, sizeof(argv) / sizeof(argv[0])); + if (argc != 2) { + return 0; + } + } else { return 0; } @@ -3458,7 +3522,6 @@ static int message_api(const char *msg, switch_stream_handle_t *stream) send_console_message(globals.console, argv[0], argv[1]); stream->write_function(stream, "+OK\n"); - free(msg_dup); return 1; } @@ -3484,151 +3547,238 @@ static void send_console_presence(struct rayo_client *client, const char *to, in /** * Send console presence */ -static int presence_api(const char *cmd, switch_stream_handle_t *stream) +static int presence_api(char *cmd, switch_stream_handle_t *stream) { - char *cmd_dup = strdup(cmd); - char *argv[2] = { 0 }; - int argc = switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0])); int is_online = 0; - - if (argc != 2) { - free(cmd_dup); + char *argv[2] = { 0 }; + if (!zstr(cmd)) { + int argc = switch_separate_string(cmd, ' ', argv, sizeof(argv) / sizeof(argv[0])); + if (argc != 2) { + return 0; + } + } else { return 0; } if (!strcmp("online", argv[1])) { is_online = 1; } else if (strcmp("offline", argv[1])) { - free(cmd_dup); return 0; } /* send presence */ send_console_presence(globals.console, argv[0], is_online); stream->write_function(stream, "+OK\n"); - free(cmd_dup); return 1; } -#define RAYO_API_SYNTAX "status | (cmd ) | (msg ) | (presence )" +#define RAYO_API_SYNTAX "status | ( ) | (cmd ) | (msg ) | (presence )" SWITCH_STANDARD_API(rayo_api) { + const char *alias; + char *cmd_dup = strdup(cmd); + char *argv[2] = { 0 }; int success = 0; - if (!strncmp("status", cmd, 6)) { - success = dump_api(cmd + 6, stream); - } else if (!strncmp("cmd", cmd, 3)) { - success = command_api(cmd + 3, stream); - } else if (!strncmp("msg", cmd, 3)) { - success = message_api(cmd + 3, stream); - } else if (!strncmp("presence", cmd, 8)) { - success = presence_api(cmd + 8, stream); + + switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0])); + + /* check if a command alias */ + alias = switch_core_hash_find(globals.cmd_aliases, argv[0]); + + if (!zstr(alias)) { + success = alias_api(alias, argv[1], stream); + } else if (!strcmp("cmd", argv[0])) { + success = command_api(argv[1], stream); + } else if (!strcmp("status", argv[0])) { + success = dump_api(argv[1], stream); + } else if (!strcmp("msg", argv[0])) { + success = message_api(argv[1], stream); + } else if (!strcmp("presence", argv[0])) { + success = presence_api(argv[1], stream); } if (!success) { stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_API_SYNTAX); } + free(cmd_dup); + return SWITCH_STATUS_SUCCESS; } +/** + * Console auto-completion for actors given validation function + */ +static switch_status_t list_actors(const char *line, const char *cursor, switch_console_callback_match_t **matches, rayo_actor_match_fn match) +{ + switch_hash_index_t *hi; + void *val; + const void *vvar; + switch_console_callback_match_t *my_matches = NULL; + switch_status_t status = SWITCH_STATUS_FALSE; + struct rayo_actor *actor; + + switch_mutex_lock(globals.actors_mutex); + for (hi = switch_hash_first(NULL, globals.actors); hi; hi = switch_hash_next(hi)) { + switch_hash_this(hi, &vvar, NULL, &val); + + actor = (struct rayo_actor *) val; + if (match(actor)) { + switch_console_push_match(&my_matches, (const char *) vvar); + } + } + switch_mutex_unlock(globals.actors_mutex); + + if (my_matches) { + *matches = my_matches; + status = SWITCH_STATUS_SUCCESS; + } + + return status; +} + +/** + * @return true if internal actor + */ +static switch_bool_t is_internal_actor(struct rayo_actor *actor) +{ + return strcmp(RAT_CLIENT, actor->type) && strcmp(RAT_PEER_SERVER, actor->type); +} + /** * Console auto-completion for all internal actors */ -switch_status_t list_internal(const char *line, const char *cursor, switch_console_callback_match_t **matches) +static switch_status_t list_internal(const char *line, const char *cursor, switch_console_callback_match_t **matches) { - switch_hash_index_t *hi; - void *val; - const void *vvar; - switch_console_callback_match_t *my_matches = NULL; - switch_status_t status = SWITCH_STATUS_FALSE; - struct rayo_actor *actor; + return list_actors(line, cursor, matches, is_internal_actor); +} - switch_mutex_lock(globals.actors_mutex); - for (hi = switch_hash_first(NULL, globals.actors); hi; hi = switch_hash_next(hi)) { - switch_hash_this(hi, &vvar, NULL, &val); - - actor = (struct rayo_actor *) val; - if (strcmp(RAT_CLIENT, actor->type) && strcmp(RAT_PEER_SERVER, actor->type)) { - switch_console_push_match(&my_matches, (const char *) vvar); - } - } - switch_mutex_unlock(globals.actors_mutex); - - if (my_matches) { - *matches = my_matches; - status = SWITCH_STATUS_SUCCESS; - } - - return status; +/** + * @return true if external actor + */ +static switch_bool_t is_external_actor(struct rayo_actor *actor) +{ + return !strcmp(RAT_CLIENT, actor->type) || !strcmp(RAT_PEER_SERVER, actor->type); } /** * Console auto-completion for all external actors */ -switch_status_t list_external(const char *line, const char *cursor, switch_console_callback_match_t **matches) +static switch_status_t list_external(const char *line, const char *cursor, switch_console_callback_match_t **matches) { - switch_hash_index_t *hi; - void *val; - const void *vvar; - switch_console_callback_match_t *my_matches = NULL; - switch_status_t status = SWITCH_STATUS_FALSE; - struct rayo_actor *actor; + return list_actors(line, cursor, matches, is_external_actor); +} - switch_mutex_lock(globals.actors_mutex); - for (hi = switch_hash_first(NULL, globals.actors); hi; hi = switch_hash_next(hi)) { - switch_hash_this(hi, &vvar, NULL, &val); - - actor = (struct rayo_actor *) val; - if (!strcmp(RAT_CLIENT, actor->type) || !strcmp(RAT_PEER_SERVER, actor->type)) { - switch_console_push_match(&my_matches, (const char *) vvar); - } - } - switch_mutex_unlock(globals.actors_mutex); - - if (my_matches) { - *matches = my_matches; - status = SWITCH_STATUS_SUCCESS; - } - - return status; +/** + * @return true + */ +static switch_bool_t is_any_actor(struct rayo_actor *actor) +{ + return SWITCH_TRUE; } /** * Console auto-completion for all actors */ -switch_status_t list_all(const char *line, const char *cursor, switch_console_callback_match_t **matches) +static switch_status_t list_all(const char *line, const char *cursor, switch_console_callback_match_t **matches) { - switch_hash_index_t *hi; - void *val; - const void *vvar; - switch_console_callback_match_t *my_matches = NULL; - switch_status_t status = SWITCH_STATUS_FALSE; - - switch_mutex_lock(globals.actors_mutex); - for (hi = switch_hash_first(NULL, globals.actors); hi; hi = switch_hash_next(hi)) { - switch_hash_this(hi, &vvar, NULL, &val); - switch_console_push_match(&my_matches, (const char *) vvar); - } - switch_mutex_unlock(globals.actors_mutex); - - if (my_matches) { - *matches = my_matches; - status = SWITCH_STATUS_SUCCESS; - } - - return status; + return list_actors(line, cursor, matches, is_any_actor); } /** - * Add an alias to an API command - * @param alias_name - * @param alias_cmd + * @return true if a server */ -static void rayo_add_cmd_alias(const char *alias_name, const char *alias_cmd) +static switch_bool_t is_server_actor(struct rayo_actor *actor) { - char *cmd = switch_core_sprintf(globals.pool, "add rayo cmd ::rayo::list_actors %s", alias_name); - switch_console_set_complete(cmd); - switch_core_hash_insert(globals.cmd_aliases, alias_name, alias_cmd); + return !strcmp(RAT_SERVER, actor->type); +} + +/** + * Console auto-completion for all servers + */ +static switch_status_t list_server(const char *line, const char *cursor, switch_console_callback_match_t **matches) +{ + return list_actors(line, cursor, matches, is_server_actor); +} + +/** + * @return true if a call + */ +static switch_bool_t is_call_actor(struct rayo_actor *actor) +{ + return !strcmp(RAT_CALL, actor->type); +} + +/** + * Console auto-completion for all calls + */ +static switch_status_t list_call(const char *line, const char *cursor, switch_console_callback_match_t **matches) +{ + return list_actors(line, cursor, matches, is_call_actor); +} + +/** + * @return true if a component + */ +switch_bool_t is_component_actor(struct rayo_actor *actor) +{ + return !strncmp(RAT_COMPONENT, actor->type, strlen(RAT_COMPONENT)); +} + +/** + * Console auto-completion for all components + */ +static switch_status_t list_component(const char *line, const char *cursor, switch_console_callback_match_t **matches) +{ + return list_actors(line, cursor, matches, is_component_actor); +} + +/** + * @return true if a record component + */ +static switch_bool_t is_record_actor(struct rayo_actor *actor) +{ + return is_component_actor(actor) && !strcmp(actor->subtype, "record"); +} + +/** + * Console auto-completion for all components + */ +static switch_status_t list_record(const char *line, const char *cursor, switch_console_callback_match_t **matches) +{ + return list_actors(line, cursor, matches, is_record_actor); +} + +/** + * @return true if an output component + */ +static switch_bool_t is_output_actor(struct rayo_actor *actor) +{ + return is_component_actor(actor) && !strcmp(actor->subtype, "output"); +} + +/** + * Console auto-completion for all components + */ +static switch_status_t list_output(const char *line, const char *cursor, switch_console_callback_match_t **matches) +{ + return list_actors(line, cursor, matches, is_output_actor); +} + +/** + * @return true if an input component + */ +static switch_bool_t is_input_actor(struct rayo_actor *actor) +{ + return is_component_actor(actor) && !strcmp(actor->subtype, "input"); +} + +/** + * Console auto-completion for all components + */ +static switch_status_t list_input(const char *line, const char *cursor, switch_console_callback_match_t **matches) +{ + return list_actors(line, cursor, matches, is_input_actor); } /** @@ -3708,98 +3858,20 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load) globals.console = rayo_console_client_create(); switch_console_set_complete("add rayo status"); - switch_console_set_complete("add rayo cmd ::rayo::list_internal"); switch_console_set_complete("add rayo msg ::rayo::list_external"); - switch_console_set_complete("add rayo presence ::rayo::list_all online"); - switch_console_set_complete("add rayo presence ::rayo::list_all offline"); + switch_console_set_complete("add rayo cmd ::rayo::list_all"); + switch_console_set_complete("add rayo presence ::rayo::list_server online"); + switch_console_set_complete("add rayo presence ::rayo::list_server offline"); + switch_console_add_complete_func("::rayo::list_all", list_all); switch_console_add_complete_func("::rayo::list_internal", list_internal); switch_console_add_complete_func("::rayo::list_external", list_external); - switch_console_add_complete_func("::rayo::list_all", list_all); + switch_console_add_complete_func("::rayo::list_server", list_server); + switch_console_add_complete_func("::rayo::list_call", list_call); + switch_console_add_complete_func("::rayo::list_component", list_component); + switch_console_add_complete_func("::rayo::list_record", list_record); + switch_console_add_complete_func("::rayo::list_output", list_output); + switch_console_add_complete_func("::rayo::list_input", list_input); - rayo_add_cmd_alias("ping", ""); - rayo_add_cmd_alias("answer", ""); - rayo_add_cmd_alias("hangup", ""); - rayo_add_cmd_alias("stop", ""); - rayo_add_cmd_alias("pause", ""); - rayo_add_cmd_alias("resume", ""); - rayo_add_cmd_alias("speed-up", ""); - rayo_add_cmd_alias("speed-down", ""); - rayo_add_cmd_alias("volume-up", ""); - rayo_add_cmd_alias("volume-down", ""); - rayo_add_cmd_alias("record", ""); - rayo_add_cmd_alias("record_pause", ""); - rayo_add_cmd_alias("record_resume", ""); - rayo_add_cmd_alias("prompt_barge", "" - "

Please press a digit.

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

Please press a digit.

]]>
" - "" - "" - "0123456789]]>" - "" - "
"); - - rayo_add_cmd_alias("prompt_long", "" - "" - "" - "" - "0123456789]]>" - "" - ""); - - rayo_add_cmd_alias("prompt_multi_digit", "" - "" - "" - "" - "0123456789]]>" - "" - ""); - - rayo_add_cmd_alias("prompt_terminator", "" - "" - "" - "" - "0123456789]]>" - "" - ""); - - rayo_add_cmd_alias("prompt_input_bad", "" - "" - "" - "" - "0123456789]]>" - "" - ""); - - rayo_add_cmd_alias("prompt_output_bad", "" - "" - "" - "" - "0123456789]]>" - "" - ""); - rayo_add_cmd_alias("input", "" - "" - "0123456789*#]]>" - ""); - rayo_add_cmd_alias("output_bad", - ""); - rayo_add_cmd_alias("join_mixer_duplex", - ""); - rayo_add_cmd_alias("join_mixer_send", - ""); - rayo_add_cmd_alias("join_mixer_recv", - ""); - rayo_add_cmd_alias("unjoin_mixer", - ""); - rayo_add_cmd_alias("unjoin", - ""); return SWITCH_STATUS_SUCCESS; } @@ -3808,9 +3880,15 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load) */ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_rayo_shutdown) { + switch_console_del_complete_func("::rayo::list_all"); switch_console_del_complete_func("::rayo::list_internal"); switch_console_del_complete_func("::rayo::list_external"); - switch_console_del_complete_func("::rayo::list_all"); + switch_console_del_complete_func("::rayo::list_server"); + switch_console_del_complete_func("::rayo::list_call"); + switch_console_del_complete_func("::rayo::list_component"); + switch_console_del_complete_func("::rayo::list_record"); + switch_console_del_complete_func("::rayo::list_output"); + switch_console_del_complete_func("::rayo::list_input"); switch_console_set_complete("del rayo"); /* stop XMPP streams */ diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.h b/src/mod/event_handlers/mod_rayo/mod_rayo.h index f4602cc6b9..dd0adb3192 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.h +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.h @@ -51,9 +51,6 @@ #define RAT_PEER_SERVER "PEER_SERVER" #define RAT_CLIENT "CLIENT" -/* these are support punchblock.. undefine once punchblock is fixed */ -#define RAYO_UUID_IN_REF_URI - struct rayo_actor; struct rayo_call; struct rayo_mixer; @@ -162,6 +159,7 @@ extern const char *rayo_call_get_dcp_jid(struct rayo_call *call); #define rayo_component_init(component, pool, type, subtype, id, parent, client_jid) _rayo_component_init(component, pool, type, subtype, id, parent, client_jid, __FILE__, __LINE__) extern struct rayo_component *_rayo_component_init(struct rayo_component *component, switch_memory_pool_t *pool, const char *type, const char *subtype, const char *id, struct rayo_actor *parent, const char *client_jid, const char *file, int line); +extern switch_bool_t is_component_actor(struct rayo_actor *); typedef iks *(*rayo_actor_xmpp_handler)(struct rayo_actor *, struct rayo_message *, void *); extern void rayo_actor_command_handler_add(const char *type, const char *subtype, const char *name, rayo_actor_xmpp_handler fn); diff --git a/src/mod/event_handlers/mod_rayo/nlsml.c b/src/mod/event_handlers/mod_rayo/nlsml.c index e446d1bfd0..b5e9cefa4f 100644 --- a/src/mod/event_handlers/mod_rayo/nlsml.c +++ b/src/mod/event_handlers/mod_rayo/nlsml.c @@ -412,7 +412,6 @@ iks *nlsml_create_dtmf_match(const char *digits) iks *interpretation = iks_insert(result, "interpretation"); iks *input = iks_insert(interpretation, "input"); - input = iks_insert(input, "input"); iks_insert_attrib(input, "mode", "dtmf"); iks_insert_attrib(input, "confidence", "100"); diff --git a/src/mod/event_handlers/mod_rayo/rayo_components.c b/src/mod/event_handlers/mod_rayo/rayo_components.c index 54e241d036..b5137d9459 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_components.c +++ b/src/mod/event_handlers/mod_rayo/rayo_components.c @@ -40,7 +40,7 @@ struct rayo_component *rayo_component_locate(const char *id, const char *file, int line) { struct rayo_actor *actor = rayo_actor_locate_by_id(id, file, line); - if (actor && !strncmp(RAT_COMPONENT, actor->type, strlen(RAT_COMPONENT))) { + if (actor && is_component_actor(actor)) { return RAYO_COMPONENT(actor); } else if (actor) { RAYO_UNLOCK(actor); @@ -58,11 +58,7 @@ void rayo_component_send_start(struct rayo_component *component, iks *iq) iks *response = iks_new_iq_result(iq); iks *ref = iks_insert(response, "ref"); iks_insert_attrib(ref, "xmlns", RAYO_NS); -#ifdef RAYO_UUID_IN_REF_URI - iks_insert_attrib(ref, "uri", component->ref); -#else iks_insert_attrib_printf(ref, "uri", "xmpp:%s", RAYO_JID(component)); -#endif RAYO_SEND_REPLY(component, iks_find_attrib(response, "to"), response); } @@ -227,15 +223,10 @@ void rayo_component_api_execute_async(struct rayo_component *component, const ch */ switch_status_t rayo_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) { - rayo_input_component_load(); - rayo_output_component_load(module_interface, pool); - rayo_prompt_component_load(); - rayo_record_component_load(pool, config_file); - - if (rayo_input_component_load() != SWITCH_STATUS_SUCCESS || - rayo_output_component_load(module_interface, pool) != SWITCH_STATUS_SUCCESS || - rayo_prompt_component_load() != SWITCH_STATUS_SUCCESS || - rayo_record_component_load(pool, config_file) != SWITCH_STATUS_SUCCESS) { + if (rayo_input_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS || + rayo_output_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS || + rayo_prompt_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS || + rayo_record_component_load(module_interface, pool, config_file) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_TERM; } return SWITCH_STATUS_SUCCESS; diff --git a/src/mod/event_handlers/mod_rayo/rayo_components.h b/src/mod/event_handlers/mod_rayo/rayo_components.h index 71891ea3d6..6e93dfbc43 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_components.h +++ b/src/mod/event_handlers/mod_rayo/rayo_components.h @@ -54,10 +54,10 @@ #define COMPONENT_COMPLETE_HANGUP "hangup", RAYO_EXT_COMPLETE_NS extern switch_status_t rayo_components_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); -extern switch_status_t rayo_input_component_load(void); -extern switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool); -extern switch_status_t rayo_prompt_component_load(void); -extern switch_status_t rayo_record_component_load(switch_memory_pool_t *pool, const char *config_file); +extern switch_status_t rayo_input_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); +extern switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); +extern switch_status_t rayo_prompt_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); +extern switch_status_t rayo_record_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file); extern switch_status_t rayo_components_shutdown(void); extern switch_status_t rayo_input_component_shutdown(void); diff --git a/src/mod/event_handlers/mod_rayo/rayo_elements.c b/src/mod/event_handlers/mod_rayo/rayo_elements.c index 89e31c7d66..34577a9492 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_elements.c +++ b/src/mod/event_handlers/mod_rayo/rayo_elements.c @@ -33,7 +33,7 @@ */ ELEMENT(RAYO_INPUT) STRING_ATTRIB(mode, any, "any,dtmf,voice") - ATTRIB(terminator,, any) + OPTIONAL_ATTRIB(terminator,, dtmf_digit) ATTRIB(recognizer,, any) ATTRIB(language, en-US, any) ATTRIB(initial-timeout, -1, positive_or_neg_one) diff --git a/src/mod/event_handlers/mod_rayo/rayo_input_component.c b/src/mod/event_handlers/mod_rayo/rayo_input_component.c index 9e10d1b7a5..3bf3b6dfb6 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_input_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_input_component.c @@ -45,6 +45,8 @@ struct input_handler; static struct { /** grammar parser */ struct srgs_parser *parser; + /** default recognizer to use if none specified */ + const char *default_recognizer; } globals; /** @@ -57,8 +59,8 @@ struct input_component { int speech_mode; /** Number of collected digits */ int num_digits; - /** Terminating digits */ - int term_digit_mask; + /** Terminating digit */ + char term_digit; /** The collected digits */ char digits[MAX_DTMF + 1]; /** grammar to match */ @@ -70,7 +72,9 @@ struct input_component { /** maximum silence allowed */ int max_silence; /** minimum speech detection confidence */ - int min_confidence; + double min_confidence; + /** sensitivity to background noise */ + double sensitivity; /** timeout after first digit is received */ int inter_digit_timeout; /** stop flag */ @@ -79,6 +83,10 @@ struct input_component { int start_timers; /** true if event fired for first digit / start of speech */ int barge_event; + /** optional language to use */ + const char *language; + /** optional recognizer to use */ + const char *recognizer; /** global data */ struct input_handler *handler; }; @@ -91,77 +99,24 @@ struct input_component { struct input_handler { /** media bug to monitor frames / control input lifecycle */ switch_media_bug_t *bug; - /** active input component - TODO multiple inputs */ - struct input_component *component; + /** active voice input component */ + struct input_component *voice_component; + /** active dtmf input component */ + struct input_component *dtmf_component; /** synchronizes media bug and dtmf callbacks */ switch_mutex_t *mutex; + /** last recognizer used */ + const char *last_recognizer; }; /** - * @return digit mask + * @param digit1 to match + * @param digit2 to match + * @return true if matching */ -static int get_digit_mask(char digit) +static int digit_test(char digit1, char digit2) { - switch(digit) { - case '0': return 1; - case '1': return 1 << 1; - case '2': return 1 << 2; - case '3': return 1 << 3; - case '4': return 1 << 4; - case '5': return 1 << 5; - case '6': return 1 << 6; - case '7': return 1 << 7; - case '8': return 1 << 8; - case '9': return 1 << 9; - case 'A': - case 'a': return 1 << 10; - case 'B': - case 'b': return 1 << 11; - case 'C': - case 'c': return 1 << 12; - case 'D': - case 'd': return 1 << 13; - case '#': return 1 << 14; - case '*': return 1 << 15; - } - return 0; -} - -/** - * @param digit_mask to check - * @param digit to look for - * @return true if set - */ -static int digit_mask_test(int digit_mask, char digit) -{ - return digit_mask & get_digit_mask(digit); -} - -/** - * @param digit_mask to set digit in - * @param digit to set - * @return the digit mask with the set digit - */ -static int digit_mask_set(int digit_mask, char digit) -{ - return digit_mask | get_digit_mask(digit); -} - -/** - * @param digit_mask to set digits in - * @param digits to add to mask - * @return the digit mask with the set digits - */ -static int digit_mask_set_from_digits(int digit_mask, const char *digits) -{ - if (!zstr(digits)) { - int digits_len = strlen(digits); - int i; - for (i = 0; i < digits_len; i++) { - digit_mask = digit_mask_set(digit_mask, digits[i]); - } - } - return digit_mask; + return digit1 && digit2 && tolower(digit1) == tolower(digit2); } /** @@ -205,15 +160,14 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c switch_mutex_lock(handler->mutex); - component = handler->component; + component = handler->dtmf_component; /* additional paranoia check */ if (!component) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Received DTMF without active input component\n"); switch_mutex_unlock(handler->mutex); return SWITCH_STATUS_SUCCESS; } - is_term_digit = digit_mask_test(component->term_digit_mask, dtmf->digit); + is_term_digit = digit_test(component->term_digit, dtmf->digit); if (!is_term_digit) { component->digits[component->num_digits] = dtmf->digit; @@ -247,7 +201,7 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c } case SMT_NO_MATCH: { /* notify of no-match and remove input component */ - handler->component = NULL; + handler->dtmf_component = NULL; switch_core_media_bug_remove(session, &handler->bug); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "NO MATCH = %s\n", component->digits); rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOMATCH); @@ -256,7 +210,7 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c case SMT_MATCH_END: { iks *result = nlsml_create_dtmf_match(component->digits); /* notify of match and remove input component */ - handler->component = NULL; + handler->dtmf_component = NULL; switch_core_media_bug_remove(session, &handler->bug); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "MATCH = %s\n", component->digits); send_match_event(RAYO_COMPONENT(component), result); @@ -279,7 +233,7 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void struct input_component *component; switch_mutex_lock(handler->mutex); - component = handler->component; + component = handler->dtmf_component; switch(type) { case SWITCH_ABC_TYPE_INIT: { @@ -294,7 +248,7 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void int elapsed_ms = (switch_micro_time_now() - component->last_digit_time) / 1000; if (component->num_digits && component->inter_digit_timeout > 0 && elapsed_ms > component->inter_digit_timeout) { enum srgs_match_type match; - handler->component = NULL; + handler->dtmf_component = NULL; switch_core_media_bug_set_flag(bug, SMBF_PRUNE); /* we got some input, check for match */ @@ -310,7 +264,7 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOMATCH); } } else if (!component->num_digits && component->initial_timeout > 0 && elapsed_ms > component->initial_timeout) { - handler->component = NULL; + handler->dtmf_component = NULL; switch_core_media_bug_set_flag(bug, SMBF_PRUNE); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "initial-timeout\n"); rayo_component_send_complete(RAYO_COMPONENT(component), INPUT_NOINPUT); @@ -323,10 +277,10 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void /* check for hangup */ if (component) { if (component->stop) { - handler->component = NULL; + handler->dtmf_component = NULL; rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP); } else { - handler->component = NULL; + handler->dtmf_component = NULL; rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_HANGUP); } } @@ -396,34 +350,53 @@ static iks *start_call_input(struct input_component *component, switch_core_sess handler = switch_core_session_alloc(session, sizeof(*handler)); switch_mutex_init(&handler->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); switch_channel_set_private(switch_core_session_get_channel(session), RAYO_INPUT_COMPONENT_PRIVATE_VAR, handler); + handler->last_recognizer = ""; } - handler->component = component; + + /* TODO break up this function by mode... dtmf/voice/fax/etc */ + component->speech_mode = strcmp(iks_find_attrib_soft(input, "mode"), "dtmf"); + if (component->speech_mode && handler->voice_component) { + /* don't allow multi voice input */ + return iks_new_error_detailed(iq, STANZA_ERROR_CONFLICT, "Multiple voice input is not allowed"); + } + if (!component->speech_mode && handler->dtmf_component) { + /* don't allow multi dtmf input */ + return iks_new_error_detailed(iq, STANZA_ERROR_CONFLICT, "Multiple dtmf input is not allowed"); + } + + if (component->speech_mode) { + handler->voice_component = component; + } else { + handler->dtmf_component = component; + } + + component->grammar = NULL; component->num_digits = 0; component->digits[0] = '\0'; component->stop = 0; - component->speech_mode = 0; component->initial_timeout = iks_find_int_attrib(input, "initial-timeout"); component->inter_digit_timeout = iks_find_int_attrib(input, "inter-digit-timeout"); component->max_silence = iks_find_int_attrib(input, "max-silence"); - component->min_confidence = (int)ceil(iks_find_decimal_attrib(input, "min-confidence") * 100.0); + component->min_confidence = iks_find_decimal_attrib(input, "min-confidence"); + component->sensitivity = iks_find_decimal_attrib(input, "sensitivity"); component->barge_event = iks_find_bool_attrib(input, "barge-event"); component->start_timers = iks_find_bool_attrib(input, "start-timers"); - /* TODO this should just be a single digit terminator? */ - component->term_digit_mask = digit_mask_set_from_digits(0, iks_find_attrib_soft(input, "terminator")); - /* TODO recognizer ignored */ - /* TODO language ignored */ + component->term_digit = iks_find_char_attrib(input, "terminator"); + component->recognizer = iks_find_attrib(input, "recognizer"); + component->language = iks_find_attrib(input, "language"); component->handler = handler; - /* parse the grammar */ - if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Failed to parse grammar body\n"); - RAYO_UNLOCK(component); - RAYO_DESTROY(component); - return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Failed to parse grammar body"); - } - /* is this voice or dtmf srgs grammar? */ - if (!strcasecmp("dtmf", iks_find_attrib_soft(input, "mode"))) { + if (!component->speech_mode) { + + /* parse the grammar */ + if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Failed to parse grammar body\n"); + RAYO_UNLOCK(component); + RAYO_DESTROY(component); + return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Failed to parse grammar body"); + } + component->last_digit_time = switch_micro_time_now(); /* acknowledge command */ @@ -431,38 +404,124 @@ static iks *start_call_input(struct input_component *component, switch_core_sess /* start dtmf input detection */ if (switch_core_media_bug_add(session, "rayo_input_component", NULL, input_component_bug_callback, handler, 0, SMBF_READ_REPLACE, &handler->bug) != SWITCH_STATUS_SUCCESS) { + handler->dtmf_component = NULL; rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR); } } else { - char *grammar = NULL; - const char *jsgf_path; - component->speech_mode = 1; - jsgf_path = srgs_grammar_to_jsgf_file(component->grammar, SWITCH_GLOBAL_dirs.grammar_dir, "gram"); - if (!jsgf_path) { + switch_stream_handle_t grammar = { 0 }; + SWITCH_STANDARD_STREAM(grammar); + + if (zstr(component->recognizer)) { + component->recognizer = globals.default_recognizer; + } + + /* if recognition engine is different, we can't handle this request */ + if (!zstr(handler->last_recognizer) && strcmp(component->recognizer, handler->last_recognizer)) { + handler->voice_component = NULL; RAYO_UNLOCK(component); RAYO_DESTROY(component); - return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Grammar error"); + return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Must use the same recognizer for the entire call"); + } + handler->last_recognizer = switch_core_session_strdup(session, component->recognizer); + + if (!strcmp(component->recognizer, "pocketsphinx")) { + const char *jsgf_path; + + /* transform SRGS grammar to JSGF */ + if (!(component->grammar = srgs_parse(globals.parser, iks_find_cdata(input, "grammar")))) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Failed to parse grammar body\n"); + handler->voice_component = NULL; + RAYO_UNLOCK(component); + RAYO_DESTROY(component); + return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Failed to parse grammar body"); + } + jsgf_path = srgs_grammar_to_jsgf_file(component->grammar, SWITCH_GLOBAL_dirs.grammar_dir, "gram"); + if (!jsgf_path) { + handler->voice_component = NULL; + RAYO_UNLOCK(component); + RAYO_DESTROY(component); + return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Grammar conversion to JSGF error"); + } + + /* build pocketsphinx grammar string */ + grammar.write_function(&grammar, + "{start-input-timers=%s,no-input-timeout=%d,speech-timeout=%d,confidence-threshold=%d}%s", + component->start_timers ? "true" : "false", + component->initial_timeout, + component->max_silence, + (int)ceil(component->min_confidence * 100.0), + jsgf_path); + } else if (!strncmp(component->recognizer, "unimrcp", strlen("unimrcp"))) { + /* send inline grammar to unimrcp */ + grammar.write_function(&grammar, "{start-input-timers=%s,confidence-threshold=%f,sensitivity-level=%f", + component->start_timers ? "true" : "false", + component->min_confidence, + component->sensitivity); + + if (component->initial_timeout > 0) { + grammar.write_function(&grammar, ",no-input-timeout=%d", + component->initial_timeout); + } + + if (component->max_silence > 0) { + grammar.write_function(&grammar, ",speech-complete-timeout=%d,speech-incomplete-timeout=%d", + component->max_silence, + component->max_silence); + } + + if (!zstr(component->language)) { + grammar.write_function(&grammar, ",speech-language=%s", component->language); + } + + if (!strcmp(iks_find_attrib_soft(input, "mode"), "any")) { + /* set dtmf params */ + if (component->inter_digit_timeout > 0) { + grammar.write_function(&grammar, ",dtmf-interdigit-timeout=%d", component->inter_digit_timeout); + } + if (component->term_digit) { + grammar.write_function(&grammar, ",dtmf-term-char=%c", component->term_digit); + } + } + + grammar.write_function(&grammar, "}inline:%s", iks_find_cdata(input, "grammar")); + } else { + /* passthrough to unknown ASR module */ + grammar.write_function(&grammar, "%s", iks_find_cdata(input, "grammar")); } /* acknowledge command */ rayo_component_send_start(RAYO_COMPONENT(component), iq); - /* TODO configurable speech detection - different engines, grammar passthrough, dtmf handled by recognizer */ - grammar = switch_mprintf("{no-input-timeout=%s,speech-timeout=%s,start-input-timers=%s,confidence-threshold=%d}%s", - component->initial_timeout, component->max_silence, - component->start_timers ? "true" : "false", - component->min_confidence, jsgf_path); /* start speech detection */ switch_channel_set_variable(switch_core_session_get_channel(session), "fire_asr_events", "true"); - if (switch_ivr_detect_speech(session, "pocketsphinx", grammar, "mod_rayo_grammar", "", NULL) != SWITCH_STATUS_SUCCESS) { + if (switch_ivr_detect_speech(session, component->recognizer, grammar.data, "mod_rayo_grammar", "", NULL) != SWITCH_STATUS_SUCCESS) { + handler->voice_component = NULL; rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR); } - switch_safe_free(grammar); + switch_safe_free(grammar.data); } return NULL; } +/** + * Create input component id for session. + * @param session requesting component + * @param input request + * @return the ID + */ +static char *create_input_component_id(switch_core_session_t *session, iks *input) +{ + const char *mode = "unk"; + if (input) { + mode = iks_find_attrib_soft(input, "mode"); + if (!strcmp(mode, "any")) { + mode = "voice"; + } + } + return switch_core_session_sprintf(session, "%s-input-%s", switch_core_session_get_uuid(session), mode); +} + /** * Start execution of input component */ @@ -470,10 +529,10 @@ static iks *start_call_input_component(struct rayo_actor *call, struct rayo_mess { iks *iq = msg->payload; switch_core_session_t *session = (switch_core_session_t *)session_data; - char *component_id = switch_mprintf("%s-input", switch_core_session_get_uuid(session)); + iks *input = iks_find(iq, "input"); + char *component_id = create_input_component_id(session, input); switch_memory_pool_t *pool = NULL; struct input_component *input_component = NULL; - iks *input = iks_find(iq, "input"); const char *error = NULL; if (!validate_call_input(input, &error)) { @@ -484,7 +543,6 @@ static iks *start_call_input_component(struct rayo_actor *call, struct rayo_mess switch_core_new_memory_pool(&pool); input_component = switch_core_alloc(pool, sizeof(*input_component)); rayo_component_init(RAYO_COMPONENT(input_component), pool, RAT_CALL_COMPONENT, "input", component_id, call, iks_find_attrib(iq, "from")); - switch_safe_free(component_id); /* start input */ return start_call_input(input_component, session, iks_find(iq, "input"), iq, NULL, 0); @@ -530,7 +588,6 @@ static iks *start_timers_call_input_component(struct rayo_actor *component, stru switch_mutex_lock(input_component->handler->mutex); if (input_component->speech_mode) { switch_ivr_detect_speech_start_input_timers(session); - rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP); } else { input_component->last_digit_time = switch_micro_time_now(); input_component->start_timers = 1; @@ -549,53 +606,63 @@ static void on_detected_speech_event(switch_event_t *event) { const char *speech_type = switch_event_get_header(event, "Speech-Type"); char *event_str = NULL; + const char *uuid = switch_event_get_header(event, "Unique-ID"); switch_event_serialize(event, &event_str, SWITCH_FALSE); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s\n", event_str); - if (!speech_type) { + if (!speech_type || !uuid) { return; } + if (!strcasecmp("detected-speech", speech_type)) { - const char *uuid = switch_event_get_header(event, "Unique-ID"); - char *component_id = switch_mprintf("%s-input", uuid); + char *component_id = switch_mprintf("%s-input-voice", uuid); struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id); + switch_safe_free(component_id); if (component) { const char *result = switch_event_get_body(event); switch_mutex_lock(INPUT_COMPONENT(component)->handler->mutex); - INPUT_COMPONENT(component)->handler->component = NULL; + INPUT_COMPONENT(component)->handler->voice_component = NULL; switch_mutex_unlock(INPUT_COMPONENT(component)->handler->mutex); if (zstr(result)) { rayo_component_send_complete(component, INPUT_NOMATCH); } else { - enum nlsml_match_type match_type = nlsml_parse(result, uuid); - switch (match_type) { - case NMT_NOINPUT: + if (strchr(result, '<')) { + /* got an XML result */ + enum nlsml_match_type match_type = nlsml_parse(result, uuid); + switch (match_type) { + case NMT_NOINPUT: + rayo_component_send_complete(component, INPUT_NOINPUT); + break; + case NMT_MATCH: { + iks *result_xml = nlsml_normalize(result); + send_match_event(RAYO_COMPONENT(component), result_xml); + iks_delete(result_xml); + break; + } + case NMT_BAD_XML: + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_WARNING, "Failed to parse NLSML result: %s!\n", result); + rayo_component_send_complete(component, INPUT_NOMATCH); + break; + case NMT_NOMATCH: + rayo_component_send_complete(component, INPUT_NOMATCH); + break; + default: + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_CRIT, "Unknown NLSML match type: %i, %s!\n", match_type, result); + rayo_component_send_complete(component, INPUT_NOMATCH); + break; + } + } else if (strstr(result, "002")) { + /* Completion-Cause: 002 no-input-timeout */ rayo_component_send_complete(component, INPUT_NOINPUT); - break; - case NMT_MATCH: { - iks *result_xml = nlsml_normalize(result); - send_match_event(RAYO_COMPONENT(component), result_xml); - iks_delete(result_xml); - break; - } - case NMT_BAD_XML: - switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_WARNING, "Failed to parse NLSML result: %s!\n", result); + } else { + /* assume no match */ rayo_component_send_complete(component, INPUT_NOMATCH); - break; - case NMT_NOMATCH: - rayo_component_send_complete(component, INPUT_NOMATCH); - break; - default: - switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_CRIT, "Unknown NLSML match type: %i, %s!\n", match_type, result); - rayo_component_send_complete(component, INPUT_NOMATCH); - break; } } RAYO_UNLOCK(component); } } else if (!strcasecmp("begin-speaking", speech_type)) { - const char *uuid = switch_event_get_header(event, "Unique-ID"); - char *component_id = switch_mprintf("%s-input", uuid); + char *component_id = switch_mprintf("%s-input-voice", uuid); struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id); switch_safe_free(component_id); if (component && INPUT_COMPONENT(component)->barge_event) { @@ -603,14 +670,13 @@ static void on_detected_speech_event(switch_event_t *event) } RAYO_UNLOCK(component); } else if (!strcasecmp("closed", speech_type)) { - const char *uuid = switch_event_get_header(event, "Unique-ID"); - char *component_id = switch_mprintf("%s-input", uuid); + char *component_id = switch_mprintf("%s-input-voice", uuid); struct rayo_component *component = RAYO_COMPONENT_LOCATE(component_id); switch_safe_free(component_id); if (component) { char *channel_state = switch_event_get_header(event, "Channel-State"); switch_mutex_lock(INPUT_COMPONENT(component)->handler->mutex); - INPUT_COMPONENT(component)->handler->component = NULL; + INPUT_COMPONENT(component)->handler->voice_component = NULL; switch_mutex_unlock(INPUT_COMPONENT(component)->handler->mutex); switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Recognizer closed\n"); if (channel_state && !strcmp("CS_HANGUP", channel_state)) { @@ -625,12 +691,63 @@ static void on_detected_speech_event(switch_event_t *event) switch_safe_free(event_str); } +/** + * Process module XML configuration + * @param pool memory pool to allocate from + * @param config_file to use + * @return SWITCH_STATUS_SUCCESS on successful configuration + */ +static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_file) +{ + switch_xml_t cfg, xml; + + /* set defaults */ + globals.default_recognizer = "pocketsphinx"; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Configuring module\n"); + if (!(xml = switch_xml_open_cfg(config_file, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", config_file); + return SWITCH_STATUS_TERM; + } + + /* get params */ + { + switch_xml_t settings = switch_xml_child(cfg, "input"); + if (settings) { + switch_xml_t param; + for (param = switch_xml_child(settings, "param"); param; param = param->next) { + const char *var = switch_xml_attr_soft(param, "name"); + const char *val = switch_xml_attr_soft(param, "value"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "param: %s = %s\n", var, val); + if (!strcasecmp(var, "default-recognizer")) { + if (!zstr(val)) { + globals.default_recognizer = switch_core_strdup(pool, val); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unsupported param: %s\n", var); + } + } + } + } + + switch_xml_free(xml); + + return SWITCH_STATUS_SUCCESS; +} + /** * Initialize input component + * @param module_interface + * @param pool memory pool to allocate from + * @param config_file to use * @return SWITCH_STATUS_SUCCESS if successful */ -switch_status_t rayo_input_component_load(void) +switch_status_t rayo_input_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) { + if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_TERM; + } + srgs_init(); nlsml_init(); diff --git a/src/mod/event_handlers/mod_rayo/rayo_output_component.c b/src/mod/event_handlers/mod_rayo/rayo_output_component.c index f92d994a64..e85a12cece 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_output_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_output_component.c @@ -1092,9 +1092,12 @@ static char *fileman_supported_formats[] = { "fileman", NULL }; /** * Initialize output component + * @param module_interface + * @param pool memory pool to allocate from + * @param config_file to use * @return SWITCH_STATUS_SUCCESS if successful */ -switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) +switch_status_t rayo_output_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) { switch_api_interface_t *api_interface; switch_file_interface_t *file_interface; diff --git a/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c b/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c index 47dbc46df3..f48e7d02d2 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_prompt_component.c @@ -620,9 +620,12 @@ static iks *forward_output_component_request(struct rayo_actor *prompt, struct r /** * Initialize prompt component + * @param module_interface + * @param pool memory pool to allocate from + * @param config_file to use * @return SWITCH_STATUS_SUCCESS if successful */ -switch_status_t rayo_prompt_component_load(void) +switch_status_t rayo_prompt_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) { /* Prompt is a convenience component that wraps and */ rayo_actor_command_handler_add(RAT_CALL, "", "set:"RAYO_PROMPT_NS":prompt", start_call_prompt_component); diff --git a/src/mod/event_handlers/mod_rayo/rayo_record_component.c b/src/mod/event_handlers/mod_rayo/rayo_record_component.c index 2db5b30771..601d8cb3aa 100644 --- a/src/mod/event_handlers/mod_rayo/rayo_record_component.c +++ b/src/mod/event_handlers/mod_rayo/rayo_record_component.c @@ -479,11 +479,12 @@ static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_ /** * Initialize record component + * @param module_interface * @param pool memory pool to allocate from * @param config_file to use * @return SWITCH_STATUS_SUCCESS if successful */ -switch_status_t rayo_record_component_load(switch_memory_pool_t *pool, const char *config_file) +switch_status_t rayo_record_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file) { if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_TERM; diff --git a/src/mod/event_handlers/mod_rayo/test_iks/main.c b/src/mod/event_handlers/mod_rayo/test_iks/main.c index 699f40267e..09a368dc4b 100644 --- a/src/mod/event_handlers/mod_rayo/test_iks/main.c +++ b/src/mod/event_handlers/mod_rayo/test_iks/main.c @@ -145,6 +145,27 @@ static void test_dialback_key(void) ASSERT_NULL(iks_server_dialback_key("s3cr3tf0rd14lb4ck", "xmpp.example.com", "example.org", NULL)); } +static void test_validate_dtmf(void) +{ + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("1")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("A")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("a")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("D")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("d")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("*")); + ASSERT_EQUALS(SWITCH_TRUE, iks_attrib_is_dtmf_digit("#")); + ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit("E")); + ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit(NULL)); + ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit("")); + ASSERT_EQUALS(SWITCH_FALSE, iks_attrib_is_dtmf_digit("11")); + ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "A")); + ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "1")); + ASSERT_EQUALS(SWITCH_FALSE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "Z")); + ASSERT_EQUALS(SWITCH_FALSE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "11")); + ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, NULL)); + ASSERT_EQUALS(SWITCH_TRUE, validate_optional_attrib(iks_attrib_is_dtmf_digit, "")); +} + /** * main program */ @@ -159,5 +180,6 @@ int main(int argc, char **argv) TEST(test_rayo_test_srgs); TEST(test_iks_helper_value_matches); TEST(test_dialback_key); + TEST(test_validate_dtmf); return 0; } diff --git a/src/mod/event_handlers/mod_rayo/test_nlsml/main.c b/src/mod/event_handlers/mod_rayo/test_nlsml/main.c index cc5331f55f..f5d6067d3b 100644 --- a/src/mod/event_handlers/mod_rayo/test_nlsml/main.c +++ b/src/mod/event_handlers/mod_rayo/test_nlsml/main.c @@ -244,8 +244,8 @@ static void test_parse_nlsml_examples(void) static const char *nlsml_dtmf_result = "" - "1 2 3 4" - ""; + "1 2 3 4" + ""; /** * Test parsing NLSML example results diff --git a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.c b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.c index 3db20be69b..9d9e3fbb14 100644 --- a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.c +++ b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.c @@ -428,7 +428,7 @@ static abyss_bool http_directory_auth(TSession *r, char *domain_name) if (globals.default_domain) { domain_name = globals.default_domain; } else { - if ((dup_domain = switch_core_get_variable_dup("domain"))) { + if ((dup_domain = switch_core_get_domain(SWITCH_TRUE))) { domain_name = dup_domain; } } diff --git a/src/switch_console.c b/src/switch_console.c index b1e2c8e271..eea6d5510b 100644 --- a/src/switch_console.c +++ b/src/switch_console.c @@ -33,6 +33,9 @@ #include #include #include +#ifndef _MSC_VER +#include +#endif #define CMD_BUFLEN 1024 #ifdef SWITCH_HAVE_LIBEDIT @@ -619,6 +622,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 +1664,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 +1775,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_core.c b/src/switch_core.c index b2dc5dc2ab..6529166686 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -311,6 +311,24 @@ SWITCH_DECLARE(const char *) switch_core_get_switchname(void) return runtime.hostname; } +SWITCH_DECLARE(char *) switch_core_get_domain(switch_bool_t dup) +{ + char *domain; + const char *var; + + switch_thread_rwlock_rdlock(runtime.global_var_rwlock); + if (!(var = switch_core_get_variable("domain"))) { + var = "freeswitch.local"; + } + if (dup) { + domain = strdup(var); + } else { + domain = (char *) var; + } + switch_thread_rwlock_unlock(runtime.global_var_rwlock); + + return domain; +} SWITCH_DECLARE(switch_status_t) switch_core_get_variables(switch_event_t **event) { diff --git a/src/switch_core_media.c b/src/switch_core_media.c index e3e42a60f7..b18ec859e8 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -156,6 +156,7 @@ typedef struct switch_rtp_engine_s { struct media_helper mh; switch_thread_t *media_thread; + switch_mutex_t *read_mutex; } switch_rtp_engine_t; @@ -1224,6 +1225,22 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session switch_assert(engine->rtp_session != NULL); engine->read_frame.datalen = 0; + if (!switch_channel_up_nosig(session->channel) || !switch_rtp_ready(engine->rtp_session) || switch_channel_test_flag(session->channel, CF_NOT_READY)) { + return SWITCH_STATUS_FALSE; + } + + if (switch_mutex_trylock(engine->read_mutex) != SWITCH_STATUS_SUCCESS) { + /* return CNG, another thread is already reading */ + *frame = &engine->read_frame; + switch_set_flag((*frame), SFF_CNG); + (*frame)->datalen = engine->read_impl.encoded_bytes_per_packet; + memset((*frame)->data, 0, (*frame)->datalen); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG1, "%s is already being read for %s\n", + switch_channel_get_name(session->channel), type2str(type)); + switch_yield(10000); + return SWITCH_STATUS_SUCCESS; + } + while (smh->media_flags[SCMF_RUNNING] && engine->read_frame.datalen == 0) { engine->read_frame.flags = SFF_NONE; @@ -1239,13 +1256,13 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session (*frame)->datalen = engine->read_impl.encoded_bytes_per_packet; memset((*frame)->data, 0, (*frame)->datalen); switch_channel_execute_on(session->channel, "execute_on_media_timeout"); - return SWITCH_STATUS_SUCCESS; + switch_goto_status(SWITCH_STATUS_SUCCESS, end); } switch_channel_hangup(session->channel, SWITCH_CAUSE_MEDIA_TIMEOUT); } - return status; + goto end; } /* Try to read an RTCP frame, if successful raise an event */ @@ -1322,7 +1339,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session /* Fast PASS! */ if (switch_test_flag((&engine->read_frame), SFF_PROXY_PACKET)) { *frame = &engine->read_frame; - return SWITCH_STATUS_SUCCESS; + switch_goto_status(SWITCH_STATUS_SUCCESS, end); } if (switch_rtp_has_dtmf(engine->rtp_session)) { @@ -1338,7 +1355,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session if (!switch_test_flag((&engine->read_frame), SFF_CNG)) { if (!engine->read_codec.implementation || !switch_core_codec_ready(&engine->read_codec)) { *frame = NULL; - return SWITCH_STATUS_GENERR; + switch_goto_status(SWITCH_STATUS_GENERR, end); } /* check for timing or codec issues */ @@ -1475,7 +1492,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session if (switch_rtp_ready(engine->rtp_session)) { if (switch_core_media_set_codec(session, 2, 0) != SWITCH_STATUS_SUCCESS) { *frame = NULL; - return SWITCH_STATUS_GENERR; + switch_goto_status(SWITCH_STATUS_GENERR, end); } if ((val = switch_channel_get_variable(session->channel, "rtp_timeout_sec"))) { @@ -1516,7 +1533,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session switch_set_flag((*frame), SFF_CNG); (*frame)->datalen = engine->read_impl.encoded_bytes_per_packet; memset((*frame)->data, 0, (*frame)->datalen); - return SWITCH_STATUS_SUCCESS; + switch_goto_status(SWITCH_STATUS_SUCCESS, end); } } @@ -1541,7 +1558,13 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session *frame = &engine->read_frame; - return SWITCH_STATUS_SUCCESS; + status = SWITCH_STATUS_SUCCESS; + + end: + + switch_mutex_unlock(engine->read_mutex); + + return status; } //? @@ -3416,6 +3439,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_proxy_remote_addr(switch_core_ if (*rvp) { v_engine->codec_params.remote_sdp_ip = switch_core_session_strdup(session, rip); v_engine->codec_params.remote_sdp_port = (switch_port_t) atoi(rvp); + switch_channel_set_flag(session->channel, CF_VIDEO_POSSIBLE); + switch_channel_set_flag(session->channel, CF_VIDEO); } if (v_engine->codec_params.remote_sdp_ip && v_engine->codec_params.remote_sdp_port) { @@ -4162,6 +4187,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi uint8_t inb = switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_INBOUND; const char *ssrc; + switch_mutex_init(&a_engine->read_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); + //switch_core_media_set_rtp_session(session, SWITCH_MEDIA_TYPE_AUDIO, a_engine->rtp_session); if ((ssrc = switch_channel_get_variable(session->channel, "rtp_use_ssrc"))) { @@ -4621,6 +4648,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi switch_thread_cond_create(&v_engine->mh.cond, pool); switch_mutex_init(&v_engine->mh.cond_mutex, SWITCH_MUTEX_NESTED, pool); + switch_mutex_init(&v_engine->read_mutex, SWITCH_MUTEX_NESTED, pool); switch_thread_create(&v_engine->media_thread, thd_attr, video_helper_thread, &v_engine->mh, switch_core_session_get_pool(session)); } @@ -6175,7 +6203,7 @@ SWITCH_DECLARE(void) switch_core_media_patch_sdp(switch_core_session_t *session) } a_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO]; - v_engine = &smh->engines[SWITCH_MEDIA_TYPE_AUDIO]; + v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO]; if (zstr(smh->mparams->local_sdp_str)) { return; diff --git a/src/switch_core_session.c b/src/switch_core_session.c index 0768feed15..a67359d677 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -2521,6 +2521,14 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_execute_application_async(sw return SWITCH_STATUS_FALSE; } +SWITCH_DECLARE(void) switch_core_session_video_reset(switch_core_session_t *session) +{ + if (switch_channel_test_flag(session->channel, CF_VIDEO)) { + switch_channel_set_flag(session->channel, CF_VIDEO_ECHO); + switch_channel_clear_flag(session->channel, CF_VIDEO_PASSIVE); + switch_core_session_refresh_video(session); + } +} SWITCH_DECLARE(switch_status_t) switch_core_session_execute_application_get_flags(switch_core_session_t *session, const char *app, const char *arg, int32_t *flags) @@ -2736,19 +2744,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_exec(switch_core_session_t * msg.string_array_arg[1] = expanded; switch_core_session_receive_message(session, &msg); - if (switch_channel_test_flag(channel, CF_VIDEO)) { - switch_channel_set_flag(channel, CF_VIDEO_ECHO); - switch_channel_clear_flag(channel, CF_VIDEO_PASSIVE); - switch_core_session_refresh_video(session); - } - application_interface->application_function(session, expanded); - if (switch_channel_test_flag(channel, CF_VIDEO)) { - switch_channel_set_flag(channel, CF_VIDEO_ECHO); - switch_channel_clear_flag(channel, CF_VIDEO_PASSIVE); - switch_core_session_refresh_video(session); - } if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE_COMPLETE) == SWITCH_STATUS_SUCCESS) { const char *resp = switch_channel_get_variable(session->channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE); switch_channel_event_set_data(session->channel, event); diff --git a/src/switch_core_state_machine.c b/src/switch_core_state_machine.c index 56811a1e75..5926e52586 100644 --- a/src/switch_core_state_machine.c +++ b/src/switch_core_state_machine.c @@ -216,6 +216,8 @@ static void switch_core_standard_on_execute(switch_core_session_t *session) top: switch_channel_clear_flag(session->channel, CF_RESET); + + switch_core_session_video_reset(session); if ((extension = switch_channel_get_caller_extension(session->channel)) == 0) { switch_channel_hangup(session->channel, SWITCH_CAUSE_NORMAL_CLEARING); diff --git a/src/switch_event.c b/src/switch_event.c index 20262677e1..b3ef5f0cc2 100644 --- a/src/switch_event.c +++ b/src/switch_event.c @@ -1,4 +1,4 @@ -/* +/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2012, Anthony Minessale II * @@ -22,11 +22,12 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): - * + * * Anthony Minessale II * Michael Jerris * Paul D. Tinsley * William King + * Raymond Chandler * * switch_event.c -- Event System * @@ -153,6 +154,8 @@ static char *EVENT_NAMES[] = { "MESSAGE", "PRESENCE_IN", "NOTIFY_IN", + "PHONE_FEATURE", + "PHONE_FEATURE_SUBSCRIBE", "PRESENCE_OUT", "PRESENCE_PROBE", "MESSAGE_WAITING", @@ -263,7 +266,7 @@ static void *SWITCH_THREAD_FUNC switch_event_dispatch_thread(switch_thread_t *th EVENT_DISPATCH_QUEUE_RUNNING[my_id] = 1; switch_mutex_unlock(EVENT_QUEUE_MUTEX); - + for (;;) { void *pop = NULL; @@ -308,11 +311,11 @@ static switch_status_t switch_event_queue_dispatch_event(switch_event_t **eventp if (!SYSTEM_RUNNING) { return SWITCH_STATUS_FALSE; } - + while (event) { int launch = 0; - - switch_mutex_lock(EVENT_QUEUE_MUTEX); + + switch_mutex_lock(EVENT_QUEUE_MUTEX); if (!PENDING && switch_queue_size(EVENT_DISPATCH_QUEUE) > (unsigned int)(DISPATCH_QUEUE_LEN * DISPATCH_THREAD_COUNT)) { if (SOFT_MAX_DISPATCH + 1 > MAX_DISPATCH) { @@ -322,7 +325,7 @@ static switch_status_t switch_event_queue_dispatch_event(switch_event_t **eventp } switch_mutex_unlock(EVENT_QUEUE_MUTEX); - + if (launch) { if (SOFT_MAX_DISPATCH + 1 < MAX_DISPATCH) { switch_event_launch_dispatch_threads(SOFT_MAX_DISPATCH + 1); @@ -336,9 +339,9 @@ static switch_status_t switch_event_queue_dispatch_event(switch_event_t **eventp *eventp = NULL; switch_queue_push(EVENT_DISPATCH_QUEUE, event); event = NULL; - + } - + return SWITCH_STATUS_SUCCESS; } @@ -579,12 +582,12 @@ SWITCH_DECLARE(switch_status_t) switch_event_init(switch_memory_pool_t *pool) { //switch_threadattr_t *thd_attr; - /* + /* This statement doesn't do anything commenting it out for now. switch_assert(switch_arraylen(EVENT_NAMES) == SWITCH_EVENT_ALL + 1); */ - + /* don't need any more dispatch threads than we have CPU's*/ MAX_DISPATCH = (switch_core_cpu_count() / 2) + 1; if (MAX_DISPATCH < 2) { @@ -751,7 +754,7 @@ SWITCH_DECLARE(char *) switch_event_get_header_idx(switch_event_t *event, const } } - return hp->value; + return hp->value; } else if (!strcmp(header_name, "_body")) { return event->body; } @@ -802,7 +805,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_del_header_val(switch_event_t *even } FREE(hp->value); - + memset(hp, 0, sizeof(*hp)); #ifdef SWITCH_EVENT_RECYCLE if (switch_queue_trypush(EVENT_HEADER_RECYCLE_QUEUE, hp) != SWITCH_STATUS_SUCCESS) { @@ -834,7 +837,7 @@ static switch_event_header_t *new_header(const char *header_name) switch_assert(header); #ifdef SWITCH_EVENT_RECYCLE } -#endif +#endif memset(header, 0, sizeof(*header)); header->name = DUP(header_name); @@ -870,15 +873,15 @@ SWITCH_DECLARE(int) switch_event_add_array(switch_event_t *event, const char *va } data = strdup(val + 7); - + len = (sizeof(char *) * max) + 1; switch_assert(len); array = malloc(len); memset(array, 0, len); - + switch_separate_string_string(data, "|:", array, max); - + for(i = 0; i < max; i++) { switch_event_add_header_string(event, SWITCH_STACK_PUSH, var, array[i]); } @@ -914,20 +917,20 @@ static switch_status_t switch_event_base_add_header(switch_event_t *event, switc } if (index_ptr || (stack & SWITCH_STACK_PUSH) || (stack & SWITCH_STACK_UNSHIFT)) { - + if (!(header = switch_event_get_header_ptr(event, header_name)) && index_ptr) { - + header = new_header(header_name); - + if (switch_test_flag(event, EF_UNIQ_HEADERS)) { switch_event_del_header(event, header_name); } fly++; } - + if (header || (header = switch_event_get_header_ptr(event, header_name))) { - + if (index_ptr) { if (index > -1 && index <= 4000) { if (index < header->idx) { @@ -936,7 +939,7 @@ static switch_status_t switch_event_base_add_header(switch_event_t *event, switc } else { int i; char **m; - + m = realloc(header->array, sizeof(char *) * (index + 1)); switch_assert(m); header->array = m; @@ -986,7 +989,7 @@ static switch_status_t switch_event_base_add_header(switch_event_t *event, switc header = new_header(header_name); } - + if ((stack & SWITCH_STACK_PUSH) || (stack & SWITCH_STACK_UNSHIFT)) { char **m = NULL; switch_size_t len = 0; @@ -1004,7 +1007,7 @@ static switch_status_t switch_event_base_add_header(switch_event_t *event, switc } i = header->idx + 1; - m = realloc(header->array, sizeof(char *) * i); + m = realloc(header->array, sizeof(char *) * i); switch_assert(m); if ((stack & SWITCH_STACK_PUSH)) { @@ -1016,7 +1019,7 @@ static switch_status_t switch_event_base_add_header(switch_event_t *event, switc m[0] = data; } - header->idx++; + header->idx++; header->array = m; redraw: @@ -1118,7 +1121,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_set_body(switch_event_t *event, con if (body) { event->body = DUP(body); } - + return SWITCH_STATUS_SUCCESS; } @@ -1170,7 +1173,7 @@ SWITCH_DECLARE(void) switch_event_destroy(switch_event_t **event) FREE(this->name); FREE(this->value); - + #ifdef SWITCH_EVENT_RECYCLE if (switch_queue_trypush(EVENT_HEADER_RECYCLE_QUEUE, this) != SWITCH_STATUS_SUCCESS) { @@ -1200,13 +1203,13 @@ SWITCH_DECLARE(void) switch_event_destroy(switch_event_t **event) SWITCH_DECLARE(void) switch_event_merge(switch_event_t *event, switch_event_t *tomerge) { switch_event_header_t *hp; - + switch_assert(tomerge && event); for (hp = tomerge->headers; hp; hp = hp->next) { if (hp->idx) { int i; - + for(i = 0; i < hp->idx; i++) { switch_event_add_header_string(event, SWITCH_STACK_PUSH, hp->name, hp->array[i]); } @@ -1232,7 +1235,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_dup(switch_event_t **event, switch_ if (todup->subclass_name && !strcmp(hp->name, "Event-Subclass")) { continue; } - + if (hp->idx) { int i; for (i = 0; i < hp->idx; i++) { @@ -1270,11 +1273,11 @@ SWITCH_DECLARE(switch_status_t) switch_event_dup_reply(switch_event_t **event, s for (hp = todup->headers; hp; hp = hp->next) { char *name = hp->name, *value = hp->value; - + if (todup->subclass_name && !strcmp(hp->name, "Event-Subclass")) { continue; } - + if (!strncasecmp(hp->name, "from_", 5)) { p = hp->name + 5; switch_snprintf(hname, sizeof(hname), "to_%s", p); @@ -1288,7 +1291,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_dup_reply(switch_event_t **event, s } else if (!strcasecmp(name, "from")) { name = "to"; } - + if (hp->idx) { int i; for (i = 0; i < hp->idx; i++) { @@ -1379,13 +1382,13 @@ SWITCH_DECLARE(switch_status_t) switch_event_binary_serialize(switch_event_t *ev tn = tpl_map(SWITCH_SERIALIZED_EVENT_MAP, &e, &sh); tpl_pack(tn, 0); - + for (eh = event->headers; eh; eh = eh->next) { if (eh->idx) continue; // no arrays yet - + sh.name = eh->name; sh.value = eh->value; - + tpl_pack(tn, 1); } @@ -1427,7 +1430,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_serialize(switch_event_t *event, ch /* * grab enough memory to store 3x the string (url encode takes one char and turns it into %XX) * so we could end up with a string that is 3 times the originals length, unlikely but rather - * be safe than destroy the string, also add one for the null. And try to be smart about using + * be safe than destroy the string, also add one for the null. And try to be smart about using * the memory, allocate and only reallocate if we need more. This avoids an alloc, free CPU * destroying loop. */ @@ -1522,11 +1525,11 @@ SWITCH_DECLARE(switch_status_t) switch_event_create_array_pair(switch_event_t ** char *name, *val; switch_event_create(event, SWITCH_EVENT_CLONE); - + for (r = 0; r < len; r++) { val = switch_str_nil(vals[r]); name = names[r]; - + if (zstr(name)) { name = "Unknown"; } @@ -1535,7 +1538,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_create_array_pair(switch_event_t ** } return SWITCH_STATUS_SUCCESS; - + } SWITCH_DECLARE(switch_status_t) switch_event_create_brackets(char *data, char a, char b, char c, switch_event_t **event, char **new_data, switch_bool_t dup) @@ -1555,17 +1558,17 @@ SWITCH_DECLARE(switch_status_t) switch_event_create_brackets(char *data, char a, } end = switch_find_end_paren(vdata, a, b); - + check_a = end; - + while (check_a && (check_b = switch_strchr_strict(check_a, a, " "))) { if ((check_b = switch_find_end_paren(check_b, a, b))) { check_a = check_b; } } - + if (check_a) end = check_a; - + if (end) { next = end; vdata++; @@ -1576,12 +1579,12 @@ SWITCH_DECLARE(switch_status_t) switch_event_create_brackets(char *data, char a, } return SWITCH_STATUS_FALSE; } - + if (!e) { switch_event_create_plain(&e, SWITCH_EVENT_CHANNEL_DATA); } - + for (;;) { if (next) { char *pnext; @@ -1595,7 +1598,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_create_brackets(char *data, char a, vnext = switch_find_end_paren(next, a, b); next = NULL; } - + if (vdata) { if (*vdata == '^' && *(vdata + 1) == '^') { @@ -1603,7 +1606,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_create_brackets(char *data, char a, c = *vdata++; } } - + if ((var_count = switch_separate_string(vdata, c, var_array, (sizeof(var_array) / sizeof(var_array[0]))))) { int x = 0; for (x = 0; x < var_count; x++) { @@ -1635,7 +1638,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_create_brackets(char *data, char a, } else { *new_data = end; } - + return SWITCH_STATUS_SUCCESS; } @@ -1687,7 +1690,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_create_json(switch_event_t **event, } } } - + cJSON_Delete(cj); *event = new_event; return SWITCH_STATUS_SUCCESS; @@ -1699,7 +1702,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_serialize_json(switch_event_t *even cJSON *cj; *str = NULL; - + cj = cJSON_CreateObject(); for (hp = event->headers; hp; hp = hp->next) { @@ -1710,9 +1713,9 @@ SWITCH_DECLARE(switch_status_t) switch_event_serialize_json(switch_event_t *even for(i = 0; i < hp->idx; i++) { cJSON_AddItemToArray(a, cJSON_CreateString(hp->array[i])); } - + cJSON_AddItemToObject(cj, hp->name, a); - + } else { cJSON_AddItemToObject(cj, hp->name, cJSON_CreateString(hp->value)); } @@ -1730,7 +1733,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_serialize_json(switch_event_t *even *str = cJSON_Print(cj); cJSON_Delete(cj); - + return SWITCH_STATUS_SUCCESS; } @@ -1901,7 +1904,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_bind_removable(const char *id, swit if (node) { *node = NULL; } - + if (subclass_name) { if (!(subclass = switch_core_hash_find(CUSTOM_HASH, subclass_name))) { switch_event_reserve_subclass_detailed(id, subclass_name); @@ -2066,9 +2069,9 @@ char *dp;\ olen += (len + l + block);\ cpos = c - data;\ if ((dp = realloc(data, olen))) {\ - data = dp;\ - c = data + cpos;\ - memset(c, 0, olen - cpos);\ + data = dp;\ + c = data + cpos;\ + memset(c, 0, olen - cpos);\ }} \ SWITCH_DECLARE(char *) switch_event_expand_headers_check(switch_event_t *event, const char *in, switch_event_t *var_list, switch_event_t *api_list, uint32_t recur) @@ -2223,7 +2226,7 @@ SWITCH_DECLARE(char *) switch_event_expand_headers_check(switch_event_t *event, int ooffset = 0; char *ptr; int idx = -1; - + if ((expanded = switch_event_expand_headers_check(event, (char *) vname, var_list, api_list, recur+1)) == vname) { expanded = NULL; } else { @@ -2266,7 +2269,7 @@ SWITCH_DECLARE(char *) switch_event_expand_headers_check(switch_event_t *event, switch_assert(cloned_sub_val); sub_val = cloned_sub_val; } - + if (offset >= 0) { sub_val += offset; } else if ((size_t) abs(offset) <= strlen(sub_val)) { @@ -2278,7 +2281,7 @@ SWITCH_DECLARE(char *) switch_event_expand_headers_check(switch_event_t *event, *ptr = '\0'; } } - + switch_safe_free(expanded); } else { @@ -2477,7 +2480,7 @@ SWITCH_DECLARE(int) switch_event_check_permission_list(switch_event_t *list, con r = default_allow; } - return r; + return r; } SWITCH_DECLARE(void) switch_event_add_presence_data_cols(switch_channel_t *channel, switch_event_t *event, const char *prefix) @@ -2485,7 +2488,7 @@ SWITCH_DECLARE(void) switch_event_add_presence_data_cols(switch_channel_t *chann const char *data; if (!prefix) prefix = ""; - + if ((data = switch_channel_get_variable(channel, "presence_data_cols"))) { char *cols[128] = { 0 }; char header_name[128] = ""; @@ -2493,17 +2496,17 @@ SWITCH_DECLARE(void) switch_event_add_presence_data_cols(switch_channel_t *chann char *data_copy = NULL; data_copy = strdup(data); - + col_count = switch_split(data_copy, ':', cols); - + for (i = 0; i < col_count; i++) { const char *val = NULL; switch_snprintf(header_name, sizeof(header_name), "%s%s", prefix, cols[i]); - + val = switch_channel_get_variable(channel, cols[i]); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header_name, val); } - + switch_safe_free(data_copy); } diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 232f0a6dd2..1f91ed9625 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -2907,6 +2907,12 @@ SWITCH_DECLARE(void) switch_ivr_delay_echo(switch_core_session_t *session, uint3 interval = read_impl.microseconds_per_packet / 1000; //samples = switch_samples_per_packet(read_impl.samples_per_second, interval); + if (delay_ms < interval * 2) { + delay_ms = interval * 2; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Minimum possible delay for this codec (%d) has been chosen\n", delay_ms); + } + + qlen = delay_ms / (interval) / 2; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Setting delay to %dms (%d frames)\n", delay_ms, qlen); jb = stfu_n_init(qlen, qlen, read_impl.samples_per_packet, read_impl.samples_per_second, 0); diff --git a/src/switch_ivr_bridge.c b/src/switch_ivr_bridge.c index f9b22f8a4f..e0d005aec9 100644 --- a/src/switch_ivr_bridge.c +++ b/src/switch_ivr_bridge.c @@ -63,7 +63,7 @@ static void *SWITCH_THREAD_FUNC video_bridge_thread(switch_thread_t *thread, voi if (!switch_stristr("loopback", source) && !switch_stristr("loopback", b_source)) { switch_channel_set_flag(channel, CF_VIDEO_PASSIVE); - switch_channel_set_flag(b_channel, CF_VIDEO_PASSIVE); + //switch_channel_set_flag(b_channel, CF_VIDEO_PASSIVE); } switch_core_session_refresh_video(vh->session_a); @@ -94,7 +94,7 @@ static void *SWITCH_THREAD_FUNC video_bridge_thread(switch_thread_t *thread, voi } switch_channel_clear_flag(channel, CF_VIDEO_PASSIVE); - switch_channel_clear_flag(b_channel, CF_VIDEO_PASSIVE); + //switch_channel_clear_flag(b_channel, CF_VIDEO_PASSIVE); switch_core_session_kill_channel(vh->session_b, SWITCH_SIG_BREAK); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(vh->session_a), SWITCH_LOG_DEBUG, "%s video thread ended.\n", switch_channel_get_name(channel)); @@ -624,7 +624,8 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj) if (vid_thread) { switch_status_t st; - if (vh.up) { + if (vh.up == 1) { + vh.up = -1; switch_core_session_kill_channel(session_a, SWITCH_SIG_BREAK); switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK); } diff --git a/src/switch_rtp.c b/src/switch_rtp.c index cfca9f20ae..b75835a270 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -3126,9 +3126,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_deactivate_jitter_buffer(switch_rtp_t return SWITCH_STATUS_FALSE; } - READ_INC(rtp_session); - stfu_n_destroy(&rtp_session->jb); - READ_DEC(rtp_session); + rtp_session->flags[SWITCH_RTP_FLAG_KILL_JB]++; return SWITCH_STATUS_SUCCESS; } @@ -3638,6 +3636,9 @@ SWITCH_DECLARE(void) switch_rtp_set_flag(switch_rtp_t *rtp_session, switch_rtp_f rtp_session->autoadj_window = 20; rtp_session->autoadj_tally = 0; rtp_flush_read_buffer(rtp_session, SWITCH_RTP_FLUSH_ONCE); + if (rtp_session->jb) { + stfu_n_reset(rtp_session->jb); + } } else if (flag == SWITCH_RTP_FLAG_NOBLOCK && rtp_session->sock_input) { switch_socket_opt_set(rtp_session->sock_input, SWITCH_SO_NONBLOCK, TRUE); } @@ -4255,6 +4256,14 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t switch_swap_linear((int16_t *)RTP_BODY(rtp_session), (int) *bytes - rtp_header_len); } + if (rtp_session->flags[SWITCH_RTP_FLAG_KILL_JB]) { + rtp_session->flags[SWITCH_RTP_FLAG_KILL_JB] = 0; + if (rtp_session->jb) { + stfu_n_destroy(&rtp_session->jb); + } + } + + if (rtp_session->jb && !rtp_session->pause_jb && rtp_session->recv_msg.header.version == 2 && *bytes) { if (rtp_session->recv_msg.header.m && rtp_session->recv_msg.header.pt != rtp_session->recv_te && !rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && !(rtp_session->rtp_bugs & RTP_BUG_IGNORE_MARK_BIT)) { @@ -4604,7 +4613,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ pt = 0; } - if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { + if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && !rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA]) { pt = 100000; } diff --git a/src/switch_scheduler.c b/src/switch_scheduler.c index b71821e298..8e0a7d0963 100644 --- a/src/switch_scheduler.c +++ b/src/switch_scheduler.c @@ -201,7 +201,7 @@ SWITCH_DECLARE(uint32_t) switch_scheduler_add_task(time_t task_runtime, switch_assert(func); if (task_runtime < now) { - container->task.repeat = task_runtime; + container->task.repeat = (uint32_t)task_runtime; task_runtime += now; } diff --git a/src/switch_utils.c b/src/switch_utils.c index a11f908dce..0eb66f6a1b 100644 --- a/src/switch_utils.c +++ b/src/switch_utils.c @@ -1273,7 +1273,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; @@ -1580,6 +1580,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 };