📄 sip_inv.c
字号:
return PJ_SUCCESS;
}
/*
* Verify incoming INVITE request.
*/
PJ_DEF(pj_status_t) pjsip_inv_verify_request(pjsip_rx_data *rdata,
unsigned *options,
const pjmedia_sdp_session *l_sdp,
pjsip_dialog *dlg,
pjsip_endpoint *endpt,
pjsip_tx_data **p_tdata)
{
pjsip_msg *msg;
pjsip_allow_hdr *allow;
pjsip_supported_hdr *sup_hdr;
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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -