From 64d4d9ea574eb0e11a7dd1d1c0088293e97c31ff Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 4 May 2012 18:59:25 -0500 Subject: [PATCH] add pickup endpoint and app to dptools add pickup/keyname to forked dial, then route a call to call app pickup(keyname) to have your channel return from originate. sub to pickup+keyname or presence map the pickup proto to use on blf --- src/include/switch_types.h | 1 + .../applications/mod_dptools/mod_dptools.c | 504 +++++++++++++++++- src/mod/endpoints/mod_sofia/sofia_presence.c | 26 + src/switch_ivr_originate.c | 19 + 4 files changed, 549 insertions(+), 1 deletion(-) diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 3c1cb1cf7c..47336a800a 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -1211,6 +1211,7 @@ typedef enum { CF_SERVICE_VIDEO, CF_ZRTP_HASH, CF_ZRTP_PASS, + CF_CHANNEL_SWAP, /* 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_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 3331226e9d..4adfca6611 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -38,7 +38,8 @@ #include SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load); -SWITCH_MODULE_DEFINITION(mod_dptools, mod_dptools_load, NULL, NULL); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_dptools_shutdown); +SWITCH_MODULE_DEFINITION(mod_dptools, mod_dptools_load, mod_dptools_shutdown, NULL); SWITCH_STANDARD_DIALPLAN(inline_dialplan_hunt) { @@ -3094,6 +3095,484 @@ SWITCH_STANDARD_APP(audio_bridge_function) } } +static struct { + switch_memory_pool_t *pool; + switch_hash_t *pickup_hash; + switch_mutex_t *pickup_mutex; +} globals; + +/* pickup channel */ + + + +typedef struct pickup_node_s { + char *key; + char *uuid; + struct pickup_node_s *next; +} pickup_node_t; + + +#define PICKUP_PROTO "pickup" +static int EC = 0; + +static int pickup_count(const char *key_name) +{ + int count = 0; + pickup_node_t *head, *np; + + switch_mutex_lock(globals.pickup_mutex); + if ((head = switch_core_hash_find(globals.pickup_hash, key_name))) { + for (np = head; np; np = np->next) count++; + } + switch_mutex_unlock(globals.pickup_mutex); + + return count; + +} + +static void pickup_send_presence(const char *key_name) +{ + + char *domain_name, *dup_key_name = NULL, *dup_domain_name = NULL, *dup_id = NULL; + switch_event_t *event; + int count; + + + dup_key_name = strdup(key_name); + key_name = dup_key_name; + + if ((domain_name = strchr(dup_key_name, '@'))) { + *domain_name++ = '\0'; + } + + if (zstr(domain_name)) { + dup_domain_name = switch_core_get_variable_dup("domain"); + domain_name = dup_domain_name; + } + + if (zstr(domain_name)) { + domain_name = "cluecon.com"; + } + + dup_id = switch_mprintf("%s@%s", key_name, domain_name); + + count = pickup_count(dup_id); + + if (count > 0) { + if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", PICKUP_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", dup_id); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", dup_id); + + + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "force-status", "Active (%d call%s)", count, count == 1 ? "" : "s"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "active"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", key_name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "confirmed"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-direction", "inbound"); + switch_event_fire(&event); + } + } else { + if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", PICKUP_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", dup_id); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", dup_id); + + + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Idle"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", dup_id); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "terminated"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-direction", "inbound"); + switch_event_fire(&event); + } + } + + switch_safe_free(dup_domain_name); + switch_safe_free(dup_key_name); + switch_safe_free(dup_id); + +} + +static void pickup_pres_event_handler(switch_event_t *event) +{ + char *to = switch_event_get_header(event, "to"); + char *dup_to = NULL, *key_name, *dup_key_name = NULL, *domain_name, *dup_domain_name = NULL; + int count = 0; + + if (!to || strncasecmp(to, "pickup+", 7) || !strchr(to, '@')) { + return; + } + + if (!(dup_to = strdup(to))) { + return; + } + + key_name = dup_to + 7; + + if ((domain_name = strchr(key_name, '@'))) { + *domain_name++ = '\0'; + } else { + dup_domain_name = switch_core_get_variable_dup("domain"); + domain_name = dup_domain_name; + } + + if (zstr(domain_name)) { + switch_safe_free(dup_to); + return; + } + + dup_key_name = switch_mprintf("%q@%q", key_name, domain_name); + count = pickup_count(dup_key_name); + + switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN); + + if (count) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", PICKUP_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", key_name); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", key_name, domain_name); + + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "force-status", "Active (%d call%s)", count, count == 1 ? "" : "s"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "active"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", key_name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "confirmed"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-direction", "inbound"); + } else { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", PICKUP_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", key_name); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", key_name, domain_name); + + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Idle"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", key_name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "terminated"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-direction", "inbound"); + + } + + switch_event_fire(&event); + switch_safe_free(dup_to); + switch_safe_free(dup_key_name); + switch_safe_free(dup_domain_name); +} + + + +static void pickup_add_session(switch_core_session_t *session, const char *key) +{ + pickup_node_t *head, *node, *np; + char *dup_key = NULL; + + if (!strchr(key, '@')) { + dup_key = switch_mprintf("%s@%s", key, switch_core_get_variable("domain")); + key = dup_key; + } + + node = malloc(sizeof(*node)); + node->key = strdup(key); + node->uuid = strdup(switch_core_session_get_uuid(session)); + node->next = NULL; + + switch_mutex_lock(globals.pickup_mutex); + head = switch_core_hash_find(globals.pickup_hash, key); + + if (head) { + for (np = head; np && np->next; np = np->next); + np->next = node; + } else { + head = node; + switch_core_hash_insert(globals.pickup_hash, key, head); + } + + switch_mutex_unlock(globals.pickup_mutex); + + pickup_send_presence(key); + + switch_safe_free(dup_key); +} + +static char *pickup_pop_uuid(const char *key, const char *uuid) +{ + pickup_node_t *node = NULL, *head; + char *r = NULL; + char *dup_key = NULL; + + if (!strchr(key, '@')) { + dup_key = switch_mprintf("%s@%s", key, switch_core_get_variable("domain")); + key = dup_key; + } + + switch_mutex_lock(globals.pickup_mutex); + + if ((head = switch_core_hash_find(globals.pickup_hash, key))) { + + switch_core_hash_delete(globals.pickup_hash, key); + + if (uuid) { + pickup_node_t *np, *lp = NULL; + + for(np = head; np; np = np->next) { + if (!strcmp(np->uuid, uuid)) { + if (lp) { + lp->next = np->next; + } else { + head = np->next; + } + + node = np; + break; + } + + lp = np; + } + + } else { + node = head; + head = head->next; + } + + + if (head) { + switch_core_hash_insert(globals.pickup_hash, key, head); + } + } + + if (node) { + r = node->uuid; + free(node->key); + free(node); + } + + switch_mutex_unlock(globals.pickup_mutex); + + if (r) pickup_send_presence(key); + + switch_safe_free(dup_key); + + return r; +} + + +typedef struct pickup_pvt_s { + char *key; + switch_event_t *vars; +} pickup_pvt_t; + +switch_endpoint_interface_t *pickup_endpoint_interface; +static switch_call_cause_t pickup_outgoing_channel(switch_core_session_t *session, + switch_event_t *var_event, + switch_caller_profile_t *outbound_profile, + switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, + switch_call_cause_t *cancel_cause); +switch_io_routines_t pickup_io_routines = { + /*.outgoing_channel */ pickup_outgoing_channel +}; + +static switch_status_t pickup_event_handler(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_channel_state_t state = switch_channel_get_running_state(channel); + pickup_pvt_t *tech_pvt = switch_core_session_get_private(session); + + switch(state) { + case CS_DESTROY: + if (tech_pvt->vars) { + switch_event_destroy(&tech_pvt->vars); + } + break; + case CS_REPORTING: + return SWITCH_STATUS_FALSE; + case CS_HANGUP: + { + + if (switch_channel_test_flag(channel, CF_CHANNEL_SWAP)) { + const char *key = switch_channel_get_variable(channel, "channel_swap_uuid"); + switch_core_session_t *swap_session; + + if ((swap_session = switch_core_session_locate(key))) { + switch_channel_t *swap_channel = switch_core_session_get_channel(swap_session); + switch_channel_hangup(swap_channel, SWITCH_CAUSE_PICKED_OFF); + switch_core_session_rwunlock(swap_session); + } + switch_channel_clear_flag(channel, CF_CHANNEL_SWAP); + } + + pickup_pop_uuid(tech_pvt->key, switch_core_session_get_uuid(session)); + } + break; + default: + break; + } + + + return SWITCH_STATUS_SUCCESS; +} + +switch_state_handler_table_t pickup_event_handlers = { + /*.on_init */ pickup_event_handler, + /*.on_routing */ pickup_event_handler, + /*.on_execute */ pickup_event_handler, + /*.on_hangup */ pickup_event_handler, + /*.on_exchange_media */ pickup_event_handler, + /*.on_soft_execute */ pickup_event_handler, + /*.on_consume_media */ pickup_event_handler, + /*.on_hibernate */ pickup_event_handler, + /*.on_reset */ pickup_event_handler, + /*.on_park */ pickup_event_handler, + /*.on_reporting */ pickup_event_handler, + /*.on_destroy */ pickup_event_handler +}; + +static switch_call_cause_t pickup_outgoing_channel(switch_core_session_t *session, + switch_event_t *var_event, + switch_caller_profile_t *outbound_profile, + switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, + switch_call_cause_t *cancel_cause) +{ + char *pickup; + switch_call_cause_t cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + switch_core_session_t *nsession; + switch_channel_t *nchannel; + char *name; + pickup_pvt_t *tech_pvt; + switch_caller_profile_t *caller_profile; + + if (zstr(outbound_profile->destination_number)) { + goto done; + } + + pickup = outbound_profile->destination_number; + + + if (!(nsession = switch_core_session_request(pickup_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, flags, pool))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error Creating Session\n"); + goto error; + } + + tech_pvt = switch_core_session_alloc(nsession, sizeof(*tech_pvt)); + tech_pvt->key = switch_core_session_strdup(nsession, pickup); + switch_event_dup(&tech_pvt->vars, var_event); + + switch_core_session_set_private(nsession, tech_pvt); + + nchannel = switch_core_session_get_channel(nsession); + caller_profile = switch_caller_profile_clone(nsession, outbound_profile); + switch_channel_set_caller_profile(nchannel, caller_profile); + + switch_channel_set_state(nchannel, CS_ROUTING); + + + + *new_session = nsession; + cause = SWITCH_CAUSE_SUCCESS; + name = switch_core_session_sprintf(nsession, "pickup/%s", pickup); + switch_channel_set_name(nchannel, name); + switch_channel_set_variable(nchannel, "process_cdr", "false"); + pickup_add_session(nsession, pickup); + + goto done; + + error: + + if (nsession) { + switch_core_session_destroy(&nsession); + } + + if (pool) { + *pool = NULL; + } + + done: + + + return cause; +} + +#define PICKUP_SYNTAX "[]" +SWITCH_STANDARD_APP(pickup_function) +{ + char *uuid = NULL; + switch_core_session_t *pickup_session; + switch_channel_t *channel = switch_core_session_get_channel(session); + + if (zstr(data)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing data. Usage: pickup %s\n", PICKUP_SYNTAX); + return; + } + + if ((uuid = pickup_pop_uuid((char *)data, NULL))) { + if ((pickup_session = switch_core_session_locate(uuid))) { + switch_channel_t *pickup_channel = switch_core_session_get_channel(pickup_session); + switch_caller_profile_t *pickup_caller_profile = switch_channel_get_caller_profile(pickup_channel), + *caller_profile = switch_channel_get_caller_profile(channel); + const char *name, *num; + switch_event_t *event; + switch_event_header_t *hp; + pickup_pvt_t *tech_pvt = switch_core_session_get_private(pickup_session); + + for(hp = tech_pvt->vars->headers; hp; hp = hp->next) { + switch_channel_set_variable(channel, hp->name, hp->value); + } + + + switch_channel_set_flag(pickup_channel, CF_CHANNEL_SWAP); + switch_channel_set_variable(pickup_channel, "channel_swap_uuid", switch_core_session_get_uuid(session)); + + name = caller_profile->caller_id_name; + num = caller_profile->caller_id_number; + + caller_profile->caller_id_name = switch_core_strdup(caller_profile->pool, pickup_caller_profile->caller_id_name); + caller_profile->caller_id_number = switch_core_strdup(caller_profile->pool, pickup_caller_profile->caller_id_number); + + caller_profile->callee_id_name = name; + caller_profile->callee_id_number = num; + + if (switch_event_create(&event, SWITCH_EVENT_CALL_UPDATE) == SWITCH_STATUS_SUCCESS) { + const char *uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Direction", "RECV"); + + if (!uuid) { + uuid = switch_channel_get_variable(channel, "originate_signal_bond"); + } + + + if (uuid) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridged-To", uuid); + } + switch_channel_event_set_data(channel, event); + switch_event_fire(&event); + } + + + switch_channel_set_state(channel, CS_HIBERNATE); + + switch_channel_mark_answered(pickup_channel); + switch_core_session_rwunlock(pickup_session); + } + free(uuid); + } +} + + + + + /* fake chan_error */ switch_endpoint_interface_t *error_endpoint_interface; static switch_call_cause_t error_outgoing_channel(switch_core_session_t *session, @@ -3985,6 +4464,14 @@ static char *file_string_supported_formats[SWITCH_MAX_CODECS] = { 0 }; #define LOG_LONG_DESC "Logs a channel variable for the channel calling the application." #define TRANSFER_LONG_DESC "Immediately transfer the calling channel to a new extension" #define SLEEP_LONG_DESC "Pause the channel for a given number of milliseconds, consuming the audio for that period of time." + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_dptools_shutdown) +{ + switch_event_unbind_callback(pickup_pres_event_handler); + + return SWITCH_STATUS_SUCCESS; +} + SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) { switch_api_interface_t *api_interface; @@ -3993,9 +4480,16 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) switch_chat_interface_t *chat_interface; switch_file_interface_t *file_interface; + globals.pool = pool; + switch_core_hash_init(&globals.pickup_hash, NULL); + switch_mutex_init(&globals.pickup_mutex, SWITCH_MUTEX_NESTED, globals.pool); + /* connect my internal structure to the blank pointer passed to me */ *module_interface = switch_loadable_module_create_module_interface(pool, modname); + switch_event_bind(modname, SWITCH_EVENT_PRESENCE_PROBE, SWITCH_EVENT_SUBCLASS_ANY, pickup_pres_event_handler, NULL); + + file_string_supported_formats[0] = "file_string"; file_interface = (switch_file_interface_t *) switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE); @@ -4019,6 +4513,11 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) user_endpoint_interface->interface_name = "user"; user_endpoint_interface->io_routines = &user_io_routines; + pickup_endpoint_interface = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); + pickup_endpoint_interface->interface_name = "pickup"; + pickup_endpoint_interface->io_routines = &pickup_io_routines; + pickup_endpoint_interface->state_handler = &pickup_event_handlers; + SWITCH_ADD_CHAT(chat_interface, "event", event_chat_send); SWITCH_ADD_CHAT(chat_interface, "api", api_chat_send); @@ -4193,6 +4692,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) SWITCH_ADD_APP(app_interface, "limit_execute", "Limit", LIMITEXECUTE_DESC, limit_execute_function, LIMITEXECUTE_USAGE, SAF_SUPPORT_NOMEDIA); SWITCH_ADD_APP(app_interface, "limit_hash_execute", "Limit", LIMITHASHEXECUTE_DESC, limit_hash_execute_function, LIMITHASHEXECUTE_USAGE, SAF_SUPPORT_NOMEDIA); + SWITCH_ADD_APP(app_interface, "pickup", "Pickup", "Pickup a call", pickup_function, PICKUP_SYNTAX, SAF_SUPPORT_NOMEDIA); + + SWITCH_ADD_DIALPLAN(dp_interface, "inline", inline_dialplan_hunt); /* indicate that the module should continue to be loaded */ diff --git a/src/mod/endpoints/mod_sofia/sofia_presence.c b/src/mod/endpoints/mod_sofia/sofia_presence.c index 8dc58d1320..6befca3439 100644 --- a/src/mod/endpoints/mod_sofia/sofia_presence.c +++ b/src/mod/endpoints/mod_sofia/sofia_presence.c @@ -1761,6 +1761,20 @@ static int sofia_dialog_probe_callback(void *pArg, int argc, char **argv, char * remote_user = to_user; remote_host = local_host; } + else if (proto && !strcasecmp(proto, "pickup")) { + local_user = to_user; + local_user_param = switch_mprintf(";proto=%s", proto); + event_status = "hold"; + if (skip_proto) { + buf_to_free = switch_mprintf("sip:%s", to_user); + } else { + buf_to_free = switch_mprintf("sip:pickup+%s", to_user); + } + remote_uri = buf_to_free; + strcpy(remote_display_buf, "pickup"); + remote_user = to_user; + remote_host = local_host; + } else if (proto && !strcasecmp(proto, "conf")) { local_user = to_user; local_user_param = switch_mprintf(";proto=%s", proto); @@ -2626,6 +2640,18 @@ static int sofia_presence_sub_callback(void *pArg, int argc, char **argv, char * stream.write_function(&stream, "\n", uuid); } stream.write_function(&stream, "\n"); + } else if (!strcasecmp(proto, "pickup")) { + stream.write_function(&stream, "\nsip:%s@%s;proto=pickup\n", + !zstr(clean_to_user) ? clean_to_user : "unknown", host); + stream.write_function(&stream, "\n", !zstr(clean_to_user) ? clean_to_user : "unknown", host); + stream.write_function(&stream, "\n\n\n"); + stream.write_function(&stream, "\nsip:%s\n", uuid); + if (skip_proto) { + stream.write_function(&stream, "\n", uuid); + } else { + stream.write_function(&stream, "\n", uuid); + } + stream.write_function(&stream, "\n"); } else if (!strcasecmp(proto, "conf")) { stream.write_function(&stream, "\nsip:%s@%s;proto=conference\n", !zstr(clean_to_user) ? clean_to_user : "unknown", host); diff --git a/src/switch_ivr_originate.c b/src/switch_ivr_originate.c index 4a3067afc2..c3da16f072 100644 --- a/src/switch_ivr_originate.c +++ b/src/switch_ivr_originate.c @@ -3541,6 +3541,25 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess if (*bleg) { switch_channel_t *bchan = switch_core_session_get_channel(*bleg); + + if (switch_channel_test_flag(bchan, CF_CHANNEL_SWAP)) { + const char *key = switch_channel_get_variable(bchan, "channel_swap_uuid"); + switch_core_session_t *swap_session, *old_session; + + if ((swap_session = switch_core_session_locate(key))) { + switch_channel_clear_flag(bchan, CF_CHANNEL_SWAP); + switch_channel_hangup(bchan, SWITCH_CAUSE_PICKED_OFF); + + switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(bchan), SWITCH_LOG_DEBUG, "Swapping %s for %s\n", + switch_core_session_get_name(swap_session), switch_channel_get_name(bchan)); + + old_session = *bleg; + *bleg = swap_session; + bchan = switch_core_session_get_channel(*bleg); + switch_channel_answer(bchan); + switch_core_session_rwunlock(old_session); + } + } if (session && caller_channel) {