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

📄 evsub.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 4 页
字号:
    PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);

    /* Subscription MUST have been attached to the transaction.
     * Initial subscription request will be attached on evsub_create_uas(),
     * while subsequent requests will be attached in tsx_state()
     */
    tsx = pjsip_rdata_get_tsx(rdata);
    PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
		     PJ_EINVALIDOP);

    /* Lock dialog */
    pjsip_dlg_inc_lock(sub->dlg);

    /* Create response: */
    status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL, 
					&tdata);
    if (status != PJ_SUCCESS)
	goto on_return;


    /* Add expires header: */
    pjsip_msg_add_hdr( tdata->msg,
		       pjsip_hdr_shallow_clone(tdata->pool, sub->expires));

    /* Add additional header, if any. */
    if (hdr_list) {
	const pjsip_hdr *hdr = hdr_list->next;
	while (hdr != hdr_list) {
	    pjsip_msg_add_hdr( tdata->msg,
			       pjsip_hdr_clone(tdata->pool, hdr));
	    hdr = hdr->next;
	}
    }

    /* Send the response: */
    status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
    if (status != PJ_SUCCESS)
	goto on_return;


on_return:

    pjsip_dlg_dec_lock(sub->dlg);
    return status;
}


/*
 * Create Subscription-State header based on current server subscription
 * state.
 */
static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
					      pjsip_evsub *sub,
					      pjsip_evsub_state state,
					      const pj_str_t *state_str,
					      const pj_str_t *reason )
{
    pjsip_sub_state_hdr *sub_state;
    pj_time_val now, delay;

    /* Get the remaining time before refresh is required */
    pj_gettimeofday(&now);
    delay = sub->refresh_time;
    PJ_TIME_VAL_SUB(delay, now);

    /* Create the Subscription-State header */
    sub_state = pjsip_sub_state_hdr_create(pool);

    /* Fill up the header */
    switch (state) {
    case PJSIP_EVSUB_STATE_NULL:
    case PJSIP_EVSUB_STATE_SENT:
    case PJSIP_EVSUB_STATE_ACCEPTED:
	pj_assert(!"Invalid state!");
	/* Treat as pending */

    case PJSIP_EVSUB_STATE_PENDING:
	sub_state->sub_state = STR_PENDING;
	sub_state->expires_param = delay.sec;
	break;

    case PJSIP_EVSUB_STATE_ACTIVE:
	sub_state->sub_state = STR_ACTIVE;
	sub_state->expires_param = delay.sec;
	break;

    case PJSIP_EVSUB_STATE_TERMINATED:
	sub_state->sub_state = STR_TERMINATED;
	if (reason != NULL)
	    pj_strdup(pool, &sub_state->reason_param, reason);
	break;

    case PJSIP_EVSUB_STATE_UNKNOWN:
	pj_assert(state_str != NULL);
	pj_strdup(pool, &sub_state->sub_state, state_str);
	break;
    }
    
    return sub_state;
}

/*
 * Create and send NOTIFY request.
 */
PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
					pjsip_evsub_state state,
					const pj_str_t *state_str,
					const pj_str_t *reason,
					pjsip_tx_data **p_tdata)
{
    pjsip_tx_data *tdata;
    pjsip_sub_state_hdr *sub_state;
    pj_status_t status;

    /* Check arguments. */
    PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);

    /* Lock dialog. */
    pjsip_dlg_inc_lock(sub->dlg);

    /* Create NOTIFY request */
    status = pjsip_dlg_create_request( sub->dlg, &pjsip_notify_method, -1, 
				       &tdata);
    if (status != PJ_SUCCESS)
	goto on_return;

    /* Add Event header */
    pjsip_msg_add_hdr(tdata->msg,
		      pjsip_hdr_shallow_clone(tdata->pool, sub->event));

    /* Add Subscription-State header */
    sub_state = sub_state_create(tdata->pool, sub, state, state_str,
				 reason);
    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);

    /* Add Allow-Events header */
    pjsip_msg_add_hdr(tdata->msg,
		      pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));

    /* Add Authentication headers. */
    pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );

    

    /* Save destination state. */
    sub->dst_state = state;
    if (state_str)
	pj_strdup(sub->pool, &sub->dst_state_str, state_str);
    else
	sub->dst_state_str.slen = 0;


    *p_tdata = tdata;

on_return:
    /* Unlock dialog */
    pjsip_dlg_dec_lock(sub->dlg);
    return status;
}


/*
 * Create NOTIFY to reflect current status.
 */
PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
						pjsip_tx_data **p_tdata )
{
    return pjsip_evsub_notify( sub, sub->state, &sub->state_str, 
			       NULL, p_tdata );
}


/*
 * Send request.
 */
PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
					      pjsip_tx_data *tdata)
{
    pj_status_t status;

    /* Must be request message. */
    PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
		     PJSIP_ENOTREQUESTMSG);

    /* Lock */
    pjsip_dlg_inc_lock(sub->dlg);

    /* Send the request. */
    status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
    if (status != PJ_SUCCESS)
	goto on_return;


    /* Special case for NOTIFY:
     * The new state was set in pjsip_evsub_notify(), but we apply the
     * new state now, when the request was actually sent.
     */
    if (pjsip_method_cmp(&tdata->msg->line.req.method, 
			 &pjsip_notify_method)==0) 
    {
	PJ_ASSERT_ON_FAIL(  sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
			    {goto on_return;});

	set_state(sub, sub->dst_state, 
		  (sub->dst_state_str.slen ? &sub->dst_state_str : NULL), 
		  NULL);

	sub->dst_state = PJSIP_EVSUB_STATE_NULL;
	sub->dst_state_str.slen = 0;

    }


on_return:
    pjsip_dlg_dec_lock(sub->dlg);
    return status;
}



/*
 * Attach subscription session to newly created transaction, if appropriate.
 */
static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
				        pjsip_event *event)
{
    /*
     * Newly created transaction will not have subscription session
     * attached to it. Find the subscription session from the dialog,
     * by matching the Event header.
     */
    pjsip_dialog *dlg;
    pjsip_event_hdr *event_hdr;
    pjsip_msg *msg;
    struct dlgsub *dlgsub_head, *dlgsub;
    pjsip_evsub *sub;
    
    dlg = pjsip_tsx_get_dlg(tsx);
    if (!dlg) {
	pj_assert(!"Transaction should have a dialog instance!");
	return NULL;
    }


    switch (event->body.tsx_state.type) {
    case PJSIP_EVENT_RX_MSG:
	msg = event->body.tsx_state.src.rdata->msg_info.msg;
	break;
    case PJSIP_EVENT_TX_MSG:
	msg = event->body.tsx_state.src.tdata->msg;
	break;
    default:
	if (tsx->role == PJSIP_ROLE_UAC)
	    msg = tsx->last_tx->msg;
	else
	    msg = NULL;
	break;
    }
    
    if (!msg) {
	//Note:
	// this transaction can be other transaction in the dialog.
	// The assertion below probably only valid for dialog that
	// only has one event subscription usage.
	//pj_assert(!"First transaction event is not TX or RX!");
	return NULL;
    }

    event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
    if (!event_hdr) {
	/* Not subscription related message */
	return NULL;
    }

    /* Find the subscription in the dialog, based on the content
     * of Event header: 
     */

    dlgsub_head = dlg->mod_data[mod_evsub.mod.id];
    if (dlgsub_head == NULL) {
	dlgsub_head = pj_pool_alloc(dlg->pool, sizeof(struct dlgsub));
	pj_list_init(dlgsub_head);
	dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
    }
    dlgsub = dlgsub_head->next;

    while (dlgsub != dlgsub_head) {

	if (pj_stricmp(&dlgsub->sub->event->event_type, 
		       &event_hdr->event_type)==0)
	{
	    /* Event type matched. 
	     * Check if event ID matched too.
	     */
	    if (pj_strcmp(&dlgsub->sub->event->id_param, 
			  &event_hdr->id_param)==0)
	    {
		
		break;

	    }
	    /*
	     * Otherwise if it is an UAC subscription, AND
	     * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND
	     * the session's event id is NULL, AND
	     * the incoming request is NOTIFY with event ID, then
	     * we consider it as a match, and update the
	     * session's event id.
	     */
	    else if (dlgsub->sub->role == PJSIP_ROLE_UAC &&
		     (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 &&
		     dlgsub->sub->event->id_param.slen==0 &&
		     !pjsip_method_cmp(&tsx->method, &pjsip_notify_method))
	    {
		/* Update session's event id. */
		pj_strdup(dlgsub->sub->pool, 
			  &dlgsub->sub->event->id_param,
			  &event_hdr->id_param);

		break;
	    }
	}
	


	dlgsub = dlgsub->next;
    }

    if (dlgsub == dlgsub_head) {
	/* This could be incoming request to create new subscription */
	PJ_LOG(4,(THIS_FILE, 
		  "Subscription not found for %.*s, event=%.*s;id=%.*s",
		  (int)tsx->method.name.slen,
		  tsx->method.name.ptr,
		  (int)event_hdr->event_type.slen,
		  event_hdr->event_type.ptr,
		  (int)event_hdr->id_param.slen,
		  event_hdr->id_param.ptr));

	/* If this is an incoming NOTIFY, reject with 481 */
	if (tsx->state == PJSIP_TSX_STATE_TRYING &&
	    pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
	{
	    pj_str_t reason = pj_str("Subscription Does Not Exist");
	    pjsip_tx_data *tdata;
	    pj_status_t status;

	    status = pjsip_dlg_create_response(dlg, 
					       event->body.tsx_state.src.rdata, 
					       481, &reason, 
					       &tdata);
	    if (status == PJ_SUCCESS) {
		status = pjsip_dlg_send_response(dlg, tsx, tdata);
	    }
	}
	return NULL;
    }

    /* Found! */
    sub = dlgsub->sub;

    /* Attach session to the transaction */
    tsx->mod_data[mod_evsub.mod.id] = sub;
    sub->pending_tsx++;

    /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction. 
     * We can only have one pending UAC SUBSCRIBE/REFER, so if another 
     * transaction is started while previous one still alive, terminate
     * the older one.
     *
     * Sample scenario:
     *	- subscribe sent to destination that doesn't exist, transaction
     *	  is still retransmitting request, then unsubscribe is sent.
     */
    if (tsx->role == PJSIP_ROLE_UAC &&
	tsx->state == PJSIP_TSX_STATE_CALLING &&
	(pjsip_method_cmp(&tsx->method, &sub->method) == 0  ||
	 pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0))
    {

	if (sub->pending_sub && 
	    sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED) 
	{
	    PJ_LOG(4,(sub->obj_name, 
		      "Cancelling pending subscription request"));

	    /* By convention, we use 490 (Request Updated) status code.
	     * When transaction handler (below) see this status code, it
	     * will ignore the transaction.
	     */
	    pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED);
	}

	sub->pending_sub = tsx;

    }

    return sub;
}


/*
 * Create response, adding custome headers and msg body.
 */
static pj_status_t create_response( pjsip_evsub *sub,
				    pjsip_rx_data *rdata,
				    int st_code,
				    const pj_str_t *st_text,
				    const pjsip_hdr *res_hdr,
				    const pjsip_msg_body *body,
				    pjsip_tx_data **p_tdata)
{
    pjsip_tx_data *tdata;
    pjsip_hdr *hdr;
    pj_status_t status;

    status = pjsip_dlg_create_response(sub->dlg, rdata,
				       st_code, st_text, &tdata);
    if (status != PJ_SUCCESS)
	return status;

    *p_tdata = tdata;

    /* Add response headers. */
    hdr = res_hdr->next;
    while (hdr != res_hdr) {
	pjsip_msg_add_hdr( tdata->msg, 
			   pjsip_hdr_clone(tdata->pool, hdr));
	hdr = hdr->next;
    }

    /* Add msg body, if any */
    if (body) {
	tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body);
	if (tdata->msg->body == NULL) {

	    PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body"));

	    /* Ignore */
	    return PJ_SUCCESS;
	}
    }

    return PJ_SUCCESS;
}

/*
 * Get subscription state from the value of Subscription-State header.
 */
static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
			   pjsip_evsub_state *state,
			   pj_str_t **state_str )
{
    if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {

	*state = PJSIP_EVSUB_STATE_TERMINATED;
	*state_str = NULL;

    } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {

	*state = PJSIP_EVSUB_STATE_ACTIVE;
	*state_str = NULL;

    } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {

	*state = PJSIP_EVSUB_STATE_PENDING;
	*state_str = NULL;

    } else {

	*state = PJSIP_EVSUB_STATE_UNKNOWN;
	*state_str = &sub_state->sub_state;

    }
}

/*
 * Transaction event processing by UAC, after subscription is sent.
 */
static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
			      pjsip_event *event )
{

    if (pjsip_method_cmp(&tsx->method, &sub->method)==0 || 
	pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method)==0) 
    {

	/* Received response to outgoing request that establishes/refresh
	 * subscription. 
	 */

	/* First time initial request is sent. */
	if (sub->state == PJSIP_EVSUB_STATE_NULL &&
	    tsx->state == PJSIP_TSX_STATE_CALLING)
	{
	    set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event);

⌨️ 快捷键说明

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