⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sip_inv.c

📁 基于sip协议的网络电话源码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* $Id: sip_inv.c 981 2007-02-19 22:19:23Z bennylp $ *//*  * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  */#include <pjsip-ua/sip_inv.h>#include <pjsip/sip_module.h>#include <pjsip/sip_endpoint.h>#include <pjsip/sip_event.h>#include <pjsip/sip_transaction.h>#include <pjmedia/sdp.h>#include <pjmedia/sdp_neg.h>#include <pjmedia/errno.h>#include <pj/string.h>#include <pj/pool.h>#include <pj/assert.h>#include <pj/os.h>#include <pj/log.h>#define THIS_FILE	"sip_invite_session.c"static const char *inv_state_names[] ={    "NULL",    "CALLING",    "INCOMING",    "EARLY",    "CONNECTING",    "CONFIRMED",    "DISCONNCTD",    "TERMINATED",};/* * Static prototypes. */static pj_status_t mod_inv_load(pjsip_endpoint *endpt);static pj_status_t mod_inv_unload(void);static pj_bool_t   mod_inv_on_rx_request(pjsip_rx_data *rdata);static pj_bool_t   mod_inv_on_rx_response(pjsip_rx_data *rdata);static void	   mod_inv_on_tsx_state(pjsip_transaction*, pjsip_event*);static void inv_on_state_null( pjsip_inv_session *inv, pjsip_event *e);static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e);static void inv_on_state_incoming( pjsip_inv_session *inv, pjsip_event *e);static void inv_on_state_early( pjsip_inv_session *inv, pjsip_event *e);static void inv_on_state_connecting( pjsip_inv_session *inv, pjsip_event *e);static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e);static void inv_on_state_disconnected( pjsip_inv_session *inv, pjsip_event *e);static void (*inv_state_handler[])( pjsip_inv_session *inv, pjsip_event *e) = {    &inv_on_state_null,    &inv_on_state_calling,    &inv_on_state_incoming,    &inv_on_state_early,    &inv_on_state_connecting,    &inv_on_state_confirmed,    &inv_on_state_disconnected,};static struct mod_inv{    pjsip_module	 mod;    pjsip_endpoint	*endpt;    pjsip_inv_callback	 cb;} mod_inv = {    {	NULL, NULL,			    /* prev, next.		*/	{ "mod-invite", 10 },		    /* Name.			*/	-1,				    /* Id			*/	PJSIP_MOD_PRIORITY_DIALOG_USAGE,    /* Priority			*/	&mod_inv_load,			    /* load()			*/	NULL,				    /* start()			*/	NULL,				    /* stop()			*/	&mod_inv_unload,		    /* unload()			*/	&mod_inv_on_rx_request,		    /* on_rx_request()		*/	&mod_inv_on_rx_response,	    /* on_rx_response()		*/	NULL,				    /* on_tx_request.		*/	NULL,				    /* on_tx_response()		*/	&mod_inv_on_tsx_state,		    /* on_tsx_state()		*/    }};/* Invite session data to be attached to transaction. */struct tsx_inv_data{    pjsip_inv_session	*inv;    pj_bool_t		 sdp_done;};/* * Module load() */static pj_status_t mod_inv_load(pjsip_endpoint *endpt){    pj_str_t allowed[] = {{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}};    pj_str_t accepted = { "application/sdp", 15 };    /* Register supported methods: INVITE, ACK, BYE, CANCEL */    pjsip_endpt_add_capability(endpt, &mod_inv.mod, PJSIP_H_ALLOW, NULL,			       PJ_ARRAY_SIZE(allowed), allowed);    /* Register "application/sdp" in Accept header */    pjsip_endpt_add_capability(endpt, &mod_inv.mod, PJSIP_H_ACCEPT, NULL,			       1, &accepted);    return PJ_SUCCESS;}/* * Module unload() */static pj_status_t mod_inv_unload(void){    /* Should remove capability here */    return PJ_SUCCESS;}/* * Set session state. */void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state,		   pjsip_event *e){    pjsip_inv_state prev_state = inv->state;    /* Set state. */    inv->state = state;    /* If state is DISCONNECTED, cause code MUST have been set. */    pj_assert(inv->state != PJSIP_INV_STATE_DISCONNECTED ||	      inv->cause != 0);    /* Call on_state_changed() callback. */    if (mod_inv.cb.on_state_changed && inv->notify)	(*mod_inv.cb.on_state_changed)(inv, e);    /* Only decrement when previous state is not already DISCONNECTED */    if (inv->state == PJSIP_INV_STATE_DISCONNECTED &&	prev_state != PJSIP_INV_STATE_DISCONNECTED)     {	pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod);    }}/* * Set cause code. */void inv_set_cause(pjsip_inv_session *inv, int cause_code,		   const pj_str_t *cause_text){    if (cause_code > inv->cause) {	inv->cause = cause_code;	if (cause_text)	    pj_strdup(inv->pool, &inv->cause_text, cause_text);	else if (cause_code/100 == 2)	    inv->cause_text = pj_str("Normal call clearing");	else	    inv->cause_text = *pjsip_get_status_text(cause_code);    }}/* * Send ACK for 2xx response. */static pj_status_t inv_send_ack(pjsip_inv_session *inv, pjsip_rx_data *rdata){    pjsip_tx_data *tdata;    pj_status_t status;    PJ_LOG(5,(inv->obj_name, "Received %s, sending ACK",	      pjsip_rx_data_get_info(rdata)));    status = pjsip_dlg_create_request(inv->dlg, &pjsip_ack_method, 				      rdata->msg_info.cseq->cseq, &tdata);    if (status != PJ_SUCCESS) {	/* Better luck next time */	pj_assert(!"Unable to create ACK!");	return status;    }    status = pjsip_dlg_send_request(inv->dlg, tdata, -1, NULL);    if (status != PJ_SUCCESS) {	/* Better luck next time */	pj_assert(!"Unable to send ACK!");	return status;    }    return PJ_SUCCESS;}/* * Module on_rx_request() * * This callback is called for these events: *  - endpoint receives request which was unhandled by higher priority *    modules (e.g. transaction layer, dialog layer). *  - dialog distributes incoming request to its usages. */static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata){    pjsip_method *method;    pjsip_dialog *dlg;    pjsip_inv_session *inv;    /* Only wants to receive request from a dialog. */    dlg = pjsip_rdata_get_dlg(rdata);    if (dlg == NULL)	return PJ_FALSE;    inv = dlg->mod_data[mod_inv.mod.id];    /* Report to dialog that we handle INVITE, CANCEL, BYE, ACK.      * If we need to send response, it will be sent in the state     * handlers.     */    method = &rdata->msg_info.msg->line.req.method;    if (method->id == PJSIP_INVITE_METHOD) {	return PJ_TRUE;    }    /* BYE and CANCEL must have existing invite session */    if (method->id == PJSIP_BYE_METHOD ||	method->id == PJSIP_CANCEL_METHOD)    {	if (inv == NULL)	    return PJ_FALSE;	return PJ_TRUE;    }    /* On receipt ACK request, when state is CONNECTING,     * move state to CONFIRMED.     */    if (method->id == PJSIP_ACK_METHOD && inv) {	/* Ignore ACK if pending INVITE transaction has not finished. */	if (inv->invite_tsx && 	    inv->invite_tsx->state < PJSIP_TSX_STATE_COMPLETED)	{	    return PJ_TRUE;	}	/* Terminate INVITE transaction, if it's still present. */	if (inv->invite_tsx && 	    inv->invite_tsx->state <= PJSIP_TSX_STATE_COMPLETED)	{	    pj_assert(inv->invite_tsx->status_code >= 200);	    pjsip_tsx_terminate(inv->invite_tsx, 				inv->invite_tsx->status_code);	    inv->invite_tsx = NULL;	}	/* On receipt of ACK, only set state to confirmed when state	 * is CONNECTING (e.g. we don't want to set the state to confirmed	 * when we receive ACK retransmission after sending non-2xx!)	 */	if (inv->state == PJSIP_INV_STATE_CONNECTING) {	    pjsip_event event;	    PJSIP_EVENT_INIT_RX_MSG(event, rdata);	    inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, &event);	}    }    return PJ_FALSE;}/* * Module on_rx_response(). * * This callback is called for these events: *  - dialog distributes incoming 2xx response to INVITE (outside *    transaction) to its usages. *  - endpoint distributes strayed responses. */static pj_bool_t mod_inv_on_rx_response(pjsip_rx_data *rdata){    pjsip_dialog *dlg;    pjsip_inv_session *inv;    pjsip_msg *msg = rdata->msg_info.msg;    dlg = pjsip_rdata_get_dlg(rdata);    /* Ignore responses outside dialog */    if (dlg == NULL)	return PJ_FALSE;    /* Ignore responses not belonging to invite session */    inv = pjsip_dlg_get_inv_session(dlg);    if (inv == NULL)	return PJ_FALSE;    /* This MAY be retransmission of 2xx response to INVITE.      * If it is, we need to send ACK.     */    if (msg->type == PJSIP_RESPONSE_MSG && msg->line.status.code/100==2 &&	rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&	inv->invite_tsx == NULL)     {	inv_send_ack(inv, rdata);	return PJ_TRUE;    }    /* No other processing needs to be done here. */    return PJ_FALSE;}/* * Module on_tsx_state() * * This callback is called by dialog framework for all transactions * inside the dialog for all its dialog usages. */static void mod_inv_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e){    pjsip_dialog *dlg;    pjsip_inv_session *inv;    dlg = pjsip_tsx_get_dlg(tsx);    if (dlg == NULL)	return;    inv = pjsip_dlg_get_inv_session(dlg);    if (inv == NULL)	return;    /* Call state handler for the invite session. */    (*inv_state_handler[inv->state])(inv, e);    /* Call on_tsx_state */    if (mod_inv.cb.on_tsx_state_changed && inv->notify)	(*mod_inv.cb.on_tsx_state_changed)(inv, tsx, e);    /* Clear invite transaction when tsx is confirmed.      * Previously we set invite_tsx to NULL only when transaction has     * terminated, but this didn't work when ACK has the same Via branch     * value as the INVITE (see http://www.pjsip.org/trac/ticket/113)     */    if (tsx->state>=PJSIP_TSX_STATE_CONFIRMED && tsx == inv->invite_tsx)	inv->invite_tsx = NULL;}/* * Initialize the invite module. */PJ_DEF(pj_status_t) pjsip_inv_usage_init( pjsip_endpoint *endpt,					  const pjsip_inv_callback *cb){    pj_status_t status;    /* Check arguments. */    PJ_ASSERT_RETURN(endpt && cb, PJ_EINVAL);    /* Some callbacks are mandatory */    PJ_ASSERT_RETURN(cb->on_state_changed && cb->on_new_session, PJ_EINVAL);    /* Check if module already registered. */    PJ_ASSERT_RETURN(mod_inv.mod.id == -1, PJ_EINVALIDOP);    /* Copy param. */    pj_memcpy(&mod_inv.cb, cb, sizeof(pjsip_inv_callback));    mod_inv.endpt = endpt;    /* Register the module. */    status = pjsip_endpt_register_module(endpt, &mod_inv.mod);    if (status != PJ_SUCCESS)	return status;    return PJ_SUCCESS;}/* * Get the instance of invite module. */PJ_DEF(pjsip_module*) pjsip_inv_usage_instance(void){    return &mod_inv.mod;}/* * Return the invite session for the specified dialog. */PJ_DEF(pjsip_inv_session*) pjsip_dlg_get_inv_session(pjsip_dialog *dlg){    return dlg->mod_data[mod_inv.mod.id];}/* * Get INVITE state name. */PJ_DEF(const char *) pjsip_inv_state_name(pjsip_inv_state state){    PJ_ASSERT_RETURN(state >= PJSIP_INV_STATE_NULL && 		     state <= PJSIP_INV_STATE_DISCONNECTED,		     "??");    return inv_state_names[state];}/* * Create UAC invite session. */PJ_DEF(pj_status_t) pjsip_inv_create_uac( pjsip_dialog *dlg,					  const pjmedia_sdp_session *local_sdp,					  unsigned options,					  pjsip_inv_session **p_inv){    pjsip_inv_session *inv;    pj_status_t status;    /* Verify arguments. */    PJ_ASSERT_RETURN(dlg && p_inv, PJ_EINVAL);    /* Must lock dialog first */    pjsip_dlg_inc_lock(dlg);    /* Normalize options */    if (options & PJSIP_INV_REQUIRE_100REL)	options |= PJSIP_INV_SUPPORT_100REL;    if (options & PJSIP_INV_REQUIRE_TIMER)	options |= PJSIP_INV_SUPPORT_TIMER;    /* Create the session */    inv = pj_pool_zalloc(dlg->pool, sizeof(pjsip_inv_session));    pj_assert(inv != NULL);    inv->pool = dlg->pool;    inv->role = PJSIP_ROLE_UAC;    inv->state = PJSIP_INV_STATE_NULL;    inv->dlg = dlg;    inv->options = options;    inv->notify = PJ_TRUE;    inv->cause = 0;    /* Object name will use the same dialog pointer. */    pj_ansi_snprintf(inv->obj_name, PJ_MAX_OBJ_NAME, "inv%p", dlg);    /* Create negotiator if local_sdp is specified. */    if (local_sdp) {	status = pjmedia_sdp_neg_create_w_local_offer(dlg->pool, local_sdp,						      &inv->neg);	if (status != PJ_SUCCESS) {	    pjsip_dlg_dec_lock(dlg);	    return status;	}    }    /* Register invite as dialog usage. */    status = pjsip_dlg_add_usage(dlg, &mod_inv.mod, inv);    if (status != PJ_SUCCESS) {	pjsip_dlg_dec_lock(dlg);	return status;    }    /* Increment dialog session */    pjsip_dlg_inc_session(dlg, &mod_inv.mod);    /* Done */    *p_inv = inv;    pjsip_dlg_dec_lock(dlg);    PJ_LOG(5,(inv->obj_name, "UAC invite session created for dialog %s",	      dlg->obj_name));    return PJ_SUCCESS;}/* * Verify incoming INVITE request. */PJ_DEF(pj_status_t) pjsip_inv_verify_request(pjsip_rx_data *rdata,					     unsigned *options,					     const pjmedia_sdp_session *l_sdp,					     pjsip_dialog *dlg,					     pjsip_endpoint *endpt,					     pjsip_tx_data **p_tdata){    pjsip_msg *msg;    pjsip_allow_hdr *allow;    pjsip_supported_hdr *sup_hdr;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -