diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h index c4d4e6d12b..8a7a80e908 100644 --- a/src/include/switch_channel.h +++ b/src/include/switch_channel.h @@ -262,6 +262,9 @@ SWITCH_DECLARE(switch_event_header_t *) switch_channel_variable_first(switch_cha */ SWITCH_DECLARE(void) switch_channel_variable_last(switch_channel_t *channel); + +SWITCH_DECLARE(switch_status_t) switch_channel_caller_extension_masquerade(switch_channel_t *orig_channel, switch_channel_t *new_channel, uint32_t offset); + /*! \brief Assign a caller extension to a given channel \param channel channel to assign extension to diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 5ec7e03b79..0c281cfd48 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -4272,9 +4272,7 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t switch_channel_set_variable(channel_b, SWITCH_HOLDING_UUID_VARIABLE, br_a); switch_channel_set_flag(channel_b, CF_XFER_ZOMBIE); switch_channel_set_flag(channel_b, CF_TRANSFER); - - //switch_channel_set_variable(channel_b, "park_timeout", "2"); - //switch_channel_set_state(channel_b, CS_PARK); + if ((a_session = switch_core_session_locate(br_a))) { const char *moh = profile->hold_music; @@ -4307,8 +4305,8 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), SIPTAG_EVENT_STR(etmp), TAG_END()); - - if (0 && b_tech_pvt) { + + if (1 && b_tech_pvt) { sofia_set_flag_locked(b_tech_pvt, TFLAG_BYE); nua_bye(b_tech_pvt->nh, SIPTAG_REASON_STR("Q.850;cause=16;text=\"normal_clearing\""), diff --git a/src/switch_channel.c b/src/switch_channel.c index fe7b02e1ce..acefb6ed81 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -1664,6 +1664,72 @@ SWITCH_DECLARE(void) switch_channel_clear_state_handler(switch_channel_t *channe switch_mutex_unlock(channel->state_mutex); } +SWITCH_DECLARE(switch_status_t) switch_channel_caller_extension_masquerade(switch_channel_t *orig_channel, switch_channel_t *new_channel, uint32_t offset) +{ + switch_caller_profile_t *caller_profile; + switch_caller_extension_t *extension = NULL, *orig_extension = NULL; + switch_caller_application_t *ap; + switch_status_t status = SWITCH_STATUS_FALSE; + switch_event_header_t *hi = NULL; + const char *no_copy = switch_channel_get_variable(orig_channel, "attended_transfer_no_copy"); + char *dup; + int i, argc = 0; + char *argv[128]; + + if (no_copy) { + dup = switch_core_session_strdup(new_channel->session, no_copy); + argc = switch_separate_string(dup, ',', argv, (sizeof(argv) / sizeof(argv[0]))); + } + + + switch_mutex_lock(orig_channel->profile_mutex); + switch_mutex_lock(new_channel->profile_mutex); + + + caller_profile = switch_caller_profile_clone(new_channel->session, new_channel->caller_profile); + switch_assert(caller_profile); + extension = switch_caller_extension_new(new_channel->session, caller_profile->destination_number, caller_profile->destination_number); + orig_extension = switch_channel_get_caller_extension(orig_channel); + + + if (extension && orig_extension) { + for(ap = orig_extension->current_application; ap && offset > 0; offset--) { + ap = ap->next; + } + + for (; ap; ap = ap->next) { + switch_caller_extension_add_application(new_channel->session, extension, ap->application_name, ap->application_data); + } + + caller_profile->destination_number = switch_core_strdup(caller_profile->pool, orig_channel->caller_profile->destination_number); + switch_channel_set_caller_profile(new_channel, caller_profile); + switch_channel_set_caller_extension(new_channel, extension); + + for (hi = orig_channel->variables->headers; hi; hi = hi->next) { + int ok = 1; + for (i = 0; i < argc; i++) { + if (!strcasecmp(argv[i], hi->name)) { + ok = 0; + break; + } + } + + if (!ok) continue; + + switch_channel_set_variable(new_channel, hi->name, hi->value); + } + + status = SWITCH_STATUS_SUCCESS; + } + + + switch_mutex_unlock(new_channel->profile_mutex); + switch_mutex_unlock(orig_channel->profile_mutex); + + + return status; +} + SWITCH_DECLARE(void) switch_channel_set_caller_extension(switch_channel_t *channel, switch_caller_extension_t *caller_extension) { switch_assert(channel != NULL); diff --git a/src/switch_ivr_bridge.c b/src/switch_ivr_bridge.c index 8f9cba49d1..66667cea2a 100644 --- a/src/switch_ivr_bridge.c +++ b/src/switch_ivr_bridge.c @@ -569,7 +569,8 @@ static switch_status_t audio_bridge_on_exchange_media(switch_core_session_t *ses } else if (state < CS_HANGUP && (var = switch_channel_get_variable(channel, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE))) { transfer_after_bridge(session, var); } else { - if (!switch_channel_test_flag(channel, CF_TRANSFER) && !switch_channel_test_flag(channel, CF_REDIRECT) && bd && !bd->clean_exit + if (!switch_channel_test_flag(channel, CF_TRANSFER) && !switch_channel_test_flag(channel, CF_REDIRECT) && + !switch_channel_test_flag(channel, CF_XFER_ZOMBIE) && bd && !bd->clean_exit && state != CS_PARK && state != CS_ROUTING && !switch_channel_test_flag(channel, CF_INNER_BRIDGE)) { switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); } @@ -1082,8 +1083,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_multi_threaded_bridge(switch_core_ses state = switch_channel_get_state(caller_channel); - if (!switch_channel_test_flag(caller_channel, CF_TRANSFER) && !switch_channel_test_flag(caller_channel, CF_REDIRECT) - && !a_leg->clean_exit && !inner_bridge) { + if (!switch_channel_test_flag(caller_channel, CF_TRANSFER) && !switch_channel_test_flag(caller_channel, CF_REDIRECT) && + !switch_channel_test_flag(caller_channel, CF_XFER_ZOMBIE) && !a_leg->clean_exit && !inner_bridge) { if ((state != CS_EXECUTE && state != CS_SOFT_EXECUTE && state != CS_PARK && state != CS_ROUTING) || (switch_channel_test_flag(peer_channel, CF_ANSWERED) && state < CS_HANGUP)) { if (switch_true(switch_channel_get_variable(caller_channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE))) { diff --git a/src/switch_ivr_originate.c b/src/switch_ivr_originate.c index 5b7c3a9a57..218098e510 100644 --- a/src/switch_ivr_originate.c +++ b/src/switch_ivr_originate.c @@ -2078,6 +2078,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess if (switch_channel_media_ready(caller_channel)) { tstatus = switch_core_session_read_frame(oglobals.session, &read_frame, SWITCH_IO_FLAG_NONE, 0); if (!SWITCH_READ_ACCEPTABLE(tstatus)) { + if (switch_channel_test_flag(caller_channel, CF_XFER_ZOMBIE)) { + continue; + } break; } } else { @@ -2133,6 +2136,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess } if (switch_core_session_write_frame(oglobals.session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { + if (switch_channel_test_flag(caller_channel, CF_XFER_ZOMBIE)) { + continue; + } break; } } @@ -2156,7 +2162,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess } } - if (caller_channel && !switch_channel_ready(caller_channel)) { + if (caller_channel && !switch_channel_ready(caller_channel) && !switch_channel_test_flag(caller_channel, CF_XFER_ZOMBIE)) { oglobals.idx = IDX_CANCEL; } @@ -2206,7 +2212,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess const char *context = switch_channel_get_variable(peer_channel, "context"); const char *dialplan = switch_channel_get_variable(peer_channel, "dialplan"); switch_core_session_t *holding_session; - + if (caller_channel) { if (switch_strlen_zero(context)) { context = switch_channel_get_variable(caller_channel, "context"); @@ -2225,7 +2231,21 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess } if ((holding_session = switch_core_session_locate(holding))) { - switch_ivr_session_transfer(holding_session, dest, dialplan, context); + switch_channel_t *holding_channel = switch_core_session_get_channel(holding_session); + switch_status_t mstatus = SWITCH_STATUS_FALSE; + + if (caller_channel) { + if ((mstatus = switch_channel_caller_extension_masquerade(caller_channel, holding_channel, 1)) == SWITCH_STATUS_SUCCESS) { + switch_channel_set_state(holding_channel, CS_RESET); + switch_channel_wait_for_state_timeout(holding_channel, CS_RESET, 5000); + switch_channel_set_state(holding_channel, CS_EXECUTE); + } + } + + if (mstatus != SWITCH_STATUS_SUCCESS) { + switch_ivr_session_transfer(holding_session, dest, dialplan, context); + } + switch_core_session_rwunlock(holding_session); holding = NULL; holding_session = NULL; @@ -2233,6 +2253,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess switch_channel_hangup(peer_channel, SWITCH_CAUSE_ATTENDED_TRANSFER); switch_core_session_rwunlock(peer_session); + force_reason = SWITCH_CAUSE_ATTENDED_TRANSFER; } else { if (peer_channel && switch_channel_ready(peer_channel)) { force_reason = SWITCH_CAUSE_ATTENDED_TRANSFER; @@ -2273,9 +2294,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess if (i != oglobals.idx) { holding = NULL; - if (force_reason != SWITCH_CAUSE_NONE) { - reason = force_reason; - } else if (oglobals.idx == IDX_TIMEOUT || to) { + if (oglobals.idx == IDX_TIMEOUT || to) { reason = SWITCH_CAUSE_NO_ANSWER; } else { if (oglobals.idx == IDX_CANCEL) { @@ -2437,6 +2456,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(oglobals.session), SWITCH_LOG_DEBUG, "Originate Cancelled by originator termination Cause: %d [%s]\n", *cause, switch_channel_cause2str(*cause)); + } else if (oglobals.idx == IDX_TIMEOUT) { + *cause = SWITCH_CAUSE_NO_ANSWER; } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(oglobals.session), SWITCH_LOG_DEBUG, "Originate Resulted in Error Cause: %d [%s]\n", *cause, switch_channel_cause2str(*cause)); @@ -2561,6 +2582,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess switch_channel_clear_flag(caller_channel, CF_XFER_ZOMBIE); } + if (force_reason != SWITCH_CAUSE_NONE) { + *cause = force_reason; + } + return status; }