From c64b31c12c6cf6287b02f245fd549f6390dbf2bf Mon Sep 17 00:00:00 2001 From: Brian West <brian@freeswitch.org> Date: Tue, 9 Jun 2009 19:05:11 +0000 Subject: [PATCH] improve handling of 302 redirect and add option to handle them in the dialplan if needed. Also all redirects back to ourself will be transfered to the dialplan automatically now bypassing the loop. git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@13734 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- conf/sip_profiles/internal.xml | 4 +- src/mod/endpoints/mod_sofia/mod_sofia.h | 3 +- src/mod/endpoints/mod_sofia/sofia.c | 180 ++++++++++++++++++----- src/mod/endpoints/mod_sofia/sofia_glue.c | 2 + 4 files changed, 151 insertions(+), 38 deletions(-) diff --git a/conf/sip_profiles/internal.xml b/conf/sip_profiles/internal.xml index 05cd9c870b..f77b7c5e03 100644 --- a/conf/sip_profiles/internal.xml +++ b/conf/sip_profiles/internal.xml @@ -134,7 +134,9 @@ <!--Uncomment if you want to force the outbound leg of a bridge to only offer the codec that the originator is using--> <!--<param name="disable-transcoding" value="true"/>--> - <!-- Disable Refer --> + <!-- Handle 302 Redirect in the dialplan --> + <!--<param name="manual-redirect" value="true"/> --> + <!-- Disable Register --> <!--<param name="disable-transfer" value="true"/> --> <!-- Disable Register --> <!--<param name="disable-register" value="true"/> --> diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index a097b1f709..65a604913e 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -191,7 +191,7 @@ typedef enum { PFLAG_AUTOFIX_TIMING, PFLAG_MESSAGE_QUERY_ON_REGISTER, PFLAG_RTP_AUTOFLUSH_DURING_BRIDGE, - PFLAG_PROXY_FOLLOW_REDIRECT, + PFLAG_MANUAL_REDIRECT, PFLAG_AUTO_NAT, /* No new flags below this line */ PFLAG_MAX @@ -621,6 +621,7 @@ struct private_object { switch_codec_implementation_t read_impl; switch_codec_implementation_t write_impl; char *user_via; + char *redirected; }; struct callback_t { diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 656f81ab2c..fb781c756c 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -1591,11 +1591,11 @@ switch_status_t reconfig_sofia(sofia_profile_t *profile) } else { sofia_clear_pflag(profile, PFLAG_RTP_AUTOFLUSH_DURING_BRIDGE); } - } else if (!strcasecmp(var, "proxy-follow-redirect")) { + } else if (!strcasecmp(var, "manual-redirect")) { if (switch_true(val)) { - sofia_set_pflag(profile, PFLAG_PROXY_FOLLOW_REDIRECT); + sofia_set_pflag(profile, PFLAG_MANUAL_REDIRECT); } else { - sofia_clear_pflag(profile, PFLAG_PROXY_FOLLOW_REDIRECT); + sofia_clear_pflag(profile, PFLAG_MANUAL_REDIRECT); } } else if (!strcasecmp(var, "outbound-use-uuid-as-callid")) { if (switch_true(val)) { @@ -2109,11 +2109,11 @@ switch_status_t config_sofia(int reload, char *profile_name) } else { sofia_clear_pflag(profile, PFLAG_RTP_AUTOFLUSH_DURING_BRIDGE); } - } else if (!strcasecmp(var, "proxy-follow-redirect")) { + } else if (!strcasecmp(var, "manual-redirect")) { if (switch_true(val)) { - sofia_set_pflag(profile, PFLAG_PROXY_FOLLOW_REDIRECT); + sofia_set_pflag(profile, PFLAG_MANUAL_REDIRECT); } else { - sofia_clear_pflag(profile, PFLAG_PROXY_FOLLOW_REDIRECT); + sofia_clear_pflag(profile, PFLAG_MANUAL_REDIRECT); } } else if (!strcasecmp(var, "inbound-proxy-media") && switch_true(val)) { sofia_set_flag(profile, TFLAG_PROXY_MEDIA); @@ -2968,42 +2968,134 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status } } - if (channel && sip && status == 100 && switch_channel_test_flag(channel, CF_OUTBOUND)) { + if (channel && sip && (status == 302 || status == 305) && switch_channel_test_flag(channel, CF_OUTBOUND)) { sip_contact_t * p_contact = sip->sip_contact; int i = 0; char var_name[80]; + const char *diversion_header; + char *full_contact = NULL; + char *invite_contact; + const char *br; - if (sofia_test_pflag(profile, PFLAG_PROXY_FOLLOW_REDIRECT) && tech_pvt->route_uri && p_contact && p_contact->m_url) { - if (p_contact->m_url->url_port) { - tech_pvt->route_uri = switch_core_session_sprintf(tech_pvt->session, "sip:%s@%s:%s", - p_contact->m_url->url_user, p_contact->m_url->url_host, p_contact->m_url->url_port); - } else { - tech_pvt->route_uri = switch_core_session_sprintf(tech_pvt->session, "sip:%s@%s", - p_contact->m_url->url_user, p_contact->m_url->url_host); + if ((br = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE))) { + switch_xml_t root = NULL, domain = NULL; + switch_core_session_t *a_session; + switch_channel_t *a_channel; + + const char *sip_redirect_profile, *sip_redirect_context, *sip_redirect_dialplan, *sip_redirect_fork; + + if ((a_session = switch_core_session_locate(br)) && (a_channel = switch_core_session_get_channel(a_session))) { + switch_stream_handle_t stream = { 0 }; + char separator[2] = "|"; + char *redirect_dialstring; + su_home_t *home = su_home_new(sizeof(*home)); + switch_assert(home != NULL); + + SWITCH_STANDARD_STREAM(stream); + + if (!(sip_redirect_profile = switch_channel_get_variable(channel, "sip_redirect_profile"))) { + sip_redirect_profile = profile->name; + } + if (!(sip_redirect_context = switch_channel_get_variable(channel, "sip_redirect_context"))) { + sip_redirect_context = "redirected"; + } + if (!(sip_redirect_dialplan = switch_channel_get_variable(channel, "sip_redirect_dialplan"))) { + sip_redirect_dialplan = "XML"; + } + + sip_redirect_fork = switch_channel_get_variable(channel, "sip_redirect_fork"); + + if (switch_true(sip_redirect_fork)) { + *separator = ','; + } + + for (p_contact = sip->sip_contact; p_contact; p_contact = p_contact->m_next) { + if (p_contact->m_url) { + full_contact = sip_header_as_string(home, (void *) sip->sip_contact); + invite_contact = sofia_glue_strip_uri(full_contact); + + switch_snprintf(var_name, sizeof(var_name), "sip_redirect_contact_%d", i); + switch_channel_set_variable(a_channel, var_name, full_contact); + + if (i == 0) { + switch_channel_set_variable(channel, "sip_redirected_to", full_contact); + switch_channel_set_variable(a_channel, "sip_redirected_to", full_contact); + } + + if (p_contact->m_url->url_user) { + switch_snprintf(var_name, sizeof(var_name), "sip_redirect_contact_user_%d", i); + switch_channel_set_variable(channel, var_name, p_contact->m_url->url_user); + switch_channel_set_variable(a_channel, var_name, p_contact->m_url->url_user); + } + if (p_contact->m_url->url_host) { + switch_snprintf(var_name, sizeof(var_name), "sip_redirect_contact_host_%d", i); + switch_channel_set_variable(channel, var_name, p_contact->m_url->url_host); + switch_channel_set_variable(a_channel, var_name, p_contact->m_url->url_host); + } + if (p_contact->m_url->url_params) { + switch_snprintf(var_name, sizeof(var_name), "sip_redirect_contact_params_%d", i); + switch_channel_set_variable(channel, var_name, p_contact->m_url->url_params); + switch_channel_set_variable(a_channel, var_name, p_contact->m_url->url_params); + } + + switch_snprintf(var_name, sizeof(var_name), "sip_redirect_dialstring_%d", i); + switch_channel_set_variable_printf(channel, var_name, "sofia/%s/%s", sip_redirect_profile, invite_contact); + switch_channel_set_variable_printf(a_channel, var_name, "sofia/%s/%s", sip_redirect_profile, invite_contact); + stream.write_function(&stream, "%ssofia/%s/%s", i ? separator : "", sip_redirect_profile, invite_contact); + free(invite_contact); + i++; + } + } + + redirect_dialstring = stream.data; + + switch_channel_set_variable(channel, "sip_redirect_dialstring", redirect_dialstring); + switch_channel_set_variable(a_channel, "sip_redirect_dialstring", redirect_dialstring); + + p_contact = sip->sip_contact; + full_contact = sip_header_as_string(home, (void *) sip->sip_contact); + + if ((diversion_header = sofia_glue_get_unknown_header(sip, "diversion"))) { + switch_channel_set_variable(channel, "sip_redirected_by", diversion_header); + switch_channel_set_variable(a_channel, "sip_redirected_by", diversion_header); + } + + if (sofia_test_pflag(profile, PFLAG_MANUAL_REDIRECT)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Redirect: Transfering to %s %s %s\n", + p_contact->m_url->url_user, sip_redirect_dialplan, sip_redirect_context); + switch_ivr_session_transfer(a_session, p_contact->m_url->url_user, sip_redirect_dialplan, sip_redirect_context); + } else if ((!strcmp(profile->sipip, p_contact->m_url->url_host)) + || (!strcmp(profile->extsipip, p_contact->m_url->url_host)) + || (switch_xml_locate_domain(p_contact->m_url->url_host, NULL, &root, &domain) == SWITCH_STATUS_SUCCESS)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Redirect: Transfering to %s\n", p_contact->m_url->url_user); + switch_ivr_session_transfer(a_session, p_contact->m_url->url_user, NULL, NULL); + } else { + invite_contact = sofia_glue_strip_uri(full_contact); + tech_pvt->redirected = switch_core_session_strdup(session, invite_contact); + free(invite_contact); + } + + if (home) { + su_home_unref(home); + home = NULL; + } + + free(stream.data); + switch_core_session_rwunlock(a_session); } + } else { + su_home_t *home = su_home_new(sizeof(*home)); + switch_assert(home != NULL); + full_contact = sip_header_as_string(home, (void *) sip->sip_contact); + invite_contact = sofia_glue_strip_uri(full_contact); - nua_set_hparams(tech_pvt->nh, NUTAG_PROXY(tech_pvt->route_uri), TAG_END()); - } + tech_pvt->redirected = switch_core_session_strdup(session, invite_contact); - while (p_contact) { - if (p_contact->m_url) { - if (p_contact->m_url->url_user) { - switch_snprintf(var_name, sizeof(var_name), "sip_redirect_contact_user_%d", i); - switch_channel_set_variable_partner(channel, var_name, p_contact->m_url->url_user); - switch_channel_set_variable(channel, var_name, p_contact->m_url->url_user); - } - if (p_contact->m_url->url_host) { - switch_snprintf(var_name, sizeof(var_name), "sip_redirect_contact_host_%d", i); - switch_channel_set_variable_partner(channel, var_name, p_contact->m_url->url_host); - switch_channel_set_variable(channel, var_name, p_contact->m_url->url_host); - } - if (p_contact->m_url->url_params) { - switch_snprintf(var_name, sizeof(var_name), "sip_redirect_contact_params_%d", i); - switch_channel_set_variable_partner(channel, var_name, p_contact->m_url->url_params); - switch_channel_set_variable(channel, var_name, p_contact->m_url->url_params); - } - p_contact = p_contact->m_next; - i++; + free(invite_contact); + + if (home) { + su_home_unref(home); + home = NULL; } } } @@ -3084,6 +3176,22 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, SIPTAG_REPLACES_STR_REF(replaces_str), SOATAG_LOCAL_SDP_STR_REF(l_sdp), SOATAG_REMOTE_SDP_STR_REF(r_sdp), TAG_END()); if (ss_state == nua_callstate_terminated) { + + if ((status == 302 || status == 305) && session) { + channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + + if (!tech_pvt || !tech_pvt->nh) { + goto done; + } + + + if (tech_pvt->redirected) { + sofia_glue_do_invite(session); + goto done; + } + } + if (sofia_private) { sofia_private->destroy_me = 1; } @@ -3493,7 +3601,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, sdp_session_t *sdp; uint8_t match = 0; int is_ok = 1; - + sofia_clear_flag_locked(tech_pvt, TFLAG_NOSDP_REINVITE); if (tech_pvt->num_codecs) { diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index 772ad1fdc3..3cdd9b3628 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -1705,6 +1705,7 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session) nua_invite(tech_pvt->nh, NUTAG_AUTOANSWER(0), NUTAG_SESSION_TIMER(session_timeout), + TAG_IF(tech_pvt->redirected, NUTAG_URL(tech_pvt->redirected)), TAG_IF(!switch_strlen_zero(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), TAG_IF(!switch_strlen_zero(tech_pvt->rpid), SIPTAG_REMOTE_PARTY_ID_STR(tech_pvt->rpid)), TAG_IF(!switch_strlen_zero(tech_pvt->preferred_id), SIPTAG_P_PREFERRED_IDENTITY_STR(tech_pvt->preferred_id)), @@ -1724,6 +1725,7 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session) SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL), TAG_IF(rep, SIPTAG_REPLACES_STR(rep)), SOATAG_HOLD(holdstr), TAG_END()); switch_safe_free(stream.data); + tech_pvt->redirected = NULL; return SWITCH_STATUS_SUCCESS; }