diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h index 8a7a80e908..d9d0406a69 100644 --- a/src/include/switch_channel.h +++ b/src/include/switch_channel.h @@ -263,6 +263,8 @@ 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(void) switch_channel_restart(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); /*! diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 0c281cfd48..bc6f4f62ea 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -4146,6 +4146,34 @@ static void launch_nightmare_xfer(nightmare_xfer_helper_t *nhelper) /*---------------------------------------*/ +static switch_status_t xfer_hanguphook(switch_core_session_t *session) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_channel_state_t state = switch_channel_get_state(channel); + + if (state == CS_HANGUP) { + switch_core_session_t *ksession; + const char *uuid = switch_channel_get_variable(channel, "att_xfer_kill_uuid"); + + if (uuid && (ksession = switch_core_session_force_locate(uuid))) { + switch_channel_t *kchannel = switch_core_session_get_channel(ksession); + + switch_channel_clear_flag(kchannel, CF_XFER_ZOMBIE); + switch_channel_clear_flag(kchannel, CF_TRANSFER); + if (switch_channel_up(kchannel)) { + switch_channel_hangup(kchannel, SWITCH_CAUSE_NORMAL_CLEARING); + } + + switch_core_session_rwunlock(ksession); + } + + switch_core_event_hook_remove_state_change(session, xfer_hanguphook); + + } + + return SWITCH_STATUS_SUCCESS; +} + void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]) { /* Incoming refer */ @@ -4279,34 +4307,31 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t switch_channel_t *a_channel = switch_core_session_get_channel(a_session); const char *tmp; + switch_core_event_hook_add_state_change(a_session, xfer_hanguphook); + switch_channel_set_variable(a_channel, "att_xfer_kill_uuid", switch_core_session_get_uuid(b_session)); + if ((tmp = switch_channel_get_variable(a_channel, SWITCH_HOLD_MUSIC_VARIABLE))) { moh = tmp; } - + if (!strcasecmp(moh, "silence")) { moh = NULL; } - //switch_channel_set_variable(a_channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE, "true"); - if (moh) { char *xdest; - //switch_channel_set_variable_printf(a_channel, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE, - // "'endless_playback:%s,park':inline", moh); xdest = switch_core_session_sprintf(a_session, "endless_playback:%s,park", moh); switch_ivr_session_transfer(a_session, xdest, "inline", NULL); } else { - //switch_channel_set_variable(a_channel, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE, "park:inline"); switch_ivr_session_transfer(a_session, "park", "inline", NULL); } - //switch_channel_set_variable_printf(a_channel, "park_command", "moh"); + switch_core_session_rwunlock(a_session); 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 (1 && b_tech_pvt) { + if (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\""), @@ -4319,11 +4344,6 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), TAG_END()); } - - //switch_channel_set_variable(channel_b, "park_timeout", "2"); - //switch_channel_set_state(channel_b, CS_PARK); - - } else if (br_a && br_b) { switch_core_session_t *tmp = NULL; diff --git a/src/switch_channel.c b/src/switch_channel.c index acefb6ed81..85cead7c5e 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -1664,6 +1664,22 @@ SWITCH_DECLARE(void) switch_channel_clear_state_handler(switch_channel_t *channe switch_mutex_unlock(channel->state_mutex); } +SWITCH_DECLARE(void) switch_channel_restart(switch_channel_t *channel) +{ + switch_channel_set_state(channel, CS_RESET); + switch_channel_wait_for_state_timeout(channel, CS_RESET, 5000); + switch_channel_set_state(channel, CS_EXECUTE); +} + +/* XXX This is a somewhat simple operation. Were essentially taking the extension that one channel + was executing and generating a new extension for another channel that starts out where the + original one left off with an optional forward offset. Since all we are really doing is + copying a few basic pool-allocated structures from one channel to another there really is + not much to worry about here in terms of threading since we use read-write locks. + While the features are nice, they only really are needed in one specific crazy attended + transfer scenario where one channel was in the middle of calling a particular extension + when it was rudely cut off by a transfer key press. XXX */ + 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; diff --git a/src/switch_ivr_originate.c b/src/switch_ivr_originate.c index 218098e510..ae78c30048 100644 --- a/src/switch_ivr_originate.c +++ b/src/switch_ivr_originate.c @@ -2204,13 +2204,21 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess } } } - + end_search: - if (peer_channel && (oglobals.idx == IDX_TIMEOUT || to || oglobals.idx == IDX_KEY_CANCEL || oglobals.idx == IDX_CANCEL)) { - const char *dest = switch_channel_get_variable(peer_channel, "destination_number"); - const char *context = switch_channel_get_variable(peer_channel, "context"); - const char *dialplan = switch_channel_get_variable(peer_channel, "dialplan"); + if (peer_channel && switch_channel_down(peer_channel)) { + switch_core_session_rwunlock(peer_session); + peer_session = NULL; + peer_channel = NULL; + + } + + if (oglobals.idx == IDX_TIMEOUT || to || oglobals.idx == IDX_KEY_CANCEL || oglobals.idx == IDX_CANCEL || + (!peer_session && oglobals.idx == IDX_NADA)) { + const char *dest = NULL; + const char *context = NULL; + const char *dialplan = NULL; switch_core_session_t *holding_session; if (caller_channel) { @@ -2236,14 +2244,21 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess 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); + switch_channel_restart(holding_channel); } } if (mstatus != SWITCH_STATUS_SUCCESS) { - switch_ivr_session_transfer(holding_session, dest, dialplan, context); + if (peer_channel) { + dest = switch_channel_get_variable(peer_channel, "destination_number"); + context = switch_channel_get_variable(peer_channel, "context"); + dialplan = switch_channel_get_variable(peer_channel, "dialplan"); + } else if (caller_channel) { + dest = switch_channel_get_variable(caller_channel, "destination_number"); + } + if (dest) { + switch_ivr_session_transfer(holding_session, dest, dialplan, context); + } } switch_core_session_rwunlock(holding_session); @@ -2251,8 +2266,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess holding_session = NULL; } - switch_channel_hangup(peer_channel, SWITCH_CAUSE_ATTENDED_TRANSFER); - switch_core_session_rwunlock(peer_session); + if (peer_channel) { + 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)) {