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

📄 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));

⌨️ 快捷键说明

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