diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 06679f53d3..e4e8517781 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -358,7 +358,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_stop_inband_dtmf_generate_session(swi \brief - NEEDDESC - \param session the session to act on */ -SWITCH_DECLARE(void) switch_ivr_session_echo(switch_core_session_t *session, switch_input_args_t *args); +SWITCH_DECLARE(switch_status_t) switch_ivr_session_echo(switch_core_session_t *session, switch_input_args_t *args); /*! \brief Stop looking for TONES diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 20c83cd898..c6dfb4376c 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -1972,6 +1972,19 @@ struct switch_ivr_dmachine_match { typedef struct switch_ivr_dmachine_match switch_ivr_dmachine_match_t; typedef switch_status_t (*switch_ivr_dmachine_callback_t) (switch_ivr_dmachine_match_t *match); +#define MAX_ARG_RECURSION 25 + +#define arg_recursion_check_start(_args) if (_args) { \ + if (_args->loops >= MAX_ARG_RECURSION) { \ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, \ + "RECURSION ERROR! It's not the best idea to call things that collect input recursively from an input callback.\n"); \ + return SWITCH_STATUS_GENERR; \ + } else {_args->loops++;} \ + } + + +#define arg_recursion_check_stop(_args) if (_args) _args->loops-- + typedef struct { switch_input_callback_function_t input_callback; void *buf; @@ -1979,6 +1992,7 @@ typedef struct { switch_read_frame_callback_function_t read_frame_callback; void *user_data; switch_ivr_dmachine_t *dmachine; + int loops; } switch_input_args_t; diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 6aa5166634..42bd11b802 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -141,6 +141,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, int sval = 0; const char *var; + arg_recursion_check_start(args); + /* if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND && !switch_channel_test_flag(channel, CF_PROXY_MODE) && !switch_channel_media_ready(channel) && !switch_channel_test_flag(channel, CF_SERVICE)) { @@ -156,12 +158,12 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, for (elapsed=0; switch_channel_up(channel) && elapsed<(ms/20); elapsed++) { if (switch_channel_test_flag(channel, CF_BREAK)) { switch_channel_clear_flag(channel, CF_BREAK); - return SWITCH_STATUS_BREAK; + switch_goto_status(SWITCH_STATUS_BREAK, end); } switch_yield(20 * 1000); } - return SWITCH_STATUS_SUCCESS; + switch_goto_status(SWITCH_STATUS_SUCCESS, end); } var = switch_channel_get_variable(channel, SWITCH_SEND_SILENCE_WHEN_IDLE_VARIABLE); @@ -183,7 +185,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Codec Error L16@%uhz %u channels %dms\n", imp.samples_per_second, imp.number_of_channels, imp.microseconds_per_packet / 1000); - return SWITCH_STATUS_FALSE; + switch_goto_status(SWITCH_STATUS_FALSE, end); } @@ -213,7 +215,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, } if (!ms) { - return SWITCH_STATUS_SUCCESS; + switch_goto_status(SWITCH_STATUS_SUCCESS, end); } for (;;) { @@ -303,6 +305,11 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, } } + + end: + + arg_recursion_check_stop(args); + if (write_frame.codec) { switch_core_codec_destroy(&codec); } @@ -874,6 +881,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session, unsigned char *abuf = NULL; switch_codec_implementation_t imp = { 0 }; + + if (switch_channel_test_flag(channel, CF_RECOVERED) && switch_channel_test_flag(channel, CF_CONTROLLED)) { switch_channel_clear_flag(channel, CF_CONTROLLED); } @@ -887,6 +896,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session, return SWITCH_STATUS_FALSE; } + arg_recursion_check_start(args); + if ((to = switch_channel_get_variable(channel, "park_timeout"))) { char *cause_str; @@ -931,7 +942,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Codec Error L16@%uhz %u channels %dms\n", imp.samples_per_second, imp.number_of_channels, imp.microseconds_per_packet / 1000); - return SWITCH_STATUS_FALSE; + switch_goto_status(SWITCH_STATUS_FALSE, end); } @@ -982,7 +993,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session, if (switch_channel_test_flag(channel, CF_UNICAST)) { if (!switch_channel_media_ready(channel)) { if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { - return SWITCH_STATUS_FALSE; + switch_goto_status(SWITCH_STATUS_FALSE, end); } } @@ -1100,6 +1111,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session, } + end: + + arg_recursion_check_stop(args); + if (write_frame.codec) { switch_core_codec_destroy(&codec); } @@ -1133,12 +1148,15 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_collect_digits_callback(switch_core_s return SWITCH_STATUS_GENERR; } + arg_recursion_check_start(args); + if (abs_timeout) { abs_started = switch_micro_time_now(); } if (digit_timeout) { digit_started = switch_micro_time_now(); } + while (switch_channel_ready(channel)) { switch_frame_t *read_frame = NULL; switch_event_t *event; @@ -1224,6 +1242,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_collect_digits_callback(switch_core_s } } + arg_recursion_check_stop(args); + return status; } @@ -2890,6 +2910,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_say(switch_core_session_t *session, channel = switch_core_session_get_channel(session); switch_assert(channel); + arg_recursion_check_start(args); + + if (zstr(module_name)) { module_name = "en"; } @@ -2971,6 +2994,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_say(switch_core_session_t *session, done: + arg_recursion_check_stop(args); + + if (hint_data) { switch_event_destroy(&hint_data); } diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index f301b11887..80b7a3ae25 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -616,7 +616,7 @@ static void *SWITCH_THREAD_FUNC echo_video_thread(switch_thread_t *thread, void } #endif -SWITCH_DECLARE(void) switch_ivr_session_echo(switch_core_session_t *session, switch_input_args_t *args) +SWITCH_DECLARE(switch_status_t) switch_ivr_session_echo(switch_core_session_t *session, switch_input_args_t *args) { switch_status_t status; switch_frame_t *read_frame; @@ -630,9 +630,11 @@ SWITCH_DECLARE(void) switch_ivr_session_echo(switch_core_session_t *session, swi #endif if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { - return; + return SWITCH_STATUS_FALSE; } + arg_recursion_check_start(args); + restart: #ifdef SWITCH_VIDEO_IN_THREADS @@ -725,6 +727,7 @@ SWITCH_DECLARE(void) switch_ivr_session_echo(switch_core_session_t *session, swi } #endif + return SWITCH_STATUS_SUCCESS; } typedef struct { @@ -3413,6 +3416,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_and_detect_speech(switch_core_se play_and_detect_speech_state_t state = { 0, "" }; switch_channel_t *channel = switch_core_session_get_channel(session); + arg_recursion_check_start(args); + if (result == NULL) { goto done; } @@ -3474,6 +3479,8 @@ done: status = SWITCH_STATUS_FALSE; } + arg_recursion_check_stop(args); + return status;; } diff --git a/src/switch_ivr_play_say.c b/src/switch_ivr_play_say.c index c15da6fb03..e7e2e4e22a 100644 --- a/src/switch_ivr_play_say.c +++ b/src/switch_ivr_play_say.c @@ -54,11 +54,14 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_phrase_macro_event(switch_core_sessio switch_bool_t sound_prefix_enforced = switch_true(switch_channel_get_variable(channel, "sound_prefix_enforced")); switch_bool_t local_sound_prefix_enforced = SWITCH_FALSE; + if (!macro_name) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No phrase macro specified.\n"); return status; } + arg_recursion_check_start(args); + if (!lang) { chan_lang = switch_channel_get_variable(channel, "default_language"); if (!chan_lang) { @@ -331,6 +334,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_phrase_macro_event(switch_core_sessio done: + arg_recursion_check_stop(args); + if (hint_data) { switch_event_destroy(&hint_data); } @@ -379,6 +384,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *se int restart_limit_on_dtmf = 0; const char *prefix, *var; + prefix = switch_channel_get_variable(channel, "sound_prefix"); if (!prefix) { @@ -404,6 +410,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *se return SWITCH_STATUS_FALSE; } + arg_recursion_check_start(args); + if (!fh) { fh = &lfh; } @@ -467,6 +475,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *se write_frame.samples = write_frame.datalen / 2; write_frame.codec = &write_codec; } else { + arg_recursion_check_stop(args); return SWITCH_STATUS_FALSE; } } @@ -519,6 +528,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *se if (switch_core_file_open(fh, file, fh->channels, read_impl.actual_samples_per_second, file_flags, NULL) != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); + arg_recursion_check_stop(args); return SWITCH_STATUS_GENERR; } @@ -590,6 +600,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *se fh->channels, read_impl.microseconds_per_packet / 1000); switch_core_file_close(fh); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); + arg_recursion_check_stop(args); return SWITCH_STATUS_GENERR; } } @@ -796,6 +807,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *se } switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); + + arg_recursion_check_stop(args); return status; } @@ -841,6 +854,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_gentones(switch_core_session_t *sessi return SWITCH_STATUS_FALSE; } + + arg_recursion_check_start(args); + memset(&ts, 0, sizeof(ts)); write_frame.codec = &write_codec; write_frame.data = data; @@ -946,6 +962,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_gentones(switch_core_session_t *sessi switch_buffer_destroy(&audio_buffer); teletone_destroy_session(&ts); + arg_recursion_check_stop(args); + return SWITCH_STATUS_SUCCESS; } @@ -1084,6 +1102,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess return SWITCH_STATUS_FALSE; } + arg_recursion_check_start(args); + if (!strcasecmp(read_impl.iananame, "l16")) { l16++; } @@ -1136,6 +1156,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess *arg++ = '\0'; } if ((status = switch_ivr_phrase_macro(session, dup, arg, lang, args)) != SWITCH_STATUS_SUCCESS) { + arg_recursion_check_stop(args); return status; } continue; @@ -1160,6 +1181,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess if (!zstr(engine) && !zstr(voice) && !zstr(text)) { if ((status = switch_ivr_speak_text(session, engine, voice, text, args)) != SWITCH_STATUS_SUCCESS) { + arg_recursion_check_stop(args); return status; } } else { @@ -1168,6 +1190,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess voice = (char *) switch_channel_get_variable(channel, "tts_voice"); if (engine && text) { if ((status = switch_ivr_speak_text(session, engine, voice, text, args)) != SWITCH_STATUS_SUCCESS) { + arg_recursion_check_stop(args); return status; } } else { @@ -1727,6 +1750,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess switch_safe_free(abuf); switch_core_session_reset(session, SWITCH_FALSE, SWITCH_FALSE); + + arg_recursion_check_stop(args); + return status; } @@ -2119,6 +2145,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session return SWITCH_STATUS_FALSE; } + arg_recursion_check_start(args); + write_frame.data = abuf; write_frame.buflen = sizeof(abuf); @@ -2149,6 +2177,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session switch_size_t mylen = strlen(text) + extra + 1; tmp = malloc(mylen); if (!tmp) { + arg_recursion_check_stop(args); return SWITCH_STATUS_MEMERR; } memset(tmp, 0, mylen); @@ -2369,6 +2398,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "done speaking text\n"); flags = 0; switch_core_speech_flush_tts(sh); + + arg_recursion_check_stop(args); return status; } @@ -2426,6 +2457,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses return SWITCH_STATUS_FALSE; } + arg_recursion_check_start(args); + sh = ↰ codec = &lcodec; timer = <imer; @@ -2466,6 +2499,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid TTS module!\n"); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); switch_ivr_clear_speech_cache(session); + arg_recursion_check_stop(args); return status; } } else if (cache_obj && strcasecmp(cache_obj->voice_name, voice_name)) { @@ -2476,6 +2510,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { flags = 0; switch_core_speech_close(sh, &flags); + arg_recursion_check_stop(args); return SWITCH_STATUS_FALSE; } switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "OPEN TTS %s\n", tts_name); @@ -2495,6 +2530,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses switch_core_speech_close(sh, &flags); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); switch_ivr_clear_speech_cache(session); + arg_recursion_check_stop(args); return SWITCH_STATUS_GENERR; } } @@ -2510,6 +2546,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses switch_core_speech_close(sh, &flags); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); switch_ivr_clear_speech_cache(session); + arg_recursion_check_stop(args); return SWITCH_STATUS_GENERR; } switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Setup timer success %u bytes per %d ms!\n", sh->samples * 2, @@ -2539,6 +2576,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses } switch_core_session_reset(session, SWITCH_FALSE, SWITCH_TRUE); + arg_recursion_check_stop(args); + return status; } diff --git a/src/switch_ivr_say.c b/src/switch_ivr_say.c index 0081ab16f5..214931b1bd 100644 --- a/src/switch_ivr_say.c +++ b/src/switch_ivr_say.c @@ -133,6 +133,8 @@ SWITCH_DECLARE(switch_say_type_t) switch_ivr_get_say_type_by_name(const char *na SWITCH_DECLARE(switch_status_t) switch_ivr_say_spell(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) { char *p; + + arg_recursion_check_start(args); for (p = tosay; p && *p; p++) { int a = tolower((int) *p); @@ -147,6 +149,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_say_spell(switch_core_session_t *sess } } + arg_recursion_check_stop(args); + return SWITCH_STATUS_SUCCESS; } @@ -172,24 +176,28 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_say_ip(switch_core_session_t *session switch_input_args_t *args) { char *a, *b, *c, *d; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + arg_recursion_check_start(args); + if (!(a = switch_core_session_strdup(session, tosay))) { - return SWITCH_STATUS_FALSE; + switch_goto_status(SWITCH_STATUS_FALSE, end); } if (!(b = strchr(a, '.'))) { - return SWITCH_STATUS_FALSE; + switch_goto_status(SWITCH_STATUS_FALSE, end); } *b++ = '\0'; if (!(c = strchr(b, '.'))) { - return SWITCH_STATUS_FALSE; + switch_goto_status(SWITCH_STATUS_FALSE, end); } *c++ = '\0'; if (!(d = strchr(c, '.'))) { - return SWITCH_STATUS_FALSE; + switch_goto_status(SWITCH_STATUS_FALSE, end); } *d++ = '\0'; @@ -202,7 +210,11 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_say_ip(switch_core_session_t *session say_file("digits/dot.wav"); say_num(atoi(d), say_args->method); - return SWITCH_STATUS_SUCCESS; + end: + + arg_recursion_check_stop(args); + + return status; }