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

📄 evsub.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 4 页
字号:


/*
 * Destroy session.
 */
static void evsub_destroy( pjsip_evsub *sub )
{
    struct dlgsub *dlgsub_head, *dlgsub;

    PJ_LOG(4,(sub->obj_name, "Subscription destroyed"));

    /* Kill timer */
    set_timer(sub, TIMER_TYPE_NONE, 0);

    /* Remote this session from dialog's list of subscription */
    dlgsub_head = sub->dlg->mod_data[mod_evsub.mod.id];
    dlgsub = dlgsub_head->next;
    while (dlgsub != dlgsub_head) {
	
	if (dlgsub->sub == sub) {
	    pj_list_erase(dlgsub);
	    break;
	}

	dlgsub = dlgsub->next;
    }

    /* Decrement dialog's session */
    pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod);
}

/*
 * Set subscription session state.
 */
static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
		       const pj_str_t *state_str, pjsip_event *event)
{
    pjsip_evsub_state prev_state = sub->state;
    pj_str_t old_state_str = sub->state_str;

    sub->state = state;

    if (state_str && state_str->slen)
	pj_strdup_with_null(sub->pool, &sub->state_str, state_str);
    else
	sub->state_str = evsub_state_names[state];

    PJ_LOG(4,(sub->obj_name, 
	      "Subscription state changed %.*s --> %.*s",
	      (int)old_state_str.slen,
	      old_state_str.ptr,
	      (int)sub->state_str.slen,
	      sub->state_str.ptr));

    if (sub->user.on_evsub_state && sub->call_cb)
	(*sub->user.on_evsub_state)(sub, event);

    if (state == PJSIP_EVSUB_STATE_TERMINATED &&
	prev_state != PJSIP_EVSUB_STATE_TERMINATED) 
    {
	if (sub->pending_tsx == 0) {
	    evsub_destroy(sub);
	}
    }
}


/*
 * Timer callback.
 */
static void on_timer( pj_timer_heap_t *timer_heap,
		      struct pj_timer_entry *entry)
{
    pjsip_evsub *sub;
    int timer_id;

    PJ_UNUSED_ARG(timer_heap);

    sub = entry->user_data;

    pjsip_dlg_inc_lock(sub->dlg);

    timer_id = entry->id;
    entry->id = TIMER_TYPE_NONE;

    switch (timer_id) {

    case TIMER_TYPE_UAC_REFRESH:
	/* Time for UAC to refresh subscription */
	if (sub->user.on_client_refresh && sub->call_cb) {
	    (*sub->user.on_client_refresh)(sub);
	} else {
	    pjsip_tx_data *tdata;
	    pj_status_t status;

	    PJ_LOG(5,(sub->obj_name, "Refreshing subscription."));
	    status = pjsip_evsub_initiate(sub, NULL, 
					  sub->expires->ivalue,
					  &tdata);
	    if (status == PJ_SUCCESS)
		pjsip_evsub_send_request(sub, tdata);
	}
	break;

    case TIMER_TYPE_UAS_TIMEOUT:
	/* Refresh from UAC has not been received */
	if (sub->user.on_server_timeout && sub->call_cb) {
	    (*sub->user.on_server_timeout)(sub);
	} else {
	    pjsip_tx_data *tdata;
	    pj_status_t status;

	    PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. "
				     "Sending NOTIFY to terminate."));
	    status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED, 
					 NULL, &STR_TIMEOUT, &tdata);
	    if (status == PJ_SUCCESS)
		pjsip_evsub_send_request(sub, tdata);
	}
	break;

    case TIMER_TYPE_UAC_TERMINATE:
	{
	    PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. "
				     "Terminating.."));
	    set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
	}
	break;

    case TIMER_TYPE_UAC_WAIT_NOTIFY:
	{
	    pjsip_tx_data *tdata;
	    pj_status_t status;

	    PJ_LOG(5,(sub->obj_name, 
		     "Timeout waiting for subsequent NOTIFY (we did "
		     "send non-2xx response for previous NOTIFY). "
		     "Unsubscribing.."));
	    status = pjsip_evsub_initiate( sub, NULL, 0, &tdata);
	    if (status == PJ_SUCCESS)
		pjsip_evsub_send_request(sub, tdata);
	}
	break;

    default:
	pj_assert(!"Invalid timer id");
    }

    pjsip_dlg_dec_lock(sub->dlg);
}


/*
 * Create subscription session, used for both client and notifier.
 */
static pj_status_t evsub_create( pjsip_dialog *dlg,
				 pjsip_role_e role,
				 const pjsip_evsub_user *user_cb,
				 const pj_str_t *event,
				 unsigned option,
				 pjsip_evsub **p_evsub )
{
    pjsip_evsub *sub;
    struct evpkg *pkg;
    struct dlgsub *dlgsub_head, *dlgsub;
    pj_status_t status;

    /* Make sure there's package register for the event name: */

    pkg = find_pkg(event);
    if (pkg == NULL)
	return PJSIP_SIMPLE_ENOPKG;


    /* Must lock dialog before using pool etc. */
    pjsip_dlg_inc_lock(dlg);

    /* Init attributes: */

    sub = pj_pool_zalloc(dlg->pool, sizeof(struct pjsip_evsub));
    sub->pool = dlg->pool;
    sub->endpt = dlg->endpt;
    sub->dlg = dlg;
    sub->pkg = pkg;
    sub->role = role;
    sub->call_cb = PJ_TRUE;
    sub->option = option;
    sub->state = PJSIP_EVSUB_STATE_NULL;
    sub->state_str = evsub_state_names[sub->state];
    sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires);
    sub->accept = pjsip_hdr_clone(sub->pool, pkg->pkg_accept);

    sub->timer.user_data = sub;
    sub->timer.cb = &on_timer;

    /* Set name. */
    pj_ansi_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
		     "evsub%p", sub);


    /* Copy callback, if any: */
    if (user_cb)
	pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user));


    /* Create Event header: */
    sub->event = pjsip_event_hdr_create(sub->pool);
    pj_strdup(sub->pool, &sub->event->event_type, event);


    /* Create subcription list: */

    dlgsub_head = pj_pool_alloc(sub->pool, sizeof(struct dlgsub));
    dlgsub = pj_pool_alloc(sub->pool, sizeof(struct dlgsub));
    dlgsub->sub = sub;

    pj_list_init(dlgsub_head);
    pj_list_push_back(dlgsub_head, dlgsub);


    /* Register as dialog usage: */

    status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head);
    if (status != PJ_SUCCESS) {
	pjsip_dlg_dec_lock(dlg);
	return status;
    }


    PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s",
	      (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"),
	      dlg->obj_name));

    *p_evsub = sub;
    pjsip_dlg_dec_lock(dlg);

    return PJ_SUCCESS;
}



/*
 * Create client subscription session.
 */
PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
					    const pjsip_evsub_user *user_cb,
					    const pj_str_t *event,
					    unsigned option,
					    pjsip_evsub **p_evsub)
{
    pjsip_evsub *sub;
    pj_status_t status;

    PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL);

    pjsip_dlg_inc_lock(dlg);
    status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, option, &sub);
    if (status != PJ_SUCCESS)
	goto on_return;

    /* Add unique Id to Event header, only when PJSIP_EVSUB_NO_EVENT_ID
     * is not specified.
     */
    if ((option & PJSIP_EVSUB_NO_EVENT_ID) == 0) {
	pj_create_unique_string(sub->pool, &sub->event->id_param);
    }

    /* Increment dlg session. */
    pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod);

    /* Done */
    *p_evsub = sub;

on_return:
    pjsip_dlg_dec_lock(dlg);
    return status;
}


/*
 * Create server subscription session from incoming request.
 */
PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
					    const pjsip_evsub_user *user_cb,
					    pjsip_rx_data *rdata,
					    unsigned option,
					    pjsip_evsub **p_evsub)
{
    pjsip_evsub *sub;
    pjsip_transaction *tsx;
    pjsip_accept_hdr *accept_hdr;
    pjsip_event_hdr *event_hdr;
    pjsip_expires_hdr *expires_hdr;
    pj_status_t status;

    /* Check arguments: */
    PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);

    /* MUST be request message: */
    PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
		     PJSIP_ENOTREQUESTMSG);

    /* Transaction MUST have been created (in the dialog) */
    tsx = pjsip_rdata_get_tsx(rdata);
    PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX);

    /* No subscription must have been attached to transaction */
    PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL, 
		     PJSIP_ETYPEEXISTS);

    /* Package MUST implement on_rx_refresh */
    PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP);

    /* Request MUST have "Event" header. We need the Event header to get 
     * the package name (don't want to add more arguments in the function).
     */
    event_hdr = (pjsip_event_hdr*) 
	pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
    if (event_hdr == NULL) {
	return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
    }

    /* Start locking the mutex: */

    pjsip_dlg_inc_lock(dlg);

    /* Create the session: */

    status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb, 
			  &event_hdr->event_type, option, &sub);
    if (status != PJ_SUCCESS)
	goto on_return;

    /* Just duplicate Event header from the request */
    sub->event = pjsip_hdr_clone(sub->pool, event_hdr);

    /* Set the method: */
    pjsip_method_copy(sub->pool, &sub->method, 
		      &rdata->msg_info.msg->line.req.method);

    /* Update expiration time according to client request: */

    expires_hdr = (pjsip_expires_hdr*)
	pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
    if (expires_hdr) {
	sub->expires->ivalue = expires_hdr->ivalue;
    }

    /* Update time. */
    update_expires(sub, sub->expires->ivalue);

    /* Update Accept header: */

    accept_hdr = (pjsip_accept_hdr*)
	pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
    if (accept_hdr)
	sub->accept = pjsip_hdr_clone(sub->pool, accept_hdr);

    /* We can start the session: */

    pjsip_dlg_inc_session(dlg, &mod_evsub.mod);
    sub->pending_tsx++;
    tsx->mod_data[mod_evsub.mod.id] = sub;


    /* Done. */
    *p_evsub = sub;


on_return:
    pjsip_dlg_dec_lock(dlg);
    return status;
}


/*
 * Forcefully destroy subscription.
 */
PJ_DEF(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub,
					   pj_bool_t notify )
{
    PJ_ASSERT_RETURN(sub, PJ_EINVAL);

    pjsip_dlg_inc_lock(sub->dlg);

    /* I think it's pretty safe to disable this check.
     
    if (sub->pending_tsx) {
	pj_assert(!"Unable to terminate when there's pending tsx");
	pjsip_dlg_dec_lock(sub->dlg);
	return PJ_EINVALIDOP;
    }
    */

    sub->call_cb = notify;
    set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);

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

/*
 * Get subscription state.
 */
PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
{
    return sub->state;
}

/*
 * Get state name.
 */
PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
{
    return sub->state_str.ptr;
}


/*
 * Initiate client subscription
 */
PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
					  const pjsip_method *method,
					  pj_int32_t expires,
					  pjsip_tx_data **p_tdata)
{
    pjsip_tx_data *tdata;
    pj_status_t status;

    PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);

    /* Use SUBSCRIBE if method is not specified */
    if (method == NULL)
	method = &pjsip_subscribe_method;

    pjsip_dlg_inc_lock(sub->dlg);

    /* Update method: */
    if (sub->state == PJSIP_EVSUB_STATE_NULL)
	pjsip_method_copy(sub->pool, &sub->method, method);

    status = pjsip_dlg_create_request( sub->dlg, 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));

    /* Update and add expires header: */
    if (expires >= 0)
	sub->expires->ivalue = expires;
    pjsip_msg_add_hdr( tdata->msg,
		       pjsip_hdr_shallow_clone(tdata->pool, sub->expires));

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

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

   
    *p_tdata = tdata;


on_return:

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


/*
 * Accept incoming subscription request.
 */
PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
					pjsip_rx_data *rdata,
				        int st_code,
					const pjsip_hdr *hdr_list )
{
    pjsip_tx_data *tdata;
    pjsip_transaction *tsx;
    pj_status_t status;

    /* Check arguments */
    PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);

    /* Can only be for server subscription: */
    PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);

    /* Only expect 2xx status code (for now) */

⌨️ 快捷键说明

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