📄 evsub.c
字号:
/*
* 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 + -