📄 sip_inv.c
字号:
pjsip_require_hdr *req_hdr; int code = 200; unsigned rem_option = 0; pj_status_t status = PJ_SUCCESS; pjsip_hdr res_hdr_list; /* Init return arguments. */ if (p_tdata) *p_tdata = NULL; /* Verify arguments. */ PJ_ASSERT_RETURN(rdata != NULL && options != NULL, PJ_EINVAL); /* Normalize options */ if (*options & PJSIP_INV_REQUIRE_100REL) *options |= PJSIP_INV_SUPPORT_100REL; if (*options & PJSIP_INV_REQUIRE_TIMER) *options |= PJSIP_INV_SUPPORT_TIMER; /* Get the message in rdata */ msg = rdata->msg_info.msg; /* Must be INVITE request. */ PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG && msg->line.req.method.id == PJSIP_INVITE_METHOD, PJ_EINVAL); /* If tdata is specified, then either dlg or endpt must be specified */ PJ_ASSERT_RETURN((!p_tdata) || (endpt || dlg), PJ_EINVAL); /* Get the endpoint */ endpt = endpt ? endpt : dlg->endpt; /* Init response header list */ pj_list_init(&res_hdr_list); /* Check the request body, see if it'inv something that we support * (i.e. SDP). */ if (msg->body) { pjsip_msg_body *body = msg->body; pj_str_t str_application = {"application", 11}; pj_str_t str_sdp = { "sdp", 3 }; pjmedia_sdp_session *sdp; /* Check content type. */ if (pj_stricmp(&body->content_type.type, &str_application) != 0 || pj_stricmp(&body->content_type.subtype, &str_sdp) != 0) { /* Not "application/sdp" */ code = PJSIP_SC_UNSUPPORTED_MEDIA_TYPE; status = PJSIP_ERRNO_FROM_SIP_STATUS(code); if (p_tdata) { /* Add Accept header to response */ pjsip_accept_hdr *acc; acc = pjsip_accept_hdr_create(rdata->tp_info.pool); PJ_ASSERT_RETURN(acc, PJ_ENOMEM); acc->values[acc->count++] = pj_str("application/sdp"); pj_list_push_back(&res_hdr_list, acc); } goto on_return; } /* Parse and validate SDP */ status = pjmedia_sdp_parse(rdata->tp_info.pool, body->data, body->len, &sdp); if (status == PJ_SUCCESS) status = pjmedia_sdp_validate(sdp); if (status != PJ_SUCCESS) { /* Unparseable or invalid SDP */ code = PJSIP_SC_BAD_REQUEST; if (p_tdata) { /* Add Warning header. */ pjsip_warning_hdr *w; w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool, pjsip_endpt_name(endpt), status); PJ_ASSERT_RETURN(w, PJ_ENOMEM); pj_list_push_back(&res_hdr_list, w); } goto on_return; } /* Negotiate with local SDP */ if (l_sdp) { pjmedia_sdp_neg *neg; /* Local SDP must be valid! */ PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(l_sdp))==PJ_SUCCESS, status); /* Create SDP negotiator */ status = pjmedia_sdp_neg_create_w_remote_offer( rdata->tp_info.pool, l_sdp, sdp, &neg); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* Negotiate SDP */ status = pjmedia_sdp_neg_negotiate(rdata->tp_info.pool, neg, 0); if (status != PJ_SUCCESS) { /* Incompatible media */ code = PJSIP_SC_NOT_ACCEPTABLE_HERE; if (p_tdata) { pjsip_accept_hdr *acc; pjsip_warning_hdr *w; /* Add Warning header. */ w = pjsip_warning_hdr_create_from_status( rdata->tp_info.pool, pjsip_endpt_name(endpt), status); PJ_ASSERT_RETURN(w, PJ_ENOMEM); pj_list_push_back(&res_hdr_list, w); /* Add Accept header to response */ acc = pjsip_accept_hdr_create(rdata->tp_info.pool); PJ_ASSERT_RETURN(acc, PJ_ENOMEM); acc->values[acc->count++] = pj_str("application/sdp"); pj_list_push_back(&res_hdr_list, acc); } goto on_return; } } } /* Check supported methods, see if peer supports UPDATE. * We just assume that peer supports standard INVITE, ACK, CANCEL, and BYE * implicitly by sending this INVITE. */ allow = pjsip_msg_find_hdr(msg, PJSIP_H_ALLOW, NULL); if (allow) { unsigned i; const pj_str_t STR_UPDATE = { "UPDATE", 6 }; for (i=0; i<allow->count; ++i) { if (pj_stricmp(&allow->values[i], &STR_UPDATE)==0) break; } if (i != allow->count) { /* UPDATE is present in Allow */ rem_option |= PJSIP_INV_SUPPORT_UPDATE; } } /* Check Supported header */ sup_hdr = pjsip_msg_find_hdr(msg, PJSIP_H_SUPPORTED, NULL); if (sup_hdr) { unsigned i; pj_str_t STR_100REL = { "100rel", 6}; pj_str_t STR_TIMER = { "timer", 5 }; for (i=0; i<sup_hdr->count; ++i) { if (pj_stricmp(&sup_hdr->values[i], &STR_100REL)==0) rem_option |= PJSIP_INV_SUPPORT_100REL; else if (pj_stricmp(&sup_hdr->values[i], &STR_TIMER)==0) rem_option |= PJSIP_INV_SUPPORT_TIMER; } } /* Check Require header */ req_hdr = pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, NULL); if (req_hdr) { unsigned i; const pj_str_t STR_100REL = { "100rel", 6}; const pj_str_t STR_TIMER = { "timer", 5 }; const pj_str_t STR_REPLACES = { "replaces", 8 }; unsigned unsupp_cnt = 0; pj_str_t unsupp_tags[PJSIP_GENERIC_ARRAY_MAX_COUNT]; for (i=0; i<req_hdr->count; ++i) { if ((*options & PJSIP_INV_SUPPORT_100REL) && pj_stricmp(&req_hdr->values[i], &STR_100REL)==0) { rem_option |= PJSIP_INV_REQUIRE_100REL; } else if ((*options && PJSIP_INV_SUPPORT_TIMER) && pj_stricmp(&req_hdr->values[i], &STR_TIMER)==0) { rem_option |= PJSIP_INV_REQUIRE_TIMER; } else if (pj_stricmp(&req_hdr->values[i], &STR_REPLACES)==0) { pj_bool_t supp; supp = pjsip_endpt_has_capability(endpt, PJSIP_H_SUPPORTED, NULL, &STR_REPLACES); if (!supp) unsupp_tags[unsupp_cnt++] = req_hdr->values[i]; } else { /* Unknown/unsupported extension tag! */ unsupp_tags[unsupp_cnt++] = req_hdr->values[i]; } } /* Check if there are required tags that we don't support */ if (unsupp_cnt) { code = PJSIP_SC_BAD_EXTENSION; status = PJSIP_ERRNO_FROM_SIP_STATUS(code); if (p_tdata) { pjsip_unsupported_hdr *unsupp_hdr; const pjsip_hdr *h; /* Add Unsupported header. */ unsupp_hdr = pjsip_unsupported_hdr_create(rdata->tp_info.pool); PJ_ASSERT_RETURN(unsupp_hdr != NULL, PJ_ENOMEM); unsupp_hdr->count = unsupp_cnt; for (i=0; i<unsupp_cnt; ++i) unsupp_hdr->values[i] = unsupp_tags[i]; pj_list_push_back(&res_hdr_list, unsupp_hdr); /* Add Supported header. */ h = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL); pj_assert(h); if (h) { sup_hdr = pjsip_hdr_clone(rdata->tp_info.pool, h); pj_list_push_back(&res_hdr_list, sup_hdr); } } goto on_return; } } /* Check if there are local requirements that are not supported * by peer. */ if ( ((*options & PJSIP_INV_REQUIRE_100REL)!=0 && (rem_option & PJSIP_INV_SUPPORT_100REL)==0) || ((*options & PJSIP_INV_REQUIRE_TIMER)!=0 && (rem_option & PJSIP_INV_SUPPORT_TIMER)==0)) { code = PJSIP_SC_EXTENSION_REQUIRED; status = PJSIP_ERRNO_FROM_SIP_STATUS(code); if (p_tdata) { const pjsip_hdr *h; /* Add Require header. */ req_hdr = pjsip_require_hdr_create(rdata->tp_info.pool); PJ_ASSERT_RETURN(req_hdr != NULL, PJ_ENOMEM); if (*options & PJSIP_INV_REQUIRE_100REL) req_hdr->values[req_hdr->count++] = pj_str("100rel"); if (*options & PJSIP_INV_REQUIRE_TIMER) req_hdr->values[req_hdr->count++] = pj_str("timer"); pj_list_push_back(&res_hdr_list, req_hdr); /* Add Supported header. */ h = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL); pj_assert(h); if (h) { sup_hdr = pjsip_hdr_clone(rdata->tp_info.pool, h); pj_list_push_back(&res_hdr_list, sup_hdr); } } goto on_return; }on_return: /* Create response if necessary */ if (code != 200 && p_tdata) { pjsip_tx_data *tdata; const pjsip_hdr *h; if (dlg) { status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata); } else { status = pjsip_endpt_create_response(endpt, rdata, code, NULL, &tdata); } if (status != PJ_SUCCESS) return status; /* Add response headers. */ h = res_hdr_list.next; while (h != &res_hdr_list) { pjsip_hdr *cloned; cloned = pjsip_hdr_clone(tdata->pool, h); PJ_ASSERT_RETURN(cloned, PJ_ENOMEM); pjsip_msg_add_hdr(tdata->msg, cloned); h = h->next; } *p_tdata = tdata; /* Can not return PJ_SUCCESS when response message is produced. * Ref: PROTOS test ~#2490 */ if (status == PJ_SUCCESS) status = PJSIP_ERRNO_FROM_SIP_STATUS(code); } return status;}/* * Create UAS invite session. */PJ_DEF(pj_status_t) pjsip_inv_create_uas( pjsip_dialog *dlg, pjsip_rx_data *rdata, const pjmedia_sdp_session *local_sdp, unsigned options, pjsip_inv_session **p_inv){ pjsip_inv_session *inv; struct tsx_inv_data *tsx_inv_data; pjsip_msg *msg; pjmedia_sdp_session *rem_sdp = NULL; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(dlg && rdata && p_inv, PJ_EINVAL); /* Dialog MUST have been initialised. */ PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata) != NULL, PJ_EINVALIDOP); msg = rdata->msg_info.msg; /* rdata MUST contain INVITE request */ PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG && msg->line.req.method.id == PJSIP_INVITE_METHOD, PJ_EINVALIDOP); /* Lock dialog */ pjsip_dlg_inc_lock(dlg); /* Normalize options */ if (options & PJSIP_INV_REQUIRE_100REL) options |= PJSIP_INV_SUPPORT_100REL; if (options & PJSIP_INV_REQUIRE_TIMER) options |= PJSIP_INV_SUPPORT_TIMER; /* Create the session */ inv = pj_pool_zalloc(dlg->pool, sizeof(pjsip_inv_session)); pj_assert(inv != NULL); inv->pool = dlg->pool; inv->role = PJSIP_ROLE_UAS; inv->state = PJSIP_INV_STATE_NULL; inv->dlg = dlg; inv->options = options; inv->notify = PJ_TRUE; inv->cause = 0; /* Object name will use the same dialog pointer. */ pj_ansi_snprintf(inv->obj_name, PJ_MAX_OBJ_NAME, "inv%p", dlg); /* Parse SDP in message body, if present. */ if (msg->body) { pjsip_msg_body *body = msg->body; /* Parse and validate SDP */ status = pjmedia_sdp_parse(inv->pool, body->data, body->len, &rem_sdp); if (status == PJ_SUCCESS) status = pjmedia_sdp_validate(rem_sdp); if (status != PJ_SUCCESS) { pjsip_dlg_dec_lock(dlg); return status; } } /* Create negotiator. */ if (rem_sdp) { status = pjmedia_sdp_neg_create_w_remote_offer(inv->pool, local_sdp, rem_sdp, &inv->neg); } else if (local_sdp) { status = pjmedia_sdp_neg_create_w_local_offer(inv->pool, local_sdp, &inv->neg); } else { status = PJ_SUCCESS; } if (status != PJ_SUCCESS) { pjsip_dlg_dec_lock(dlg); return status; } /* Register invite as dialog usage. */ status = pjsip_dlg_add_usage(dlg, &mod_inv.mod, inv); if (status != PJ_SUCCESS) { pjsip_dlg_dec_lock(dlg); return status; } /* Increment session in the dialog. */ pjsip_dlg_inc_session(dlg, &mod_inv.mod); /* Save the invite transaction. */ inv->invite_tsx = pjsip_rdata_get_tsx(rdata); /* Attach our data to the transaction. */ tsx_inv_data = pj_pool_zalloc(inv->invite_tsx->pool, sizeof(struct tsx_inv_data)); tsx_inv_data->inv = inv; inv->invite_tsx->mod_data[mod_inv.mod.id] = tsx_inv_data; /* Done */ pjsip_dlg_dec_lock(dlg); *p_inv = inv; PJ_LOG(5,(inv->obj_name, "UAS invite session created for dialog %s", dlg->obj_name)); return PJ_SUCCESS;}/* * Forcefully terminate the session. */PJ_DEF(pj_status_t) pjsip_inv_terminate( pjsip_inv_session *inv, int st_code, pj_bool_t notify){ PJ_ASSERT_RETURN(inv, PJ_EINVAL); /* Lock dialog. */ pjsip_dlg_inc_lock(inv->dlg); /* Set callback notify flag. */ inv->notify = notify; /* If there's pending transaction, terminate the transaction. * This may subsequently set the INVITE session state to * disconnected. */ if (inv->invite_tsx && inv->invite_tsx->state <= PJSIP_TSX_STATE_COMPLETED) { pjsip_tsx_terminate(inv->invite_tsx, st_code); } /* Set cause. */ inv_set_cause(inv, st_code, NULL); /* Forcefully terminate the session if state is not DISCONNECTED */ if (inv->state != PJSIP_INV_STATE_DISCONNECTED) { inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, NULL); } /* Done. * The dec_lock() below will actually destroys the dialog if it * has no other session. */ pjsip_dlg_dec_lock(inv->dlg); return PJ_SUCCESS;}static void *clone_sdp(pj_pool_t *pool, const void *data, unsigned len){ PJ_UNUSED_ARG(len); return pjmedia_sdp_session_clone(pool, data);}static int print_sdp(pjsip_msg_body *body, char *buf, pj_size_t len){ return pjmedia_sdp_print(body->data, buf, len);}PJ_DEF(pj_status_t) pjsip_create_sdp_body( pj_pool_t *pool, pjmedia_sdp_session *sdp, pjsip_msg_body **p_body){ const pj_str_t STR_APPLICATION = { "application", 11}; const pj_str_t STR_SDP = { "sdp", 3 }; pjsip_msg_body *body; body = pj_pool_zalloc(pool, sizeof(pjsip_msg_body)); PJ_ASSERT_RETURN(body != NULL, PJ_ENOMEM); body->content_type.type = STR_APPLICATION; body->content_type.subtype = STR_SDP; body->data = sdp; body->len = 0; body->clone_data = &clone_sdp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -