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.
This commit is contained in:
Hristo Trendev 2017-06-21 19:00:52 +02:00
parent dcc0bf72ec
commit b11955db0b
4 changed files with 64 additions and 7 deletions

View File

@ -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

View File

@ -4815,7 +4815,7 @@ SWITCH_STANDARD_API(pause_function)
return SWITCH_STATUS_SUCCESS;
}
#define ORIGINATE_SYNTAX "<call url> <exten>|&<application_name>(<app_args>) [<dialplan>] [<context>] [<cid_name>] [<cid_num>] [<timeout_sec>]"
#define ORIGINATE_SYNTAX "<call url> <exten>|&<application_name>(<app_args>) [<dialplan>] [<context>] [<cid_name>] [<cid_num>] [<timeout_sec>] [<originator_uuid>]"
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));

View File

@ -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;

View File

@ -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);