diff --git a/src/include/switch_caller.h b/src/include/switch_caller.h index 3f702fae08..506c01207b 100644 --- a/src/include/switch_caller.h +++ b/src/include/switch_caller.h @@ -185,6 +185,22 @@ SWITCH_DECLARE(void) switch_caller_extension_add_application(_In_ switch_core_se _In_ switch_caller_extension_t *caller_extension, _In_z_ const char *application_name, _In_z_ const char *extra_data); +/*! + \brief Insert an application (instruction) to the given extension, before any of the low priority applications + \param session session associated with the extension (bound by scope) + \param caller_extension extension to add the application to + \param application_names the name of the applications + \param application_data optional argument to the applications + \param app_count number of applications in the array + \param low_priority_applications array of application names to insert before + \param low_priority_count number of applications in the array +*/ +SWITCH_DECLARE(void) switch_caller_extension_insert_applications(_In_ switch_core_session_t *session, + _In_ switch_caller_extension_t *caller_extension, + _In_z_ const char **application_names, _In_z_ const char **application_data, + _In_z_ size_t app_count, _In_opt_ char **low_priority_applications, + _In_z_ size_t low_priority_count); + /*! \brief Add an application (instruction) to the given extension \param session session associated with the extension (bound by scope) diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h index 7fbbce968e..aad0d7d8b6 100644 --- a/src/include/switch_channel.h +++ b/src/include/switch_channel.h @@ -720,6 +720,8 @@ SWITCH_DECLARE(switch_status_t) switch_channel_bind_device_state_handler(switch_ SWITCH_DECLARE(switch_status_t) switch_channel_unbind_device_state_handler(switch_device_state_function_t function); SWITCH_DECLARE(const char *) switch_channel_device_state2str(switch_device_state_t device_state); SWITCH_DECLARE(switch_status_t) switch_channel_pass_sdp(switch_channel_t *from_channel, switch_channel_t *to_channel, const char *sdp); +SWITCH_DECLARE(void) switch_channel_set_post_dialplan_function(switch_channel_t *channel, switch_post_dialplan_function_t function); +SWITCH_DECLARE(switch_post_dialplan_function_t) switch_channel_get_post_dialplan_function(switch_channel_t *channel); SWITCH_END_EXTERN_C #endif diff --git a/src/include/switch_types.h b/src/include/switch_types.h index bc773ebe42..8ba35725ba 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -2571,6 +2571,8 @@ typedef switch_status_t (*switch_input_callback_function_t) (switch_core_session typedef switch_status_t (*switch_read_frame_callback_function_t) (switch_core_session_t *session, switch_frame_t *frame, void *user_data); typedef struct switch_say_interface switch_say_interface_t; +typedef void (*switch_post_dialplan_function_t) (switch_core_session_t *, switch_caller_extension_t *, const char *); + #define DMACHINE_MAX_DIGIT_LEN 512 typedef enum { diff --git a/src/switch_caller.c b/src/switch_caller.c index 4485920c01..3250cdf0d7 100644 --- a/src/switch_caller.c +++ b/src/switch_caller.c @@ -570,8 +570,6 @@ SWITCH_DECLARE(void) switch_caller_extension_add_application(switch_core_session caller_application->application_name = switch_core_session_strdup(session, application_name); caller_application->application_data = switch_core_session_strdup(session, application_data); - - if (caller_application->application_data && strstr(caller_application->application_data, "\\'")) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "App not added, Invalid character sequence in data string [%s]\n", caller_application->application_data); @@ -587,7 +585,92 @@ SWITCH_DECLARE(void) switch_caller_extension_add_application(switch_core_session caller_extension->last_application = caller_application; caller_extension->current_application = caller_extension->applications; } +} + +SWITCH_DECLARE(void) switch_caller_extension_insert_applications(switch_core_session_t *session, + switch_caller_extension_t *caller_extension, + const char **application_names, + const char **application_data, + size_t app_count, + char **low_priority_applications, + size_t low_priority_count) +{ + switch_caller_application_t *new_applications = NULL, *last_new_application = NULL; + switch_caller_application_t *prev = NULL, *current; + + switch_assert(session != NULL); + switch_assert(application_names != NULL); + switch_assert(application_data != NULL); + + // Allocate memory for all applications upfront + for (size_t i = 0; i < app_count; ++i) { + switch_caller_application_t *caller_application; + + if (zstr(application_names[i])) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, + "Skipping null or empty application name at index [%zu]\n", i); + continue; + } + // Allocate and populate new application + caller_application = switch_core_session_alloc(session, sizeof(switch_caller_application_t)); + caller_application->application_name = switch_core_session_strdup(session, application_names[i]); + caller_application->application_data = switch_core_session_strdup(session, application_data[i]); + + if (caller_application->application_data && strstr(caller_application->application_data, "\\'")) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, + "Skipping invalid character sequence in application data [%s]\n", + caller_application->application_data); + continue; + } + + // Append to the local new applications list + if (!new_applications) { + new_applications = caller_application; + } else { + last_new_application->next = caller_application; + } + last_new_application = caller_application; + } + + if (!new_applications) { + // No valid applications to insert + return; + } + + // Traverse the existing applications list to find the insertion point + current = caller_extension->applications; + while (current) { + for (size_t i = 0; i < low_priority_count; ++i) { + if (low_priority_applications[i] && strcmp(current->application_name, low_priority_applications[i]) == 0) { + // Insert the new applications before the current application + if (prev) { + prev->next = new_applications; + } else { + caller_extension->applications = new_applications; + } + last_new_application->next = current; + + // Update the last_application if needed + if (current == caller_extension->last_application) { + caller_extension->last_application = last_new_application; + } + return; + } + } + prev = current; + current = current->next; + } + + // Append to the end if no priority match was found + if (!caller_extension->applications) { + caller_extension->applications = new_applications; + } else if (caller_extension->last_application) { + caller_extension->last_application->next = new_applications; + } + + caller_extension->last_application = last_new_application; + caller_extension->current_application = caller_extension->applications; } /* For Emacs: diff --git a/src/switch_channel.c b/src/switch_channel.c index 96e79f21c5..129678ed7d 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -189,6 +189,7 @@ struct switch_channel { switch_device_node_t *device_node; char *device_id; switch_event_t *log_tags; + switch_post_dialplan_function_t post_dialplan_function; }; static void process_device_hup(switch_channel_t *channel); @@ -5846,6 +5847,22 @@ SWITCH_DECLARE(switch_status_t) switch_channel_pass_sdp(switch_channel_t *from_c return status; } +SWITCH_DECLARE(void) switch_channel_set_post_dialplan_function(switch_channel_t *channel, switch_post_dialplan_function_t function) +{ + assert(function != NULL); + if (channel) { + channel->post_dialplan_function = function; + } +} + +SWITCH_DECLARE(switch_post_dialplan_function_t) switch_channel_get_post_dialplan_function(switch_channel_t *channel) +{ + if (channel) { + return channel->post_dialplan_function; + } + return NULL; +} + /* For Emacs: * Local Variables: * mode:c diff --git a/src/switch_core_state_machine.c b/src/switch_core_state_machine.c index 28185a8935..32d3d933d3 100644 --- a/src/switch_core_state_machine.c +++ b/src/switch_core_state_machine.c @@ -238,6 +238,7 @@ static void switch_core_standard_on_routing(switch_core_session_t *session) switch_caller_extension_t *extension = NULL; char *expanded = NULL; char *dpstr = NULL; + const char *profile_name = NULL; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Standard ROUTING\n", switch_channel_get_name(session->channel)); @@ -289,6 +290,14 @@ static void switch_core_standard_on_routing(switch_core_session_t *session) UNPROTECT_INTERFACE(dialplan_interface); if (extension) { + profile_name = switch_channel_get_variable(session->channel, "sofia_profile_name"); + if (!zstr(profile_name)) { + switch_post_dialplan_function_t func = switch_channel_get_post_dialplan_function(session->channel); + if (func) { + func(session, extension, profile_name); + } + } + switch_channel_set_caller_extension(session->channel, extension); switch_channel_set_state(session->channel, CS_EXECUTE); goto end; @@ -370,9 +379,7 @@ static void switch_core_standard_on_execute(switch_core_session_t *session) switch_channel_set_variable(session->channel, "next_application_on_execute", ""); switch_channel_set_variable(session->channel, "next_application_data_on_execute", ""); } - - - + while (switch_channel_get_state(session->channel) == CS_EXECUTE && extension->current_application) { switch_caller_application_t *current_application = extension->current_application; diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index e2cec69089..17257a4b6c 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -4,7 +4,7 @@ noinst_PROGRAMS = switch_event switch_hash switch_ivr_originate switch_utils swi switch_ivr_play_say switch_core_codec switch_rtp switch_xml noinst_PROGRAMS += switch_core_video switch_core_db switch_vad switch_packetizer switch_core_session test_sofia switch_ivr_async switch_core_asr switch_log -noinst_PROGRAMS+= switch_hold switch_sip +noinst_PROGRAMS+= switch_hold switch_sip switch_mod_telnyx if HAVE_PCAP noinst_PROGRAMS += switch_rtp_pcap diff --git a/tests/unit/conf_telnyx/freeswitch.xml b/tests/unit/conf_telnyx/freeswitch.xml new file mode 100644 index 0000000000..ebc25718b5 --- /dev/null +++ b/tests/unit/conf_telnyx/freeswitch.xml @@ -0,0 +1,211 @@ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + +
+
diff --git a/tests/unit/switch_mod_telnyx.c b/tests/unit/switch_mod_telnyx.c new file mode 100644 index 0000000000..ca28443dcc --- /dev/null +++ b/tests/unit/switch_mod_telnyx.c @@ -0,0 +1,96 @@ +#include +#include + +#include + +int timeout_sec = 10; + +FST_CORE_BEGIN("./conf_telnyx") +{ + FST_SUITE_BEGIN(switch_mod_telnyx) + { + FST_SETUP_BEGIN() + { + fst_requires_module("mod_telnyx"); + } + FST_SETUP_END() + + FST_TEARDOWN_BEGIN() + { + } + FST_TEARDOWN_END() + + FST_SESSION_BEGIN(test_switch_caller_extension_insert_applications) + { + switch_caller_extension_t *caller_extension = NULL; + + // Prepare test data + const char *application_names[] = { "app1", "app2", "app3" }; + const char *application_data[] = { "data1", "data2", "data3" }; + const size_t app_count = sizeof(application_names) / sizeof(application_names[0]); + + // Low priority applications (ensure they remain at the end) + char *low_priority[] = { "low_priority_app" }; + const size_t low_priority_count = sizeof(low_priority) / sizeof(low_priority[0]); + switch_caller_application_t *current; + switch_caller_application_t *low_prio; + + // Initialize a caller extension + caller_extension = switch_core_session_alloc(fst_session, sizeof(switch_caller_extension_t)); + fst_check(caller_extension != NULL); + + switch_caller_extension_add_application(fst_session, caller_extension, "low_priority_app", "data"); + low_prio = caller_extension->applications; + + // Insert applications into the caller extension + switch_caller_extension_insert_applications( + fst_session, + caller_extension, + application_names, + application_data, + app_count, + low_priority, + low_priority_count + ); + + // Verify the applications are inserted correctly + current = caller_extension->applications; + + // Check the order of applications + for (size_t i = 0; i < app_count; ++i) { + fst_check(current != NULL); + fst_check(strcmp(current->application_name, application_names[i]) == 0); + fst_check(strcmp(current->application_data, application_data[i]) == 0); + current = current->next; + } + + // Verify the last_application pointer + fst_check(caller_extension->last_application != NULL); + fst_check(current == low_prio); + } + FST_SESSION_END() + + FST_TEST_BEGIN(post_dialplan) + { + switch_core_session_t *session = NULL; + switch_call_cause_t cause; + char *originate_str = switch_mprintf("sofia/gateway/test_gateway/+15553332901"); + + switch_ivr_originate(NULL, &session, &cause, originate_str, timeout_sec, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + switch_safe_free(originate_str); + fst_requires(session); + + if (session) { + switch_channel_t *channel = switch_core_session_get_channel(session); + + fst_requires(channel); + + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + switch_core_session_rwunlock(session); + } + } + FST_TEST_END() + } + FST_SUITE_END() +} +FST_CORE_END()