Author: cseket Date: Sat Apr 12 12:59:25 2008 New Revision: 8099 Added: freeswitch/branches/cseket/src/mod/endpoints/mod_sofia/sofia.c - copied, changed from r5617, /freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia.c Log: add 'auto' sip-port, which means bind to any free port, choosen by the os Copied: freeswitch/branches/cseket/src/mod/endpoints/mod_sofia/sofia.c (from r5617, /freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia.c) ============================================================================== --- /freeswitch/trunk/src/mod/endpoints/mod_sofia/sofia.c (original) +++ freeswitch/branches/cseket/src/mod/endpoints/mod_sofia/sofia.c Sat Apr 12 12:59:25 2008 @@ -28,38 +28,186 @@ * Paul D. Tinsley <pdt at jackhammer.org> * Bret McDanel <trixter AT 0xdecafbad.com> * Marcel Barbulescu <marcelbarbulescu@gmail.com> + * Norman Brandinger * * * sofia.c -- SOFIA SIP Endpoint (sofia code) * */ #include "mod_sofia.h" +#include "sofia-sip/msg_parser.h" +#include "sofia-sip/sip_extra.h" +#include "sofia-sip/tport_tag.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[]; +extern su_log_t stun_log[]; + +static void set_variable_sip_param(switch_channel_t *channel, char *header_type, sip_param_t const *params); + +static void sofia_info_send_sipfrag(switch_core_session_t *aleg, switch_core_session_t *bleg); 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, 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, tagi_t tags[]); + +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, tagi_t tags[]) +{ + if (status >= 300 && sip) { + const char *call_id = sip->sip_call_id->i_id; + char *sql; + switch_core_hash_delete(profile->sub_hash, call_id); + + sql = switch_mprintf("delete from sip_subscriptions where call_id='%q'", call_id); + switch_assert(sql != NULL); + sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); + nua_handle_destroy(nh); + } +} + +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, + tagi_t tags[]) +{ + switch_channel_t *channel = NULL; + + /* 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")) { + nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS(nua), TAG_END()); + return; + } + + /* make sure we have a proper "talk" event */ + if (!session || strcasecmp(sip->sip_event->o_type, "talk")) { + goto error; + } + + channel = switch_core_session_get_channel(session); + switch_assert(channel != NULL); + if (!switch_channel_test_flag(channel, CF_OUTBOUND)) { + 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(nua), TAG_END()); + return; + } + +error: + nua_respond(nh, 481, "Subscription Does Not Exist", NUTAG_WITH_THIS(nua), TAG_END()); + return; +} + +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, + tagi_t tags[]) +{ + const char *tmp; + switch_channel_t *channel; + + if (!session) return; + + channel = switch_core_session_get_channel(session); + + + if (sip->sip_reason && sip->sip_reason->re_protocol && + (!strcasecmp(sip->sip_reason->re_protocol, "Q.850") || !strcasecmp(sip->sip_reason->re_protocol, "FreeSWITCH")) && + sip->sip_reason->re_cause) { + private_object_t *tech_pvt = switch_core_session_get_private(session); + tech_pvt->q850_cause = atoi(sip->sip_reason->re_cause); + } + + if (sip->sip_user_agent && !switch_strlen_zero(sip->sip_user_agent->g_string)){ + switch_channel_set_variable(channel, "sip_user_agent", sip->sip_user_agent->g_string); + } + 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); + } + return; +} + +void sofia_handle_sip_r_message(int status, sofia_profile_t *profile, nua_handle_t *nh, sip_t const *sip) +{ + if (status == 503) { + const char *user = NULL, *host = NULL; + char *sql; + + if (sip->sip_to && sip->sip_to->a_url) { + user = sip->sip_to->a_url->url_user; + host = sip->sip_to->a_url->url_host; + + sql = switch_mprintf("delete from sip_registrations where sip_user='%q' and sip_host='%q'", user, host); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Deleting registration for %s@%s\n", user, host); + sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); + } + } + + nua_handle_destroy(nh); +} + 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[]) + 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[]) { 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; if (sofia_private) { - if (!switch_strlen_zero(sofia_private->uuid)) { - + if ((gateway = sofia_private->gateway)) { + if (switch_thread_rwlock_tryrdlock(gateway->profile->rwlock) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Profile %s is locked\n", gateway->profile->name); + return; + } + } else if (!switch_strlen_zero(sofia_private->uuid)) { if ((session = switch_core_session_locate(sofia_private->uuid))) { tech_pvt = switch_core_session_get_private(session); channel = switch_core_session_get_channel(tech_pvt->session); 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, (char *) 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); + } } else { /* too late */ return; @@ -72,6 +220,15 @@ nua_event_name(event), status, phrase, session ? switch_channel_get_name(channel) : "n/a"); } + if (session) { + switch_core_session_signal_lock(session); + + if (channel && switch_channel_get_state(channel) >= CS_HANGUP) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Channel is already hungup.\n"); + goto done; + } + } + if ((profile->pflags & PFLAG_AUTH_ALL) && tech_pvt && tech_pvt->key && sip) { sip_authorization_t const *authorization = NULL; @@ -84,8 +241,8 @@ if (authorization) { char network_ip[80]; get_addr(network_ip, sizeof(network_ip), &((struct sockaddr_in *) msg_addrinfo(nua_current_request(nua))->ai_addr)->sin_addr); - auth_res = sofia_reg_parse_auth(profile, authorization, - (char *) sip->sip_request->rq_method_name, tech_pvt->key, strlen(tech_pvt->key), network_ip, NULL); + auth_res = sofia_reg_parse_auth(profile, authorization, sip, + (char *) sip->sip_request->rq_method_name, tech_pvt->key, strlen(tech_pvt->key), network_ip, NULL, 0, REG_INVITE, NULL); } if (auth_res != AUTH_OK) { @@ -100,7 +257,7 @@ } if (sip && (status == 401 || status == 407)) { - sofia_reg_handle_sip_r_challenge(status, phrase, nua, profile, nh, session, sip, tags); + sofia_reg_handle_sip_r_challenge(status, phrase, nua, profile, nh, session, gateway, sip, tags); goto done; } @@ -112,38 +269,23 @@ break; case nua_r_message: - { - if (status == 503) { - const char *user = NULL, *host = NULL; - char *sql; - - if (sip->sip_to && sip->sip_to->a_url) { - user = sip->sip_to->a_url->url_user; - host = sip->sip_to->a_url->url_host; - - sql = switch_mprintf("delete from sip_registrations where user='%q' and host='%q'", user, host); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Deleting registration for %s@%s\n", user, host); - sofia_glue_execute_sql(profile, SWITCH_TRUE, sql, NULL); - switch_safe_free(sql); - } - } - - nua_handle_destroy(nh); - - } + sofia_handle_sip_r_message(status, profile, nh, sip); break; - case nua_r_get_params: case nua_r_invite: + sofia_handle_sip_r_invite(session, status, phrase, nua, profile, nh, sofia_private, sip, tags); + break; + case nua_r_get_params: case nua_r_unregister: case nua_r_options: case nua_i_fork: case nua_r_info: case nua_r_bye: + break; case nua_i_bye: + sofia_handle_sip_i_bye(session, status, phrase, nua, profile, nh, sofia_private, sip, tags); + break; case nua_r_unsubscribe: case nua_r_publish: - case nua_r_notify: - case nua_i_notify: case nua_i_cancel: case nua_i_error: case nua_i_active: @@ -151,6 +293,12 @@ case nua_i_terminated: case nua_r_set_params: break; + case nua_r_notify: + sofia_handle_sip_r_notify(session, status, phrase, nua, profile, nh, sofia_private, sip, tags); + break; + case nua_i_notify: + sofia_handle_sip_i_notify(session, status, phrase, nua, profile, nh, sofia_private, sip, tags); + break; case nua_r_register: sofia_reg_handle_sip_r_register(status, phrase, nua, profile, nh, sofia_private, sip, tags); break; @@ -203,13 +351,16 @@ done: + if (gateway) { + sofia_reg_release_gateway(gateway); + } + if (session) { + switch_core_session_signal_unlock(session); switch_core_session_rwunlock(session); - } } - void event_handler(switch_event_t *event) { char *subclass, *sql; @@ -220,10 +371,15 @@ char *contact_str = switch_event_get_header(event, "orig-contact"); char *exp_str = switch_event_get_header(event, "orig-expires"); char *rpid = switch_event_get_header(event, "orig-rpid"); - long expires = (long) time(NULL) + atol(exp_str); + char *call_id = switch_event_get_header(event, "orig-call-id"); + char *user_agent = switch_event_get_header(event, "user-agent"); + long expires = (long) switch_timestamp(NULL); char *profile_name = switch_event_get_header(event, "orig-profile-name"); sofia_profile_t *profile = NULL; - char buf[512]; + + if (exp_str) { + expires += atol(exp_str); + } if (!rpid) { rpid = "unknown"; @@ -233,26 +389,24 @@ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Profile\n"); return; } - - - if (!sofia_reg_find_reg_url(profile, from_user, from_host, buf, sizeof(buf))) { - sql = switch_mprintf("insert into sip_registrations values ('%q','%q','%q','Regestered', '%q', %ld)", - from_user, from_host, contact_str, rpid, expires); + if (sofia_test_pflag(profile, PFLAG_MULTIREG)) { + sql = switch_mprintf("delete from sip_registrations where call_id='%q'", call_id); } else { - sql = - switch_mprintf - ("update sip_registrations set contact='%q', rpid='%q', expires=%ld where user='%q' and host='%q'", - contact_str, rpid, expires, from_user, from_host); - + sql = switch_mprintf("delete from sip_registrations where sip_user='%q' and sip_host='%q'", from_user, from_host); } + switch_mutex_lock(profile->ireg_mutex); + sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); + + sql = switch_mprintf("insert into sip_registrations values ('%q', '%q','%q','%q','Registered', '%q', %ld, '%q')", + call_id, from_user, from_host, contact_str, rpid, expires, user_agent); + if (sql) { - sofia_glue_execute_sql(profile, SWITCH_FALSE, sql, profile->ireg_mutex); - switch_safe_free(sql); - sql = NULL; + 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); - } + switch_mutex_unlock(profile->ireg_mutex); + if (profile) { sofia_glue_release_profile(profile); @@ -260,17 +414,85 @@ } } +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 = 0; + uint32_t gateway_loops = 0; + int loops = 0; + uint32_t qsize; + + ireg_loops = IREG_SECONDS; + gateway_loops = GATEWAY_SECONDS; + + sofia_set_pflag_locked(profile, PFLAG_WORKER_RUNNING); + + switch_queue_create(&profile->sql_queue, 500000, profile->pool); + + qsize = switch_queue_size(profile->sql_queue); + + while ((mod_sofia_globals.running == 1 && sofia_test_pflag(profile, PFLAG_RUNNING)) || qsize) { + if (qsize) { + void *pop; + switch_mutex_lock(profile->ireg_mutex); + while (switch_queue_trypop(profile->sql_queue, &pop) == SWITCH_STATUS_SUCCESS && pop) { + sofia_glue_actually_execute_sql(profile, SWITCH_TRUE, (char *) pop, NULL); + free(pop); + } + switch_mutex_unlock(profile->ireg_mutex); + } + + if (++loops >= 100) { + if (++ireg_loops >= IREG_SECONDS) { + sofia_reg_check_expire(profile, switch_timestamp(NULL)); + ireg_loops = 0; + } + + if (++gateway_loops >= GATEWAY_SECONDS) { + sofia_reg_check_gateway(profile, switch_timestamp(NULL)); + gateway_loops = 0; + } + loops = 0; + } + + switch_yield(10000); + qsize = switch_queue_size(profile->sql_queue); + } + + sofia_clear_pflag_locked(profile, PFLAG_WORKER_RUNNING); + + return NULL; +} + + +void launch_sofia_worker_thread(sofia_profile_t *profile) +{ + switch_thread_t *thread; + switch_threadattr_t *thd_attr = NULL; + int x = 0; + + 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_increase(thd_attr); + 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; + } + } +} 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; - uint32_t ireg_loops = 0; - uint32_t gateway_loops = 0; switch_event_t *s_event; - + int tportlog = 0; + switch_mutex_lock(mod_sofia_globals.mutex); mod_sofia_globals.threads++; switch_mutex_unlock(mod_sofia_globals.mutex); @@ -286,11 +508,20 @@ goto end; } + if (switch_test_flag(profile, TFLAG_TPORT_LOG)) { + tportlog = 1; + } profile->nua = nua_create(profile->s_root, /* Event loop */ - sofia_event_callback, /* Callback for processing events */ - profile, /* Additional data to pass to callback */ - NUTAG_URL(profile->bindurl), NTATAG_UDP_MTU(65536), TAG_END()); /* Last tag should always finish the sequence */ + sofia_event_callback, /* Callback for processing events */ + profile, /* Additional data to pass to callback */ + NUTAG_URL(profile->bindurl), + TAG_IF(sofia_test_pflag(profile, PFLAG_TLS), NUTAG_SIPS_URL(profile->tls_bindurl)), + TAG_IF(sofia_test_pflag(profile, PFLAG_TLS), NUTAG_CERTIFICATE_DIR(profile->tls_cert_dir)), + TAG_IF(sofia_test_pflag(profile, PFLAG_TLS), TPTAG_TLS_VERSION(profile->tls_version)), + NTATAG_UDP_MTU(65536), + TAG_IF(tportlog, TPTAG_LOG(1)), + 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\n", profile->name); @@ -298,62 +529,72 @@ goto end; } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created agent for %s\n", profile->name); nua_set_params(profile->nua, - //NUTAG_EARLY_MEDIA(1), + NUTAG_APPL_METHOD("OPTIONS"), + NUTAG_APPL_METHOD("NOTIFY"), + NUTAG_APPL_METHOD("INFO"), NUTAG_AUTOANSWER(0), NUTAG_AUTOALERT(0), - NUTAG_ALLOW("REGISTER"), - NUTAG_ALLOW("REFER"), + TAG_IF((profile->mflags & MFLAG_REGISTER), NUTAG_ALLOW("REGISTER")), + TAG_IF((profile->mflags & MFLAG_REFER), NUTAG_ALLOW("REFER")), NUTAG_ALLOW("INFO"), + NUTAG_ALLOW("NOTIFY"), + NUTAG_ALLOW_EVENTS("talk"), + NUTAG_SESSION_TIMER(profile->session_timeout), + NTATAG_MAX_PROCEEDING(profile->max_proceeding), TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW("PUBLISH")), - TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW("NOTIFY")), TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW("SUBSCRIBE")), TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ENABLEMESSAGE(1)), TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW_EVENTS("presence")), + TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW_EVENTS("dialog")), + TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW_EVENTS("call-info")), + TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW_EVENTS("sla")), + TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW_EVENTS("include-session-description")), TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW_EVENTS("presence.winfo")), TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW_EVENTS("message-summary")), - SIPTAG_SUPPORTED_STR("100rel, precondition"), SIPTAG_USER_AGENT_STR(SOFIA_USER_AGENT), TAG_END()); + SIPTAG_SUPPORTED_STR("100rel, precondition, timer"), 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); 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 */ - NUTAG_URL(node->url), TAG_END()); /* Last tag should always finish the sequence */ + sofia_event_callback, /* Callback for processing events */ + profile, /* Additional data to pass to callback */ + NUTAG_URL(node->url), TAG_END()); /* Last tag should always finish the sequence */ nua_set_params(node->nua, + NUTAG_APPL_METHOD("OPTIONS"), NUTAG_EARLY_MEDIA(1), NUTAG_AUTOANSWER(0), NUTAG_AUTOALERT(0), - NUTAG_ALLOW("REGISTER"), - NUTAG_ALLOW("REFER"), + TAG_IF((profile->mflags & MFLAG_REGISTER), NUTAG_ALLOW("REGISTER")), + TAG_IF((profile->mflags & MFLAG_REFER), NUTAG_ALLOW("REFER")), NUTAG_ALLOW("INFO"), TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW("PUBLISH")), TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ENABLEMESSAGE(1)), - SIPTAG_SUPPORTED_STR("100rel, precondition"), SIPTAG_USER_AGENT_STR(SOFIA_USER_AGENT), TAG_END()); - + SIPTAG_SUPPORTED_STR("100rel, precondition"), 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->gateway_mutex, SWITCH_MUTEX_NESTED, profile->pool); - ireg_loops = IREG_SECONDS; - gateway_loops = GATEWAY_SECONDS; - 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"); + 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(s_event, SWITCH_STACK_BOTTOM, "module_name", "%s", "mod_sofia"); switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "profile_name", "%s", profile->name); switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "profile_uri", "%s", 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(s_event, SWITCH_STACK_BOTTOM, "profile_tls_uri", "%s", profile->tls_url); + } switch_event_fire(&s_event); } @@ -365,46 +606,55 @@ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Starting thread for %s\n", profile->name); - profile->started = time(NULL); - switch_yield(1000000); + profile->started = switch_timestamp(NULL); sofia_set_pflag_locked(profile, PFLAG_RUNNING); - - while (mod_sofia_globals.running == 1 && sofia_test_pflag(profile, PFLAG_RUNNING)) { - if (++ireg_loops >= IREG_SECONDS) { - sofia_reg_check_expire(profile, time(NULL)); - ireg_loops = 0; - } + launch_sofia_worker_thread(profile); - if (++gateway_loops >= GATEWAY_SECONDS) { - sofia_reg_check_gateway(profile, time(NULL)); - gateway_loops = 0; - } + 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); } - - //sofia_reg_check_expire(profile, 0); - //sofia_reg_check_gateway(profile, 0); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock %s\n", profile->name); switch_thread_rwlock_wrlock(profile->rwlock); sofia_reg_unregister(profile); nua_shutdown(profile->nua); + su_root_run(profile->s_root); + sofia_clear_pflag_locked(profile, PFLAG_RUNNING); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "waiting for worker thread\n"); - su_root_run(profile->s_root); + while(sofia_test_pflag(profile, PFLAG_WORKER_RUNNING)) { + switch_yield(100000); + } + + while(profile->inuse) { + switch_yield(100000); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "waiting for %d session(s)\n", profile->inuse); + } 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); 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"); + 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(s_event, SWITCH_STACK_BOTTOM, "module_name", "%s", "mod_sofia"); switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "profile_name", "%s", profile->name); switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "profile_uri", "%s", 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(s_event, SWITCH_STACK_BOTTOM, "profile_tls_uri", "%s", profile->tls_url); + } switch_event_fire(&s_event); } @@ -413,8 +663,9 @@ 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->sub_hash); switch_thread_rwlock_unlock(profile->rwlock); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write unlock %s\n", profile->name); @@ -422,13 +673,10 @@ if (sofia_test_pflag(profile, PFLAG_RESPAWN)) { config_sofia(1, profile->name); } - - switch_core_destroy_memory_pool(&pool); - - end: + switch_core_destroy_memory_pool(&pool); - + end: switch_mutex_lock(mod_sofia_globals.mutex); mod_sofia_globals.threads--; switch_mutex_unlock(mod_sofia_globals.mutex); @@ -444,9 +692,12 @@ 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_increase(thd_attr); switch_thread_create(&thread, thd_attr, sofia_profile_thread_run, profile, profile->pool); } + + static void logger(void *logarg, char const *fmt, va_list ap) { char *data = NULL; @@ -473,7 +724,6 @@ } } - static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag) { switch_xml_t gateway_tag, param; @@ -487,6 +737,14 @@ name = "anonymous"; } + switch_mutex_lock(mod_sofia_globals.hash_mutex); + if ((gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, name))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate gateway '%s'\n", name); + switch_mutex_unlock(mod_sofia_globals.hash_mutex); + goto skip; + } + switch_mutex_unlock(mod_sofia_globals.hash_mutex); + if ((gateway = switch_core_alloc(profile->pool, sizeof(*gateway)))) { char *register_str = "true", *scheme = "Digest", *realm = NULL, @@ -498,8 +756,14 @@ *context = "default", *expire_seconds = "3600", *retry_seconds = "30", - *from_domain = ""; - + *from_user = "", + *from_domain = "", + *register_proxy = NULL, + *contact_params = NULL, + *params = NULL, + *register_transport = NULL; + + gateway->register_transport = SOFIA_TRANSPORT_UDP; gateway->pool = profile->pool; gateway->profile = profile; gateway->name = switch_core_strdup(gateway->pool, name); @@ -532,8 +796,23 @@ expire_seconds = val; } else if (!strcmp(var, "retry-seconds")) { 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, "register-proxy")) { + register_proxy = 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; } } @@ -551,6 +830,10 @@ goto skip; } + if (switch_strlen_zero(from_user)) { + from_user = username; + } + if (switch_strlen_zero(extension)) { extension = username; } @@ -567,6 +850,10 @@ from_domain = realm; } + if (switch_strlen_zero(register_proxy)) { + register_proxy = proxy; + } + gateway->retry_seconds = atoi(retry_seconds); if (gateway->retry_seconds < 10) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "INVALID: retry_seconds correcting the value to 30\n"); @@ -580,10 +867,23 @@ if (switch_true(caller_id_in_from)) { switch_set_flag(gateway, REG_FLAG_CALLERID); } - gateway->register_from = switch_core_sprintf(gateway->pool, "sip:%s@%s", username, from_domain); - gateway->register_contact = switch_core_sprintf(gateway->pool, "sip:%s@%s:%d", extension, - profile->extsipip ? profile->extsipip : profile->sipip, profile->sip_port); - + register_transport = (char *)sofia_glue_transport2str(gateway->register_transport); + if (contact_params) { + if (*contact_params == ';') { + params = switch_core_sprintf(gateway->pool, "%s&transport=%s", contact_params, register_transport); + } else { + params = switch_core_sprintf(gateway->pool, ";%s&transport=%s", contact_params, register_transport); + } + } else { + params = switch_core_sprintf(gateway->pool, ";transport=%s", register_transport); + } + + gateway->register_url = switch_core_sprintf(gateway->pool, "sip:%s;transport=%s", register_proxy, register_transport); + gateway->register_from = switch_core_sprintf(gateway->pool, "<sip:%s@%s;transport=%s>", from_user, from_domain, register_transport); + gateway->register_contact = switch_core_sprintf(gateway->pool, "<sip:%s@%s:%d%s>", extension, + profile->extsipip ? profile->extsipip : profile->sipip, + sofia_glue_transport_has_tls(gateway->register_transport) ? profile->tls_sip_port : profile->sip_port, params); + 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); @@ -596,35 +896,45 @@ if ((gateway->freq = atoi(gateway->expires_str)) < 5) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, - "Invalid Freq: %d. Setting Register-Frequency to 3600\n", gateway->freq); + "Invalid Freq: %d. Setting Register-Frequency to 3600\n", gateway->freq); gateway->freq = 3600; } gateway->freq -= 2; - if ((gp = sofia_reg_find_gateway(gateway->name))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate gateway '%s'\n", gateway->name); - sofia_reg_release_gateway(gp); - } else if ((gp=sofia_reg_find_gateway(gateway->register_from))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate uri '%s'\n", gateway->register_from); - sofia_reg_release_gateway(gp); - } else if ((gp=sofia_reg_find_gateway(gateway->register_contact))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate contact '%s'\n", gateway->register_contact); - sofia_reg_release_gateway(gp); - } else { - gateway->next = profile->gateways; - profile->gateways = gateway; - sofia_reg_add_gateway(gateway->name, gateway); - sofia_reg_add_gateway(gateway->register_from, gateway); - sofia_reg_add_gateway(gateway->register_contact, gateway); - } + gateway->next = profile->gateways; + profile->gateways = gateway; + sofia_reg_add_gateway(gateway->name, gateway); } skip: - assert(gateway_tag); + 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_ERROR, "Error Adding Alias [%s] for profile [%s] (name in use)\n", + dname, profile->name); + } + } + + if (switch_true(parse)) { + switch_xml_t ut, gateways_tag; + 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); + } + } + } } + switch_status_t config_sofia(int reload, char *profile_name) { char *cf = "sofia.conf"; @@ -633,14 +943,19 @@ sofia_profile_t *profile = NULL; char url[512] = ""; int profile_found = 0; - + switch_event_t *params = NULL;; + if (!reload) { su_init(); + if (sip_update_default_mclass(sip_extend_mclass(NULL)) < 0) { + su_deinit(); + return SWITCH_STATUS_FALSE; + } + su_log_redirect(NULL, logger, NULL); su_log_redirect(tport_log, logger, NULL); } - if (!switch_strlen_zero(profile_name) && (profile = sofia_glue_find_profile(profile_name))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Profile [%s] Already exists.\n", switch_str_nil(profile_name)); status = SWITCH_STATUS_FALSE; @@ -648,9 +963,11 @@ return status; } - snprintf(url, sizeof(url), "profile=%s", switch_str_nil(profile_name)); - - if (!(xml = switch_xml_open_cfg(cf, &cfg, url))) { + switch_event_create(¶ms, SWITCH_EVENT_MESSAGE); + 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); status = SWITCH_STATUS_FALSE; goto done; @@ -662,8 +979,6 @@ 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, "log-level-trace")) { - su_log_set_level(tport_log, atoi(val)); } } } @@ -674,12 +989,13 @@ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Settings, check the new config!\n"); } else { char *xprofilename = (char *) switch_xml_attr_soft(xprofile, "name"); + char *xprofiledomain = (char *) switch_xml_attr(xprofile, "domain"); switch_memory_pool_t *pool = NULL; if (!xprofilename) { xprofilename = "unnamed"; } - + if (profile_name) { if (strcasecmp(profile_name, xprofilename)) { continue; @@ -687,7 +1003,7 @@ profile_found = 1; } } - + /* 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"); @@ -699,20 +1015,27 @@ goto done; } - - profile->pool = pool; + profile->user_agent = SOFIA_USER_AGENT; profile->name = switch_core_strdup(profile->pool, xprofilename); - snprintf(url, sizeof(url), "sofia_reg_%s", xprofilename); - + switch_snprintf(url, sizeof(url), "sofia_reg_%s", xprofilename); + + if (xprofiledomain) { + profile->domain_name = switch_core_strdup(profile->pool, xprofiledomain); + } else { + profile->domain_name = profile->name; + } + profile->dbname = switch_core_strdup(profile->pool, url); switch_core_hash_init(&profile->chat_hash, profile->pool); + switch_core_hash_init(&profile->sub_hash, profile->pool); switch_thread_rwlock_create(&profile->rwlock, profile->pool); switch_mutex_init(&profile->flag_mutex, SWITCH_MUTEX_NESTED, profile->pool); profile->dtmf_duration = 100; - profile->codec_ms = 20; - + profile->tls_version = 0; + profile->mflags = MFLAG_REFER | MFLAG_REGISTER; + 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"); @@ -721,31 +1044,47 @@ profile->debug = atoi(val); } else if (!strcasecmp(var, "use-rtp-timer") && switch_true(val)) { switch_set_flag(profile, TFLAG_TIMER); - - } else if (!strcasecmp(var, "odbc-dsn")) { + } else if (!strcasecmp(var, "sip-trace") && switch_true(val)) { + switch_set_flag(profile, TFLAG_TPORT_LOG); + } else if (!strcasecmp(var, "odbc-dsn") && !switch_strlen_zero(val)) { #ifdef SWITCH_HAVE_ODBC profile->odbc_dsn = switch_core_strdup(profile->pool, val); if ((profile->odbc_user = strchr(profile->odbc_dsn, ':'))) { *profile->odbc_user++ = '\0'; + if ((profile->odbc_pass = strchr(profile->odbc_user, ':'))) { + *profile->odbc_pass++ = '\0'; + } } - if ((profile->odbc_pass = strchr(profile->odbc_user, ':'))) { - *profile->odbc_pass++ = '\0'; - } - + #else switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ODBC IS NOT AVAILABLE!\n"); #endif - + } else if (!strcasecmp(var, "user-agent-string")) { + profile->user_agent = switch_core_strdup(profile->pool, val);; + } else if (!strcasecmp(var, "dtmf-type")) { + if (!strcasecmp(val, "rfc2833")) { + profile->dtmf_type = DTMF_2833; + } else if (!strcasecmp(val, "info")) { + profile->dtmf_type = DTMF_INFO; + } else { + profile->dtmf_type = DTMF_NONE; + } + } else if (!strcasecmp(var, "record-template")) { + profile->record_template = switch_core_strdup(profile->pool, val);; } else if (!strcasecmp(var, "inbound-no-media") && switch_true(val)) { switch_set_flag(profile, TFLAG_INB_NOMEDIA); + } else if (!strcasecmp(var, "inbound-bypass-media") && switch_true(val)) { + switch_set_flag(profile, TFLAG_INB_NOMEDIA); } else if (!strcasecmp(var, "inbound-late-negotiation") && switch_true(val)) { switch_set_flag(profile, TFLAG_LATE_NEGOTIATION); + } else if (!strcasecmp(var, "inbound-proxy-media") && switch_true(val)) { + switch_set_flag(profile, TFLAG_PROXY_MEDIA); } else if (!strcasecmp(var, "rfc2833-pt")) { profile->te = (switch_payload_t) atoi(val); } else if (!strcasecmp(var, "cng-pt")) { profile->cng_pt = (switch_payload_t) atoi(val); } else if (!strcasecmp(var, "sip-port")) { - profile->sip_port = atoi(val); + profile->sip_port = strcasecmp(val, "auto") ? atoi(val) : 0; } else if (!strcasecmp(var, "vad")) { if (!strcasecmp(val, "in")) { switch_set_flag(profile, TFLAG_VAD_IN); @@ -767,16 +1106,16 @@ if (!strcasecmp(val, "auto")) { profile->extsipip = switch_core_strdup(profile->pool, mod_sofia_globals.guess_ip); } else { - char *ip = NULL; + char *ip = mod_sofia_globals.guess_ip; switch_port_t port = 0; if (sofia_glue_ext_address_lookup(&ip, &port, val, profile->pool) == SWITCH_STATUS_SUCCESS) { - - if (ip) { - profile->extsipip = switch_core_strdup(profile->pool, ip); - } + profile->extsipip = switch_core_strdup(profile->pool, ip); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get external ip.\n"); } } - + } else if (!strcasecmp(var, "force-register-domain")) { + profile->reg_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")) { @@ -785,14 +1124,54 @@ 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, "session-timeout")) { + int v_session_timeout = atoi(val); + if (v_session_timeout >= 0) { + profile->session_timeout = v_session_timeout; + } + } else if (!strcasecmp(var, "max-proceeding")) { + int v_max_proceeding = atoi(val); + if (v_max_proceeding >= 0) { + profile->max_proceeding = v_max_proceeding; + } + } else if (!strcasecmp(var, "rtp-timeout-sec")) { + int v = atoi(val); + if (v >= 0) { + profile->rtp_timeout_sec = v; + } + } else if (!strcasecmp(var, "rtp-hold-timeout-sec")) { + int v = atoi(val); + if (v >= 0) { + profile->rtp_hold_timeout_sec = v; + } + } else if (!strcasecmp(var, "disable-transfer") && switch_true(val)) { + profile->mflags &= ~MFLAG_REFER; + } else if (!strcasecmp(var, "disable-register") && switch_true(val)) { + profile->mflags &= ~MFLAG_REGISTER; } else if (!strcasecmp(var, "manage-presence")) { if (switch_true(val)) { profile->pflags |= PFLAG_PRESENCE; } + } else if (!strcasecmp(var, "require-secure-rtp")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_SECURE; + } + } else if (!strcasecmp(var, "multiple-registrations")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_MULTIREG; + } + } else if (!strcasecmp(var, "supress-cng")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_SUPRESS_CNG; + } } else if (!strcasecmp(var, "NDLB-to-in-200-contact")) { if (switch_true(val)) { profile->ndlb |= PFLAG_NDLB_TO_IN_200_CONTACT; } + } else if (!strcasecmp(var, "NDLB-broken-auth-hash")) { + if (switch_true(val)) { + profile->ndlb |= PFLAG_NDLB_BROKEN_AUTH_HASH; + } } else if (!strcasecmp(var, "pass-rfc2833")) { if (switch_true(val)) { profile->pflags |= PFLAG_PASS_RFC2833; @@ -819,6 +1198,10 @@ if (switch_true(val)) { profile->pflags |= PFLAG_BLIND_REG; } + } else if (!strcasecmp(var, "accept-blind-auth")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_BLIND_AUTH; + } } else if (!strcasecmp(var, "auth-all-packets")) { if (switch_true(val)) { profile->pflags |= PFLAG_AUTH_ALL; @@ -827,6 +1210,10 @@ if (switch_true(val)) { profile->pflags |= PFLAG_FULL_ID; } + } else if (!strcasecmp(var, "inbound-reg-force-matching-username")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_CHECKUSER; + } } else if (!strcasecmp(var, "bitpacking")) { if (!strcasecmp(val, "aal2")) { profile->codec_flags = SWITCH_CODEC_FLAG_AAL2; @@ -835,6 +1222,18 @@ profile->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-inbound-acl")) { + if (profile->acl_count < SOFIA_MAX_ACL) { + profile->acl[profile->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")) { + 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, "alias")) { sip_alias_node_t *node; if ((node = switch_core_alloc(profile->pool, sizeof(*node)))) { @@ -849,8 +1248,6 @@ profile->max_calls = atoi(val); } else if (!strcasecmp(var, "codec-prefs")) { profile->codec_string = switch_core_strdup(profile->pool, val); - } else if (!strcasecmp(var, "codec-ms")) { - profile->codec_ms = atoi(val); } else if (!strcasecmp(var, "dtmf-duration")) { int dur = atoi(val); if (dur > 10 && dur < 8000) { @@ -858,7 +1255,28 @@ } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Duration out of bounds!\n"); } - } + + /* + * handle TLS params #1 + */ + } else if (!strcasecmp(var, "tls")) { + if (switch_true(val)) { + sofia_set_pflag(profile, PFLAG_TLS); + } + } else if (!strcasecmp(var, "tls-bind-params")) { + profile->tls_bind_params = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "tls-sip-port")) { + profile->tls_sip_port = atoi(val); + } else if (!strcasecmp(var, "tls-cert-dir")) { + profile->tls_cert_dir = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "tls-version")) { + + if (!strcasecmp(val, "tlsv1")) { + profile->tls_version = 1; + } else { + profile->tls_version = 0; + } + } } if (!profile->cng_pt) { @@ -891,10 +1309,6 @@ profile->rtpip = switch_core_strdup(profile->pool, "127.0.0.1"); } - if (!profile->sip_port) { - profile->sip_port = atoi(SOFIA_DEFAULT_PORT); - } - if (!profile->dialplan) { profile->dialplan = switch_core_strdup(profile->pool, "XML"); } @@ -903,18 +1317,51 @@ profile->sipdomain = switch_core_strdup(profile->pool, profile->sipip); } if (profile->extsipip) { - profile->url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->extsipip, profile->sip_port); + if (!profile->sip_port) { + profile->url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:*", profile->extsipip); + } else { + profile->url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->extsipip, profile->sip_port); + } profile->bindurl = switch_core_sprintf(profile->pool, "%s;maddr=%s", profile->url, profile->sipip); } else { - profile->url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->sipip, profile->sip_port); + if (!profile->sip_port) { + profile->url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:*", profile->sipip); + } else { + profile->url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->sipip, profile->sip_port); + } profile->bindurl = profile->url; } if (profile->bind_params) { - char *url = profile->bindurl; - profile->bindurl = switch_core_sprintf(profile->pool, "%s;%s", url, profile->bind_params); + char *bindurl = profile->bindurl; + profile->bindurl = switch_core_sprintf(profile->pool, "%s;%s", bindurl, profile->bind_params); + } + + /* + * handle TLS params #2 + */ + if (sofia_test_pflag(profile, PFLAG_TLS)) { + if (!profile->tls_sip_port) { + profile->tls_sip_port = atoi(SOFIA_DEFAULT_TLS_PORT); + } + + if (profile->extsipip) { + profile->tls_url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->extsipip, profile->tls_sip_port); + profile->tls_bindurl = switch_core_sprintf(profile->pool, "sips:mod_sofia@%s:%d;maddr=%s", profile->extsipip, profile->tls_sip_port, profile->sipip); + } else { + profile->tls_url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->sipip, profile->tls_sip_port); + profile->tls_bindurl = switch_core_sprintf(profile->pool, "sips:mod_sofia@%s:%d", profile->sipip, 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); + } + + if (!profile->tls_cert_dir) { + profile->tls_cert_dir = switch_core_sprintf(profile->pool, "%s/ssl", SWITCH_GLOBAL_dirs.conf_dir); + } } - } if (profile) { switch_xml_t aliases_tag, alias_tag; @@ -928,26 +1375,29 @@ if ((domains_tag = switch_xml_child(xprofile, "domains"))) { for (domain_tag = switch_xml_child(domains_tag, "domain"); domain_tag; domain_tag = domain_tag->next) { - switch_xml_t droot, actual_domain_tag, ut; - char *dname = (char *) switch_xml_attr_soft(domain_tag, "name"); - char *parse = (char *) switch_xml_attr_soft(domain_tag, "parse"); - + 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 (!switch_strlen_zero(dname)) { - if (switch_true(parse)) { - if (switch_xml_locate_domain(dname, NULL, &droot, &actual_domain_tag) == SWITCH_STATUS_SUCCESS) { - for (ut = switch_xml_child(actual_domain_tag, "user"); ut; ut = ut->next) { - if (((gateways_tag = switch_xml_child(ut, "gateways")))) { - parse_gateways(profile, gateways_tag); - } + if (!strcasecmp(dname, "all")) { + switch_xml_t xml_root, x_domains; + if (switch_xml_locate("directory", NULL, NULL, NULL, &xml_root, &x_domains, NULL) == 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(droot); + switch_xml_free(xml_root); } + } else if (switch_xml_locate_domain(dname, NULL, &droot, &x_domain_tag) == SWITCH_STATUS_SUCCESS) { + parse_domain_tag(profile, x_domain_tag, dname, parse, alias); + switch_xml_free(droot); } - sofia_glue_add_profile(switch_core_strdup(profile->pool, dname), profile); } } } - + 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"); @@ -957,13 +1407,12 @@ 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); + aname, profile->name); } } } } - if (profile->sipip) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Started Profile %s [%s]\n", profile->name, url); launch_sofia_profile_thread(profile); @@ -978,6 +1427,9 @@ } } done: + + switch_event_destroy(¶ms); + if (xml) { switch_xml_free(xml); } @@ -988,7 +1440,131 @@ } return status; +} + +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, tagi_t tags[]) +{ + 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); + + switch_channel_clear_flag(channel, CF_REQ_MEDIA); + + if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { + + if (!switch_test_flag(tech_pvt, TFLAG_SENT_UPDATE)) { + return; + } + + switch_clear_flag_locked(tech_pvt, TFLAG_SENT_UPDATE); + + if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { + const char *r_sdp = NULL; + switch_core_session_message_t msg = { 0 }; + + 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)) { + r_sdp = sip->sip_payload->pl_data; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Passing %d %s to other leg\n", status, phrase); + + 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 (r_sdp) { + msg.pointer_arg = switch_core_session_strdup(other_session, r_sdp); + msg.pointer_arg_size = strlen(r_sdp); + } + if (switch_core_session_receive_message(other_session, &msg) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Other leg is not available\n"); + nua_respond(tech_pvt->nh, 403, "Hangup in progress", TAG_END()); + } + switch_core_session_rwunlock(other_session); + } + return; + } + + 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)) { + const char *from_user = "", *from_host = "", *to_user = "", *to_host = "", *contact_user = "", *contact_host = ""; + const char *user_agent = "", *call_id = ""; + 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); + } + + 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); + } + + if (contact) { + contact_user = switch_str_nil(contact->url_user); + contact_host = switch_str_nil(contact->url_host); + } + + if (profile->pflags & PFLAG_PRESENCE) { + sql = switch_mprintf( + "insert into sip_dialogs values('%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q')", + call_id, + switch_core_session_get_uuid(session), + to_user, + to_host, + from_user, + from_host, + contact_user, + contact_host, + astate, + "outbound", + user_agent + ); + + switch_assert(sql); + + sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); + } + } else if (status == 200 && (profile->pflags & PFLAG_PRESENCE)) { + char *sql = NULL; + sql = switch_mprintf("update sip_dialogs set state='%s' where uuid='%s';\n", astate, switch_core_session_get_uuid(session)); + switch_assert(sql); + sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); + } + } + } } static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, @@ -1001,12 +1577,11 @@ switch_channel_t *channel = NULL; private_object_t *tech_pvt = NULL; const char *replaces_str = NULL; - char *uuid; + const char *uuid; switch_core_session_t *other_session = NULL; switch_channel_t *other_channel = NULL; char st[80] = ""; - tl_gets(tags, NUTAG_CALLSTATE_REF(ss_state), NUTAG_OFFER_RECV_REF(offer_recv), @@ -1017,13 +1592,9 @@ if (session) { channel = switch_core_session_get_channel(session); - assert(channel != NULL); - tech_pvt = switch_core_session_get_private(session); - assert(tech_pvt != NULL); - assert(tech_pvt->nh != NULL); - - + switch_assert(tech_pvt != NULL); + switch_assert(tech_pvt->nh != NULL); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Channel %s entering state [%s]\n", switch_channel_get_name(channel), nua_callstate_name(ss_state)); @@ -1040,6 +1611,19 @@ goto done; } + if (status == 183 && !r_sdp) { + status = 180; + } + + if (channel && (status == 180 || status == 183) && switch_channel_test_flag(channel, CF_OUTBOUND)) { + const char *val; + if ((val = switch_channel_get_variable(channel, "sip_auto_answer")) && switch_true(val)) { + nua_notify(nh, NUTAG_NEWSUB(1), NUTAG_SUBSTATE(nua_substate_active), SIPTAG_EVENT_STR("talk"), TAG_END()); + } + } + + state_process: + switch ((enum nua_callstate) ss_state) { case nua_callstate_init: break; @@ -1052,7 +1636,7 @@ if (status == 180) { switch_channel_mark_ring_ready(channel); if (!switch_channel_test_flag(channel, CF_GEN_RINGBACK)) { - if (switch_channel_test_flag(channel, CF_BYPASS_MEDIA)) { + if (switch_channel_test_flag(channel, CF_PROXY_MODE)) { if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { switch_core_session_message_t msg; @@ -1069,9 +1653,15 @@ } if (r_sdp) { - if (switch_channel_test_flag(channel, CF_BYPASS_MEDIA)) { + if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { switch_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); switch_channel_mark_pre_answered(channel); + switch_set_flag(tech_pvt, TFLAG_SDP); + if (switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { + if (sofia_glue_activate_rtp(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) { + goto done; + } + } if (!switch_channel_test_flag(channel, CF_GEN_RINGBACK) && (uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { other_channel = switch_core_session_get_channel(other_session); @@ -1084,7 +1674,9 @@ } goto done; } else { - if (switch_test_flag(tech_pvt, TFLAG_LATE_NEGOTIATION)) { + if (switch_channel_test_flag(channel, CF_PROXY_MEDIA) && !switch_channel_test_flag(tech_pvt->channel, CF_OUTBOUND)) { + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "PROXY MEDIA"); + } else if (switch_test_flag(tech_pvt, TFLAG_LATE_NEGOTIATION) && !switch_channel_test_flag(tech_pvt->channel, CF_OUTBOUND)) { switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "DELAYED NEGOTIATION"); } else { if (sofia_glue_tech_media(tech_pvt, (char *) r_sdp) != SWITCH_STATUS_SUCCESS) { @@ -1104,13 +1696,27 @@ case nua_callstate_received: if (tech_pvt && !switch_test_flag(tech_pvt, TFLAG_SDP)) { if (r_sdp && !switch_test_flag(tech_pvt, TFLAG_SDP)) { - if (switch_channel_test_flag(channel, CF_BYPASS_MEDIA)) { + if (switch_channel_test_flag(channel, CF_PROXY_MODE)) { switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOMEDIA"); switch_set_flag_locked(tech_pvt, TFLAG_READY); - switch_channel_set_state(channel, CS_INIT); - //switch_core_session_thread_launch(session); + if (switch_channel_get_state(channel) == CS_NEW) { + switch_channel_set_state(channel, CS_INIT); + } + switch_set_flag(tech_pvt, TFLAG_SDP); goto done; - } else { + } else if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MEDIA)) { + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "PROXY MEDIA"); + switch_set_flag_locked(tech_pvt, TFLAG_READY); + if (switch_channel_get_state(channel) == CS_NEW) { + switch_channel_set_state(channel, CS_INIT); + } + } else if (switch_test_flag(tech_pvt, TFLAG_LATE_NEGOTIATION)) { + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "DELAYED NEGOTIATION"); + switch_set_flag_locked(tech_pvt, TFLAG_READY); + if (switch_channel_get_state(channel) == CS_NEW) { + switch_channel_set_state(channel, CS_INIT); + } + } else { sdp_parser_t *parser; sdp_session_t *sdp; uint8_t match = 0; @@ -1127,59 +1733,77 @@ if (match) { nua_handle_t *bnh; sip_replaces_t *replaces; + su_home_t *home = NULL; switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED"); switch_set_flag_locked(tech_pvt, TFLAG_READY); - switch_channel_set_state(channel, CS_INIT); - //switch_core_session_thread_launch(session); - - if (replaces_str && (replaces = sip_replaces_make(tech_pvt->sofia_private->home, replaces_str)) - && (bnh = nua_handle_by_replaces(nua, replaces))) { - sofia_private_t *b_private; - - switch_log_printf(SWITCH_CHANNEL_LOG, 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))) { - char *br_b = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); - char *br_a = b_private->uuid; - - if (br_b) { - switch_ivr_uuid_bridge(br_a, br_b); - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); - switch_channel_hangup(channel, SWITCH_CAUSE_ATTENDED_TRANSFER); + if (switch_channel_get_state(channel) == CS_NEW) { + switch_channel_set_state(channel, CS_INIT); + } + switch_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_LOG, 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_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); + char *br_a = b_private->uuid; + + if (br_b) { + switch_ivr_uuid_bridge(br_a, br_b); + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); + switch_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD); + 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); } - } 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); } - nua_handle_unref(bnh); + su_home_unref(home); + home = NULL; } + goto done; } switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "NO CODECS"); - nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); } } else { - if (switch_channel_test_flag(channel, CF_BYPASS_MEDIA)) { + if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { goto done; } else { switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOSDP"); - switch_set_flag(tech_pvt, TFLAG_LATE_NEGOTIATION); + sofia_glue_tech_choose_port(tech_pvt, 0); sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0); - + switch_channel_set_state(channel, CS_HIBERNATE); nua_respond(tech_pvt->nh, SIP_200_OK, SIPTAG_CONTACT_STR(tech_pvt->profile->url), - SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), SOATAG_AUDIO_AUX("cn telephone-event"), NUTAG_INCLUDE_EXTRA_SDP(1), TAG_END()); + SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), + SOATAG_REUSE_REJECTED(1), + SOATAG_ORDERED_USER(1), + SOATAG_AUDIO_AUX("cn telephone-event"), + NUTAG_INCLUDE_EXTRA_SDP(1), + TAG_END()); goto done; } } + + } else { + ss_state = nua_callstate_completed; + goto state_process; } break; @@ -1192,7 +1816,24 @@ uint8_t match = 0; if (r_sdp) { - if (switch_channel_test_flag(channel, CF_BYPASS_MEDIA)) { + if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { + if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) + && (other_session = switch_core_session_locate(uuid))) { + switch_core_session_message_t msg = { 0 }; + + msg.message_id = SWITCH_MESSAGE_INDICATE_MEDIA_REDIRECT; + msg.from = __FILE__; + msg.string_arg = (char *) r_sdp; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Passing SDP to other leg.\n%s\n", r_sdp); + if (switch_core_session_receive_message(other_session, &msg) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Other leg is not available\n"); + nua_respond(tech_pvt->nh, 403, "Hangup in progress", TAG_END()); + } + switch_core_session_rwunlock(other_session); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, 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 { if (tech_pvt->num_codecs) { @@ -1204,12 +1845,12 @@ } } if (match) { - if (sofia_glue_tech_choose_port(tech_pvt) != SWITCH_STATUS_SUCCESS) { + if (sofia_glue_tech_choose_port(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) { goto done; } sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0); switch_set_flag_locked(tech_pvt, TFLAG_REINVITE); - if (sofia_glue_activate_rtp(tech_pvt) != SWITCH_STATUS_SUCCESS) { + if (sofia_glue_activate_rtp(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Reinvite RTP Error!\n"); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } @@ -1219,17 +1860,30 @@ switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } } + + + nua_respond(tech_pvt->nh, SIP_200_OK, + SIPTAG_CONTACT_STR(tech_pvt->reply_contact), + SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), + SOATAG_REUSE_REJECTED(1), + SOATAG_ORDERED_USER(1), + SOATAG_AUDIO_AUX("cn telephone-event"), + NUTAG_INCLUDE_EXTRA_SDP(1), + TAG_END()); } } break; case nua_callstate_ready: + if (channel) { + switch_channel_clear_flag(channel, CF_REQ_MEDIA); + } if (tech_pvt && nh == tech_pvt->nh2) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Cheater Reinvite!\n"); switch_set_flag_locked(tech_pvt, TFLAG_REINVITE); tech_pvt->nh = tech_pvt->nh2; tech_pvt->nh2 = NULL; - if (sofia_glue_tech_choose_port(tech_pvt) == SWITCH_STATUS_SUCCESS) { - if (sofia_glue_activate_rtp(tech_pvt) != SWITCH_STATUS_SUCCESS) { + if (sofia_glue_tech_choose_port(tech_pvt, 0) == SWITCH_STATUS_SUCCESS) { + if (sofia_glue_activate_rtp(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cheater Reinvite RTP Error!\n"); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } @@ -1240,6 +1894,7 @@ if (channel) { if (switch_test_flag(tech_pvt, TFLAG_EARLY_MEDIA)) { switch_set_flag_locked(tech_pvt, TFLAG_ANS); + switch_set_flag(tech_pvt, TFLAG_SDP); switch_channel_mark_answered(channel); if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { @@ -1254,9 +1909,15 @@ r_sdp = (const char *) switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE); } if (r_sdp && !switch_test_flag(tech_pvt, TFLAG_SDP)) { - if (switch_channel_test_flag(channel, CF_BYPASS_MEDIA)) { + if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { switch_set_flag_locked(tech_pvt, TFLAG_ANS); + switch_set_flag_locked(tech_pvt, TFLAG_SDP); switch_channel_mark_answered(channel); + if (switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { + if (sofia_glue_activate_rtp(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) { + goto done; + } + } if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) && (other_session = switch_core_session_locate(uuid))) { other_channel = switch_core_session_get_channel(other_session); @@ -1283,27 +1944,37 @@ if (match) { switch_set_flag_locked(tech_pvt, TFLAG_ANS); - if (sofia_glue_tech_choose_port(tech_pvt) == SWITCH_STATUS_SUCCESS) { - if (sofia_glue_activate_rtp(tech_pvt) == SWITCH_STATUS_SUCCESS) { + if (sofia_glue_tech_choose_port(tech_pvt, 0) == SWITCH_STATUS_SUCCESS) { + if (sofia_glue_activate_rtp(tech_pvt, 0) == SWITCH_STATUS_SUCCESS) { switch_channel_mark_answered(channel); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "RTP Error!\n"); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } + if (switch_channel_get_state(channel) == CS_HIBERNATE) { + switch_set_flag_locked(tech_pvt, TFLAG_READY); + if (switch_channel_get_state(channel) == CS_NEW) { + switch_channel_set_state(channel, CS_INIT); + } + switch_set_flag(tech_pvt, TFLAG_SDP); + } goto done; } } switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "NO CODECS"); - nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); } } - } break; case nua_callstate_terminating: + if (session) { + if (!switch_test_flag(tech_pvt, TFLAG_BYE)) { + switch_set_flag_locked(tech_pvt, TFLAG_BYE); + } + } break; case nua_callstate_terminated: if (session) { @@ -1312,27 +1983,30 @@ if (switch_test_flag(tech_pvt, TFLAG_NOHUP)) { switch_clear_flag_locked(tech_pvt, TFLAG_NOHUP); } else { - snprintf(st, sizeof(st), "%d", status); + int cause; + if (tech_pvt->q850_cause) { + cause = tech_pvt->q850_cause; + } else { + cause = sofia_glue_sip_cause_to_freeswitch(status); + } + switch_snprintf(st, sizeof(st), "%d", status); switch_channel_set_variable(channel, "sip_term_status", st); - switch_channel_hangup(channel, sofia_glue_sip_cause_to_freeswitch(status)); + switch_snprintf(st, sizeof(st), "%d", cause); + switch_channel_set_variable(channel, "sip_term_cause", st); + switch_channel_hangup(channel, cause); } } if (tech_pvt->sofia_private) { - if (tech_pvt->sofia_private->home) { - su_home_unref(tech_pvt->sofia_private->home); - } - free(tech_pvt->sofia_private); + sofia_private = tech_pvt->sofia_private; tech_pvt->sofia_private = NULL; + free(sofia_private); } tech_pvt->nh = NULL; } else if (sofia_private) { - if (sofia_private->home) { - su_home_unref(sofia_private->home); - } free(sofia_private); } @@ -1344,11 +2018,9 @@ } done: - return; } - /*---------------------------------------*/ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]) { @@ -1356,29 +2028,44 @@ sip_from_t const *from; sip_to_t const *to; sip_refer_to_t const *refer_to; - private_object_t *tech_pvt = NULL; + private_object_t *tech_pvt = switch_core_session_get_private(session); char *etmp = NULL, *exten = NULL; - switch_channel_t *channel_a = NULL, *channel_b = 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; - tech_pvt = switch_core_session_get_private(session); - channel_a = switch_core_session_get_channel(session); + if (!(profile->mflags & MFLAG_REFER)) { + nua_respond(nh, SIP_403_FORBIDDEN, NUTAG_WITH_THIS(nua), TAG_END()); + goto done; + } if (!sip->sip_cseq || !(etmp = switch_mprintf("refer;id=%u", sip->sip_cseq->cs_seq))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); goto done; } - - if (switch_channel_test_flag(channel_a, CF_BYPASS_MEDIA)) { - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + if (switch_channel_test_flag(channel_a, CF_PROXY_MODE)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Transfer on bypass media not allowed.\n"); + nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), TAG_END()); goto done; } from = sip->sip_from; to = sip->sip_to; + + home = su_home_new(sizeof(*home)); + switch_assert(home != NULL); + + if (sip->sip_referred_by) { + full_ref_by = sip_header_as_string(home, (void *) sip->sip_referred_by); + } if ((refer_to = sip->sip_refer_to)) { + full_ref_to = sip_header_as_string(home, (void *) sip->sip_refer_to); + if (profile->pflags & PFLAG_FULL_ID) { exten = switch_mprintf("%s@%s", (char *) refer_to->r_url->url_user, (char *) refer_to->r_url->url_host); } else { @@ -1393,11 +2080,10 @@ char *rep; if ((rep = strchr(refer_to->r_url->url_headers, '='))) { - char *br_a = NULL, *br_b = NULL; + const char *br_a = NULL, *br_b = NULL; char *buf; - rep++; - + rep++; if ((buf = switch_core_session_alloc(session, strlen(rep) + 1))) { rep = url_unescape(buf, (const char *) rep); @@ -1406,7 +2092,8 @@ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); goto done; } - if ((replaces = sip_replaces_make(tech_pvt->sofia_private->home, rep)) + + if ((replaces = sip_replaces_make(home, rep)) && (bnh = nua_handle_by_replaces(nua, replaces))) { sofia_private_t *b_private = NULL; private_object_t *b_tech_pvt = NULL; @@ -1422,56 +2109,88 @@ br_a = switch_channel_get_variable(channel_a, SWITCH_SIGNAL_BOND_VARIABLE); br_b = switch_channel_get_variable(channel_b, SWITCH_SIGNAL_BOND_VARIABLE); - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Attended Transfer [%s][%s]\n", br_a, br_b); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Attended Transfer [%s][%s]\n", switch_str_nil(br_a), switch_str_nil(br_b)); if (br_a && br_b) { - switch_ivr_uuid_bridge(br_a, br_b); + switch_core_session_t *new_b_session = NULL, *a_session = NULL; + + switch_ivr_uuid_bridge(br_b, br_a); switch_channel_set_variable(channel_b, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); - switch_set_flag_locked(tech_pvt, TFLAG_BYE); - switch_set_flag_locked(b_tech_pvt, TFLAG_BYE); - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), SIPTAG_EVENT_STR(etmp), TAG_END()); + switch_clear_flag_locked(b_tech_pvt, TFLAG_SIP_HOLD); + switch_ivr_park_session(b_session); + new_b_session = switch_core_session_locate(br_b); + a_session = switch_core_session_locate(br_a); + sofia_info_send_sipfrag(a_session, new_b_session); + if (new_b_session) { + switch_core_session_rwunlock(new_b_session); + } + if (a_session) { + switch_core_session_rwunlock(a_session); + } + //switch_channel_hangup(channel_b, SWITCH_CAUSE_ATTENDED_TRANSFER); } else { if (!br_a && !br_b) { switch_set_flag_locked(tech_pvt, TFLAG_NOHUP); switch_set_flag_locked(b_tech_pvt, TFLAG_XFER); b_tech_pvt->xferto = switch_core_session_strdup(b_session, switch_core_session_get_uuid(session)); - } else if (!br_a && br_b) { - switch_core_session_t *br_b_session; + switch_set_flag_locked(tech_pvt, TFLAG_BYE); + nua_notify(tech_pvt->nh, + NUTAG_NEWSUB(1), + SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + NUTAG_SUBSTATE(nua_substate_terminated), + SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), + SIPTAG_EVENT_STR(etmp), + TAG_END()); + } else { + switch_core_session_t *t_session; + switch_channel_t *hup_channel; + const char *ext; + + if (br_a && !br_b) { + t_session = switch_core_session_locate(br_a); + hup_channel = channel_b; + } 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; + switch_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD); + switch_clear_flag_locked(h_tech_pvt, TFLAG_SIP_HOLD); + switch_channel_hangup(channel_b, SWITCH_CAUSE_ATTENDED_TRANSFER); + } - if ((br_b_session = switch_core_session_locate(br_b))) { - private_object_t *br_b_tech_pvt = switch_core_session_get_private(br_b_session); - switch_channel_t *br_b_channel = switch_core_session_get_channel(br_b_session); - - switch_set_flag_locked(tech_pvt, TFLAG_NOHUP); - - switch_channel_clear_state_handler(br_b_channel, NULL); - switch_channel_set_state_flag(br_b_channel, CF_TRANSFER); - switch_channel_set_state(br_b_channel, CS_TRANSMIT); - - switch_set_flag_locked(tech_pvt, TFLAG_REINVITE); - tech_pvt->local_sdp_audio_ip = switch_core_session_strdup(session, b_tech_pvt->local_sdp_audio_ip); - tech_pvt->local_sdp_audio_port = b_tech_pvt->local_sdp_audio_port; - - tech_pvt->remote_sdp_audio_ip = switch_core_session_strdup(session, br_b_tech_pvt->remote_sdp_audio_ip); - tech_pvt->remote_sdp_audio_port = br_b_tech_pvt->remote_sdp_audio_port; - if (sofia_glue_activate_rtp(tech_pvt) == SWITCH_STATUS_SUCCESS) { - br_b_tech_pvt->kick = switch_core_session_strdup(br_b_session, switch_core_session_get_uuid(session)); - } else { - switch_channel_hangup(channel_a, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + if (t_session) { + switch_channel_t *t_channel = switch_core_session_get_channel(t_session); + ext = switch_channel_get_variable(hup_channel, "destination_number"); + + if (!switch_strlen_zero(full_ref_by)) { + switch_channel_set_variable(t_channel, SOFIA_SIP_HEADER_PREFIX "Referred-By", full_ref_by); } - switch_core_session_rwunlock(br_b_session); + if (!switch_strlen_zero(full_ref_to)) { + switch_channel_set_variable(t_channel, SOFIA_REFER_TO_VARIABLE, full_ref_to); + } + + switch_ivr_session_transfer(t_session, ext, NULL, NULL); + nua_notify(tech_pvt->nh, + NUTAG_NEWSUB(1), + SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + NUTAG_SUBSTATE(nua_substate_terminated), + SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), + SIPTAG_EVENT_STR(etmp), + TAG_END()); + switch_core_session_rwunlock(t_session); + switch_channel_hangup(hup_channel, SWITCH_CAUSE_ATTENDED_TRANSFER); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Session to transfer to not found.\n"); + nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + NUTAG_SUBSTATE(nua_substate_terminated), + SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), TAG_END()); } - - switch_channel_hangup(channel_b, SWITCH_CAUSE_ATTENDED_TRANSFER); } - switch_set_flag_locked(tech_pvt, TFLAG_BYE); - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), - NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), SIPTAG_EVENT_STR(etmp), TAG_END()); - } if (b_session) { switch_core_session_rwunlock(b_session); @@ -1488,30 +2207,50 @@ switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING; uint32_t timeout = 60; char *tuuid_str; + const char *port = NULL; + switch_status_t status; - channel = switch_core_session_get_channel(a_session); + if (refer_to && refer_to->r_url && refer_to->r_url->url_port) { + port = refer_to->r_url->url_port; + } + if (switch_strlen_zero(port)) { + port = "5060"; + } + channel = switch_core_session_get_channel(a_session); + exten = switch_mprintf("sofia/%s/%s@%s:%s", profile->name, - (char *) refer_to->r_url->url_user, (char *) refer_to->r_url->url_host, refer_to->r_url->url_port); - + refer_to->r_url->url_user, + refer_to->r_url->url_host, + port); + switch_channel_set_variable(channel, SOFIA_REPLACES_HEADER, rep); - - if (switch_ivr_originate(a_session, - &tsession, &cause, exten, timeout, &noop_state_handler, NULL, NULL, NULL) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Create Outgoing Channel! [%s]\n", exten); - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), - NUTAG_SUBSTATE(nua_substate_terminated), - SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), TAG_END()); - goto done; + if (!switch_strlen_zero(full_ref_by)) { + switch_channel_set_variable(channel, SOFIA_SIP_HEADER_PREFIX "Referred-By", full_ref_by); } + if (!switch_strlen_zero(full_ref_to)) { + switch_channel_set_variable(channel, SOFIA_REFER_TO_VARIABLE, full_ref_to); + } + status = switch_ivr_originate(a_session, + &tsession, &cause, exten, timeout, &noop_state_handler, NULL, NULL, NULL, SOF_NONE); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Create Outgoing Channel! [%s]\n", exten); + nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + NUTAG_SUBSTATE(nua_substate_terminated), + SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), TAG_END()); + switch_core_session_rwunlock(a_session); + + if (status != SWITCH_STATUS_SUCCESS) { + goto done; + } + tuuid_str = switch_core_session_get_uuid(tsession); switch_ivr_uuid_bridge(br_a, tuuid_str); switch_channel_set_variable(channel_a, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); switch_set_flag_locked(tech_pvt, TFLAG_BYE); - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), SIPTAG_EVENT_STR(etmp), TAG_END()); switch_core_session_rwunlock(tsession); } else { @@ -1522,7 +2261,7 @@ error: switch_log_printf(SWITCH_CHANNEL_LOG, 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, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), TAG_END()); } @@ -1540,211 +2279,241 @@ if (exten) { switch_channel_t *channel = switch_core_session_get_channel(session); - char *br; + const char *br; if ((br = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE))) { switch_core_session_t *b_session; - + if ((b_session = switch_core_session_locate(br))) { - switch_channel_set_variable(channel, "TRANSFER_FALLBACK", from->a_user); - switch_ivr_session_transfer(b_session, exten, profile->dialplan, profile->context); + switch_channel_t *b_channel = switch_core_session_get_channel(b_session); + switch_channel_set_variable(channel, "transfer_fallback_extension", from->a_user); + if (!switch_strlen_zero(full_ref_by)) { + switch_channel_set_variable(b_channel, SOFIA_SIP_HEADER_PREFIX "Referred-By", full_ref_by); + } + if (!switch_strlen_zero(full_ref_to)) { + switch_channel_set_variable(b_channel, SOFIA_REFER_TO_VARIABLE, full_ref_to); + } + switch_ivr_session_transfer(b_session, exten, NULL, NULL); switch_core_session_rwunlock(b_session); } switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "BLIND_TRANSFER"); - - /* - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), SIPTAG_EVENT_STR(etmp), TAG_END()); - */ + } else { - exten = switch_mprintf("sip:%s@%s:%s", (char *) refer_to->r_url->url_user, (char *) refer_to->r_url->url_host, refer_to->r_url->url_port); - tech_pvt->dest = switch_core_session_strdup(session, exten); - - - switch_set_flag_locked(tech_pvt, TFLAG_NOHUP); - - /* - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), - NUTAG_SUBSTATE(nua_substate_terminated), - SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), - SIPTAG_EVENT_STR(etmp), - TAG_END()); - */ - sofia_glue_do_xfer_invite(session); - + switch_log_printf(SWITCH_CHANNEL_LOG, 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"), + NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), + TAG_END()); } } done: + if (home) { + su_home_unref(home); + home = NULL; + } + if (exten && strchr(exten, '@')) { switch_safe_free(exten); } if (etmp) { switch_safe_free(etmp); } - - } - 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, 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_DEFAULT_DTMF_DURATION }; + + if (session) { + /* Get the channel */ + switch_channel_t *channel = switch_core_session_get_channel(session); - //placeholder for string searching - char *signal_ptr; - - //Try and find signal information in the payload - signal_ptr = strstr(sip->sip_payload->pl_data, "Signal="); - - //See if we found a match - if (signal_ptr) { - struct private_object *tech_pvt = NULL; - switch_channel_t *channel = NULL; - char dtmf_digit[2] = { 0, 0 }; - - //Get the channel - channel = switch_core_session_get_channel(session); - - //Barf if we didn't get it - assert(channel != NULL); - - //make sure we have our privates - tech_pvt = switch_core_session_get_private(session); + /* Barf if we didn't get our private */ + assert(switch_core_session_get_private(session)); - //Barf if we didn't get it - assert(tech_pvt != NULL); + 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, "dtmf-relay")) { + /* Try and find signal information in the payload */ + if ((signal_ptr = switch_stristr("Signal=", sip->sip_payload->pl_data))) { + /* move signal_ptr where we need it (right past Signal=) */ + signal_ptr = signal_ptr + 7; + dtmf.digit = *signal_ptr; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Bad signal\n"); + goto fail; + } - //move signal_ptr where we need it (right past Signal=) - signal_ptr = signal_ptr + 7; + if ((signal_ptr = switch_stristr("Duration=", sip->sip_payload->pl_data))) { + int tmp; + signal_ptr += 9; + if ((tmp = atoi(signal_ptr)) <= 0) { + tmp = SWITCH_DEFAULT_DTMF_DURATION; + } + dtmf.duration = tmp * 8; + } + } else if (!strncasecmp(sip->sip_content_type->c_type, "application", 11) && !strcasecmp(sip->sip_content_type->c_subtype, "dtmf")) { + dtmf.digit = *sip->sip_payload->pl_data; + } else { + goto fail; + } + + if (dtmf.digit) { + /* queue it up */ + switch_channel_queue_dtmf(channel, &dtmf); - //put the digit somewhere we can muck with - strncpy(dtmf_digit, signal_ptr, 1); + /* print debug info */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "INFO DTMF(%c)\n", dtmf.digit); - //queue it up - switch_channel_queue_dtmf(channel, dtmf_digit); + /* Send 200 OK response */ + nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS(nua), TAG_END()); + + return; + } else { + goto fail; + } + } - //print debug info - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "INFO DTMF(%s)\n", dtmf_digit); + if ((clientcode_header = sofia_glue_get_unknown_header(sip, "x-clientcode"))) { + if (!switch_strlen_zero(clientcode_header)) { + switch_channel_set_variable(channel, "call_clientcode", clientcode_header); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Setting CMC to %s\n", clientcode_header); + nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS(nua), TAG_END()); + } else { + goto fail; + } + return; + } - } else { //unknown info type - sip_from_t const *from; + if ((rec_header = sofia_glue_get_unknown_header(sip, "record"))) { + if (switch_strlen_zero(profile->record_template)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Record attempted but no template defined.\n"); + nua_respond(nh, 488, "Recording not enabled", NUTAG_WITH_THIS(nua), TAG_END()); + } else { + if (!strcasecmp(rec_header, "on")) { + char *file; - from = sip->sip_from; + file = switch_channel_expand_variables(channel, profile->record_template); + switch_ivr_record_session(session, file, 0, NULL); + switch_channel_set_variable(channel, "sofia_record_file", file); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Recording %s to %s\n", switch_channel_get_name(channel), file); + nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS(nua), TAG_END()); + if (file != profile->record_template) { + free(file); + file = NULL; + } + } else { + const char *file; - //print in the logs if something comes through we don't understand - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unknown INFO Recieved: %s%s" URL_PRINT_FORMAT "[%s]\n", - from->a_display ? from->a_display : "", from->a_display ? " " : "", URL_PRINT_ARGS(from->a_url), sip->sip_payload->pl_data); + if ((file = switch_channel_get_variable(channel, "sofia_record_file"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, 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(nua), TAG_END()); + } else { + nua_respond(nh, 488, "Nothing to stop", NUTAG_WITH_THIS(nua), TAG_END()); + } + } + } + return; + } } - return; + + fail: + nua_respond(nh, 488, "Unsupported Request", NUTAG_WITH_THIS(nua), TAG_END()); } -#define url_set_chanvars(session, url, varprefix) _url_set_chanvars(session, url, #varprefix "_user", #varprefix "_host", #varprefix "_port", #varprefix "_uri") + +#define url_set_chanvars(session, url, varprefix) _url_set_chanvars(session, url, #varprefix "_user", #varprefix "_host", #varprefix "_port", #varprefix "_uri", #varprefix "_params") 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 *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 (!switch_strlen_zero(url->url_params)) { + switch_channel_set_variable(channel, params_var, url->url_params); + } } + if (switch_strlen_zero(user)) { + user = "nobody"; + } + + if (switch_strlen_zero(host)) { + host = "nowhere"; + } + + check_decode(user, session); + if (user) { switch_channel_set_variable(channel, user_var, user); } - if (!port) { - port = SOFIA_DEFAULT_PORT; + + 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, port); + uri = switch_core_session_sprintf(session, "%s@%s%s", user, host, new_port); } else { - uri = switch_core_session_sprintf(session, "%s:%s", host, port); + uri = switch_core_session_sprintf(session, "%s%s", host, new_port); } - switch_channel_set_variable_nodup(channel, uri_var, uri); + switch_channel_set_variable(channel, uri_var, uri); switch_channel_set_variable(channel, host_var, host); } return uri; } -void process_rpid(sip_unknown_t *un, private_object_t *tech_pvt) -{ - int argc, x, screen = 1; - char *mydata, *argv[10] = { 0 }; - if (!switch_strlen_zero(un->un_value)) { - if ((mydata = strdup(un->un_value))) { - argc = switch_separate_string(mydata, ';', argv, (sizeof(argv) / sizeof(argv[0]))); - - // Do We really need this at this time - // clid_uri = argv[0]; - - for (x = 1; x < argc && argv[x]; x++) { - // we dont need to do anything with party yet we should only be seeing party=calling here anyway - // maybe thats a dangerous assumption bit oh well yell at me later - // if (!strncasecmp(argv[x], "party", 5)) { - // party = argv[x]; - // } else - if (!strncasecmp(argv[x], "privacy=", 8)) { - char *arg = argv[x] + 9; - - if (!strcasecmp(arg, "yes")) { - switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME | SWITCH_CPF_HIDE_NUMBER); - } else if (!strcasecmp(arg, "full")) { - switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME | SWITCH_CPF_HIDE_NUMBER); - } else if (!strcasecmp(arg, "name")) { - switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME); - } else if (!strcasecmp(arg, "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); - } - - } else if (!strncasecmp(argv[x], "screen=", 7) && screen > 0) { - char *arg = argv[x] + 8; - if (!strcasecmp(arg, "no")) { - screen = 0; - switch_clear_flag(tech_pvt->caller_profile, SWITCH_CPF_SCREEN); - } - } - } - free(mydata); - } - } -} - void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip, tagi_t tags[]) { switch_core_session_t *session = NULL; 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_alert_info_t *alert_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]; switch_event_t *v_event = NULL; uint32_t sess_count = switch_core_session_count(); uint32_t sess_max = switch_core_session_limit(0); - int is_auth = 0; + int is_auth = 0, calling_myself = 0; + su_addrinfo_t *my_addrinfo = msg_addrinfo(nua_current_request(nua)); + - if ((profile->soft_max && sess_count >= profile->soft_max) || sess_count >= sess_max) { - nua_respond(nh, 480, "Maximum Calls In Progress", SIPTAG_RETRY_AFTER_STR("300"), TAG_END()); + if (sess_count >= sess_max || !(profile->pflags & PFLAG_RUNNING)) { + nua_respond(nh, 503, "Maximum Calls In Progress", SIPTAG_RETRY_AFTER_STR("300"), TAG_END()); return; } @@ -1754,26 +2523,45 @@ return; } + + get_addr(network_ip, sizeof(network_ip), &((struct sockaddr_in *) my_addrinfo->ai_addr)->sin_addr); - if (!(sip->sip_contact && sip->sip_contact->m_url)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO CONTACT!\n"); - nua_respond(nh, 400, "Missing Contact Header", TAG_END()); - return; + if (profile->acl_count) { + uint32_t x = 0; + for (x = 0 ; x < profile->acl_count; x++) { + if (!switch_check_network_list_ip(network_ip, profile->acl[x])) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "IP %s Rejected by acl %s\n", network_ip, profile->acl[x]); + nua_respond(nh, SIP_403_FORBIDDEN, TAG_END()); + return; + } + } } - - if ((profile->pflags & PFLAG_AUTH_CALLS) || sip->sip_proxy_authorization || sip->sip_authorization) { - if (sofia_reg_handle_register(nua, profile, nh, sip, REG_INVITE, key, sizeof(key), &v_event)) { - if (v_event) { - switch_event_destroy(&v_event); + + + if ((profile->pflags & PFLAG_AUTH_CALLS) || (!(profile->pflags & PFLAG_BLIND_AUTH) && (sip->sip_proxy_authorization || sip->sip_authorization))) { + if (strcmp(network_ip, profile->sipip)) { + if (sofia_reg_handle_register(nua, profile, nh, sip, REG_INVITE, key, sizeof(key), &v_event)) { + if (v_event) { + switch_event_destroy(&v_event); + } + return; } - return; + } else { + calling_myself++; } is_auth++; } + + + if (!(sip->sip_contact && sip->sip_contact->m_url)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO CONTACT!\n"); + nua_respond(nh, 400, "Missing Contact Header", TAG_END()); + return; + } + if (!sofia_endpoint_interface || !(session = switch_core_session_request(sofia_endpoint_interface, NULL))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Session Alloc Failed!\n"); - nua_respond(nh, SIP_503_SERVICE_UNAVAILABLE, TAG_END()); + nua_respond(nh, 503, "Maximum Calls In Progress", SIPTAG_RETRY_AFTER_STR("300"), TAG_END()); return; } @@ -1789,13 +2577,15 @@ tech_pvt->key = switch_core_session_strdup(session, key); } - get_addr(network_ip, sizeof(network_ip), &((struct sockaddr_in *) msg_addrinfo(nua_current_request(nua))->ai_addr)->sin_addr); - - channel = switch_core_session_get_channel(session); + channel = tech_pvt->channel = switch_core_session_get_channel(session); + if (is_auth) { switch_channel_set_variable(channel, "sip_authorized", "true"); } + if (calling_myself) { + switch_channel_set_variable(channel, "sip_looped_call", "true"); + } if (v_event) { switch_event_header_t *hp; @@ -1803,7 +2593,6 @@ for(hp = v_event->headers; hp; hp = hp->next) { switch_channel_set_variable(channel, hp->name, hp->value); } - switch_event_destroy(&v_event); } @@ -1820,23 +2609,53 @@ } } - if (!switch_strlen_zero(sip->sip_from->a_display)) { - char *tmp; - tmp = switch_core_session_strdup(session, sip->sip_from->a_display); - if (*tmp == '"') { - char *p; + switch_channel_set_variable(channel, "sip_from_comment", sip->sip_from->a_comment); - tmp++; - if ((p = strchr(tmp, '"'))) { - *p = '\0'; - } - } - displayname = tmp; + 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_domain_name", profile->domain_name); + + if (!switch_strlen_zero(sip->sip_from->a_display)) { + displayname = sip->sip_from->a_display; } else { - displayname = switch_strlen_zero(from_user) ? "unkonwn" : from_user; + displayname = switch_strlen_zero(from_user) ? "unknown" : from_user; + } + } + + if ((rpid = sip_remote_party_id(sip))) { + if (rpid->rpid_url && rpid->rpid_url->url_user) { + from_user = rpid->rpid_url->url_user; + } + if (!switch_strlen_zero(rpid->rpid_display)) { + displayname = rpid->rpid_display; + } + } + + if ((passerted = sip_p_asserted_identity(sip))) { + if (passerted->paid_url && passerted->paid_url->url_user) { + from_user = passerted->paid_url->url_user; + } + if (!switch_strlen_zero(passerted->paid_display)) { + displayname = passerted->paid_display; + } + } + + if ((ppreferred = sip_p_preferred_identity(sip))) { + if (ppreferred->ppid_url && ppreferred->ppid_url->url_user) { + from_user = ppreferred->ppid_url->url_user; + } + if (!switch_strlen_zero(ppreferred->ppid_display)) { + displayname = ppreferred->ppid_display; } } + if (from_user) { + check_decode(from_user, session); + } + if (sip->sip_request->rq_url) { const char *req_uri = url_set_chanvars(session, sip->sip_request->rq_url, sip_req); if (profile->pflags & PFLAG_FULL_ID) { @@ -1844,20 +2663,76 @@ } else { destination_number = sip->sip_request->rq_url->url_user; } + check_decode(destination_number, session); } if (sip->sip_to && sip->sip_to->a_url) { - char *val; + const char *host, *user; + int port; + sofia_transport_t transport; + url_t *transport_url; + + if (sip->sip_record_route && sip->sip_record_route->r_url) { + 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 ((val = switch_channel_get_variable(channel, "sip_to_uri"))) { - tech_pvt->to_uri = switch_core_session_sprintf(session, "sip:%s", val); + if (switch_channel_get_variable(channel, "sip_to_uri")) { + + 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->sip_port; + } + + tech_pvt->to_uri = switch_core_session_sprintf(session, "sip:%s@%s:%d;transport=%s", + user, host, port, sofia_glue_transport2str(transport)); + if (profile->ndlb & PFLAG_NDLB_TO_IN_200_CONTACT) { - tech_pvt->reply_contact = tech_pvt->to_uri; + if (strchr(tech_pvt->to_uri, '>')) { + tech_pvt->reply_contact = tech_pvt->to_uri; + } else { + tech_pvt->reply_contact = switch_core_session_sprintf(session, "<%s>", tech_pvt->to_uri); + } } else { - tech_pvt->reply_contact = switch_core_session_strdup(session, profile->url); + const char *url; + + if ((url = (sofia_glue_transport_has_tls(transport)) ? profile->tls_url : profile->url)) { + if (strchr(url, '>')) { + tech_pvt->reply_contact = switch_core_session_sprintf(session, "%s;transport=%s", url, sofia_glue_transport2str(transport)); + } else { + tech_pvt->reply_contact = switch_core_session_sprintf(session, "<%s;transport=%s>", url, sofia_glue_transport2str(transport)); + } + } else { + switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } } } else { - tech_pvt->reply_contact = switch_core_session_strdup(session, profile->url); + const char *url; + + if ((url = (sofia_glue_transport_has_tls(transport)) ? profile->tls_url : profile->url)) { + if (strchr(url, '>')) { + tech_pvt->reply_contact = switch_core_session_sprintf(session, "%s;transport=%s", url, sofia_glue_transport2str(transport)); + } else { + tech_pvt->reply_contact = switch_core_session_sprintf(session, "<%s;transport=%s>", url, sofia_glue_transport2str(transport)); + } + } else { + switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } } } @@ -1868,14 +2743,39 @@ } } + 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 (!switch_strlen_zero(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_attach_private(session, profile, tech_pvt, channel_name); sofia_glue_tech_prepare_codecs(tech_pvt); switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "INBOUND CALL"); - if (switch_test_flag(tech_pvt, TFLAG_INB_NOMEDIA)) { - switch_channel_set_flag(channel, CF_BYPASS_MEDIA); + switch_channel_set_flag(channel, CF_PROXY_MODE); + } + + if (switch_test_flag(tech_pvt, TFLAG_PROXY_MEDIA)) { + switch_channel_set_flag(channel, CF_PROXY_MEDIA); } if (!tech_pvt->call_id && sip->sip_call_id && sip->sip_call_id->i_id) { @@ -1886,6 +2786,10 @@ 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 && !switch_strlen_zero(sip->sip_user_agent->g_string)){ + switch_channel_set_variable(channel, "sip_user_agent", sip->sip_user_agent->g_string); + } if (sip->sip_via) { if (sip->sip_via->v_host) { @@ -1901,16 +2805,16 @@ if (sip->sip_max_forwards) { char max_forwards[32]; - snprintf(max_forwards, sizeof(max_forwards), "%lu", sip->sip_max_forwards->mf_count); + 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 (sip->sip_request->rq_url) { sofia_gateway_t *gateway; - char *from_key = switch_core_session_sprintf(session, "sip:%s@%s", - (char *) sip->sip_request->rq_url->url_user, - (char *) sip->sip_request->rq_url->url_host); + char *from_key; + char *user = (char *) sip->sip_request->rq_url->url_user; + check_decode(user, session); + from_key = switch_core_session_sprintf(session, "sip:%s@%s", user, sip->sip_request->rq_url->url_host); if ((gateway = sofia_reg_find_gateway(from_key))) { context = gateway->register_context; @@ -1919,6 +2823,9 @@ } } + if (!context) { + context = switch_channel_get_variable(channel, "user_context"); + } if (!context) { if (profile->context && !strcasecmp(profile->context, "_domain_")) { @@ -1928,21 +2835,70 @@ } } + 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(profile->home, (void *) alert_info); + switch_channel_set_variable(channel, "alert_info", tmp); + su_free(profile->home, tmp); + } + + if (sofia_test_pflag(profile, PFLAG_PRESENCE)) { + 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); + } + + check_decode(displayname, session); tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), from_user, - profile->dialplan, - displayname, from_user, network_ip, NULL, NULL, NULL, MODNAME, context, destination_number); + dialplan, + displayname, + from_user, + network_ip, + NULL, NULL, NULL, MODNAME, context, destination_number); if (tech_pvt->caller_profile) { + 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); + } + } + /* 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, "Alert-Info", 10)) { + if (!strncasecmp(un->un_name, "Diversion", 9)) { + /* Basic Diversion Support for Diversion Indication in SIP */ + /* draft-levy-sip-diversion-08 */ if (!switch_strlen_zero(un->un_value)) { - switch_channel_set_variable(channel, "alert_info", 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, "Remote-Party-ID", 15)) { - process_rpid(un, tech_pvt); } else if (!strncasecmp(un->un_name, "X-", 2) || !strncasecmp(un->un_name, "P-", 2)) { if (!switch_strlen_zero(un->un_value)) { char *new_name; @@ -1957,33 +2913,95 @@ switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); } - if (!(tech_pvt->sofia_private = malloc(sizeof(*tech_pvt->sofia_private)))) { + if (!(sofia_private = malloc(sizeof(*sofia_private)))) { abort(); } - memset(tech_pvt->sofia_private, 0, sizeof(*tech_pvt->sofia_private)); - tech_pvt->sofia_private->home = su_home_new(sizeof(*tech_pvt->sofia_private->home)); - sofia_presence_set_chat_hash(tech_pvt, sip); + memset(sofia_private, 0, sizeof(*sofia_private)); + tech_pvt->sofia_private = sofia_private; + + if ((profile->pflags & PFLAG_PRESENCE)) { + sofia_presence_set_chat_hash(tech_pvt, sip); + } switch_copy_string(tech_pvt->sofia_private->uuid, switch_core_session_get_uuid(session), sizeof(tech_pvt->sofia_private->uuid)); nua_handle_bind(nh, tech_pvt->sofia_private); tech_pvt->nh = nh; - if (switch_core_session_thread_launch(session) == SWITCH_STATUS_SUCCESS) { + if (sip && switch_core_session_thread_launch(session) == SWITCH_STATUS_SUCCESS) { + 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; + char *sql = 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 (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); + } + + if (from) { + dialog_from_user = switch_str_nil(from->url_user); + dialog_from_host = switch_str_nil(from->url_host); + } + + if (contact) { + contact_user = switch_str_nil(contact->url_user); + contact_host = switch_str_nil(contact->url_host); + } + + + if (profile->pflags & PFLAG_PRESENCE) { + + sql = switch_mprintf( + "insert into sip_dialogs values('%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q')", + call_id, + tech_pvt->sofia_private->uuid, + to_user, + to_host, + dialog_from_user, + dialog_from_host, + contact_user, + contact_host, + "confirmed", + "inbound", + user_agent + ); + + switch_assert(sql); + sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); + } + return; } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "LUKE: I'm hit, but not bad.\n"); - - switch_mutex_lock(profile->flag_mutex); - - profile->soft_max = sess_count - 10; - switch_core_session_limit(profile->soft_max); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "LUKE'S VOICE: Artoo, see what you can do with it. Hang on back there....\n" - "Green laserfire moves past the beeping little robot as his head turns. " - "After a few beeps and a twist of his mechanical arm,\n" - "Artoo reduces the max sessions to %d thus, saving the switch from certian doom.\n", - profile->soft_max); + if (sess_count > 110) { + switch_mutex_lock(profile->flag_mutex); + switch_core_session_limit(sess_count - 10); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "LUKE: I'm hit, but not bad.\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "LUKE'S VOICE: Artoo, see what you can do with it. Hang on back there....\n" + "Green laserfire moves past the beeping little robot as his head turns. " + "After a few beeps and a twist of his mechanical arm,\n" + "Artoo reduces the max sessions to %d thus, saving the switch from certian doom.\n", + sess_count - 10); - switch_mutex_unlock(profile->flag_mutex); + switch_mutex_unlock(profile->flag_mutex); + } if (tech_pvt->hash_key) { switch_core_hash_delete(tech_pvt->profile->chat_hash, tech_pvt->hash_key); @@ -1992,7 +3010,7 @@ nua_handle_bind(nh, NULL); free(tech_pvt->sofia_private); switch_core_session_destroy(&session); - nua_respond(nh, 480, "Maximum Calls In Progress", SIPTAG_RETRY_AFTER_STR("300"), TAG_END()); + nua_respond(nh, 503, "Maximum Calls In Progress", SIPTAG_RETRY_AFTER_STR("300"), TAG_END()); } void sofia_handle_sip_i_options(int status, @@ -2000,13 +3018,118 @@ nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip, tagi_t tags[]) { nua_respond(nh, SIP_200_OK, - //SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), - //SOATAG_AUDIO_AUX("cn telephone-event"), - //NUTAG_INCLUDE_EXTRA_SDP(1), + NUTAG_WITH_THIS(nua), TAG_END()); + nua_handle_destroy(nh); } +static void sofia_info_send_sipfrag(switch_core_session_t *aleg, switch_core_session_t *bleg) +{ + private_object_t *b_tech_pvt = NULL, *a_tech_pvt = NULL; + char message[256] = ""; + if (aleg && bleg) { + a_tech_pvt = (private_object_t *) switch_core_session_get_private(aleg); + b_tech_pvt = (private_object_t *) switch_core_session_get_private(bleg); + + if (b_tech_pvt && a_tech_pvt && a_tech_pvt->caller_profile) { + switch_caller_profile_t *acp = a_tech_pvt->caller_profile; + if (switch_strlen_zero(acp->caller_id_name)) { + snprintf(message, sizeof(message), "From:\r\nTo: %s\r\n", acp->caller_id_number); + } else { + snprintf(message, sizeof(message), "From:\r\nTo: \"%s\" %s\r\n", acp->caller_id_name, acp->caller_id_number); + } + nua_info(b_tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), SIPTAG_PAYLOAD_STR(message), 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 parms 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 parms data or the */ + /* equal (=) sign spearating 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 expandtab: + */