📄 nua_session.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2006 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@CFILE nua_session.c * @brief SIP session handling * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * * @date Created: Wed Mar 8 16:17:27 EET 2006 ppessi */#include "config.h"#include <stddef.h>#include <stdlib.h>#include <string.h>#include <limits.h>#include <assert.h>#include <sofia-sip/string0.h>#include <sofia-sip/sip_protos.h>#include <sofia-sip/sip_status.h>#include <sofia-sip/sip_util.h>#define NTA_LEG_MAGIC_T struct nua_handle_s#define NTA_OUTGOING_MAGIC_T struct nua_handle_s#define NTA_INCOMING_MAGIC_T struct nua_handle_s#define NTA_RELIABLE_MAGIC_T struct nua_handle_s#include "nua_stack.h"#include <sofia-sip/soa.h>#if !defined(random) && defined(_WIN32)#define random rand#endif#ifndef SDP_Htypedef struct sdp_session_s sdp_session_t;#endif/* ---------------------------------------------------------------------- *//* Session event usage */struct session_usage;static char const *nua_session_usage_name(nua_dialog_usage_t const *du);static int nua_session_usage_add(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du);static void nua_session_usage_remove(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du);static nua_usage_class const nua_session_usage[1] = { { 0 /* sizeof (struct session_usage) */, sizeof nua_session_usage, nua_session_usage_add, nua_session_usage_remove, nua_session_usage_name, }};static char const *nua_session_usage_name(nua_dialog_usage_t const *du){ return "session";}staticint nua_session_usage_add(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du){ if (ds->ds_has_session) return -1; ds->ds_has_session = 1; return 0;}staticvoid nua_session_usage_remove(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du){ ds->ds_has_session = 0;}/* ======================================================================== *//* INVITE and call (session) processing */static int ua_invite2(nua_t *, nua_handle_t *, nua_event_t e, int restarted, tagi_t const *tags);static int process_response_to_invite(nua_handle_t *nh, nta_outgoing_t *orq, sip_t const *sip);static int process_100rel(nua_handle_t *nh, nta_outgoing_t *orq, sip_t const *sip);static void cancel_invite(nua_handle_t *nh, nua_dialog_usage_t *du, sip_time_t now), refresh_invite(nua_handle_t *nh, nua_dialog_usage_t *du, sip_time_t now), session_timeout(nua_handle_t *nh, nua_dialog_usage_t *du, sip_time_t now);static void restart_invite(nua_handle_t *nh, tagi_t *tags);static int process_response_to_prack(nua_handle_t *nh, nta_outgoing_t *orq, sip_t const *sip);static void nsession_destroy(nua_handle_t *nh);static int use_session_timer(nua_handle_t *nh, int uas, msg_t *msg, sip_t *);static int init_session_timer(nua_handle_t *nh, sip_t const *);static void set_session_timer(nua_handle_t *nh);static int nh_referral_check(nua_handle_t *nh, tagi_t const *tags);static void nh_referral_respond(nua_handle_t *, int status, char const *phrase);static void signal_call_state_change(nua_handle_t *nh, int status, char const *phrase, enum nua_callstate next_state, char const *oa_recv, char const *oa_sent);staticint session_get_description(msg_t *msg, sip_t const *sip, char const **return_sdp, size_t *return_len);staticint session_include_description(soa_session_t *soa, msg_t *msg, sip_t *sip);staticint session_make_description(su_home_t *home, soa_session_t *soa, sip_content_disposition_t **return_cd, sip_content_type_t **return_ct, sip_payload_t **return_pl);staticint session_process_response(nua_handle_t *nh, struct nua_client_request *cr, nta_outgoing_t *orq, sip_t const *sip, char const **return_received);intnua_stack_invite(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags){ nua_session_state_t *ss = nh->nh_ss; struct nua_client_request *cr = ss->ss_crequest; char const *what; if (nh_is_special(nh)) what = "Invalid handle for INVITE"; else if (cr->cr_orq) { what = "INVITE request already in progress"; } else if (nh_referral_check(nh, tags) < 0) { what = "Invalid referral"; } else if (nua_stack_init_handle(nua, nh, nh_has_invite, NULL, TAG_NEXT(tags)) < 0) { what = "Handle initialization failed"; } else return ua_invite2(nua, nh, e, 0, tags); UA_EVENT2(e, 900, what); signal_call_state_change(nh, 900, what, nua_callstate_init, 0, 0); return e;}static intua_invite2(nua_t *nua, nua_handle_t *nh, nua_event_t e, int restarted, tagi_t const *tags){ nua_session_state_t *ss = nh->nh_ss; struct nua_client_request *cr = ss->ss_crequest; nua_dialog_usage_t *du; int offer_sent = 0; msg_t *msg = NULL; sip_t *sip; char const *what; if (ss->ss_state == nua_callstate_terminated) ss->ss_state = nua_callstate_init; du = nua_dialog_usage_add(nh, nh->nh_ds, nua_session_usage, NULL); what = nua_internal_error; /* Internal error */ msg = du ? nua_creq_msg(nua, nh, cr, restarted, SIP_METHOD_INVITE, NUTAG_USE_DIALOG(1), NUTAG_ADD_CONTACT(1), TAG_NEXT(tags)) : NULL; sip = sip_object(msg); if (du && sip && nh->nh_soa) { soa_init_offer_answer(nh->nh_soa); if (sip->sip_payload) offer_sent = 0; else if (soa_generate_offer(nh->nh_soa, 0, NULL) < 0) offer_sent = -1; else offer_sent = 1; } assert(cr->cr_orq == NULL); if (du && sip && offer_sent >= 0) { sip_time_t invite_timeout = NH_PGET(nh, invite_timeout); if (invite_timeout == 0) invite_timeout = UINT_MAX; /* Cancel if we don't get response */ nua_dialog_usage_set_refresh(du, invite_timeout); /* Add session timer headers */ use_session_timer(nh, 0, msg, sip); ss->ss_100rel = NH_PGET(nh, early_media); ss->ss_precondition = sip_has_feature(sip->sip_require, "precondition"); if (ss->ss_precondition) ss->ss_update_needed = ss->ss_100rel = 1; if (offer_sent > 0 && session_include_description(nh->nh_soa, msg, sip) < 0) sip = NULL, what = "Internal media error"; if (sip && nh->nh_soa && NH_PGET(nh, media_features) && !nua_dialog_is_established(nh->nh_ds) && !sip->sip_accept_contact && !sip->sip_reject_contact) { sip_accept_contact_t ac[1]; sip_accept_contact_init(ac); ac->cp_params = (msg_param_t *) soa_media_features(nh->nh_soa, 1, msg_home(msg)); if (ac->cp_params) { msg_header_replace_param(msg_home(msg), ac->cp_common, "explicit"); sip_add_dup(msg, sip, (sip_header_t *)ac); } } if (sip && nh->nh_auth) { if (auc_authorize(&nh->nh_auth, msg, sip) < 0) sip = NULL, what = "Internal authentication error"; } if (sip) cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta, process_response_to_invite, nh, NULL, msg, NTATAG_REL100(ss->ss_100rel), SIPTAG_END(), TAG_NEXT(tags)); if (cr->cr_orq) { cr->cr_offer_sent = offer_sent; cr->cr_usage = du; du->du_pending = cancel_invite; du->du_refresh = 0; signal_call_state_change(nh, 0, "INVITE sent", nua_callstate_calling, 0, offer_sent ? "offer" : 0); return cr->cr_event = e; } } msg_destroy(msg); if (du && !du->du_ready) nua_dialog_usage_remove(nh, nh->nh_ds, du); UA_EVENT2(e, 900, what); signal_call_state_change(nh, 900, what, nua_callstate_init, 0, 0); return e;}static int process_response_to_invite(nua_handle_t *nh, nta_outgoing_t *orq, sip_t const *sip){ nua_t *nua = nh->nh_nua; nua_session_state_t *ss = nh->nh_ss; nua_client_request_t *cr = ss->ss_crequest; nua_dialog_usage_t *du = cr->cr_usage; int status = sip->sip_status->st_status; char const *phrase = sip->sip_status->st_phrase; int terminated = 0; int gracefully = 1; char const *received = NULL; assert(du);#if HAVE_SOFIA_SMIME if (status < 300) { int sm_status; msg_t *response; /* decrypt sdp payload if it's S/MIME */ /* XXX msg had a problem!!?? */ response = nta_outgoing_getresponse(orq); sm_status = sm_decode_message(nua->sm, response, sip); switch (sm_status) { case SM_SMIME_DISABLED: case SM_ERROR: status = 493, phrase = "Undecipherable"; break; case SM_SUCCESS: break; default: break; } }#endif if (status >= 300) { if (sip->sip_retry_after) gracefully = 0; terminated = sip_response_terminates_dialog(status, sip_method_invite, &gracefully); if (!terminated) { if (nua_creq_check_restart(nh, cr, orq, sip, restart_invite)) return 0; if (nh->nh_ss->ss_state < nua_callstate_ready) terminated = 1; } } else if (status >= 200) { du->du_ready = 1; if (!ss->ss_usage) ss->ss_usage = du; cr->cr_usage = NULL; /* XXX - check remote tag, handle forks */ /* Set route, contact, nh_ds->ds_remote_tag */ nua_dialog_uac_route(nh, nh->nh_ds, sip, 1); nua_dialog_store_peer_info(nh, nh->nh_ds, sip); init_session_timer(nh, sip); set_session_timer(nh); /* signal_call_state_change */ if (session_process_response(nh, cr, orq, sip, &received) >= 0) { ss->ss_ack_needed = received ? received : ""; if (NH_PGET(nh, auto_ack) || /* Auto-ACK response to re-INVITE unless auto_ack is set to 0 */ (ss->ss_state == nua_callstate_ready && !NH_PISSET(nh, auto_ack))) nua_stack_ack(nua, nh, nua_r_ack, NULL); else signal_call_state_change(nh, status, phrase, nua_callstate_completing, received, 0); nh_referral_respond(nh, SIP_200_OK); return 0; } status = 900, phrase = "Malformed Session in Response"; nua_stack_ack(nua, nh, nua_r_ack, NULL); gracefully = 1; } else if (sip->sip_rseq) { /* Reliable provisional response */ nh_referral_respond(nh, status, phrase); return process_100rel(nh, orq, sip); /* signal_call_state_change */ } else { /* Provisional response */ nh_referral_respond(nh, status, phrase); session_process_response(nh, cr, orq, sip, &received); signal_call_state_change(nh, status, phrase, nua_callstate_proceeding, received, 0); return 0; } cr->cr_usage = NULL; nh_referral_respond(nh, status, phrase); nua_stack_process_response(nh, cr, orq, sip, TAG_END()); if (terminated) signal_call_state_change(nh, status, phrase, nua_callstate_terminated, 0, 0); if (terminated < 0) { nua_dialog_terminated(nh, nh->nh_ds, status, phrase); } else if (terminated > 0) { nua_dialog_usage_remove(nh, nh->nh_ds, du); } else if (gracefully) { char *reason = su_sprintf(NULL, "SIP;cause=%u;text=\"%s\"", status, phrase); signal_call_state_change(nh, status, phrase, nua_callstate_terminating, 0, 0); nua_stack_post_signal(nh, nua_r_bye, SIPTAG_REASON_STR(reason), TAG_END()); su_free(NULL, reason); } return 0;}int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags){ nua_session_state_t *ss = nh->nh_ss; struct nua_client_request *cr = ss->ss_crequest; nta_outgoing_t *ack = NULL; msg_t *msg; sip_t *sip; int status = 200; char const *phrase = "OK", *reason = NULL, *sent = NULL; char const *received = ss->ss_ack_needed; if (!ss->ss_ack_needed) return UA_EVENT2(nua_i_error, 900, "No response to ACK"); ss->ss_ack_needed = 0; if (!received[0]) received = NULL; if (tags) { nua_stack_set_params(nua, nh, nua_r_ack, tags); } msg = nua_creq_msg(nua, nh, cr, 0, SIP_METHOD_ACK, /* NUTAG_COPY(0), */ TAG_NEXT(tags)); sip = sip_object(msg); if (sip && nh->nh_soa) { if (tags) soa_set_params(nh->nh_soa, TAG_NEXT(tags)); if (cr->cr_offer_recv && !cr->cr_answer_sent) { if (soa_generate_answer(nh->nh_soa, NULL) < 0 ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -