From 19dbd07ff8feb21c8ea9ea0b760e4f6ad8e13b0e Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Fri, 21 Feb 2014 20:45:22 +0000 Subject: [PATCH] Allow access to headers from INVITE This adds a sip profile parameter parse-all-invite-headers that when set parses all headers from an INVITE into channel variables. The headers are converted to lowercase, underscores are replaced with dashes, and the result is prefixed with sip_i_. Headers than exist more than once are set as arrays. FS-6075 --resolve Thanks-to: Peter Olsson --- src/mod/endpoints/mod_sofia/mod_sofia.h | 1 + src/mod/endpoints/mod_sofia/sofia.c | 199 ++++++++++++++++++++++++ 2 files changed, 200 insertions(+) diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index b600822bd3..54beceb3fd 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -271,6 +271,7 @@ typedef enum { PFLAG_TCP_PINGPONG, PFLAG_TCP_PING2PONG, PFLAG_MESSAGES_RESPOND_200_OK, + PFLAG_PARSE_ALL_INVITE_HEADERS, /* No new flags below this line */ PFLAG_MAX } PFLAGS; diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index a3db892176..cf4e5a5267 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -325,7 +325,196 @@ static void extract_vars(sofia_profile_t *profile, sip_t const *sip, } } +/** + * Add a specific SIP INVITE header to the channel variables, prefixed with "sip_i_" + */ +static void sofia_add_invite_header_to_chanvars(switch_channel_t *channel, nua_handle_t *nh, void *sip_header, const char *var) +{ + switch_assert(channel); + switch_assert(nh); + switch_assert(var); + if (sip_header) { + char *full; + if ((full = sip_header_as_string(nh->nh_home, sip_header))) { + switch_channel_set_variable(channel, var, full); + su_free(nh->nh_home, full); + } + } +} + +/** + * Deep search into the SIP message to recreate the original headers, including multiple Diversions, etc. + * Finally sets the "sip_invite_headers" to a string containing the 'original' SIP headers, except that the order may have changed. + * Multiple headers will have the original internal order, though. + * + * @param sip A sip_t struct containing the parsed message + * @param session A call session + * @param nh A NUA handle for string allocation + */ +static void sofia_parse_all_invite_headers(sip_t const *sip, switch_core_session_t *session, nua_handle_t *nh) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + sip_unknown_t *un; + sip_p_asserted_identity_t *passerted; + sip_p_preferred_identity_t *ppreferred; + sip_remote_party_id_t *rpid; + sip_reply_to_t *reply_to; + sip_alert_info_t *alert_info; + + if (!sip) return; + + /* Add simple (unique) headers first */ + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_from, "sip_i_from"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_to, "sip_i_to"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_call_id, "sip_i_call_id"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_cseq, "sip_i_cseq"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_route, "sip_i_route"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_max_forwards, "sip_i_max_forwards"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_proxy_require, "sip_i_proxy_require"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_contact, "sip_i_contact"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_user_agent, "sip_i_user_agent"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_subject, "sip_i_subject"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_priority, "sip_i_priority"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_organization, "sip_i_organization"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_in_reply_to, "sip_i_in_reply_to"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_accept_encoding, "sip_i_accept_encoding"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_accept_language, "sip_i_accept_language"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_allow, "sip_i_allow"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_require, "sip_i_require"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_supported, "sip_i_supported"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_date, "sip_i_date"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_timestamp, "sip_i_timestamp"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_expires, "sip_i_expires"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_min_expires, "sip_i_min_expires"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_session_expires, "sip_i_session_expires"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_min_se, "sip_i_min_se"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_privacy, "sip_i_privacy"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_mime_version, "sip_i_mime_version"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_content_type, "sip_i_content_type"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_content_encoding, "sip_i_content_encoding"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_content_language, "sip_i_content_language"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_content_disposition, "sip_i_content_disposition"); + sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_content_length, "sip_i_content_length"); + + /* Add all other headers - which might exist more than once */ + + if (sip->sip_via) { + sip_via_t *vp; + for (vp = sip->sip_via; vp; vp = vp->v_next) { + char *v = sip_header_as_string(nh->nh_home, (void *) vp); + switch_channel_add_variable_var_check(channel, "sip_i_via", v, SWITCH_FALSE, SWITCH_STACK_PUSH); + su_free(nh->nh_home, v); + } + } + + if (sip->sip_record_route) { + sip_record_route_t *rrp; + for (rrp = sip->sip_record_route; rrp; rrp = rrp->r_next) { + char *rr = sip_header_as_string(nh->nh_home, (void *) rrp); + switch_channel_add_variable_var_check(channel, "sip_i_record_route", rr, SWITCH_FALSE, SWITCH_STACK_PUSH); + su_free(nh->nh_home, rr); + } + } + + if (sip->sip_proxy_authorization) { + sip_proxy_authorization_t *vp; + for (vp = sip->sip_proxy_authorization; vp; vp = vp->au_next) { + char *v = sip_header_as_string(nh->nh_home, (void *) vp); + switch_channel_add_variable_var_check(channel, "sip_i_proxy_authorization", v, SWITCH_FALSE, SWITCH_STACK_PUSH); + su_free(nh->nh_home, v); + } + } + + if (sip->sip_call_info) { + sip_call_info_t *vp; + for (vp = sip->sip_call_info; vp; vp = vp->ci_next) { + char *v = sip_header_as_string(nh->nh_home, (void *) vp); + switch_channel_add_variable_var_check(channel, "sip_i_call_info", v, SWITCH_FALSE, SWITCH_STACK_PUSH); + su_free(nh->nh_home, v); + } + } + + if (sip->sip_accept) { + sip_accept_t *vp; + for (vp = sip->sip_accept; vp; vp = vp->ac_next) { + char *v = sip_header_as_string(nh->nh_home, (void *) vp); + switch_channel_add_variable_var_check(channel, "sip_i_accept", v, SWITCH_FALSE, SWITCH_STACK_PUSH); + su_free(nh->nh_home, v); + } + } + + if (sip->sip_authorization) { + sip_authorization_t *vp; + for (vp = sip->sip_authorization; vp; vp = vp->au_next) { + char *v = sip_header_as_string(nh->nh_home, (void *) vp); + switch_channel_add_variable_var_check(channel, "sip_i_authorization", v, SWITCH_FALSE, SWITCH_STACK_PUSH); + su_free(nh->nh_home, v); + } + } + + if ((alert_info = sip_alert_info(sip))) { + sip_alert_info_t *vp; + for (vp = alert_info; vp; vp = vp->ai_next) { + char *v = sip_header_as_string(nh->nh_home, (void *) vp); + switch_channel_add_variable_var_check(channel, "sip_i_alert_info", v, SWITCH_FALSE, SWITCH_STACK_PUSH); + su_free(nh->nh_home, v); + } + } + + if ((passerted = sip_p_asserted_identity(sip))) { + sip_p_asserted_identity_t *vp; + for (vp = passerted; vp; vp = vp->paid_next) { + char *v = sip_header_as_string(nh->nh_home, (void *) vp); + switch_channel_add_variable_var_check(channel, "sip_i_p_asserted_identity", v, SWITCH_FALSE, SWITCH_STACK_PUSH); + su_free(nh->nh_home, v); + } + } + + if ((ppreferred = sip_p_preferred_identity(sip))) { + sip_p_preferred_identity_t *vp; + for (vp = ppreferred; vp; vp = vp->ppid_next) { + char *v = sip_header_as_string(nh->nh_home, (void *) vp); + switch_channel_add_variable_var_check(channel, "sip_i_p_preferred_identity", v, SWITCH_FALSE, SWITCH_STACK_PUSH); + su_free(nh->nh_home, v); + } + } + + if ((rpid = sip_remote_party_id(sip))) { + sip_remote_party_id_t *vp; + for (vp = rpid; vp; vp = vp->rpid_next) { + char *v = sip_header_as_string(nh->nh_home, (void *) vp); + switch_channel_add_variable_var_check(channel, "sip_i_remote_party_id", v, SWITCH_FALSE, SWITCH_STACK_PUSH); + su_free(nh->nh_home, v); + } + } + + if ((reply_to = sip_reply_to(sip))) { + sip_reply_to_t *vp; + for (vp = reply_to; vp; vp = vp->rplyto_next) { + char *v = sip_header_as_string(nh->nh_home, (void *) vp); + switch_channel_add_variable_var_check(channel, "sip_i_reply_to", v, SWITCH_FALSE, SWITCH_STACK_PUSH); + su_free(nh->nh_home, v); + } + } + + /* Loop through the unknown headers */ + for (un = sip->sip_unknown; un; un = un->un_next) { + if (!zstr(un->un_name) && !zstr(un->un_value)) { + char *parsed_name; + if ((parsed_name = switch_mprintf("sip_i_%s", un->un_name))) { + char *p, *x = parsed_name; + switch_tolower_max(x); + while ((p = strchr(x, '-'))) { + *p = '_'; + x = ++p; + } + switch_channel_add_variable_var_check(channel, parsed_name, un->un_value, SWITCH_FALSE, SWITCH_STACK_PUSH); + free(parsed_name); + } + } + } +} void sofia_handle_sip_i_notify(switch_core_session_t *session, int status, char const *phrase, @@ -4546,6 +4735,12 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) } else { sofia_clear_flag(profile, TFLAG_ENABLE_SOA); } + } else if (!strcasecmp(var, "parse-all-invite-headers")) { + if (switch_true(val)) { + sofia_set_pflag(profile, PFLAG_PARSE_ALL_INVITE_HEADERS); + } else { + sofia_clear_pflag(profile, PFLAG_PARSE_ALL_INVITE_HEADERS); + } } else if (!strcasecmp(var, "bitpacking")) { if (!strcasecmp(val, "aal2")) { profile->codec_flags = SWITCH_CODEC_FLAG_AAL2; @@ -9139,6 +9334,10 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia sofia_presence_set_chat_hash(tech_pvt, sip); } + if (sofia_test_pflag(profile, PFLAG_PARSE_ALL_INVITE_HEADERS)) { + sofia_parse_all_invite_headers(sip, session, nh); + } + if (sip->sip_to) { to = sip->sip_to->a_url; }