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