📄 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 + -