From b11955db0bfdcccd5ad98fd689870153d31154f3 Mon Sep 17 00:00:00 2001 From: Hristo Trendev Date: Wed, 21 Jun 2017 19:00:52 +0200 Subject: [PATCH] FS-10416: [mod_commands] add new outbound channels to an in-progress originate This allows new endpoints (outbound channels) to be called, after an originate is already in progress, where any of the originally called endpoints need to continue to ring. One use case would be to convert a 302 Moved Temporarily destination to SIP endpoint(s) and then to add the new endpoints to an in-progress originate, without cancelling any of the other (already ringing) outbound channels. --- src/include/switch_types.h | 1 + .../applications/mod_commands/mod_commands.c | 28 ++++++++++++- src/switch_core_session.c | 1 + src/switch_ivr_originate.c | 41 ++++++++++++++++--- 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 2cc1000e2f..d9cd67332f 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -1547,6 +1547,7 @@ typedef enum { CF_AWAITING_STREAM_CHANGE, CF_PROCESSING_STREAM_CHANGE, CF_STREAM_CHANGED, + CF_ADD_ENDPOINTS, /* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */ /* IF YOU ADD NEW ONES CHECK IF THEY SHOULD PERSIST OR ZERO THEM IN switch_core_session.c switch_core_session_request_xml() */ CF_FLAG_MAX diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 758e8c9570..7b0add370e 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -4815,7 +4815,7 @@ SWITCH_STANDARD_API(pause_function) return SWITCH_STATUS_SUCCESS; } -#define ORIGINATE_SYNTAX " |&() [] [] [] [] []" +#define ORIGINATE_SYNTAX " |&() [] [] [] [] [] []" SWITCH_STANDARD_API(originate_function) { switch_channel_t *caller_channel; @@ -4841,7 +4841,7 @@ SWITCH_STANDARD_API(originate_function) switch_assert(mycmd); argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); - if (argc < 2 || argc > 7) { + if (argc < 2 || argc > 8) { stream->write_function(stream, "-USAGE: %s\n", ORIGINATE_SYNTAX); goto done; } @@ -4871,6 +4871,30 @@ SWITCH_STANDARD_API(originate_function) timeout = atoi(argv[6]); } + /* It is OK to use the caller_session and caller_channel variables instead of adding new ones, since this isn't a real originate */ + if (argv[7]) { + caller_session = switch_core_session_locate(argv[7]); + if (caller_session) { + caller_channel = switch_core_session_get_channel(caller_session); + if (caller_channel) { + if (switch_channel_test_flag(caller_channel, CF_ORIGINATOR) && switch_channel_test_flag(caller_channel, CF_ADD_ENDPOINTS) && + !switch_channel_get_variable(caller_channel, "originate_add_endpoints")) { + switch_channel_set_variable(caller_channel, "originate_add_endpoints", aleg); + stream->write_function(stream, "+OK %s\n", switch_core_session_get_uuid(caller_session)); + } else { + stream->write_function(stream, "-ERR originator is in the wrong state (originator: %d, add endpoints: %d, var: %s)\n", + switch_channel_test_flag(caller_channel, CF_ORIGINATOR), + switch_channel_test_flag(caller_channel, CF_ADD_ENDPOINTS), + switch_channel_get_variable(caller_channel, "originate_add_endpoints")); + } + } + switch_core_session_rwunlock(caller_session); + } else { + stream->write_function(stream, "-ERR originator session not found\n"); + } + goto done; + } + if (switch_ivr_originate(NULL, &caller_session, &cause, aleg, timeout, NULL, cid_name, cid_num, NULL, NULL, SOF_NONE, NULL) != SWITCH_STATUS_SUCCESS || !caller_session) { stream->write_function(stream, "-ERR %s\n", switch_channel_cause2str(cause)); diff --git a/src/switch_core_session.c b/src/switch_core_session.c index a810cc45cd..c81cbe969b 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -2153,6 +2153,7 @@ SWITCH_DECLARE(switch_core_session_t *) switch_core_session_request_xml(switch_e flags[CF_SIMPLIFY] = 0; flags[CF_VIDEO_READY] = 0; flags[CF_VIDEO_DECODED_READ] = 0; + flags[CF_ADD_ENDPOINTS] = 0; if (!(session = switch_core_session_request_uuid(endpoint_interface, direction, SOF_NO_LIMITS, pool, uuid))) { return NULL; diff --git a/src/switch_ivr_originate.c b/src/switch_ivr_originate.c index a8b9ddfb44..bcfa7ee18d 100644 --- a/src/switch_ivr_originate.c +++ b/src/switch_ivr_originate.c @@ -1929,6 +1929,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess originate_status_t originate_status[MAX_PEERS] = { {0} }; switch_originate_flag_t dftflags = SOF_NONE, myflags = dftflags; char *pipe_names[MAX_PEERS] = { 0 }; + const char *newep = NULL; char *data = NULL; switch_status_t status = SWITCH_STATUS_SUCCESS; switch_channel_t *caller_channel = NULL; @@ -1941,7 +1942,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess time_t start, global_start; switch_time_t last_retry_start = 0; switch_frame_t *read_frame = NULL; - int r = 0, i, and_argc = 0, or_argc = 0; + int r = 0, i, and_argc = 0, or_argc = 0, and_argc_offset = 0; int32_t sleep_ms = 1000, try = 0, retries = 1, retry_timelimit_sec = 0; int32_t min_retry_period_ms = sleep_ms; switch_codec_t write_codec = { 0 }; @@ -2613,6 +2614,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess last_retry_start = switch_micro_time_now(); } + add_endpoints: p = pipe_names[r]; while (p && *p) { @@ -2646,20 +2648,20 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess p++; } - and_argc = switch_separate_string(pipe_names[r], ',', peer_names, (sizeof(peer_names) / sizeof(peer_names[0]))); + and_argc += switch_separate_string(pipe_names[r], ',', peer_names, (sizeof(peer_names) / sizeof(peer_names[0]))); if ((flags & SOF_NOBLOCK) && and_argc > 1) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Only calling the first element in the list in this mode.\n"); and_argc = 1; } - for (i = 0; i < and_argc; i++) { + for (i = and_argc_offset; i < and_argc; i++) { const char *current_variable; switch_event_t *local_var_event = NULL, *originate_var_event = NULL; end = NULL; - chan_type = peer_names[i]; + chan_type = peer_names[i-and_argc_offset]; /* strip leading spaces */ @@ -3055,7 +3057,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess for (;;) { uint32_t valid_channels = 0; - for (i = 0; i < and_argc; i++) { + for (i = and_argc_offset; i < and_argc; i++) { int state; time_t elapsed; @@ -3135,6 +3137,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess if (caller_channel) { soft_holding = switch_channel_get_variable(caller_channel, SWITCH_SOFT_HOLDING_UUID_VARIABLE); + switch_channel_set_flag(caller_channel, CF_ADD_ENDPOINTS); } while ((!caller_channel || switch_channel_ready(caller_channel) || switch_channel_test_flag(caller_channel, CF_XFER_ZOMBIE)) && @@ -3279,6 +3282,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess break; case SWITCH_STATUS_BREAK: status = SWITCH_STATUS_FALSE; + switch_channel_clear_flag(caller_channel, CF_ADD_ENDPOINTS); + switch_channel_set_variable(caller_channel, "originate_add_endpoints", NULL); goto done; break; default: @@ -3382,6 +3387,17 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess do_continue: + if(caller_channel) { + newep = switch_channel_get_variable(caller_channel, "originate_add_endpoints"); + if (newep) { + switch_channel_set_variable(caller_channel, "originate_add_endpoints", NULL); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Add new originate endpoint(s): %s\n", newep); + and_argc_offset = and_argc; + pipe_names[r] = strdup(newep); + goto add_endpoints; + } + } + if (!read_packet) { switch_yield(20000); } @@ -3390,6 +3406,21 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess notready: if (caller_channel) { + + switch_channel_clear_flag(caller_channel, CF_ADD_ENDPOINTS); + newep = switch_channel_get_variable(caller_channel, "originate_add_endpoints"); + if (newep) { + switch_channel_set_variable(caller_channel, "originate_add_endpoints", NULL); + + /* Only add new endpoints at this stage, if it's not originator cancel and if no outbound leg was aswered */ + if(oglobals.idx != IDX_CANCEL && oglobals.hups == and_argc) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Add new originate endpoints: %s\n", newep); + and_argc_offset = and_argc; + pipe_names[r] = strdup(newep); + goto add_endpoints; + } + } + holding = switch_channel_get_variable(caller_channel, SWITCH_HOLDING_UUID_VARIABLE); switch_channel_set_variable(caller_channel, SWITCH_HOLDING_UUID_VARIABLE, NULL);