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