From 5aabb54f68f495bfd51c083bd89d4fdaa1a9ba6c Mon Sep 17 00:00:00 2001 From: Dragos Oancea Date: Fri, 4 Sep 2020 08:57:28 +0000 Subject: [PATCH] [core] eavesdrop: init L16 codec at right ptime in certain conditions. [core] eavesdrop: avoid eavesdropping on itself and return error. [core] eavesdrop: adjust buffer operations for ptime mismatch and for when ptimes are the same. [core] eavesdrop: add buffering based on LCM (Least Common Multiple) when ptime mismatch, and have audio write thread enabled when ptime eavesdropee < ptime eavesdropper. [unit-tests] add unit-tests for eavesdrop. --- src/switch_ivr_async.c | 113 +++++- tests/unit/Makefile.am | 5 + tests/unit/conf_eavesdrop/freeswitch.xml | 238 +++++++++++++ tests/unit/conf_eavesdrop/gw/eavestest.xml | 14 + tests/unit/switch_eavesdrop.c | 392 +++++++++++++++++++++ 5 files changed, 743 insertions(+), 19 deletions(-) create mode 100644 tests/unit/conf_eavesdrop/freeswitch.xml create mode 100644 tests/unit/conf_eavesdrop/gw/eavestest.xml create mode 100644 tests/unit/switch_eavesdrop.c diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index a6972e847c..07f28a36e8 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -2184,6 +2184,25 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_update_display(switch_core_ return status; } +/*Greatest Common Divisor*/ +static uint32_t switch_gcd(uint32_t x, uint32_t y) +{ + if (y == 0) { + return x; + } + + return switch_gcd(y, x % y); +} + +/*Least Common Multiple*/ +static uint32_t switch_lcm(uint32_t x, uint32_t y) +{ + uint32_t gcd = switch_gcd(x, y); + + if (gcd) return (x * y) / gcd; + + return 0; +} SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session_t *session, const char *uuid, const char *require_group, switch_eavesdrop_flag_t flags) @@ -2213,11 +2232,17 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session const char *vval; int buf_size = 0; int channels; + int lcm, buff_min_len, buffered = 1; if (!switch_channel_media_up(channel)) { goto end; } + if (tsession == session) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Eavesdrop target invalid.\n"); + goto end; + } + while(switch_channel_state_change_pending(tchannel) || !switch_channel_media_up(tchannel)) { switch_yield(10000); if (!--sanity) break; @@ -2286,8 +2311,21 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session goto end; } - - if (switch_core_codec_init(&codec, + if (tread_impl.decoded_bytes_per_packet < read_impl.decoded_bytes_per_packet) { + if (switch_core_codec_init(&codec, + "L16", + NULL, + NULL, + read_impl.actual_samples_per_second, + read_impl.microseconds_per_packet / 1000, + read_impl.number_of_channels, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot init codec\n"); + goto end; + } + } else { + if (switch_core_codec_init(&codec, "L16", NULL, NULL, @@ -2298,10 +2336,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot init codec\n"); goto end; + } + buffered = 0; } - switch_core_session_get_read_impl(session, &read_impl); - ep->read_impl = read_impl; ep->tread_impl = tread_impl; @@ -2440,6 +2478,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session switch_core_session_receive_message(tsession, &msg); } + lcm = switch_lcm(tread_impl.decoded_bytes_per_packet, read_impl.decoded_bytes_per_packet); + while (switch_channel_up_nosig(tchannel) && switch_channel_ready(channel)) { uint32_t len = sizeof(buf); switch_event_t *event = NULL; @@ -2569,15 +2609,24 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session channels = 1; } - tlen = tread_impl.decoded_bytes_per_packet * channels; + tlen = ep->read_impl.decoded_bytes_per_packet * channels; if (len > tlen) { len = tlen; } + if (buffered) { + buff_min_len = lcm * 2; + if (switch_buffer_inuse(ep->buffer) < buff_min_len) { + continue; + } + } else { + buff_min_len = len; + } + if (ep->buffer) { switch_buffer_lock(ep->buffer); - while (switch_buffer_inuse(ep->buffer) >= len) { + while (switch_buffer_inuse(ep->buffer) >= buff_min_len) { int tchanged = 0, changed = 0; write_frame.datalen = (uint32_t) switch_buffer_read(ep->buffer, buf, len); @@ -2592,7 +2641,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session tchanged = 1; } - if (read_impl.number_of_channels != ep->tread_impl.number_of_channels || + if (read_impl.number_of_channels != ep->read_impl.number_of_channels || read_impl.actual_samples_per_second != ep->read_impl.actual_samples_per_second) { changed = 1; } @@ -2606,6 +2655,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session ep->read_impl.number_of_channels, read_impl.actual_samples_per_second, read_impl.number_of_channels); + + tlen = read_impl.decoded_bytes_per_packet * channels; + + if (len > tlen) { + len = tlen; + } + } if (tchanged) { @@ -2615,28 +2671,44 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session ep->tread_impl.number_of_channels, tread_impl.actual_samples_per_second, tread_impl.number_of_channels); + } + - tlen = tread_impl.decoded_bytes_per_packet * channels; - - if (len > tlen) { - len = tlen; - } - - switch_core_codec_destroy(&codec); + switch_core_codec_destroy(&codec); + if (tread_impl.decoded_bytes_per_packet < read_impl.decoded_bytes_per_packet) { if (switch_core_codec_init(&codec, "L16", NULL, NULL, - tread_impl.actual_samples_per_second, - tread_impl.microseconds_per_packet / 1000, - tread_impl.number_of_channels, + read_impl.actual_samples_per_second, + read_impl.microseconds_per_packet / 1000, + read_impl.number_of_channels, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot init codec\n"); + switch_buffer_unlock(ep->buffer); + goto end; + } + buffered = 1; + lcm = switch_lcm(tread_impl.decoded_bytes_per_packet, read_impl.decoded_bytes_per_packet); + } else { + if (switch_core_codec_init(&codec, + "L16", + NULL, + NULL, + tread_impl.actual_samples_per_second, + tread_impl.microseconds_per_packet / 1000, + tread_impl.number_of_channels, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot init codec\n"); switch_buffer_unlock(ep->buffer); goto end; } + if (buffered == 1) { + buffered = 0; + } } ep->read_impl = read_impl; @@ -2658,11 +2730,14 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session switch_buffer_unlock(ep->buffer); switch_buffer_lock(ep->buffer); - } + if (ep->tread_impl.decoded_bytes_per_packet == ep->read_impl.decoded_bytes_per_packet) { + /* push just the number of samples worth of a packet. */ + break; + } + } switch_buffer_unlock(ep->buffer); } - } end_loop: diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 6e7e128d62..2511a143c2 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -7,8 +7,13 @@ noinst_PROGRAMS += switch_core_video switch_core_db switch_vad switch_core_asr AM_LDFLAGS += -avoid-version -no-undefined $(SWITCH_AM_LDFLAGS) $(openssl_LIBS) AM_LDFLAGS += $(FREESWITCH_LIBS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS) +# "make check" will not run these. +examples = switch_eavesdrop + if HAVE_FVAD AM_CFLAGS += -DSWITCH_HAVE_FVAD endif TESTS = $(noinst_PROGRAMS) + +bin_PROGRAMS = $(examples) diff --git a/tests/unit/conf_eavesdrop/freeswitch.xml b/tests/unit/conf_eavesdrop/freeswitch.xml new file mode 100644 index 0000000000..4a65caf6d8 --- /dev/null +++ b/tests/unit/conf_eavesdrop/freeswitch.xml @@ -0,0 +1,238 @@ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/unit/conf_eavesdrop/gw/eavestest.xml b/tests/unit/conf_eavesdrop/gw/eavestest.xml new file mode 100644 index 0000000000..a2f268424e --- /dev/null +++ b/tests/unit/conf_eavesdrop/gw/eavestest.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/tests/unit/switch_eavesdrop.c b/tests/unit/switch_eavesdrop.c new file mode 100644 index 0000000000..867ad7a6bc --- /dev/null +++ b/tests/unit/switch_eavesdrop.c @@ -0,0 +1,392 @@ +#include +#include +#include + +static switch_memory_pool_t *pool = NULL; + +static switch_status_t test_detect_long_tone_in_file(const char *filepath, int rate, int freq, int ptime) { + teletone_multi_tone_t mt; + teletone_tone_map_t map; + int16_t data[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 }; + size_t len = (rate * ptime / 1000) /*packet len in samples */ * 8; /*length of chunk that must contain tone*/ + size_t fin = 0; + switch_status_t status; + switch_file_handle_t fh = { 0 }; + uint8_t fail = 0, gaps = 0, audio = 0; + uint32_t pos = 0; + size_t full_len = 0; + + status = switch_core_file_open(&fh, filepath, 1, rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL); + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot open file [%s]\n", filepath); + return SWITCH_STATUS_FALSE; + } + + mt.sample_rate = rate; + map.freqs[0] = (teletone_process_t)freq; + + teletone_multi_tone_init(&mt, &map); + + len = (rate * 2 / 100) /*packet len in samples */ * 8; + + while (switch_core_file_read(&fh, &data, &len) == SWITCH_STATUS_SUCCESS) { + fin += len; + /*skip silence at the beginning of the file, 1 second max. */ + if (!teletone_multi_tone_detect(&mt, data, len)) { + if ((fin > rate && !audio) || gaps > 30) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Too many gaps in audio or no tone detected 1st second. [%u][%d]\n", fin, gaps); + fail = 1; + break; + } + gaps++; + continue; + } else { + audio++; + } + } + + switch_core_file_close(&fh); + + if (fail) { + return SWITCH_STATUS_FALSE; + } + return SWITCH_STATUS_SUCCESS; +} + +FST_CORE_BEGIN("./conf_eavesdrop") + +{ +FST_SUITE_BEGIN(switch_eavesdrop) +{ + FST_SETUP_BEGIN() + { + fst_requires_module("mod_loopback"); + fst_requires_module("mod_sofia"); + switch_core_set_variable("link_ip", switch_core_get_variable("local_ip_v4")); + } + FST_SETUP_END() + + FST_TEARDOWN_BEGIN() + { + } + FST_TEARDOWN_END() + + FST_TEST_BEGIN(test_eavesdrop_bridged_same_ptime_20ms) + { + switch_core_session_t *session1 = NULL; + switch_core_session_t *session2 = NULL; + switch_core_session_t *session3 = NULL; + + switch_channel_t *channel1 = NULL; + switch_channel_t *channel2 = NULL; + switch_channel_t *channel3 = NULL; + + switch_status_t status; + switch_call_cause_t cause; + switch_stream_handle_t stream = { 0 }; + char eavesdrop_command[256] = { 0 }; + char rec_path[256]; + char rec_uuid[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 }; + char eaves_dialstr[256] = { 0 }; + + switch_uuid_str(rec_uuid, sizeof(rec_uuid)); + + /*parked 20 ms ptime */ + status = switch_ivr_originate(NULL, &session1, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332220", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session1); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel1 = switch_core_session_get_channel(session1); + fst_requires(channel1); + + snprintf(eaves_dialstr, sizeof(eaves_dialstr), "{ignore_early_media=true}{sip_h_X-UnitTestRecfile=%s}sofia/gateway/eavestest/+15553332230", rec_uuid); + + /*eavesdropper 20 ms ptime*/ + status = switch_ivr_originate(NULL, &session2, &cause, eaves_dialstr, 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session2); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel2 = switch_core_session_get_channel(session2); + fst_requires(channel2); + + /*milliwatt tone 20 ms ptime*/ + status = switch_ivr_originate(NULL, &session3, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332226", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session3); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel3 = switch_core_session_get_channel(session3); + fst_requires(channel3); + + SWITCH_STANDARD_STREAM(stream); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_bridge %s %s", switch_core_session_get_uuid(session1), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session1, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command),"uuid_setvar_multi %s eavesdrop_enable_dtmf=false;eavesdrop_whisper_bleg=true;eavesdrop_whisper_aleg=false", switch_core_session_get_uuid(session3)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_transfer %s 'eavesdrop:%s' inline", switch_core_session_get_uuid(session3), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + switch_safe_free(stream.data); + + sleep(5); // it will record ~ 5 secs + + snprintf(rec_path, sizeof(rec_path), "/tmp/eaves-%s.wav", rec_uuid); + + fst_requires(switch_file_exists(rec_path, fst_pool) == SWITCH_STATUS_SUCCESS); + + fst_requires(test_detect_long_tone_in_file(rec_path, 8000, 300, 20) == SWITCH_STATUS_SUCCESS); + + unlink(rec_path); + + switch_channel_hangup(channel1, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel2, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel3, SWITCH_CAUSE_NORMAL_CLEARING); + + switch_core_session_rwunlock(session1); + switch_core_session_rwunlock(session2); + switch_core_session_rwunlock(session3); + + } + FST_TEST_END() + + FST_TEST_BEGIN(test_eavesdrop_bridged_ptime_mismatch_20ms_30ms) + { + switch_core_session_t *session1 = NULL; + switch_core_session_t *session2 = NULL; + switch_core_session_t *session3 = NULL; + + switch_channel_t *channel1 = NULL; + switch_channel_t *channel2 = NULL; + switch_channel_t *channel3 = NULL; + + switch_status_t status; + switch_call_cause_t cause; + switch_stream_handle_t stream = { 0 }; + char eavesdrop_command[256] = { 0 }; + char rec_path[256]; + char rec_uuid[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 }; + char eaves_dialstr[256] = { 0 }; + + switch_uuid_str(rec_uuid, sizeof(rec_uuid)); + + /*parked 20 ms ptime */ + status = switch_ivr_originate(NULL, &session1, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332220", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session1); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel1 = switch_core_session_get_channel(session1); + fst_requires(channel1); + + snprintf(eaves_dialstr, sizeof(eaves_dialstr), "{ignore_early_media=true}{sip_h_X-UnitTestRecfile=%s}sofia/gateway/eavestest/+15553332230", rec_uuid); + + /*eavesdropper 20 ms ptime*/ + status = switch_ivr_originate(NULL, &session2, &cause, eaves_dialstr, 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session2); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel2 = switch_core_session_get_channel(session2); + fst_requires(channel2); + + /*milliwatt tone 30 ms ptime*/ + status = switch_ivr_originate(NULL, &session3, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332222", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session3); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel3 = switch_core_session_get_channel(session3); + fst_requires(channel3); + + SWITCH_STANDARD_STREAM(stream); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_bridge %s %s", switch_core_session_get_uuid(session1), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session1, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command),"uuid_setvar_multi %s eavesdrop_enable_dtmf=false;eavesdrop_whisper_bleg=true;eavesdrop_whisper_aleg=false", switch_core_session_get_uuid(session3)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_transfer %s 'eavesdrop:%s' inline", switch_core_session_get_uuid(session3), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + switch_safe_free(stream.data); + + sleep(5); // it will record ~ 5 secs + + snprintf(rec_path, sizeof(rec_path), "/tmp/eaves-%s.wav", rec_uuid); + + fst_requires(switch_file_exists(rec_path, fst_pool) == SWITCH_STATUS_SUCCESS); + + fst_requires(test_detect_long_tone_in_file(rec_path, 8000, 300, 20) == SWITCH_STATUS_SUCCESS); + + unlink(rec_path); + + switch_channel_hangup(channel1, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel2, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel3, SWITCH_CAUSE_NORMAL_CLEARING); + + switch_core_session_rwunlock(session1); + switch_core_session_rwunlock(session2); + switch_core_session_rwunlock(session3); + + } + FST_TEST_END() + + FST_TEST_BEGIN(test_eavesdrop_bridged_ptime_mismatch_30ms_20ms) + { + switch_core_session_t *session1 = NULL; + switch_core_session_t *session2 = NULL; + switch_core_session_t *session3 = NULL; + + switch_channel_t *channel1 = NULL; + switch_channel_t *channel2 = NULL; + switch_channel_t *channel3 = NULL; + + switch_status_t status; + switch_call_cause_t cause; + switch_stream_handle_t stream = { 0 }; + char eavesdrop_command[256] = { 0 }; + char rec_path[256]; + char rec_uuid[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 }; + char eaves_dialstr[256] = { 0 }; + + switch_uuid_str(rec_uuid, sizeof(rec_uuid)); + + /*parked 30 ms ptime */ + status = switch_ivr_originate(NULL, &session1, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332231", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session1); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel1 = switch_core_session_get_channel(session1); + fst_requires(channel1); + + snprintf(eaves_dialstr, sizeof(eaves_dialstr), "{ignore_early_media=true}{sip_h_X-UnitTestRecfile=%s}sofia/gateway/eavestest/+15553332240", rec_uuid); + + /*eavesdropper 30 ms ptime*/ + status = switch_ivr_originate(NULL, &session2, &cause, eaves_dialstr, 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session2); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel2 = switch_core_session_get_channel(session2); + fst_requires(channel2); + + /*milliwatt tone 20 ms ptime*/ + status = switch_ivr_originate(NULL, &session3, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332226", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session3); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel3 = switch_core_session_get_channel(session3); + fst_requires(channel3); + + SWITCH_STANDARD_STREAM(stream); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_bridge %s %s", switch_core_session_get_uuid(session1), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session1, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command),"uuid_setvar_multi %s eavesdrop_enable_dtmf=false;eavesdrop_whisper_bleg=true;eavesdrop_whisper_aleg=false", switch_core_session_get_uuid(session3)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_transfer %s 'eavesdrop:%s' inline", switch_core_session_get_uuid(session3), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + switch_safe_free(stream.data); + + sleep(5); // it will record ~ 5 secs + + snprintf(rec_path, sizeof(rec_path), "/tmp/eaves-%s.wav", rec_uuid); + + fst_requires(switch_file_exists(rec_path, fst_pool) == SWITCH_STATUS_SUCCESS); + + fst_requires(test_detect_long_tone_in_file(rec_path, 8000, 300, 30) == SWITCH_STATUS_SUCCESS); + + unlink(rec_path); + + switch_channel_hangup(channel1, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel2, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel3, SWITCH_CAUSE_NORMAL_CLEARING); + + switch_core_session_rwunlock(session1); + switch_core_session_rwunlock(session2); + switch_core_session_rwunlock(session3); + + } + FST_TEST_END() + + FST_TEST_BEGIN(test_eavesdrop_bridged_ptime_mismatch_reneg) + { + switch_core_session_t *session1 = NULL; + switch_core_session_t *session2 = NULL; + switch_core_session_t *session3 = NULL; + + switch_channel_t *channel1 = NULL; + switch_channel_t *channel2 = NULL; + switch_channel_t *channel3 = NULL; + + switch_status_t status; + switch_call_cause_t cause; + switch_stream_handle_t stream = { 0 }; + char eavesdrop_command[256] = { 0 }; + char rec_path[256]; + char rec_uuid[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 }; + char eaves_dialstr[256] = { 0 }; + + switch_uuid_str(rec_uuid, sizeof(rec_uuid)); + + /*parked 30 ms ptime */ + status = switch_ivr_originate(NULL, &session1, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332231", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session1); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel1 = switch_core_session_get_channel(session1); + fst_requires(channel1); + + snprintf(eaves_dialstr, sizeof(eaves_dialstr), "{ignore_early_media=true}{sip_h_X-UnitTestRecfile=%s}sofia/gateway/eavestest/+15553332240", rec_uuid); + + /*eavesdropper 30 ms ptime*/ + status = switch_ivr_originate(NULL, &session2, &cause, eaves_dialstr, 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session2); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel2 = switch_core_session_get_channel(session2); + fst_requires(channel2); + + /*milliwatt tone 20 ms ptime*/ + status = switch_ivr_originate(NULL, &session3, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332226", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session3); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel3 = switch_core_session_get_channel(session3); + fst_requires(channel3); + + SWITCH_STANDARD_STREAM(stream); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_bridge %s %s", switch_core_session_get_uuid(session1), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session1, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command),"uuid_setvar_multi %s eavesdrop_enable_dtmf=false;eavesdrop_whisper_bleg=true;eavesdrop_whisper_aleg=false", switch_core_session_get_uuid(session3)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_transfer %s 'eavesdrop:%s' inline", switch_core_session_get_uuid(session3), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + + sleep(2); + + // codec reneg for eavesdropper + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_media_reneg %s = PCMU@20i", switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + + sleep(1); + + // codec reneg for eavesdroppee + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_media_reneg %s = PCMU@30i", switch_core_session_get_uuid(session3)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + switch_safe_free(stream.data); + + sleep(2); + + snprintf(rec_path, sizeof(rec_path), "/tmp/eaves-%s.wav", rec_uuid); + + fst_requires(switch_file_exists(rec_path, fst_pool) == SWITCH_STATUS_SUCCESS); + + fst_requires(test_detect_long_tone_in_file(rec_path, 8000, 300, 30) == SWITCH_STATUS_SUCCESS); + + unlink(rec_path); + + switch_channel_hangup(channel1, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel2, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel3, SWITCH_CAUSE_NORMAL_CLEARING); + + switch_core_session_rwunlock(session1); + switch_core_session_rwunlock(session2); + switch_core_session_rwunlock(session3); + + } + FST_TEST_END() + +} +FST_SUITE_END() +} +FST_CORE_END() +