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()