freeswitch/src/mod/endpoints/mod_sofia/sofia.c

11736 lines
451 KiB
C

/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Anthony Minessale II <anthm@freeswitch.org>
* Ken Rice <krice@freeswitch.org>
* Paul D. Tinsley <pdt at jackhammer.org>
* Bret McDanel <trixter AT 0xdecafbad.com>
* Marcel Barbulescu <marcelbarbulescu@gmail.com>
* Norman Brandinger
* Raymond Chandler <intralanman@freeswitch.org>
* Nathan Patrick <npatrick at corp.sonic.net>
* Joseph Sullivan <jossulli@amazon.com>
* Emmanuel Schmidbauer <e.schmidbauer@gmail.com>
* William King <william.king@quentustech.com>
* David Knell <david.knell@telng.com>
*
* sofia.c -- SOFIA SIP Endpoint (sofia code)
*
*/
#include "mod_sofia.h"
extern su_log_t tport_log[];
extern su_log_t iptsec_log[];
extern su_log_t nea_log[];
extern su_log_t nta_log[];
extern su_log_t nth_client_log[];
extern su_log_t nth_server_log[];
extern su_log_t nua_log[];
extern su_log_t soa_log[];
extern su_log_t sresolv_log[];
#ifdef HAVE_SOFIA_STUN
extern su_log_t stun_log[];
#endif
extern su_log_t su_log_default[];
static void config_sofia_profile_urls(sofia_profile_t * profile);
static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag, const char *gwname);
static void parse_domain_tag(sofia_profile_t *profile, switch_xml_t x_domain_tag, const char *dname, const char *parse, const char *alias);
void sofia_handle_sip_i_reinvite(switch_core_session_t *session,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de,
tagi_t tags[]);
static void set_variable_sip_param(switch_channel_t *channel, char *header_type, sip_param_t const *params);
static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de,
tagi_t tags[]);
static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status,
char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de,
tagi_t tags[]);
static void sofia_handle_sip_r_options(switch_core_session_t *session, int status, char const *phrase, nua_t *nua, sofia_profile_t *profile,
nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de, tagi_t tags[]);
static void sofia_set_accept_language_channel_variable(switch_channel_t *channel, sip_t const *sip)
{
if (sip->sip_accept_language) {
sip_accept_language_t *sip_accept_language = NULL;
int count = 0;
for(sip_accept_language = sip->sip_accept_language; sip_accept_language; sip_accept_language = sip_accept_language->aa_next) {
char var_name[64] = "";
if (zstr(sip_accept_language->aa_value)) {
continue;
}
if (count == 0) {
switch_channel_set_variable(channel, "sip_accept_language", sip_accept_language->aa_value);
}
switch_snprintf(var_name, sizeof(var_name), "sip_accept_language_%d_value", count);
switch_channel_set_variable(channel, var_name, sip_accept_language->aa_value);
if (!zstr(sip_accept_language->aa_q)) {
switch_snprintf(var_name, sizeof(var_name), "sip_accept_language_%d_q", count);
switch_channel_set_variable(channel, var_name, sip_accept_language->aa_q);
}
count++;
}
switch_channel_set_variable_printf(channel, "sip_accept_language_count", "%d", count);
}
}
void sofia_handle_sip_r_notify(switch_core_session_t *session, int status,
char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de, tagi_t tags[])
{
private_object_t *tech_pvt = switch_core_session_get_private(session);
switch_core_session_t *other_session;
if (tech_pvt->proxy_refer_uuid && (other_session = switch_core_session_locate(tech_pvt->proxy_refer_uuid))) {
switch_core_session_message_t *msg;
msg = switch_core_session_alloc(other_session, sizeof(*msg));
msg->message_id = SWITCH_MESSAGE_INDICATE_RESPOND;
msg->from = __FILE__;
msg->numeric_arg = status;
msg->string_arg = switch_core_session_strdup(other_session, phrase);
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
} else {
tech_pvt->proxy_refer_uuid = NULL;
}
if (status == 481 && sip && !sip->sip_retry_after && sip->sip_call_id && (!sofia_private || !sofia_private->is_call)) {
char *sql;
sql = switch_mprintf("delete from sip_subscriptions where call_id='%q'", sip->sip_call_id->i_id);
switch_assert(sql != NULL);
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
nua_handle_destroy(nh);
}
}
#define url_set_chanvars(session, url, varprefix) _url_set_chanvars(session, url, #varprefix "_user", #varprefix "_host", #varprefix "_port", #varprefix "_uri", #varprefix "_params")
static const char *_url_set_chanvars(switch_core_session_t *session, url_t *url, const char *user_var,
const char *host_var, const char *port_var, const char *uri_var, const char *params_var)
{
const char *user = NULL, *host = NULL, *port = NULL;
char *uri = NULL;
switch_channel_t *channel = switch_core_session_get_channel(session);
char new_port[25] = "";
if (url) {
user = url->url_user;
host = url->url_host;
port = url->url_port;
if (!zstr(url->url_params)) {
switch_channel_set_variable(channel, params_var, url->url_params);
}
}
if (zstr(user)) {
user = "nobody";
}
if (zstr(host)) {
host = "nowhere";
}
check_decode(user, session);
if (user) {
switch_channel_set_variable(channel, user_var, user);
}
if (port) {
switch_snprintf(new_port, sizeof(new_port), ":%s", port);
}
switch_channel_set_variable(channel, port_var, port);
if (host) {
if (user) {
uri = switch_core_session_sprintf(session, "%s@%s%s", user, host, new_port);
} else {
uri = switch_core_session_sprintf(session, "%s%s", host, new_port);
}
switch_channel_set_variable(channel, uri_var, uri);
switch_channel_set_variable(channel, host_var, host);
}
return uri;
}
static char *strip_quotes(const char *in)
{
char *t = (char *) in;
char *r = (char *) in;
if (t && *t == '"') {
t++;
if (end_of(t) == '"') {
r = strdup(t);
switch_assert(r);
end_of(r) = '\0';
}
}
return r;
}
static void extract_header_vars(sofia_profile_t *profile, sip_t const *sip,
switch_core_session_t *session, nua_handle_t *nh)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
char *full;
if (sip) {
if (sip->sip_route) {
const char *v = switch_channel_get_variable(channel, "sip_full_route");
if (!v) {
sip_route_t *rp;
switch_stream_handle_t stream = { 0 };
int x = 0;
SWITCH_STANDARD_STREAM(stream);
for (rp = sip->sip_route; rp; rp = rp->r_next) {
char *route = sip_header_as_string(nh->nh_home, (void *) rp);
stream.write_function(&stream, x == 0 ? "%s" : ",%s", route);
su_free(nh->nh_home, route);
x++;
}
switch_channel_set_variable(channel, "sip_full_route", stream.data);
free(stream.data);
}
}
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
if (sip->sip_contact) {
char *c = sip_header_as_string(nh->nh_home, (void *) sip->sip_contact);
switch_channel_set_variable(channel, "sip_recover_contact", c);
su_free(nh->nh_home, c);
}
}
if (sip->sip_record_route) {
sip_record_route_t *rrp;
switch_stream_handle_t forward_stream = { 0 };
switch_stream_handle_t reverse_stream = { 0 };
int x = 0;
char *tmp[128] = { 0 };
int y = 0;
SWITCH_STANDARD_STREAM(forward_stream);
SWITCH_STANDARD_STREAM(reverse_stream);
for(rrp = sip->sip_record_route; rrp; rrp = rrp->r_next) {
char *rr = sip_header_as_string(nh->nh_home, (void *) rrp);
forward_stream.write_function(&forward_stream, x == 0 ? "%s" : ",%s", rr);
tmp[y++] = rr;
if (y == 127) break;
x++;
}
y--;
x = 0;
while(y >= 0) {
reverse_stream.write_function(&reverse_stream, x == 0 ? "%s" : ",%s", tmp[y]);
su_free(nh->nh_home, tmp[y]);
y--;
x++;
}
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND || switch_channel_test_flag(channel, CF_RECOVERED)) {
switch_channel_set_variable(channel, "sip_invite_route_uri", (char *)reverse_stream.data);
switch_channel_set_variable(channel, "sip_invite_record_route", (char *)forward_stream.data);
} else {
switch_channel_set_variable(channel, "sip_invite_route_uri", (char *)forward_stream.data);
switch_channel_set_variable(channel, "sip_invite_record_route", (char *)reverse_stream.data);
}
free(reverse_stream.data);
free(forward_stream.data);
}
if (sip->sip_via) {
sip_via_t *vp;
switch_stream_handle_t stream = { 0 };
int x = 0;
SWITCH_STANDARD_STREAM(stream);
for(vp = sip->sip_via; vp; vp = vp->v_next) {
char *v = sip_header_as_string(nh->nh_home, (void *) vp);
stream.write_function(&stream, x == 0 ? "%s" : ",%s", v);
su_free(nh->nh_home, v);
x++;
}
switch_channel_set_variable(channel, "sip_full_via", (char *)stream.data);
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND || switch_stristr("TCP", (char *)stream.data)) {
switch_channel_set_variable(channel, "sip_recover_via", (char *)stream.data);
}
free(stream.data);
}
if (sip->sip_from) {
char *p = strip_quotes(sip->sip_from->a_display);
if (p) {
switch_channel_set_variable(channel, "sip_from_display", p);
}
if (p != sip->sip_from->a_display) free(p);
if ((full = sip_header_as_string(nh->nh_home, (void *) sip->sip_from))) {
switch_channel_set_variable(channel, "sip_full_from", full);
su_free(nh->nh_home, full);
}
}
if (sip->sip_to) {
char *p = strip_quotes(sip->sip_to->a_display);
if (p) {
switch_channel_set_variable(channel, "sip_to_display", p);
}
if (p != sip->sip_to->a_display) free(p);
if ((full = sip_header_as_string(nh->nh_home, (void *) sip->sip_to))) {
switch_channel_set_variable(channel, "sip_full_to", full);
su_free(nh->nh_home, full);
}
}
}
}
static void extract_vars(sofia_profile_t *profile, sip_t const *sip,
switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
if (sip) {
if (sip->sip_from)
url_set_chanvars(session, sip->sip_from->a_url, sip_from);
if (sip->sip_request)
url_set_chanvars(session, sip->sip_request->rq_url, sip_req);
if (sip->sip_to)
url_set_chanvars(session, sip->sip_to->a_url, sip_to);
if (sip->sip_contact)
url_set_chanvars(session, sip->sip_contact->m_url, sip_contact);
if (sip->sip_referred_by)
url_set_chanvars(session, sip->sip_referred_by->b_url, sip_referred_by);
if (sip->sip_to && sip->sip_to->a_tag) {
switch_channel_set_variable(channel, "sip_to_tag", sip->sip_to->a_tag);
}
if (sip->sip_from && sip->sip_from->a_tag) {
switch_channel_set_variable(channel, "sip_from_tag", sip->sip_from->a_tag);
}
if (sip->sip_cseq && sip->sip_cseq->cs_seq) {
char sip_cseq[40] = "";
switch_snprintf(sip_cseq, sizeof(sip_cseq), "%d", sip->sip_cseq->cs_seq);
switch_channel_set_variable(channel, "sip_cseq", sip_cseq);
}
if (sip->sip_call_id && sip->sip_call_id->i_id) {
switch_channel_set_variable(channel, "sip_call_id", sip->sip_call_id->i_id);
}
}
}
/**
* 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);
}
}
}
}
static switch_status_t sofia_pass_notify(switch_core_session_t *session, const char *uuid, const char *payload)
{
switch_core_session_t *other_session;
if ((other_session = switch_core_session_locate(uuid))) {
switch_core_session_message_t *msg;
msg = switch_core_session_alloc(other_session, sizeof(*msg));
MESSAGE_STAMP_FFL(msg);
msg->message_id = SWITCH_MESSAGE_INDICATE_BLIND_TRANSFER_RESPONSE;
msg->string_arg = switch_core_session_strdup(other_session, payload);
msg->from = __FILE__;
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_FALSE;
}
void sofia_handle_sip_i_notify(switch_core_session_t *session, int status,
char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de, tagi_t tags[])
{
switch_channel_t *channel = NULL;
private_object_t *tech_pvt = NULL;
switch_event_t *s_event = NULL;
sofia_gateway_subscription_t *gw_sub_ptr;
int sub_state;
sofia_gateway_t *gateway = NULL;
const char *session_id_header = sofia_glue_session_id_header(session, profile);
tl_gets(tags, NUTAG_SUBSTATE_REF(sub_state), TAG_END());
/* make sure we have a proper event */
if (!sip || !sip->sip_event) {
goto error;
}
/* Automatically return a 200 OK for Event: keep-alive */
if (!strcasecmp(sip->sip_event->o_type, "keep-alive")) {
/* XXX MTK - is this right? in this case isn't sofia is already sending a 200 itself also? */
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
goto end;
}
if (session) {
channel = switch_core_session_get_channel(session);
switch_assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
switch_assert(tech_pvt != NULL);
}
if (tech_pvt && tech_pvt->proxy_refer_uuid && sofia_test_pflag(profile, PFLAG_PROXY_REFER) && sip->sip_payload && sip->sip_payload->pl_data &&
sip->sip_content_type && sip->sip_content_type->c_type && switch_stristr("sipfrag", sip->sip_content_type->c_type)) {
if (sofia_pass_notify(session, tech_pvt->proxy_refer_uuid, sip->sip_payload->pl_data) == SWITCH_STATUS_SUCCESS) {
if (tech_pvt->proxy_refer_msg) {
msg_ref_destroy(tech_pvt->proxy_refer_msg);
tech_pvt->proxy_refer_msg = NULL;
}
tech_pvt->proxy_refer_msg = msg_ref_create(de->data->e_msg);
//nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
} else {
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
goto end;
}
/* For additional NOTIFY event packages see http://www.iana.org/assignments/sip-events. */
if (sip->sip_content_type &&
sip->sip_content_type->c_type && sip->sip_payload && sip->sip_payload->pl_data && !strcasecmp(sip->sip_event->o_type, "refer")) {
if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_NOTIFY_REFER) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "content-type", sip->sip_content_type->c_type);
switch_event_add_body(s_event, "%s", sip->sip_payload->pl_data);
}
}
/* add common headers for the NOTIFY to the switch_event and fire if it exists */
if (s_event != NULL) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "event-package", sip->sip_event->o_type);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "event-id", sip->sip_event->o_id);
if (sip->sip_contact) {
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "contact", "%s@%s",
sip->sip_contact->m_url->url_user, sip->sip_contact->m_url->url_host);
}
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "from", "%s@%s", sip->sip_from->a_url->url_user, sip->sip_from->a_url->url_host);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-tag", sip->sip_from->a_tag);
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "to", "%s@%s", sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "to-tag", sip->sip_to->a_tag);
if (sip->sip_call_id && sip->sip_call_id->i_id) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "call-id", sip->sip_call_id->i_id);
}
if (sip->sip_subscription_state && sip->sip_subscription_state->ss_substate) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "subscription-substate", sip->sip_subscription_state->ss_substate);
}
if (sip->sip_subscription_state && sip->sip_subscription_state->ss_reason) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "subscription-reason", sip->sip_subscription_state->ss_reason);
}
if (sip->sip_subscription_state && sip->sip_subscription_state->ss_retry_after) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "subscription-retry-after", sip->sip_subscription_state->ss_retry_after);
}
if (sip->sip_subscription_state && sip->sip_subscription_state->ss_expires) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "subscription-expires", sip->sip_subscription_state->ss_expires);
}
if (session) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "UniqueID", switch_core_session_get_uuid(session));
}
switch_event_fire(&s_event);
}
if (!strcasecmp(sip->sip_event->o_type, "refer")) {
if (session && channel && tech_pvt) {
if (sip->sip_payload && sip->sip_payload->pl_data) {
char *p;
int status_val = 0;
if ((p = strchr(sip->sip_payload->pl_data, ' '))) {
p++;
status_val = atoi(p);
}
if (!status_val || status_val >= 200) {
switch_channel_set_variable(channel, "sip_refer_reply", sip->sip_payload->pl_data);
if (status_val == 200) {
switch_channel_hangup(channel, SWITCH_CAUSE_BLIND_TRANSFER);
}
if ((int)tech_pvt->want_event == 9999) {
tech_pvt->want_event = 0;
}
}
}
}
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
}
/* if no session, assume it could be an incoming notify from a gateway subscription */
if (session) {
if (!zstr(profile->proxy_notify_events) && (!strcasecmp(profile->proxy_notify_events, "all") || strstr(profile->proxy_notify_events, sip->sip_event->o_type))) {
switch_core_session_t *other_session;
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
const char *full_to = NULL;
char *pl = NULL;
char *unknown = NULL;
full_to = switch_str_nil(switch_channel_get_variable(switch_core_session_get_channel(other_session), "sip_full_to"));
if (sip->sip_payload && sip->sip_payload->pl_data) {
pl = switch_core_session_strdup(other_session, (char*)sip->sip_payload->pl_data);
}
unknown = sofia_glue_get_non_extra_unknown_headers(sip);
nua_notify(other_tech_pvt->nh, NUTAG_NEWSUB(1), NUTAG_SUBSTATE(nua_substate_active),
TAG_IF((full_to), SIPTAG_TO_STR(full_to)), SIPTAG_SUBSCRIPTION_STATE_STR("active"),
SIPTAG_EVENT_STR(sip->sip_event->o_type), TAG_IF(!zstr(unknown), SIPTAG_HEADER_STR(unknown)),
TAG_IF(!zstr(pl), SIPTAG_PAYLOAD_STR(pl)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
switch_safe_free(unknown);
switch_core_session_rwunlock(other_session);
}
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
goto end;
}
/* make sure we have a proper "talk" event */
if (strcasecmp(sip->sip_event->o_type, "talk")) {
goto error;
}
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
switch_channel_answer(channel);
switch_channel_set_variable(channel, "auto_answer_destination", switch_channel_get_variable(channel, "destination_number"));
switch_ivr_session_transfer(session, "auto_answer", NULL, NULL);
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
goto end;
}
}
if (!sofia_private || zstr(sofia_private->gateway_name)) {
if (profile->debug) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Gateway information missing Subscription Event: %s\n",
sip->sip_event->o_type);
}
goto error;
}
if (!(gateway = sofia_reg_find_gateway(sofia_private->gateway_name))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Gateway information missing\n");
goto error;
}
/* find the corresponding gateway subscription (if any) */
if (!(gw_sub_ptr = sofia_find_gateway_subscription(gateway, sip->sip_event->o_type))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
"Could not find gateway subscription. Gateway: %s. Subscription Event: %s\n",
gateway->name, sip->sip_event->o_type);
goto error;
}
if (!(gw_sub_ptr->state == SUB_STATE_SUBED || gw_sub_ptr->state == SUB_STATE_SUBSCRIBE)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Ignoring notify due to subscription state: %d\n", gw_sub_ptr->state);
goto error;
}
if (sip->sip_subscription_state && sip->sip_subscription_state->ss_expires) {
int delta = atoi(sip->sip_subscription_state->ss_expires);
delta /= 2;
if (delta < 1) {
delta = 1;
}
gw_sub_ptr->expires = switch_epoch_time_now(NULL) + delta;
}
/* dispatch freeswitch event */
if (switch_event_create(&s_event, SWITCH_EVENT_NOTIFY_IN) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "event", sip->sip_event->o_type);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "pl_data", sip->sip_payload ? sip->sip_payload->pl_data : "");
if ( sip->sip_content_type != NULL )
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "sip_content_type", sip->sip_content_type->c_type);
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "port", "%d", gateway->profile->sip_port);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "module_name", "mod_sofia");
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile_name", gateway->profile->name);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile_uri", gateway->profile->url);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "gateway_name", gateway->name);
if ( sip->sip_call_info != NULL ) {
sip_call_info_t *call_info = sip->sip_call_info;
char *nua_hold = sip_header_as_string(nua_handle_home(nh), (void *) call_info);
size_t cur_len = strlen(nua_hold);
char *hold = strdup(nua_hold);
su_free(nua_handle_home(nh), nua_hold);
while ((call_info = call_info->ci_next) != NULL) {
char *tmp = sip_header_as_string(nua_handle_home(nh), (void *) call_info);
size_t tmp_len = strlen(tmp);
hold = realloc(hold, cur_len + tmp_len + 2);
switch_assert(hold);
strncpy(hold + cur_len, ",", 2);
strncpy(hold + cur_len + 1, tmp, tmp_len +1);
su_free(nua_handle_home(nh), tmp);
cur_len = cur_len + tmp_len + 2;
}
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "Call-Info", hold);
switch_safe_free(hold);
}
switch_event_fire(&s_event);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "dispatched freeswitch event for message-summary NOTIFY\n");
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to create event\n");
goto error;
}
goto end;
error:
if (sip && sip->sip_event && sip->sip_event->o_type && !strcasecmp(sip->sip_event->o_type, "message-summary")) {
/* unsolicited mwi, just say ok */
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
if (sofia_test_pflag(profile, PFLAG_FORWARD_MWI_NOTIFY)) {
const char *mwi_status = NULL;
char network_ip[80];
uint32_t x = 0;
int acl_ok = 1;
char *last_acl = NULL;
if (sip->sip_to && sip->sip_to->a_url->url_user && sip->sip_to->a_url->url_host
&& sip->sip_payload && sip->sip_payload->pl_data ) {
sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), NULL);
for (x = 0; x < profile->acl_count; x++) {
last_acl = profile->acl[x];
if (!(acl_ok = switch_check_network_list_ip(network_ip, last_acl))) {
break;
}
}
if ( acl_ok )
{
mwi_status = switch_stristr("Messages-Waiting: ", sip->sip_payload->pl_data);
if ( mwi_status ) {
char *mwi_stat;
mwi_status += strlen( "Messages-Waiting: " );
mwi_stat = switch_strip_whitespace( mwi_status );
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
"Forwarding unsolicited MWI ( %s : %s@%s )\n",
mwi_stat, sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host );
if (switch_event_create(&s_event, SWITCH_EVENT_MESSAGE_WAITING) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "MWI-Messages-Waiting", mwi_stat );
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM,
"MWI-Message-Account", "%s@%s", sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host );
switch_event_fire(&s_event);
}
switch_safe_free(mwi_stat);
}
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Dropping unsolicited MWI ( %s@%s ) because of ACL\n",
sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host );
};
}
}
} else {
nua_respond(nh, 481, "Subscription Does Not Exist", NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
}
end:
if (!gateway && sub_state == nua_substate_terminated && sofia_private && sofia_private != &mod_sofia_globals.destroy_private &&
sofia_private != &mod_sofia_globals.keep_private) {
sofia_private->destroy_nh = 1;
sofia_private->destroy_me = 1;
}
if (gateway) {
sofia_reg_release_gateway(gateway);
}
}
/*
* This will fire an event containing X-headers from the BYE response
*/
void sofia_handle_sip_r_bye(switch_core_session_t *session, int status,
char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de, tagi_t tags[])
{
if (profile && sofia_test_pflag(profile, PFLAG_FIRE_BYE_RESPONSE_EVENTS) && sip && sip->sip_call_id && !zstr(sip->sip_call_id->i_id) && sofia_private && !zstr_buf(sofia_private->uuid_str)) {
switch_event_t *bye_response_event = NULL;
sip_unknown_t *un;
if (switch_event_create_subclass(&bye_response_event, SWITCH_EVENT_CUSTOM, MY_EVENT_BYE_RESPONSE) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header(bye_response_event, SWITCH_STACK_BOTTOM, "call-id", "%s", sip->sip_call_id->i_id);
switch_event_add_header(bye_response_event, SWITCH_STACK_BOTTOM, "Unique-ID", "%s", sofia_private->uuid_str);
for (un = sip->sip_unknown; un; un = un->un_next) {
if (!zstr(un->un_value)) {
switch_event_add_header(bye_response_event, SWITCH_STACK_BOTTOM, un->un_name, "%s", un->un_value);
}
}
switch_event_fire(&bye_response_event);
}
}
}
void sofia_handle_sip_i_bye(switch_core_session_t *session, int status,
char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de, tagi_t tags[])
{
const char *tmp;
switch_channel_t *channel;
private_object_t *tech_pvt;
char *extra_headers;
const char *call_info = NULL;
const char *vval = NULL;
const char *session_id_header = sofia_glue_session_id_header(session, profile);
#ifdef MANUAL_BYE
int cause;
char st[80] = "";
#endif
if (!session) {
return;
}
channel = switch_core_session_get_channel(session);
tech_pvt = switch_core_session_get_private(session);
#ifdef MANUAL_BYE
status = 200;
phrase = "OK";
if (switch_channel_test_flag(tech_pvt->channel, CF_SLA_BARGING)) {
const char *bargee_uuid = switch_channel_get_variable(channel, "sip_barging_uuid");
switch_core_session_t *bargee_session;
uint32_t ttl = 0;
if ((bargee_session = switch_core_session_locate(bargee_uuid))) {
//switch_channel_t *bargee_channel = switch_core_session_get_channel(bargee_session);
if ((ttl = switch_core_media_bug_count(bargee_session, "eavesdrop")) == 1) {
if (switch_core_session_check_interface(bargee_session, sofia_endpoint_interface)) {
switch_channel_clear_flag(switch_core_session_get_channel(bargee_session), CF_SLA_BARGE);
}
}
switch_core_session_rwunlock(bargee_session);
}
}
if (switch_channel_test_flag(tech_pvt->channel, CF_SLA_BARGE)) {
switch_core_session_t *new_session, *other_session;
const char *other_uuid = switch_channel_get_partner_uuid(tech_pvt->channel);
char *cmd = NULL;
if (!zstr(other_uuid) && (other_session = switch_core_session_locate(other_uuid))) {
switch_channel_t *other_channel = switch_core_session_get_channel(other_session);
switch_mutex_lock(profile->ireg_mutex);
if (switch_ivr_eavesdrop_pop_eavesdropper(session, &new_session) == SWITCH_STATUS_SUCCESS) {
switch_channel_t *new_channel = switch_core_session_get_channel(new_session);
const char *new_uuid = switch_core_session_get_uuid(new_session);
switch_caller_profile_t *cp = switch_channel_get_caller_profile(new_channel);
cp->caller_id_name = cp->orig_caller_id_name;
cp->caller_id_number = cp->orig_caller_id_number;
switch_channel_set_variable(new_channel, SWITCH_SIGNAL_BOND_VARIABLE, NULL);
switch_channel_set_flag(other_channel, CF_REDIRECT);
switch_channel_set_state(new_channel, CS_RESET);
switch_ivr_uuid_bridge(new_uuid, other_uuid);
cmd = switch_core_session_sprintf(session, "sleep:500,sofia_sla:%s inline", new_uuid);
switch_channel_clear_flag(other_channel, CF_REDIRECT);
switch_core_session_rwunlock(new_session);
}
switch_mutex_unlock(profile->ireg_mutex);
switch_core_session_rwunlock(other_session);
}
if (!zstr(cmd)) {
switch_ivr_eavesdrop_exec_all(session, "transfer", cmd);
}
}
sofia_set_flag_locked(tech_pvt, TFLAG_BYE);
call_info = switch_channel_get_variable(channel, "presence_call_info_full");
if (sip->sip_reason) {
char *reason_header = sip_header_as_string(nh->nh_home, (void *) sip->sip_reason);
if (!zstr(reason_header)) {
switch_channel_set_variable(channel, "sip_reason", reason_header);
switch_channel_set_variable_partner(channel, "sip_reason", reason_header);
}
}
if (sip->sip_reason && sip->sip_reason->re_protocol && (!strcasecmp(sip->sip_reason->re_protocol, "Q.850")
|| !strcasecmp(sip->sip_reason->re_protocol, "FreeSWITCH")
|| !strcasecmp(sip->sip_reason->re_protocol, profile->sdp_username)) && sip->sip_reason->re_cause) {
tech_pvt->q850_cause = atoi(sip->sip_reason->re_cause);
cause = tech_pvt->q850_cause;
} else {
cause = sofia_glue_sip_cause_to_freeswitch(status);
}
if (sip->sip_content_type && sip->sip_content_type->c_type) {
switch_channel_set_variable(channel, "sip_bye_content_type", sip->sip_content_type->c_type);
}
if (sip->sip_payload && sip->sip_payload->pl_data) {
switch_channel_set_variable(channel, "sip_bye_payload", sip->sip_payload->pl_data);
}
switch_snprintf(st, sizeof(st), "%d", status);
switch_channel_set_variable(channel, "sip_term_status", st);
switch_snprintf(st, sizeof(st), "sip:%d", status);
switch_channel_set_variable(channel, SWITCH_PROTO_SPECIFIC_HANGUP_CAUSE_VARIABLE, st);
switch_channel_set_variable_partner(channel, "sip_hangup_phrase", phrase);
switch_snprintf(st, sizeof(st), "%d", cause);
switch_channel_set_variable(channel, "sip_term_cause", st);
extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_BYE_HEADER_PREFIX);
sofia_glue_set_extra_headers(session, sip, SOFIA_SIP_BYE_HEADER_PREFIX);
if (!(vval = switch_channel_get_variable(channel, "sip_copy_custom_headers")) || switch_true(vval)) {
switch_core_session_t *nsession = NULL;
switch_core_session_get_partner(session, &nsession);
if (nsession) {
const char *vval;
switch_channel_t *nchannel = switch_core_session_get_channel(nsession);
if (!(vval = switch_channel_get_variable(nchannel, "sip_copy_custom_headers")) || switch_true(vval)) {
switch_ivr_transfer_variable(session, nsession, SOFIA_SIP_BYE_HEADER_PREFIX_T);
}
switch_core_session_rwunlock(nsession);
}
}
switch_channel_hangup(channel, cause);
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(call_info, SIPTAG_CALL_INFO_STR(call_info)), TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
switch_safe_free(extra_headers);
if (sofia_private) {
sofia_private->destroy_me = 1;
sofia_private->destroy_nh = 1;
}
#endif
if (sip->sip_user_agent && !zstr(sip->sip_user_agent->g_string)) {
switch_channel_set_variable(channel, "sip_user_agent", sip->sip_user_agent->g_string);
} else if (sip->sip_server && !zstr(sip->sip_server->g_string)) {
switch_channel_set_variable(channel, "sip_user_agent", sip->sip_server->g_string);
}
sofia_set_accept_language_channel_variable(channel, sip);
if ((tmp = sofia_glue_get_unknown_header(sip, "rtp-txstat"))) {
switch_channel_set_variable(channel, "sip_rtp_txstat", tmp);
}
if ((tmp = sofia_glue_get_unknown_header(sip, "rtp-rxstat"))) {
switch_channel_set_variable(channel, "sip_rtp_rxstat", tmp);
}
if ((tmp = sofia_glue_get_unknown_header(sip, "P-RTP-Stat"))) {
switch_channel_set_variable(channel, "sip_p_rtp_stat", tmp);
}
tech_pvt->got_bye = 1;
switch_channel_set_variable(channel, "sip_hangup_disposition", "recv_bye");
return;
}
void sofia_handle_sip_r_message(int status, sofia_profile_t *profile, nua_handle_t *nh, sip_t const *sip)
{
const char *call_id;
int *mstatus;
if (!(sip && sip->sip_call_id)) {
nua_handle_destroy(nh);
return;
}
call_id = sip->sip_call_id->i_id;
switch_mutex_lock(profile->flag_mutex);
mstatus = switch_core_hash_find(profile->chat_hash, call_id);
switch_mutex_unlock(profile->flag_mutex);
if (mstatus) {
*mstatus = status;
}
}
void sofia_wait_for_reply(struct private_object *tech_pvt, nua_event_t event, uint32_t timeout)
{
time_t exp = switch_epoch_time_now(NULL) + timeout;
tech_pvt->want_event = event;
while (switch_channel_ready(tech_pvt->channel) && tech_pvt->want_event && switch_epoch_time_now(NULL) < exp) {
switch_yield(100000);
}
}
void sofia_send_callee_id(switch_core_session_t *session, const char *name, const char *number)
{
const char *uuid;
switch_core_session_t *session_b;
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_caller_profile_t *caller_profile = switch_channel_get_caller_profile(channel);
if (switch_channel_inbound_display(channel)) {
if (zstr(name)) {
name = caller_profile->caller_id_name;
}
if (zstr(number)) {
number = caller_profile->caller_id_number;
}
if (zstr(name)) {
name = number;
}
if (zstr(number)) {
name = number = "UNKNOWN";
}
if (!zstr(name) && !strcmp(name,"_undef_")) {
name = "";
}
} else {
if (zstr(name)) {
name = caller_profile->callee_id_name;
}
if (zstr(number)) {
number = caller_profile->callee_id_number;
}
if (zstr(name)) {
name = number;
}
if (zstr(number)) {
number = caller_profile->destination_number;
}
if (!zstr(name) && !strcmp(name,"_undef_")) {
name = "";
}
}
if ((uuid = switch_channel_get_partner_uuid(channel)) && (session_b = switch_core_session_locate(uuid))) {
switch_core_session_message_t *msg;
//switch_channel_t *channel_b = switch_core_session_get_channel(session_b);
//switch_channel_set_profile_var(channel_b, "callee_id_name", name);
//switch_channel_set_profile_var(channel_b, "callee_id_number", number);
msg = switch_core_session_alloc(session_b, sizeof(*msg));
MESSAGE_STAMP_FFL(msg);
msg->message_id = SWITCH_MESSAGE_INDICATE_DISPLAY;
msg->string_array_arg[0] = switch_core_session_strdup(session_b, name);
msg->string_array_arg[1] = switch_core_session_strdup(session_b, number);
msg->from = __FILE__;
switch_core_session_queue_message(session_b, msg);
switch_core_session_rwunlock(session_b);
}
}
void sofia_update_callee_id(switch_core_session_t *session, sofia_profile_t *profile, sip_t const *sip,
switch_bool_t send)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
sip_p_asserted_identity_t *passerted = NULL;
char *name = NULL;
const char *number = "unknown", *tmp;
switch_caller_profile_t *caller_profile;
char *dup = NULL;
switch_event_t *event;
const char *val;
int fs = 0, lazy = 0, att = 0;
const char *name_var = "callee_id_name";
const char *num_var = "callee_id_number";
if (switch_true(switch_channel_get_variable(channel, SWITCH_IGNORE_DISPLAY_UPDATES_VARIABLE)) || !sofia_test_pflag(profile, PFLAG_SEND_DISPLAY_UPDATE)) {
return;
}
if (switch_channel_inbound_display(channel)) {
name_var = "caller_id_name";
num_var = "caller_id_number";
}
number = (char *) switch_channel_get_variable(channel, num_var);
name = (char *) switch_channel_get_variable(channel, name_var);
if (zstr(number) && sip->sip_to) {
number = sip->sip_to->a_url->url_user;
}
if ((val = sofia_glue_get_unknown_header(sip, "X-FS-Display-Number"))) {
number = val;
fs++;
}
if ((val = sofia_glue_get_unknown_header(sip, "X-FS-Display-Name"))) {
name = (char *) val;
check_decode(name, session);
fs++;
}
if ((val = sofia_glue_get_unknown_header(sip, "X-FS-Lazy-Attended-Transfer"))) {
lazy = switch_true(val);
fs++;
}
if ((val = sofia_glue_get_unknown_header(sip, "X-FS-Attended-Transfer"))) {
att = switch_true(val);
fs++;
}
if (!fs) {
sip_remote_party_id_t *rpid;
if ((passerted = sip_p_asserted_identity(sip))) {
if (passerted->paid_url->url_user) {
number = passerted->paid_url->url_user;
}
if (!zstr(passerted->paid_display)) {
dup = strdup(passerted->paid_display);
switch_assert(dup);
if (*dup == '"') {
name = dup + 1;
} else {
name = dup;
}
if (end_of(name) == '"') {
end_of(name) = '\0';
}
}
} else if ((rpid = sip_remote_party_id(sip))) {
if (rpid->rpid_url->url_user) {
number = rpid->rpid_url->url_user;
}
if (!zstr(rpid->rpid_display)) {
dup = strdup(rpid->rpid_display);
switch_assert(dup);
if (*dup == '"') {
name = dup + 1;
} else {
name = dup;
}
if (end_of(name) == '"') {
end_of(name) = '\0';
}
}
}
}
if (zstr(number)) {
if ((tmp = switch_channel_get_variable(channel, num_var)) && !zstr(tmp)) {
number = (char *) tmp;
}
if (zstr(number)) {
number = "unknown";
}
}
if (zstr(name)) {
if ((tmp = switch_channel_get_variable(channel, name_var)) && !zstr(tmp)) {
name = (char *) tmp;
}
}
if (zstr(name)) {
name = (char *) number;
}
if (zstr(name) || zstr(number)) {
goto end;
}
caller_profile = switch_channel_get_caller_profile(channel);
if (switch_channel_inbound_display(channel)) {
if (!strcmp(caller_profile->caller_id_name, name) && !strcmp(caller_profile->caller_id_number, number)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "%s Same Caller ID \"%s\" <%s>\n", switch_channel_get_name(channel), name, number);
send = 0;
} else {
caller_profile->caller_id_name = switch_sanitize_number(switch_core_strdup(caller_profile->pool, name));
caller_profile->caller_id_number = switch_sanitize_number(switch_core_strdup(caller_profile->pool, number));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Update Caller ID to \"%s\" <%s>\n", switch_channel_get_name(channel), name, number);
}
} else {
if (!strcmp(caller_profile->callee_id_name, name) && !strcmp(caller_profile->callee_id_number, number)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "%s Same Callee ID \"%s\" <%s>\n", switch_channel_get_name(channel), name, number);
send = 0;
} else {
caller_profile->callee_id_name = switch_sanitize_number(switch_core_strdup(caller_profile->pool, name));
caller_profile->callee_id_number = switch_sanitize_number(switch_core_strdup(caller_profile->pool, number));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s Update Callee ID to \"%s\" <%s>\n", switch_channel_get_name(channel), name, number);
if (lazy || (att && !switch_channel_get_partner_uuid(channel))) {
switch_channel_flip_cid(channel);
}
}
}
if (send) {
if (switch_event_create(&event, SWITCH_EVENT_CALL_UPDATE) == SWITCH_STATUS_SUCCESS) {
const char *uuid = switch_channel_get_partner_uuid(channel);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Direction", "RECV");
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);
}
sofia_send_callee_id(session, NULL, NULL);
}
end:
switch_safe_free(dup);
}
static void tech_send_ack(nua_handle_t *nh, private_object_t *tech_pvt, const char *r_sdp)
{
const char *invite_full_from = switch_channel_get_variable(tech_pvt->channel, "sip_invite_full_from");
const char *invite_full_to = switch_channel_get_variable(tech_pvt->channel, "sip_invite_full_to");
int soa = sofia_use_soa(tech_pvt);
const char *session_id_header = sofia_glue_session_id_header(tech_pvt->session, tech_pvt->profile);
if (sofia_test_pflag(tech_pvt->profile, PFLAG_TRACK_CALLS)) {
const char *invite_full_via = switch_channel_get_variable(tech_pvt->channel, "sip_invite_full_via");
const char *invite_route_uri = switch_channel_get_variable(tech_pvt->channel, "sip_invite_route_uri");
nua_ack(nh,
TAG_IF(invite_full_from, SIPTAG_FROM_STR(invite_full_from)),
TAG_IF(invite_full_to, SIPTAG_TO_STR(invite_full_to)),
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)),
TAG_IF((zstr(tech_pvt->user_via) && !zstr(invite_full_via)), SIPTAG_VIA_STR(invite_full_via)),
TAG_IF(!zstr(invite_route_uri), SIPTAG_ROUTE_STR(invite_route_uri)),
TAG_IF(r_sdp && soa, SOATAG_USER_SDP_STR(r_sdp)),
TAG_IF(r_sdp && soa, SOATAG_REUSE_REJECTED(1)),
TAG_IF(r_sdp && soa, SOATAG_AUDIO_AUX("cn telephone-event")),
TAG_IF(r_sdp && !soa, SIPTAG_CONTENT_TYPE_STR("application/sdp")),
TAG_IF(r_sdp && !soa, SIPTAG_PAYLOAD_STR(r_sdp)),
TAG_IF(r_sdp && !soa, NUTAG_MEDIA_ENABLE(0)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
nua_ack(nh,
TAG_IF(invite_full_from, SIPTAG_FROM_STR(invite_full_from)),
TAG_IF(invite_full_to, SIPTAG_TO_STR(invite_full_to)),
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)),
TAG_IF(r_sdp && soa, SOATAG_USER_SDP_STR(r_sdp)),
TAG_IF(r_sdp && soa, SOATAG_REUSE_REJECTED(1)),
TAG_IF(r_sdp && soa, SOATAG_AUDIO_AUX("cn telephone-event")),
TAG_IF(r_sdp && !soa, SIPTAG_CONTENT_TYPE_STR("application/sdp")),
TAG_IF(r_sdp && !soa, SIPTAG_PAYLOAD_STR(r_sdp)),
TAG_IF(r_sdp && !soa, NUTAG_MEDIA_ENABLE(0)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
}
static void notify_watched_header(switch_core_session_t *session, const char *msgline, const char *hdrname, const char *hdrval)
{
switch_event_t *event = NULL;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Found known watched header in message '%s', %s: %s\n", msgline, hdrname, hdrval);
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_NOTIFY_WATCHED_HEADER) == SWITCH_STATUS_SUCCESS) {
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SIP-Message", msgline);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Header-Name", hdrname);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Header-Value", hdrval);
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Failed creating event of type %s!\n", MY_EVENT_NOTIFY_WATCHED_HEADER);
}
}
static void sofia_handle_sip_r_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, switch_core_session_t *session, int status, const char *phrase, sip_t const *sip, sofia_dispatch_event_t *de, tagi_t tags[])
{
private_object_t *tech_pvt = switch_core_session_get_private(session);
switch_core_session_t *other_session;
if (status < 200) {
return;
}
if (tech_pvt->proxy_refer_uuid && (other_session = switch_core_session_locate(tech_pvt->proxy_refer_uuid))) {
switch_core_session_message_t *msg;
msg = switch_core_session_alloc(other_session, sizeof(*msg));
msg->message_id = SWITCH_MESSAGE_INDICATE_RESPOND;
msg->from = __FILE__;
msg->numeric_arg = status;
msg->string_arg = switch_core_session_strdup(other_session, phrase);
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
} else {
tech_pvt->proxy_refer_uuid = NULL;
}
}
//sofia_dispatch_event_t *de
static void our_sofia_event_callback(nua_event_t event,
int status,
char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de, tagi_t tags[])
{
struct private_object *tech_pvt = NULL;
auth_res_t auth_res = AUTH_FORBIDDEN;
switch_core_session_t *session = NULL;
switch_channel_t *channel = NULL;
sofia_gateway_t *gateway = NULL;
int locked = 0;
int check_destroy = 1;
profile->last_sip_event = switch_time_now();
/* sofia_private will be == &mod_sofia_globals.keep_private whenever a request is done with a new handle that has to be
freed whenever the request is done */
if (nh && sofia_private == &mod_sofia_globals.keep_private) {
if (status >= 300) {
nua_handle_bind(nh, NULL);
nua_handle_destroy(nh);
return;
}
}
if (sofia_private && sofia_private != &mod_sofia_globals.destroy_private && sofia_private != &mod_sofia_globals.keep_private) {
if (!zstr(sofia_private->gateway_name)) {
if (!(gateway = sofia_reg_find_gateway(sofia_private->gateway_name))) {
return;
}
} else if (!zstr(sofia_private->uuid)) {
if ((session = de->init_session)) {
de->init_session = NULL;
} else if ((session = de->session) || (session = switch_core_session_locate(sofia_private->uuid))) {
tech_pvt = switch_core_session_get_private(session);
channel = switch_core_session_get_channel(session);
if (tech_pvt) {
switch_mutex_lock(tech_pvt->sofia_mutex);
locked = 1;
} else {
if (session != de->session) switch_core_session_rwunlock(session);
return;
}
if (status >= 180 && !*sofia_private->auth_gateway_name) {
const char *gwname = switch_channel_get_variable(channel, "sip_use_gateway");
if (!zstr(gwname)) {
switch_set_string(sofia_private->auth_gateway_name, gwname);
}
}
if (!tech_pvt->call_id && sip && sip->sip_call_id && sip->sip_call_id->i_id) {
tech_pvt->call_id = switch_core_session_strdup(session, sip->sip_call_id->i_id);
switch_channel_set_variable(channel, "sip_call_id", tech_pvt->call_id);
}
if (tech_pvt->gateway_name) {
gateway = sofia_reg_find_gateway(tech_pvt->gateway_name);
}
if (channel && switch_channel_down(channel)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Channel is already hungup.\n");
goto done;
}
} else {
/* we can't find the session it must be hanging up or something else, its too late to do anything with it. */
return;
}
}
}
if (session && tech_pvt && tech_pvt->watch_headers && sip) {
char msgline[512];
int hi;
msg_header_t *h = NULL;
if (sip->sip_request) {
h = (msg_header_t *)sip->sip_request;
msg_header_field_e(msgline, sizeof(msgline), h, 0);
} else if (sip->sip_status) {
h = (msg_header_t *)sip->sip_status;
msg_header_field_e(msgline, sizeof(msgline), h, 0);
}
if (h) {
sip_unknown_t *un = NULL;
char buf[512];
char *c = NULL;
msgline[sizeof(msgline)-1] = '\0';
c = strchr(msgline, '\r');
if (c) {
*c = '\0';
}
/* Faster (ie hash-based) search here would be nice? ie, make watch_headers a hash? */
/* Search first in the valid headers */
for (h = h->sh_succ; h; h = h->sh_succ) {
sip_header_t *sh = (sip_header_t *)h;
if (!sh->sh_class->hc_name) {
continue;
}
for (hi = 0; tech_pvt->watch_headers[hi]; hi++) {
if (!strcasecmp(tech_pvt->watch_headers[hi], sh->sh_class->hc_name)) {
msg_header_field_e(buf, sizeof(buf), h, 0);
buf[sizeof(buf)-1] = '\0';
notify_watched_header(session, msgline, sh->sh_class->hc_name, buf);
}
}
}
/* Search now in the unknown headers */
for (un = sip->sip_unknown; un; un = un->un_next) {
for (hi = 0; tech_pvt->watch_headers[hi]; hi++) {
if (!strcasecmp(tech_pvt->watch_headers[hi], un->un_name)) {
notify_watched_header(session, msgline, un->un_name, un->un_value);
}
}
}
}
}
if (sofia_test_pflag(profile, PFLAG_AUTH_ALL) && tech_pvt && tech_pvt->key && sip && (event < nua_r_set_params || event > nua_r_authenticate)) {
sip_authorization_t const *authorization = NULL;
if (sip->sip_authorization) {
authorization = sip->sip_authorization;
} else if (sip->sip_proxy_authorization) {
authorization = sip->sip_proxy_authorization;
}
if (authorization) {
char network_ip[80];
int network_port;
sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
auth_res = sofia_reg_parse_auth(profile, authorization, sip, de,
(char *) sip->sip_request->rq_method_name, tech_pvt->key, strlen(tech_pvt->key), network_ip, network_port, NULL, 0,
REG_INVITE, NULL, NULL, NULL, NULL);
}
if ((auth_res != AUTH_OK && auth_res != AUTH_RENEWED)) {
//switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
nua_respond(nh, SIP_401_UNAUTHORIZED, TAG_END());
goto done;
}
if (channel) {
switch_channel_set_variable(channel, "sip_authorized", "true");
}
}
if (sip && (status == 401 || status == 407)) {
sofia_reg_handle_sip_r_challenge(status, phrase, nua, profile, nh, sofia_private, session, gateway, sip, de, tags);
goto done;
}
if (sip && sip->sip_payload && sip->sip_payload->pl_data) {
if (sip->sip_payload->pl_len != strlen(sip->sip_payload->pl_data)) {
sip->sip_payload->pl_data = su_strndup(nh->nh_home, sip->sip_payload->pl_data, sip->sip_payload->pl_len);
}
}
switch (event) {
case nua_r_get_params:
case nua_i_fork:
case nua_r_info:
break;
case nua_r_unregister:
case nua_r_unsubscribe:
case nua_i_terminated:
case nua_r_publish:
case nua_i_error:
case nua_i_active:
case nua_r_set_params:
case nua_i_prack:
case nua_r_prack:
break;
case nua_i_cancel:
if (sip && channel) {
switch_channel_set_variable(channel, "sip_hangup_disposition", "recv_cancel");
switch_channel_set_variable(channel, "sip_invite_failure_status", "487");
switch_channel_set_variable(channel, "sip_invite_failure_phrase", "CANCEL");
if (sip->sip_reason) {
char *reason_header = sip_header_as_string(nh->nh_home, (void *) sip->sip_reason);
if (!zstr(reason_header)) {
switch_channel_set_variable(channel, "sip_reason", reason_header);
switch_channel_set_variable_partner(channel, "sip_reason", reason_header);
}
}
}
break;
case nua_r_cancel:
{
if (status > 299 && nh) {
nua_handle_destroy(nh);
}
}
break;
case nua_i_ack:
{
if (channel && sip) {
const char *r_sdp = NULL;
sofia_glue_store_session_id(session, profile, sip, 0);
if (sip->sip_payload && sip->sip_payload->pl_data) {
if (sofia_test_flag(tech_pvt, TFLAG_PASS_ACK)) {
r_sdp = sip->sip_payload->pl_data;
if (tech_pvt->mparams.last_sdp_str) {
tech_pvt->mparams.prev_sdp_str = tech_pvt->mparams.last_sdp_str;
}
tech_pvt->mparams.last_sdp_str = NULL;
if (!zstr(tech_pvt->mparams.prev_sdp_str) && strcmp(tech_pvt->mparams.prev_sdp_str, sip->sip_payload->pl_data)) {
switch_channel_set_variable(channel, "sip_reinvite_sdp", sip->sip_payload->pl_data);
tech_pvt->mparams.last_sdp_str = switch_core_session_strdup(session, sip->sip_payload->pl_data);
} else {
tech_pvt->mparams.last_sdp_str = tech_pvt->mparams.prev_sdp_str;
}
} else {
switch_channel_set_variable(channel, "sip_reinvite_sdp", sip->sip_payload->pl_data);
tech_pvt->mparams.last_sdp_str = switch_core_session_strdup(session, sip->sip_payload->pl_data);
}
}
if (sip->sip_to && sip->sip_to->a_tag) {
switch_channel_set_variable(channel, "sip_to_tag", sip->sip_to->a_tag);
}
if (sip->sip_from && sip->sip_from->a_tag) {
switch_channel_set_variable(channel, "sip_from_tag", sip->sip_from->a_tag);
}
if (sip->sip_cseq && sip->sip_cseq->cs_seq) {
char sip_cseq[40] = "";
switch_snprintf(sip_cseq, sizeof(sip_cseq), "%d", sip->sip_cseq->cs_seq);
switch_channel_set_variable(channel, "sip_cseq", sip_cseq);
}
if (sip->sip_call_id && sip->sip_call_id->i_id) {
switch_channel_set_variable(channel, "sip_call_id", sip->sip_call_id->i_id);
}
extract_header_vars(profile, sip, session, nh);
switch_core_recovery_track(session);
sofia_set_flag(tech_pvt, TFLAG_GOT_ACK);
if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MEDIA) && r_sdp) {
if (sofia_test_pflag(tech_pvt->profile, PFLAG_3PCC_PROXY)) {
switch_channel_set_variable_partner(tech_pvt->channel, SWITCH_B_SDP_VARIABLE, r_sdp);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "3PCC-PROXY, Got my ACK\n");
sofia_media_activate_rtp(tech_pvt);
switch_core_media_proxy_remote_addr(tech_pvt->session, r_sdp);
sofia_set_flag(tech_pvt, TFLAG_3PCC_HAS_ACK);
sofia_clear_flag(tech_pvt, TFLAG_PASS_ACK);
}
}
if (sofia_test_flag(tech_pvt, TFLAG_PASS_ACK)) {
switch_core_session_t *other_session;
sofia_clear_flag(tech_pvt, TFLAG_PASS_ACK);
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
if (switch_core_session_compare(session, other_session)) {
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
tech_send_ack(other_tech_pvt->nh, other_tech_pvt, r_sdp);
}
switch_core_session_rwunlock(other_session);
}
}
}
}
case nua_r_ack:
if (channel)
switch_channel_set_flag(channel, CF_MEDIA_ACK);
break;
case nua_r_shutdown:
if (status >= 200) {
sofia_set_pflag(profile, PFLAG_SHUTDOWN);
su_root_break(profile->s_root);
}
break;
case nua_r_message:
sofia_handle_sip_r_message(status, profile, nh, sip);
break;
case nua_r_invite:
sofia_handle_sip_r_invite(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_r_options:
sofia_handle_sip_r_options(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_i_bye:
sofia_handle_sip_i_bye(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_r_bye:
sofia_handle_sip_r_bye(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_r_notify:
if (session) {
sofia_handle_sip_r_notify(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
}
break;
case nua_i_notify:
sofia_handle_sip_i_notify(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_r_register:
sofia_reg_handle_sip_r_register(status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_i_options:
sofia_handle_sip_i_options(status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_i_invite:
if (session && sofia_private) {
if (sofia_private->is_call > 1) {
sofia_handle_sip_i_reinvite(session, nua, profile, nh, sofia_private, sip, de, tags);
} else {
sofia_private->is_call++;
sofia_handle_sip_i_invite(session, nua, profile, nh, sofia_private, sip, de, tags);
}
}
break;
case nua_i_publish:
sofia_presence_handle_sip_i_publish(nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_i_register:
//nua_respond(nh, SIP_200_OK, SIPTAG_CONTACT(sip->sip_contact), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
//nua_handle_destroy(nh);
sofia_reg_handle_sip_i_register(nua, profile, nh, &sofia_private, sip, de, tags);
break;
case nua_i_state:
sofia_handle_sip_i_state(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_i_message:
{
int handle_message = 1;
int proxy_message = sofia_test_pflag(profile, PFLAG_PROXY_MESSAGE);
if (!proxy_message && session) {
switch_channel_t *channel = switch_core_session_get_channel(session);
proxy_message = switch_channel_var_true(channel, "sip_proxy_message");
}
if (proxy_message) {
if (sofia_proxy_sip_i_message(nua, profile, nh, session, sip, de, tags) == SWITCH_STATUS_SUCCESS) {
handle_message = 0;
}
}
if (handle_message) {
sofia_presence_handle_sip_i_message(status, phrase, nua, profile, nh, session, sofia_private, sip, de, tags);
}
}
break;
case nua_i_info:
{
int handle_info = 1;
int proxy_info = sofia_test_pflag(profile, PFLAG_PROXY_INFO);
if (!proxy_info && session) {
switch_channel_t *channel = switch_core_session_get_channel(session);
proxy_info = switch_channel_var_true(channel, "sip_proxy_info");
}
if (proxy_info) {
if (sofia_proxy_sip_i_info(nua, profile, nh, session, sip, de, tags) == SWITCH_STATUS_SUCCESS) {
handle_info = 0;
}
}
if (handle_info) {
sofia_handle_sip_i_info(nua, profile, nh, session, sip, de, tags);
}
}
break;
case nua_i_update:
if (session) {
sofia_update_callee_id(session, profile, sip, SWITCH_TRUE);
}
break;
case nua_r_update:
if (session && tech_pvt && locked) {
sofia_clear_flag_locked(tech_pvt, TFLAG_UPDATING_DISPLAY);
}
break;
case nua_r_refer:
if (session) {
sofia_handle_sip_r_refer(nua, profile, nh, session, status, phrase, sip, de, tags);
}
break;
case nua_i_refer:
if (session) {
sofia_handle_sip_i_refer(nua, profile, nh, session, sip, de, tags);
} else if (sip) {
const char *req_user = NULL, *req_host = NULL, *action = NULL, *ref_by_user = NULL, *ref_to_user = NULL, *ref_to_host = NULL;
char *refer_to = NULL, *referred_by = NULL, *method = NULL, *full_url = NULL;
char *params = NULL, *iparams = NULL;
switch_event_t *event;
char *tmp;
if (sip->sip_refer_to) {
ref_to_user = sip->sip_refer_to->r_url->url_user;
ref_to_host = sip->sip_refer_to->r_url->url_host;
if (sip->sip_refer_to->r_url->url_params && switch_stristr("method=", sip->sip_refer_to->r_url->url_params)) {
params = su_strdup(nua_handle_home(nh), sip->sip_refer_to->r_url->url_params);
}
if ((refer_to = sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_refer_to))) {
if ((iparams = strchr(refer_to, ';'))) {
*iparams++ = '\0';
if (!params || !switch_stristr("method=", params)) {
params = iparams;
}
}
if ((tmp = sofia_glue_get_url_from_contact(refer_to, 0))) {
refer_to = tmp;
}
}
if (params) {
method = switch_find_parameter(params, "method", NULL);
full_url = switch_find_parameter(params, "full_url", NULL);
}
}
if (!method) {
method = strdup("INVITE");
switch_assert(method);
}
if (!strcasecmp(method, "INVITE")) {
action = "call";
} else if (!strcasecmp(method, "BYE")) {
action = "end";
} else {
action = method;
}
if (sip->sip_referred_by) {
referred_by = sofia_glue_get_url_from_contact(sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_referred_by), 0);
ref_by_user = sip->sip_referred_by->b_url->url_user;
} else if(sip->sip_to){
referred_by = sofia_glue_get_url_from_contact(sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_to), 0);
ref_by_user = sip->sip_to->a_url->url_user;
}
if (sip->sip_to) {
req_user = sip->sip_to->a_url->url_user;
req_host = sip->sip_to->a_url->url_host;
}
if (switch_event_create(&event, SWITCH_EVENT_CALL_SETUP_REQ) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Requesting-Component", "mod_sofia");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Target-Component", req_user);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Target-Domain", req_host);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Request-Action", action);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Request-Target", "sofia/%s/%s", profile->name, refer_to);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Request-Target-URI", "%s", refer_to);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Request-Target-Extension", ref_to_user);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Request-Target-Domain", ref_to_host);
if (switch_true(full_url)) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "full_url", "true");
}
if (sip->sip_call_id && sip->sip_call_id->i_id) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Request-Call-ID", sip->sip_call_id->i_id);
}
if (!zstr(referred_by)) {
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Request-Sender", "sofia/%s/%s", profile->name, referred_by);
}
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "var_origination_caller_id_number", ref_by_user);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "var_origination_caller_id_name", ref_by_user);
switch_event_fire(&event);
}
{
char *sql;
sofia_nat_parse_t np = { { 0 } };
char *contact_str;
char *proto = "sip", *orig_proto = "sip";
const char *call_id, *full_from, *full_to, *full_via, *from_user = NULL, *from_host = NULL, *to_user, *to_host, *full_agent;
char to_tag[13] = "";
char *event_str = "refer";
sip_accept_t *ap = sip->sip_accept;
char accept_header[256] = "";
np.fs_path = 1;
contact_str = sofia_glue_gen_contact_str(profile, sip, nh, de, &np);
call_id = sip->sip_call_id ? sip->sip_call_id->i_id : "";
full_from = sip_header_as_string(nh->nh_home, (void *) sip->sip_from);
full_to = sip_header_as_string(nh->nh_home, (void *) sip->sip_to);
full_via = sip_header_as_string(nh->nh_home, (void *) sip->sip_via);
full_agent = sip_header_as_string(nh->nh_home, (void *) sip->sip_user_agent);
switch_stun_random_string(to_tag, 12, NULL);
if (sip->sip_from) {
from_user = sip->sip_from->a_url->url_user;
from_host = sip->sip_from->a_url->url_host;
} else {
from_user = "n/a";
from_host = "n/a";
}
if (sip->sip_to) {
to_user = sip->sip_to->a_url->url_user;
to_host = sip->sip_to->a_url->url_host;
} else {
to_user = "n/a";
to_host = "n/a";
}
while (ap) {
switch_snprintf(accept_header + strlen(accept_header), sizeof(accept_header) - strlen(accept_header),
"%s%s ", ap->ac_type, ap->ac_next ? "," : "");
ap = ap->ac_next;
}
sql = switch_mprintf("insert into sip_subscriptions "
"(proto,sip_user,sip_host,sub_to_user,sub_to_host,presence_hosts,event,contact,call_id,full_from,"
"full_via,expires,user_agent,accept,profile_name,hostname,network_port,network_ip,version,orig_proto, full_to) "
"values ('%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q',%ld,'%q','%q','%q','%q','%d','%q',-1,'%q','%q;tag=%q')",
proto, from_user, from_host, to_user, to_host, profile->presence_hosts ? profile->presence_hosts : "",
event_str, contact_str, call_id, full_from, full_via,
(long) switch_epoch_time_now(NULL) + 60,
full_agent, accept_header, profile->name, mod_sofia_globals.hostname,
np.network_port, np.network_ip, orig_proto, full_to, to_tag);
switch_assert(sql != NULL);
if (mod_sofia_globals.debug_presence > 0 || mod_sofia_globals.debug_sla > 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "%s REFER SUBSCRIBE %s@%s %s@%s\n%s\n",
profile->name, from_user, from_host, to_user, to_host, sql);
}
sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
sip_to_tag(nh->nh_home, sip->sip_to, to_tag);
}
nua_respond(nh, SIP_202_ACCEPTED, SIPTAG_TO(sip->sip_to), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
switch_safe_free(method);
switch_safe_free(full_url);
}
break;
case nua_r_subscribe:
sofia_presence_handle_sip_r_subscribe(status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_i_subscribe:
sofia_presence_handle_sip_i_subscribe(status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_i_media_error:
{
if (sofia_private && sofia_private->call_id && sofia_private->network_ip && sofia_private->network_port) {
char *sql;
switch_event_t *event = NULL;
sql = switch_mprintf("delete from sip_registrations where call_id='%q' and network_ip='%q' and network_port='%q'",
sofia_private->call_id, sofia_private->network_ip, sofia_private->network_port);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "SOCKET DISCONNECT: %s %s:%s\n",
sofia_private->call_id, sofia_private->network_ip, sofia_private->network_port);
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
switch_core_del_registration(sofia_private->user, sofia_private->realm, sofia_private->call_id);
if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", profile->url);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "user-agent",
(sip && sip->sip_user_agent) ? sip->sip_user_agent->g_string : "unknown");
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", sofia_private->user, sofia_private->realm);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "status", "Unregistered");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-source", "register");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
switch_event_fire(&event);
}
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_UNREGISTER) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "profile-name", profile->name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from-user", sofia_private->user);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from-host", sofia_private->realm);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-id", sofia_private->call_id);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "reason", "socket-disconnection");
switch_event_fire(&event);
}
sofia_reg_check_socket(profile, sofia_private->call_id, sofia_private->network_ip, sofia_private->network_port);
nua_handle_destroy(nh);
}
}
break;
case nua_r_authenticate:
if (status >= 500) {
if (sofia_private && !zstr(sofia_private->gateway_name)) {
sofia_gateway_t *gateway = NULL;
if ((gateway = sofia_reg_find_gateway(sofia_private->gateway_name))) {
gateway->state = REG_STATE_FAILED;
gateway->failure_status = status;
sofia_reg_release_gateway(gateway);
}
} else {
nua_handle_destroy(nh);
}
}
break;
default:
if (status > 100) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s: unknown event %d: %03d %s\n", nua_event_name(event), event,
status, phrase);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s: unknown event %d\n", nua_event_name(event), event);
}
break;
}
done:
if (tech_pvt && tech_pvt->want_event && event == tech_pvt->want_event) {
tech_pvt->want_event = 0;
}
if (sofia_private && sofia_private->call_id) {
check_destroy = 0;
}
switch (event) {
case nua_i_subscribe:
case nua_r_notify:
check_destroy = 0;
break;
case nua_i_notify:
if (sip && sip->sip_event && !strcmp(sip->sip_event->o_type, "dialog") && sip->sip_event->o_params && !strcmp(sip->sip_event->o_params[0], "sla")) {
check_destroy = 0;
}
break;
default:
break;
}
if ((sofia_private && sofia_private == &mod_sofia_globals.destroy_private)) {
nua_handle_bind(nh, NULL);
nua_handle_destroy(nh);
nh = NULL;
}
if (check_destroy) {
if (nh && ((sofia_private && sofia_private->destroy_nh) || !nua_handle_magic(nh))) {
if (sofia_private) {
nua_handle_bind(nh, NULL);
}
if (tech_pvt && (tech_pvt->nh == nh)) {
tech_pvt->nh = NULL;
}
nua_handle_destroy(nh);
nh = NULL;
}
}
if (sofia_private && sofia_private->destroy_me) {
if (tech_pvt) {
tech_pvt->sofia_private = NULL;
}
if (nh) {
nua_handle_bind(nh, NULL);
}
sofia_private->destroy_me = 12;
sofia_private_free(sofia_private);
}
if (gateway) {
sofia_reg_release_gateway(gateway);
}
if (locked && tech_pvt) {
switch_mutex_unlock(tech_pvt->sofia_mutex);
}
if (session && session != de->session) {
switch_core_session_rwunlock(session);
}
}
static uint32_t DE_THREAD_CNT = 0;
void *SWITCH_THREAD_FUNC sofia_msg_thread_run_once(switch_thread_t *thread, void *obj)
{
sofia_dispatch_event_t *de = (sofia_dispatch_event_t *) obj;
switch_memory_pool_t *pool = NULL;
switch_mutex_lock(mod_sofia_globals.mutex);
DE_THREAD_CNT++;
switch_mutex_unlock(mod_sofia_globals.mutex);
if (de) {
pool = de->pool;
de->pool = NULL;
sofia_process_dispatch_event(&de);
}
if (pool) {
switch_core_destroy_memory_pool(&pool);
}
switch_mutex_lock(mod_sofia_globals.mutex);
DE_THREAD_CNT--;
switch_mutex_unlock(mod_sofia_globals.mutex);
return NULL;
}
void sofia_process_dispatch_event_in_thread(sofia_dispatch_event_t **dep)
{
sofia_dispatch_event_t *de = *dep;
switch_memory_pool_t *pool;
//sofia_profile_t *profile = (*dep)->profile;
switch_thread_data_t *td;
switch_core_new_memory_pool(&pool);
*dep = NULL;
de->pool = pool;
td = switch_core_alloc(pool, sizeof(*td));
td->func = sofia_msg_thread_run_once;
td->obj = de;
switch_thread_pool_launch_thread(&td);
}
void sofia_process_dispatch_event(sofia_dispatch_event_t **dep)
{
sofia_dispatch_event_t *de = *dep;
nua_handle_t *nh = de->nh;
nua_t *nua = de->nua;
sofia_profile_t *profile = de->profile;
sofia_private_t *sofia_private = nua_handle_magic(de->nh);
*dep = NULL;
our_sofia_event_callback(de->data->e_event, de->data->e_status, de->data->e_phrase, de->nua, de->profile,
de->nh, sofia_private, de->sip, de, (tagi_t *) de->data->e_tags);
nua_destroy_event(de->event);
su_free(nh->nh_home, de);
switch_mutex_lock(profile->flag_mutex);
profile->queued_events--;
switch_mutex_unlock(profile->flag_mutex);
nua_handle_unref(nh);
nua_stack_unref(nua);
}
static int msg_queue_threads = 0;
//static int count = 0;
void *SWITCH_THREAD_FUNC sofia_msg_thread_run(switch_thread_t *thread, void *obj)
{
void *pop;
switch_queue_t *q = (switch_queue_t *) obj;
int my_id;
for (my_id = 0; my_id < mod_sofia_globals.msg_queue_len; my_id++) {
if (mod_sofia_globals.msg_queue_thread[my_id] == thread) {
break;
}
}
switch_mutex_lock(mod_sofia_globals.mutex);
msg_queue_threads++;
switch_mutex_unlock(mod_sofia_globals.mutex);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MSG Thread %d Started\n", my_id);
for(;;) {
if (switch_queue_pop(q, &pop) != SWITCH_STATUS_SUCCESS) {
switch_cond_next();
continue;
}
if (pop) {
sofia_dispatch_event_t *de = (sofia_dispatch_event_t *) pop;
sofia_process_dispatch_event(&de);
} else {
break;
}
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MSG Thread Ended\n");
switch_mutex_lock(mod_sofia_globals.mutex);
msg_queue_threads--;
switch_mutex_unlock(mod_sofia_globals.mutex);
return NULL;
}
void sofia_msg_thread_start(int idx)
{
if (idx >= mod_sofia_globals.max_msg_queues ||
idx >= SOFIA_MAX_MSG_QUEUE || (idx < mod_sofia_globals.msg_queue_len && mod_sofia_globals.msg_queue_thread[idx])) {
return;
}
switch_mutex_lock(mod_sofia_globals.mutex);
if (idx >= mod_sofia_globals.msg_queue_len) {
int i;
mod_sofia_globals.msg_queue_len = idx + 1;
for (i = 0; i < mod_sofia_globals.msg_queue_len; i++) {
if (!mod_sofia_globals.msg_queue_thread[i]) {
switch_threadattr_t *thd_attr = NULL;
switch_threadattr_create(&thd_attr, mod_sofia_globals.pool);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
//switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME);
switch_thread_create(&mod_sofia_globals.msg_queue_thread[i],
thd_attr,
sofia_msg_thread_run,
mod_sofia_globals.msg_queue,
mod_sofia_globals.pool);
}
}
}
switch_mutex_unlock(mod_sofia_globals.mutex);
}
//static int foo = 0;
void sofia_queue_message(sofia_dispatch_event_t *de)
{
int launch = 0;
if (mod_sofia_globals.running == 0 || !mod_sofia_globals.msg_queue) {
sofia_process_dispatch_event(&de);
return;
}
if (de->profile && sofia_test_pflag(de->profile, PFLAG_THREAD_PER_REG) &&
de->data->e_event == nua_i_register && DE_THREAD_CNT < mod_sofia_globals.max_reg_threads) {
sofia_process_dispatch_event_in_thread(&de);
return;
}
if ((switch_queue_size(mod_sofia_globals.msg_queue) > (SOFIA_MSG_QUEUE_SIZE * (unsigned int)msg_queue_threads))) {
launch++;
}
if (launch) {
if (mod_sofia_globals.msg_queue_len < mod_sofia_globals.max_msg_queues) {
sofia_msg_thread_start(mod_sofia_globals.msg_queue_len + 1);
}
}
switch_queue_push(mod_sofia_globals.msg_queue, de);
}
static void set_call_id(private_object_t *tech_pvt, sip_t const *sip)
{
if (!tech_pvt->call_id && tech_pvt->session && tech_pvt->channel && sip && sip->sip_call_id && sip->sip_call_id->i_id) {
tech_pvt->call_id = switch_core_session_strdup(tech_pvt->session, sip->sip_call_id->i_id);
switch_channel_set_variable(tech_pvt->channel, "sip_call_id", tech_pvt->call_id);
}
}
void sofia_event_callback(nua_event_t event,
int status,
char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
tagi_t tags[])
{
sofia_dispatch_event_t *de;
int critical = (((SOFIA_MSG_QUEUE_SIZE * mod_sofia_globals.max_msg_queues) * 900) / 1000);
uint32_t sess_count = switch_core_session_count();
uint32_t sess_max = switch_core_session_limit(0);
switch(event) {
case nua_i_terminated:
if ((status == 401 || status == 407 || status == 403) && sofia_private) {
switch_core_session_t *session;
if ((session = switch_core_session_locate(sofia_private->uuid))) {
switch_channel_t *channel = switch_core_session_get_channel(session);
int end = 0;
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND && !switch_channel_test_flag(channel, CF_ANSWERED)) {
private_object_t *tech_pvt = switch_core_session_get_private(session);
if (status == 403) {
switch_channel_set_flag(channel, CF_NO_CDR);
switch_channel_hangup(channel, SWITCH_CAUSE_CALL_REJECTED);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "detaching session %s\n", sofia_private->uuid);
if (!zstr(tech_pvt->call_id)) {
tech_pvt->sofia_private = NULL;
tech_pvt->nh = NULL;
sofia_set_flag(tech_pvt, TFLAG_BYE);
switch_mutex_lock(profile->flag_mutex);
switch_core_hash_insert(profile->chat_hash, tech_pvt->call_id, strdup(switch_core_session_get_uuid(session)));
switch_mutex_unlock(profile->flag_mutex);
nua_handle_destroy(nh);
} else {
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
}
end++;
}
switch_core_session_rwunlock(session);
if (end) {
goto end;
}
}
}
break;
case nua_i_invite:
case nua_i_register:
case nua_i_options:
case nua_i_notify:
case nua_i_info:
if (event == nua_i_invite) {
if (sip->sip_session_expires && profile->minimum_session_expires) {
if (sip->sip_session_expires->x_delta < profile->minimum_session_expires) {
char buf[64] = "";
switch_snprintf(buf, sizeof(buf), "Min-SE: %d", profile->minimum_session_expires);
nua_respond(nh, SIP_422_SESSION_TIMER_TOO_SMALL, SIPTAG_HEADER_STR(buf),TAG_END());
goto end;
}
}
}
if (!sofia_private) {
if (sess_count >= sess_max || !sofia_test_pflag(profile, PFLAG_RUNNING) || !switch_core_ready_inbound()) {
nua_respond(nh, 503, "Maximum Calls In Progress", SIPTAG_RETRY_AFTER_STR("300"), NUTAG_WITH_THIS(nua), TAG_END());
goto end;
}
if (switch_queue_size(mod_sofia_globals.msg_queue) > (unsigned int)critical) {
nua_respond(nh, 503, "System Busy", SIPTAG_RETRY_AFTER_STR("300"), NUTAG_WITH_THIS(nua), TAG_END());
goto end;
}
if (sofia_test_pflag(profile, PFLAG_STANDBY)) {
nua_respond(nh, 503, "System Paused", NUTAG_WITH_THIS(nua), TAG_END());
goto end;
}
}
break;
default:
break;
}
switch_mutex_lock(profile->flag_mutex);
profile->queued_events++;
switch_mutex_unlock(profile->flag_mutex);
de = su_alloc(nh->nh_home, sizeof(*de));
memset(de, 0, sizeof(*de));
nua_save_event(nua, de->event);
de->nh = nua_handle_ref(nh);
de->data = nua_event_data(de->event);
de->sip = sip_object(de->data->e_msg);
de->profile = profile;
de->nua = nua_stack_ref(nua);
if (event == nua_i_invite && !sofia_private) {
switch_core_session_t *session;
private_object_t *tech_pvt = NULL;
if (!(sofia_private = su_alloc(nh->nh_home, sizeof(*sofia_private)))) {
abort();
}
memset(sofia_private, 0, sizeof(*sofia_private));
sofia_private->is_call++;
sofia_private->is_static++;
nua_handle_bind(nh, sofia_private);
if (sip->sip_call_id && sip->sip_call_id->i_id) {
char *uuid;
switch_mutex_lock(profile->flag_mutex);
if ((uuid = (char *) switch_core_hash_find(profile->chat_hash, sip->sip_call_id->i_id))) {
switch_core_hash_delete(profile->chat_hash, sip->sip_call_id->i_id);
}
switch_mutex_unlock(profile->flag_mutex);
if (uuid) {
if ((session = switch_core_session_locate(uuid))) {
tech_pvt = switch_core_session_get_private(session);
switch_copy_string(sofia_private->uuid_str, switch_core_session_get_uuid(session), sizeof(sofia_private->uuid_str));
sofia_private->uuid = sofia_private->uuid_str;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Re-attaching to session %s\n", sofia_private->uuid);
de->init_session = session;
sofia_clear_flag(tech_pvt, TFLAG_BYE);
tech_pvt->sofia_private = NULL;
tech_pvt->nh = NULL;
switch_core_session_queue_signal_data(session, de);
switch_core_session_rwunlock(session);
session = NULL;
free(uuid);
uuid = NULL;
goto end;
} else {
free(uuid);
uuid = NULL;
sip = NULL;
}
}
}
if (!sip || !sip->sip_call_id || zstr(sip->sip_call_id->i_id)) {
nua_respond(nh, 503, "INVALID INVITE", TAG_END());
nua_destroy_event(de->event);
su_free(nh->nh_home, de);
switch_mutex_lock(profile->flag_mutex);
profile->queued_events--;
switch_mutex_unlock(profile->flag_mutex);
nua_handle_unref(nh);
nua_stack_unref(nua);
goto end;
}
if (sofia_test_pflag(profile, PFLAG_CALLID_AS_UUID)) {
session = switch_core_session_request_uuid(sofia_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL, sip->sip_call_id->i_id);
} else {
session = switch_core_session_request(sofia_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL);
}
if (session) {
const char *channel_name = NULL;
tech_pvt = sofia_glue_new_pvt(session);
if (sip->sip_from) {
channel_name = url_set_chanvars(session, sip->sip_from->a_url, sip_from);
}
if (!channel_name && sip->sip_contact) {
channel_name = url_set_chanvars(session, sip->sip_contact->m_url, sip_contact);
}
if (sip->sip_referred_by) {
channel_name = url_set_chanvars(session, sip->sip_referred_by->b_url, sip_referred_by);
}
sofia_glue_attach_private(session, profile, tech_pvt, channel_name);
set_call_id(tech_pvt, sip);
} else {
nua_respond(nh, 503, "Maximum Calls In Progress", SIPTAG_RETRY_AFTER_STR("300"), TAG_END());
nua_destroy_event(de->event);
su_free(nh->nh_home, de);
switch_mutex_lock(profile->flag_mutex);
profile->queued_events--;
switch_mutex_unlock(profile->flag_mutex);
nua_handle_unref(nh);
nua_stack_unref(nua);
goto end;
}
if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) {
char *uuid;
if (!switch_core_session_running(session) && !switch_core_session_started(session)) {
nua_handle_bind(nh, NULL);
sofia_private_free(sofia_private);
switch_core_session_destroy(&session);
nua_respond(nh, 503, "Maximum Calls In Progress", SIPTAG_RETRY_AFTER_STR("300"), TAG_END());
}
switch_mutex_lock(profile->flag_mutex);
if ((uuid = switch_core_hash_find(profile->chat_hash, tech_pvt->call_id))) {
free(uuid);
uuid = NULL;
switch_core_hash_delete(profile->chat_hash, tech_pvt->call_id);
}
switch_mutex_unlock(profile->flag_mutex);
goto end;
}
switch_copy_string(sofia_private->uuid_str, switch_core_session_get_uuid(session), sizeof(sofia_private->uuid_str));
sofia_private->uuid = sofia_private->uuid_str;
de->init_session = session;
switch_core_session_queue_signal_data(session, de);
goto end;
}
if (sofia_private && sofia_private != &mod_sofia_globals.destroy_private && sofia_private != &mod_sofia_globals.keep_private) {
switch_core_session_t *session;
if ((session = switch_core_session_locate(sofia_private->uuid))) {
switch_core_session_queue_signal_data(session, de);
switch_core_session_rwunlock(session);
goto end;
}
}
sofia_queue_message(de);
end:
//switch_cond_next();
return;
}
void event_handler(switch_event_t *event)
{
char *subclass, *sql;
char *class;
switch_event_t *pevent;
/* Get Original Event Name */
class = switch_event_get_header_nil(event, "orig-event-name");
if (!strcasecmp(class, "PRESENCE_IN")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "\nGot Presence IN event via MultiCast\n");
if (switch_event_create(&pevent, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "alt_event_type", switch_event_get_header_nil(event, "orig-alt_event_type"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "answer-state", switch_event_get_header_nil(event, "orig-answer-state"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "astate", switch_event_get_header_nil(event, "orig-astate"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "call-direction", switch_event_get_header_nil(event, "orig-call-direction"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Caller-Callee-ID-Number", switch_event_get_header_nil(event, "Orig-Caller-Callee-ID-Number"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Caller-Caller-ID-Name", switch_event_get_header_nil(event, "Orig-Caller-Caller-ID-Name"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Caller-Caller-ID-Number", switch_event_get_header_nil(event, "Orig-Caller-Caller-ID-Number"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Caller-Destination-Number", switch_event_get_header_nil(event, "Orig-Caller-Destination-Number"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Caller-Direction", switch_event_get_header_nil(event, "Orig-Caller-Direction"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Caller-Username", switch_event_get_header_nil(event, "Orig-Caller-Username"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "channel-state", switch_event_get_header_nil(event, "orig-channel-state"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "force-status", switch_event_get_header_nil(event, "orig-force-status"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "from", switch_event_get_header_nil(event, "orig-from"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "login", switch_event_get_header_nil(event, "orig-login"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Other-Leg-Caller-ID-Name", switch_event_get_header_nil(event, "Orig-Other-Leg-Caller-ID-Name"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Other-Leg-Caller-ID-Number", switch_event_get_header_nil(event, "Orig-Other-Leg-Caller-ID-Number"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "presence-call-direction", switch_event_get_header_nil(event, "orig-presence-call-direction"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "presence-call-info-state", switch_event_get_header_nil(event, "orig-presence-call-info-state"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Presence-Privacy", switch_event_get_header_nil(event, "Orig-Presence-Privacy"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "proto", switch_event_get_header_nil(event, "orig-proto"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "register-source", switch_event_get_header_nil(event, "orig-register-source"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "resub", switch_event_get_header_nil(event, "orig-resub"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "rpid", switch_event_get_header_nil(event, "orig-rpid"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "status", switch_event_get_header_nil(event, "orig-status"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "Unique-ID", switch_event_get_header_nil(event, "Orig-Unique-ID"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "variable_sip_from_user", switch_event_get_header_nil(event, "Orig-variable_sip_from_user"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "variable_sip_to_user", switch_event_get_header_nil(event, "Orig-variable_sip_to_user"));
/* we cannot use switch_event_fire, or otherwise we'll start an endless loop */
sofia_presence_event_handler(pevent);
return;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "\nCannot inject PRESENCE_IN event\n");
return;
}
} else if (!strcasecmp(class, "MESSAGE_WAITING")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "\nGot MWI event via MultiCast\n");
if (switch_event_create(&pevent, SWITCH_EVENT_MESSAGE_WAITING) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "MWI-Messages-Waiting", switch_event_get_header_nil(event, "orig-MWI-Messages-Waiting"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "MWI-Message-Account", switch_event_get_header_nil(event, "orig-MWI-Message-Account"));
switch_event_add_header_string(pevent, SWITCH_STACK_BOTTOM, "MWI-Voice-Message", switch_event_get_header_nil(event, "orig-MWI-Voice-Message"));
/* we cannot use switch_event_fire, or otherwise we'll start an endless loop */
sofia_presence_event_handler(pevent);
return;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "\nCannot inject MWI event\n");
return;
}
} else if ((subclass = switch_event_get_header_nil(event, "orig-event-subclass")) && !strcasecmp(subclass, MY_EVENT_UNREGISTER)) {
char *profile_name = switch_event_get_header_nil(event, "orig-profile-name");
char *from_user = switch_event_get_header_nil(event, "orig-from-user");
char *from_host = switch_event_get_header_nil(event, "orig-from-host");
char *call_id = switch_event_get_header_nil(event, "orig-call-id");
char *contact_str = switch_event_get_header_nil(event, "orig-contact");
sofia_profile_t *profile = NULL;
if (!profile_name || !(profile = sofia_glue_find_profile(profile_name))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Profile\n");
return;
}
if (sofia_test_pflag(profile, PFLAG_MULTIREG)) {
sql = switch_mprintf("delete from sip_registrations where call_id='%q'", call_id);
} else {
sql = switch_mprintf("delete from sip_registrations where sip_user='%q' and sip_host='%q'", from_user, from_host);
}
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Expired propagated registration for %s@%s->%s\n", from_user, from_host, contact_str);
sofia_glue_release_profile(profile);
} else if ((subclass = switch_event_get_header_nil(event, "orig-event-subclass")) && !strcasecmp(subclass, MY_EVENT_REGISTER)) {
char *from_user = switch_event_get_header_nil(event, "orig-from-user");
char *from_host = switch_event_get_header_nil(event, "orig-from-host");
char *to_host = switch_event_get_header_nil(event, "orig-to-host");
char *contact_str = switch_event_get_header_nil(event, "orig-contact");
char *exp_str = switch_event_get_header_nil(event, "orig-expires");
char *rpid = switch_event_get_header_nil(event, "orig-rpid");
char *call_id = switch_event_get_header_nil(event, "orig-call-id");
char *user_agent = switch_event_get_header_nil(event, "orig-user-agent");
long expires = (long) switch_epoch_time_now(NULL);
char *profile_name = switch_event_get_header_nil(event, "orig-profile-name");
char *to_user = switch_event_get_header_nil(event, "orig-to-user");
char *presence_hosts = switch_event_get_header_nil(event, "orig-presence-hosts");
char *network_ip = switch_event_get_header_nil(event, "orig-network-ip");
char *network_port = switch_event_get_header_nil(event, "orig-network-port");
char *username = switch_event_get_header_nil(event, "orig-username");
char *realm = switch_event_get_header_nil(event, "orig-realm");
char *orig_server_host = switch_event_get_header_nil(event, "orig-FreeSWITCH-IPv4");
char *orig_hostname = switch_event_get_header_nil(event, "orig-FreeSWITCH-Hostname");
char *fixed_contact_str = NULL;
sofia_profile_t *profile = NULL;
char guess_ip4[256];
char *mwi_account = NULL;
char *dup_mwi_account = NULL;
char *mwi_user = NULL;
char *mwi_host = NULL;
if ((mwi_account = switch_event_get_header_nil(event, "orig-mwi-account"))) {
dup_mwi_account = strdup(mwi_account);
switch_assert(dup_mwi_account != NULL);
switch_split_user_domain(dup_mwi_account, &mwi_user, &mwi_host);
}
if (!mwi_user) {
mwi_user = (char *) from_user;
}
if (!mwi_host) {
mwi_host = (char *) from_host;
}
if (exp_str) {
expires += atol(exp_str);
}
if (!rpid) {
rpid = "unknown";
}
if (!profile_name || !(profile = sofia_glue_find_profile(profile_name))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Profile\n");
goto end;
}
if (sofia_test_pflag(profile, PFLAG_MULTIREG)) {
sql = switch_mprintf("delete from sip_registrations where call_id='%q'", call_id);
} else {
sql = switch_mprintf("delete from sip_registrations where sip_user='%q' and sip_host='%q'", from_user, from_host);
}
if (mod_sofia_globals.rewrite_multicasted_fs_path && contact_str) {
const char *needle = ";fs_path=";
char *sptr, *eptr = NULL;
/* allocate enough room for worst-case scenario */
size_t len = strlen(contact_str) + strlen(to_host) + 14;
fixed_contact_str = malloc(len);
switch_assert(fixed_contact_str);
switch_copy_string(fixed_contact_str, contact_str, len);
if ((sptr = strstr(fixed_contact_str, needle))) {
char *origsptr = strstr(contact_str, needle);
eptr = strchr(++origsptr, ';');
} else {
sptr = strchr(fixed_contact_str, '\0') - 1;
}
switch (mod_sofia_globals.rewrite_multicasted_fs_path) {
case 1:
switch_snprintf(sptr, len - (sptr - fixed_contact_str), ";fs_path=sip:%s%s", to_host, eptr ? eptr : ">");
break;
case 2:
switch_snprintf(sptr, len - (sptr - fixed_contact_str), ";fs_path=sip:%s%s", orig_server_host, eptr ? eptr : ">");
break;
case 3:
switch_snprintf(sptr, len - (sptr - fixed_contact_str), ";fs_path=sip:%s%s", orig_hostname, eptr ? eptr : ">");
break;
default:
switch_snprintf(sptr, len - (sptr - fixed_contact_str), ";fs_path=sip:%s%s", to_host, eptr ? eptr : ">");
break;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Rewrote contact string from '%s' to '%s'\n", contact_str, fixed_contact_str);
contact_str = fixed_contact_str;
}
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
switch_find_local_ip(guess_ip4, sizeof(guess_ip4), NULL, AF_INET);
sql = switch_mprintf("insert into sip_registrations "
"(call_id, sip_user, sip_host, presence_hosts, contact, status, rpid, expires,"
"user_agent, server_user, server_host, profile_name, hostname, network_ip, network_port, sip_username, sip_realm,"
"mwi_user, mwi_host, orig_server_host, orig_hostname, ping_status, ping_count) "
"values ('%q','%q','%q','%q','%q','Registered','%q',%ld, '%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q', '%q', %d)",
call_id, from_user, from_host, presence_hosts, contact_str, rpid, expires, user_agent, to_user, guess_ip4,
profile_name, mod_sofia_globals.hostname, network_ip, network_port, username, realm, mwi_user, mwi_host,
orig_server_host, orig_hostname, "Reachable", 0);
if (sql) {
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Propagating registration for %s@%s->%s\n", from_user, from_host, contact_str);
}
sofia_glue_release_profile(profile);
end:
switch_safe_free(fixed_contact_str);
switch_safe_free(dup_mwi_account);
} else if ((subclass = switch_event_get_header_nil(event, "orig-event-subclass")) && !strcasecmp(subclass, MY_EVENT_SIP_USER_STATE)) {
char *profile_name = switch_event_get_header_nil(event, "orig-profile-name");
char *from_user = switch_event_get_header_nil(event, "orig-from-user");
char *from_host = switch_event_get_header_nil(event, "orig-from-host");
const char *call_id = switch_event_get_header_nil(event, "orig-call-id");
char *ping_status = switch_event_get_header_nil(event, "orig-Ping-Status");
sofia_profile_t *profile = NULL;
if (!profile_name || !(profile = sofia_glue_find_profile(profile_name))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Profile\n");
} else {
if (!strcmp(ping_status, "REACHABLE")) {
sql = switch_mprintf("update sip_registrations set ping_status='%q' where sip_user='%q' and sip_host='%q' and call_id='%q'",
"Reachable", from_user, from_host, call_id);
} else {
sql = switch_mprintf("update sip_registrations set ping_status='%q' where sip_user='%q' and sip_host='%q' and call_id='%q'",
"Unreachable", from_user, from_host, call_id);
}
if (sql) {
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Propagating sip_user_state for %s@%s. Ping-Status: %s\n", from_user, from_host, ping_status);
}
sofia_glue_release_profile(profile);
}
}
}
static void sofia_perform_profile_start_failure(sofia_profile_t *profile, char *profile_name, char *file, int line)
{
int arg = 0;
switch_event_t *s_event;
if (profile) {
if (!strcasecmp(profile->shutdown_type, "true")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile %s could not load! Shutting down!\n", profile->name);
switch_core_session_ctl(SCSC_SHUTDOWN, &arg);
} else if (!strcasecmp(profile->shutdown_type, "elegant")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile %s could not load! Waiting for calls to finish, then shutting down!\n",
profile->name);
switch_core_session_ctl(SCSC_SHUTDOWN_ELEGANT, &arg);
} else if (!strcasecmp(profile->shutdown_type, "asap")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile %s could not load! Shutting down ASAP!\n", profile->name);
switch_core_session_ctl(SCSC_SHUTDOWN_ASAP, &arg);
} else if (!strcasecmp(profile->shutdown_type, "reincarnate-now")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile %s could not load! Asking for reincarnation now!\n", profile->name);
switch_core_session_ctl(SCSC_REINCARNATE_NOW, &arg);
}
}
if ((switch_event_create(&s_event, SWITCH_EVENT_FAILURE) == SWITCH_STATUS_SUCCESS)) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "module_name", "mod_sofia");
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile_name", profile_name);
if (profile) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile_uri", profile->url);
}
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "failure_message", "Profile failed to start.");
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "file", file);
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "line", "%d", line);
switch_event_fire(&s_event);
}
}
/* not a static function so that it's still visible on stacktraces */
void watchdog_triggered_abort(void) {
abort();
}
#define sofia_profile_start_failure(p, xp) sofia_perform_profile_start_failure(p, xp, __FILE__, __LINE__)
#define SQLLEN 1024 * 1024
void *SWITCH_THREAD_FUNC sofia_profile_worker_thread_run(switch_thread_t *thread, void *obj)
{
sofia_profile_t *profile = (sofia_profile_t *) obj;
uint32_t ireg_loops = profile->ireg_seconds; /* Number of loop iterations done when we haven't checked for registrations */
uint32_t iping_loops = profile->iping_freq; /* Number of loop iterations done when we haven't checked for ping expires */
uint32_t gateway_loops = GATEWAY_SECONDS; /* Number of loop iterations done when we haven't checked for gateways */
void *pop;
int tick = 0, x = 0;
sofia_set_pflag_locked(profile, PFLAG_WORKER_RUNNING);
while ((mod_sofia_globals.running == 1 && sofia_test_pflag(profile, PFLAG_RUNNING))) {
if (tick) {
if (profile->watchdog_enabled) {
uint32_t event_diff = 0, step_diff = 0, event_fail = 0, step_fail = 0;
if (profile->step_timeout) {
step_diff = (uint32_t) ((switch_time_now() - profile->last_root_step) / 1000);
if (step_diff > profile->step_timeout) {
step_fail = 1;
}
}
if (profile->event_timeout) {
event_diff = (uint32_t) ((switch_time_now() - profile->last_sip_event) / 1000);
if (event_diff > profile->event_timeout) {
event_fail = 1;
}
}
if (step_fail && profile->event_timeout && !event_fail) {
step_fail = 0;
}
if (event_fail || step_fail) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Profile %s: SIP STACK FAILURE DETECTED BY WATCHDOG!\n"
"GOODBYE CRUEL WORLD, I'M LEAVING YOU TODAY....GOODBYE, GOODBYE, GOOD BYE\n", profile->name);
switch_yield(2000000);
watchdog_triggered_abort();
}
}
if (!sofia_test_pflag(profile, PFLAG_STANDBY)) {
if (++ireg_loops >= (uint32_t)profile->ireg_seconds) {
time_t now = switch_epoch_time_now(NULL);
sofia_reg_check_expire(profile, now, 0);
ireg_loops = 0;
}
if(++iping_loops >= (uint32_t)profile->iping_freq) {
time_t now = switch_epoch_time_now(NULL);
sofia_reg_check_ping_expire(profile, now, profile->iping_seconds);
iping_loops = 0;
}
if (++gateway_loops >= GATEWAY_SECONDS) {
sofia_reg_check_gateway(profile, switch_epoch_time_now(NULL));
sofia_sub_check_gateway(profile, switch_epoch_time_now(NULL));
gateway_loops = 0;
}
}
tick = 0;
}
if (switch_queue_pop_timeout(mod_sofia_globals.general_event_queue, &pop, 100000) == SWITCH_STATUS_SUCCESS) {
do {
switch_event_t *event = (switch_event_t *) pop;
general_event_handler(event);
switch_event_destroy(&event);
pop = NULL;
switch_queue_trypop(mod_sofia_globals.general_event_queue, &pop);
} while (pop);
}
sofia_glue_fire_events(profile);
if (++x == 10) {
tick = 1;
x = 0;
}
}
sofia_clear_pflag_locked(profile, PFLAG_WORKER_RUNNING);
return NULL;
}
switch_thread_t *launch_sofia_worker_thread(sofia_profile_t *profile)
{
switch_thread_t *thread = NULL;
switch_threadattr_t *thd_attr = NULL;
int x = 0;
switch_xml_t cfg = NULL, xml = NULL, xprofile = NULL, xprofiles = NULL, gateways_tag = NULL, domains_tag = NULL, domain_tag = NULL;
switch_event_t *params = NULL;
char *cf = "sofia.conf";
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Launching worker thread for %s\n", profile->name);
/* Parse gateways */
switch_event_create(&params, SWITCH_EVENT_REQUEST_PARAMS);
switch_assert(params);
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "profile", profile->name);
if (!(xml = switch_xml_open_cfg(cf, &cfg, params))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
goto end;
}
if ((xprofiles = switch_xml_child(cfg, "profiles"))) {
if ((xprofile = switch_xml_find_child(xprofiles, "profile", "name", profile->name))) {
if ((gateways_tag = switch_xml_child(xprofile, "gateways"))) {
parse_gateways(profile, gateways_tag, NULL);
}
if ((domains_tag = switch_xml_child(xprofile, "domains"))) {
switch_event_t *xml_params;
switch_event_create(&xml_params, SWITCH_EVENT_REQUEST_PARAMS);
switch_assert(xml_params);
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "purpose", "gateways");
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "profile", profile->name);
for (domain_tag = switch_xml_child(domains_tag, "domain"); domain_tag; domain_tag = domain_tag->next) {
switch_xml_t droot, x_domain_tag;
const char *dname = switch_xml_attr_soft(domain_tag, "name");
const char *parse = switch_xml_attr_soft(domain_tag, "parse");
const char *alias = switch_xml_attr_soft(domain_tag, "alias");
if (!zstr(dname)) {
if (!strcasecmp(dname, "all")) {
switch_xml_t xml_root, x_domains;
if (switch_xml_locate("directory", NULL, NULL, NULL, &xml_root, &x_domains, xml_params, SWITCH_FALSE) ==
SWITCH_STATUS_SUCCESS) {
for (x_domain_tag = switch_xml_child(x_domains, "domain"); x_domain_tag; x_domain_tag = x_domain_tag->next) {
dname = switch_xml_attr_soft(x_domain_tag, "name");
parse_domain_tag(profile, x_domain_tag, dname, parse, alias);
}
switch_xml_free(xml_root);
}
} else if (switch_xml_locate_domain(dname, xml_params, &droot, &x_domain_tag) == SWITCH_STATUS_SUCCESS) {
parse_domain_tag(profile, x_domain_tag, dname, parse, alias);
switch_xml_free(droot);
}
}
}
switch_event_destroy(&xml_params);
}
}
}
switch_threadattr_create(&thd_attr, profile->pool);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
//switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME);
switch_thread_create(&thread, thd_attr, sofia_profile_worker_thread_run, profile, profile->pool);
while (!sofia_test_pflag(profile, PFLAG_WORKER_RUNNING)) {
switch_yield(100000);
if (++x >= 100) {
break;
}
}
end:
switch_event_destroy(&params);
if (xml) {
switch_xml_free(xml);
}
return thread;
}
void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void *obj)
{
sofia_profile_t *profile = (sofia_profile_t *) obj;
//switch_memory_pool_t *pool;
sip_alias_node_t *node;
switch_event_t *s_event;
int use_100rel = !sofia_test_pflag(profile, PFLAG_DISABLE_100REL);
int use_timer = !sofia_test_pflag(profile, PFLAG_DISABLE_TIMER);
int use_rfc_5626 = sofia_test_pflag(profile, PFLAG_ENABLE_RFC5626);
const char *supported = NULL;
int sanity, attempts = 0;
switch_thread_t *worker_thread;
switch_status_t st;
char qname [128] = "";
switch_mutex_lock(mod_sofia_globals.mutex);
mod_sofia_globals.threads++;
switch_mutex_unlock(mod_sofia_globals.mutex);
profile->s_root = su_root_create(NULL);
//profile->home = su_home_new(sizeof(*profile->home));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Creating agent for %s\n", profile->name);
if (!sofia_glue_init_sql(profile)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open SQL Database [%s]!\n", profile->name);
sofia_profile_start_failure(profile, profile->name);
sofia_glue_del_profile(profile);
goto end;
}
supported = switch_core_sprintf(profile->pool, "%s%s%spath, replaces", use_100rel ? "precondition, 100rel, " : "", use_timer ? "timer, " : "", use_rfc_5626 ? "outbound, " : "");
if (sofia_test_pflag(profile, PFLAG_AUTO_NAT) && switch_nat_get_type()) {
if ( (! sofia_test_pflag(profile, PFLAG_TLS) || ! profile->tls_only) && switch_nat_add_mapping(profile->sip_port, SWITCH_NAT_UDP, NULL, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created UDP nat mapping for %s port %d\n", profile->name, profile->sip_port);
}
if (switch_nat_add_mapping(profile->sip_port, SWITCH_NAT_TCP, NULL, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created TCP nat mapping for %s port %d\n", profile->name, profile->sip_port);
}
if (sofia_test_pflag(profile, PFLAG_TLS)
&& switch_nat_add_mapping(profile->tls_sip_port, SWITCH_NAT_TCP, NULL, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created TCP/TLS nat mapping for %s port %d\n", profile->name, profile->tls_sip_port);
}
}
/* We have to init the verify_subjects here as during config stage profile->home isn't setup, it should be freed when profile->home is freed */
if ( (profile->tls_verify_policy & TPTLS_VERIFY_SUBJECTS_IN) && profile->tls_verify_in_subjects_str && ! profile->tls_verify_in_subjects) {
profile->tls_verify_in_subjects = su_strlst_dup_split((su_home_t *)profile->nua, profile->tls_verify_in_subjects_str, "|");
}
do {
profile->nua = nua_create(profile->s_root, /* Event loop */
sofia_event_callback, /* Callback for processing events */
profile, /* Additional data to pass to callback */
TAG_IF( ! sofia_test_pflag(profile, PFLAG_TLS) || ! profile->tls_only, NUTAG_URL(profile->bindurl)),
NTATAG_USER_VIA(1),
TPTAG_PONG2PING(1),
NTATAG_TCP_RPORT(0),
NTATAG_TLS_RPORT(0),
NUTAG_RETRY_AFTER_ENABLE(0),
NUTAG_AUTO_INVITE_100(0),
TAG_IF(!strchr(profile->sipip, ':'),
SOATAG_AF(SOA_AF_IP4_ONLY)),
TAG_IF(strchr(profile->sipip, ':'),
SOATAG_AF(SOA_AF_IP6_ONLY)),
TAG_IF(sofia_test_pflag(profile, PFLAG_TLS),
NUTAG_SIPS_URL(profile->tls_bindurl)),
TAG_IF(profile->ws_bindurl,
NUTAG_WS_URL(profile->ws_bindurl)),
TAG_IF(profile->wss_bindurl,
NUTAG_WSS_URL(profile->wss_bindurl)),
TAG_IF(profile->tls_cert_dir,
NUTAG_CERTIFICATE_DIR(profile->tls_cert_dir)),
TAG_IF(sofia_test_pflag(profile, PFLAG_TLS) && profile->tls_passphrase,
TPTAG_TLS_PASSPHRASE(profile->tls_passphrase)),
TAG_IF(sofia_test_pflag(profile, PFLAG_TLS),
TPTAG_TLS_VERIFY_POLICY(profile->tls_verify_policy)),
TAG_IF(sofia_test_pflag(profile, PFLAG_TLS),
TPTAG_TLS_VERIFY_DEPTH(profile->tls_verify_depth)),
TAG_IF(sofia_test_pflag(profile, PFLAG_TLS),
TPTAG_TLS_VERIFY_DATE(profile->tls_verify_date)),
TAG_IF(sofia_test_pflag(profile, PFLAG_TLS) && profile->tls_verify_in_subjects,
TPTAG_TLS_VERIFY_SUBJECTS(profile->tls_verify_in_subjects)),
TAG_IF(sofia_test_pflag(profile, PFLAG_TLS),
TPTAG_TLS_CIPHERS(profile->tls_ciphers)),
TAG_IF(sofia_test_pflag(profile, PFLAG_TLS),
TPTAG_TLS_VERSION(profile->tls_version)),
TAG_IF(sofia_test_pflag(profile, PFLAG_TLS) && profile->tls_timeout,
TPTAG_TLS_TIMEOUT(profile->tls_timeout)),
TAG_IF(!strchr(profile->sipip, ':'),
NTATAG_UDP_MTU(65535)),
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_SRV),
NTATAG_USE_SRV(0)),
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_NAPTR),
NTATAG_USE_NAPTR(0)),
TAG_IF(sofia_test_pflag(profile, PFLAG_TCP_PINGPONG),
TPTAG_PINGPONG(profile->tcp_pingpong)),
TAG_IF(sofia_test_pflag(profile, PFLAG_TCP_PING2PONG),
TPTAG_PINGPONG(profile->tcp_ping2pong)),
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_SRV503),
NTATAG_SRV_503(0)),
TAG_IF(sofia_test_pflag(profile, PFLAG_SOCKET_TCP_KEEPALIVE),
TPTAG_SOCKET_KEEPALIVE(profile->socket_tcp_keepalive)),
TAG_IF(sofia_test_pflag(profile, PFLAG_TCP_KEEPALIVE),
TPTAG_KEEPALIVE(profile->tcp_keepalive)),
NTATAG_DEFAULT_PROXY(profile->outbound_proxy),
NTATAG_SERVER_RPORT(profile->server_rport_level),
NTATAG_CLIENT_RPORT(profile->client_rport_level),
TPTAG_LOG(sofia_test_flag(profile, TFLAG_TPORT_LOG)),
TPTAG_CAPT(sofia_test_flag(profile, TFLAG_CAPTURE) ? mod_sofia_globals.capture_server : NULL),
TAG_IF(sofia_test_pflag(profile, PFLAG_SIPCOMPACT),
NTATAG_SIPFLAGS(MSG_DO_COMPACT)),
TAG_IF(profile->timer_t1, NTATAG_SIP_T1(profile->timer_t1)),
TAG_IF(profile->timer_t1x64, NTATAG_SIP_T1X64(profile->timer_t1x64)),
TAG_IF(profile->timer_t2, NTATAG_SIP_T2(profile->timer_t2)),
TAG_IF(profile->timer_t4, NTATAG_SIP_T4(profile->timer_t4)),
SIPTAG_ACCEPT_STR("application/sdp, multipart/mixed"),
TAG_IF(sofia_test_pflag(profile, PFLAG_NO_CONNECTION_REUSE),
TPTAG_REUSE(0)),
TAG_END()); /* Last tag should always finish the sequence */
if (!profile->nua) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Creating SIP UA for profile: %s (%s) ATTEMPT %d (RETRY IN %d SEC)\n",
profile->name, profile->bindurl, attempts + 1, profile->bind_attempt_interval);
if (attempts < profile->bind_attempts) {
switch_yield(1000000 * profile->bind_attempt_interval);
}
}
} while (!profile->nua && attempts++ < profile->bind_attempts);
if (!profile->nua) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Creating SIP UA for profile: %s (%s)\n"
"The likely causes for this are:\n" "1) Another application is already listening on the specified address.\n"
"2) The IP the profile is attempting to bind to is not local to this system.\n", profile->name, profile->bindurl);
sofia_profile_start_failure(profile, profile->name);
sofia_glue_del_profile(profile);
goto end;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created agent for %s\n", profile->name);
nua_set_params(profile->nua,
SIPTAG_ALLOW_STR("INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO"),
SIPTAG_USER_AGENT(SIP_NONE),
NUTAG_AUTOANSWER(0),
NUTAG_AUTOACK(0),
NUTAG_AUTOALERT(0),
NUTAG_ENABLEMESSENGER(1),
NTATAG_EXTRA_100(0),
TAG_IF(sofia_test_pflag(profile, PFLAG_ALLOW_UPDATE), NUTAG_ALLOW("UPDATE")),
TAG_IF((profile->mflags & MFLAG_REGISTER), NUTAG_ALLOW("REGISTER")),
TAG_IF((profile->mflags & MFLAG_REFER), NUTAG_ALLOW("REFER")),
TAG_IF(!sofia_test_pflag(profile, PFLAG_DISABLE_100REL), NUTAG_ALLOW("PRACK")),
NUTAG_ALLOW("INFO"),
NUTAG_ALLOW("NOTIFY"),
NUTAG_ALLOW_EVENTS("talk"),
NUTAG_ALLOW_EVENTS("hold"),
NUTAG_ALLOW_EVENTS("conference"),
NUTAG_APPL_METHOD("OPTIONS"),
NUTAG_APPL_METHOD("INVITE"),
NUTAG_APPL_METHOD("REFER"),
NUTAG_APPL_METHOD("REGISTER"),
NUTAG_APPL_METHOD("NOTIFY"), NUTAG_APPL_METHOD("INFO"), NUTAG_APPL_METHOD("ACK"), NUTAG_APPL_METHOD("SUBSCRIBE"),
#ifdef MANUAL_BYE
NUTAG_APPL_METHOD("BYE"),
#endif
NUTAG_APPL_METHOD("MESSAGE"),
TAG_IF(profile->session_timeout && profile->minimum_session_expires, NUTAG_MIN_SE(profile->minimum_session_expires)),
NUTAG_SESSION_TIMER(profile->session_timeout),
NTATAG_MAX_PROCEEDING(profile->max_proceeding),
TAG_IF(profile->pres_type, NUTAG_ALLOW("PUBLISH")),
TAG_IF(profile->pres_type, NUTAG_ALLOW("SUBSCRIBE")),
TAG_IF(profile->pres_type, NUTAG_ENABLEMESSAGE(1)),
TAG_IF(profile->pres_type, NUTAG_ALLOW_EVENTS("presence")),
TAG_IF(profile->pres_type, NUTAG_ALLOW_EVENTS("as-feature-event")),
TAG_IF((profile->pres_type || sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE)), NUTAG_ALLOW_EVENTS("dialog")),
TAG_IF((profile->pres_type || sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE)), NUTAG_ALLOW_EVENTS("line-seize")),
TAG_IF(profile->pres_type, NUTAG_ALLOW_EVENTS("call-info")),
TAG_IF((profile->pres_type || sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE)), NUTAG_ALLOW_EVENTS("sla")),
TAG_IF(profile->pres_type, NUTAG_ALLOW_EVENTS("include-session-description")),
TAG_IF(profile->pres_type, NUTAG_ALLOW_EVENTS("presence.winfo")),
TAG_IF(profile->pres_type, NUTAG_ALLOW_EVENTS("message-summary")),
TAG_IF(profile->pres_type == PRES_TYPE_PNP, NUTAG_ALLOW_EVENTS("ua-profile")),
NUTAG_ALLOW_EVENTS("refer"), SIPTAG_SUPPORTED_STR(supported),
TAG_IF(strcasecmp(profile->user_agent, "_undef_"), SIPTAG_USER_AGENT_STR(profile->user_agent)),
TAG_END());
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set params for %s\n", profile->name);
if (sofia_test_pflag(profile, PFLAG_AUTO_ASSIGN_PORT) || sofia_test_pflag(profile, PFLAG_AUTO_ASSIGN_TLS_PORT)) {
sip_via_t *vias = nta_agent_via(profile->nua->nua_nta);
sip_via_t *via = NULL;
for (via = vias; via; via = via->v_next) {
if (sofia_test_pflag(profile, PFLAG_AUTO_ASSIGN_PORT) && !strcmp(via->v_protocol, "SIP/2.0/UDP")) {
profile->sip_port = (switch_port_t)atoi(via->v_port);
if (!profile->extsipport) profile->extsipport = profile->sip_port;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Found auto sip port %d for %s\n", profile->sip_port, profile->name);
}
if (sofia_test_pflag(profile, PFLAG_AUTO_ASSIGN_TLS_PORT) && !strcmp(via->v_protocol, "SIP/2.0/TLS")) {
profile->tls_sip_port = (switch_port_t)atoi(via->v_port);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Found auto sip port %d for %s (TLS)\n", profile->tls_sip_port, profile->name);
}
}
config_sofia_profile_urls(profile);
}
for (node = profile->aliases; node; node = node->next) {
node->nua = nua_create(profile->s_root, /* Event loop */
sofia_event_callback, /* Callback for processing events */
profile, /* Additional data to pass to callback */
NTATAG_SERVER_RPORT(profile->server_rport_level), NUTAG_URL(node->url), TAG_END()); /* Last tag should always finish the sequence */
nua_set_params(node->nua,
SIPTAG_USER_AGENT(SIP_NONE),
NUTAG_APPL_METHOD("OPTIONS"),
NUTAG_APPL_METHOD("REFER"),
NUTAG_APPL_METHOD("SUBSCRIBE"),
NUTAG_AUTOANSWER(0),
NUTAG_AUTOACK(0),
NUTAG_AUTOALERT(0),
TAG_IF((profile->mflags & MFLAG_REGISTER), NUTAG_ALLOW("REGISTER")),
TAG_IF((profile->mflags & MFLAG_REFER), NUTAG_ALLOW("REFER")),
NUTAG_ALLOW("INFO"),
TAG_IF(profile->pres_type, NUTAG_ALLOW("PUBLISH")),
TAG_IF(profile->pres_type, NUTAG_ENABLEMESSAGE(1)),
SIPTAG_SUPPORTED_STR(supported),
TAG_IF(strcasecmp(profile->user_agent, "_undef_"), SIPTAG_USER_AGENT_STR(profile->user_agent)),
TAG_END());
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Activated db for %s\n", profile->name);
switch_mutex_init(&profile->ireg_mutex, SWITCH_MUTEX_NESTED, profile->pool);
switch_mutex_init(&profile->dbh_mutex, SWITCH_MUTEX_NESTED, profile->pool);
switch_mutex_init(&profile->gateway_mutex, SWITCH_MUTEX_NESTED, profile->pool);
switch_queue_create(&profile->event_queue, SOFIA_QUEUE_SIZE, profile->pool);
switch_snprintf(qname, sizeof(qname), "sofia:%s", profile->name);
switch_sql_queue_manager_init_name(qname,
&profile->qm,
2,
profile->odbc_dsn ? profile->odbc_dsn : profile->dbname,
SWITCH_MAX_TRANS,
profile->pre_trans_execute,
profile->post_trans_execute,
profile->inner_pre_trans_execute,
profile->inner_post_trans_execute);
switch_sql_queue_manager_start(profile->qm);
if (switch_event_create(&s_event, SWITCH_EVENT_PUBLISH) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "service", "_sip._udp,_sip._tcp,_sip._sctp%s",
(sofia_test_pflag(profile, PFLAG_TLS)) ? ",_sips._tcp" : "");
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "port", "%d", profile->sip_port);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "module_name", "mod_sofia");
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile_name", profile->name);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile_uri", profile->url);
if (sofia_test_pflag(profile, PFLAG_TLS)) {
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "tls_port", "%d", profile->tls_sip_port);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile_tls_uri", profile->tls_url);
}
switch_event_fire(&s_event);
}
sofia_glue_add_profile(profile->name, profile);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Starting thread for %s\n", profile->name);
profile->started = switch_epoch_time_now(NULL);
sofia_set_pflag_locked(profile, PFLAG_RUNNING);
worker_thread = launch_sofia_worker_thread(profile);
switch_yield(1000000);
while (mod_sofia_globals.running == 1 && sofia_test_pflag(profile, PFLAG_RUNNING) && sofia_test_pflag(profile, PFLAG_WORKER_RUNNING)) {
su_root_step(profile->s_root, 1000);
profile->last_root_step = switch_time_now();
}
sofia_clear_pflag_locked(profile, PFLAG_RUNNING);
sofia_reg_close_handles(profile);
switch_core_session_hupall_matching_var("sofia_profile_name", profile->name, SWITCH_CAUSE_MANAGER_REQUEST);
sanity = 10;
while (profile->inuse) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Waiting for %d session(s)\n", profile->inuse);
su_root_step(profile->s_root, 1000);
if (!--sanity) {
break;
} else if (sanity == 5) {
switch_core_session_hupall_matching_var("sofia_profile_name", profile->name, SWITCH_CAUSE_MANAGER_REQUEST);
}
}
sofia_reg_unregister(profile);
nua_shutdown(profile->nua);
sanity = 100;
while (!sofia_test_pflag(profile, PFLAG_SHUTDOWN) || profile->queued_events > 0) {
su_root_step(profile->s_root, 1000);
if (!--sanity) {
break;
}
}
sofia_clear_pflag_locked(profile, PFLAG_RUNNING);
sofia_clear_pflag_locked(profile, PFLAG_SHUTDOWN);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Waiting for worker thread\n");
if ( worker_thread ) {
switch_thread_join(&st, worker_thread);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR: Sofia worker thead failed to start\n");
}
sanity = 4;
while (profile->inuse) {
switch_core_session_hupall_matching_var("sofia_profile_name", profile->name, SWITCH_CAUSE_MANAGER_REQUEST);
switch_yield(5000000);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Waiting for %d session(s)\n", profile->inuse);
if (!--sanity) {
break;
}
}
nua_destroy(profile->nua);
switch_mutex_lock(profile->ireg_mutex);
switch_mutex_unlock(profile->ireg_mutex);
switch_mutex_lock(profile->flag_mutex);
switch_mutex_unlock(profile->flag_mutex);
switch_sql_queue_manager_destroy(&profile->qm);
if (switch_event_create(&s_event, SWITCH_EVENT_UNPUBLISH) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "service", "_sip._udp,_sip._tcp,_sip._sctp%s",
(sofia_test_pflag(profile, PFLAG_TLS)) ? ",_sips._tcp" : "");
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "port", "%d", profile->sip_port);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "module_name", "mod_sofia");
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile_name", profile->name);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile_uri", profile->url);
if (sofia_test_pflag(profile, PFLAG_TLS)) {
switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "tls_port", "%d", profile->tls_sip_port);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile_tls_uri", profile->tls_url);
}
switch_event_fire(&s_event);
}
if (sofia_test_pflag(profile, PFLAG_AUTO_NAT) && switch_nat_get_type()) {
if (switch_nat_del_mapping(profile->sip_port, SWITCH_NAT_UDP) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Deleted UDP nat mapping for %s port %d\n", profile->name, profile->sip_port);
}
if (switch_nat_del_mapping(profile->sip_port, SWITCH_NAT_TCP) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Deleted TCP nat mapping for %s port %d\n", profile->name, profile->sip_port);
}
if (sofia_test_pflag(profile, PFLAG_TLS) && switch_nat_del_mapping(profile->tls_sip_port, SWITCH_NAT_TCP) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Deleted TCP/TLS nat mapping for %s port %d\n", profile->name, profile->tls_sip_port);
}
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock %s\n", profile->name);
switch_thread_rwlock_wrlock(profile->rwlock);
//su_home_unref(profile->home);
su_root_destroy(profile->s_root);
//pool = profile->pool;
sofia_glue_del_profile(profile);
switch_core_hash_destroy(&profile->chat_hash);
switch_core_hash_destroy(&profile->reg_nh_hash);
switch_core_hash_destroy(&profile->mwi_debounce_hash);
switch_thread_rwlock_unlock(profile->rwlock);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write unlock %s\n", profile->name);
if (sofia_test_pflag(profile, PFLAG_RESPAWN)) {
config_sofia(SOFIA_CONFIG_RESPAWN, profile->name);
}
sofia_profile_destroy(profile);
end:
switch_mutex_lock(mod_sofia_globals.mutex);
mod_sofia_globals.threads--;
switch_mutex_unlock(mod_sofia_globals.mutex);
return NULL;
}
void sofia_profile_destroy(sofia_profile_t *profile)
{
if (!profile->inuse) {
switch_memory_pool_t *pool = profile->pool;
switch_core_destroy_memory_pool(&pool);
} else {
sofia_set_pflag(profile, PFLAG_DESTROY);
}
}
void launch_sofia_profile_thread(sofia_profile_t *profile)
{
//switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL;
switch_threadattr_create(&thd_attr, profile->pool);
switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME);
switch_thread_create(&profile->thread, thd_attr, sofia_profile_thread_run, profile, profile->pool);
}
static void logger(void *logarg, char const *fmt, va_list ap)
{
if (!fmt) return;
switch_log_vprintf(SWITCH_CHANNEL_LOG_CLEAN, mod_sofia_globals.tracelevel, fmt, ap);
}
static su_log_t *sofia_get_logger(const char *name)
{
if (!strcasecmp(name, "tport")) {
return tport_log;
} else if (!strcasecmp(name, "iptsec")) {
return iptsec_log;
} else if (!strcasecmp(name, "nea")) {
return nea_log;
} else if (!strcasecmp(name, "nta")) {
return nta_log;
} else if (!strcasecmp(name, "nth_client")) {
return nth_client_log;
} else if (!strcasecmp(name, "nth_server")) {
return nth_server_log;
} else if (!strcasecmp(name, "nua")) {
return nua_log;
} else if (!strcasecmp(name, "soa")) {
return soa_log;
} else if (!strcasecmp(name, "sresolv")) {
return sresolv_log;
#ifdef HAVE_SOFIA_STUN
} else if (!strcasecmp(name, "stun")) {
return stun_log;
#endif
} else if (!strcasecmp(name, "default")) {
return su_log_default;
} else {
return NULL;
}
}
switch_status_t sofia_set_loglevel(const char *name, int level)
{
su_log_t *log = NULL;
if (level < 0 || level > 9) {
return SWITCH_STATUS_FALSE;
}
if (!strcasecmp(name, "all")) {
su_log_set_level(su_log_default, level);
su_log_set_level(tport_log, level);
su_log_set_level(iptsec_log, level);
su_log_set_level(nea_log, level);
su_log_set_level(nta_log, level);
su_log_set_level(nth_client_log, level);
su_log_set_level(nth_server_log, level);
su_log_set_level(nua_log, level);
su_log_set_level(soa_log, level);
su_log_set_level(sresolv_log, level);
#ifdef HAVE_SOFIA_STUN
su_log_set_level(stun_log, level);
#endif
return SWITCH_STATUS_SUCCESS;
}
if (!(log = sofia_get_logger(name))) {
return SWITCH_STATUS_FALSE;
}
su_log_set_level(log, level);
return SWITCH_STATUS_SUCCESS;
}
int sofia_get_loglevel(const char *name)
{
su_log_t *log = NULL;
if ((log = sofia_get_logger(name))) {
return log->log_level;
} else {
return -1;
}
}
static void parse_gateway_subscriptions(sofia_profile_t *profile, sofia_gateway_t *gateway, switch_xml_t gw_subs_tag)
{
switch_xml_t subscription_tag, param;
for (subscription_tag = switch_xml_child(gw_subs_tag, "subscription"); subscription_tag; subscription_tag = subscription_tag->next) {
sofia_gateway_subscription_t *gw_sub;
if ((gw_sub = switch_core_alloc(profile->pool, sizeof(*gw_sub)))) {
char *expire_seconds = "3600", *retry_seconds = "30", *content_type = "NO_CONTENT_TYPE";
uint32_t username_in_request = 0;
char *event = (char *) switch_xml_attr_soft(subscription_tag, "event");
gw_sub->event = switch_core_strdup(gateway->pool, event);
gw_sub->gateway = gateway;
gw_sub->next = NULL;
for (param = switch_xml_child(subscription_tag, "param"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
char *val = (char *) switch_xml_attr_soft(param, "value");
if (!strcmp(var, "expire-seconds")) {
expire_seconds = val;
} else if (!strcmp(var, "retry-seconds")) {
retry_seconds = val;
} else if (!strcmp(var, "content-type")) {
content_type = val;
} else if (!strcmp(var, "username-in-request")) {
username_in_request = switch_true(val);
}
}
gw_sub->retry_seconds = atoi(retry_seconds);
if (gw_sub->retry_seconds < 10) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "INVALID: retry_seconds correcting the value to 30\n");
gw_sub->retry_seconds = 30;
}
gw_sub->expires_str = switch_core_strdup(gateway->pool, expire_seconds);
if ((gw_sub->freq = atoi(gw_sub->expires_str)) < 5) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Freq: %d. Setting Register-Frequency to 3600\n", gw_sub->freq);
gw_sub->freq = 3600;
}
if(username_in_request) {
gw_sub->request_uri = gateway->register_to;
} else {
gw_sub->request_uri = gateway->register_url;
}
gw_sub->freq -= 2;
gw_sub->content_type = switch_core_strdup(gateway->pool, content_type);
gw_sub->next = gateway->subscriptions;
}
gateway->subscriptions = gw_sub;
}
}
static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag, const char *gwname)
{
switch_xml_t gateway_tag, param = NULL, x_params, gw_subs_tag;
sofia_gateway_t *gp;
for (gateway_tag = switch_xml_child(gateways_tag, "gateway"); gateway_tag; gateway_tag = gateway_tag->next) {
char *name = (char *) switch_xml_attr_soft(gateway_tag, "name");
sofia_gateway_t *gateway;
char *pkey = switch_mprintf("%s::%s", profile->name, name);
if (zstr(name) || switch_regex_match(name, "^[\\w\\.\\-\\_]+$") != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Ignoring invalid name '%s'\n", name ? name : "NULL");
free(pkey);
goto skip;
}
if (gwname && strcmp(gwname, name)) {
free(pkey);
goto skip;
}
switch_mutex_lock(mod_sofia_globals.hash_mutex);
if ((gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, name)) && (gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey)) && !gp->deleted) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate gateway '%s'\n", name);
switch_mutex_unlock(mod_sofia_globals.hash_mutex);
free(pkey);
goto skip;
}
free(pkey);
switch_mutex_unlock(mod_sofia_globals.hash_mutex);
if ((gateway = switch_core_alloc(profile->pool, sizeof(*gateway)))) {
const char *sipip, *format;
switch_uuid_t uuid;
uint32_t ping_freq = 0, extension_in_contact = 0, ping_monitoring = 0, distinct_to = 0, rfc_5626 = 0;
int ping_max = 1, ping_min = 1;
char *register_str = "true", *scheme = "Digest",
*realm = NULL,
*username = NULL,
*auth_username = NULL,
*password = NULL,
*caller_id_in_from = "false",
*extension = NULL,
*proxy = NULL,
*options_user_agent = NULL,
*context = profile->context,
*expire_seconds = "3600",
*retry_seconds = "30",
*fail_908_retry_seconds = NULL,
*timeout_seconds = "60",
*from_user = "", *from_domain = NULL, *outbound_proxy = NULL, *register_proxy = NULL, *contact_host = NULL,
*contact_params = "", *params = NULL, *register_transport = NULL,
*reg_id = NULL, *str_rfc_5626 = "";
if (!context) {
context = "default";
}
switch_uuid_get(&uuid);
switch_uuid_format(gateway->uuid_str, &uuid);
gateway->register_transport = SOFIA_TRANSPORT_UDP;
gateway->pool = profile->pool;
gateway->profile = profile;
gateway->name = switch_core_strdup(gateway->pool, name);
gateway->freq = 0;
gateway->next = NULL;
gateway->ping = 0;
gateway->ping_freq = 0;
gateway->ping_max = 0;
gateway->ping_min = 0;
gateway->ping_sent = 0;
gateway->ping_time = 0;
gateway->ping_count = 0;
gateway->ping_monitoring = SWITCH_FALSE;
gateway->ib_calls = 0;
gateway->ob_calls = 0;
gateway->ib_failed_calls = 0;
gateway->ob_failed_calls = 0;
gateway->destination_prefix = "";
if ((x_params = switch_xml_child(gateway_tag, "variables"))) {
param = switch_xml_child(x_params, "variable");
} else {
param = switch_xml_child(gateway_tag, "variable");
}
for (; param; param = param->next) {
const char *var = switch_xml_attr(param, "name");
const char *val = switch_xml_attr(param, "value");
const char *direction = switch_xml_attr(param, "direction");
int in = 0, out = 0;
if (var && val) {
if (direction) {
if (!strcasecmp(direction, "inbound")) {
in = 1;
} else if (!strcasecmp(direction, "outbound")) {
out = 1;
}
} else {
in = out = 1;
}
if (in) {
if (!gateway->ib_vars) {
switch_event_create_plain(&gateway->ib_vars, SWITCH_EVENT_GENERAL);
}
switch_event_add_header_string(gateway->ib_vars, SWITCH_STACK_BOTTOM, var, val);
}
if (out) {
if (!gateway->ob_vars) {
switch_event_create_plain(&gateway->ob_vars, SWITCH_EVENT_GENERAL);
}
switch_event_add_header_string(gateway->ob_vars, SWITCH_STACK_BOTTOM, var, val);
}
}
}
if ((x_params = switch_xml_child(gateway_tag, "params"))) {
param = switch_xml_child(x_params, "param");
} else {
param = switch_xml_child(gateway_tag, "param");
}
for (; param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
char *val = (char *) switch_xml_attr_soft(param, "value");
if (!strcmp(var, "register")) {
register_str = val;
} else if (!strcmp(var, "scheme")) {
scheme = val;
} else if (!strcmp(var, "realm")) {
realm = val;
} else if (!strcmp(var, "username")) {
username = val;
} else if (!strcmp(var, "extension-in-contact")) {
extension_in_contact = switch_true(val);
} else if (!strcmp(var, "auth-username")) {
auth_username = val;
} else if (!strcmp(var, "password")) {
password = val;
} else if (!strcmp(var, "caller-id-in-from")) {
caller_id_in_from = val;
} else if (!strcmp(var, "extension")) {
extension = val;
} else if (!strcmp(var, "ping")) {
ping_freq = atoi(val);
} else if (!strcmp(var, "ping-max")) {
ping_max = atoi(val);
} else if (!strcmp(var, "ping-min")) {
ping_min = atoi(val);
} else if (!strcmp(var, "ping-user-agent")) {
options_user_agent = val;
} else if (!strcmp(var, "ping-monitoring")) { // if true then every gw ping result will fire a gateway status event
ping_monitoring = switch_true(val);
} else if (!strcmp(var, "proxy")) {
proxy = val;
} else if (!strcmp(var, "context")) {
context = val;
} else if (!strcmp(var, "expire-seconds")) {
expire_seconds = val;
} else if (!strcmp(var, "908-retry-seconds")) {
fail_908_retry_seconds = val;
} else if (!strcmp(var, "retry-seconds")) {
retry_seconds = val;
} else if (!strcmp(var, "timeout-seconds")) {
timeout_seconds = val;
} else if (!strcmp(var, "retry_seconds")) { // support typo for back compat
retry_seconds = val;
} else if (!strcmp(var, "from-user")) {
from_user = val;
} else if (!strcmp(var, "from-domain")) {
from_domain = val;
} else if (!strcmp(var, "contact-host")) {
contact_host = val;
} else if (!strcmp(var, "register-proxy")) {
register_proxy = val;
} else if (!strcmp(var, "outbound-proxy")) {
outbound_proxy = val;
} else if (!strcmp(var, "distinct-to")) {
distinct_to = switch_true(val);
} else if (!strcmp(var, "destination-prefix")) {
if (!zstr(val)) {
gateway->destination_prefix = switch_core_strdup(gateway->pool, val);
}
} else if (!strcmp(var, "rfc-5626")) {
rfc_5626 = switch_true(val);
} else if (!strcmp(var, "reg-id")) {
reg_id = val;
} else if (!strcmp(var, "contact-params")) {
contact_params = val;
} else if (!strcmp(var, "register-transport")) {
sofia_transport_t transport = sofia_glue_str2transport(val);
if (transport == SOFIA_TRANSPORT_UNKNOWN || (!sofia_test_pflag(profile, PFLAG_TLS) && sofia_glue_transport_has_tls(transport))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR: unsupported transport\n");
goto skip;
}
gateway->register_transport = transport;
}
}
/* RFC 5626 enable in the GW profile and the UA profile */
if (rfc_5626 && sofia_test_pflag(profile, PFLAG_ENABLE_RFC5626)) {
char str_guid[su_guid_strlen + 1];
su_guid_t guid[1];
su_guid_generate(guid);
su_guid_sprintf(str_guid, su_guid_strlen + 1, guid);
str_rfc_5626 = switch_core_sprintf(gateway->pool, ";reg-id=%s;+sip.instance=\"<urn:uuid:%s>\"",reg_id,str_guid);
}
if (zstr(realm)) {
if (zstr(proxy)) {
realm = name;
} else {
realm = proxy;
}
}
if (switch_true(register_str)) {
if (zstr(username)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR: username param is REQUIRED!\n");
goto skip;
}
if (zstr(password)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR: password param is REQUIRED!\n");
goto skip;
}
} else {
if (zstr(username)) {
username = "FreeSWITCH";
}
if (zstr(password)) {
password = "";
}
}
if (zstr(from_user)) {
from_user = username;
}
if (zstr(proxy)) {
proxy = realm;
}
if (!switch_true(register_str)) {
gateway->state = REG_STATE_NOREG;
gateway->status = SOFIA_GATEWAY_UP;
gateway->uptime = switch_time_now();
}
if (zstr(auth_username)) {
auth_username = username;
}
if (!zstr(register_proxy)) {
if (strncasecmp(register_proxy, "sip:", 4) && strncasecmp(register_proxy, "sips:", 5)) {
gateway->register_sticky_proxy = switch_core_sprintf(gateway->pool, "sip:%s", register_proxy);
} else {
gateway->register_sticky_proxy = switch_core_strdup(gateway->pool, register_proxy);
}
}
if (!zstr(outbound_proxy)) {
if (strncasecmp(outbound_proxy, "sip:", 4) && strncasecmp(outbound_proxy, "sips:", 5)) {
gateway->outbound_sticky_proxy = switch_core_sprintf(gateway->pool, "sip:%s", outbound_proxy);
} else {
gateway->outbound_sticky_proxy = switch_core_strdup(gateway->pool, outbound_proxy);
}
}
gateway->retry_seconds = atoi(retry_seconds);
if (fail_908_retry_seconds) {
gateway->fail_908_retry_seconds = atoi(fail_908_retry_seconds);
}
if (gateway->retry_seconds < 5) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid retry-seconds of %d on gateway %s, using the value of 30 instead.\n",
gateway->retry_seconds, name);
gateway->retry_seconds = 30;
}
gateway->reg_timeout_seconds = atoi(timeout_seconds);
if (gateway->reg_timeout_seconds < 5) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid timeout-seconds of %d on gateway %s, using the value of 60 instead.\n",
gateway->reg_timeout_seconds, name);
gateway->reg_timeout_seconds = 60;
}
gateway->register_scheme = switch_core_strdup(gateway->pool, scheme);
gateway->register_context = switch_core_strdup(gateway->pool, context);
gateway->register_realm = switch_core_strdup(gateway->pool, realm);
gateway->register_username = switch_core_strdup(gateway->pool, username);
gateway->auth_username = switch_core_strdup(gateway->pool, auth_username);
gateway->register_password = switch_core_strdup(gateway->pool, password);
gateway->distinct_to = distinct_to;
gateway->options_user_agent = options_user_agent;
if (switch_true(caller_id_in_from)) {
sofia_set_flag(gateway, REG_FLAG_CALLERID);
}
register_transport = (char *) sofia_glue_transport2str(gateway->register_transport);
if (! zstr(contact_params)) {
if (*contact_params == ';') {
params = switch_core_sprintf(gateway->pool, "%s;transport=%s;gw=%s", contact_params, register_transport, gateway->name);
} else {
params = switch_core_sprintf(gateway->pool, ";%s;transport=%s;gw=%s", contact_params, register_transport, gateway->name);
}
} else {
params = switch_core_sprintf(gateway->pool, ";transport=%s;gw=%s", register_transport, gateway->name);
}
if (!zstr(from_domain)) {
gateway->from_domain = switch_core_strdup(gateway->pool, from_domain);
}
if (!zstr(register_transport) && !switch_stristr("transport=", proxy)) {
gateway->register_url = switch_core_sprintf(gateway->pool, "sip:%s;transport=%s", proxy, register_transport);
} else {
gateway->register_url = switch_core_sprintf(gateway->pool, "sip:%s", proxy);
}
gateway->register_from = switch_core_sprintf(gateway->pool, "<sip:%s@%s>",
from_user, !zstr(from_domain) ? from_domain : proxy);
if (ping_freq) {
if (ping_freq >= 5) {
gateway->ping_freq = ping_freq;
gateway->ping_max = ping_max;
gateway->ping_min = ping_min;
gateway->ping_monitoring = ping_monitoring;
gateway->ping = switch_epoch_time_now(NULL) + ping_freq;
gateway->options_to_uri = switch_core_sprintf(gateway->pool, "<sip:%s>",
!zstr(from_domain) ? from_domain : proxy);
gateway->options_from_uri = gateway->options_to_uri;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR: invalid ping!\n");
}
}
if (contact_host) {
if (!strcmp(contact_host, "sip-ip")) {
sipip = profile->sipip;
} else {
sipip = contact_host;
}
} else if (profile->extsipip) {
sipip = profile->extsipip;
} else {
sipip = profile->sipip;
}
if (zstr(extension)) {
extension = username;
} else {
gateway->real_extension = switch_core_strdup(gateway->pool, extension);
}
gateway->extension = switch_core_strdup(gateway->pool, extension);
if (!strncasecmp(proxy, "sip:", 4)) {
gateway->register_proxy = switch_core_strdup(gateway->pool, proxy);
gateway->register_to = switch_core_sprintf(gateway->pool, "sip:%s@%s", username, proxy + 4);
} else {
gateway->register_proxy = switch_core_sprintf(gateway->pool, "sip:%s", proxy);
gateway->register_to = switch_core_sprintf(gateway->pool, "sip:%s@%s", username, proxy);
}
/* This checks to make sure we provide the right contact on register for targets behind nat with us. */
if (sofia_test_pflag(profile, PFLAG_AUTO_NAT)) {
char *register_host = NULL;
register_host = sofia_glue_get_register_host(gateway->register_proxy);
if (register_host && switch_is_lan_addr(register_host)) {
sipip = profile->sipip;
}
switch_safe_free(register_host);
}
if (extension_in_contact) {
if (rfc_5626) {
format = strchr(sipip, ':') ? "<sip:%s@[%s]:%d>%s" : "<sip:%s@%s:%d%s>%s";
gateway->register_contact = switch_core_sprintf(gateway->pool, format, extension,
sipip,
sofia_glue_transport_has_tls(gateway->register_transport) ?
profile->tls_sip_port : profile->extsipport, params, str_rfc_5626);
} else {
format = strchr(sipip, ':') ? "<sip:%s@[%s]:%d%s>" : "<sip:%s@%s:%d%s>";
gateway->register_contact = switch_core_sprintf(gateway->pool, format, extension,
sipip,
sofia_glue_transport_has_tls(gateway->register_transport) ?
profile->tls_sip_port : profile->extsipport, params);
}
} else {
if (rfc_5626) {
format = strchr(sipip, ':') ? "<sip:gw+%s@[%s]:%d%s>%s" : "<sip:gw+%s@%s:%d%s>%s";
gateway->register_contact = switch_core_sprintf(gateway->pool, format, gateway->name,
sipip,
sofia_glue_transport_has_tls(gateway->register_transport) ?
profile->tls_sip_port : profile->extsipport, params, str_rfc_5626);
} else {
format = strchr(sipip, ':') ? "<sip:gw+%s@[%s]:%d%s>" : "<sip:gw+%s@%s:%d%s>";
gateway->register_contact = switch_core_sprintf(gateway->pool, format, gateway->name,
sipip,
sofia_glue_transport_has_tls(gateway->register_transport) ?
profile->tls_sip_port : profile->extsipport, params);
}
}
gateway->expires_str = switch_core_strdup(gateway->pool, expire_seconds);
if ((gateway->freq = atoi(gateway->expires_str)) < 5) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"Invalid register-frequency of %d on gateway %s, using the value of 3600 instead\n", gateway->freq, name);
gateway->freq = 3600;
}
if ((gw_subs_tag = switch_xml_child(gateway_tag, "subscriptions"))) {
parse_gateway_subscriptions(profile, gateway, gw_subs_tag);
}
sofia_reg_add_gateway(profile, gateway->name, gateway);
}
skip:
switch_assert(gateway_tag);
}
}
static void parse_domain_tag(sofia_profile_t *profile, switch_xml_t x_domain_tag, const char *dname, const char *parse, const char *alias)
{
if (switch_true(alias)) {
if (sofia_glue_add_profile(switch_core_strdup(profile->pool, dname), profile) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Alias [%s] for profile [%s]\n", dname, profile->name);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Alias [%s] for profile [%s] (already exists)\n", dname, profile->name);
}
}
if (switch_true(parse)) {
switch_xml_t gts, gt, uts, ut, gateways_tag;
/* Backwards Compatibility */
for (ut = switch_xml_child(x_domain_tag, "user"); ut; ut = ut->next) {
if (((gateways_tag = switch_xml_child(ut, "gateways")))) {
parse_gateways(profile, gateways_tag, NULL);
}
}
/* New Method with <groups> tags and users are now inside a <users> tag */
for (gts = switch_xml_child(x_domain_tag, "groups"); gts; gts = gts->next) {
for (gt = switch_xml_child(gts, "group"); gt; gt = gt->next) {
for (uts = switch_xml_child(gt, "users"); uts; uts = uts->next) {
for (ut = switch_xml_child(uts, "user"); ut; ut = ut->next) {
if (((gateways_tag = switch_xml_child(ut, "gateways")))) {
parse_gateways(profile, gateways_tag, NULL);
}
}
}
}
}
}
}
static void config_sofia_profile_urls(sofia_profile_t * profile)
{
if (profile->extsipip) {
char *ipv6 = strchr(profile->extsipip, ':');
profile->public_url = switch_core_sprintf(profile->pool,
"sip:%s@%s%s%s:%d",
profile->contact_user,
ipv6 ? "[" : "", profile->extsipip, ipv6 ? "]" : "", profile->extsipport);
}
if (profile->extsipip && !sofia_test_pflag(profile, PFLAG_AUTO_NAT)) {
char *ipv6 = strchr(profile->extsipip, ':');
profile->url = switch_core_sprintf(profile->pool,
"sip:%s@%s%s%s:%d",
profile->contact_user, ipv6 ? "[" : "", profile->extsipip, ipv6 ? "]" : "", profile->extsipport);
profile->bindurl = switch_core_sprintf(profile->pool, "%s;maddr=%s", profile->url, profile->sipip);
} else {
char *ipv6 = strchr(profile->sipip, ':');
profile->url = switch_core_sprintf(profile->pool,
"sip:%s@%s%s%s:%d",
profile->contact_user, ipv6 ? "[" : "", profile->sipip, ipv6 ? "]" : "", profile->sip_port);
profile->bindurl = profile->url;
}
profile->tcp_contact = switch_core_sprintf(profile->pool, "<%s;transport=tcp>", profile->url);
if (profile->public_url) {
profile->tcp_public_contact = switch_core_sprintf(profile->pool, "<%s;transport=tcp>", profile->public_url);
}
if (profile->bind_params) {
char *bindurl;
if (!switch_stristr("transport=", profile->bind_params)) {
profile->bind_params = switch_core_sprintf(profile->pool, "%s;transport=udp,tcp", profile->bind_params);
}
bindurl = switch_core_sprintf(profile->pool, "%s;%s", profile->bindurl, profile->bind_params);
profile->bindurl = bindurl;
} else {
char *bindurl = switch_core_sprintf(profile->pool, "%s;transport=udp,tcp", profile->bindurl);
profile->bindurl = bindurl;
}
if ( profile->ws_port ) {
char *ip = !zstr(profile->ws_ip) ? profile->ws_ip : profile->sipip;
switch_port_t port = profile->ws_port;
char *ipv6 = strchr(ip, ':');
profile->ws_bindurl =
switch_core_sprintf(profile->pool,
"sip:%s@%s%s%s:%d;transport=ws",
profile->contact_user, ipv6 ? "[" : "", ip, ipv6 ? "]" : "", port);
}
if ( profile->wss_port ) {
char *ip = !zstr(profile->wss_ip) ? profile->wss_ip : profile->sipip;
switch_port_t port = profile->wss_port;
char *ipv6 = strchr(ip, ':');
profile->wss_bindurl =
switch_core_sprintf(profile->pool,
"sips:%s@%s%s%s:%d;transport=wss",
profile->contact_user, ipv6 ? "[" : "", ip, ipv6 ? "]" : "", port);
}
/*
* handle TLS params #2
*/
if (sofia_test_pflag(profile, PFLAG_TLS)) {
if (!profile->tls_sip_port && !sofia_test_pflag(profile, PFLAG_AUTO_ASSIGN_TLS_PORT)) {
profile->tls_sip_port = (switch_port_t) atoi(SOFIA_DEFAULT_TLS_PORT);
}
if (profile->extsipip) {
char *ipv6 = strchr(profile->extsipip, ':');
profile->tls_public_url = switch_core_sprintf(profile->pool,
"sip:%s@%s%s%s:%d",
profile->contact_user,
ipv6 ? "[" : "", profile->extsipip, ipv6 ? "]" : "", profile->tls_sip_port);
}
if (profile->extsipip && !sofia_test_pflag(profile, PFLAG_AUTO_NAT)) {
char *ipv6 = strchr(profile->extsipip, ':');
profile->tls_url =
switch_core_sprintf(profile->pool,
"sip:%s@%s%s%s:%d",
profile->contact_user, ipv6 ? "[" : "", profile->extsipip, ipv6 ? "]" : "", profile->tls_sip_port);
profile->tls_bindurl =
switch_core_sprintf(profile->pool,
"sips:%s@%s%s%s:%d;maddr=%s",
profile->contact_user,
ipv6 ? "[" : "", profile->extsipip, ipv6 ? "]" : "", profile->tls_sip_port, profile->sipip);
} else {
char *ipv6 = strchr(profile->sipip, ':');
profile->tls_url =
switch_core_sprintf(profile->pool,
"sip:%s@%s%s%s:%d",
profile->contact_user, ipv6 ? "[" : "", profile->sipip, ipv6 ? "]" : "", profile->tls_sip_port);
profile->tls_bindurl =
switch_core_sprintf(profile->pool,
"sips:%s@%s%s%s:%d",
profile->contact_user, ipv6 ? "[" : "", profile->sipip, ipv6 ? "]" : "", profile->tls_sip_port);
}
if (profile->tls_bind_params) {
char *tls_bindurl = profile->tls_bindurl;
profile->tls_bindurl = switch_core_sprintf(profile->pool, "%s;%s", tls_bindurl, profile->tls_bind_params);
}
profile->tls_contact = switch_core_sprintf(profile->pool, "<%s;transport=tls>", profile->tls_url);
if (profile->tls_public_url) {
profile->tls_public_contact = switch_core_sprintf(profile->pool, "<%s;transport=tls>", profile->tls_public_url);
}
}
}
#ifdef SOFIA_CUSTOM_TIME
/* appears to not be granular enough */
static void sofia_time(su_time_t *tv)
{
switch_time_t now;
if (tv) {
now = switch_micro_time_now();
tv->tv_sec = ((uint32_t) (now / 1000000)) + 2208988800UL;
tv->tv_usec = (uint32_t) (now % 1000000);
}
}
#endif
switch_status_t sofia_init(void)
{
su_init();
if (sip_update_default_mclass(sip_extend_mclass(NULL)) < 0) {
su_deinit();
sip_cloned_parser_destroy();
return SWITCH_STATUS_GENERR;
}
#ifdef SOFIA_TIME
su_set_time_func(sofia_time);
#endif
/* Redirect loggers in sofia */
su_log_redirect(su_log_default, logger, NULL);
su_log_redirect(tport_log, logger, NULL);
su_log_redirect(iptsec_log, logger, NULL);
su_log_redirect(nea_log, logger, NULL);
su_log_redirect(nta_log, logger, NULL);
su_log_redirect(nth_client_log, logger, NULL);
su_log_redirect(nth_server_log, logger, NULL);
su_log_redirect(nua_log, logger, NULL);
su_log_redirect(soa_log, logger, NULL);
su_log_redirect(sresolv_log, logger, NULL);
#ifdef HAVE_SOFIA_STUN
su_log_redirect(stun_log, logger, NULL);
#endif
return SWITCH_STATUS_SUCCESS;
}
switch_status_t config_gateway(const char *profile_name, const char *gateway_name)
{
switch_xml_t cfg, xml = NULL, xprofiles, xprofile, gateways_tag;
switch_status_t status = SWITCH_STATUS_SUCCESS;
sofia_profile_t *profile = NULL;
switch_event_t *params = NULL;
const char *cf = "sofia.conf";
if (zstr(profile_name) || !(profile = sofia_glue_find_profile(profile_name))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Profile [%s] does not exist.\n", profile_name);
status = SWITCH_STATUS_FALSE;
return status;
}
switch_event_create(&params, SWITCH_EVENT_REQUEST_PARAMS);
switch_assert(params);
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "profile", profile_name);
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "reconfig", "true");
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "gateway", gateway_name);
if (!(xml = switch_xml_open_cfg(cf, &cfg, params))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", gateway_name);
status = SWITCH_STATUS_FALSE;
goto done;
}
if ((xprofiles = switch_xml_child(cfg, "profiles"))) {
if ((xprofile = switch_xml_find_child(xprofiles, "profile", "name", profile->name))) {
if ((gateways_tag = switch_xml_child(xprofile, "gateways"))) {
parse_gateways(profile, gateways_tag, strcmp(gateway_name, "_all_") ? gateway_name : NULL);
}
}
}
status = SWITCH_STATUS_SUCCESS;
done:
sofia_glue_release_profile(profile);
if (xml) switch_xml_free(xml);
switch_event_destroy(&params);
return status;
}
switch_status_t config_sofia(sofia_config_t reload, char *profile_name)
{
char *cf = "sofia.conf";
switch_xml_t cfg, xml = NULL, xprofile, param, settings, profiles;
switch_status_t status = SWITCH_STATUS_SUCCESS;
sofia_profile_t *profile = NULL;
char url[512] = "";
int profile_found = 0;
switch_event_t *params = NULL;
sofia_profile_t *profile_already_started = NULL;
if (!zstr(profile_name) && (profile = sofia_glue_find_profile(profile_name))) {
if (reload == SOFIA_CONFIG_RESCAN) {
profile_already_started = profile;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Profile [%s] Already exists.\n", switch_str_nil(profile_name));
status = SWITCH_STATUS_FALSE;
sofia_glue_release_profile(profile);
return status;
}
}
switch_event_create(&params, SWITCH_EVENT_REQUEST_PARAMS);
switch_assert(params);
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "profile", profile_name);
if (reload == SOFIA_CONFIG_RESCAN) {
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "reconfig", "true");
}
if (!(xml = switch_xml_open_cfg(cf, &cfg, params))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
status = SWITCH_STATUS_FALSE;
goto done;
}
mod_sofia_globals.auto_restart = SWITCH_TRUE;
mod_sofia_globals.reg_deny_binding_fetch_and_no_lookup = SWITCH_FALSE; /* handle backwards compatilibity - by default use new behavior */
mod_sofia_globals.rewrite_multicasted_fs_path = SWITCH_FALSE;
if ((settings = switch_xml_child(cfg, "global_settings"))) {
for (param = switch_xml_child(settings, "param"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
char *val = (char *) switch_xml_attr_soft(param, "value");
if (!strcasecmp(var, "log-level")) {
su_log_set_level(NULL, atoi(val));
} else if (!strcasecmp(var, "tracelevel")) {
mod_sofia_globals.tracelevel = switch_log_str2level(val);
} else if (!strcasecmp(var, "debug-presence")) {
mod_sofia_globals.debug_presence = atoi(val);
} else if (!strcasecmp(var, "debug-sla")) {
mod_sofia_globals.debug_sla = atoi(val);
} else if (!strcasecmp(var, "max-reg-threads") && val) {
int x = atoi(val);
if (x > 0) {
mod_sofia_globals.max_reg_threads = x;
}
} else if (!strcasecmp(var, "auto-restart")) {
mod_sofia_globals.auto_restart = switch_true(val);
} else if (!strcasecmp(var, "reg-deny-binding-fetch-and-no-lookup")) { /* backwards compatibility */
mod_sofia_globals.reg_deny_binding_fetch_and_no_lookup = switch_true(val); /* remove when noone complains about the extra lookup */
if (switch_true(val)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Enabling reg-deny-binding-fetch-and-no-lookup - this functionality is "
"deprecated and will be removed - let FS devs know if you think it should stay\n");
}
} else if (!strcasecmp(var, "rewrite-multicasted-fs-path")) {
if( (!strcasecmp(val, "to_host")) || (!strcasecmp(val, "1")) ) {
/* old behaviour */
mod_sofia_globals.rewrite_multicasted_fs_path = 1;
} else if (!strcasecmp(val, "original_server_host")) {
mod_sofia_globals.rewrite_multicasted_fs_path = 2;
} else if (!strcasecmp(val, "original_hostname")) {
mod_sofia_globals.rewrite_multicasted_fs_path = 3;
} else {
mod_sofia_globals.rewrite_multicasted_fs_path = SWITCH_FALSE;
}
} else if (!strcasecmp(var, "capture-server")) {
mod_sofia_globals.capture_server = switch_core_strdup(mod_sofia_globals.pool, val);
}
}
}
if ((profiles = switch_xml_child(cfg, "profiles"))) {
for (xprofile = switch_xml_child(profiles, "profile"); xprofile; xprofile = xprofile->next) {
char *xprofilename = (char *) switch_xml_attr_soft(xprofile, "name");
char *xprofiledomain = (char *) switch_xml_attr(xprofile, "domain");
if (!(settings = switch_xml_child(xprofile, "settings"))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Settings, check the new config!\n");
sofia_profile_start_failure(NULL, xprofilename);
} else {
switch_memory_pool_t *pool = NULL;
if (!xprofilename) {
xprofilename = "unnamed";
}
if (profile_name) {
if (strcasecmp(profile_name, xprofilename)) {
continue;
} else {
profile_found = 1;
}
}
if (!profile_already_started) {
/* Setup the pool */
if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
sofia_profile_start_failure(NULL, xprofilename);
goto done;
}
if (!(profile = (sofia_profile_t *) switch_core_alloc(pool, sizeof(*profile)))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n");
sofia_profile_start_failure(NULL, xprofilename);
goto done;
}
profile->tls_verify_policy = TPTLS_VERIFY_NONE;
sofia_set_pflag(profile, PFLAG_AUTO_INVITE_100);
/* lib default */
profile->tls_verify_depth = 2;
switch_mutex_init(&profile->gw_mutex, SWITCH_MUTEX_NESTED, pool);
profile->trans_timeout = 100;
profile->auto_rtp_bugs = RTP_BUG_CISCO_SKIP_MARK_BIT_2833;// | RTP_BUG_SONUS_SEND_INVALID_TIMESTAMP_2833;
profile->pool = pool;
profile->user_agent = switch_core_sprintf(profile->pool, "FreeSWITCH-mod_sofia/%s", switch_version_full());
profile->sip_user_ping_max = 3;
profile->sip_user_ping_min = 1;
profile->name = switch_core_strdup(profile->pool, xprofilename);
switch_snprintf(url, sizeof(url), "sofia_reg_%s", xprofilename);
if (xprofiledomain) {
profile->domain_name = switch_core_strdup(profile->pool, xprofiledomain);
}
profile->dbname = switch_core_strdup(profile->pool, url);
switch_core_hash_init(&profile->chat_hash);
switch_core_hash_init(&profile->reg_nh_hash);
switch_core_hash_init(&profile->mwi_debounce_hash);
switch_thread_rwlock_create(&profile->rwlock, profile->pool);
switch_mutex_init(&profile->flag_mutex, SWITCH_MUTEX_NESTED, profile->pool);
profile->dtmf_duration = 100;
profile->rtp_digit_delay = 40;
profile->sip_force_expires = 0;
profile->sip_force_expires_min = 0;
profile->sip_force_expires_max = 0;
profile->sip_expires_max_deviation = 0;
profile->sip_expires_late_margin = 60;
profile->sip_subscription_max_deviation = 0;
profile->tls_ciphers = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH";
profile->tls_version = SOFIA_TLS_VERSION_TLSv1;
profile->tls_version |= SOFIA_TLS_VERSION_TLSv1_1;
profile->tls_version |= SOFIA_TLS_VERSION_TLSv1_2;
profile->tls_timeout = 300;
profile->mflags = MFLAG_REFER | MFLAG_REGISTER;
profile->server_rport_level = 1;
profile->client_rport_level = 1;
profile->tls_cert_dir = SWITCH_GLOBAL_dirs.certs_dir;
sofia_set_pflag(profile, PFLAG_DISABLE_100REL);
sofia_set_pflag(profile, PFLAG_ENABLE_CHAT);
profile->auto_restart = 1;
sofia_set_media_flag(profile, SCMF_AUTOFIX_TIMING);
sofia_set_media_flag(profile, SCMF_RTP_AUTOFLUSH_DURING_BRIDGE);
profile->contact_user = SOFIA_DEFAULT_CONTACT_USER;
sofia_set_pflag(profile, PFLAG_PASS_CALLEE_ID);
sofia_set_pflag(profile, PFLAG_ALLOW_UPDATE);
sofia_set_pflag(profile, PFLAG_SEND_DISPLAY_UPDATE);
sofia_set_pflag(profile, PFLAG_MESSAGE_QUERY_ON_FIRST_REGISTER);
//sofia_set_pflag(profile, PFLAG_PRESENCE_ON_FIRST_REGISTER);
sofia_clear_pflag(profile, PFLAG_CHANNEL_XML_FETCH_ON_NIGHTMARE_TRANSFER);
sofia_clear_pflag(profile, PFLAG_MAKE_EVERY_TRANSFER_A_NIGHTMARE);
sofia_clear_pflag(profile, PFLAG_FIRE_TRANFER_EVENTS);
sofia_clear_pflag(profile, PFLAG_BLIND_AUTH_ENFORCE_RESULT);
sofia_clear_pflag(profile, PFLAG_BLIND_AUTH_REPLY_403);
sofia_clear_pflag(profile, PFLAG_AUTH_REQUIRE_USER);
sofia_clear_pflag(profile, PFLAG_AUTH_CALLS_ACL_ONLY);
sofia_clear_pflag(profile, PFLAG_USE_PORT_FOR_ACL_CHECK);
profile->shutdown_type = "false";
profile->local_network = "localnet.auto";
sofia_set_flag(profile, TFLAG_ENABLE_SOA);
sofia_set_pflag(profile, PFLAG_CID_IN_1XX);
profile->mndlb |= SM_NDLB_ALLOW_NONDUP_SDP;
profile->te = 101;
profile->ireg_seconds = IREG_SECONDS;
profile->iping_seconds = IPING_SECONDS;
profile->iping_freq = IPING_FREQUENCY;
profile->paid_type = PAID_DEFAULT;
profile->bind_attempts = 2;
profile->bind_attempt_interval = 5;
profile->dtmf_type = DTMF_2833;
profile->tls_verify_policy = TPTLS_VERIFY_NONE;
/* lib default */
profile->tls_verify_depth = 2;
profile->tls_verify_date = SWITCH_TRUE;
} else {
/* you could change profile->foo here if it was a minor change like context or dialplan ... */
profile->acl_count = 0;
profile->nat_acl_count = 0;
profile->reg_acl_count = 0;
profile->proxy_acl_count = 0;
sofia_set_pflag(profile, PFLAG_PASS_CALLEE_ID);
profile->ib_calls = 0;
profile->ob_calls = 0;
profile->ib_failed_calls = 0;
profile->ob_failed_calls = 0;
profile->shutdown_type = "false";
profile->rtpip_index = 0;
profile->rtpip_index6 = 0;
if (xprofiledomain) {
profile->domain_name = switch_core_strdup(profile->pool, xprofiledomain);
}
}
for (param = switch_xml_child(settings, "param"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
char *val = (char *) switch_xml_attr_soft(param, "value");
int found = 1; // Used to break up long if/elseif chain (MSVC2015 fails (parser stack overflow) otherwise)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s [%s]\n", var, val);
if (!strcasecmp(var, "debug") && val) {
profile->debug = atoi(val);
} else if (!strcasecmp(var, "parse-invite-tel-params")) {
profile->parse_invite_tel_params = switch_true(val);
} else if (!strcasecmp(var, "keepalive-method") && !zstr(val)) {
if (!strcasecmp(val, "info")) {
profile->keepalive = KA_INFO;
} else {
profile->keepalive = KA_MESSAGE;
}
} else if (!strcasecmp(var, "bind-attempts") && val) {
int ba = atoi(val) - 1;
if (ba >= 0) {
profile->bind_attempts = ba;
}
} else if (!strcasecmp(var, "bind-attempt-interval") && val) {
int bai = atoi(val);
if (bai >= 0) {
profile->bind_attempt_interval = bai;
}
} else if (!strcasecmp(var, "shutdown-on-fail")) {
profile->shutdown_type = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "sip-trace")) {
if (switch_true(val)) {
sofia_set_flag(profile, TFLAG_TPORT_LOG);
} else {
sofia_clear_flag(profile, TFLAG_TPORT_LOG);
}
} else if (!strcasecmp(var, "sip-capture")) {
if (switch_true(val)) {
sofia_set_flag(profile, TFLAG_CAPTURE);
nua_set_params(profile->nua, TPTAG_CAPT(mod_sofia_globals.capture_server), TAG_END());
} else {
sofia_clear_flag(profile, TFLAG_CAPTURE);
}
} else if (!strcasecmp(var, "socket-tcp-keepalive") && !zstr(val)) {
profile->socket_tcp_keepalive = atoi(val);
sofia_set_pflag(profile, PFLAG_SOCKET_TCP_KEEPALIVE);
} else if (!strcasecmp(var, "tcp-keepalive") && !zstr(val)) {
profile->tcp_keepalive = atoi(val);
sofia_set_pflag(profile, PFLAG_TCP_KEEPALIVE);
} else if (!strcasecmp(var, "tcp-pingpong") && !zstr(val)) {
profile->tcp_pingpong = atoi(val);
sofia_set_pflag(profile, PFLAG_TCP_PINGPONG);
} else if (!strcasecmp(var, "tcp-ping2pong") && !zstr(val)) {
profile->tcp_ping2pong = atoi(val);
sofia_set_pflag(profile, PFLAG_TCP_PING2PONG);
} else if ((!strcasecmp(var, "proxy-refer-replaces") || !strcasecmp(var, "proxy-refer")) && !zstr(val)) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_PROXY_REFER);
} else {
sofia_clear_pflag(profile, PFLAG_PROXY_REFER);
}
} else if (!strcasecmp(var, "sip-messages-respond-200-ok") && !zstr(val)) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_MESSAGES_RESPOND_200_OK);
} else {
sofia_clear_pflag(profile, PFLAG_MESSAGES_RESPOND_200_OK);
}
} else if (!strcasecmp(var, "sip-subscribe-respond-200-ok") && !zstr(val)) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_SUBSCRIBE_RESPOND_200_OK);
} else {
sofia_clear_pflag(profile, PFLAG_SUBSCRIBE_RESPOND_200_OK);
}
} else if (!strcasecmp(var, "odbc-dsn") && !zstr(val)) {
profile->odbc_dsn = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "db-pre-trans-execute") && !zstr(val)) {
profile->pre_trans_execute = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "db-post-trans-execute") && !zstr(val)) {
profile->post_trans_execute = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "db-inner-pre-trans-execute") && !zstr(val)) {
profile->inner_pre_trans_execute = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "db-inner-post-trans-execute") && !zstr(val)) {
profile->inner_post_trans_execute = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "forward-unsolicited-mwi-notify")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_FORWARD_MWI_NOTIFY);
} else {
sofia_clear_pflag(profile, PFLAG_FORWARD_MWI_NOTIFY);
}
} else if (!strcasecmp(var, "NDLB-proxy-never-patch-reinvites")) {
if (switch_true(val)) {
profile->mndlb |= SM_NDLB_NEVER_PATCH_REINVITE;
} else {
profile->mndlb &= ~SM_NDLB_NEVER_PATCH_REINVITE;
}
} else if (!strcasecmp(var, "registration-thread-frequency") && !zstr(val)) {
profile->ireg_seconds = atoi(val);
if (profile->ireg_seconds < 0) {
profile->ireg_seconds = IREG_SECONDS;
}
} else if (!strcasecmp(var, "ping-mean-interval") && !zstr(val)) {
profile->iping_seconds = atoi(val);
if (profile->iping_seconds < 0) {
profile->iping_seconds = IPING_SECONDS;
}
} else if (!strcasecmp(var, "ping-thread-frequency") && !zstr(val)) {
profile->iping_freq = atoi(val);
if (profile->iping_freq < 0) {
profile->iping_freq = IPING_FREQUENCY;
}
} else if (!strcasecmp(var, "user-agent-string")) {
profile->user_agent = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "auto-restart")) {
profile->auto_restart = switch_true(val);
} else if (!strcasecmp(var, "log-auth-failures")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_LOG_AUTH_FAIL);
} else {
sofia_clear_pflag(profile, PFLAG_LOG_AUTH_FAIL);
}
} else if (!strcasecmp(var, "confirm-blind-transfer")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_CONFIRM_BLIND_TRANSFER);
} else {
sofia_clear_pflag(profile, PFLAG_CONFIRM_BLIND_TRANSFER);
}
} else if (!strcasecmp(var, "allow-update")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_ALLOW_UPDATE);
} else {
sofia_clear_pflag(profile, PFLAG_ALLOW_UPDATE);
}
} else if (!strcasecmp(var, "send-display-update")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_SEND_DISPLAY_UPDATE);
} else {
sofia_clear_pflag(profile, PFLAG_SEND_DISPLAY_UPDATE);
}
} else if (!strcasecmp(var, "mwi-use-reg-callid")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_MWI_USE_REG_CALLID);
} else {
sofia_clear_pflag(profile, PFLAG_MWI_USE_REG_CALLID);
}
} else if (!strcasecmp(var, "tcp-unreg-on-socket-close")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_TCP_UNREG_ON_SOCKET_CLOSE);
} else {
sofia_clear_pflag(profile, PFLAG_TCP_UNREG_ON_SOCKET_CLOSE);
}
} else if (!strcasecmp(var, "tcp-always-nat")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_TCP_ALWAYS_NAT);
} else {
sofia_clear_pflag(profile, PFLAG_TCP_ALWAYS_NAT);
}
} else if (!strcasecmp(var, "tls-always-nat")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_TLS_ALWAYS_NAT);
} else {
sofia_clear_pflag(profile, PFLAG_TLS_ALWAYS_NAT);
}
} else if (!strcasecmp(var, "presence-proto-lookup")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_PRESENCE_MAP);
} else {
sofia_clear_pflag(profile, PFLAG_PRESENCE_MAP);
}
} else if (!strcasecmp(var, "profile-standby")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_STANDBY);
} else {
sofia_clear_pflag(profile, PFLAG_STANDBY);
}
} else if (!strcasecmp(var, "liberal-dtmf")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_LIBERAL_DTMF);
} else {
sofia_clear_pflag(profile, PFLAG_LIBERAL_DTMF);
}
} else if (!strcasecmp(var, "rtp-digit-delay") && !zstr(val)) {
int delay = atoi(val);
if (delay < 0) {
delay = 0;
}
profile->rtp_digit_delay = (uint32_t) delay;
} else if (!strcasecmp(var, "watchdog-enabled")) {
profile->watchdog_enabled = switch_true(val);
} else if (!strcasecmp(var, "watchdog-step-timeout") && !zstr(val)) {
profile->step_timeout = atoi(val);
} else if (!strcasecmp(var, "watchdog-event-timeout") && !zstr(val)) {
profile->event_timeout = atoi(val);
} else if (!strcasecmp(var, "in-dialog-chat")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_IN_DIALOG_CHAT);
} else {
sofia_clear_pflag(profile, PFLAG_IN_DIALOG_CHAT);
}
} else if (!strcasecmp(var, "enable-chat")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_ENABLE_CHAT);
} else {
sofia_clear_pflag(profile, PFLAG_ENABLE_CHAT);
}
} else if (!strcasecmp(var, "fire-bye-response-events")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_FIRE_BYE_RESPONSE_EVENTS);
} else {
sofia_clear_pflag(profile, PFLAG_FIRE_BYE_RESPONSE_EVENTS);
}
} else if (!strcasecmp(var, "fire-message-events")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_FIRE_MESSAGE_EVENTS);
} else {
sofia_clear_pflag(profile, PFLAG_FIRE_MESSAGE_EVENTS);
}
} else if (!strcasecmp(var, "t38-passthru")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_T38_PASSTHRU);
} else {
sofia_clear_pflag(profile, PFLAG_T38_PASSTHRU);
}
} else if (!strcasecmp(var, "presence-disable-early")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_PRESENCE_DISABLE_EARLY);
} else {
sofia_clear_pflag(profile, PFLAG_PRESENCE_DISABLE_EARLY);
}
} else if (!strcasecmp(var, "ignore-183nosdp")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_IGNORE_183NOSDP);
} else {
sofia_clear_pflag(profile, PFLAG_IGNORE_183NOSDP);
}
} else if (!strcasecmp(var, "presence-probe-on-register")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_PRESENCE_PROBE_ON_REGISTER);
} else {
sofia_clear_pflag(profile, PFLAG_PRESENCE_PROBE_ON_REGISTER);
}
} else if (!strcasecmp(var, "send-presence-on-register")) {
if (val && (switch_true(val) || !strcasecmp(val, "all"))) {
sofia_set_pflag(profile, PFLAG_PRESENCE_ON_REGISTER);
} else if (val && !strcasecmp(val, "first-only")) {
sofia_clear_pflag(profile, PFLAG_PRESENCE_ON_REGISTER);
sofia_set_pflag(profile, PFLAG_PRESENCE_ON_FIRST_REGISTER);
} else {
sofia_clear_pflag(profile, PFLAG_PRESENCE_ON_REGISTER);
sofia_clear_pflag(profile, PFLAG_PRESENCE_ON_FIRST_REGISTER);
}
} else if (!strcasecmp(var, "cid-in-1xx")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_CID_IN_1XX);
} else {
sofia_clear_pflag(profile, PFLAG_CID_IN_1XX);
}
} else if (!strcasecmp(var, "disable-hold")) {
if (switch_true(val)) {
sofia_set_media_flag(profile, SCMF_DISABLE_HOLD);
} else {
sofia_clear_media_flag(profile, SCMF_DISABLE_HOLD);
}
} else if (!strcasecmp(var, "auto-jitterbuffer-msec") && !zstr(val)) {
int msec = atoi(val);
if (msec > 19) {
profile->jb_msec = switch_core_strdup(profile->pool, val);
}
} else if (!strcasecmp(var, "dtmf-type")) {
if (val && !strcasecmp(val, "rfc2833")) {
profile->dtmf_type = DTMF_2833;
} else if (val && !strcasecmp(val, "info")) {
profile->dtmf_type = DTMF_INFO;
} else {
profile->dtmf_type = DTMF_NONE;
}
} else if (!strcasecmp(var, "NDLB-force-rport")) {
if (val && !strcasecmp(val, "safe")) {
profile->server_rport_level = 3;
profile->client_rport_level = 1;
} else if (val && !strcasecmp(val, "disabled")) {
profile->server_rport_level = 0;
profile->client_rport_level = 0;
} else if (val && !strcasecmp(val, "client-only")) {
profile->client_rport_level = 1;
} else if (val && !strcasecmp(val, "server-only")) {
profile->client_rport_level = 0;
profile->server_rport_level = 1;
} else if (switch_true(val)) {
profile->server_rport_level = 2;
profile->client_rport_level = 1;
}
} else if (!strcasecmp(var, "auto-rtp-bugs")) {
switch_core_media_parse_rtp_bugs(&profile->auto_rtp_bugs, val);
} else if (!strcasecmp(var, "manual-rtp-bugs")) {
switch_core_media_parse_rtp_bugs(&profile->manual_rtp_bugs, val);
} else if (!strcasecmp(var, "manual-video-rtp-bugs")) {
switch_core_media_parse_rtp_bugs(&profile->manual_video_rtp_bugs, val);
} else if (!strcasecmp(var, "dbname")) {
profile->dbname = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "presence-hosts")) {
profile->presence_hosts = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "caller-id-type")) {
profile->cid_type = sofia_cid_name2type(val);
} else if (!strcasecmp(var, "record-template")) {
profile->record_template = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "record-path")) {
profile->record_path = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "inbound-no-media") || !strcasecmp(var, "inbound-bypass-media")) {
if (switch_true(val)) {
sofia_set_flag(profile, TFLAG_INB_NOMEDIA);
} else {
sofia_clear_flag(profile, TFLAG_INB_NOMEDIA);
}
} else if (!strcasecmp(var, "inbound-late-negotiation")) {
if (switch_true(val)) {
sofia_set_flag(profile, TFLAG_LATE_NEGOTIATION);
} else {
sofia_clear_flag(profile, TFLAG_LATE_NEGOTIATION);
}
} else if (!strcasecmp(var, "rtp-notimer-during-bridge")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_RTP_NOTIMER_DURING_BRIDGE);
} else {
sofia_clear_pflag(profile, PFLAG_RTP_NOTIMER_DURING_BRIDGE);
}
} else if (!strcasecmp(var, "manual-redirect")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_MANUAL_REDIRECT);
} else {
sofia_clear_pflag(profile, PFLAG_MANUAL_REDIRECT);
}
} else if (!strcasecmp(var, "inbound-proxy-media")) {
if (switch_true(val)) {
sofia_set_flag(profile, TFLAG_PROXY_MEDIA);
} else {
sofia_clear_flag(profile, TFLAG_PROXY_MEDIA);
}
} else if (!strcasecmp(var, "inbound-zrtp-passthru")) {
if (switch_true(val)) {
sofia_set_flag(profile, TFLAG_ZRTP_PASSTHRU);
} else {
sofia_clear_flag(profile, TFLAG_ZRTP_PASSTHRU);
}
} else if (!strcasecmp(var, "force-subscription-expires") && !zstr(val)) {
int tmp = atoi(val);
if (tmp > 0) {
profile->force_subscription_expires = tmp;
}
} else if (!strcasecmp(var, "force-publish-expires") && !zstr(val)) {
int tmp = atoi(val);
if (tmp > 0) {
profile->force_publish_expires = tmp;
}
} else if (!strcasecmp(var, "send-message-query-on-register")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_MESSAGE_QUERY_ON_REGISTER);
} else if (val && !strcasecmp(val, "first-only")) {
sofia_clear_pflag(profile, PFLAG_MESSAGE_QUERY_ON_REGISTER);
sofia_set_pflag(profile, PFLAG_MESSAGE_QUERY_ON_FIRST_REGISTER);
} else {
sofia_clear_pflag(profile, PFLAG_MESSAGE_QUERY_ON_REGISTER);
sofia_clear_pflag(profile, PFLAG_MESSAGE_QUERY_ON_FIRST_REGISTER);
}
} else if (!strcasecmp(var, "inbound-reg-in-new-thread") && val) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_THREAD_PER_REG);
} else {
sofia_clear_pflag(profile, PFLAG_THREAD_PER_REG);
}
} else if (!strcasecmp(var, "inbound-use-callid-as-uuid")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_CALLID_AS_UUID);
} else {
sofia_clear_pflag(profile, PFLAG_CALLID_AS_UUID);
}
} else if (!strcasecmp(var, "outbound-use-uuid-as-callid")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_UUID_AS_CALLID);
} else {
sofia_clear_pflag(profile, PFLAG_UUID_AS_CALLID);
}
} else if (!strcasecmp(var, "track-calls")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_TRACK_CALLS);
}
} else if (!strcasecmp(var, "NDLB-received-in-nat-reg-contact")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_RECIEVED_IN_NAT_REG_CONTACT);
} else {
sofia_clear_pflag(profile, PFLAG_RECIEVED_IN_NAT_REG_CONTACT);
}
} else if (!strcasecmp(var, "aggressive-nat-detection")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_AGGRESSIVE_NAT_DETECTION);
} else {
sofia_clear_pflag(profile, PFLAG_AGGRESSIVE_NAT_DETECTION);
}
} else if (!strcasecmp(var, "disable-rtp-auto-adjust")) {
if (switch_true(val)) {
sofia_set_media_flag(profile, SCMF_DISABLE_RTP_AUTOADJ);
} else {
sofia_clear_media_flag(profile, SCMF_DISABLE_RTP_AUTOADJ);
}
} else if (!strcasecmp(var, "NDLB-support-asterisk-missing-srtp-auth")) {
if (switch_true(val)) {
profile->mndlb |= SM_NDLB_DISABLE_SRTP_AUTH;
} else {
profile->mndlb &= ~SM_NDLB_DISABLE_SRTP_AUTH;
}
} else if (!strcasecmp(var, "user-agent-filter")) {
profile->user_agent_filter = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "max-registrations-per-extension") && !zstr(val)) {
profile->max_registrations_perext = atoi(val);
} else if (!strcasecmp(var, "rfc2833-pt") && !zstr(val)) {
profile->te = (switch_payload_t) atoi(val);
} else if (!strcasecmp(var, "cng-pt") && !sofia_test_media_flag(profile, SCMF_SUPPRESS_CNG) && !zstr(val)) {
profile->cng_pt = (switch_payload_t) atoi(val);
} else if (!strcasecmp(var, "sip-port") && !zstr(val)) {
if (!strcasecmp(val, "auto")) {
sofia_set_pflag(profile, PFLAG_AUTO_ASSIGN_PORT);
} else {
profile->sip_port = (switch_port_t) atoi(val);
if (!profile->extsipport) profile->extsipport = profile->sip_port;
}
} else if (!strcasecmp(var, "vad") && !zstr(val)) {
if (!strcasecmp(val, "in")) {
profile->vflags |= VAD_IN;
} else if (!strcasecmp(val, "out")) {
profile->vflags |= VAD_OUT;
} else if (!strcasecmp(val, "both")) {
profile->vflags |= VAD_OUT | VAD_IN;
} else if (strcasecmp(val, "none")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid option %s for VAD\n", val);
}
} else if (!strcasecmp(var, "ext-rtp-ip")) {
if (!zstr(val)) {
char *ip = mod_sofia_globals.guess_ip;
if (!strcmp(val, "0.0.0.0")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid IP 0.0.0.0 replaced with %s\n",
mod_sofia_globals.guess_ip);
} else if (!strcasecmp(val, "auto-nat")) {
ip = NULL;
} else {
ip = strcasecmp(val, "auto") ? val : mod_sofia_globals.guess_ip;
sofia_clear_pflag(profile, PFLAG_AUTO_NAT);
}
if (ip) {
if (!strncasecmp(ip, "autonat:", 8)) {
profile->extrtpip = switch_core_strdup(profile->pool, ip + 8);
if (zstr(profile->extsipip)) {
profile->extsipip = switch_core_strdup(profile->pool, profile->extrtpip);
}
sofia_set_pflag(profile, PFLAG_AUTO_NAT);
} else {
profile->extrtpip = switch_core_strdup(profile->pool, ip);
}
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid ext-rtp-ip\n");
}
} else if (!strcasecmp(var, "rtp-ip")) {
char *ip = mod_sofia_globals.guess_ip;
char buf[64];
if (zstr(val)) {
ip = mod_sofia_globals.guess_ip;
} else if (!strcmp(val, "0.0.0.0")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid IP 0.0.0.0 replaced with %s\n", mod_sofia_globals.guess_ip);
} else if (!strncasecmp(val, "interface:", 10)) {
char *ifname = val+10;
int family = AF_UNSPEC;
if (!strncasecmp(ifname, "auto/", 5)) { ifname += 5; family = AF_UNSPEC; }
if (!strncasecmp(ifname, "ipv4/", 5)) { ifname += 5; family = AF_INET; }
if (!strncasecmp(ifname, "ipv6/", 5)) { ifname += 5; family = AF_INET6; }
if (switch_find_interface_ip(buf, sizeof(buf), NULL, ifname, family) == SWITCH_STATUS_SUCCESS) {
ip = buf;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Using %s IP for interface %s for rtp-ip\n", ip, val+10);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown IP for interface %s for rtp-ip\n", val+10);
}
} else {
ip = strcasecmp(val, "auto") ? val : mod_sofia_globals.guess_ip;
}
if (strchr(ip, ':')) {
if (profile->rtpip_index < MAX_RTPIP) {
profile->rtpip6[profile->rtpip_index6++] = switch_core_strdup(profile->pool, ip);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Max IPs configured for profile %s.\n", profile->name);
}
} else {
if (profile->rtpip_index6 < MAX_RTPIP) {
profile->rtpip[profile->rtpip_index++] = switch_core_strdup(profile->pool, ip);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Max IPs configured for profile %s.\n", profile->name);
}
}
} else if (!strcasecmp(var, "sip-ip") && val) {
char *ip = mod_sofia_globals.guess_ip;
char buf[64];
if (!strcmp(val, "0.0.0.0")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid IP 0.0.0.0 replaced with %s\n", mod_sofia_globals.guess_ip);
} else if (!strncasecmp(val, "interface:", 10)) {
char *ifname = val+10;
int family = AF_UNSPEC;
if (!strncasecmp(ifname, "auto/", 5)) { ifname += 5; family = AF_UNSPEC; }
if (!strncasecmp(ifname, "ipv4/", 5)) { ifname += 5; family = AF_INET; }
if (!strncasecmp(ifname, "ipv6/", 5)) { ifname += 5; family = AF_INET6; }
if (switch_find_interface_ip(buf, sizeof(buf), NULL, ifname, family) == SWITCH_STATUS_SUCCESS) {
ip = buf;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Using %s IP for interface %s for sip-ip\n", ip, val+10);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown IP for interface %s for sip-ip\n", val+10);
}
} else {
ip = strcasecmp(val, "auto") ? val : mod_sofia_globals.guess_ip;
}
profile->sipip = switch_core_strdup(profile->pool, ip);
} else if (!strcasecmp(var, "ext-sip-port") && val) {
int tmp = atoi(val);
if (tmp > 0) profile->extsipport = (switch_port_t)tmp;
} else if (!strcasecmp(var, "ext-sip-ip")) {
if (!zstr(val)) {
char *ip = mod_sofia_globals.guess_ip;
char stun_ip[50] = "";
char *myip = stun_ip;
switch_copy_string(stun_ip, ip, sizeof(stun_ip));
if (!strcasecmp(val, "0.0.0.0")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid IP 0.0.0.0 replaced with %s\n",
mod_sofia_globals.guess_ip);
} else if (!strcasecmp(val, "auto-nat")) {
ip = NULL;
} else if (strcasecmp(val, "auto")) {
if (sofia_glue_ext_address_lookup(profile, &myip, &profile->extsipport, val, profile->pool) == SWITCH_STATUS_SUCCESS) {
ip = myip;
sofia_clear_pflag(profile, PFLAG_AUTO_NAT);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get external ip.\n");
}
}
if (ip) {
if (!strncasecmp(ip, "autonat:", 8)) {
profile->extsipip = switch_core_strdup(profile->pool, ip + 8);
sofia_set_pflag(profile, PFLAG_AUTO_NAT);
} else {
profile->extsipip = switch_core_strdup(profile->pool, ip);
}
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid ext-sip-ip\n");
}
} else if (!strcasecmp(var, "local-network-acl")) {
if (val && !strcasecmp(val, "none")) {
profile->local_network = NULL;
} else {
profile->local_network = switch_core_strdup(profile->pool, val);
}
} else if (!strcasecmp(var, "force-register-domain")) {
profile->reg_domain = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "force-register-db-domain")) {
profile->reg_db_domain = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "force-subscription-domain")) {
profile->sub_domain = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "bind-params")) {
profile->bind_params = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "sip-domain")) {
profile->sipdomain = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "rtp-timer-name")) {
profile->timer_name = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "hold-music")) {
profile->hold_music = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "outbound-proxy") && val) {
if (strncasecmp(val, "sip:", 4) && strncasecmp(val, "sips:", 5)) {
profile->outbound_proxy = switch_core_sprintf(profile->pool, "sip:%s", val);
} else {
profile->outbound_proxy = switch_core_strdup(profile->pool, val);
}
} else if (!strcasecmp(var, "rtcp-audio-interval-msec")) {
profile->rtcp_audio_interval_msec = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "rtcp-video-interval-msec")) {
profile->rtcp_video_interval_msec = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "session-timeout") && !zstr(val)) {
int v_session_timeout = atoi(val);
if (v_session_timeout >= 0) {
profile->session_timeout = v_session_timeout;
}
} else if (!strcasecmp(var, "max-proceeding") && !zstr(val)) {
int v_max_proceeding = atoi(val);
if (v_max_proceeding >= 0) {
profile->max_proceeding = v_max_proceeding;
}
} else if (!strcasecmp(var, "rtp-timeout-sec") && !zstr(val)) {
int v = atoi(val);
if (v >= 0) {
profile->rtp_timeout_sec = v;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"rtp-timeout-sec deprecated use media_timeout variable.\n");
}
} else if (!strcasecmp(var, "rtp-hold-timeout-sec") && !zstr(val)) {
int v = atoi(val);
if (v >= 0) {
profile->rtp_hold_timeout_sec = v;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"rtp-hold-timeout-sec deprecated use media_hold_timeout variable.\n");
}
} else if (!strcasecmp(var, "disable-transfer")) {
if (switch_true(val)) {
profile->mflags &= ~MFLAG_REFER;
} else {
profile->mflags |= MFLAG_REFER;
}
} else if (!strcasecmp(var, "disable-register")) {
if (switch_true(val)) {
profile->mflags &= ~MFLAG_REGISTER;
} else {
profile->mflags |= MFLAG_REGISTER;
}
} else if (!strcasecmp(var, "media-option") && !zstr(val)) {
if (!strcasecmp(val, "resume-media-on-hold")) {
profile->media_options |= MEDIA_OPT_MEDIA_ON_HOLD;
} else if (!strcasecmp(val, "bypass-media-after-att-xfer")) {
profile->media_options |= MEDIA_OPT_BYPASS_AFTER_ATT_XFER;
} else if (!strcasecmp(val, "bypass-media-after-hold")) {
if (profile->media_options & MEDIA_OPT_MEDIA_ON_HOLD) {
profile->media_options |= MEDIA_OPT_BYPASS_AFTER_HOLD;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
"bypass-media-after-hold can be set only with resume-media-on-hold media-option\n");
}
} else if (!strcasecmp(val, "none")) {
profile->media_options = 0;
}
} else if (!strcasecmp(var, "pnp-provision-url")) {
profile->pnp_prov_url = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "manage-presence")) {
if (val && !strcasecmp(val, "passive")) {
profile->pres_type = PRES_TYPE_PASSIVE;
} else if (val && !strcasecmp(val, "pnp")) {
profile->pres_type = PRES_TYPE_PNP;
} else if (switch_true(val)) {
profile->pres_type = PRES_TYPE_FULL;
} else {
profile->pres_type = 0;
}
} else if (!strcasecmp(var, "presence-hold-state")) {
if (val && !strcasecmp(val, "confirmed")) {
profile->pres_held_type = PRES_HELD_CONFIRMED;
} else if (val && !strcasecmp(val, "terminated")) {
profile->pres_held_type = PRES_HELD_TERMINATED;
} else {
profile->pres_held_type = 0;
}
} else if (!strcasecmp(var, "presence-privacy")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_PRESENCE_PRIVACY);
} else {
sofia_clear_pflag(profile, PFLAG_PRESENCE_PRIVACY);
}
} else if (!strcasecmp(var, "update-refresher")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_UPDATE_REFRESHER);
} else {
sofia_clear_pflag(profile, PFLAG_UPDATE_REFRESHER);
}
} else if (!strcasecmp(var, "rfc-7989")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_RFC7989_SESSION_ID);
} else {
sofia_clear_pflag(profile, PFLAG_RFC7989_SESSION_ID);
}
} else if (!strcasecmp(var, "rfc-7989-filter")) {
profile->rfc7989_filter = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "rfc-7989-force-old")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_RFC7989_FORCE_OLD);
} else {
sofia_clear_pflag(profile, PFLAG_RFC7989_FORCE_OLD);
}
} else if (!strcasecmp(var, "manage-shared-appearance")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE);
profile->pres_type = PRES_TYPE_FULL;
sofia_set_pflag(profile, PFLAG_MULTIREG);
} else if (val && !strcasecmp(val, "sylantro")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
"Sylantro support has been removed.\n"
"It was incomplete anyway, and we fully support the broadsoft SCA shared line spec.");
sofia_clear_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE);
} else {
sofia_clear_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE);
}
} else if (!strcasecmp(var, "disable-srv")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_DISABLE_SRV);
} else {
sofia_clear_pflag(profile, PFLAG_DISABLE_SRV);
}
} else if (!strcasecmp(var, "disable-naptr")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_DISABLE_NAPTR);
} else {
sofia_clear_pflag(profile, PFLAG_DISABLE_NAPTR);
}
} else if (!strcasecmp(var, "disable-srv503")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_DISABLE_SRV503);
} else {
sofia_clear_pflag(profile, PFLAG_DISABLE_SRV503);
}
} else if (!strcasecmp(var, "unregister-on-options-fail")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_UNREG_OPTIONS_FAIL);
} else {
sofia_clear_pflag(profile, PFLAG_UNREG_OPTIONS_FAIL);
}
} else if (!strcasecmp(var, "sip-user-ping-max") && !zstr(val)) {
profile->sip_user_ping_max = atoi(val);
} else if (!strcasecmp(var, "sip-user-ping-min") && !zstr(val)) {
profile->sip_user_ping_min = atoi(val);
} else if (!strcasecmp(var, "require-secure-rtp")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_SECURE);
} else {
sofia_clear_pflag(profile, PFLAG_SECURE);
}
} else if (!strcasecmp(var, "auto-invite-100")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_AUTO_INVITE_100);
} else {
sofia_clear_pflag(profile, PFLAG_AUTO_INVITE_100);
}
} else {
found = 0;
}
if (found) continue;
if (!strcasecmp(var, "multiple-registrations")) {
if (val && !strcasecmp(val, "call-id")) {
sofia_set_pflag(profile, PFLAG_MULTIREG);
} else if (val && (!strcasecmp(val, "contact") || switch_true(val))) {
sofia_set_pflag(profile, PFLAG_MULTIREG);
sofia_set_pflag(profile, PFLAG_MULTIREG_CONTACT);
} else if (!switch_true(val)) {
sofia_clear_pflag(profile, PFLAG_MULTIREG);
sofia_clear_pflag(profile, PFLAG_MULTIREG_CONTACT);
}
} else if (!strcasecmp(var, "supress-cng") || !strcasecmp(var, "suppress-cng")) {
if (switch_true(val)) {
sofia_set_media_flag(profile, SCMF_SUPPRESS_CNG);
profile->cng_pt = 0;
} else {
sofia_clear_media_flag(profile, SCMF_SUPPRESS_CNG);
}
} else if (!strcasecmp(var, "NDLB-broken-auth-hash")) {
if (switch_true(val)) {
profile->ndlb |= PFLAG_NDLB_BROKEN_AUTH_HASH;
} else {
profile->ndlb &= ~PFLAG_NDLB_BROKEN_AUTH_HASH;
}
} else if (!strcasecmp(var, "NDLB-sendrecv-in-session")) {
if (switch_true(val)) {
profile->mndlb |= SM_NDLB_SENDRECV_IN_SESSION;
} else {
profile->mndlb &= ~SM_NDLB_SENDRECV_IN_SESSION;
}
} else if (!strcasecmp(var, "NDLB-allow-bad-iananame")) {
if (switch_true(val)) {
profile->ndlb |= SM_NDLB_ALLOW_BAD_IANANAME;
} else {
profile->ndlb &= ~SM_NDLB_ALLOW_BAD_IANANAME;
}
} else if (!strcasecmp(var, "NDLB-expires-in-register-response")) {
if (switch_true(val)) {
profile->ndlb |= PFLAG_NDLB_EXPIRES_IN_REGISTER_RESPONSE;
} else {
profile->ndlb &= ~PFLAG_NDLB_EXPIRES_IN_REGISTER_RESPONSE;
}
} else if (!strcasecmp(var, "NDLB-allow-crypto-in-avp")) {
if (switch_true(val)) {
profile->mndlb |= SM_NDLB_ALLOW_CRYPTO_IN_AVP;
} else {
profile->mndlb &= ~SM_NDLB_ALLOW_CRYPTO_IN_AVP;
}
} else if (!strcasecmp(var, "NDLB-allow-nondup-sdp")) {
if (switch_true(val)) {
profile->mndlb |= SM_NDLB_ALLOW_NONDUP_SDP;
} else {
profile->mndlb &= ~SM_NDLB_ALLOW_NONDUP_SDP;
}
} else if (!strcasecmp(var, "pass-rfc2833")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_PASS_RFC2833);
} else {
sofia_clear_pflag(profile, PFLAG_PASS_RFC2833);
}
} else if (!strcasecmp(var, "rtp-autofix-timing")) {
if (switch_true(val)) {
sofia_set_media_flag(profile, SCMF_AUTOFIX_TIMING);
} else {
sofia_clear_media_flag(profile, SCMF_AUTOFIX_TIMING);
}
} else if (!strcasecmp(var, "contact-user")) {
profile->contact_user = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "nat-options-ping")) {
if (val && !strcasecmp(val, "udp-only")) {
sofia_set_pflag(profile, PFLAG_UDP_NAT_OPTIONS_PING);
} else if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_NAT_OPTIONS_PING);
} else {
sofia_clear_pflag(profile, PFLAG_NAT_OPTIONS_PING);
sofia_clear_pflag(profile, PFLAG_UDP_NAT_OPTIONS_PING);
}
} else if (!strcasecmp(var, "all-reg-options-ping")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_ALL_REG_OPTIONS_PING);
} else {
sofia_clear_pflag(profile, PFLAG_ALL_REG_OPTIONS_PING);
}
} else if (!strcasecmp(var, "inbound-codec-negotiation")) {
if (val && !strcasecmp(val, "greedy")) {
sofia_set_media_flag(profile, SCMF_CODEC_GREEDY);
} else if (val && !strcasecmp(val, "scrooge")) {
sofia_set_media_flag(profile, SCMF_CODEC_GREEDY);
sofia_set_media_flag(profile, SCMF_CODEC_SCROOGE);
} else {
sofia_clear_media_flag(profile, SCMF_CODEC_SCROOGE);
sofia_clear_media_flag(profile, SCMF_CODEC_GREEDY);
}
} else if (!strcasecmp(var, "disable-transcoding")) {
if (switch_true(val)) {
sofia_set_media_flag(profile, SCMF_DISABLE_TRANSCODING);
} else {
sofia_clear_media_flag(profile, SCMF_DISABLE_TRANSCODING);
}
} else if (!strcasecmp(var, "rtp-rewrite-timestamps")) {
if (switch_true(val)) {
sofia_set_media_flag(profile, SCMF_REWRITE_TIMESTAMPS);
} else {
sofia_clear_media_flag(profile, SCMF_REWRITE_TIMESTAMPS);
}
} else if (!strcasecmp(var, "auth-calls")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_AUTH_CALLS);
} else {
sofia_clear_pflag(profile, PFLAG_AUTH_CALLS);
}
} else if (!strcasecmp(var, "auth-messages")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_AUTH_MESSAGES);
} else {
sofia_clear_pflag(profile, PFLAG_AUTH_MESSAGES);
}
} else if (!strcasecmp(var, "auth-subscriptions")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_AUTH_SUBSCRIPTIONS);
} else {
sofia_clear_pflag(profile, PFLAG_AUTH_SUBSCRIPTIONS);
}
} else if (!strcasecmp(var, "extended-info-parsing")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_EXTENDED_INFO_PARSING);
} else {
sofia_clear_pflag(profile, PFLAG_EXTENDED_INFO_PARSING);
}
} else if (!strcasecmp(var, "nonce-ttl") && !zstr(val)) {
profile->nonce_ttl = atoi(val);
} else if (!strcasecmp(var, "max-auth-validity") && !zstr(val)) {
profile->max_auth_validity = atoi(val);
} else if (!strcasecmp(var, "auth-require-user")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_AUTH_REQUIRE_USER);
} else {
sofia_clear_pflag(profile, PFLAG_AUTH_REQUIRE_USER);
}
} else if (!strcasecmp(var, "accept-blind-reg")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_BLIND_REG);
} else {
sofia_clear_pflag(profile, PFLAG_BLIND_REG);
}
} else if (!strcasecmp(var, "3pcc-reinvite-bridged-on-ack")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_3PCC_REINVITE_BRIDGED_ON_ACK);
} else {
sofia_clear_pflag(profile, PFLAG_3PCC_REINVITE_BRIDGED_ON_ACK);
}
} else if (!strcasecmp(var, "enable-3pcc")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_3PCC);
} else if (!strcasecmp(val, "proxy")) {
sofia_set_pflag(profile, PFLAG_3PCC_PROXY);
}
} else if (!strcasecmp(var, "accept-blind-auth")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_BLIND_AUTH);
} else {
sofia_clear_pflag(profile, PFLAG_BLIND_AUTH);
}
} else if (!strcasecmp(var, "auth-all-packets")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_AUTH_ALL);
} else {
sofia_clear_pflag(profile, PFLAG_AUTH_ALL);
}
} else if (!strcasecmp(var, "full-id-in-dialplan")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_FULL_ID);
} else {
sofia_clear_pflag(profile, PFLAG_FULL_ID);
}
} else if (!strcasecmp(var, "inbound-reg-force-matching-username")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_CHECKUSER);
} else {
sofia_clear_pflag(profile, PFLAG_CHECKUSER);
}
} else if (!strcasecmp(var, "enable-timer")) {
if (!switch_true(val)) {
sofia_set_pflag(profile, PFLAG_DISABLE_TIMER);
} else {
sofia_clear_pflag(profile, PFLAG_DISABLE_TIMER);
}
} else if (!strcasecmp(var, "enable-rfc-5626")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_ENABLE_RFC5626);
} else {
sofia_clear_pflag(profile, PFLAG_ENABLE_RFC5626);
}
} else if (!strcasecmp(var, "minimum-session-expires") && !zstr(val)) {
profile->minimum_session_expires = atoi(val);
/* per RFC 4028: minimum_session_expires must be > 90 */
if (profile->minimum_session_expires < 90) {
profile->minimum_session_expires = 90;
}
} else if (!strcasecmp(var, "enable-100rel")) {
if (switch_true(val)) {
sofia_clear_pflag(profile, PFLAG_DISABLE_100REL);
} else {
sofia_set_pflag(profile, PFLAG_DISABLE_100REL);
}
} else if (!strcasecmp(var, "enable-compact-headers")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_SIPCOMPACT);
} else {
sofia_clear_pflag(profile, PFLAG_SIPCOMPACT);
}
} else if (!strcasecmp(var, "pass-callee-id")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_PASS_CALLEE_ID);
} else {
sofia_clear_pflag(profile, PFLAG_PASS_CALLEE_ID);
}
} else if (!strcasecmp(var, "enable-soa")) {
if (switch_true(val)) {
sofia_set_flag(profile, TFLAG_ENABLE_SOA);
} 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 (val && !strcasecmp(val, "aal2")) {
profile->codec_flags = SWITCH_CODEC_FLAG_AAL2;
} else {
profile->codec_flags = 0;
}
} else if (!strcasecmp(var, "username")) {
profile->sdp_username = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "context")) {
profile->context = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "apply-nat-acl") && !zstr(val)) {
if (!strcasecmp(val,"none")) {
profile->nat_acl_count = 0;
} else if (profile->nat_acl_count < SOFIA_MAX_ACL) {
if (!profile->extsipip && profile->sipip && switch_check_network_list_ip(profile->sipip, val)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Not adding acl %s because it's the local network\n", val);
} else {
profile->nat_acl[profile->nat_acl_count++] = switch_core_strdup(profile->pool, val);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Max acl records of %d reached\n", SOFIA_MAX_ACL);
}
} else if (!strcasecmp(var, "apply-inbound-acl") && !zstr(val)) {
if (!strcasecmp(val,"none")) {
profile->acl_count = 0;
} else if (profile->acl_count < SOFIA_MAX_ACL) {
char *list, *pass = NULL, *fail = NULL;
list = switch_core_strdup(profile->pool, val);
if ((pass = strchr(list, ':'))) {
*pass++ = '\0';
if ((fail = strchr(pass, ':'))) {
*fail++ = '\0';
}
if (zstr(pass)) pass = NULL;
if (zstr(fail)) fail = NULL;
profile->acl_pass_context[profile->acl_count] = pass;
profile->acl_fail_context[profile->acl_count] = fail;
}
profile->acl[profile->acl_count++] = list;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Max acl records of %d reached\n", SOFIA_MAX_ACL);
}
} else if (!strcasecmp(var, "apply-proxy-acl") && !zstr(val)) {
if (!strcasecmp(val,"none")) {
profile->proxy_acl_count = 0;
} else if (profile->proxy_acl_count < SOFIA_MAX_ACL) {
profile->proxy_acl[profile->proxy_acl_count++] = switch_core_strdup(profile->pool, val);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Max acl records of %d reached\n", SOFIA_MAX_ACL);
}
} else if (!strcasecmp(var, "apply-register-acl") && !zstr(val)) {
if (!strcasecmp(val,"none")) {
profile->reg_acl_count = 0;
} else if (profile->reg_acl_count < SOFIA_MAX_ACL) {
profile->reg_acl[profile->reg_acl_count++] = switch_core_strdup(profile->pool, val);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Max acl records of %d reached\n", SOFIA_MAX_ACL);
}
} else if (!strcasecmp(var, "apply-candidate-acl") && !zstr(val)) {
if (!strcasecmp(val,"none")) {
profile->cand_acl_count = 0;
} else if (profile->cand_acl_count < SWITCH_MAX_CAND_ACL) {
profile->cand_acl[profile->cand_acl_count++] = switch_core_strdup(profile->pool, val);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Max acl records of %d reached\n", SWITCH_MAX_CAND_ACL);
}
} else if (!strcasecmp(var, "alias")) {
sip_alias_node_t *node;
if (zstr(val)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Alias Param has no data...\n");
} else {
if ((node = switch_core_alloc(profile->pool, sizeof(*node)))) {
if ((node->url = switch_core_strdup(profile->pool, val))) {
node->next = profile->aliases;
profile->aliases = node;
}
}
}
} else if (!strcasecmp(var, "dialplan")) {
profile->dialplan = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "max-calls") && !zstr(val)) {
profile->max_calls = atoi(val);
} else if (!strcasecmp(var, "codec-prefs")) {
profile->inbound_codec_string = switch_core_strdup(profile->pool, val);
profile->outbound_codec_string = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "inbound-codec-prefs")) {
profile->inbound_codec_string = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "outbound-codec-prefs")) {
profile->outbound_codec_string = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "challenge-realm")) {
profile->challenge_realm = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "dtmf-duration") && !zstr(val)) {
uint32_t dur = atoi(val);
if (dur >= switch_core_min_dtmf_duration(0) && dur <= switch_core_max_dtmf_duration(0)) {
profile->dtmf_duration = dur;
} else {
profile->dtmf_duration = SWITCH_DEFAULT_DTMF_DURATION;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Duration out of bounds, using default of %d!\n",
SWITCH_DEFAULT_DTMF_DURATION);
}
} else if (!strcasecmp(var, "ws-binding") && !zstr(val)) {
int tmp;
char *p;
profile->ws_ip = switch_core_strdup(profile->pool, val);
if ((p = strrchr(profile->ws_ip, ':'))) {
*p++ = '\0';
if ((tmp = atol(p)) && tmp > 0) {
profile->ws_port = (switch_port_t) tmp;
}
}
} else if (!strcasecmp(var, "wss-binding") && !zstr(val)) {
int tmp;
char *p;
profile->wss_ip = switch_core_strdup(profile->pool, val);
if ((p = strrchr(profile->wss_ip, ':'))) {
*p++ = '\0';
if ((tmp = atol(p)) && tmp > 0) {
profile->wss_port = (switch_port_t) tmp;
}
}
/*
* handle TLS params #1
*/
} else if (!strcasecmp(var, "tls")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_TLS);
if (profile->tls_bind_params) {
if (!switch_stristr("transport=tls", profile->tls_bind_params)) {
profile->tls_bind_params = switch_core_sprintf(profile->pool, "%s;transport=tls", profile->tls_bind_params);
}
} else {
profile->tls_bind_params = switch_core_strdup(profile->pool, "transport=tls");
}
} else {
sofia_clear_pflag(profile, PFLAG_TLS);
}
} else if (!strcasecmp(var, "tls-bind-params")) {
if (val && switch_stristr("transport=tls", val)) {
profile->tls_bind_params = switch_core_strdup(profile->pool, val);
} else {
profile->tls_bind_params = switch_core_sprintf(profile->pool, "%s;transport=tls", val);
}
} else if (!strcasecmp(var, "tls-only")) {
profile->tls_only = switch_true(val);
} else if (!strcasecmp(var, "tls-verify-date")) {
profile->tls_verify_date = switch_true(val);
} else if (!strcasecmp(var, "tls-verify-depth") && !zstr(val)) {
profile->tls_verify_depth = atoi(val);
} else if (!strcasecmp(var, "tls-verify-policy")) {
profile->tls_verify_policy = sofia_glue_str2tls_verify_policy(val);
} else if (!strcasecmp(var, "tls-sip-port") && !zstr(val)) {
if (!strcasecmp(val, "auto")) {
sofia_set_pflag(profile, PFLAG_AUTO_ASSIGN_TLS_PORT);
} else {
sofia_clear_pflag(profile, PFLAG_AUTO_ASSIGN_TLS_PORT);
profile->tls_sip_port = (switch_port_t) atoi(val);
}
} else if (!strcasecmp(var, "tls-cert-dir") && !zstr(val)) {
profile->tls_cert_dir = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "tls-passphrase") && !zstr(val)) {
profile->tls_passphrase = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "tls-verify-in-subjects") && !zstr(val)) {
profile->tls_verify_in_subjects_str = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "tls-ciphers") && !zstr(val)) {
profile->tls_ciphers = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "tls-version") && !zstr(val)) {
char *ps = val, *pe = val;
profile->tls_version = 0;
while (ps && *pe) {
int n;
pe = strchr(ps,',');
if (!pe && !(pe = memchr(ps,0,1024))) break;
n = (int)(pe-ps);
if (n==5 && !strncasecmp(ps, "sslv2", n))
profile->tls_version |= SOFIA_TLS_VERSION_SSLv2;
if (n==5 && !strncasecmp(ps, "sslv3", n))
profile->tls_version |= SOFIA_TLS_VERSION_SSLv3;
if (n==6 && !strncasecmp(ps, "sslv23", n))
profile->tls_version |= SOFIA_TLS_VERSION_SSLv2 | SOFIA_TLS_VERSION_SSLv3;
if (n==5 && !strncasecmp(ps, "tlsv1", n))
profile->tls_version |= SOFIA_TLS_VERSION_TLSv1;
if (n==7 && !strncasecmp(ps, "tlsv1.1", n))
profile->tls_version |= SOFIA_TLS_VERSION_TLSv1_1;
if (n==7 && !strncasecmp(ps, "tlsv1.2", n))
profile->tls_version |= SOFIA_TLS_VERSION_TLSv1_2;
ps=pe+1;
}
} else if (!strcasecmp(var, "tls-timeout") && !zstr(val)) {
int v = atoi(val);
profile->tls_timeout = v > 0 ? (unsigned int)v : 300;
} else if (!strcasecmp(var, "timer-T1") && !zstr(val)) {
int v = atoi(val);
if (v > 0) {
profile->timer_t1 = v;
} else {
profile->timer_t1 = 500;
}
} else if (!strcasecmp(var, "timer-T1X64") && !zstr(val)) {
int v = atoi(val);
if (v > 0) {
profile->timer_t1x64 = v;
} else {
profile->timer_t1x64 = 32000;
}
} else if (!strcasecmp(var, "timer-T2") && !zstr(val)) {
int v = atoi(val);
if (v > 0) {
profile->timer_t2 = v;
} else {
profile->timer_t2 = 4000;
}
} else if (!strcasecmp(var, "timer-T4") && !zstr(val)) {
int v = atoi(val);
if (v > 0) {
profile->timer_t4 = v;
} else {
profile->timer_t4 = 4000;
}
} else if (!strcasecmp(var, "sip-options-respond-503-on-busy")) {
if (switch_true(val)) {
sofia_set_pflag(profile, PFLAG_OPTIONS_RESPOND_503_ON_BUSY);
} else {
sofia_clear_pflag(profile, PFLAG_OPTIONS_RESPOND_503_ON_BUSY);
}
} else if (!strcasecmp(var, "sip-expires-late-margin") && !zstr(val)) {
int32_t sip_expires_late_margin = atoi(val);
if (sip_expires_late_margin >= 0) {
profile->sip_expires_late_margin = sip_expires_late_margin;
} else {
profile->sip_expires_late_margin = 60;
}
} else if (!strcasecmp(var, "sip-force-expires-min") && !zstr(val)) {
int32_t sip_force_expires_min = atoi(val);
if (sip_force_expires_min >= 0) {
profile->sip_force_expires_min = sip_force_expires_min;
} else {
profile->sip_force_expires_min = 0;
}
} else if (!strcasecmp(var, "sip-force-expires-max") && !zstr(val)) {
int32_t sip_force_expires_max = atoi(val);
if (sip_force_expires_max >= 0) {
profile->sip_force_expires_max = sip_force_expires_max;
} else {
profile->sip_force_expires_max = 0;
}
} else if (!strcasecmp(var, "sip-force-expires") && !zstr(val)) {
int32_t sip_force_expires = atoi(val);
if (sip_force_expires >= 0) {
profile->sip_force_expires = sip_force_expires;
} else {
profile->sip_force_expires = 0;
}
} else if (!strcasecmp(var, "sip-expires-max-deviation") && !zstr(val)) {
int32_t sip_expires_max_deviation = atoi(val);
if (sip_expires_max_deviation >= 0) {
profile->sip_expires_max_deviation = sip_expires_max_deviation;
} else {
profile->sip_expires_max_deviation = 0;
}
} else if (!strcasecmp(var, "sip-subscription-max-deviation") && !zstr(val)) {
int32_t sip_subscription_max_deviation = atoi(val);
if (sip_subscription_max_deviation >= 0) {
profile->sip_subscription_max_deviation = sip_subscription_max_deviation;
} else {
profile->sip_subscription_max_deviation = 0;
}
} else if (!strcasecmp(var, "reuse-connections")) {
switch_bool_t value = switch_true(val);
if (!value) {
sofia_set_pflag(profile, PFLAG_NO_CONNECTION_REUSE);
} else {
sofia_clear_pflag(profile, PFLAG_NO_CONNECTION_REUSE);
}
} else if (!strcasecmp(var, "p-asserted-id-parse")) {
if (!val) {
profile->paid_type = PAID_DEFAULT;
} else if (!strncasecmp(val, "default", 7)) {
profile->paid_type = PAID_DEFAULT;
} else if (!strncasecmp(val, "user-only", 9)) {
profile->paid_type = PAID_USER;
} else if (!strncasecmp(val, "user-domain", 11)) {
profile->paid_type = PAID_USER_DOMAIN;
} else if (!strncasecmp(val, "verbatim", 8)) {
profile->paid_type = PAID_VERBATIM;
} else {
profile->paid_type = PAID_DEFAULT;
}
} else if (!strcasecmp(var, "channel-xml-fetch-on-nightmare-transfer")) {
if(switch_true(val)) {
sofia_set_pflag(profile, PFLAG_CHANNEL_XML_FETCH_ON_NIGHTMARE_TRANSFER);
} else {
sofia_clear_pflag(profile, PFLAG_CHANNEL_XML_FETCH_ON_NIGHTMARE_TRANSFER);
}
} else if (!strcasecmp(var, "make-every-transfer-a-nightmare")) {
if(switch_true(val)) {
sofia_set_pflag(profile, PFLAG_MAKE_EVERY_TRANSFER_A_NIGHTMARE);
} else {
sofia_clear_pflag(profile, PFLAG_MAKE_EVERY_TRANSFER_A_NIGHTMARE);
}
} else if (!strcasecmp(var, "fire-transfer-events")) {
if(switch_true(val)) {
sofia_set_pflag(profile, PFLAG_FIRE_TRANFER_EVENTS);
} else {
sofia_clear_pflag(profile, PFLAG_FIRE_TRANFER_EVENTS);
}
} else if (!strcasecmp(var, "enforce-blind-auth-result")) {
if(switch_true(val)) {
sofia_set_pflag(profile, PFLAG_BLIND_AUTH_ENFORCE_RESULT);
} else {
sofia_clear_pflag(profile, PFLAG_BLIND_AUTH_ENFORCE_RESULT);
}
} else if (!strcasecmp(var, "blind-auth-reply-403")) {
if(switch_true(val)) {
sofia_set_pflag(profile, PFLAG_BLIND_AUTH_REPLY_403);
} else {
sofia_clear_pflag(profile, PFLAG_BLIND_AUTH_REPLY_403);
}
} else if (!strcasecmp(var, "auth-calls-acl-only")) {
if(switch_true(val)) {
sofia_set_pflag(profile, PFLAG_AUTH_CALLS_ACL_ONLY);
} else {
sofia_clear_pflag(profile, PFLAG_AUTH_CALLS_ACL_ONLY);
}
} else if (!strcasecmp(var, "use-port-for-acl-check")) {
if(switch_true(val)) {
sofia_set_pflag(profile, PFLAG_USE_PORT_FOR_ACL_CHECK);
} else {
sofia_clear_pflag(profile, PFLAG_USE_PORT_FOR_ACL_CHECK);
}
} else if (!strcasecmp(var, "apply-inbound-acl-x-token")) {
profile->acl_inbound_x_token_header = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "apply-proxy-acl-x-token")) {
profile->acl_proxy_x_token_header = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "proxy-hold")) {
if(switch_true(val)) {
sofia_set_pflag(profile, PFLAG_PROXY_HOLD);
} else {
sofia_clear_pflag(profile, PFLAG_PROXY_HOLD);
}
} else if (!strcasecmp(var, "proxy-info")) {
if(switch_true(val)) {
sofia_set_pflag(profile, PFLAG_PROXY_INFO);
} else {
sofia_clear_pflag(profile, PFLAG_PROXY_INFO);
}
} else if (!strcasecmp(var, "proxy-message")) {
if(switch_true(val)) {
sofia_set_pflag(profile, PFLAG_PROXY_MESSAGE);
} else {
sofia_clear_pflag(profile, PFLAG_PROXY_MESSAGE);
}
} else if (!strcasecmp(var, "proxy-notify-events")) {
profile->proxy_notify_events = switch_core_strdup(profile->pool, val);
} else if (!strcasecmp(var, "proxy-info-content-types")) {
profile->proxy_info_content_types = switch_core_strdup(profile->pool, val);
}
}
if (sofia_test_flag(profile, TFLAG_ZRTP_PASSTHRU) && !sofia_test_flag(profile, TFLAG_LATE_NEGOTIATION)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "ZRTP passthrough implictly enables inbound-late-negotiation\n");
sofia_set_flag(profile, TFLAG_LATE_NEGOTIATION);
}
if (sofia_test_flag(profile, TFLAG_INB_NOMEDIA) && !sofia_test_flag(profile, TFLAG_LATE_NEGOTIATION)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "inbound-bypass-media implictly enables inbound-late-negotiation\n");
sofia_set_flag(profile, TFLAG_LATE_NEGOTIATION);
}
if (sofia_test_pflag(profile, PFLAG_SEND_DISPLAY_UPDATE) && !sofia_test_pflag(profile, PFLAG_ALLOW_UPDATE)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "send-display-update=true is set, but we can't comply because allow-update=false\n");
sofia_clear_pflag(profile, PFLAG_SEND_DISPLAY_UPDATE);
}
if (sofia_test_pflag(profile, PFLAG_PROXY_HOLD)) {
if (profile->media_options & MEDIA_OPT_MEDIA_ON_HOLD) {
profile->media_options &= ~MEDIA_OPT_MEDIA_ON_HOLD;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "proxy-hold=true is set, incompatible with media-option=resume-media-on-hold\n");
}
if (profile->media_options & MEDIA_OPT_BYPASS_AFTER_HOLD) {
profile->media_options &= ~MEDIA_OPT_BYPASS_AFTER_HOLD;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "proxy-hold=true is set, incompatible with media-option=bypass-media-after-hold\n");
}
}
if ((!profile->cng_pt) && (!sofia_test_media_flag(profile, SCMF_SUPPRESS_CNG))) {
profile->cng_pt = SWITCH_RTP_CNG_PAYLOAD;
}
if (!profile->sipip) {
profile->sipip = switch_core_strdup(profile->pool, mod_sofia_globals.guess_ip);
}
if (!profile->rtpip[0] && !profile->rtpip6[0]) {
profile->rtpip[profile->rtpip_index++] = switch_core_strdup(profile->pool, mod_sofia_globals.guess_ip);
}
if (switch_nat_get_type()) {
char *ip = switch_core_get_variable_dup("nat_public_addr");
if (ip && !strchr(profile->sipip, ':')) {
if (!profile->extrtpip) {
profile->extrtpip = switch_core_strdup(profile->pool, ip);
}
if (!profile->extsipip) {
profile->extsipip = switch_core_strdup(profile->pool, ip);
}
sofia_set_pflag(profile, PFLAG_AUTO_NAT);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "NAT detected setting external ip to %s\n", ip);
}
switch_safe_free(ip);
}
if (profile->nonce_ttl < 60) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Setting nonce TTL to 60 seconds\n");
profile->nonce_ttl = 60;
}
if (!profile->max_auth_validity) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Setting MAX Auth Validity to 0 Attempts\n");
profile->max_auth_validity = 0;
}
if (!profile->sdp_username) {
profile->sdp_username = switch_core_strdup(profile->pool, "FreeSWITCH");
}
if (!profile->rtpip[0] && !profile->rtpip6[0]) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Setting ip to '127.0.0.1'\n");
profile->rtpip[profile->rtpip_index++] = switch_core_strdup(profile->pool, "127.0.0.1");
}
if (!profile->sip_port && !sofia_test_pflag(profile, PFLAG_AUTO_ASSIGN_PORT)) {
profile->sip_port = (switch_port_t) atoi(SOFIA_DEFAULT_PORT);
if (!profile->extsipport) profile->extsipport = profile->sip_port;
}
if (!profile->dialplan) {
profile->dialplan = switch_core_strdup(profile->pool, "XML");
}
if (!profile->context) {
profile->context = switch_core_strdup(profile->pool, "default");
}
if (!profile->sipdomain) {
profile->sipdomain = switch_core_strdup(profile->pool, profile->sipip);
}
if (profile->pres_type == PRES_TYPE_PNP) {
if (!profile->pnp_prov_url) {
profile->pnp_prov_url = switch_core_sprintf(profile->pool, "http://%s/provision/", mod_sofia_globals.guess_ip);
}
if (!profile->pnp_notify_profile) {
profile->pnp_notify_profile = switch_core_strdup(profile->pool, mod_sofia_globals.guess_ip);
}
if (!profile->extsipip) {
profile->extsipip = switch_core_strdup(profile->pool, mod_sofia_globals.guess_ip);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "we're configured to provision to [%s] on profile [%s]\n",
profile->pnp_prov_url, profile->pnp_notify_profile);
}
config_sofia_profile_urls(profile);
if (profile->tls_cert_dir) {
if (profile->wss_ip) {
char *key, *cert;
key = switch_core_sprintf(profile->pool, "%s/%s", profile->tls_cert_dir, "wss.key");
if (switch_file_exists(key, profile->pool) != SWITCH_STATUS_SUCCESS) key = NULL;
cert = switch_core_sprintf(profile->pool, "%s/%s", profile->tls_cert_dir, "wss.crt");
if (switch_file_exists(cert, profile->pool) != SWITCH_STATUS_SUCCESS) cert = NULL;
if ( !key || !cert) {
key = switch_core_sprintf(profile->pool, "%s/%s", profile->tls_cert_dir, "wss.pem");
if ( switch_file_exists(key, profile->pool) != SWITCH_STATUS_SUCCESS ) {
switch_core_gen_certs(key);
}
}
}
if (sofia_test_pflag(profile, PFLAG_TLS)) {
char *key = switch_core_sprintf(profile->pool, "%s/%s", profile->tls_cert_dir, "agent.pem");
char *ca = switch_core_sprintf(profile->pool, "%s/%s", profile->tls_cert_dir, "cafile.pem");;
if (switch_file_exists(key, profile->pool) != SWITCH_STATUS_SUCCESS) key = NULL;
if (switch_file_exists(ca, profile->pool) != SWITCH_STATUS_SUCCESS) ca = NULL;
if ( !key || !ca ) {
key = switch_core_sprintf(profile->pool, "%s/%s", profile->tls_cert_dir, "tls.pem");
if ( switch_file_exists(key, profile->pool) != SWITCH_STATUS_SUCCESS ) {
switch_core_gen_certs(key);
}
}
}
}
}
if (profile) {
if (profile_already_started) {
switch_xml_t gateways_tag, domain_tag, domains_tag, aliases_tag, alias_tag;
if (sofia_test_flag(profile, TFLAG_ZRTP_PASSTHRU)) {
sofia_set_flag(profile, TFLAG_LATE_NEGOTIATION);
}
if ((gateways_tag = switch_xml_child(xprofile, "gateways"))) {
parse_gateways(profile, gateways_tag, NULL);
}
status = SWITCH_STATUS_SUCCESS;
if ((domains_tag = switch_xml_child(xprofile, "domains"))) {
switch_event_t *xml_params;
switch_event_create(&xml_params, SWITCH_EVENT_REQUEST_PARAMS);
switch_assert(xml_params);
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "purpose", "gateways");
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "profile", profile->name);
for (domain_tag = switch_xml_child(domains_tag, "domain"); domain_tag; domain_tag = domain_tag->next) {
switch_xml_t droot, x_domain_tag;
const char *dname = switch_xml_attr_soft(domain_tag, "name");
const char *parse = switch_xml_attr_soft(domain_tag, "parse");
const char *alias = switch_xml_attr_soft(domain_tag, "alias");
if (!zstr(dname)) {
if (!strcasecmp(dname, "all")) {
switch_xml_t xml_root, x_domains;
if (switch_xml_locate("directory", NULL, NULL, NULL, &xml_root, &x_domains, xml_params, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
for (x_domain_tag = switch_xml_child(x_domains, "domain"); x_domain_tag; x_domain_tag = x_domain_tag->next) {
dname = switch_xml_attr_soft(x_domain_tag, "name");
parse_domain_tag(profile, x_domain_tag, dname, parse, alias);
}
switch_xml_free(xml_root);
}
} else if (switch_xml_locate_domain(dname, xml_params, &droot, &x_domain_tag) == SWITCH_STATUS_SUCCESS) {
parse_domain_tag(profile, x_domain_tag, dname, parse, alias);
switch_xml_free(droot);
}
}
}
switch_event_destroy(&xml_params);
}
if ((aliases_tag = switch_xml_child(xprofile, "aliases"))) {
for (alias_tag = switch_xml_child(aliases_tag, "alias"); alias_tag; alias_tag = alias_tag->next) {
char *aname = (char *) switch_xml_attr_soft(alias_tag, "name");
if (!zstr(aname)) {
if (sofia_glue_add_profile(switch_core_strdup(profile->pool, aname), profile) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Alias [%s] for profile [%s]\n", aname, profile->name);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Alias [%s] for profile [%s] (already exists)\n",
aname, profile->name);
}
}
}
}
} else {
switch_xml_t aliases_tag, alias_tag;
if ((aliases_tag = switch_xml_child(xprofile, "aliases"))) {
for (alias_tag = switch_xml_child(aliases_tag, "alias"); alias_tag; alias_tag = alias_tag->next) {
char *aname = (char *) switch_xml_attr_soft(alias_tag, "name");
if (!zstr(aname)) {
if (sofia_glue_add_profile(switch_core_strdup(profile->pool, aname), profile) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Alias [%s] for profile [%s]\n", aname, profile->name);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Adding Alias [%s] for profile [%s] (name in use)\n",
aname, profile->name);
}
}
}
}
if (profile->sipip) {
switch_event_t *s_event;
if (!profile->extsipport) profile->extsipport = profile->sip_port;
launch_sofia_profile_thread(profile);
if (profile->odbc_dsn) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Connecting ODBC Profile %s [%s]\n", profile->name, url);
switch_yield(1000000);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Started Profile %s [%s]\n", profile->name, url);
}
if ((switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_PROFILE_START) == SWITCH_STATUS_SUCCESS)) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "module_name", "mod_sofia");
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile_name", profile->name);
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile_uri", profile->url);
switch_event_fire(&s_event);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Unable to start Profile %s due to no configured sip-ip\n", profile->name);
sofia_profile_start_failure(profile, profile->name);
}
profile = NULL;
}
if (profile_found) {
break;
}
}
}
}
done:
if (profile_already_started) {
sofia_glue_release_profile(profile_already_started);
}
switch_event_destroy(&params);
if (xml) {
switch_xml_free(xml);
}
if (profile_name && !profile_found) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No Such Profile '%s'\n", profile_name);
status = SWITCH_STATUS_FALSE;
}
return status;
}
const char *sofia_gateway_status_name(sofia_gateway_status_t status)
{
static const char *status_names[] = { "DOWN", "UP", NULL };
if (status < SOFIA_GATEWAY_INVALID) {
return status_names[status];
} else {
return "INVALID";
}
}
const char *sofia_sip_user_status_name(sofia_sip_user_status_t status)
{
static const char *status_names[] = { "UNREACHABLE", "REACHABLE", NULL };
if (status < SOFIA_REG_INVALID) {
return status_names[status];
} else {
return "INVALID";
}
}
struct cb_helper_sip_user_status {
char *status;
size_t status_len;
char *contact;
size_t contact_len;
int count;
};
int sofia_sip_user_status_callback(void *pArg, int argc, char **argv, char **columnNames)
{
struct cb_helper_sip_user_status *cbt = (struct cb_helper_sip_user_status *) pArg;
if (argc != 3) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "expected 3 arguments from query, instead got %d\n", argc);
return 0;
}
switch_copy_string(cbt->status, argv[0], cbt->status_len);
cbt->count = (argv[1] && switch_is_number(argv[1])) ? atoi(argv[1]) : 0;
switch_copy_string(cbt->contact, argv[2], cbt->contact_len);
return 1;
}
static void sofia_handle_sip_r_options(switch_core_session_t *session, int status,
char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de,
tagi_t tags[])
{
sofia_gateway_t *gateway = NULL;
switch_bool_t do_fire_gateway_state_event = SWITCH_FALSE;
if (sofia_private && !zstr(sofia_private->gateway_name)) {
gateway = sofia_reg_find_gateway(sofia_private->gateway_name);
sofia_private->destroy_me = 1;
}
if (gateway) {
if (status >= 200 && status < 600 && status != 408 && status != 503) {
if (gateway->state == REG_STATE_FAILED) {
gateway->state = REG_STATE_UNREGED;
}
if (gateway->ping_count < 0) {
gateway->ping_count = 0;
}
if (gateway->ping_count < gateway->ping_max) {
gateway->ping_count++;
if (gateway->ping_count >= gateway->ping_min && gateway->status != SOFIA_GATEWAY_UP) {
gateway->status = SOFIA_GATEWAY_UP;
gateway->uptime = switch_time_now();
do_fire_gateway_state_event = SWITCH_TRUE;
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
"Ping succeeded %s with code %d - count %d/%d/%d, state %s\n",
gateway->name, status, gateway->ping_min, gateway->ping_count, gateway->ping_max, sofia_gateway_status_name(gateway->status));
}
if (gateway->ping_sent) {
gateway->ping_time = (float)(switch_time_now() - gateway->ping_sent) / 1000;
gateway->ping_sent = 0;
}
} else {
if (gateway->state == REG_STATE_REGED) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Unregister %s\n", gateway->name);
gateway->state = REG_STATE_FAILED;
gateway->ping_time = 0;
}
if (gateway->ping_count > 0) {
gateway->ping_count--;
}
if (gateway->ping_count < gateway->ping_min && gateway->status != SOFIA_GATEWAY_DOWN) {
gateway->status = SOFIA_GATEWAY_DOWN;
do_fire_gateway_state_event = SWITCH_TRUE;
gateway->ping_time = 0;
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
"Ping failed %s with code %d - count %d/%d/%d, state %s\n",
gateway->name, status, gateway->ping_min, gateway->ping_count, gateway->ping_max, sofia_gateway_status_name(gateway->status));
}
if (gateway->ping_monitoring || do_fire_gateway_state_event) {
sofia_reg_fire_custom_gateway_state_event(gateway, status, phrase);
}
gateway->ping = switch_epoch_time_now(NULL) + gateway->ping_freq;
sofia_reg_release_gateway(gateway);
gateway->pinging = 0;
} else if (sip && sip->sip_to && sip->sip_call_id && sip->sip_call_id->i_id && strchr(sip->sip_call_id->i_id, '_')) {
const char *call_id = strchr(sip->sip_call_id->i_id, '_') + 1;
char *sql;
struct cb_helper_sip_user_status sip_user_status;
char ping_status[255] = "";
char sip_contact[1024] = "";
int sip_user_ping_min = profile->sip_user_ping_min;
int sip_user_ping_max = profile->sip_user_ping_max;
char *sip_user = switch_mprintf("%s@%s", sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host);
int ping_time = 0;
if (sofia_private && sofia_private->ping_sent) {
ping_time = (int)(switch_time_now() - sofia_private->ping_sent);
}
sip_user_status.status = ping_status;
sip_user_status.status_len = sizeof(ping_status);
sip_user_status.contact = sip_contact;
sip_user_status.contact_len = sizeof(sip_contact);
sql = switch_mprintf("select ping_status, ping_count, contact from sip_registrations where sip_user='%q' and sip_host='%q' and call_id='%q'",
sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, sofia_sip_user_status_callback, &sip_user_status);
switch_safe_free(sql);
if (status != 200 && status != 486) {
sip_user_status.count--;
if (sip_user_status.count >= 0) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Ping to sip user '%s@%s' failed with code %d - count %d, state %s\n",
sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, status, sip_user_status.count, sip_user_status.status);
sql = switch_mprintf("update sip_registrations set ping_count=%d, ping_time=%d where sip_user='%q' and sip_host='%q' and call_id='%q'",
sip_user_status.count, ping_time, sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
switch_safe_free(sql);
}
if (sip_user_status.count < sip_user_ping_min) {
if (strcmp(sip_user_status.status, "Unreachable")) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Sip user '%s@%s' is now Unreachable\n",
sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host);
sql = switch_mprintf("update sip_registrations set ping_status='Unreachable', ping_time=%d where sip_user='%q' and sip_host='%q' and call_id='%q'",
ping_time, sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
switch_safe_free(sql);
sofia_reg_fire_custom_sip_user_state_event(profile, sip_user, sip_user_status.contact, sip->sip_to->a_url->url_user,
sip->sip_to->a_url->url_host, call_id, SOFIA_REG_REACHABLE, status, phrase);
if (sofia_test_pflag(profile, PFLAG_UNREG_OPTIONS_FAIL)) {
time_t now = switch_epoch_time_now(NULL);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Expire sip user '%s@%s' due to options failure\n",
sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host);
sql = switch_mprintf("update sip_registrations set expires=%ld, ping_time=%d where sip_user='%q' and sip_host='%q' and call_id='%q'",
(long) now, ping_time, sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
switch_safe_free(sql);
}
}
}
} else {
sip_user_status.count++;
if (sip_user_status.count <= sip_user_ping_max) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Ping to sip user '%s@%s' succeeded with code %d - count %d, state %s\n",
sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, status, sip_user_status.count, sip_user_status.status);
sql = switch_mprintf("update sip_registrations set ping_count=%d, ping_time=%d where sip_user='%q' and sip_host='%q' and call_id='%q'",
sip_user_status.count, ping_time, sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
switch_safe_free(sql);
}
if (sip_user_status.count >= sip_user_ping_min) {
if (strcmp(sip_user_status.status, "Reachable")) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Sip user '%s@%s' is now Reachable\n",
sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host);
sql = switch_mprintf("update sip_registrations set ping_status='Reachable' where sip_user='%q' and sip_host='%q' and call_id='%q'",
sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
switch_safe_free(sql);
sofia_reg_fire_custom_sip_user_state_event(profile, sip_user, sip_user_status.contact, sip->sip_to->a_url->url_user,
sip->sip_to->a_url->url_host, call_id, SOFIA_REG_UNREACHABLE, status, phrase);
}
}
}
switch_safe_free(sip_user);
}
}
static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status,
char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de,
tagi_t tags[])
{
char *call_info = NULL;
if (sip && session) {
switch_channel_t *channel = switch_core_session_get_channel(session);
const char *uuid;
switch_core_session_t *other_session;
private_object_t *tech_pvt = switch_core_session_get_private(session);
char network_ip[80];
int network_port = 0;
switch_caller_profile_t *caller_profile = NULL;
int has_t38 = 0;
switch_channel_clear_flag(channel, CF_REQ_MEDIA);
if (status < 200) {
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
if(switch_core_session_compare(session, other_session)) {
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
if (sofia_test_flag(other_tech_pvt, TFLAG_3PCC)) {
sofia_set_flag_locked(tech_pvt, TFLAG_SKIP_EARLY);
}
}
switch_core_session_rwunlock(other_session);
}
if (sofia_test_flag(tech_pvt, TFLAG_SKIP_EARLY)) {
return;
}
}
if (status >= 900) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s status %d received.\n",
switch_channel_get_name(channel), status);
return;
}
if (status > 299) {
switch_channel_set_variable(channel, "sip_hangup_disposition", "recv_refuse");
}
if (status >= 400) {
char status_str[5];
switch_snprintf(status_str, sizeof(status_str), "%d", status);
switch_channel_set_variable(channel, "sip_invite_failure_status", status_str);
switch_channel_set_variable(channel, "sip_invite_failure_phrase", phrase);
switch_channel_set_variable_partner(channel, "sip_invite_failure_status", status_str);
switch_channel_set_variable_partner(channel, "sip_invite_failure_phrase", phrase);
} else {
switch_channel_set_variable_partner(channel, "sip_invite_failure_status", NULL);
switch_channel_set_variable_partner(channel, "sip_invite_failure_phrase", NULL);
}
if (status >= 400 && sip->sip_reason && sip->sip_reason->re_protocol && (!strcasecmp(sip->sip_reason->re_protocol, "Q.850")
|| !strcasecmp(sip->sip_reason->re_protocol, "FreeSWITCH")
|| !strcasecmp(sip->sip_reason->re_protocol, profile->sdp_username)) && sip->sip_reason->re_cause) {
tech_pvt->q850_cause = atoi(sip->sip_reason->re_cause);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote Reason: %d\n", tech_pvt->q850_cause);
}
sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
switch_channel_set_variable_printf(channel, "sip_local_network_addr", "%s", profile->extsipip ? profile->extsipip : profile->sipip);
switch_channel_set_variable(channel, "sip_reply_host", network_ip);
switch_channel_set_variable_printf(channel, "sip_reply_port", "%d", network_port);
switch_channel_set_variable_printf(channel, "sip_network_ip", "%s", network_ip);
switch_channel_set_variable_printf(channel, "sip_network_port", "%d", network_port);
if ((caller_profile = switch_channel_get_caller_profile(channel)) && !zstr(network_ip) &&
(zstr(caller_profile->network_addr) || strcmp(caller_profile->network_addr, network_ip))) {
caller_profile->network_addr = switch_core_strdup(caller_profile->pool, network_ip);
}
if (tech_pvt->mparams.last_sdp_response) {
tech_pvt->mparams.prev_sdp_response = tech_pvt->mparams.last_sdp_response;
}
tech_pvt->mparams.last_sdp_response = NULL;
if (sip->sip_payload && sip->sip_payload->pl_data) {
switch_core_media_set_sdp_codec_string(session, sip->sip_payload->pl_data, SDP_TYPE_RESPONSE);
if (!zstr(tech_pvt->mparams.prev_sdp_response) && !strcmp(tech_pvt->mparams.prev_sdp_response, sip->sip_payload->pl_data)) {
tech_pvt->mparams.last_sdp_response = tech_pvt->mparams.prev_sdp_response;
} else {
tech_pvt->mparams.last_sdp_response = switch_core_session_strdup(session, sip->sip_payload->pl_data);
}
}
if (status > 299 && switch_channel_test_app_flag_key("T38", tech_pvt->channel, CF_APP_T38_REQ)) {
switch_channel_set_private(channel, "t38_options", NULL);
switch_channel_clear_app_flag_key("T38", tech_pvt->channel, CF_APP_T38);
switch_channel_clear_app_flag_key("T38", tech_pvt->channel, CF_APP_T38_REQ);
switch_channel_set_app_flag_key("T38", tech_pvt->channel, CF_APP_T38_FAIL);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s T38 invite failed\n", switch_channel_get_name(tech_pvt->channel));
}
if (sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE)) {
if (channel && sip->sip_call_info) {
char *p;
call_info = sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_call_info);
if (switch_stristr("appearance", call_info)) {
switch_channel_set_variable(channel, "presence_call_info_full", call_info);
if ((p = strchr(call_info, ';'))) {
switch_channel_set_variable(channel, "presence_call_info", p + 1);
}
}
} else if ((status == 180 || status == 183 || status == 200)) {
char buf[128] = "";
char *sql;
char *state = "active";
if (status != 200) {
state = "progressing";
}
if (sip->sip_from && sip->sip_from->a_url->url_user && sip->sip_from->a_url->url_host &&
sip->sip_to && sip->sip_to->a_url->url_user && sip->sip_to->a_url->url_host) {
sql =
switch_mprintf("select 'appearance-index=1' from sip_subscriptions where expires > -1 and hostname='%q' and event='call-info' and "
"sub_to_user='%q' and sub_to_host='%q'", mod_sofia_globals.hostname, sip->sip_to->a_url->url_user,
sip->sip_from->a_url->url_host);
sofia_glue_execute_sql2str(profile, profile->dbh_mutex, sql, buf, sizeof(buf));
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "QUERY SQL %s [%s]\n", sql, buf);
}
free(sql);
if (!zstr(buf)) {
sql = switch_mprintf("update sip_dialogs set call_info='%q',call_info_state='%q' "
"where uuid='%q'", buf, state, switch_core_session_get_uuid(session));
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "QUERY SQL %s\n", sql);
}
sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Auto-Fixing Broken SLA [<sip:%s>;%s]\n",
sip->sip_from->a_url->url_host, buf);
switch_channel_set_variable_printf(channel, "presence_call_info_full", "<sip:%s>;%s", sip->sip_from->a_url->url_host, buf);
switch_channel_set_variable(channel, "presence_call_info", buf);
}
}
}
}
if ((status == 180 || status == 183 || status > 199)) {
const char *vval;
sofia_set_accept_language_channel_variable(channel, sip);
if (status > 199) {
sofia_glue_set_extra_headers(session, sip, SOFIA_SIP_RESPONSE_HEADER_PREFIX);
} else {
sofia_glue_set_extra_headers(session, sip, SOFIA_SIP_PROGRESS_HEADER_PREFIX);
}
if (!(vval = switch_channel_get_variable(channel, "sip_copy_custom_headers")) || switch_true(vval)) {
switch_core_session_t *other_session;
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
if (status > 199) {
switch_ivr_transfer_variable(session, other_session, SOFIA_SIP_RESPONSE_HEADER_PREFIX_T);
} else {
switch_ivr_transfer_variable(session, other_session, SOFIA_SIP_PROGRESS_HEADER_PREFIX_T);
}
switch_core_session_rwunlock(other_session);
}
}
}
if ((status == 180 || status == 183 || status == 200)) {
const char *x_freeswitch_support;
switch_channel_set_flag(channel, CF_MEDIA_ACK);
sofia_glue_store_session_id(session, profile, sip, 1);
if ((x_freeswitch_support = sofia_glue_get_unknown_header(sip, "X-FS-Support"))) {
tech_pvt->x_freeswitch_support_remote = switch_core_session_strdup(session, x_freeswitch_support);
}
if (sip->sip_user_agent && sip->sip_user_agent->g_string) {
switch_channel_set_variable(channel, "sip_user_agent", sip->sip_user_agent->g_string);
} else if (sip->sip_server && sip->sip_server->g_string) {
switch_channel_set_variable(channel, "sip_user_agent", sip->sip_server->g_string);
}
sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_allow, "sip_allow");
sofia_update_callee_id(session, profile, sip, SWITCH_FALSE);
if (sofia_test_media_flag(tech_pvt->profile, SCMF_AUTOFIX_TIMING)) {
switch_core_media_reset_autofix(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO);
}
}
if (channel && (status == 300 || status == 301 || status == 302 || status == 305) && switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_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;
const char *v;
if ((v = switch_channel_get_variable(channel, "outbound_redirect_fatal")) && switch_true(v)) {
su_home_t *home = su_home_new(sizeof(*home));
switch_assert(home != NULL);
for (p_contact = sip->sip_contact; p_contact; p_contact = p_contact->m_next) {
full_contact = sip_header_as_string(home, (void *) p_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(channel, var_name, full_contact);
if (i == 0) {
switch_channel_set_variable(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);
}
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);
}
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);
}
free(invite_contact);
i++;
}
su_home_unref(home);
home = NULL;
switch_snprintf(var_name, sizeof(var_name), "sip:%d", status);
switch_channel_set_variable(channel, SWITCH_PROTO_SPECIFIC_HANGUP_CAUSE_VARIABLE, var_name);
switch_channel_hangup(channel, SWITCH_CAUSE_REQUESTED_CHAN_UNAVAIL);
goto end;
}
if (!p_contact) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Missing contact header in redirect request\n");
goto end;
}
if ((br = switch_channel_get_partner_uuid(channel))) {
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) {
full_contact = sip_header_as_string(home, (void *) p_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_printf(channel, "sip_redirect_count", "%d", i);
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)) {
if (!(v = switch_channel_get_variable(channel, "outbound_redirect_info"))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Redirect: Transfering to %s %s %s\n",
p_contact->m_url->url_user, sip_redirect_dialplan, sip_redirect_context);
if (switch_true(switch_channel_get_variable(channel, "recording_follow_transfer"))) {
switch_ivr_transfer_recordings(session, a_session);
}
switch_ivr_session_transfer(a_session, p_contact->m_url->url_user, sip_redirect_dialplan, sip_redirect_context);
}
switch_channel_hangup(channel, SWITCH_CAUSE_REDIRECTION_TO_NEW_DESTINATION);
} else if( !p_contact->m_url->url_host ) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Received redirect with invalid URI\n");
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "REDIRECT_ERROR");
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
} else if ((!strcmp(profile->sipip, p_contact->m_url->url_host))
|| (profile->extsipip && !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_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Redirect: Transfering to %s\n",
p_contact->m_url->url_user);
if (switch_true(switch_channel_get_variable(channel, "recording_follow_transfer"))) {
switch_ivr_transfer_recordings(session, a_session);
}
switch_ivr_session_transfer(a_session, p_contact->m_url->url_user, NULL, NULL);
switch_channel_hangup(channel, SWITCH_CAUSE_REDIRECTION_TO_NEW_DESTINATION);
switch_xml_free(root);
} else {
invite_contact = sofia_glue_strip_uri(full_contact);
tech_pvt->redirected = switch_core_session_strdup(session, invite_contact);
free(invite_contact);
}
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);
switch_channel_set_variable(channel, "sip_redirected_to", invite_contact);
tech_pvt->redirected = switch_core_session_strdup(session, invite_contact);
free(invite_contact);
su_home_unref(home);
home = NULL;
}
}
if (sip->sip_payload && sip->sip_payload->pl_data && switch_stristr("m=image", sip->sip_payload->pl_data)) {
has_t38 = 1;
}
if (switch_channel_test_flag(channel, CF_PROXY_MODE)) {
switch_channel_clear_flag(channel, CF_T38_PASSTHRU);
has_t38 = 0;
}
if (switch_channel_test_flag(channel, CF_PROXY_MEDIA) && has_t38) {
if (switch_core_media_ready(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO)) {
switch_core_media_udptl_mode(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO);
if ((uuid = switch_channel_get_partner_uuid(channel)) && (other_session = switch_core_session_locate(uuid))) {
if (switch_core_session_compare(session, other_session)) {
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
if (switch_core_media_ready(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO)) {
switch_core_media_udptl_mode(other_tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO);
}
}
switch_core_session_rwunlock(other_session);
}
}
has_t38 = 0;
}
if (status > 199 && (switch_channel_test_flag(channel, CF_PROXY_MODE) ||
switch_channel_test_flag(channel, CF_PROXY_MEDIA) ||
(switch_channel_test_flag(tech_pvt->channel, CF_T38_PASSTHRU) && (has_t38 || status > 299)))) {
if (sofia_test_flag(tech_pvt, TFLAG_SENT_UPDATE)) {
sofia_clear_flag_locked(tech_pvt, TFLAG_SENT_UPDATE);
if ((uuid = switch_channel_get_partner_uuid(channel)) && (other_session = switch_core_session_locate(uuid))) {
const char *r_sdp = NULL;
switch_core_session_message_t *msg;
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
//switch_channel_t *other_channel = switch_core_session_get_channel(other_session);
if (sip->sip_payload && sip->sip_payload->pl_data &&
sip->sip_content_type && sip->sip_content_type->c_subtype && switch_stristr("sdp", sip->sip_content_type->c_subtype)) {
tech_pvt->mparams.remote_sdp_str = switch_core_session_strdup(tech_pvt->session, sip->sip_payload->pl_data);
r_sdp = tech_pvt->mparams.remote_sdp_str;
switch_core_media_proxy_remote_addr(session, NULL);
}
if (status == 415) {
int new_status = 488;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Overriding %d %s with %d\n", status, phrase, new_status);
status = new_status;
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Passing %d %s to other leg\n%s\n", status, phrase, switch_str_nil(r_sdp));
if (switch_core_session_compare(session, other_session)) {
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
sofia_set_flag(other_tech_pvt, TFLAG_PASS_ACK);
}
if (status == 491 && (switch_channel_test_flag(tech_pvt->channel, CF_T38_PASSTHRU) ||
switch_channel_test_flag(channel, CF_PROXY_MODE))) {
nua_respond(other_tech_pvt->nh, SIP_491_REQUEST_PENDING, TAG_END());
switch_core_session_rwunlock(other_session);
goto end;
} else if (status > 299) {
switch_core_media_reset_t38(session);
switch_core_media_reset_t38(other_session);
} else if (status == 200 && switch_channel_test_flag(channel, CF_T38_PASSTHRU) &&
has_t38 && sip->sip_payload && sip->sip_payload->pl_data) {
switch_t38_options_t *t38_options = switch_core_media_extract_t38_options(session, sip->sip_payload->pl_data);
if (!t38_options) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Could not parse T.38 options from sdp.\n");
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "T.38 NEGOTIATION ERROR");
switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
switch_core_session_rwunlock(other_session);
goto end;
} else {
switch_core_media_process_t38_passthru(session, other_session, t38_options);
}
}
msg = switch_core_session_alloc(other_session, sizeof(*msg));
msg->message_id = SWITCH_MESSAGE_INDICATE_RESPOND;
msg->from = __FILE__;
msg->numeric_arg = status;
msg->string_arg = switch_core_session_strdup(other_session, phrase);
if (status == 200 && switch_channel_test_flag(tech_pvt->channel, CF_T38_PASSTHRU) && has_t38) {
msg->pointer_arg = switch_core_session_strdup(other_session, "t38");
} else if (r_sdp) {
msg->pointer_arg = switch_core_session_strdup(other_session, r_sdp);
msg->pointer_arg_size = strlen(r_sdp);
}
if (status == 200 && switch_channel_test_flag(channel, CF_T38_PASSTHRU) && has_t38) {
if (switch_core_media_ready(session, SWITCH_MEDIA_TYPE_AUDIO) &&
switch_core_media_ready(other_session, SWITCH_MEDIA_TYPE_AUDIO)) {
switch_channel_clear_flag(channel, CF_NOTIMER_DURING_BRIDGE);
switch_core_media_udptl_mode(session, SWITCH_MEDIA_TYPE_AUDIO);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Activating T38 Passthru\n");
}
}
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
}
goto end;
}
}
if ((status == 180 || status == 183 || status == 200)) {
const char *astate = "early";
url_t *from = NULL, *to = NULL, *contact = NULL;
if (sip->sip_to) {
to = sip->sip_to->a_url;
}
if (sip->sip_from) {
from = sip->sip_from->a_url;
}
if (sip->sip_contact) {
contact = sip->sip_contact->m_url;
}
if (status == 200) {
astate = "confirmed";
}
if ((!switch_channel_test_flag(channel, CF_EARLY_MEDIA) && !switch_channel_test_flag(channel, CF_ANSWERED) &&
!switch_channel_test_flag(channel, CF_RING_READY)) || switch_channel_test_flag(channel, CF_RECOVERING)) {
const char *from_user = "", *from_host = "", *to_user = "", *to_host = "", *contact_user = "", *contact_host = "";
const char *user_agent = "", *call_id = "";
const char *to_tag = "";
const char *from_tag = "";
char *sql = NULL;
if (sip->sip_user_agent) {
user_agent = switch_str_nil(sip->sip_user_agent->g_string);
}
if (sip->sip_call_id) {
call_id = switch_str_nil(sip->sip_call_id->i_id);
}
if (to) {
from_user = switch_str_nil(to->url_user);
from_tag = switch_str_nil(sip->sip_to->a_tag);
}
if (from) {
from_host = switch_str_nil(from->url_host);
to_user = switch_str_nil(from->url_user);
to_host = switch_str_nil(from->url_host);
to_tag = switch_str_nil(sip->sip_from->a_tag);
}
if (contact) {
contact_user = switch_str_nil(contact->url_user);
contact_host = switch_str_nil(contact->url_host);
}
if (profile->pres_type) {
const char *presence_data = switch_channel_get_variable(channel, "presence_data");
const char *presence_id = switch_channel_get_variable(channel, "presence_id");
char *full_contact = NULL;
char *p = NULL;
time_t now;
if (sip->sip_contact) {
full_contact = sip_header_as_string(nua_handle_home(tech_pvt->nh), (void *) sip->sip_contact);
}
if (call_info && (p = strchr(call_info, ';'))) {
p++;
}
now = switch_epoch_time_now(NULL);
sql = switch_mprintf("insert into sip_dialogs "
"(call_id,uuid,sip_to_user,sip_to_host,sip_to_tag,sip_from_user,sip_from_host,sip_from_tag,contact_user,"
"contact_host,state,direction,user_agent,profile_name,hostname,contact,presence_id,presence_data,"
"call_info,rcd,call_info_state) "
"values('%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q',%ld,'')",
call_id,
switch_core_session_get_uuid(session),
to_user, to_host, to_tag, from_user, from_host, from_tag, contact_user,
contact_host, astate, "outbound", user_agent,
profile->name, mod_sofia_globals.hostname, switch_str_nil(full_contact),
switch_str_nil(presence_id), switch_str_nil(presence_data), switch_str_nil(p), (long) now);
switch_assert(sql);
sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
if ( full_contact ) {
su_free(nua_handle_home(tech_pvt->nh), full_contact);
}
}
} else if (status == 200 && (profile->pres_type)) {
char *sql = NULL;
const char *presence_data = switch_channel_get_variable(channel, "presence_data");
const char *presence_id = switch_channel_get_variable(channel, "presence_id");
sql = switch_mprintf("update sip_dialogs set state='%q',presence_id='%q',presence_data='%q' "
"where uuid='%q';\n", astate, switch_str_nil(presence_id), switch_str_nil(presence_data),
switch_core_session_get_uuid(session));
switch_assert(sql);
sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
}
extract_header_vars(profile, sip, session, nh);
extract_vars(profile, sip, session);
switch_channel_clear_flag(tech_pvt->channel, CF_RECOVERING);
}
}
end:
if (call_info) {
su_free(nua_handle_home(nh), call_info);
}
if (!session && (status == 180 || status == 183 || status == 200)) {
/* nevermind */
nua_handle_bind(nh, NULL);
nua_handle_destroy(nh);
}
}
/* Pure black magic, if you can't understand this code you are lucky.........*/
void *SWITCH_THREAD_FUNC media_on_hold_thread_run(switch_thread_t *thread, void *obj)
{
switch_core_session_t *other_session = NULL, *session = (switch_core_session_t *) obj;
const char *uuid;
if (switch_core_session_read_lock(session) == SWITCH_STATUS_SUCCESS) {
switch_channel_t *channel = switch_core_session_get_channel(session);
private_object_t *tech_pvt = switch_core_session_get_private(session);
if ((uuid = switch_channel_get_partner_uuid(channel)) && (other_session = switch_core_session_locate(uuid))) {
if (switch_core_session_compare(session, other_session)) {
switch_channel_t *other_channel = switch_core_session_get_channel(other_session);
sofia_set_flag_locked(tech_pvt, TFLAG_HOLD_LOCK);
switch_yield(250000);
switch_channel_wait_for_flag(channel, CF_MEDIA_ACK, SWITCH_TRUE, 10000, NULL);
switch_channel_wait_for_flag(other_channel, CF_MEDIA_ACK, SWITCH_TRUE, 10000, NULL);
if (switch_channel_direction(tech_pvt->channel) == SWITCH_CALL_DIRECTION_INBOUND) {
switch_ivr_3p_media(switch_core_session_get_uuid(other_session), SMF_REBRIDGE|SMF_REPLYONLY_B);
} else {
switch_ivr_3p_media(switch_core_session_get_uuid(other_session), SMF_REBRIDGE);
}
switch_core_media_clear_rtp_flag(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_AUTOADJ);
switch_core_media_clear_rtp_flag(other_session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_AUTOADJ);
switch_core_media_toggle_hold(session, 1);
}
switch_core_session_rwunlock(other_session);
}
switch_core_session_rwunlock(session);
}
return NULL;
}
static void launch_media_on_hold(switch_core_session_t *session)
{
switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL;
switch_threadattr_create(&thd_attr, switch_core_session_get_pool(session));
switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
switch_thread_create(&thread, thd_attr, media_on_hold_thread_run, session, switch_core_session_get_pool(session));
}
static void mark_transfer_record(switch_core_session_t *session, const char *br_a, const char *br_b)
{
switch_core_session_t *br_b_session, *br_a_session;
switch_channel_t *channel;
const char *uvar1, *dvar1, *uvar2, *dvar2;
channel = switch_core_session_get_channel(session);
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
uvar1 = "sip_from_user";
dvar1 = "sip_from_host";
} else {
uvar1 = "sip_to_user";
dvar1 = "sip_to_host";
}
if ((br_b_session = switch_core_session_locate(br_b)) ) {
switch_channel_t *br_b_channel = switch_core_session_get_channel(br_b_session);
switch_caller_profile_t *cp = switch_channel_get_caller_profile(br_b_channel);
if (switch_channel_direction(br_b_channel) == SWITCH_CALL_DIRECTION_INBOUND) {
uvar2 = "sip_from_user";
dvar2 = "sip_from_host";
} else {
uvar2 = "sip_to_user";
dvar2 = "sip_to_host";
}
cp->transfer_source = switch_core_sprintf(cp->pool,
"%ld:%s:att_xfer:%s@%s/%s@%s",
(long) switch_epoch_time_now(NULL),
cp->uuid_str,
switch_channel_get_variable(channel, uvar1),
switch_channel_get_variable(channel, dvar1),
switch_channel_get_variable(br_b_channel, uvar2),
switch_channel_get_variable(br_b_channel, dvar2));
switch_channel_add_variable_var_check(br_b_channel, SWITCH_TRANSFER_HISTORY_VARIABLE, cp->transfer_source, SWITCH_FALSE, SWITCH_STACK_PUSH);
switch_channel_set_variable(br_b_channel, SWITCH_TRANSFER_SOURCE_VARIABLE, cp->transfer_source);
switch_core_session_rwunlock(br_b_session);
}
if ((br_a_session = switch_core_session_locate(br_a)) ) {
switch_channel_t *br_a_channel = switch_core_session_get_channel(br_a_session);
switch_caller_profile_t *cp = switch_channel_get_caller_profile(br_a_channel);
if (switch_channel_direction(br_a_channel) == SWITCH_CALL_DIRECTION_INBOUND) {
uvar2 = "sip_from_user";
dvar2 = "sip_from_host";
} else {
uvar2 = "sip_to_user";
dvar2 = "sip_to_host";
}
cp->transfer_source = switch_core_sprintf(cp->pool,
"%ld:%s:att_xfer:%s@%s/%s@%s",
(long) switch_epoch_time_now(NULL),
cp->uuid_str,
switch_channel_get_variable(channel, uvar1),
switch_channel_get_variable(channel, dvar1),
switch_channel_get_variable(br_a_channel, uvar2),
switch_channel_get_variable(br_a_channel, dvar2));
switch_channel_add_variable_var_check(br_a_channel, SWITCH_TRANSFER_HISTORY_VARIABLE, cp->transfer_source, SWITCH_FALSE, SWITCH_STACK_PUSH);
switch_channel_set_variable(br_a_channel, SWITCH_TRANSFER_SOURCE_VARIABLE, cp->transfer_source);
switch_core_session_rwunlock(br_a_session);
}
}
static void sofia_handle_sip_i_state(switch_core_session_t *session, int status,
char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de,
tagi_t tags[])
{
const char *l_sdp = NULL, *r_sdp = NULL;
int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0;
int ss_state = nua_callstate_init;
switch_channel_t *channel = NULL;
private_object_t *tech_pvt = NULL;
const char *replaces_str = NULL;
switch_core_session_t *other_session = NULL;
switch_channel_t *other_channel = NULL;
//private_object_t *other_tech_pvt = NULL;
char st[80] = "";
int is_dup_sdp = 0;
switch_event_t *s_event = NULL;
char *p;
char *patched_sdp = NULL;
const char *session_id_header = sofia_glue_session_id_header(session, profile);
tl_gets(tags,
NUTAG_CALLSTATE_REF(ss_state),
NUTAG_OFFER_RECV_REF(offer_recv),
NUTAG_ANSWER_RECV_REF(answer_recv),
NUTAG_OFFER_SENT_REF(offer_sent),
NUTAG_ANSWER_SENT_REF(answer_sent),
SIPTAG_REPLACES_STR_REF(replaces_str), SOATAG_LOCAL_SDP_STR_REF(l_sdp), SOATAG_REMOTE_SDP_STR_REF(r_sdp), TAG_END());
if (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 (!r_sdp && (status == 100 || status == 200)) {
if (ss_state == nua_callstate_completing) {
if (tech_pvt->mparams.last_sdp_response) {
r_sdp = tech_pvt->mparams.last_sdp_response;
}
} else if (ss_state == nua_callstate_received || ss_state == nua_callstate_ready) {
if (tech_pvt->mparams.last_sdp_str) {
r_sdp = tech_pvt->mparams.last_sdp_str;
}
}
}
if (tech_pvt->mparams.last_sdp_str) {
tech_pvt->mparams.prev_sdp_str = tech_pvt->mparams.last_sdp_str;
}
if (tech_pvt->mparams.last_sdp_response) {
tech_pvt->mparams.prev_sdp_response = tech_pvt->mparams.last_sdp_response;
}
tech_pvt->mparams.last_sdp_str = NULL;
tech_pvt->mparams.last_sdp_response = NULL;
if (r_sdp && (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA))) {
const char *var;
if ((var = switch_channel_get_variable(channel, "bypass_media_sdp_filter"))) {
if ((patched_sdp = switch_core_media_process_sdp_filter(r_sdp, var, session))) {
r_sdp = patched_sdp;
}
}
}
if ((switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) ||
(sofia_test_flag(profile, TFLAG_INB_NOMEDIA) || sofia_test_flag(profile, TFLAG_PROXY_MEDIA))) {
/* This marr in our code brought to you by people who can't read........ */
if (profile->ndlb & SM_NDLB_ALLOW_BAD_IANANAME && r_sdp && (p = (char *) switch_stristr("g729a/8000", r_sdp))) {
p += 4;
*p++ = '/';
*p++ = '8';
*p++ = '0';
*p++ = '0';
*p++ = '0';
*p++ = ' ';
}
}
}
if (ss_state == nua_callstate_terminated) {
if ((status == 300 || status == 301 || 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;
}
}
if (session) {
if ((switch_channel_test_flag(channel, CF_ANSWERED) && (status == 180 || status == 183) && !r_sdp) || (ss_state == nua_callstate_ready && status >= 300)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Channel %s skipping state [%s][%d]\n",
switch_channel_get_name(channel), nua_callstate_name(ss_state), status);
goto done;
} else if (switch_channel_test_flag(channel, CF_EARLY_MEDIA) && (status == 180 || status == 183) && r_sdp) {
switch_channel_set_flag(tech_pvt->channel, CF_REINVITE);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Channel %s entering state [%s][%d]\n",
switch_channel_get_name(channel), nua_callstate_name(ss_state), status);
if (r_sdp) {
switch_channel_set_variable(channel, SWITCH_R_SDP_VARIABLE, r_sdp);
if (!(profile->mndlb & SM_NDLB_ALLOW_NONDUP_SDP) || (!zstr(tech_pvt->mparams.remote_sdp_str) && !strcmp(tech_pvt->mparams.remote_sdp_str, r_sdp))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Duplicate SDP\n%s\n", r_sdp);
is_dup_sdp = 1;
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Remote SDP:\n%s\n", r_sdp);
tech_pvt->mparams.remote_sdp_str = switch_core_session_strdup(session, r_sdp);
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND && status < 200) {
switch_channel_mark_pre_answered(channel);
}
//if ((sofia_test_flag(tech_pvt, TFLAG_LATE_NEGOTIATION) || switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND)) {
// switch_core_media_set_sdp_codec_string(session, r_sdp, status < 200 ? SDP_TYPE_REQUEST : SDP_TYPE_RESPONSE);
//}
switch_core_media_set_sdp_codec_string(session, r_sdp, SDP_TYPE_REQUEST);
sofia_glue_pass_sdp(tech_pvt, (char *) r_sdp);
sofia_set_flag(tech_pvt, TFLAG_NEW_SDP);
}
}
}
if (status == 988) {
goto done;
}
if (status == 183 && !r_sdp) {
if ((channel && switch_true(switch_channel_get_variable(channel, "sip_ignore_183nosdp"))) || sofia_test_pflag(profile, PFLAG_IGNORE_183NOSDP)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Ignoring 183 w/o sdp\n", channel ? switch_channel_get_name(channel) : "None");
goto done;
}
status = 180;
}
if (status == 180 && r_sdp) {
status = 183;
}
if (channel && profile->pres_type && ss_state == nua_callstate_ready && status == 200) {
const char* to_tag = "";
char *sql = NULL;
to_tag = switch_str_nil(switch_channel_get_variable(channel, "sip_to_tag"));
sql = switch_mprintf("update sip_dialogs set sip_to_tag='%q' "
"where uuid='%q' and sip_to_tag = ''", to_tag, switch_core_session_get_uuid(session));
if (mod_sofia_globals.debug_presence > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "QUERY SQL %s\n", sql);
}
sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
}
if (channel && (status == 180 || status == 183) && switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
const char *full_to = NULL;
const char *var;
if ((var = switch_channel_get_variable(channel, "sip_auto_answer")) && switch_true(var) &&
!((var = switch_channel_get_variable(channel, "sip_auto_answer_suppress_notify")) && switch_true(var))) {
full_to = switch_str_nil(switch_channel_get_variable(channel, "sip_full_to"));
nua_notify(nh,
NUTAG_NEWSUB(1),
NUTAG_WITH_THIS_MSG(de->data->e_msg),
NUTAG_SUBSTATE(nua_substate_terminated),
TAG_IF((full_to), SIPTAG_TO_STR(full_to)),
SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"),
SIPTAG_EVENT_STR("talk"),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
}
state_process:
switch ((enum nua_callstate) ss_state) {
case nua_callstate_terminated:
case nua_callstate_terminating:
case nua_callstate_ready:
case nua_callstate_completed:
case nua_callstate_received:
case nua_callstate_proceeding:
case nua_callstate_completing:
case nua_callstate_calling:
if (!(session && channel && tech_pvt))
goto done;
default:
break;
}
switch ((enum nua_callstate) ss_state) {
case nua_callstate_init:
break;
case nua_callstate_authenticating:
break;
case nua_callstate_calling:
tech_pvt->sent_last_invite = 1;
tech_pvt->sent_invites++;
break;
case nua_callstate_proceeding:
if (sofia_test_flag(tech_pvt, TFLAG_SKIP_EARLY)) {
sofia_clear_flag_locked(tech_pvt, TFLAG_SKIP_EARLY);
goto done;
}
switch (status) {
case 180:
switch_channel_mark_ring_ready(channel);
break;
case 182:
switch_channel_mark_ring_ready_value(channel, SWITCH_RING_READY_QUEUED);
break;
default:
break;
}
if (r_sdp) {
if (switch_channel_test_flag(channel, CF_PROXY_MODE)) {
char ibuf[35] = "", pbuf[35] = "";
const char *ptr;
if ((ptr = switch_stristr("c=IN IP4", r_sdp))) {
int i = 0;
ptr += 8;
while(*ptr == ' ') {
ptr++;
}
while(*ptr && *ptr != ' ' && *ptr != '\r' && *ptr != '\n') {
ibuf[i++] = *ptr++;
}
switch_channel_set_variable(channel, SWITCH_REMOTE_MEDIA_IP_VARIABLE, ibuf);
}
if ((ptr = switch_stristr("m=audio", r_sdp))) {
int i = 0;
ptr += 7;
while(*ptr == ' ') {
ptr++;
}
while(*ptr && *ptr != ' ' && *ptr != '\r' && *ptr != '\n') {
pbuf[i++] = *ptr++;
}
switch_channel_set_variable(channel, SWITCH_REMOTE_MEDIA_PORT_VARIABLE, pbuf);
}
if (switch_channel_test_flag(channel, CF_PROXY_MEDIA) && switch_channel_direction(tech_pvt->channel) == SWITCH_CALL_DIRECTION_INBOUND) {
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "PROXY MEDIA");
}
sofia_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA);
switch_channel_mark_pre_answered(channel);
sofia_set_flag(tech_pvt, TFLAG_SDP);
if (switch_channel_test_flag(channel, CF_PROXY_MEDIA) || switch_channel_test_flag(channel, CF_REINVITE)) {
if (sofia_media_activate_rtp(tech_pvt) != SWITCH_STATUS_SUCCESS) {
goto done;
}
}
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
other_channel = switch_core_session_get_channel(other_session);
switch_channel_pass_sdp(channel, other_channel, r_sdp);
//switch_channel_pre_answer(other_channel);
switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_PROGRESS);
switch_core_session_rwunlock(other_session);
}
goto done;
} else {
if (sofia_test_flag(tech_pvt, TFLAG_LATE_NEGOTIATION) && switch_channel_direction(tech_pvt->channel) == SWITCH_CALL_DIRECTION_INBOUND) {
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "DELAYED NEGOTIATION");
} else if (switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "PROXY MEDIA");
switch_core_media_patch_sdp(tech_pvt->session);
if (sofia_media_activate_rtp(tech_pvt) != SWITCH_STATUS_SUCCESS) {
nua_respond(nh, SIP_488_NOT_ACCEPTABLE,
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
} else{
switch_channel_mark_pre_answered(channel);
}
} else {
if (sofia_media_tech_media(tech_pvt, (char *) r_sdp, SDP_TYPE_REQUEST) != SWITCH_STATUS_SUCCESS) {
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR");
nua_respond(nh, SIP_488_NOT_ACCEPTABLE,
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),TAG_END());
switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
}
}
goto done;
}
}
break;
case nua_callstate_completing:
{
int send_ack = 1;
if (!switch_channel_test_flag(channel, CF_ANSWERED)) {
const char *wait_for_ack = switch_channel_get_variable(channel, "sip_wait_for_aleg_ack");
if (switch_true(wait_for_ack)) {
switch_core_session_t *other_session;
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
if (switch_core_session_compare(session, other_session)) {
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
if (!sofia_test_flag(tech_pvt, TFLAG_3PCC) && !sofia_test_flag(other_tech_pvt, TFLAG_3PCC)) {
sofia_set_flag(other_tech_pvt, TFLAG_PASS_ACK);
send_ack = 0;
}
}
switch_core_session_rwunlock(other_session);
}
}
}
if (switch_channel_test_flag(channel, CF_3P_NOMEDIA_REQUESTED)) {
if (switch_channel_test_flag(channel, CF_3P_NOMEDIA_REQUESTED_BLEG)) {
switch_core_session_t *other_session;
switch_channel_clear_flag(channel, CF_3P_NOMEDIA_REQUESTED_BLEG);
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
if (switch_core_session_compare(session, other_session)) {
//switch_channel_t *other_channel = switch_core_session_get_channel(other_session);
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
sofia_glue_clear_soa(other_session, SWITCH_TRUE);
if (sofia_use_soa(other_tech_pvt)) {
nua_ack(other_tech_pvt->nh,
TAG_IF(!zstr(other_tech_pvt->user_via), SIPTAG_VIA_STR(other_tech_pvt->user_via)),
SIPTAG_CONTACT_STR(other_tech_pvt->reply_contact),
SOATAG_USER_SDP_STR(r_sdp),
SOATAG_REUSE_REJECTED(1),
SOATAG_RTP_SELECT(1),
SOATAG_AUDIO_AUX("cn telephone-event"),
TAG_IF(sofia_test_pflag(other_tech_pvt->profile, PFLAG_DISABLE_100REL), NUTAG_INCLUDE_EXTRA_SDP(1)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
nua_ack(other_tech_pvt->nh,
NUTAG_MEDIA_ENABLE(0),
TAG_IF(!zstr(other_tech_pvt->user_via), SIPTAG_VIA_STR(other_tech_pvt->user_via)),
SIPTAG_CONTACT_STR(other_tech_pvt->reply_contact),
TAG_IF(r_sdp, SIPTAG_CONTENT_TYPE_STR("application/sdp")),
TAG_IF(r_sdp, SIPTAG_PAYLOAD_STR(r_sdp)),
SOATAG_AUDIO_AUX("cn telephone-event"),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
nua_ack(tech_pvt->nh,
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
switch_core_session_rwunlock(other_session);
}
} else {
switch_channel_set_variable(channel, SWITCH_R_SDP_VARIABLE, r_sdp);
}
switch_channel_clear_flag(channel, CF_3P_NOMEDIA_REQUESTED);
goto done;
} else if (switch_channel_test_flag(channel, CF_3P_MEDIA_REQUESTED)) {
uint8_t match = 0;
switch_channel_clear_flag(channel, CF_PROXY_MODE);
switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0);
switch_core_media_prepare_codecs(tech_pvt->session, SWITCH_FALSE);
if (tech_pvt->mparams.num_codecs) {
match = sofia_media_negotiate_sdp(session, r_sdp, SDP_TYPE_REQUEST);
}
if (!match) {
if (switch_channel_get_state(channel) != CS_NEW) {
nua_respond(tech_pvt->nh, SIP_488_NOT_ACCEPTABLE,
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),TAG_END());
}
} else {
switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 0);
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED");
sofia_set_flag_locked(tech_pvt, TFLAG_READY);
sofia_set_flag(tech_pvt, TFLAG_SDP);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "RESTABLISH MEDIA SDP:\n%s\n", tech_pvt->mparams.local_sdp_str);
switch_channel_set_flag(channel, CF_REQ_MEDIA);
switch_channel_set_flag(channel, CF_MEDIA_ACK);
switch_channel_set_flag(channel, CF_MEDIA_SET);
switch_core_media_activate_rtp(session);
if (sofia_use_soa(tech_pvt)) {
nua_ack(tech_pvt->nh,
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SOATAG_USER_SDP_STR(tech_pvt->mparams.local_sdp_str),
SOATAG_REUSE_REJECTED(1),
SOATAG_RTP_SELECT(1),
SOATAG_AUDIO_AUX("cn telephone-event"),
TAG_IF(sofia_test_pflag(tech_pvt->profile, PFLAG_DISABLE_100REL), NUTAG_INCLUDE_EXTRA_SDP(1)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
nua_ack(tech_pvt->nh,
NUTAG_MEDIA_ENABLE(0),
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
TAG_IF(tech_pvt->mparams.local_sdp_str, SIPTAG_CONTENT_TYPE_STR("application/sdp")),
TAG_IF(tech_pvt->mparams.local_sdp_str, SIPTAG_PAYLOAD_STR(tech_pvt->mparams.local_sdp_str)),
SOATAG_AUDIO_AUX("cn telephone-event"),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
switch_channel_clear_flag(channel, CF_3P_MEDIA_REQUESTED);
goto done;
}
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "NO CODECS");
switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
switch_channel_clear_flag(channel, CF_3P_MEDIA_REQUESTED);
goto done;
//ss_state = nua_callstate_ready;
//goto state_process;
}
if (r_sdp && sofia_test_flag(tech_pvt, TFLAG_3PCC_INVITE) && !sofia_test_flag(tech_pvt, TFLAG_SDP)) {
sofia_set_flag(tech_pvt, TFLAG_SDP);
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
other_channel = switch_core_session_get_channel(other_session);
//other_tech_pvt = switch_core_session_get_private(other_session);
switch_channel_pass_sdp(channel, other_channel, r_sdp);
switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_ANSWER);
switch_core_session_rwunlock(other_session);
}
goto done;
}
if (send_ack) {
tech_send_ack(nh, tech_pvt, NULL);
} else {
ss_state = nua_callstate_ready;
goto state_process;
}
}
goto done;
case nua_callstate_received:
tech_pvt->recv_invites++;
tech_pvt->sent_last_invite = 0;
if (!sofia_test_flag(tech_pvt, TFLAG_SDP)) {
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
int r = sofia_test_flag(other_tech_pvt, TFLAG_REINVITED);
switch_core_session_rwunlock(other_session);
if (r) {
/* Due to a race between simultaneous reinvites to both legs of a bridge,
an earlier call to nua_invite silently failed.
So we reject the incoming invite with a 491 and redo the failed outgoing invite. */
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
"Other leg already handling a reinvite, so responding with 491\n");
nua_respond(tech_pvt->nh, SIP_491_REQUEST_PENDING,
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
sofia_glue_do_invite(session);
goto done;
}
}
if (r_sdp && !sofia_test_flag(tech_pvt, TFLAG_SDP)) {
if (switch_channel_test_flag(channel, CF_PROXY_MODE)) {
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOMEDIA");
sofia_set_flag_locked(tech_pvt, TFLAG_READY);
if (switch_channel_get_state(channel) == CS_NEW) {
switch_channel_set_state(channel, CS_INIT);
}
sofia_set_flag(tech_pvt, TFLAG_SDP);
goto done;
} else if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MEDIA)) {
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "PROXY MEDIA");
sofia_set_flag_locked(tech_pvt, TFLAG_READY);
if (switch_channel_get_state(channel) == CS_NEW) {
switch_channel_set_state(channel, CS_INIT);
}
} else if (sofia_test_flag(tech_pvt, TFLAG_LATE_NEGOTIATION)) {
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "DELAYED NEGOTIATION");
sofia_set_flag_locked(tech_pvt, TFLAG_READY);
if (switch_channel_get_state(channel) == CS_NEW) {
switch_channel_set_state(channel, CS_INIT);
}
} else {
uint8_t match = 0;
if (tech_pvt->mparams.num_codecs) {
match = sofia_media_negotiate_sdp(session, r_sdp, SDP_TYPE_REQUEST);
}
if (!match) {
if (switch_channel_get_state(channel) != CS_NEW) {
nua_respond(tech_pvt->nh, SIP_488_NOT_ACCEPTABLE, TAG_END());
}
} else {
nua_handle_t *bnh;
sip_replaces_t *replaces;
su_home_t *home = NULL;
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED");
sofia_set_flag_locked(tech_pvt, TFLAG_READY);
if (switch_channel_get_state(channel) == CS_NEW) {
switch_channel_set_state(channel, CS_INIT);
} else {
nua_respond(tech_pvt->nh, SIP_200_OK, TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
}
sofia_set_flag(tech_pvt, TFLAG_SDP);
if (replaces_str) {
home = su_home_new(sizeof(*home));
switch_assert(home != NULL);
if ((replaces = sip_replaces_make(home, replaces_str))
&& (bnh = nua_handle_by_replaces(nua, replaces))) {
sofia_private_t *b_private;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Processing Replaces Attended Transfer\n");
while (switch_channel_get_state(channel) < CS_EXECUTE) {
switch_yield(10000);
}
if ((b_private = nua_handle_magic(bnh))) {
const char *br_b = switch_channel_get_partner_uuid(channel);
char *br_a = b_private->uuid;
if (br_b) {
switch_core_session_t *tmp;
if (switch_true(switch_channel_get_variable(channel, "recording_follow_transfer")) &&
(tmp = switch_core_session_locate(br_a))) {
switch_ivr_transfer_recordings(session, tmp);
switch_core_session_rwunlock(tmp);
}
switch_channel_set_variable_printf(channel, "transfer_to", "att:%s", br_b);
mark_transfer_record(session, br_a, br_b);
switch_ivr_uuid_bridge(br_a, br_b);
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER");
sofia_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD);
switch_channel_clear_flag(channel, CF_LEG_HOLDING);
sofia_clear_flag_locked(tech_pvt, TFLAG_HOLD_LOCK);
switch_channel_hangup(channel, SWITCH_CAUSE_ATTENDED_TRANSFER);
} else {
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER_ERROR");
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
} else {
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER_ERROR");
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
nua_handle_unref(bnh);
}
su_home_unref(home);
home = NULL;
}
goto done;
}
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "NO CODECS");
switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
}
} else {
if (sofia_test_pflag(profile, PFLAG_3PCC)) {
if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "No SDP in INVITE and 3pcc=yes cannot work with bypass or proxy media, hanging up.\n");
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "3PCC DISABLED");
switch_channel_hangup(channel, SWITCH_CAUSE_MANDATORY_IE_MISSING);
} else {
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOSDP");
switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0);
switch_core_media_prepare_codecs(session, 1);
switch_channel_set_state(channel, CS_HIBERNATE);
switch_core_media_gen_local_sdp(session, SDP_TYPE_REQUEST, NULL, 0, NULL, 0);
sofia_set_flag_locked(tech_pvt, TFLAG_3PCC);
if (sofia_use_soa(tech_pvt)) {
nua_respond(tech_pvt->nh, SIP_200_OK,
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SOATAG_USER_SDP_STR(tech_pvt->mparams.local_sdp_str),
SOATAG_REUSE_REJECTED(1),
SOATAG_AUDIO_AUX("cn telephone-event"),
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_100REL), NUTAG_INCLUDE_EXTRA_SDP(1)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
nua_respond(tech_pvt->nh, SIP_200_OK,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SIPTAG_CONTENT_TYPE_STR("application/sdp"), SIPTAG_PAYLOAD_STR(tech_pvt->mparams.local_sdp_str),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
}
} else if (sofia_test_pflag(profile, PFLAG_3PCC_PROXY)) {
//3PCC proxy mode delays the 200 OK until the call is answered
// so can be made to work with bypass media as we have time to find out what the other end thinks codec offer should be...
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOSDP");
sofia_set_flag_locked(tech_pvt, TFLAG_3PCC);
//switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0);
//switch_core_media_gen_local_sdp(session, NULL, 0, NULL, 0);
sofia_set_flag(tech_pvt, TFLAG_LATE_NEGOTIATION);
//Moves into CS_INIT so call moves forward into the dialplan
switch_channel_set_state(channel, CS_INIT);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "No SDP in INVITE and 3pcc not enabled, hanging up.\n");
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "3PCC DISABLED");
switch_channel_hangup(channel, SWITCH_CAUSE_MANDATORY_IE_MISSING);
}
goto done;
}
} else if (tech_pvt && sofia_test_flag(tech_pvt, TFLAG_SDP) && !r_sdp) {
sofia_set_flag_locked(tech_pvt, TFLAG_NOSDP_REINVITE);
if ((switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) && sofia_test_pflag(profile, PFLAG_3PCC_PROXY)) {
sofia_set_flag_locked(tech_pvt, TFLAG_3PCC);
sofia_clear_flag(tech_pvt, TFLAG_ENABLE_SOA);
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
switch_core_session_message_t *msg;
if (switch_core_session_compare(session, other_session)) {
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
sofia_clear_flag(other_tech_pvt, TFLAG_ENABLE_SOA);
}
msg = switch_core_session_alloc(other_session, sizeof(*msg));
msg->message_id = SWITCH_MESSAGE_INDICATE_MEDIA_REDIRECT;
msg->from = __FILE__;
msg->string_arg = NULL;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Passing NOSDP to other leg.\n");
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
"NOSDP Re-INVITE to a proxy mode channel that is not in a bridge.\n");
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
goto done;
}
if (switch_channel_var_true(channel, "sip_unhold_nosdp")) {
switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, "sendrecv",
zstr(tech_pvt->mparams.local_sdp_str) || !switch_channel_test_flag(channel, CF_PROXY_MODE));
} else {
switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL,
zstr(tech_pvt->mparams.local_sdp_str) || !switch_channel_test_flag(channel, CF_PROXY_MODE));
}
if (zstr(tech_pvt->mparams.local_sdp_str)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Cannot find a SDP\n");
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
} else {
if (sofia_use_soa(tech_pvt)) {
nua_respond(tech_pvt->nh, SIP_200_OK,
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SOATAG_USER_SDP_STR(tech_pvt->mparams.local_sdp_str),
SOATAG_REUSE_REJECTED(1),
SOATAG_AUDIO_AUX("cn telephone-event"),
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_100REL), NUTAG_INCLUDE_EXTRA_SDP(1)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
nua_respond(tech_pvt->nh, SIP_200_OK,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SIPTAG_CONTENT_TYPE_STR("application/sdp"), SIPTAG_PAYLOAD_STR(tech_pvt->mparams.local_sdp_str),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
}
goto done;
} else {
ss_state = nua_callstate_completed;
goto state_process;
}
break;
case nua_callstate_early:
if (answer_recv) {
uint8_t match = 0;
switch_assert(tech_pvt);
sofia_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA);
switch_channel_mark_pre_answered(channel);
sofia_set_flag(tech_pvt, TFLAG_SDP);
match = sofia_media_negotiate_sdp(session, r_sdp, SDP_TYPE_RESPONSE);
if (match) {
if (switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0) != SWITCH_STATUS_SUCCESS) {
goto done;
}
switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 0);
if (sofia_media_activate_rtp(tech_pvt) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Early Media RTP Error!\n");
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Processing updated SDP\n");
} else {
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Early Media Codec Error!\n");
}
}
break;
case nua_callstate_completed:
if (r_sdp) {
const char *var;
uint8_t match = 0, is_ok = 1, is_t38 = 0;
tech_pvt->mparams.hold_laps = 0;
if ((var = switch_channel_get_variable(channel, "sip_ignore_reinvites")) && switch_true(var)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Ignoring Re-invite\n");
nua_respond(tech_pvt->nh, SIP_200_OK, TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
goto done;
}
if (switch_stristr("m=image", r_sdp)) {
is_t38 = 1;
}
if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
if ((sofia_test_media_flag(profile, SCMF_DISABLE_HOLD)
|| ((var = switch_channel_get_variable(channel, "rtp_disable_hold")) && switch_true(var)))
&& ((switch_stristr("sendonly", r_sdp) || switch_stristr("0.0.0.0", r_sdp) || switch_stristr("inactive", r_sdp)) || tech_pvt->mparams.hold_laps)) {
nua_respond(tech_pvt->nh, SIP_200_OK, TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
if (tech_pvt->mparams.hold_laps) {
tech_pvt->mparams.hold_laps = 0;
} else {
tech_pvt->mparams.hold_laps = 1;
}
goto done;
}
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
switch_core_session_message_t *msg;
private_object_t *other_tech_pvt;
int media_on_hold = switch_true(switch_channel_get_variable_dup(channel, "bypass_media_resume_on_hold", SWITCH_FALSE, -1));
switch_core_media_clear_rtp_flag(other_session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_AUTOADJ);
if (switch_channel_test_flag(channel, CF_PROXY_MODE) && !is_t38 &&
((profile->media_options & MEDIA_OPT_MEDIA_ON_HOLD) || media_on_hold)) {
if (switch_stristr("sendonly", r_sdp) || switch_stristr("0.0.0.0", r_sdp) || switch_stristr("inactive", r_sdp)) {
tech_pvt->mparams.hold_laps = 1;
switch_channel_set_variable(channel, SWITCH_R_SDP_VARIABLE, r_sdp);
switch_channel_clear_flag(channel, CF_PROXY_MODE);
switch_core_media_set_local_sdp(tech_pvt->session, NULL, SWITCH_FALSE);
switch_core_media_clear_rtp_flag(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_AUTOADJ);
if (!switch_channel_media_ready(channel)) {
//if (switch_channel_direction(tech_pvt->channel) == SWITCH_CALL_DIRECTION_INBOUND) {
//const char *r_sdp = switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE);
switch_core_media_prepare_codecs(tech_pvt->session, SWITCH_TRUE);
if (sofia_media_tech_media(tech_pvt, r_sdp, SDP_TYPE_REQUEST) != SWITCH_STATUS_SUCCESS) {
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR");
switch_core_session_rwunlock(other_session);
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
goto done;
}
//}
}
if (!switch_core_media_ready(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO)) {
switch_core_media_prepare_codecs(tech_pvt->session, SWITCH_FALSE);
if (switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0) != SWITCH_STATUS_SUCCESS) {
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
switch_core_session_rwunlock(other_session);
goto done;
}
}
switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 1);
if (sofia_use_soa(tech_pvt)) {
nua_respond(tech_pvt->nh, SIP_200_OK,
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SOATAG_USER_SDP_STR(tech_pvt->mparams.local_sdp_str),
SOATAG_REUSE_REJECTED(1),
SOATAG_AUDIO_AUX("cn telephone-event"),
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_100REL), NUTAG_INCLUDE_EXTRA_SDP(1)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
nua_respond(tech_pvt->nh, SIP_200_OK,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SIPTAG_CONTENT_TYPE_STR("application/sdp"), SIPTAG_PAYLOAD_STR(tech_pvt->mparams.local_sdp_str),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
switch_channel_set_flag(channel, CF_PROXY_MODE);
switch_yield(250000);
launch_media_on_hold(session);
switch_core_session_rwunlock(other_session);
goto done;
}
}
if (switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
sofia_media_activate_rtp(tech_pvt);
switch_core_media_proxy_remote_addr(session, r_sdp);
if ((tech_pvt->profile->mndlb & SM_NDLB_NEVER_PATCH_REINVITE)) {
nua_respond(tech_pvt->nh, SIP_200_OK, TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "NOT proxying re-invite.\n");
switch_core_session_rwunlock(other_session);
goto done;
}
}
other_tech_pvt = switch_core_session_get_private(other_session);
if (sofia_test_flag(other_tech_pvt, TFLAG_REINVITED)) {
/* The other leg won the reinvite race */
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Other leg already handling reinvite, so responding with 491\n");
nua_respond(tech_pvt->nh, SIP_491_REQUEST_PENDING,
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
switch_core_session_rwunlock(other_session);
goto done;
}
sofia_set_flag(tech_pvt, TFLAG_REINVITED);
msg = switch_core_session_alloc(other_session, sizeof(*msg));
msg->message_id = SWITCH_MESSAGE_INDICATE_MEDIA_REDIRECT;
msg->from = __FILE__;
msg->string_arg = switch_core_session_strdup(other_session, r_sdp);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Passing SDP to other leg.\n%s\n", r_sdp);
if (sofia_test_flag(tech_pvt, TFLAG_SIP_HOLD)) {
if (!switch_stristr("sendonly", r_sdp) && !switch_stristr("inactive", r_sdp)) {
sofia_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD);
switch_channel_clear_flag(channel, CF_LEG_HOLDING);
switch_channel_presence(tech_pvt->channel, "unknown", "unhold", NULL);
}
} else if (switch_stristr("sendonly", r_sdp) && !switch_stristr("inactive", r_sdp)) {
const char *msg = "hold";
if (sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE)) {
const char *info = switch_channel_get_variable(channel, "presence_call_info");
if (info) {
if (switch_stristr("private", info)) {
msg = "hold-private";
}
}
}
sofia_set_flag_locked(tech_pvt, TFLAG_SIP_HOLD);
switch_channel_set_flag(channel, CF_LEG_HOLDING);
switch_channel_presence(tech_pvt->channel, "unknown", msg, NULL);
}
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
"Re-INVITE to a no-media channel that is not in a bridge.\n");
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
goto done;
} else {
int hold_related = 0;
if (sofia_test_flag(tech_pvt, TFLAG_SIP_HOLD)) {
hold_related = 2;
} else if (switch_stristr("sendonly", r_sdp) || switch_stristr("0.0.0.0", r_sdp) || switch_stristr("inactive", r_sdp)) {
hold_related = 1;
}
if (!is_t38 && hold_related && switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
if (switch_core_session_compare(session, other_session)) {
switch_core_session_message_t *msg;
const char *hold_msg = "hold";
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
if (sofia_test_pflag(profile, PFLAG_PROXY_HOLD)) {
switch_channel_set_flag(tech_pvt->channel, CF_REINVITE);
if (tech_pvt->mparams.num_codecs){
match = sofia_media_negotiate_sdp(session, r_sdp, SDP_TYPE_REQUEST);
}
if (!match) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Reinvite Codec Error!\n");
nua_respond(tech_pvt->nh, SIP_488_NOT_ACCEPTABLE,
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
switch_core_session_rwunlock(other_session);
goto done;
}
msg = switch_core_session_alloc(other_session, sizeof(*msg));
if (switch_stristr("inactive", r_sdp)) {
sofia_set_flag_locked(other_tech_pvt, TFLAG_SIP_HOLD_INACTIVE);
//switch_channel_set_variable(channel, "sofia_hold_inactive", "true");
} else {
sofia_clear_flag_locked(other_tech_pvt, TFLAG_SIP_HOLD_INACTIVE);
}
if (hold_related == 1) {
msg->message_id = SWITCH_MESSAGE_INDICATE_HOLD;
if (sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE)) {
const char *info = switch_channel_get_variable(channel, "presence_call_info");
if (info) {
if (switch_stristr("private", info)) {
hold_msg = "hold-private";
}
}
}
sofia_set_flag_locked(tech_pvt, TFLAG_SIP_HOLD);
switch_channel_set_flag(channel, CF_LEG_HOLDING);
switch_channel_presence(tech_pvt->channel, "unknown", hold_msg, NULL);
} else {
hold_msg = "unhold";
msg->message_id = SWITCH_MESSAGE_INDICATE_UNHOLD;
sofia_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD);
switch_channel_clear_flag(channel, CF_LEG_HOLDING);
switch_channel_presence(tech_pvt->channel, "unknown", hold_msg, NULL);
}
msg->from = __FILE__;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Indicating %s to other leg.\n%s\n", hold_msg, r_sdp);
switch_core_session_queue_message(other_session, msg);
switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 0);
if (sofia_use_soa(tech_pvt)){
nua_respond(tech_pvt->nh, SIP_200_OK,
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SOATAG_USER_SDP_STR(tech_pvt->mparams.local_sdp_str),
SOATAG_REUSE_REJECTED(1),
SOATAG_AUDIO_AUX("cn telephone-event"),
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_100REL), NUTAG_INCLUDE_EXTRA_SDP(1)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
nua_respond(tech_pvt->nh, SIP_200_OK,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SIPTAG_CONTENT_TYPE_STR("application/sdp"), SIPTAG_PAYLOAD_STR(tech_pvt->mparams.local_sdp_str),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
switch_core_session_rwunlock(other_session);
goto done;
}
}
switch_core_session_rwunlock(other_session);
}
if (switch_channel_test_app_flag_key("T38", tech_pvt->channel, CF_APP_T38_NEGOTIATED)) {
if (sofia_use_soa(tech_pvt)) {
nua_respond(tech_pvt->nh, SIP_200_OK,
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SOATAG_USER_SDP_STR(tech_pvt->mparams.local_sdp_str),
SOATAG_REUSE_REJECTED(1),
SOATAG_AUDIO_AUX("cn telephone-event"),
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_100REL), NUTAG_INCLUDE_EXTRA_SDP(1)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
nua_respond(tech_pvt->nh, SIP_200_OK,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SIPTAG_CONTENT_TYPE_STR("application/sdp"), SIPTAG_PAYLOAD_STR(tech_pvt->mparams.local_sdp_str),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
goto done;
}
switch_channel_set_flag(tech_pvt->channel, CF_REINVITE);
if (tech_pvt->mparams.num_codecs) {
match = sofia_media_negotiate_sdp(session, r_sdp, SDP_TYPE_REQUEST);
}
if (switch_channel_test_flag(channel, CF_PROXY_MODE)) {
nua_respond(tech_pvt->nh, SIP_200_OK, TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
goto done;
}
if (match && sofia_test_flag(tech_pvt, TFLAG_NOREPLY)) {
sofia_clear_flag(tech_pvt, TFLAG_NOREPLY);
goto done;
}
if (match) {
if (switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0) != SWITCH_STATUS_SUCCESS) {
goto done;
}
switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 0);
if (sofia_media_activate_rtp(tech_pvt) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Reinvite RTP Error!\n");
is_ok = 0;
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Processing updated SDP\n");
} else {
if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
nua_respond(tech_pvt->nh, SIP_200_OK, TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
goto done;
}
switch_channel_clear_flag(tech_pvt->channel, CF_REINVITE);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Reinvite resulted in codec negotiation failure.\n");
is_ok = 0;
}
}
if (is_ok) {
if (switch_core_session_local_crypto_key(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO)) {
switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 0);
}
if (!switch_channel_test_flag(tech_pvt->channel, CF_AWAITING_STREAM_CHANGE)) {
if (sofia_use_soa(tech_pvt)) {
nua_respond(tech_pvt->nh, SIP_200_OK,
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SOATAG_USER_SDP_STR(tech_pvt->mparams.local_sdp_str),
SOATAG_REUSE_REJECTED(1),
SOATAG_AUDIO_AUX("cn telephone-event"),
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_100REL), NUTAG_INCLUDE_EXTRA_SDP(1)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
nua_respond(tech_pvt->nh, SIP_200_OK,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SIPTAG_CONTENT_TYPE_STR("application/sdp"), SIPTAG_PAYLOAD_STR(tech_pvt->mparams.local_sdp_str),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
}
if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_REINVITE) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(session));
switch_event_fire(&s_event);
}
} else {
nua_respond(tech_pvt->nh, SIP_488_NOT_ACCEPTABLE,
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
}
break;
case nua_callstate_ready:
if (!switch_channel_test_flag(channel, CF_PROXY_MODE) && !switch_channel_test_flag(channel, CF_PROXY_MEDIA) &&
r_sdp && (!is_dup_sdp || sofia_test_flag(tech_pvt, TFLAG_NEW_SDP)) && switch_core_media_ready(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO) && !sofia_test_flag(tech_pvt, TFLAG_NOSDP_REINVITE)) {
/* sdp changed since 18X w sdp, we're supposed to ignore it but we, of course, were pressured into supporting it */
uint8_t match = 0;
sofia_clear_flag(tech_pvt, TFLAG_NEW_SDP);
switch_channel_set_flag(tech_pvt->channel, CF_REINVITE);
if (tech_pvt->mparams.num_codecs) {
match = sofia_media_negotiate_sdp(session, r_sdp, SDP_TYPE_RESPONSE);
}
if (match) {
if (switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0) != SWITCH_STATUS_SUCCESS) {
goto done;
}
switch_core_media_gen_local_sdp(session, SDP_TYPE_RESPONSE, NULL, 0, NULL, 0);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Processing updated SDP\n");
switch_channel_set_flag(tech_pvt->channel, CF_REINVITE);
if (sofia_media_activate_rtp(tech_pvt) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "RTP Error!\n");
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
goto done;
}
} else {
switch_channel_clear_flag(tech_pvt->channel, CF_REINVITE);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Codec Error! %s\n", r_sdp);
goto done;
}
}
if (r_sdp && sofia_test_flag(tech_pvt, TFLAG_NOSDP_REINVITE)) {
sofia_clear_flag_locked(tech_pvt, TFLAG_NOSDP_REINVITE);
if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
if (switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
if (sofia_media_activate_rtp(tech_pvt) != SWITCH_STATUS_SUCCESS) {
goto done;
}
}
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
other_channel = switch_core_session_get_channel(other_session);
switch_channel_pass_sdp(channel, other_channel, r_sdp);
if (sofia_test_flag(tech_pvt, TFLAG_3PCC) && sofia_test_pflag(profile, PFLAG_3PCC_PROXY)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "3PCC-PROXY, Got my ACK\n");
sofia_set_flag(tech_pvt, TFLAG_3PCC_HAS_ACK);
} else {
switch_core_session_message_t *msg;
if (sofia_test_pflag(profile, PFLAG_3PCC_REINVITE_BRIDGED_ON_ACK)) {
msg = switch_core_session_alloc(other_session, sizeof(*msg));
msg->message_id = SWITCH_MESSAGE_INDICATE_MEDIA_REDIRECT;
msg->from = __FILE__;
msg->string_arg = switch_core_session_strdup(other_session, r_sdp);
switch_core_session_queue_message(other_session, msg);
}
switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_ANSWER);
}
switch_core_session_rwunlock(other_session);
}
} else {
uint8_t match = 0;
int is_ok = 1;
if (!tech_pvt) goto done;
if (tech_pvt->mparams.num_codecs) {
match = sofia_media_negotiate_sdp(session, r_sdp, SDP_TYPE_RESPONSE);
}
if (match) {
switch_channel_set_flag(tech_pvt->channel, CF_REINVITE);
if (sofia_media_activate_rtp(tech_pvt) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "RTP Error!\n");
switch_channel_set_variable(tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RTP ERROR");
is_ok = 0;
}
switch_channel_clear_flag(tech_pvt->channel, CF_REINVITE);
} else {
switch_channel_set_variable(tech_pvt->channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR");
is_ok = 0;
}
if (!is_ok) {
nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
}
}
goto done;
}
if (channel) {
switch_channel_clear_flag(channel, CF_REQ_MEDIA);
}
if (tech_pvt && nh == tech_pvt->nh2) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Cheater Reinvite!\n");
switch_channel_set_flag(tech_pvt->channel, CF_REINVITE);
tech_pvt->nh = tech_pvt->nh2;
tech_pvt->nh2 = NULL;
if (switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0) == SWITCH_STATUS_SUCCESS) {
if (sofia_media_activate_rtp(tech_pvt) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cheater Reinvite RTP Error!\n");
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
}
goto done;
}
if (channel) {
if (sofia_test_flag(tech_pvt, TFLAG_EARLY_MEDIA) && !sofia_test_flag(tech_pvt, TFLAG_ANS)) {
sofia_set_flag_locked(tech_pvt, TFLAG_ANS);
sofia_set_flag(tech_pvt, TFLAG_SDP);
switch_channel_mark_answered(channel);
if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
//other_channel = switch_core_session_get_channel(other_session);
//switch_channel_answer(other_channel);
switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_ANSWER);
switch_core_session_rwunlock(other_session);
}
}
goto done;
}
if (!r_sdp && !sofia_test_flag(tech_pvt, TFLAG_SDP)) {
r_sdp = (const char *) switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE);
}
if (r_sdp && !sofia_test_flag(tech_pvt, TFLAG_SDP)) {
if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
sofia_set_flag_locked(tech_pvt, TFLAG_ANS);
sofia_set_flag_locked(tech_pvt, TFLAG_SDP);
switch_channel_mark_answered(channel);
if (switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
if (sofia_media_activate_rtp(tech_pvt) != SWITCH_STATUS_SUCCESS) {
goto done;
}
}
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
other_channel = switch_core_session_get_channel(other_session);
switch_channel_pass_sdp(channel, other_channel, r_sdp);
//switch_channel_answer(other_channel);
switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_ANSWER);
switch_core_session_rwunlock(other_session);
}
if (sofia_test_flag(tech_pvt, TFLAG_3PCC) && sofia_test_pflag(profile, PFLAG_3PCC_PROXY)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "3PCC-PROXY, Got my ACK\n");
sofia_set_flag(tech_pvt, TFLAG_3PCC_HAS_ACK);
}
goto done;
} else {
uint8_t match = 0;
match = sofia_media_negotiate_sdp(session, r_sdp, SDP_TYPE_RESPONSE);
sofia_set_flag_locked(tech_pvt, TFLAG_ANS);
if (match) {
switch_channel_check_zrtp(channel);
if (switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0) == SWITCH_STATUS_SUCCESS) {
if (sofia_media_activate_rtp(tech_pvt) == SWITCH_STATUS_SUCCESS) {
switch_channel_mark_answered(channel);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "RTP Error!\n");
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
if (sofia_test_flag(tech_pvt, TFLAG_3PCC)) {
/* Check if we are in 3PCC proxy mode, if so then set the flag to indicate we received the ack */
if (sofia_test_pflag(profile, PFLAG_3PCC_PROXY)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "3PCC-PROXY, Got my ACK\n");
sofia_set_flag(tech_pvt, TFLAG_3PCC_HAS_ACK);
} else if (switch_channel_get_state(channel) == CS_HIBERNATE) {
sofia_set_flag_locked(tech_pvt, TFLAG_READY);
switch_channel_set_state(channel, CS_INIT);
sofia_set_flag(tech_pvt, TFLAG_SDP);
}
}
goto done;
}
}
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "NO CODECS");
switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
}
}
}
break;
case nua_callstate_terminating:
if (status == 488 || switch_channel_get_state(channel) == CS_HIBERNATE) {
tech_pvt->q850_cause = SWITCH_CAUSE_MANDATORY_IE_MISSING;
}
case nua_callstate_terminated:
sofia_set_flag_locked(tech_pvt, TFLAG_BYE);
if (sofia_test_flag(tech_pvt, TFLAG_NOHUP)) {
sofia_clear_flag_locked(tech_pvt, TFLAG_NOHUP);
} else if (switch_channel_up(channel)) {
int cause;
if (tech_pvt->q850_cause) {
cause = tech_pvt->q850_cause;
} else {
cause = sofia_glue_sip_cause_to_freeswitch(status);
}
if (status) {
switch_snprintf(st, sizeof(st), "%d", status);
switch_channel_set_variable(channel, "sip_term_status", st);
switch_snprintf(st, sizeof(st), "sip:%d", status);
switch_channel_set_variable(channel, SWITCH_PROTO_SPECIFIC_HANGUP_CAUSE_VARIABLE, st);
if (phrase) {
switch_channel_set_variable_partner(channel, "sip_hangup_phrase", phrase);
}
sofia_glue_set_extra_headers(session, sip, SOFIA_SIP_BYE_HEADER_PREFIX);
}
switch_snprintf(st, sizeof(st), "%d", cause);
switch_channel_set_variable(channel, "sip_term_cause", st);
switch_channel_hangup(channel, cause);
ss_state = nua_callstate_terminated;
}
if (ss_state == nua_callstate_terminated) {
if (tech_pvt->sofia_private) {
tech_pvt->sofia_private = NULL;
}
tech_pvt->nh = NULL;
if (nh) {
nua_handle_bind(nh, NULL);
nua_handle_destroy(nh);
}
}
break;
}
done:
switch_safe_free(patched_sdp);
if ((enum nua_callstate) ss_state == nua_callstate_ready && channel && session && tech_pvt) {
sofia_set_flag(tech_pvt, TFLAG_SIMPLIFY);
}
return;
}
typedef struct {
char *exten;
char *exten_with_params;
char *event;
char *reply_uuid;
char *bridge_to_uuid;
switch_event_t *vars;
switch_memory_pool_t *pool;
sofia_profile_t *profile;
} nightmare_xfer_helper_t;
void *SWITCH_THREAD_FUNC nightmare_xfer_thread_run(switch_thread_t *thread, void *obj)
{
nightmare_xfer_helper_t *nhelper = (nightmare_xfer_helper_t *) obj;
switch_memory_pool_t *pool;
switch_status_t status = SWITCH_STATUS_FALSE;
switch_core_session_t *session, *a_session;
if ((a_session = switch_core_session_locate(nhelper->bridge_to_uuid))) {
switch_core_session_t *tsession = NULL;
switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING;
uint32_t timeout = 60;
char *tuuid_str;
if ((session = switch_core_session_locate(nhelper->reply_uuid))) {
private_object_t *tech_pvt = switch_core_session_get_private(session);
switch_channel_t *channel_a = switch_core_session_get_channel(session);
const char *session_id_header = sofia_glue_session_id_header(session, nhelper->profile);
if ((status = switch_ivr_originate(NULL, &tsession, &cause, nhelper->exten_with_params, timeout, NULL, NULL, NULL,
switch_channel_get_caller_profile(channel_a), nhelper->vars, SOF_NONE, NULL, NULL)) == SWITCH_STATUS_SUCCESS) {
if (switch_channel_up(channel_a)) {
if (switch_true(switch_channel_get_variable(channel_a, "recording_follow_transfer"))) {
switch_ivr_transfer_recordings(session, a_session);
}
tuuid_str = switch_core_session_get_uuid(tsession);
switch_channel_set_variable_printf(channel_a, "transfer_to", "att:%s", tuuid_str);
mark_transfer_record(session, nhelper->bridge_to_uuid, tuuid_str);
switch_ivr_uuid_bridge(nhelper->bridge_to_uuid, tuuid_str);
switch_channel_set_variable(channel_a, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER");
} else {
switch_channel_hangup(switch_core_session_get_channel(tsession), SWITCH_CAUSE_ORIGINATOR_CANCEL);
status = SWITCH_STATUS_FALSE;
}
switch_core_session_rwunlock(tsession);
}
if (status == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "The nightmare is over.....\n");
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "1 .. 2 .. Freddie's commin' for you...\n");
}
nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag"),
NUTAG_SUBSTATE(nua_substate_terminated),SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"),
SIPTAG_PAYLOAD_STR(status == SWITCH_STATUS_SUCCESS ? "SIP/2.0 200 OK\r\n" :
"SIP/2.0 403 Forbidden\r\n"), SIPTAG_EVENT_STR(nhelper->event),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
switch_core_session_rwunlock(session);
}
switch_core_session_rwunlock(a_session);
}
switch_event_destroy(&nhelper->vars);
pool = nhelper->pool;
switch_core_destroy_memory_pool(&pool);
return NULL;
}
static void launch_nightmare_xfer(nightmare_xfer_helper_t *nhelper)
{
switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL;
switch_threadattr_create(&thd_attr, nhelper->pool);
switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
switch_thread_create(&thread, thd_attr, nightmare_xfer_thread_run, nhelper, nhelper->pool);
}
/*---------------------------------------*/
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;
}
nua_handle_t *sofia_global_nua_handle_by_replaces(sip_replaces_t *replaces)
{
nua_handle_t *nh = NULL;
switch_hash_index_t *hi;
const void *var;
void *val;
sofia_profile_t *profile;
switch_mutex_lock(mod_sofia_globals.hash_mutex);
if (mod_sofia_globals.profile_hash) {
for (hi = switch_core_hash_first(mod_sofia_globals.profile_hash); hi; hi = switch_core_hash_next(&hi)) {
switch_core_hash_this(hi, &var, NULL, &val);
if ((profile = (sofia_profile_t *) val)) {
if (!(nh = nua_handle_by_replaces(profile->nua, replaces))) {
nh = nua_handle_by_call_id(profile->nua, replaces->rp_call_id);
}
if (nh)
break;
}
}
switch_safe_free(hi);
}
switch_mutex_unlock(mod_sofia_globals.hash_mutex);
return nh;
}
static switch_status_t sofia_process_proxy_refer(switch_core_session_t *session, const char *refer_to)
{
switch_core_session_t *other_session;
private_object_t *tech_pvt = switch_core_session_get_private(session);
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
switch_core_session_message_t *msg;
tech_pvt->proxy_refer_uuid = switch_core_session_strdup(session, switch_core_session_get_uuid(other_session));
msg = switch_core_session_alloc(other_session, sizeof(*msg));
MESSAGE_STAMP_FFL(msg);
msg->message_id = SWITCH_MESSAGE_INDICATE_DEFLECT;
msg->string_arg = switch_core_session_strdup(other_session, refer_to);
msg->string_array_arg[0] = switch_core_session_strdup(other_session, switch_core_session_get_uuid(session));
msg->from = __FILE__;
switch_core_session_queue_message(other_session, msg);
switch_core_session_rwunlock(other_session);
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_FALSE;
}
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,
sofia_dispatch_event_t *de, tagi_t tags[])
{
/* Incoming refer */
sip_from_t const *from;
//sip_to_t const *to;
sip_refer_to_t const *refer_to;
private_object_t *tech_pvt = switch_core_session_get_private(session);
char *etmp = NULL, *exten = NULL;
switch_channel_t *channel_a = switch_core_session_get_channel(session);
switch_channel_t *channel_b = NULL;
su_home_t *home = NULL;
char *full_ref_by = NULL;
char *full_ref_to = NULL;
nightmare_xfer_helper_t *nightmare_xfer_helper;
switch_memory_pool_t *npool;
switch_event_t *event = NULL;
const char *session_id_header = sofia_glue_session_id_header(session, profile);
if (!(profile->mflags & MFLAG_REFER)) {
nua_respond(nh, SIP_403_FORBIDDEN, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
goto done;
}
if (!sip->sip_cseq || !(etmp = switch_mprintf("refer;id=%u", sip->sip_cseq->cs_seq))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Memory Error!\n");
goto done;
}
home = su_home_new(sizeof(*home));
switch_assert(home != NULL);
if ((refer_to = sip->sip_refer_to)) {
full_ref_to = sip_header_as_string(home, (void *) sip->sip_refer_to);
}
if (full_ref_to && sofia_test_pflag(profile, PFLAG_PROXY_REFER)) {
if (sofia_process_proxy_refer(session, full_ref_to) == SWITCH_STATUS_SUCCESS) {
if (tech_pvt->proxy_refer_msg) {
msg_ref_destroy(tech_pvt->proxy_refer_msg);
tech_pvt->proxy_refer_msg = NULL;
}
tech_pvt->proxy_refer_msg = msg_ref_create(de->data->e_msg);
//nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS_MSG(de->data->e_msg), SIPTAG_EXPIRES_STR("60"), TAG_END());
goto done;
}
}
from = sip->sip_from;
//to = sip->sip_to;
nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS_MSG(de->data->e_msg), SIPTAG_EXPIRES_STR("60"),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
switch_channel_set_variable(tech_pvt->channel, SOFIA_REPLACES_HEADER, NULL);
if (sip->sip_referred_by) {
full_ref_by = sip_header_as_string(home, (void *) sip->sip_referred_by);
}
if (refer_to) {
char *rep = NULL;
if (sofia_test_pflag(profile, PFLAG_FULL_ID)) {
exten = switch_core_session_sprintf(session, "%s@%s", (char *) refer_to->r_url->url_user, (char *) refer_to->r_url->url_host);
} else {
exten = (char *) refer_to->r_url->url_user;
}
if (refer_to->r_url->url_params) {
switch_channel_set_variable(tech_pvt->channel, "sip_refer_to_params", refer_to->r_url->url_params);
}
switch_core_session_queue_indication(session, SWITCH_MESSAGE_REFER_EVENT);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Process REFER to [%s@%s]\n", exten, (char *) refer_to->r_url->url_host);
switch_channel_set_variable(tech_pvt->channel, "transfer_disposition", "recv_replace");
if (refer_to->r_url->url_headers) {
rep = (char *) switch_stristr("Replaces=", refer_to->r_url->url_headers);
}
if (rep) {
sip_replaces_t *replaces;
nua_handle_t *bnh = NULL;
const char *br_a = NULL, *br_b = NULL;
char *buf;
char *p;
rep = switch_core_session_strdup(session, rep + 9);
if ((buf = switch_core_session_alloc(session, strlen(rep) + 1))) {
rep = url_unescape(buf, (const char *) rep);
if ((p = strchr(rep, ';'))) {
*p = '\0';
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Replaces: [%s]\n", rep);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Memory Error!\n");
goto done;
}
if ((replaces = sip_replaces_make(home, rep))) {
if (!(bnh = nua_handle_by_replaces(nua, replaces))) {
if (!(bnh = nua_handle_by_call_id(nua, replaces->rp_call_id))) {
bnh = sofia_global_nua_handle_by_replaces(replaces);
}
}
}
if (bnh && !sofia_test_pflag(profile, PFLAG_MAKE_EVERY_TRANSFER_A_NIGHTMARE)) {
sofia_private_t *b_private = NULL;
private_object_t *b_tech_pvt = NULL;
switch_core_session_t *b_session = NULL;
switch_channel_set_variable(channel_a, SOFIA_REPLACES_HEADER, rep);
if ((b_private = nua_handle_magic(bnh))) {
int deny_refer_requests = 0;
if (!(b_session = switch_core_session_locate(b_private->uuid))) {
goto done;
}
b_tech_pvt = (private_object_t *) switch_core_session_get_private(b_session);
channel_b = switch_core_session_get_channel(b_session);
switch_channel_set_variable(channel_a, "refer_uuid", b_private->uuid);
switch_channel_set_variable(channel_b, "transfer_disposition", "replaced");
br_a = switch_channel_get_partner_uuid(channel_a);
br_b = switch_channel_get_partner_uuid(channel_b);
if (!switch_ivr_uuid_exists(br_a)) {
br_a = NULL;
}
if (!switch_ivr_uuid_exists(br_b)) {
br_b = NULL;
}
if (channel_a && switch_true(switch_channel_get_variable(channel_a, "deny_refer_requests"))) {
deny_refer_requests = 1;
}
if (!deny_refer_requests && channel_b && switch_true(switch_channel_get_variable(channel_b, "deny_refer_requests"))) {
deny_refer_requests = 1;
}
if (!deny_refer_requests && br_a) {
switch_core_session_t *a_session;
if ((a_session = switch_core_session_locate(br_a))) {
switch_channel_t *a_channel = switch_core_session_get_channel(a_session);
if (a_channel && switch_true(switch_channel_get_variable(a_channel, "deny_refer_requests"))) {
deny_refer_requests = 1;
}
switch_core_session_rwunlock(a_session);
}
}
if (!deny_refer_requests && br_b) {
switch_core_session_t *b_session;
if ((b_session = switch_core_session_locate(br_b))) {
switch_channel_t *b_channel = switch_core_session_get_channel(b_session);
if (b_channel && switch_true(switch_channel_get_variable(b_channel, "deny_refer_requests"))) {
deny_refer_requests = 1;
}
switch_core_session_rwunlock(b_session);
}
}
if (deny_refer_requests) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Denying Attended Transfer, variable [deny_refer_requests] was set to true\n");
nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"),
NUTAG_SUBSTATE(nua_substate_terminated),SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden\r\n"), SIPTAG_EVENT_STR(etmp), TAG_END());
} else if (switch_channel_test_flag(channel_b, CF_ORIGINATOR)) {
switch_core_session_t *a_session;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE,
"Attended Transfer on originating session %s\n", switch_core_session_get_uuid(b_session));
switch_channel_set_variable_printf(channel_b, "transfer_to", "satt:%s", br_a);
switch_channel_set_variable(channel_b, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER");
sofia_clear_flag_locked(b_tech_pvt, TFLAG_SIP_HOLD);
switch_channel_clear_flag(channel_b, CF_LEG_HOLDING);
sofia_clear_flag_locked(tech_pvt, TFLAG_HOLD_LOCK);
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);
if ((a_session = switch_core_session_locate(br_a))) {
const char *moh = profile->hold_music;
switch_core_session_t *tmpsess = NULL;
switch_channel_t *a_channel = switch_core_session_get_channel(a_session);
switch_caller_profile_t *prof = switch_channel_get_caller_profile(channel_b);
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));
switch_channel_set_variable(a_channel, "att_xfer_destination_number", prof->destination_number);
switch_channel_set_variable(a_channel, "att_xfer_callee_id_name", prof->callee_id_name);
switch_channel_set_variable(a_channel, "att_xfer_callee_id_number", prof->callee_id_number);
if (profile->media_options & MEDIA_OPT_BYPASS_AFTER_ATT_XFER) {
switch_channel_set_flag(a_channel, CF_BYPASS_MEDIA_AFTER_BRIDGE);
}
if ((tmp = switch_channel_get_hold_music(a_channel))) {
moh = tmp;
}
if (!zstr(moh) && !strcasecmp(moh, "silence")) {
moh = NULL;
}
if(sofia_test_pflag(profile, PFLAG_FIRE_TRANFER_EVENTS)) {
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_REPLACED) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel_b, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_replaced_by", br_a);
switch_event_fire(&event);
}
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_TRANSFEROR) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel_a, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_original_call_id", br_a);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_destination_call_id", br_b);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_destination_peer_uuid", switch_core_session_get_uuid(b_session));
switch_event_fire(&event);
}
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_TRANSFEREE) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(a_channel, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_replaced_call_id", switch_core_session_get_uuid(b_session));
switch_event_fire(&event);
}
}
if (moh) {
char *xdest;
xdest = switch_core_session_sprintf(a_session, "m:\":endless_playback:%s\"park", moh);
switch_ivr_session_transfer(a_session, xdest, "inline", NULL);
} else {
switch_ivr_session_transfer(a_session, "park", "inline", NULL);
}
if (switch_true(switch_channel_get_variable(channel_a, "recording_follow_transfer"))) {
switch_ivr_transfer_recordings(session, a_session);
}
if (switch_true(switch_channel_get_variable(channel_b, "recording_follow_transfer")) && (tmpsess = switch_core_session_locate(br_a))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE,
"Early transfer detected with no media, moving recording bug to other leg\n");
switch_ivr_transfer_recordings(b_session, tmpsess);
switch_core_session_rwunlock(tmpsess);
}
switch_core_session_rwunlock(a_session);
nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"),
NUTAG_SUBSTATE(nua_substate_terminated),SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK\r\n"), SIPTAG_EVENT_STR(etmp),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
if (b_tech_pvt && !sofia_test_flag(b_tech_pvt, TFLAG_BYE)) {
char *q850 = NULL;
const char *val = NULL;
sofia_set_flag_locked(b_tech_pvt, TFLAG_BYE);
val = switch_channel_get_variable(tech_pvt->channel, "disable_q850_reason");
if (!val || switch_true(val)) {
q850 = switch_core_session_sprintf(a_session, "Q.850;cause=16;text=\"normal_clearing\"");
}
nua_bye(b_tech_pvt->nh,
SIPTAG_CONTACT(SIP_NONE),
TAG_IF(!zstr(q850), SIPTAG_REASON_STR(q850)),
TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
} else {
nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"),
NUTAG_SUBSTATE(nua_substate_terminated),SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"),
SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden\r\n"), SIPTAG_EVENT_STR(etmp),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
} else if (br_a && br_b) {
switch_core_session_t *tmp = NULL;
switch_event_t *event = NULL;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Attended Transfer [%s][%s]\n",
switch_str_nil(br_a), switch_str_nil(br_b));
if ((tmp = switch_core_session_locate(br_b))) {
switch_channel_t *tchannel = switch_core_session_get_channel(tmp);
if ((profile->media_options & MEDIA_OPT_BYPASS_AFTER_ATT_XFER)) {
switch_channel_set_flag(tchannel, CF_BYPASS_MEDIA_AFTER_BRIDGE);
}
switch_channel_set_variable(tchannel, "transfer_disposition", "bridge");
switch_channel_set_flag(tchannel, CF_ATTENDED_TRANSFER);
switch_core_session_rwunlock(tmp);
}
if ((profile->media_options & MEDIA_OPT_BYPASS_AFTER_ATT_XFER) && (tmp = switch_core_session_locate(br_a))) {
switch_channel_t *tchannel = switch_core_session_get_channel(tmp);
switch_channel_set_flag(tchannel, CF_BYPASS_MEDIA_AFTER_BRIDGE);
switch_core_session_rwunlock(tmp);
}
if (switch_true(switch_channel_get_variable(channel_a, "recording_follow_transfer")) &&
(tmp = switch_core_session_locate(br_a))) {
switch_channel_set_variable(switch_core_session_get_channel(tmp), "transfer_disposition", "bridge");
switch_ivr_transfer_recordings(session, tmp);
switch_core_session_rwunlock(tmp);
}
if (switch_true(switch_channel_get_variable(channel_b, "recording_follow_transfer")) &&
(tmp = switch_core_session_locate(br_b))) {
switch_ivr_transfer_recordings(b_session, tmp);
switch_core_session_rwunlock(tmp);
}
switch_channel_set_variable_printf(channel_a, "transfer_to", "att:%s", br_b);
mark_transfer_record(session, br_a, br_b);
if(sofia_test_pflag(profile, PFLAG_FIRE_TRANFER_EVENTS)) {
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_REPLACED) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel_b, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_replaced_by", br_a);
switch_event_fire(&event);
}
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_TRANSFEROR) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel_a, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_original_call_id", br_a);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_destination_call_id", br_b);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_destination_peer_uuid", switch_core_session_get_uuid(b_session));
switch_event_fire(&event);
}
if ((tmp = switch_core_session_locate(br_a))) {
switch_channel_t *tchannel = switch_core_session_get_channel(tmp);
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_TRANSFEREE) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(tchannel, event);
switch_event_fire(&event);
}
switch_core_session_rwunlock(tmp);
}
}
switch_ivr_uuid_bridge(br_a, br_b);
switch_channel_set_variable(channel_b, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER");
nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"),
NUTAG_SUBSTATE(nua_substate_terminated),SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK\r\n"), SIPTAG_EVENT_STR(etmp),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
sofia_clear_flag_locked(b_tech_pvt, TFLAG_SIP_HOLD);
switch_channel_clear_flag(channel_b, CF_LEG_HOLDING);
sofia_clear_flag_locked(tech_pvt, TFLAG_HOLD_LOCK);
switch_channel_set_variable(channel_b, "park_timeout", "2:attended_transfer");
switch_channel_set_state(channel_b, CS_PARK);
switch_channel_wait_for_state_timeout(channel_b, CS_PARK, 5000);
} else {
if (!br_a && !br_b) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
"Cannot transfer channels that are not in a bridge.\n");
nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"),
NUTAG_SUBSTATE(nua_substate_terminated),SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"), SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden\r\n"),
SIPTAG_EVENT_STR(etmp),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
switch_core_session_t *t_session, *hup_session;
switch_channel_t *hup_channel;
switch_event_t *event = NULL;
const char *ext;
if (br_a && !br_b) {
t_session = switch_core_session_locate(br_a);
hup_channel = channel_b;
hup_session = b_session;
} else {
private_object_t *h_tech_pvt = (private_object_t *) switch_core_session_get_private(b_session);
t_session = switch_core_session_locate(br_b);
hup_channel = channel_a;
hup_session = session;
sofia_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD);
switch_channel_clear_flag(tech_pvt->channel, CF_LEG_HOLDING);
sofia_clear_flag_locked(h_tech_pvt, TFLAG_SIP_HOLD);
switch_channel_clear_flag(h_tech_pvt->channel, CF_LEG_HOLDING);
switch_channel_hangup(channel_b, SWITCH_CAUSE_ATTENDED_TRANSFER);
}
if (t_session) {
switch_channel_t *t_channel = switch_core_session_get_channel(t_session);
const char *idest = switch_channel_get_variable(hup_channel, "inline_destination");
ext = switch_channel_get_variable(hup_channel, "destination_number");
if (!zstr(full_ref_by)) {
switch_channel_set_variable(t_channel, SOFIA_SIP_HEADER_PREFIX "Referred-By", full_ref_by);
}
if (!zstr(full_ref_to)) {
switch_channel_set_variable(t_channel, SOFIA_REFER_TO_VARIABLE, full_ref_to);
}
if (switch_true(switch_channel_get_variable(hup_channel, "recording_follow_transfer"))) {
switch_ivr_transfer_recordings(hup_session, t_session);
}
if(sofia_test_pflag(profile, PFLAG_FIRE_TRANFER_EVENTS)) {
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_TRANSFEROR) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel_a, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_original_call_id", switch_core_session_get_uuid(hup_session));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_destination_call_id", switch_core_session_get_uuid(t_session));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_destination_peer_uuid", switch_channel_get_partner_uuid(t_channel));
switch_event_fire(&event);
}
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_TRANSFEREE) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(t_channel, event);
switch_event_fire(&event);
}
}
if (idest) {
switch_ivr_session_transfer(t_session, idest, "inline", NULL);
} else {
switch_ivr_session_transfer(t_session, ext, NULL, NULL);
}
nua_notify(tech_pvt->nh,
NUTAG_NEWSUB(1),
SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"),
NUTAG_SUBSTATE(nua_substate_terminated),SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"),
SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK\r\n"), SIPTAG_EVENT_STR(etmp),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
switch_core_session_rwunlock(t_session);
switch_channel_hangup(hup_channel, SWITCH_CAUSE_ATTENDED_TRANSFER);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Session to transfer to not found.\n");
nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"),
NUTAG_SUBSTATE(nua_substate_terminated),SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"),
SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden\r\n"), SIPTAG_EVENT_STR(etmp),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
}
}
switch_core_session_rwunlock(b_session);
}
nua_handle_unref(bnh);
} else { /* the other channel is on a different box, we have to go find them */
if (exten && (br_a = switch_channel_get_partner_uuid(channel_a))) {
switch_core_session_t *a_session;
switch_channel_t *channel;
if ((a_session = switch_core_session_locate(br_a))) {
const char *port = NULL;
const char *rep_h = NULL;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
"REFER from %s replaces %s (%s@%s) with %s on another server\n",
switch_core_session_get_uuid(session), rep, exten, (char *) refer_to->r_url->url_host, br_a);
if (refer_to->r_url->url_port) {
port = refer_to->r_url->url_port;
}
channel = switch_core_session_get_channel(a_session);
exten = NULL;
if (sofia_test_pflag(profile, PFLAG_CHANNEL_XML_FETCH_ON_NIGHTMARE_TRANSFER)) {
switch_xml_t xml_root = NULL, xml_channel = NULL;
switch_event_t *xml_params = NULL;
const char *xml_url = NULL, *use_profile = profile->name, *dial_prefix = NULL, *absolute_dial_string = NULL;
switch_xml_t params = NULL, param = NULL;
switch_event_create(&xml_params, SWITCH_EVENT_REQUEST_PARAMS);
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "purpose", "nightmare_xfer");
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "profile", profile->name);
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "refer-to-user", refer_to->r_url->url_user);
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "refer-to-host", refer_to->r_url->url_host);
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "refer-to-params", refer_to->r_url->url_params ? refer_to->r_url->url_params : "");
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "refer-to-headers", refer_to->r_url->url_headers ? refer_to->r_url->url_headers : "");
if (replaces) {
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "replaces-call-id", replaces->rp_call_id);
}
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "refer-from-channel-id", switch_core_session_get_uuid(session));
switch_event_add_header_string(xml_params, SWITCH_STACK_BOTTOM, "refer-for-channel-id", br_a);
if (switch_xml_locate("channels", NULL, NULL, NULL,
&xml_root, &xml_channel, xml_params, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
if ((params = switch_xml_child(xml_channel, "params"))) {
for (param = switch_xml_child(params, "param"); param; param = param->next) {
const char *name = switch_xml_attr(param, "name");
const char *value = switch_xml_attr(param, "value");
if (!(name && value)) continue;
if (!strcasecmp(name, "sip-url")) {
xml_url = value;
} else if (!strcasecmp(name, "dial-prefix")) {
dial_prefix = value;
} else if (!strcasecmp(name, "absolute-dial-string")) {
absolute_dial_string = value;
} else if (!strcasecmp(name, "sip-profile")) {
use_profile = value;
}
}
}
if (absolute_dial_string) {
exten = switch_core_session_sprintf(session, "%s%s", dial_prefix, absolute_dial_string);
} else if (xml_url) {
exten = switch_core_session_sprintf(session, "%ssofia/%s/%s", dial_prefix, use_profile, xml_url);
}
switch_xml_free(xml_root);
}
switch_event_destroy(&xml_params);
}
if (zstr(exten)) {
exten = switch_core_session_sprintf(session, "sofia/%s/sip:%s@%s%s%s",
profile->name, refer_to->r_url->url_user,
refer_to->r_url->url_host, port ? ":" : "", port ? port : "");
}
switch_core_new_memory_pool(&npool);
nightmare_xfer_helper = switch_core_alloc(npool, sizeof(*nightmare_xfer_helper));
nightmare_xfer_helper->exten = switch_core_strdup(npool, exten);
if (refer_to->r_url->url_params || refer_to->r_url->url_headers) {
if (refer_to->r_url->url_headers) {
nightmare_xfer_helper->exten_with_params = switch_core_sprintf(npool,
"{sip_invite_params=%s?%s}%s",
refer_to->r_url->url_params ? refer_to->r_url->
url_params : "", refer_to->r_url->url_headers, exten);
} else {
nightmare_xfer_helper->exten_with_params = switch_core_sprintf(npool,
"{sip_invite_params=%s}%s", refer_to->r_url->url_params,
exten);
}
} else {
nightmare_xfer_helper->exten_with_params = nightmare_xfer_helper->exten;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Nightmare transfer to '%s'\n", nightmare_xfer_helper->exten_with_params);
nightmare_xfer_helper->event = switch_core_strdup(npool, etmp);
nightmare_xfer_helper->reply_uuid = switch_core_strdup(npool, switch_core_session_get_uuid(session));
nightmare_xfer_helper->bridge_to_uuid = switch_core_strdup(npool, br_a);
nightmare_xfer_helper->pool = npool;
if (refer_to->r_url->url_headers) {
char *h, *v, *hp;
p = switch_core_session_strdup(session, refer_to->r_url->url_headers);
while (p && *p) {
h = p;
if ((p = strchr(p, '='))) {
*p++ = '\0';
v = p;
if ((p = strchr(p, '&'))) {
*p++ = '\0';
}
url_unescape(h, (const char *) h);
url_unescape(v, (const char *) v);
if (strcasecmp("Replaces", h)) {
hp = switch_core_session_sprintf(session, "%s%s", SOFIA_SIP_HEADER_PREFIX, h);
switch_channel_set_variable(channel, hp, v);
} else {
// use this one instead of rep value from above to keep all parameters
switch_channel_set_variable(channel, SOFIA_REPLACES_HEADER, v);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Exporting replaces URL header [%s:%s]\n",
h, v);
}
}
}
switch_event_create(&nightmare_xfer_helper->vars, SWITCH_EVENT_CHANNEL_DATA);
rep_h = switch_channel_get_variable(channel, SOFIA_REPLACES_HEADER);
if (rep_h) {
switch_event_add_header_string(nightmare_xfer_helper->vars, SWITCH_STACK_BOTTOM, SOFIA_REPLACES_HEADER, rep_h);
} else {
switch_event_add_header_string(nightmare_xfer_helper->vars, SWITCH_STACK_BOTTOM, SOFIA_REPLACES_HEADER, rep);
}
if (!zstr(full_ref_by)) {
switch_event_add_header_string(nightmare_xfer_helper->vars, SWITCH_STACK_BOTTOM, "Referred-By", full_ref_by);
}
if (!zstr(full_ref_to)) {
switch_event_add_header_string(nightmare_xfer_helper->vars, SWITCH_STACK_BOTTOM, SOFIA_REFER_TO_VARIABLE, full_ref_to);
}
if(sofia_test_pflag(profile, PFLAG_FIRE_TRANFER_EVENTS)) {
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_TRANSFEROR) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_original_call_id", br_a);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_destination_call_id", br_b);
switch_channel_event_set_data(channel_a, event);
switch_event_fire(&event);
}
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_TRANSFEREE) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
}
switch_event_add_header_string(nightmare_xfer_helper->vars, SWITCH_STACK_BOTTOM, "sip_h_X-FS-Refer-From", switch_core_session_get_uuid(session));
switch_event_add_header_string(nightmare_xfer_helper->vars, SWITCH_STACK_BOTTOM, "sip_h_X-FS-Refer-For", br_a);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Good Luck, you'll need it......\n");
nightmare_xfer_helper->profile = profile;
launch_nightmare_xfer(nightmare_xfer_helper);
switch_core_session_rwunlock(a_session);
} else {
goto error;
}
} else {
error:
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid Transfer! [%s]\n", br_a);
switch_channel_set_variable(channel_a, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER_ERROR");
nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"),
NUTAG_SUBSTATE(nua_substate_terminated),SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"), SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden\r\n"), SIPTAG_EVENT_STR(etmp),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
}
goto done;
}
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Missing Refer-To\n");
goto done;
}
if (exten) {
switch_channel_t *channel = switch_core_session_get_channel(session);
const char *br = switch_channel_get_partner_uuid(channel);
switch_core_session_t *b_session;
switch_channel_set_variable_printf(channel, "transfer_to", "blind:%s", br ? br : exten);
switch_channel_set_variable_printf(channel, "transfer_destination", "blind:%s", exten);
if (!zstr(br) && (b_session = switch_core_session_locate(br))) {
const char *var;
switch_channel_t *b_channel = switch_core_session_get_channel(b_session);
switch_event_t *event = NULL;
switch_channel_set_variable(channel, "transfer_fallback_extension", from->a_user);
if (!zstr(full_ref_by)) {
switch_channel_set_variable(b_channel, SOFIA_SIP_HEADER_PREFIX "Referred-By", full_ref_by);
}
if (!zstr(full_ref_to)) {
switch_channel_set_variable(b_channel, SOFIA_REFER_TO_VARIABLE, full_ref_to);
}
if (switch_true(switch_channel_get_variable(channel, "recording_follow_transfer"))) {
switch_ivr_transfer_recordings(session, b_session);
}
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "BLIND_TRANSFER");
if (((var = switch_channel_get_variable(channel, "confirm_blind_transfer")) && switch_true(var)) ||
sofia_test_pflag(profile, PFLAG_CONFIRM_BLIND_TRANSFER)) {
switch_channel_set_state_flag(b_channel, CF_CONFIRM_BLIND_TRANSFER);
switch_channel_set_variable(channel, "sip_blind_transfer_event", etmp);
switch_channel_set_variable(b_channel, "blind_transfer_uuid", switch_core_session_get_uuid(session));
switch_channel_set_variable(channel, "blind_transfer_uuid", switch_core_session_get_uuid(b_session));
switch_channel_set_variable(channel, "park_timeout", "600:blind_transfer");
switch_channel_set_state(channel, CS_PARK);
} else {
nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"),
NUTAG_SUBSTATE(nua_substate_terminated),
SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"),
SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK\r\n"), SIPTAG_EVENT_STR(etmp),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
if (refer_to->r_url->url_params) {
switch_channel_set_variable(b_channel, "sip_h_X-FS-Refer-Params", refer_to->r_url->url_params);
}
if(sofia_test_pflag(profile, PFLAG_FIRE_TRANFER_EVENTS)) {
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_TRANSFEROR) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel_a, event);
switch_event_fire(&event);
}
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_TRANSFEREE) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(b_channel, event);
switch_event_fire(&event);
}
}
switch_ivr_session_transfer(b_session, exten, NULL, NULL);
switch_core_session_rwunlock(b_session);
} else {
switch_event_t *event;
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_ERROR) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Error-Type", "blind_transfer");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Transfer-Exten", exten);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Full-Refer-To", full_ref_to);
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot Blind Transfer 1 Legged calls\n");
switch_channel_set_variable(channel_a, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER_ERROR");
nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"),
NUTAG_SUBSTATE(nua_substate_terminated),
SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"),
SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden\r\n"), SIPTAG_EVENT_STR(etmp),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
}
done:
if (home) {
su_home_unref(home);
home = NULL;
}
switch_safe_free(etmp);
}
static switch_status_t create_info_event(sip_t const *sip,
nua_handle_t *nh, switch_event_t **revent)
{
sip_alert_info_t *alert_info = sip_alert_info(sip);
switch_event_t *event;
if (!(sip && switch_event_create(&event, SWITCH_EVENT_RECV_INFO) == SWITCH_STATUS_SUCCESS)) {
return SWITCH_STATUS_FALSE;
}
if (sip->sip_content_type) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SIP-Content-Type", sip->sip_content_type->c_type);
}
if (sip->sip_from) {
if (sip->sip_from->a_url->url_user) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SIP-From-User", sip->sip_from->a_url->url_user);
}
if (sip->sip_from->a_url->url_host) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SIP-From-Host", sip->sip_from->a_url->url_host);
}
}
if (sip->sip_to) {
if (sip->sip_to->a_url->url_user) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SIP-To-User", sip->sip_to->a_url->url_user);
}
if (sip->sip_to->a_url->url_host) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SIP-To-Host", sip->sip_to->a_url->url_host);
}
}
if (sip->sip_contact) {
if (sip->sip_contact->m_url->url_user) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SIP-Contact-User", sip->sip_contact->m_url->url_user);
}
if (sip->sip_contact->m_url->url_host) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SIP-Contact-Host", sip->sip_contact->m_url->url_host);
}
}
if (sip->sip_call_info) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Call-Info",
sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_call_info));
}
if (alert_info) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Alert-Info", sip_header_as_string(nua_handle_home(nh), (void *) alert_info));
}
if (sip->sip_payload && sip->sip_payload->pl_data) {
switch_event_add_body(event, "%s", sip->sip_payload->pl_data);
}
*revent = event;
return SWITCH_STATUS_SUCCESS;
}
switch_status_t sofia_proxy_sip_i_message(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, switch_core_session_t *session, sip_t const *sip,
sofia_dispatch_event_t *de, tagi_t tags[])
{
switch_core_session_t *other_session = NULL;
const char *session_id_header = sofia_glue_session_id_header(session, profile);
if (session && switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
if (switch_core_session_compare(session, other_session)) {
private_object_t *other_tech_pvt = NULL;
const char *ct = NULL;
char *pl = NULL;
if (sip && sip->sip_payload && sip->sip_payload->pl_data) {
pl = sip->sip_payload->pl_data;
}
other_tech_pvt = (private_object_t *) switch_core_session_get_private(other_session);
if (sip && sip->sip_content_type->c_type && sip->sip_content_type->c_subtype) {
ct = sip->sip_content_type->c_type;
}
nua_message(other_tech_pvt->nh,
TAG_IF(ct, SIPTAG_CONTENT_TYPE_STR(su_strdup(other_tech_pvt->nh->nh_home, ct))),
TAG_IF(!zstr(other_tech_pvt->user_via), SIPTAG_VIA_STR(other_tech_pvt->user_via)),
TAG_IF(pl, SIPTAG_PAYLOAD_STR(su_strdup(other_tech_pvt->nh->nh_home, pl))),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
switch_core_session_rwunlock(other_session);
nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_FALSE;
}
switch_status_t sofia_proxy_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, switch_core_session_t *session, sip_t const *sip,
sofia_dispatch_event_t *de, tagi_t tags[])
{
switch_core_session_t *other_session = NULL;
const char *session_id_header = sofia_glue_session_id_header(session, profile);
if (session && switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
if (switch_core_session_compare(session, other_session)) {
private_object_t *other_tech_pvt = NULL;
const char *ct = NULL;
char *pl = NULL;
switch_channel_t *channel = switch_core_session_get_channel(session);
if (sip && sip->sip_payload && sip->sip_payload->pl_data) {
pl = sip->sip_payload->pl_data;
}
other_tech_pvt = (private_object_t *) switch_core_session_get_private(other_session);
if (sip && sip->sip_content_type->c_type && sip->sip_content_type->c_subtype) {
ct = sip->sip_content_type->c_type;
}
if (sip && sip->sip_content_type->c_type && !strncasecmp(sip->sip_content_type->c_type, "application", 11) && !strcasecmp(sip->sip_content_type->c_subtype, "media_control+xml")) {
if (switch_channel_test_flag(channel, CF_VIDEO)) {
switch_core_media_gen_key_frame(session);
switch_channel_set_flag(channel, CF_VIDEO_REFRESH_REQ);
}
}
nua_info(other_tech_pvt->nh,
TAG_IF(ct, SIPTAG_CONTENT_TYPE_STR(su_strdup(other_tech_pvt->nh->nh_home, ct))),
TAG_IF(!zstr(other_tech_pvt->user_via), SIPTAG_VIA_STR(other_tech_pvt->user_via)),
TAG_IF(pl, SIPTAG_PAYLOAD_STR(su_strdup(other_tech_pvt->nh->nh_home, pl))),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
switch_core_session_rwunlock(other_session);
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_FALSE;
}
void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, switch_core_session_t *session, sip_t const *sip,
sofia_dispatch_event_t *de, tagi_t tags[])
{
/* placeholder for string searching */
const char *signal_ptr;
const char *rec_header;
const char *clientcode_header;
switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0), 0, SWITCH_DTMF_ENDPOINT };
switch_event_t *event;
private_object_t *tech_pvt = NULL;
switch_channel_t *channel = NULL;
const char *session_id_header = sofia_glue_session_id_header(session, profile);
if (session) {
tech_pvt = (private_object_t *) switch_core_session_get_private(session);
channel = switch_core_session_get_channel(session);
}
if (sofia_test_pflag(profile, PFLAG_EXTENDED_INFO_PARSING)) {
if (sip && sip->sip_content_type && sip->sip_content_type->c_type && sip->sip_content_type->c_subtype &&
sip->sip_payload && sip->sip_payload->pl_data) {
if (!strncasecmp(sip->sip_content_type->c_type, "freeswitch", 10)) {
if (!strcasecmp(sip->sip_content_type->c_subtype, "session-event")) {
if (session) {
if (create_info_event(sip, nh, &event) == SWITCH_STATUS_SUCCESS) {
if (switch_core_session_queue_event(session, &event) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "queued freeswitch event for INFO\n");
nua_respond(nh, SIP_200_OK, SIPTAG_CONTENT_TYPE_STR("freeswitch/session-event-response"),
SIPTAG_PAYLOAD_STR("+OK MESSAGE QUEUED"), NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
switch_event_destroy(&event);
nua_respond(nh, SIP_200_OK, SIPTAG_CONTENT_TYPE_STR("freeswitch/session-event-response"),
SIPTAG_PAYLOAD_STR("-ERR MESSAGE NOT QUEUED"), NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
}
} else {
nua_respond(nh, SIP_200_OK, SIPTAG_CONTENT_TYPE_STR("freeswitch/session-event-response"),
SIPTAG_PAYLOAD_STR("-ERR INVALID SESSION"), NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
return;
} else if (!strcasecmp(sip->sip_content_type->c_subtype, "api-request")) {
char *cmd = strdup(sip->sip_payload->pl_data);
char *arg;
switch_stream_handle_t stream = { 0 };
switch_status_t status;
switch_assert(cmd);
SWITCH_STANDARD_STREAM(stream);
switch_assert(stream.data);
if ((arg = strchr(cmd, ':'))) {
*arg++ = '\0';
}
if ((status = switch_api_execute(cmd, arg, NULL, &stream)) == SWITCH_STATUS_SUCCESS) {
nua_respond(nh, SIP_200_OK, SIPTAG_CONTENT_TYPE_STR("freeswitch/api-response"),
SIPTAG_PAYLOAD_STR(stream.data), NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
nua_respond(nh, SIP_200_OK, SIPTAG_CONTENT_TYPE_STR("freeswitch/api-response"),
SIPTAG_PAYLOAD_STR("-ERR INVALID COMMAND"), NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
switch_safe_free(stream.data);
switch_safe_free(cmd);
return;
}
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
return;
}
}
}
if (session) {
const char *vval;
/* Barf if we didn't get our private */
assert(switch_core_session_get_private(session));
sofia_glue_set_extra_headers(session, sip, SOFIA_SIP_INFO_HEADER_PREFIX);
if (!zstr(profile->proxy_info_content_types) && sip && sip->sip_content_type && sip->sip_content_type->c_type && sip->sip_content_type->c_subtype &&
(!strcasecmp(profile->proxy_info_content_types,"all") || strstr(profile->proxy_info_content_types,sip->sip_content_type->c_type))) {
switch_core_session_t *other_session;
if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) {
char *pl = NULL;
char *ct = NULL;
char *extra_headers = NULL;
char *unknown = NULL;
private_object_t *other_tech_pvt = switch_core_session_get_private(other_session);
ct = switch_core_session_strdup(other_session, (char*)sip->sip_content_type->c_type);
if (sip->sip_payload && sip->sip_payload->pl_data) {
pl = switch_core_session_strdup(other_session,(char*)sip->sip_payload->pl_data);
}
unknown = sofia_glue_get_non_extra_unknown_headers(sip);
extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_INFO_HEADER_PREFIX);
nua_info(other_tech_pvt->nh,
SIPTAG_CONTENT_TYPE_STR(ct),
TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)),
TAG_IF(!zstr(unknown), SIPTAG_HEADER_STR(unknown)),
TAG_IF(!zstr(other_tech_pvt->user_via), SIPTAG_VIA_STR(other_tech_pvt->user_via)),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_IF(!zstr(pl), SIPTAG_PAYLOAD_STR(pl)),
TAG_END());
switch_safe_free(extra_headers);
switch_safe_free(unknown);
switch_core_session_rwunlock(other_session);
}
}
if (sip && sip->sip_content_type && sip->sip_content_type->c_type && !strcasecmp(sip->sip_content_type->c_type, "freeswitch/data")) {
char *data = NULL;
if (sip->sip_payload && sip->sip_payload->pl_data) {
data = sip->sip_payload->pl_data;
}
if ((vval = switch_channel_get_variable(channel, "sip_copy_custom_headers")) && switch_true(vval)) {
switch_core_session_t *nsession = NULL;
switch_core_session_get_partner(session, &nsession);
if (nsession) {
switch_core_session_message_t *msg;
switch_ivr_transfer_variable(session, nsession, SOFIA_SIP_INFO_HEADER_PREFIX_T);
msg = switch_core_session_alloc(nsession, sizeof(*msg));
MESSAGE_STAMP_FFL(msg);
msg->message_id = SWITCH_MESSAGE_INDICATE_INFO;
msg->string_array_arg[2] = switch_core_session_strdup(nsession, data);
msg->from = __FILE__;
switch_core_session_queue_message(nsession, msg);
switch_core_session_rwunlock(nsession);
}
}
}
if (sip && sip->sip_content_type && sip->sip_content_type->c_subtype && sip->sip_content_type->c_type &&
!strncasecmp(sip->sip_content_type->c_type, "message", 7) &&
!strcasecmp(sip->sip_content_type->c_subtype, "update_display")) {
sofia_update_callee_id(session, profile, sip, SWITCH_TRUE);
goto end;
}
if (sip && sip->sip_content_type && sip->sip_content_type->c_type && sip->sip_content_type->c_subtype &&
sip->sip_payload && sip->sip_payload->pl_data) {
if (!strncasecmp(sip->sip_content_type->c_type, "application", 11) && !strcasecmp(sip->sip_content_type->c_subtype, "media_control+xml")) {
if (switch_channel_test_flag(channel, CF_VIDEO)) {
switch_core_media_gen_key_frame(session);
switch_channel_set_flag(channel, CF_VIDEO_REFRESH_REQ);
}
} else if (!strncasecmp(sip->sip_content_type->c_type, "application", 11) &&
!strcasecmp(sip->sip_content_type->c_subtype, "vnd.nortelnetworks.digits")) {
int tmp;
if ((signal_ptr = switch_stristr("d=", sip->sip_payload->pl_data))) {
signal_ptr = signal_ptr + 2;
while (*signal_ptr && *signal_ptr == ' ') {
signal_ptr++;
}
if (*signal_ptr && (*signal_ptr == '*' || *signal_ptr == '#' || *signal_ptr == 'A' || *signal_ptr == 'B'
|| *signal_ptr == 'C' || *signal_ptr == 'D')) {
dtmf.digit = *signal_ptr;
} else {
tmp = atoi(signal_ptr);
dtmf.digit = switch_rfc2833_to_char(tmp);
}
dtmf.duration = 100;
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Bad signal\n");
goto end;
}
} else if (!strncasecmp(sip->sip_content_type->c_type, "application", 11) && !strcasecmp(sip->sip_content_type->c_subtype, "dtmf-relay")) {
/* Try and find signal information in the payload */
if ((signal_ptr = switch_stristr("Signal=", sip->sip_payload->pl_data))) {
int tmp;
/* move signal_ptr where we need it (right past Signal=) */
signal_ptr = signal_ptr + 7;
/* handle broken devices with spaces after the = (cough) VegaStream (cough) */
while (*signal_ptr && *signal_ptr == ' ')
signal_ptr++;
if (*signal_ptr
&& (*signal_ptr == '*' || *signal_ptr == '#' || *signal_ptr == 'A' || *signal_ptr == 'B' || *signal_ptr == 'C'
|| *signal_ptr == 'D')) {
dtmf.digit = *signal_ptr;
} else {
tmp = atoi(signal_ptr);
dtmf.digit = switch_rfc2833_to_char(tmp);
}
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Bad signal\n");
goto end;
}
if ((signal_ptr = switch_stristr("Duration=", sip->sip_payload->pl_data))) {
int tmp;
signal_ptr += 9;
/* handle broken devices with spaces after the = (cough) VegaStream (cough) */
while (*signal_ptr && *signal_ptr == ' ')
signal_ptr++;
if ((tmp = atoi(signal_ptr)) <= 0) {
tmp = switch_core_default_dtmf_duration(0);
}
dtmf.duration = tmp * 8;
}
} else if (!strncasecmp(sip->sip_content_type->c_type, "application", 11) && !strcasecmp(sip->sip_content_type->c_subtype, "dtmf")) {
int tmp = atoi(sip->sip_payload->pl_data);
dtmf.digit = switch_rfc2833_to_char(tmp);
}
if (dtmf.digit) {
if (tech_pvt->mparams.dtmf_type == DTMF_INFO ||
sofia_test_pflag(tech_pvt->profile, PFLAG_LIBERAL_DTMF) || switch_channel_test_flag(tech_pvt->channel, CF_LIBERAL_DTMF)) {
/* queue it up */
switch_channel_queue_dtmf(channel, &dtmf);
/* print debug info */
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "INFO DTMF(%c)\n", dtmf.digit);
if (switch_channel_test_flag(channel, CF_PROXY_MODE)) {
const char *uuid;
switch_core_session_t *session_b;
if ((uuid = switch_channel_get_partner_uuid(channel)) && (session_b = switch_core_session_locate(uuid))) {
while (switch_channel_has_dtmf(channel)) {
switch_dtmf_t idtmf = { 0, 0 };
if (switch_channel_dequeue_dtmf(channel, &idtmf) == SWITCH_STATUS_SUCCESS) {
switch_core_session_send_dtmf(session_b, &idtmf);
}
}
switch_core_session_rwunlock(session_b);
}
}
/* Send 200 OK response */
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
"IGNORE INFO DTMF(%c) (This channel was not configured to use INFO DTMF!)\n", dtmf.digit);
}
goto end;
}
}
if ((clientcode_header = sofia_glue_get_unknown_header(sip, "x-clientcode"))) {
if (!zstr(clientcode_header)) {
switch_channel_set_variable(channel, "call_clientcode", clientcode_header);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Setting CMC to %s\n", clientcode_header);
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
goto end;
}
if ((rec_header = sofia_glue_get_unknown_header(sip, "record"))) {
if (zstr(profile->record_template)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Record attempted but no template defined.\n");
nua_respond(nh, 488, "Recording not enabled", NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
if (!strcasecmp(rec_header, "on")) {
char *file = NULL, *tmp = NULL;
if (switch_true(switch_channel_get_variable(channel, "sip_disable_recording"))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Record attempted but is disabled by sip_disable_recording variable.\n");
nua_respond(nh, 488, "Recording disabled for this channel", NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
} else {
tmp = switch_mprintf("%s%s%s", profile->record_path ? profile->record_path : "${recordings_dir}",
SWITCH_PATH_SEPARATOR, profile->record_template);
file = switch_channel_expand_variables(channel, tmp);
switch_ivr_record_session(session, file, 0, NULL);
switch_channel_set_variable(channel, "sofia_record_file", file);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Recording %s to %s\n", switch_channel_get_name(channel),
file);
switch_safe_free(tmp);
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
if (file != profile->record_template) {
free(file);
file = NULL;
}
}
} else {
const char *file;
if ((file = switch_channel_get_variable(channel, "sofia_record_file"))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Done recording %s to %s\n",
switch_channel_get_name(channel), file);
switch_ivr_stop_record_session(session, file);
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
} else {
nua_respond(nh, 488, "Nothing to stop", NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
}
}
}
}
}
end:
if (create_info_event(sip, nh, &event) == SWITCH_STATUS_SUCCESS) {
if (channel) {
switch_channel_event_set_data(channel, event);
}
switch_event_fire(&event);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG1, "dispatched freeswitch event for INFO\n");
}
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)),
TAG_END());
return;
}
void sofia_handle_sip_i_reinvite(switch_core_session_t *session,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de,
tagi_t tags[])
{
char *call_info = NULL;
switch_channel_t *channel = NULL;
private_object_t *tech_pvt = NULL;
nua_respond(nh, SIP_100_TRYING, TAG_END());
if (session) {
channel = switch_core_session_get_channel(session);
tech_pvt = switch_core_session_get_private(session);
if (sip && sip->sip_payload && sip->sip_payload->pl_data) {
tech_pvt->mparams.last_sdp_str = switch_core_session_strdup(session, sip->sip_payload->pl_data);
}
}
if (session && profile && sip && sofia_test_pflag(profile, PFLAG_TRACK_CALLS)) {
switch_channel_t *channel = switch_core_session_get_channel(session);
private_object_t *tech_pvt = (private_object_t *) switch_core_session_get_private(session);
char network_ip[80];
int network_port = 0;
char via_space[2048];
char branch[16] = "";
sofia_glue_store_session_id(session, profile, sip, 0);
sofia_clear_flag(tech_pvt, TFLAG_GOT_ACK);
sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
switch_stun_random_string(branch, sizeof(branch) - 1, "0123456789abcdef");
switch_snprintf(via_space, sizeof(via_space), "SIP/2.0/UDP %s;rport=%d;branch=%s", network_ip, network_port, branch);
switch_channel_set_variable(channel, "sip_full_via", via_space);
switch_channel_set_variable_printf(channel, "sip_network_port", "%d", network_port);
switch_channel_set_variable_printf(channel, "sip_recieved_port", "%d", network_port);
switch_channel_set_variable_printf(channel, "sip_via_rport", "%d", network_port);
switch_core_recovery_track(session);
}
if (profile && sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE)) {
if (channel && sip->sip_call_info) {
char *p;
if ((call_info = sip_header_as_string(nua_handle_home(nh), (void *) sip->sip_call_info))) {
if (switch_stristr("appearance", call_info)) {
switch_channel_set_variable(channel, "presence_call_info_full", call_info);
if ((p = strchr(call_info, ';'))) {
switch_channel_set_variable(channel, "presence_call_info", p + 1);
}
}
su_free(nua_handle_home(nh), call_info);
}
}
}
if (channel) {
if (tech_pvt->mparams.last_sdp_str) {
tech_pvt->mparams.prev_sdp_str = tech_pvt->mparams.last_sdp_str;
}
tech_pvt->mparams.last_sdp_str = NULL;
if (sip->sip_payload && sip->sip_payload->pl_data) {
if (!zstr(tech_pvt->mparams.prev_sdp_str) && strcmp(tech_pvt->mparams.prev_sdp_str, sip->sip_payload->pl_data)) {
switch_channel_set_variable(channel, "sip_reinvite_sdp", sip->sip_payload->pl_data);
tech_pvt->mparams.last_sdp_str = switch_core_session_strdup(session, sip->sip_payload->pl_data);
} else {
tech_pvt->mparams.last_sdp_str = tech_pvt->mparams.prev_sdp_str;
}
}
switch_channel_execute_on(channel, "execute_on_sip_reinvite");
}
}
switch_status_t sofia_locate_user(char* user, switch_core_session_t *session, sip_t const *sip, switch_xml_t* x_user)
{
char *username, *domain;
switch_event_t *v_event = NULL;
switch_status_t result = SWITCH_STATUS_FALSE;
if (!session) {
return SWITCH_STATUS_FALSE;
}
if (zstr(user)) {
return SWITCH_STATUS_FALSE;
}
if (!(username = switch_core_session_strdup(session, user))) {
return SWITCH_STATUS_FALSE;
}
if (!(domain = strchr(username, '@'))) {
return SWITCH_STATUS_FALSE;
}
*domain++ = '\0';
if (switch_event_create(&v_event, SWITCH_EVENT_REQUEST_PARAMS) == SWITCH_STATUS_SUCCESS) {
sip_unknown_t *un;
for (un = sip->sip_unknown; un; un = un->un_next) {
switch_event_add_header_string(v_event, SWITCH_STACK_BOTTOM, un->un_name, un->un_value);
};
}
result = switch_xml_locate_user_merged("id", username, domain, NULL, x_user, v_event);
if (v_event) {
switch_event_destroy(&v_event);
}
return result;
}
void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip, sofia_dispatch_event_t *de, tagi_t tags[])
{
char key[128] = "";
sip_unknown_t *un;
sip_remote_party_id_t *rpid = NULL;
sip_p_asserted_identity_t *passerted = NULL;
sip_p_preferred_identity_t *ppreferred = NULL;
sip_privacy_t *privacy = NULL;
sip_alert_info_t *alert_info = NULL;
sip_call_info_t *call_info = NULL;
private_object_t *tech_pvt = NULL;
switch_channel_t *channel = NULL;
//const char *channel_name = NULL;
const char *displayname = NULL;
const char *destination_number = NULL;
const char *from_user = NULL, *from_host = NULL;
const char *referred_by_user = NULL;//, *referred_by_host = NULL;
const char *context = NULL;
const char *dialplan = NULL;
char network_ip[80];
char proxied_client_ip[80];
switch_event_t *v_event = NULL;
switch_xml_t x_user = NULL;
uint32_t sess_count = switch_core_session_count();
uint32_t sess_max = switch_core_session_limit(0);
int is_auth = 0, calling_myself = 0;
int network_port = 0;
char *is_nat = NULL;
char *aniii = NULL;
char acl_token[512] = "";
sofia_transport_t transport;
const char *gw_name = NULL;
const char *gw_param_name = NULL;
char *call_info_str = NULL;
nua_handle_t *bnh = NULL;
char sip_acl_authed_by[512] = "";
char sip_acl_token[512] = "";
const char *dialog_from_user = "", *dialog_from_host = "", *to_user = "", *to_host = "", *contact_user = "", *contact_host = "";
const char *user_agent = "", *call_id = "";
url_t *from = NULL, *to = NULL, *contact = NULL;
const char *to_tag = "";
const char *from_tag = "";
char *sql = NULL;
char *acl_context = NULL;
const char *r_sdp = NULL;
int is_tcp = 0, is_tls = 0;
const char *uparams = NULL;
char *name_params = NULL;
const char *req_uri = NULL;
char *req_user = NULL;
switch_time_t sip_invite_time;
const char *session_id_header;
sofia_glue_store_session_id(session, profile, sip, 0);
session_id_header = sofia_glue_session_id_header(session, profile);
if (sip && sip->sip_contact && sip->sip_contact->m_url->url_params) {
uparams = sip->sip_contact->m_url->url_params;
} else {
uparams = NULL;
}
if (uparams) {
if (switch_stristr("transport=tcp", uparams)) {
is_tcp = 1;
} else if (switch_stristr("transport=tls", uparams)) {
is_tls = 1;
}
}
profile->ib_calls++;
if (sip && sip->sip_payload && sip->sip_payload->pl_data) {
r_sdp = sip->sip_payload->pl_data;
}
if (!session || (sess_count >= sess_max || !sofia_test_pflag(profile, PFLAG_RUNNING))) {
nua_respond(nh, 503, "Maximum Calls In Progress", SIPTAG_RETRY_AFTER_STR("300"),
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
goto fail;
}
tech_pvt = switch_core_session_get_private(session);
sip_invite_time = switch_micro_time_now();
if (!sip || !sip->sip_request || !sip->sip_request->rq_method_name) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received an invalid packet!\n");
nua_respond(nh, SIP_503_SERVICE_UNAVAILABLE,
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
goto fail;
}
if (!(sip->sip_contact)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO CONTACT!\n");
nua_respond(nh, 400, "Missing Contact Header",
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
goto fail;
}
sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "%s receiving invite from %s:%d version: %s\n",
switch_channel_get_name(tech_pvt->channel), network_ip, network_port, switch_version_full_human());
if (sip->sip_via && sip->sip_via->v_protocol && switch_stristr("sip/2.0/ws", sip->sip_via->v_protocol)) {
is_nat = "websockets";
}
if (r_sdp) {
switch_core_media_set_sdp_codec_string(session, r_sdp, SDP_TYPE_REQUEST);
}
if (sofia_test_pflag(profile, PFLAG_AGGRESSIVE_NAT_DETECTION) ||
(sofia_test_pflag(profile, PFLAG_TLS_ALWAYS_NAT) && (is_tcp || is_tls)) ||
(!is_tcp && !is_tls && (zstr(network_ip) || !switch_check_network_list_ip(network_ip, profile->local_network)) &&
profile->server_rport_level >= 2 && sip->sip_user_agent &&
sip->sip_user_agent->g_string &&
(!strncasecmp(sip->sip_user_agent->g_string, "Polycom", 7) || !strncasecmp(sip->sip_user_agent->g_string, "KIRK Wireless Server", 20)))
) {
if (sip->sip_via) {
const char *port = sip->sip_via->v_port;
const char *host = sip->sip_via->v_host;
if (host && sip->sip_via->v_received) {
is_nat = "via received";
} else if (host && strcmp(network_ip, host)) {
is_nat = "via host";
} else if (port && atoi(port) != network_port) {
is_nat = "via port";
}
}
}
if (!is_nat && profile->nat_acl_count) {
uint32_t x = 0;
int contact_private_ip = 1;
int network_private_ip = 0;
char *last_acl = NULL;
const char *contact_host = NULL;
if (sip->sip_via && sip->sip_via->v_host) {
contact_host = sip->sip_via->v_host;
} else if (sip->sip_contact) {
contact_host = sip->sip_contact->m_url->url_host;
}
if (!zstr(contact_host)) {
/* NAT mode double check logic and examples.
Example 1: the contact_host is 192.168.1.100 and the network_ip is also 192.168.1.100 the end point
is most likely behind nat with us so we need to veto that decision to turn on nat processing.
Example 2: the contact_host is 192.168.1.100 and the network_ip is 192.0.2.100 which is a public internet ip
the remote endpoint is likely behind a remote nat traversing the public internet.
This secondary check is here to double check the conclusion of nat settigs to ensure we don't set net
in cases where we don't really need to be doing this.
Why would you want to do this? Well if your FreeSWITCH is behind nat and you want to talk to endpoints behind
remote NAT over the public internet in addition to endpoints behind nat with you. This simplifies that process.
*/
for (x = 0; x < profile->nat_acl_count; x++) {
last_acl = profile->nat_acl[x];
if ((contact_private_ip = switch_check_network_list_ip(contact_host, last_acl))) {
break;
}
}
if (contact_private_ip) {
for (x = 0; x < profile->nat_acl_count; x++) {
if ((network_private_ip = switch_check_network_list_ip(network_ip, profile->nat_acl[x]))) {
break;
}
}
}
if (contact_private_ip && !network_private_ip) {
is_nat = last_acl;
}
}
}
if (profile->acl_count) {
uint32_t x = 0;
int ok = 1;
char *last_acl = NULL;
const char *token = NULL;
int acl_port = sofia_test_pflag(profile, PFLAG_USE_PORT_FOR_ACL_CHECK) ? network_port : 0;
for (x = 0; x < profile->acl_count; x++) {
last_acl = profile->acl[x];
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "verifying acl \"%s\" for ip/port %s:%i.\n",
switch_str_nil(last_acl), network_ip, acl_port);
if ((ok = switch_check_network_list_ip_port_token(network_ip, acl_port, last_acl, &token))) {
if (profile->acl_pass_context[x]) {
acl_context = profile->acl_pass_context[x];
}
if(!token && profile->acl_inbound_x_token_header) {
const char * x_auth_token = sofia_glue_get_unknown_header(sip, profile->acl_inbound_x_token_header);
if (!zstr(x_auth_token)) {
token = x_auth_token;
}
}
break;
}
if (profile->acl_fail_context[x]) {
acl_context = profile->acl_fail_context[x];
} else {
acl_context = NULL;
}
}
if (ok) {
if (token) {
switch_set_string(acl_token, token);
}
if (sofia_test_pflag(profile, PFLAG_AUTH_CALLS)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "IP %s Approved by acl \"%s[%s]\". Access Granted.\n",
network_ip, switch_str_nil(last_acl), acl_token);
switch_set_string(sip_acl_authed_by, last_acl);
switch_set_string(sip_acl_token, acl_token);
is_auth = 1;
}
} else {
int network_ip_is_proxy = 0;
const char* x_auth_ip = network_ip;
/* Check if network_ip is a proxy allowed to send us calls */
if (profile->proxy_acl_count) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%d acls to check for proxy\n", profile->proxy_acl_count);
for (x = 0; x < profile->proxy_acl_count; x++) {
last_acl = profile->proxy_acl[x];
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "checking %s against acl %s\n", network_ip, last_acl);
if (switch_check_network_list_ip_port_token(network_ip, network_port, last_acl, &token)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s is a proxy according to the %s acl\n", network_ip, last_acl);
network_ip_is_proxy = 1;
break;
}
}
}
/*
* if network_ip is a proxy allowed to send calls, check for auth
* ip header and see if it matches against the inbound acl
*/
if (network_ip_is_proxy) {
const char * x_auth_port = sofia_glue_get_unknown_header(sip, "X-AUTH-PORT");
int x_auth_port_i = sofia_test_pflag(profile, PFLAG_USE_PORT_FOR_ACL_CHECK) ? zstr(x_auth_port) ? 0 : atoi(x_auth_port) : 0;
/*
* if network_ip is a proxy allowed to send calls,
* authorize call if proxy provided matched token header
*/
if (profile->acl_proxy_x_token_header) {
const char * x_auth_token = sofia_glue_get_unknown_header(sip, profile->acl_proxy_x_token_header);
if (!zstr(x_auth_token)) {
token = x_auth_token;
switch_copy_string(proxied_client_ip, x_auth_ip, sizeof(proxied_client_ip));
ok = 1;
}
}
if (!ok && (x_auth_ip = sofia_glue_get_unknown_header(sip, "X-AUTH-IP")) && !zstr(x_auth_ip)) {
for (x = 0; x < profile->acl_count; x++) {
last_acl = profile->acl[x];
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "verifying acl \"%s\" from proxy for ip/port %s:%i.\n",
switch_str_nil(last_acl), x_auth_ip, x_auth_port_i);
if ((ok = switch_check_network_list_ip_port_token(x_auth_ip, x_auth_port_i, last_acl, &token))) {
switch_copy_string(proxied_client_ip, x_auth_ip, sizeof(proxied_client_ip));
if (profile->acl_pass_context[x]) {
acl_context = profile->acl_pass_context[x];
}
break;
}
if (profile->acl_fail_context[x]) {
acl_context = profile->acl_fail_context[x];
} else {
acl_context = NULL;
}
}
} else {
x_auth_ip = network_ip;
}
}
if (ok) {
if (token) {
switch_set_string(acl_token, token);
}
if (sofia_test_pflag(profile, PFLAG_AUTH_CALLS)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "IP %s Approved by acl \"%s[%s]\". Access Granted.\n",
x_auth_ip, switch_str_nil(last_acl), acl_token);
switch_set_string(sip_acl_authed_by, last_acl);
switch_set_string(sip_acl_token, acl_token);
is_auth = 1;
}
} else {
if (!sofia_test_pflag(profile, PFLAG_AUTH_CALLS)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "IP %s Rejected by acl \"%s\"\n", x_auth_ip, switch_str_nil(last_acl));
if (!acl_context) {
nua_respond(nh, SIP_403_FORBIDDEN,
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
goto fail;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "IP %s Rejected by acl \"%s\". Falling back to Digest auth.\n",
x_auth_ip, switch_str_nil(last_acl));
}
}
}
}
}
if (!is_auth && sofia_test_pflag(profile, PFLAG_AUTH_CALLS) && sofia_test_pflag(profile, PFLAG_AUTH_CALLS_ACL_ONLY)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "IP/Port %s %i Rejected by acls and auth-calls-acl-only flag is set, rejecting call\n",
network_ip, network_port);
nua_respond(nh, SIP_403_FORBIDDEN,
TAG_IF(!zstr(session_id_header), SIPTAG_HEADER_STR(session_id_header)), TAG_END());
goto fail;
}
if (!is_auth && sofia_test_pflag(profile, PFLAG_AUTH_CALLS) && sofia_test_pflag(profile, PFLAG_BLIND_AUTH)) {
char *user = NULL;
switch_status_t blind_result = SWITCH_STATUS_FALSE;
if (!strcmp(network_ip, profile->sipip) && network_port == profile->sip_port) {
calling_myself++;
}
if (sip->sip_from) {
user = switch_core_session_sprintf(session, "%s@%s", sip->sip_from->a_url->url_user, sip->sip_from->a_url->url_host);
blind_result = sofia_locate_user(user, session, sip, &x_user);
}
if (!sofia_test_pflag(profile, PFLAG_BLIND_AUTH_ENFORCE_RESULT) || blind_result == SWITCH_STATUS_SUCCESS) {
is_auth++;
} else if (sofia_test_pflag(profile, PFLAG_BLIND_AUTH_REPLY_403)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "blind auth enforce 403 enabled and couldn't find user %s, rejecting call\n", user);
nua_respond(nh, SIP_403_FORBIDDEN, TAG_END());
goto fail;
}
}
tech_pvt->from_user = switch_core_session_strdup(session, sip->sip_from->a_url->url_user);
tech_pvt->mparams.remote_ip = switch_core_session_strdup(session, network_ip);
tech_pvt->mparams.remote_port = network_port;
if (!is_auth &&
(sofia_test_pflag(profile, PFLAG_AUTH_CALLS)
|| (!sofia_test_pflag(profile, PFLAG_BLIND_AUTH) && (sip->sip_proxy_authorization || sip->sip_authorization)))) {
if (!strcmp(network_ip, profile->sipip) && network_port == profile->sip_port) {
calling_myself++;
} else {
switch_event_create(&v_event, SWITCH_EVENT_REQUEST_PARAMS);
if (sofia_reg_handle_register(nua, profile, nh, sip, de, REG_INVITE, key, sizeof(key), &v_event, NULL, NULL, &x_user)) {
if (v_event) {
switch_event_destroy(&v_event);
}
if (x_user) {
switch_xml_free(x_user);
}
if (sip->sip_authorization || sip->sip_proxy_authorization) {
goto fail;
}
return;
}
}
is_auth++;
}
channel = tech_pvt->channel = switch_core_session_get_channel(session);
switch_channel_set_variable_printf(channel, "sip_local_network_addr", "%s", profile->extsipip ? profile->extsipip : profile->sipip);
switch_channel_set_variable_printf(channel, "sip_network_ip", "%s", network_ip);
switch_channel_set_variable_printf(channel, "sip_network_port", "%d", network_port);
switch_channel_set_variable_printf(channel, "sip_invite_stamp", "%" SWITCH_TIME_T_FMT, sip_invite_time);
if (*acl_token) {
switch_channel_set_variable(channel, "acl_token", acl_token);
if (sofia_locate_user(acl_token, session, sip, &x_user) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Authenticating user %s\n", acl_token);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Error Authenticating user %s\n", acl_token);
if (sofia_test_pflag(profile, PFLAG_AUTH_REQUIRE_USER)) {
nua_respond(nh, SIP_480_TEMPORARILY_UNAVAILABLE, TAG_END());
goto fail;
}
}
}
if (sip->sip_via) {
char tmp[35] = "";
const char *ipv6 = strchr(tech_pvt->mparams.remote_ip, ':');
transport = sofia_glue_via2transport(sip->sip_via);
tech_pvt->record_route =
switch_core_session_sprintf(session,
"sip:%s%s%s:%d;transport=%s",
ipv6 ? "[" : "", tech_pvt->mparams.remote_ip, ipv6 ? "]" : "",
tech_pvt->mparams.remote_port, sofia_glue_transport2str(transport));
switch_channel_set_variable(channel, "sip_received_ip", tech_pvt->mparams.remote_ip);
snprintf(tmp, sizeof(tmp), "%d", tech_pvt->mparams.remote_port);
switch_channel_set_variable(channel, "sip_received_port", tmp);
switch_channel_set_variable(channel, "sip_via_protocol", sofia_glue_transport2str(sofia_glue_via2transport(sip->sip_via)));
}
if (*key != '\0') {
tech_pvt->key = switch_core_session_strdup(session, key);
}
if (is_auth) {
switch_channel_set_variable(channel, "sip_authorized", "true");
if (!zstr(sip_acl_authed_by)) {
switch_channel_set_variable(channel, "sip_acl_authed_by", sip_acl_authed_by);
}
if (!zstr(sip_acl_token)) {
switch_channel_set_variable(channel, "sip_acl_token", sip_acl_token);
}
}
if (calling_myself) {
switch_channel_set_variable(channel, "sip_looped_call", "true");
}
tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session),
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, MODNAME, NULL, NULL);
switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
if (x_user) {
const char *ruser = NULL, *rdomain = NULL, *user = switch_xml_attr(x_user, "id"), *domain = switch_xml_attr(x_user, "domain-name");
if (v_event) {
switch_event_header_t *hp;
for (hp = v_event->headers; hp; hp = hp->next) {
switch_channel_set_variable(channel, hp->name, hp->value);
}
ruser = switch_event_get_header(v_event, "user_name");
rdomain = switch_event_get_header(v_event, "domain_name");
switch_channel_set_variable(channel, "requested_user_name", ruser);
switch_channel_set_variable(channel, "requested_domain_name", rdomain);
}
if (!user) user = ruser;
if (!domain) domain = rdomain;
switch_ivr_set_user_xml(session, NULL, user, domain, x_user);
switch_xml_free(x_user);
x_user = NULL;
}
if (v_event) {
switch_event_destroy(&v_event);
}
if (sip->sip_from) {
from_user = sip->sip_from->a_url->url_user;
from_host = sip->sip_from->a_url->url_host;
//channel_name = url_set_chanvars(session, sip->sip_from->a_url, sip_from);
if (sip->sip_from->a_url->url_params) {
aniii = switch_find_parameter(sip->sip_from->a_url->url_params, "isup-oli", switch_core_session_get_pool(session));
}
if (!zstr(from_user)) {
if (*from_user == '+') {
switch_channel_set_variable(channel, "sip_from_user_stripped", (const char *) (from_user + 1));
} else {
switch_channel_set_variable(channel, "sip_from_user_stripped", from_user);
}
}
switch_channel_set_variable(channel, "sip_from_comment", sip->sip_from->a_comment);
if (sip->sip_from->a_params) {
set_variable_sip_param(channel, "from", sip->sip_from->a_params);
}
switch_channel_set_variable(channel, "sofia_profile_name", profile->name);
switch_channel_set_variable(channel, "sofia_profile_url", profile->url);
switch_channel_set_variable(channel, "recovery_profile_name", profile->name);
switch_channel_set_variable(channel, "sofia_profile_domain_name", profile->domain_name);
if (!zstr(sip->sip_from->a_display)) {
displayname = sip->sip_from->a_display;
} else {
displayname = zstr(from_user) ? "unknown" : from_user;
}
}
if ((rpid = sip_remote_party_id(sip))) {
if (rpid->rpid_url->url_user) {
char *full_rpid_header = sip_header_as_string(nh->nh_home, (void *) rpid);
from_user = rpid->rpid_url->url_user;
if (!zstr(full_rpid_header)) {
switch_channel_set_variable(channel, "sip_Remote-Party-ID", full_rpid_header);
}
}
if (!zstr(rpid->rpid_display)) {
displayname = rpid->rpid_display;
}
switch_channel_set_variable(channel, "sip_cid_type", "rpid");
tech_pvt->cid_type = CID_TYPE_RPID;
}
if ((passerted = sip_p_asserted_identity(sip))) {
if (passerted->paid_url->url_user) {
char *full_paid_header = sip_header_as_string(nh->nh_home, (void *) passerted);
//char *full_paid_header = (char *)(passerted->paid_common->h_data);
from_user = passerted->paid_url->url_user;
if (!zstr(full_paid_header)) {
if (profile->paid_type == PAID_DEFAULT || profile->paid_type == PAID_USER) {
switch_channel_set_variable(channel, "sip_P-Asserted-Identity", from_user);
} else if (profile->paid_type == PAID_USER_DOMAIN) {
switch_channel_set_variable(channel, "sip_P-Asserted-Identity",
switch_core_session_sprintf(session, "%s@%s", passerted->paid_url->url_user, passerted->paid_url->url_host));
} else if (profile->paid_type == PAID_VERBATIM) {
switch_channel_set_variable(channel, "sip_P-Asserted-Identity", full_paid_header);
}
}
}
if (!zstr(passerted->paid_display)) {
displayname = passerted->paid_display;
}
switch_channel_set_variable(channel, "sip_cid_type", "pid");
tech_pvt->cid_type = CID_TYPE_PID;
}
if ((ppreferred = sip_p_preferred_identity(sip))) {
if (ppreferred->ppid_url->url_user) {
char *full_ppid_header = sip_header_as_string(nh->nh_home, (void *) ppreferred);
from_user = ppreferred->ppid_url->url_user;
if (!zstr(full_ppid_header)) {
switch_channel_set_variable(channel, "sip_P-Preferred-Identity", full_ppid_header);
}
}
if (!zstr(ppreferred->ppid_display)) {
displayname = ppreferred->ppid_display;
}
switch_channel_set_variable(channel, "sip_cid_type", "pid");
tech_pvt->cid_type = CID_TYPE_PID;
}
if (from_user) {
check_decode(from_user, session);
if ((name_params = strchr(from_user, ';'))) {
*name_params++ = '\0';
switch_channel_set_variable(channel, "sip_name_params", name_params);
}
}
extract_header_vars(profile, sip, session, nh);
sofia_add_invite_header_to_chanvars(channel, nh, sip->sip_allow, "sip_allow");
req_uri = url_set_chanvars(session, sip->sip_request->rq_url, sip_req);
if (sip->sip_request->rq_url->url_user) {
req_user = switch_core_session_strdup(session, sip->sip_request->rq_url->url_user);
if (profile->parse_invite_tel_params) {
if (strchr(req_user, ';')) {
int argc1, x1 = 0;
char *argv1[32] = { 0 };
if ((argc1 = switch_separate_string(req_user, ';', argv1, (sizeof(argv1) / sizeof(argv1[0]))))) {
for (x1 = 0; x1 < argc1; x1++) {
if (x1 == 0) {
switch_channel_set_variable(channel, "sip_req_user", argv1[0]);
} else {
int argc2 = 0;
char *argv2[2] = { 0 };
if ((argc2 = switch_separate_string(argv1[x1], '=', argv2, (sizeof(argv2) / sizeof(argv2[0])))) == 2) {
char *var_name = NULL;
var_name = switch_mprintf("sip_invite_%s", argv2[0]);
switch_channel_set_variable(channel, var_name, argv2[1]);
switch_safe_free( var_name );
} else {
char *var_name = NULL;
var_name = switch_mprintf("sip_invite_%s", argv1[x1]);
switch_channel_set_variable(channel, var_name, "true");
switch_safe_free( var_name );
}
}
}
}
}
}
}
if (sofia_test_pflag(profile, PFLAG_FULL_ID)) {
destination_number = req_uri;
} else {
destination_number = req_user;
}
if (sip->sip_request->rq_url->url_params && (sofia_glue_find_parameter(sip->sip_request->rq_url->url_params, "intercom=true"))) {
switch_channel_set_variable(channel, "sip_auto_answer_detected", "true");
}
if (!destination_number && sip->sip_to) {
destination_number = sip->sip_to->a_url->url_user;
}
/* The human network, OH THE HUMANITY!!! lets send invites with no number! */
if (!destination_number && sip->sip_from) {
destination_number = sip->sip_from->a_url->url_user;
}
if (destination_number) {
check_decode(destination_number, session);
} else {
destination_number = "service";
}
if (sip->sip_to) {
const char *host, *user;
int port, check_nat = 0;
url_t *transport_url;
if (sip->sip_record_route) {
transport_url = sip->sip_record_route->r_url;
} else {
transport_url = sip->sip_contact->m_url;
}
transport = sofia_glue_url2transport(transport_url);
tech_pvt->transport = transport;
url_set_chanvars(session, sip->sip_to->a_url, sip_to);
if (switch_channel_get_variable(channel, "sip_to_uri")) {
const char *ipv6;
const char *tmp, *at, *url = NULL;
host = switch_channel_get_variable(channel, "sip_to_host");
user = switch_channel_get_variable(channel, "sip_to_user");
switch_channel_set_variable(channel, "sip_to_comment", sip->sip_to->a_comment);
if (sip->sip_to->a_params) {
set_variable_sip_param(channel, "to", sip->sip_to->a_params);
}
if (sip->sip_contact->m_url->url_port) {
port = atoi(sip->sip_contact->m_url->url_port);
} else {
port = sofia_glue_transport_has_tls(transport) ? profile->tls_sip_port : profile->extsipport;
}
ipv6 = strchr(host, ':');
tech_pvt->to_uri =
switch_core_session_sprintf(session,
"sip:%s@%s%s%s:%d;transport=%s",
user, ipv6 ? "[" : "", host, ipv6 ? "]" : "", port, sofia_glue_transport2str(transport));
if (sofia_glue_check_nat(profile, tech_pvt->mparams.remote_ip)) {
check_nat = 1;
}
url = sofia_glue_get_profile_url(profile, tech_pvt->mparams.remote_ip, transport);
if (!url) {
if (check_nat) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Nat detected but no external address configured.\n");
}
url = profile->url;
}
if (!url) {
switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
tmp = sofia_overcome_sip_uri_weakness(session, url, transport, SWITCH_TRUE, NULL, NULL);
if ((at = strchr(tmp, '@'))) {
url = switch_core_session_sprintf(session, "sip:%s%s", user, at);
}
if (url) {
const char *brackets = NULL;
const char *proto = NULL;
brackets = strchr(url, '>');
proto = switch_stristr("transport=", url);
tech_pvt->reply_contact = switch_core_session_sprintf(session, "%s%s%s%s%s",
brackets ? "" : "<", url,
proto ? "" : ";transport=",
proto ? "" : sofia_glue_transport2str(transport), brackets ? "" : ">");
} else {
switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
} else {
const char *url = NULL;
url = sofia_glue_get_profile_url(profile, tech_pvt->mparams.remote_ip, transport);
if (url) {
const char *brackets = NULL;
const char *proto = NULL;
brackets = strchr(url, '>');
proto = switch_stristr("transport=", url);
tech_pvt->reply_contact = switch_core_session_sprintf(session, "%s%s%s%s%s",
brackets ? "" : "<", url,
proto ? "" : ";transport=",
proto ? "" : sofia_glue_transport2str(transport), brackets ? "" : ">");
} else {
switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
}
}
}
if (sofia_glue_check_nat(profile, tech_pvt->mparams.remote_ip)) {
tech_pvt->user_via = sofia_glue_create_external_via(session, profile, tech_pvt->transport);
nua_set_hparams(nh, SIPTAG_VIA_STR(tech_pvt->user_via), TAG_END());
}
url_set_chanvars(session, sip->sip_contact->m_url, sip_contact);
if (sip->sip_referred_by) {
referred_by_user = sip->sip_referred_by->b_url->url_user;
//referred_by_host = sip->sip_referred_by->b_url->url_host;
//channel_name = url_set_chanvars(session, sip->sip_referred_by->b_url, sip_referred_by);
check_decode(referred_by_user, session);
if (!zstr(referred_by_user)) {
if (*referred_by_user == '+') {
switch_channel_set_variable(channel, "sip_referred_by_user_stripped", (const char *) (referred_by_user + 1));
} else {
switch_channel_set_variable(channel, "sip_referred_by_user_stripped", referred_by_user);
}
}
switch_channel_set_variable(channel, "sip_referred_by_cid", sip->sip_referred_by->b_cid);
if (sip->sip_referred_by->b_params) {
set_variable_sip_param(channel, "referred_by", sip->sip_referred_by->b_params);
}
}
//sofia_glue_set_name(tech_pvt, channel_name);
switch_core_media_prepare_codecs(tech_pvt->session, SWITCH_FALSE);
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "INBOUND CALL");
if (sofia_test_flag(tech_pvt, TFLAG_INB_NOMEDIA)) {
switch_channel_set_flag(channel, CF_PROXY_MODE);
}
if (profile->media_options & MEDIA_OPT_BYPASS_AFTER_HOLD) {
switch_channel_set_flag(channel, CF_BYPASS_MEDIA_AFTER_HOLD);
}
if (sofia_test_flag(tech_pvt, TFLAG_PROXY_MEDIA)) {
switch_channel_set_flag(channel, CF_PROXY_MEDIA);
}
if (sofia_test_flag(tech_pvt, TFLAG_ZRTP_PASSTHRU)) {
switch_channel_set_flag(channel, CF_ZRTP_PASSTHRU_REQ);
}
if (sip->sip_subject && sip->sip_subject->g_string) {
switch_channel_set_variable(channel, "sip_subject", sip->sip_subject->g_string);
}
if (sip->sip_user_agent && !zstr(sip->sip_user_agent->g_string)) {
switch_channel_set_variable(channel, "sip_user_agent", sip->sip_user_agent->g_string);
}
sofia_set_accept_language_channel_variable(channel, sip);
if (sip->sip_via) {
if (sip->sip_via->v_host) {
switch_channel_set_variable(channel, "sip_via_host", sip->sip_via->v_host);
}
if (sip->sip_via->v_port) {
switch_channel_set_variable(channel, "sip_via_port", sip->sip_via->v_port);
}
if (sip->sip_via->v_rport) {
switch_channel_set_variable(channel, "sip_via_rport", sip->sip_via->v_rport);
}
}
if (sip->sip_multipart) {
msg_multipart_t *mp;
for (mp = sip->sip_multipart; mp; mp = mp->mp_next) {
if (mp->mp_payload && mp->mp_payload->pl_data && mp->mp_content_type && mp->mp_content_type->c_type) {
char *val = switch_core_session_sprintf(session, "%s:%s", mp->mp_content_type->c_type, mp->mp_payload->pl_data);
switch_channel_add_variable_var_check(channel, "sip_multipart", val, SWITCH_FALSE, SWITCH_STACK_PUSH);
}
}
}
if (sip->sip_max_forwards) {
char max_forwards[32];
switch_snprintf(max_forwards, sizeof(max_forwards), "%lu", sip->sip_max_forwards->mf_count);
switch_channel_set_variable(channel, SWITCH_MAX_FORWARDS_VARIABLE, max_forwards);
}
if (acl_context) context = acl_context;
if (!context) {
context = switch_channel_get_variable(channel, "user_context");
}
if (!context) {
if (profile->context && !strcasecmp(profile->context, "_domain_")) {
context = from_host;
} else {
context = profile->context;
}
}
if (!(dialplan = switch_channel_get_variable(channel, "inbound_dialplan"))) {
dialplan = profile->dialplan;
}
if ((alert_info = sip_alert_info(sip))) {
char *tmp = sip_header_as_string(nh->nh_home, (void *) alert_info);
switch_channel_set_variable(channel, "alert_info", tmp);
su_free(nh->nh_home, tmp);
}
if ((call_info = sip_call_info(sip))) {
call_info_str = sip_header_as_string(nh->nh_home, (void *) call_info);
if (sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE) && switch_stristr("appearance", call_info_str)) {
char *p;
switch_channel_set_variable(channel, "presence_call_info_full", call_info_str);
if ((p = strchr(call_info_str, ';'))) {
p++;
switch_channel_set_variable(channel, "presence_call_info", p);
}
}
if (call_info->ci_params && (msg_params_find(call_info->ci_params, "answer-after=0"))) {
switch_channel_set_variable(channel, "sip_auto_answer_detected", "true");
}
switch_channel_set_variable(channel, "sip_call_info", call_info_str);
call_info = call_info->ci_next;
while (call_info) {
call_info_str = sip_header_as_string(nh->nh_home, (void *) call_info);
switch_channel_add_variable_var_check(channel, "sip_call_info", call_info_str, SWITCH_FALSE, SWITCH_STACK_PUSH);
call_info = call_info->ci_next;
}
call_info = sip_call_info(sip);
} else if (sofia_test_pflag(profile, PFLAG_MANAGE_SHARED_APPEARANCE)) {
char buf[128] = "";
char *sql;
char *state = "progressing";
if (sip->sip_from && sip->sip_from->a_url->url_user && sip->sip_from->a_url->url_host &&
sip->sip_to && sip->sip_to->a_url->url_user && sip->sip_to->a_url->url_host) {
sql =
switch_mprintf("select 'appearance-index=1' from sip_subscriptions where expires > -1 and hostname='%q' and event='call-info' and "
"sub_to_user='%q' and sub_to_host='%q'", mod_sofia_globals.hostname, sip->sip_to->a_url->url_user,
sip->sip_from->a_url->url_host);
sofia_glue_execute_sql2str(profile, profile->dbh_mutex, sql, buf, sizeof(buf));
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "QUERY SQL %s [%s]\n", sql, buf);
}
free(sql);
if (!zstr(buf)) {
sql = switch_mprintf("update sip_dialogs set call_info='%q',call_info_state='%q' "
"where uuid='%q'", buf, state, switch_core_session_get_uuid(session));
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "QUERY SQL %s\n", sql);
}
sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Auto-Fixing Broken SLA [<sip:%s>;%s]\n",
sip->sip_from->a_url->url_host, buf);
switch_channel_set_variable_printf(channel, "presence_call_info_full", "<sip:%s>;%s", sip->sip_from->a_url->url_host, buf);
switch_channel_set_variable(channel, "presence_call_info", buf);
call_info_str = switch_core_session_sprintf(session, "<sip:%s>;%s", sip->sip_from->a_url->url_host, buf);
}
}
}
if (profile->pres_type) {
const char *presence_id = switch_channel_get_variable(channel, "presence_id");
if (zstr(presence_id)) {
const char *user = switch_str_nil(sip->sip_from->a_url->url_user);
const char *host = switch_str_nil(sip->sip_from->a_url->url_host);
char *tmp = switch_mprintf("%s@%s", user, host);
switch_assert(tmp);
switch_channel_set_variable(channel, "presence_id", tmp);
free(tmp);
}
}
if (sip->sip_request->rq_url->url_params) {
gw_param_name = switch_find_parameter(sip->sip_request->rq_url->url_params, "gw", switch_core_session_get_pool(session));
}
if (strstr(destination_number, "gw+")) {
if (sofia_test_pflag(profile, PFLAG_FULL_ID)) {
char *tmp;
gw_name = switch_core_session_strdup(session, destination_number + 3);
if ((tmp = strchr(gw_name, '@'))) {
*tmp = '\0';
}
} else {
gw_name = destination_number + 3;
}
}
if (gw_name || gw_param_name) {
sofia_gateway_t *gateway = NULL;
char *extension = NULL;
if (gw_name && ((gateway = sofia_reg_find_gateway(gw_name)))) {
gw_param_name = NULL;
extension = gateway->extension;
}
if (!gateway && gw_param_name) {
if ((gateway = sofia_reg_find_gateway(gw_param_name))) {
extension = gateway->real_extension;
}
}
if (gateway) {
context = switch_core_session_strdup(session, gateway->register_context);
switch_channel_set_variable(channel, "sip_gateway", gateway->name);
if (!zstr(extension)) {
if (!strcasecmp(extension, "auto_to_user") && sip->sip_to) {
destination_number = sip->sip_to->a_url->url_user;
} else if (!strcasecmp(extension, "auto")) {
if (gw_name && sip->sip_to) {
destination_number = sip->sip_to->a_url->url_user;
}
} else {
destination_number = switch_core_session_strdup(session, extension);
}
} else if (!gw_param_name && sip->sip_to) {
destination_number = sip->sip_to->a_url->url_user;
}
gateway->ib_calls++;
if (gateway->ib_vars) {
switch_event_header_t *hp;
for (hp = gateway->ib_vars->headers; hp; hp = hp->next) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s setting variable [%s]=[%s]\n",
switch_channel_get_name(channel), hp->name, hp->value);
switch_channel_set_variable(channel, hp->name, hp->value);
}
}
sofia_reg_release_gateway(gateway);
}
}
if (call_info_str) {
char *sql;
char cid[512] = "";
char *str;
char *p = NULL;
const char *user = NULL, *host = NULL, *from_user = NULL, *from_host = NULL;
if (sip->sip_to) {
user = sip->sip_to->a_url->url_user;
host = sip->sip_to->a_url->url_host;
}
if (sip->sip_from) {
from_user = sip->sip_from->a_url->url_user;
from_host = sip->sip_from->a_url->url_host;
}
if (!user) user = from_user;
if (!host) user = from_host;
if (user && host && from_user && !strcmp(user, from_user)) {
if ((p = strchr(call_info_str, ';'))) {
p++;
}
sql = switch_mprintf(
"select call_id from sip_dialogs where (call_info='%q' or call_info='%q;appearance-state=held') and "
"((sip_from_user='%q' and sip_from_host='%q') or presence_id='%q@%q') and call_id is not null",
switch_str_nil(p), switch_str_nil(p), user, host, user, host);
if ((str = sofia_glue_execute_sql2str(profile, profile->dbh_mutex, sql, cid, sizeof(cid)))) {
bnh = nua_handle_by_call_id(nua, str);
}
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PICK SQL %s [%s] [%s] %d\n", sql, str, cid, !!bnh);
}
free(sql);
}
}
check_decode(displayname, session);
profile_dup_clean(from_user, tech_pvt->caller_profile->username, tech_pvt->caller_profile->pool);
profile_dup_clean(dialplan, tech_pvt->caller_profile->dialplan, tech_pvt->caller_profile->pool);
profile_dup_clean(displayname, tech_pvt->caller_profile->caller_id_name, tech_pvt->caller_profile->pool);
profile_dup_clean(from_user, tech_pvt->caller_profile->caller_id_number, tech_pvt->caller_profile->pool);
profile_dup_clean(displayname, tech_pvt->caller_profile->orig_caller_id_name, tech_pvt->caller_profile->pool);
profile_dup_clean(from_user, tech_pvt->caller_profile->orig_caller_id_number, tech_pvt->caller_profile->pool);
profile_dup_clean(network_ip, tech_pvt->caller_profile->network_addr, tech_pvt->caller_profile->pool);
profile_dup_clean(from_user, tech_pvt->caller_profile->ani, tech_pvt->caller_profile->pool);
profile_dup_clean(aniii, tech_pvt->caller_profile->aniii, tech_pvt->caller_profile->pool);
profile_dup_clean(context, tech_pvt->caller_profile->context, tech_pvt->caller_profile->pool);
profile_dup_clean(destination_number, tech_pvt->caller_profile->destination_number, tech_pvt->caller_profile->pool);
if (!bnh && sip->sip_replaces) {
if (!(bnh = nua_handle_by_replaces(nua, sip->sip_replaces))) {
if (!(bnh = nua_handle_by_call_id(nua, sip->sip_replaces->rp_call_id))) {
bnh = sofia_global_nua_handle_by_replaces(sip->sip_replaces);
}
}
}
if (sip->sip_replaces) {
msg_common_t *rp_common = sip->sip_replaces->rp_common;
switch_channel_set_variable(channel, "sip_replaces_call_id", sip->sip_replaces->rp_call_id);
if (rp_common->h_class->hc_params) {
int i, n;
msg_param_t const *params = * (msg_param_t const **) ((char *)rp_common + rp_common->h_class->hc_params);
for (i = 0; params[i]; i++) {
msg_param_t param = params[i];
if (strchr(param, '=')) {
n = strcspn(param, "=");
switch_channel_set_variable_name_printf(channel, param + n + 1, "sip_replaces_%.*s", n, param);
} else {
switch_channel_set_variable_name_printf(channel, "true", "sip_replaces_%s", param);
}
}
}
}
if (bnh) {
sofia_private_t *b_private = NULL;
if ((b_private = nua_handle_magic(bnh))) {
switch_core_session_t *b_session = NULL;
if ((b_session = switch_core_session_locate(b_private->uuid))) {
switch_channel_t *b_channel = switch_core_session_get_channel(b_session);
sofia_handle_sip_i_invite_replaces(session, channel, b_channel, b_private->uuid, tech_pvt, call_info, profile, is_nat, sip);
switch_core_session_rwunlock(b_session);
}
}
nua_handle_unref(bnh);
} else if (sip->sip_replaces && sip->sip_replaces->rp_call_id) {
switch_core_session_t *b_session = NULL;
if ((b_session = switch_core_session_locate((char*) sip->sip_replaces->rp_call_id))) {
switch_channel_t *b_channel = switch_core_session_get_channel(b_session);
sofia_handle_sip_i_invite_replaces(session, channel, b_channel, (char*) sip->sip_replaces->rp_call_id, tech_pvt, call_info, profile, is_nat, sip);
switch_core_session_rwunlock(b_session);
}
}
if (tech_pvt->caller_profile) {
int first_history_info = 1;
if (rpid) {
if (rpid->rpid_privacy) {
if (!strcasecmp(rpid->rpid_privacy, "yes")) {
switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME | SWITCH_CPF_HIDE_NUMBER);
} else if (!strcasecmp(rpid->rpid_privacy, "full")) {
switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME | SWITCH_CPF_HIDE_NUMBER);
} else if (!strcasecmp(rpid->rpid_privacy, "name")) {
switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME);
} else if (!strcasecmp(rpid->rpid_privacy, "number")) {
switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NUMBER);
} else {
switch_clear_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME);
switch_clear_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NUMBER);
}
}
if (rpid->rpid_screen && !strcasecmp(rpid->rpid_screen, "no")) {
switch_clear_flag(tech_pvt->caller_profile, SWITCH_CPF_SCREEN);
}
}
if ((privacy = sip_privacy(sip))) {
char *full_priv_header = sip_header_as_string(nh->nh_home, (void *) privacy);
if (!zstr(full_priv_header)) {
switch_channel_set_variable(channel, "sip_Privacy", full_priv_header);
}
if (msg_params_find(privacy->priv_values, "id")) {
switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME | SWITCH_CPF_HIDE_NUMBER);
}
}
/* Loop thru unknown Headers Here so we can do something with them */
for (un = sip->sip_unknown; un; un = un->un_next) {
if (!strncasecmp(un->un_name, "Accept-Language", 15)) {
if (!zstr(un->un_value)) {
char *tmp_name;
if ((tmp_name = switch_mprintf("%s%s", SOFIA_SIP_HEADER_PREFIX, un->un_name))) {
switch_channel_set_variable(channel, tmp_name, un->un_value);
free(tmp_name);
}
}
} else if (!strncasecmp(un->un_name, "Diversion", 9)) {
/* Basic Diversion Support for Diversion Indication in SIP */
/* draft-levy-sip-diversion-08 */
if (!zstr(un->un_value)) {
char *tmp_name;
if ((tmp_name = switch_mprintf("%s%s", SOFIA_SIP_HEADER_PREFIX, un->un_name))) {
switch_channel_set_variable(channel, tmp_name, un->un_value);
free(tmp_name);
}
}
} else if (!strncasecmp(un->un_name, "History-Info", 12)) {
if (first_history_info) {
/* If the header exists first time, make sure to remove old info and re-set the variable */
switch_channel_set_variable(channel, "sip_history_info", un->un_value);
first_history_info = 0;
} else {
/* Append the History-Info into one long string */
const char *history_var = switch_channel_get_variable(channel, "sip_history_info");
if (!zstr(history_var)) {
char *tmp_str;
if ((tmp_str = switch_mprintf("%s, %s", history_var, un->un_value))) {
switch_channel_set_variable(channel, "sip_history_info", tmp_str);
free(tmp_str);
} else {
switch_channel_set_variable(channel, "sip_history_info", un->un_value);
}
} else {
switch_channel_set_variable(channel, "sip_history_info", un->un_value);
}
}
} else if (!strcasecmp(un->un_name, "X-FS-Channel-Name") && !zstr(un->un_value)) {
switch_channel_set_name(channel, un->un_value);
switch_channel_set_variable(channel, "push_channel_name", "true");
} else if (!strcasecmp(un->un_name, "X-FS-Support")) {
tech_pvt->x_freeswitch_support_remote = switch_core_session_strdup(session, un->un_value);
} else if (!strcasecmp(un->un_name, "Geolocation")) {
switch_channel_set_variable(channel, "sip_geolocation", un->un_value);
} else if (!strcasecmp(un->un_name, "Geolocation-Error")) {
switch_channel_set_variable(channel, "sip_geolocation_error", un->un_value);
} else if (!strcasecmp(un->un_name, "userLocation")) {
switch_channel_set_variable(channel, "sip_user_location", un->un_value);
} else if (!strncasecmp(un->un_name, "X-", 2) || !strncasecmp(un->un_name, "P-", 2) || !strcasecmp(un->un_name, "User-to-User") || !strncasecmp(un->un_name, "On", 2)) {
if (!zstr(un->un_value)) {
char new_name[512] = "";
int reps = 0;
for (;;) {
char postfix[25] = "";
if (reps > 0) {
switch_snprintf(postfix, sizeof(postfix), "-%d", reps);
}
reps++;
switch_snprintf(new_name, sizeof(new_name), "%s%s%s", SOFIA_SIP_HEADER_PREFIX, un->un_name, postfix);
if (switch_channel_get_variable(channel, new_name)) {
continue;
}
switch_channel_set_variable(channel, new_name, un->un_value);
break;
}
}
}
}
}
tech_pvt->sofia_private = sofia_private;
tech_pvt->nh = nh;
if (profile->pres_type && sofia_test_pflag(profile, PFLAG_IN_DIALOG_CHAT)) {
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;
}
if (sip->sip_from) {
from = sip->sip_from->a_url;
}
contact = sip->sip_contact->m_url;
if (sip->sip_user_agent) {
user_agent = switch_str_nil(sip->sip_user_agent->g_string);
}
if (sip->sip_call_id) {
call_id = switch_str_nil(sip->sip_call_id->i_id);
}
if (to) {
to_user = switch_str_nil(to->url_user);
to_host = switch_str_nil(to->url_host);
to_tag = switch_str_nil(sip->sip_to->a_tag);
}
if (from) {
dialog_from_user = switch_str_nil(from->url_user);
dialog_from_host = switch_str_nil(from->url_host);
from_tag = switch_str_nil(sip->sip_from->a_tag);
}
contact_user = switch_str_nil(contact->url_user);
contact_host = switch_str_nil(contact->url_host);
if (profile->pres_type) {
const char *presence_data = switch_channel_get_variable(channel, "presence_data");
const char *presence_id = switch_channel_get_variable(channel, "presence_id");
char *full_contact = "";
char *p = NULL;
time_t now;
full_contact = sip_header_as_string(nua_handle_home(tech_pvt->nh), (void *) sip->sip_contact);
if (call_info_str && switch_stristr("appearance", call_info_str)) {
switch_channel_set_variable(channel, "presence_call_info_full", call_info_str);
if ((p = strchr(call_info_str, ';'))) {
p++;
switch_channel_set_variable(channel, "presence_call_info", p);
}
}
now = switch_epoch_time_now(NULL);
sql = switch_mprintf("insert into sip_dialogs "
"(call_id,uuid,sip_to_user,sip_to_host,sip_to_tag,sip_from_user,sip_from_host,sip_from_tag,contact_user,"
"contact_host,state,direction,user_agent,profile_name,hostname,contact,presence_id,presence_data,"
"call_info,rcd,call_info_state) "
"values('%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q',%ld,'')",
call_id,
tech_pvt->sofia_private->uuid,
to_user, to_host, to_tag, dialog_from_user, dialog_from_host, from_tag,
contact_user, contact_host, "confirmed", "inbound", user_agent,
profile->name, mod_sofia_globals.hostname, switch_str_nil(full_contact),
switch_str_nil(presence_id), switch_str_nil(presence_data), switch_str_nil(p), now);
switch_assert(sql);
sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
if ( full_contact ) {
su_free(nua_handle_home(tech_pvt->nh), full_contact);
}
}
if (is_nat) {
sofia_set_flag(tech_pvt, TFLAG_NAT);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Setting NAT mode based on %s\n", is_nat);
switch_channel_set_variable(channel, "sip_nat_detected", "true");
}
return;
fail:
profile->ib_failed_calls++;
return;
}
void sofia_handle_sip_i_invite_replaces(switch_core_session_t *session, switch_channel_t *channel, switch_channel_t *b_channel, char* uuid, private_object_t *tech_pvt, sip_call_info_t *call_info, sofia_profile_t *profile, char *is_nat, sip_t const *sip)
{
const char *bridge_uuid;
switch_caller_profile_t *orig_cp, *cp;
//const char *sent_name, *sent_number;
orig_cp = switch_channel_get_caller_profile(b_channel);
if (orig_cp) {
tech_pvt->caller_profile->callee_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->callee_id_name);
tech_pvt->caller_profile->callee_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->callee_id_number);
if (!call_info) {
tech_pvt->caller_profile->caller_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->caller_id_name);
tech_pvt->caller_profile->caller_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->caller_id_number);
}
cp = switch_caller_profile_dup(tech_pvt->caller_profile->pool, orig_cp);
switch_channel_set_originator_caller_profile(channel, cp);
}
#if 0
sent_name = switch_channel_get_variable(b_channel, "last_sent_callee_id_name");
sent_number = switch_channel_get_variable(b_channel, "last_sent_callee_id_number");
if (!zstr(sent_name) && !zstr(sent_number)) {
tech_pvt->caller_profile->callee_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, sent_name);
tech_pvt->caller_profile->callee_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, sent_number);
} else {
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
tech_pvt->caller_profile->callee_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->callee_id_name);
tech_pvt->caller_profile->callee_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->callee_id_number);
} else {
tech_pvt->caller_profile->callee_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->caller_id_name);
tech_pvt->caller_profile->callee_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->caller_id_number);
}
}
#endif
if (is_nat) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Setting NAT mode based on %s\n", is_nat);
}
tech_pvt->caller_profile->dialplan = "inline";
bridge_uuid = switch_channel_get_partner_uuid(b_channel);
if (bridge_uuid) {
switch_core_session_t *bridge_session = NULL;
if ((bridge_session = switch_core_session_locate(bridge_uuid))) {
switch_core_session_rwunlock(bridge_session);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "could not locate partner_uuid %s, resetting\n", bridge_uuid);
bridge_uuid = NULL;
}
}
if (call_info) {
switch_event_t *event = NULL;
if (!zstr(bridge_uuid) && switch_channel_test_flag(b_channel, CF_LEG_HOLDING)) {
const char *b_call_id = switch_channel_get_variable(b_channel, "sip_call_id");
if (b_call_id) {
char *sql = switch_mprintf("update sip_dialogs set call_info_state='idle' where call_id='%q'", b_call_id);
if (mod_sofia_globals.debug_sla > 1) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "QUERY SQL %s\n", sql);
}
sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE);
switch_channel_presence(b_channel, "unknown", "idle", NULL);
}
switch_channel_set_flag(tech_pvt->channel, CF_SLA_INTERCEPT);
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool,
"answer,intercept:%s", bridge_uuid);
if (sofia_test_pflag(profile, PFLAG_FIRE_TRANFER_EVENTS)
&& sip && sip->sip_call_id
&& switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_REPLACED) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(b_channel, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_replaced_by", sip->sip_call_id->i_id);
switch_event_fire(&event);
}
} else {
switch_caller_profile_t *bcp = switch_channel_get_caller_profile(b_channel);
if (switch_channel_test_flag(b_channel, CF_BRIDGE_ORIGINATOR)) {
switch_channel_set_flag(tech_pvt->channel, CF_BRIDGE_ORIGINATOR);
}
if (!zstr(bcp->callee_id_name)) {
tech_pvt->caller_profile->callee_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, bcp->callee_id_name);
}
if (!zstr(bcp->callee_id_number)) {
tech_pvt->caller_profile->callee_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, bcp->callee_id_number);
}
if (!zstr(bcp->caller_id_name)) {
tech_pvt->caller_profile->caller_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, bcp->caller_id_name);
}
if (!zstr(bcp->caller_id_number)) {
tech_pvt->caller_profile->caller_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, bcp->caller_id_number);
}
if (bcp->originatee_caller_profile) {
switch_caller_profile_t *cp;
cp = switch_caller_profile_dup(tech_pvt->caller_profile->pool,
bcp->originatee_caller_profile);
switch_channel_set_originatee_caller_profile(tech_pvt->channel, cp);
}
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool,
"answer,sofia_sla:%s", uuid);
}
} else {
char const *nightmare_xfer_uuid = NULL;
switch_event_t *event = NULL;
if (switch_channel_var_true(channel, "sip_replaces_a-leg")) {
switch_channel_mark_hold(b_channel, SWITCH_FALSE);
if (sip) {
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, "answer,intercept:%s", sip->sip_replaces->rp_call_id);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "call %s picked up on a-leg\n", sip->sip_replaces->rp_call_id);
if (sofia_test_pflag(profile, PFLAG_FIRE_TRANFER_EVENTS)
&& sip->sip_call_id
&& switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_INTERCEPTED) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(b_channel, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "intercepted_by", sip->sip_call_id->i_id);
switch_event_fire(&event);
}
}
} else {
if (sip) {
if ((nightmare_xfer_uuid = sofia_glue_get_unknown_header(sip, "X-FS-Refer-For"))) {
switch_channel_set_variable(b_channel, "transfer_refer_for", nightmare_xfer_uuid);
}
if ((nightmare_xfer_uuid = sofia_glue_get_unknown_header(sip, "X-FS-Refer-From"))) {
switch_channel_set_variable(b_channel, "transfer_refer_from", nightmare_xfer_uuid);
}
}
if (!zstr(bridge_uuid)) {
if (sip && sip->sip_replaces && sip->sip_replaces->rp_params && sip->sip_replaces->rp_call_id && switch_channel_test_flag(b_channel, CF_BRIDGED) &&
switch_true(switch_find_parameter(*(sip->sip_replaces->rp_params), "early-only", switch_core_session_get_pool(session)))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "call %s intercept rejected\n", bridge_uuid);
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, "hangup:CALL_REJECTED");
} else {
switch_channel_mark_hold(b_channel, SWITCH_FALSE);
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, "answer,intercept:%s", bridge_uuid);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "call %s intercepted\n", bridge_uuid);
if (sofia_test_pflag(profile, PFLAG_FIRE_TRANFER_EVENTS)
&& sip && sip->sip_call_id
&& switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_INTERCEPTED) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(b_channel, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "intercepted_by", sip->sip_call_id->i_id);
switch_event_fire(&event);
}
}
} else {
const char *b_app = switch_channel_get_variable(b_channel, SWITCH_CURRENT_APPLICATION_VARIABLE);
const char *b_data = switch_channel_get_variable(b_channel, SWITCH_CURRENT_APPLICATION_DATA_VARIABLE);
if (b_data && b_app) {
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, "answer,%s:%s", b_app, b_data);
} else if (b_app) {
tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, "answer,%s", b_app);
}
if (sofia_test_pflag(profile, PFLAG_FIRE_TRANFER_EVENTS)
&& sip && sip->sip_call_id
&& switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_REPLACED) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(b_channel, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "att_xfer_replaced_by", sip->sip_call_id->i_id);
switch_event_fire(&event);
}
switch_channel_hangup(b_channel, SWITCH_CAUSE_ATTENDED_TRANSFER);
}
}
}
}
void sofia_handle_sip_i_options(int status,
char const *phrase,
nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
sofia_dispatch_event_t *de,
tagi_t tags[])
{
uint32_t sess_count = switch_core_session_count();
uint32_t sess_max = switch_core_session_limit(0);
if (sofia_test_pflag(profile, PFLAG_OPTIONS_RESPOND_503_ON_BUSY) &&
(sess_count >= sess_max || !sofia_test_pflag(profile, PFLAG_RUNNING) || !switch_core_ready_inbound())) {
nua_respond(nh, 503, "Maximum Calls In Progress", NUTAG_WITH_THIS_MSG(de->data->e_msg), SIPTAG_RETRY_AFTER_STR("300"), TAG_END());
} else {
switch_assert(sip);
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg),
TAG_IF(sip->sip_record_route, SIPTAG_RECORD_ROUTE(sip->sip_record_route)), TAG_END());
}
}
/*
* This subroutine will take the a_params of a sip_addr_s structure and spin through them.
* Each param will be used to create a channel variable.
* In the SIP RFC's, this data is called generic-param.
* Note that the tag-param is also included in the a_params list.
*
* From: "John Doe" <sip:5551212@1.2.3.4>;tag=ed23266b52cbb17eo2;ref=101;mbid=201
*
* For example, the header above will produce an a_params list with three entries
* tag=ed23266b52cbb17eo2
* ref=101
* mbid=201
*
* The a_params list is parsed and the lvalue is used to create the channel variable name while the
* rvalue is used to create the channel variable value.
*
* If no equal (=) sign is found during parsing, a channel variable name is created with the param and
* the value is set to NULL.
*
* Pointers are used for copying the sip_header_name for performance reasons. There are no calls to
* any string functions and no memory is allocated/dealocated. The only limiter is the size of the
* sip_header_name array.
*/
static void set_variable_sip_param(switch_channel_t *channel, char *header_type, sip_param_t const *params)
{
char sip_header_name[128] = "";
char var1[] = "sip_";
char *cp, *sh, *sh_end, *sh_save;
/* Build the static part of the sip_header_name variable from */
/* the header_type. If the header type is "referred_by" then */
/* sip_header_name = "sip_referred_by_". */
sh = sip_header_name;
sh_end = sh + sizeof(sip_header_name) - 1;
for (cp = var1; *cp; cp++, sh++) {
*sh = *cp;
}
*sh = '\0';
/* Copy the header_type to the sip_header_name. Before copying */
/* each character, check that we aren't going to overflow the */
/* the sip_header_name buffer. We have to account for the */
/* trailing underscore and NULL that will be added to the end. */
for (cp = header_type; (*cp && (sh < (sh_end - 1))); cp++, sh++) {
*sh = *cp;
}
*sh++ = '_';
*sh = '\0';
/* sh now points to the NULL at the end of the partially built */
/* sip_header_name variable. This is also the start of the */
/* variable part of the sip_header_name built from the lvalue */
/* of the params data. */
sh_save = sh;
while (params && params[0]) {
/* Copy the params data to the sip_header_name variable until */
/* the end of the params string is reached, an '=' is detected */
/* or until the sip_header_name buffer has been exhausted. */
for (cp = (char *) (*params); ((*cp != '=') && *cp && (sh < sh_end)); cp++, sh++) {
*sh = *cp;
}
/* cp now points to either the end of the params data or the */
/* equal (=) sign separating the lvalue and rvalue. */
if (*cp == '=')
cp++;
*sh = '\0';
switch_channel_set_variable(channel, sip_header_name, cp);
/* Bump pointer to next param in the list. Also reset the */
/* sip_header_name pointer to the beginning of the dynamic area */
params++;
sh = sh_save;
}
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
*/