📄 evsub.c
字号:
return;
}
/* Only interested in final response */
if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
tsx->state != PJSIP_TSX_STATE_TERMINATED)
{
return;
}
/* Clear pending subscription */
if (tsx == sub->pending_sub)
sub->pending_sub = NULL;
/* Handle authentication. */
if (tsx->status_code==401 || tsx->status_code==407) {
pjsip_tx_data *tdata;
pj_status_t status;
if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
/* Previously failed transaction has terminated */
return;
}
status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
event->body.tsx_state.src.rdata,
tsx->last_tx, &tdata);
if (status == PJ_SUCCESS)
status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL);
if (status != PJ_SUCCESS) {
/* Authentication failed! */
set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
NULL,
event);
return;
}
return;
}
if (tsx->status_code/100 == 2) {
/* Successfull SUBSCRIBE request!
* This could be:
* - response to initial SUBSCRIBE request
* - response to subsequent refresh
* - response to unsubscription
*/
if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
/* Ignore; this transaction has been processed before */
return;
}
/* Update UAC refresh time, if response contains Expires header,
* only when we're not unsubscribing.
*/
if (sub->expires->ivalue != 0) {
pjsip_msg *msg;
pjsip_expires_hdr *expires;
msg = event->body.tsx_state.src.rdata->msg_info.msg;
expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
if (expires) {
sub->expires->ivalue = expires->ivalue;
}
}
/* Update time */
update_expires(sub, sub->expires->ivalue);
/* Start UAC refresh timer, only when we're not unsubscribing */
if (sub->expires->ivalue != 0) {
unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
timeout));
set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
} else {
/* Otherwise set timer to terminate client subscription when
* NOTIFY to end subscription is not received.
*/
set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
}
/* Set state, if necessary */
pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
if (sub->state == PJSIP_EVSUB_STATE_SENT) {
set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event);
}
} else {
/* Failed SUBSCRIBE request!
*
* The RFC 3265 says that if outgoing SUBSCRIBE fails with status
* other than 481, the subscription is still considered valid for
* the duration of the last Expires.
*
* Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
* expiration, theoritically the expiration is still valid for the
* next 5 seconds even when we receive non-481 failed response.
*
* Ah, what the heck!
*
* Just terminate now!
*
*/
if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
/* Ignore, has been handled before */
return;
}
/* Ignore 490 (Request Updated) status.
* This happens when application sends SUBSCRIBE/REFER while
* another one is still in progress.
*/
if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) {
return;
}
/* Kill any timer. */
set_timer(sub, TIMER_TYPE_NONE, 0);
/* Set state to TERMINATED */
set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
NULL, event);
}
} else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
/* Incoming NOTIFY.
* This can be the result of:
* - Initial subscription response
* - UAS updating the resource info.
* - Unsubscription response.
*/
int st_code = 200;
pj_str_t *st_text = NULL;
pjsip_hdr res_hdr;
pjsip_msg_body *body = NULL;
pjsip_rx_data *rdata;
pjsip_msg *msg;
pjsip_sub_state_hdr *sub_state;
pjsip_evsub_state new_state;
pj_str_t *new_state_str;
pjsip_tx_data *tdata;
pj_status_t status;
int next_refresh;
/* Only want to handle initial NOTIFY receive event. */
if (tsx->state != PJSIP_TSX_STATE_TRYING)
return;
rdata = event->body.tsx_state.src.rdata;
msg = rdata->msg_info.msg;
pj_list_init(&res_hdr);
/* Get subscription state header. */
sub_state = pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
if (sub_state == NULL) {
pjsip_warning_hdr *warn_hdr;
pj_str_t warn_text = { "Missing Subscription-State header", 33};
/* Bad request! Add warning header. */
st_code = PJSIP_SC_BAD_REQUEST;
warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
pjsip_endpt_name(sub->endpt),
&warn_text);
pj_list_push_back(&res_hdr, warn_hdr);
}
/* Call application registered callback to handle incoming NOTIFY,
* if any.
*/
if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) {
(*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
&res_hdr, &body);
/* Application MUST specify final response! */
PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
/* Must be a valid status code */
PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
}
/* If non-2xx should be returned, then send the response.
* No need to update server subscription state.
*/
if (st_code >= 300) {
status = create_response(sub, rdata, st_code, st_text, &res_hdr,
body, &tdata);
if (status == PJ_SUCCESS) {
status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
}
/* Start timer to terminate subscription, just in case server
* is not able to generate NOTIFY to our response.
*/
if (status == PJ_SUCCESS) {
unsigned timeout = TIME_UAC_WAIT_NOTIFY;
set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
} else {
set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
}
return;
}
/* Update expiration from the value of expires param in
* Subscription-State header, but ONLY when subscription state
* is "active" or "pending", AND the header contains expires param.
*/
if (sub->expires->ivalue != 0 &&
sub_state->expires_param >= 0 &&
(pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
{
next_refresh = sub_state->expires_param;
} else {
next_refresh = sub->expires->ivalue;
}
/* Update time */
update_expires(sub, next_refresh);
/* Start UAC refresh timer, only when we're not unsubscribing */
if (sub->expires->ivalue != 0) {
unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
next_refresh - TIME_UAC_REFRESH : next_refresh;
PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
}
/* Find out the state */
get_hdr_state(sub_state, &new_state, &new_state_str);
/* Send response. */
status = create_response(sub, rdata, st_code, st_text, &res_hdr,
body, &tdata);
if (status == PJ_SUCCESS)
status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
/* Set the state */
if (status == PJ_SUCCESS) {
set_state(sub, new_state, new_state_str, event);
} else {
set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
}
} else {
/*
* Unexpected method!
*/
PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
(int)tsx->method.name.slen, tsx->method.name.ptr));
}
}
/*
* Transaction event processing by UAS, after subscription is accepted.
*/
static void on_tsx_state_uas( 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)
{
/*
* Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
*
*/
pjsip_rx_data *rdata;
pjsip_event_hdr *event_hdr;
pjsip_expires_hdr *expires;
pjsip_msg *msg;
pjsip_tx_data *tdata;
int st_code = 200;
pj_str_t *st_text = NULL;
pjsip_hdr res_hdr;
pjsip_msg_body *body = NULL;
pjsip_evsub_state old_state;
pj_str_t old_state_str;
pj_status_t status;
/* Only wants to handle the first event when the request is
* received.
*/
if (tsx->state != PJSIP_TSX_STATE_TRYING)
return;
rdata = event->body.tsx_state.src.rdata;
msg = rdata->msg_info.msg;
/* Set expiration time based on client request (in Expires header),
* or package default expiration time.
*/
event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
if (event_hdr && expires) {
struct evpkg *evpkg;
evpkg = find_pkg(&event_hdr->event_type);
if (evpkg) {
if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
sub->expires->ivalue = expires->ivalue;
else
sub->expires->ivalue = evpkg->pkg_expires;
}
}
/* Update time (before calling on_rx_refresh, since application
* will send NOTIFY.
*/
update_expires(sub, sub->expires->ivalue);
/* Save old state.
* If application respond with non-2xx, revert to old state.
*/
old_state = sub->state;
old_state_str = sub->state_str;
if (sub->expires->ivalue == 0) {
sub->state = PJSIP_EVSUB_STATE_TERMINATED;
sub->state_str = evsub_state_names[sub->state];
} else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
sub->state_str = evsub_state_names[sub->state];
}
/* Call application's on_rx_refresh, just in case it wants to send
* response other than 200 (OK)
*/
pj_list_init(&res_hdr);
if (sub->user.on_rx_refresh && sub->call_cb) {
(*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
&res_hdr, &body);
}
/* Application MUST specify final response! */
PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
/* Must be a valid status code */
PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
/* Create and send response */
status = create_response(sub, rdata, st_code, st_text, &res_hdr,
body, &tdata);
if (status == PJ_SUCCESS) {
/* Add expires header: */
pjsip_msg_add_hdr( tdata->msg,
pjsip_hdr_shallow_clone(tdata->pool,
sub->expires));
/* Send */
status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
}
/* Update state or revert state */
if (st_code/100==2) {
if (sub->expires->ivalue == 0) {
set_state(sub, sub->state, NULL, event);
} else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
set_state(sub, sub->state, NULL, event);
}
/* Set UAS timeout timer, when state is not terminated. */
if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
sub->expires->ivalue));
set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
sub->expires->ivalue);
}
} else {
sub->state = old_state;
sub->state_str = old_state_str;
}
} else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
/* Handle authentication */
if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
(tsx->status_code==401 || tsx->status_code==407))
{
pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
pjsip_tx_data *tdata;
pj_status_t status;
status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
tsx->last_tx, &tdata);
if (status == PJ_SUCCESS)
status = pjsip_dlg_send_request( sub->dlg, tdata, -1, NULL );
if (status != PJ_SUCCESS) {
/* Can't authenticate. Terminate session (?) */
set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
return;
}
}
/*
* Terminate event usage if we receive 481, 408, and 7 class
* responses.
*/
if (sub->state != PJSIP_EVSUB_STATE_TERMINATED &&
(tsx->status_code==481 || tsx->status_code==408 ||
tsx->status_code/100 == 7))
{
set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
return;
}
} else {
/*
* Unexpected method!
*/
PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
(int)tsx->method.name.slen, tsx->method.name.ptr));
}
}
/*
* Notification when transaction state has changed!
*/
static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
{
pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
if (sub == NULL) {
sub = on_new_transaction(tsx, event);
if (sub == NULL)
return;
}
/* Call on_tsx_state callback, if any. */
if (sub->user.on_tsx_state && sub->call_cb)
(*sub->user.on_tsx_state)(sub, tsx, event);
/* Process the event: */
if (sub->role == PJSIP_ROLE_UAC) {
on_tsx_state_uac(sub, tsx, event);
} else {
on_tsx_state_uas(sub, tsx, event);
}
/* Check transaction TERMINATE event */
if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
--sub->pending_tsx;
if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
sub->pending_tsx == 0)
{
evsub_destroy(sub);
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -