diff options
Diffstat (limited to 'pjsip/src')
-rw-r--r-- | pjsip/src/pjsip/sip_dialog.c | 238 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_call.c | 25 |
2 files changed, 261 insertions, 2 deletions
diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index c46e84ec..3b87e906 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -46,6 +46,7 @@ pj_bool_t pjsip_include_allow_hdr_in_dlg = PJSIP_INCLUDE_ALLOW_HDR_IN_DLG; /* Contact header string */ static const pj_str_t HCONTACT = { "Contact", 7 }; + PJ_DEF(pj_bool_t) pjsip_method_creates_dialog(const pjsip_method *m) { const pjsip_method subscribe = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}}; @@ -89,6 +90,7 @@ static pj_status_t create_dialog( pjsip_user_agent *ua, dlg->add_allow = pjsip_include_allow_hdr_in_dlg; pj_list_init(&dlg->inv_hdr); + pj_list_init(&dlg->rem_cap_hdr); status = pj_mutex_create_recursive(pool, dlg->obj_name, &dlg->mutex_); if (status != PJ_SUCCESS) @@ -523,6 +525,9 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua, dlg->remote.tag_hval = pj_hash_calc(0, dlg->remote.info->tag.ptr, dlg->remote.info->tag.slen); + /* Update remote capabilities info */ + pjsip_dlg_update_remote_cap(dlg, rdata->msg_info.msg, PJ_TRUE); + /* Register this dialog to user agent. */ status = pjsip_ua_register_dlg( ua, dlg ); if (status != PJ_SUCCESS) @@ -1774,11 +1779,18 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) { pjsip_contact_hdr *contact; + /* Update remote capability info, when To tags in the dialog remote + * info and the incoming response are different, e.g: first response + * with To-tag or forking, apply strict update. + */ + pjsip_dlg_update_remote_cap(dlg, rdata->msg_info.msg, + pj_strcmp(&dlg->remote.info->tag, + &rdata->msg_info.to->tag)); + /* Update To tag. */ pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag); /* No need to update remote's tag_hval since its never used. */ - /* RFC 3271 Section 12.1.2: * The route set MUST be set to the list of URIs in the Record-Route * header field from the response, taken in reverse order and @@ -1860,7 +1872,6 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) dlg_update_routeset(dlg, rdata); } - /* Pass to dialog usages. */ for (i=0; i<dlg->usage_cnt; ++i) { pj_bool_t processed; @@ -1962,3 +1973,226 @@ void pjsip_dlg_on_tsx_state( pjsip_dialog *dlg, pjsip_dlg_dec_lock(dlg); } + +/* + * Check if the specified capability is supported by remote. + */ +PJ_DEF(pjsip_dialog_cap_status) pjsip_dlg_remote_has_cap( + pjsip_dialog *dlg, + int htype, + const pj_str_t *hname, + const pj_str_t *token) +{ + const pjsip_generic_array_hdr *hdr; + pjsip_dialog_cap_status cap_status = PJSIP_DIALOG_CAP_UNSUPPORTED; + unsigned i; + + PJ_ASSERT_RETURN(dlg && token, PJ_FALSE); + + pjsip_dlg_inc_lock(dlg); + + hdr = (const pjsip_generic_array_hdr*) + pjsip_dlg_get_remote_cap_hdr(dlg, htype, hname); + if (!hdr) { + cap_status = PJSIP_DIALOG_CAP_UNKNOWN; + } else { + for (i=0; i<hdr->count; ++i) { + if (!pj_stricmp(&hdr->values[i], token)) { + cap_status = PJSIP_DIALOG_CAP_SUPPORTED; + break; + } + } + } + + pjsip_dlg_dec_lock(dlg); + + return cap_status; +} + + +/* + * Update remote capability of ACCEPT, ALLOW, and SUPPORTED from + * the received message. + */ +PJ_DEF(pj_status_t) pjsip_dlg_update_remote_cap(pjsip_dialog *dlg, + const pjsip_msg *msg, + pj_bool_t strict) +{ + pjsip_hdr_e htypes[] = + { PJSIP_H_ACCEPT, PJSIP_H_ALLOW, PJSIP_H_SUPPORTED }; + unsigned i; + + PJ_ASSERT_RETURN(dlg && msg, PJ_EINVAL); + + pjsip_dlg_inc_lock(dlg); + + /* Retrieve all specified capability header types */ + for (i = 0; i < PJ_ARRAY_SIZE(htypes); ++i) { + const pjsip_generic_array_hdr *hdr; + pj_status_t status; + + /* Find this capability type in the message */ + hdr = (const pjsip_generic_array_hdr*) + pjsip_msg_find_hdr(msg, htypes[i], NULL); + if (!hdr) { + /* Not found. + * If strict update is specified, remote this capability type + * from the capability list. + */ + if (strict) + pjsip_dlg_remove_remote_cap_hdr(dlg, htypes[i], NULL); + } else { + /* Found, a capability type may be specified in multiple headers, + * so combine all the capability tags/values into a temporary + * header. + */ + pjsip_generic_array_hdr tmp_hdr; + + /* Init temporary header */ + pjsip_generic_array_hdr_init(dlg->pool, &tmp_hdr, NULL); + pj_memcpy(&tmp_hdr, hdr, sizeof(pjsip_hdr)); + + while (hdr) { + unsigned j; + + /* Append the header content to temporary header */ + for(j=0; j<hdr->count && + tmp_hdr.count<PJSIP_GENERIC_ARRAY_MAX_COUNT; ++j) + { + tmp_hdr.values[tmp_hdr.count++] = hdr->values[j]; + } + + /* Get the next header for this capability */ + hdr = (const pjsip_generic_array_hdr*) + pjsip_msg_find_hdr(msg, htypes[i], hdr->next); + } + + /* Save this capability */ + status = pjsip_dlg_set_remote_cap_hdr(dlg, &tmp_hdr); + if (status != PJ_SUCCESS) { + pjsip_dlg_dec_lock(dlg); + return status; + } + } + } + + pjsip_dlg_dec_lock(dlg); + + return PJ_SUCCESS; +} + + +/* + * Get the value of the specified capability header field of remote. + */ +PJ_DEF(const pjsip_hdr*) pjsip_dlg_get_remote_cap_hdr(pjsip_dialog *dlg, + int htype, + const pj_str_t *hname) +{ + pjsip_hdr *hdr; + + /* Check arguments. */ + PJ_ASSERT_RETURN(dlg, NULL); + PJ_ASSERT_RETURN((htype != PJSIP_H_OTHER) || (hname && hname->slen), + NULL); + + pjsip_dlg_inc_lock(dlg); + + hdr = dlg->rem_cap_hdr.next; + while (hdr != &dlg->rem_cap_hdr) { + if ((htype != PJSIP_H_OTHER && htype == hdr->type) || + (htype == PJSIP_H_OTHER && pj_stricmp(&hdr->name, hname) == 0)) + { + pjsip_dlg_dec_lock(dlg); + return hdr; + } + hdr = hdr->next; + } + + pjsip_dlg_dec_lock(dlg); + + return NULL; +} + + +/* + * Set remote capability header from a SIP header containing array + * of capability tags/values. + */ +PJ_DEF(pj_status_t) pjsip_dlg_set_remote_cap_hdr( + pjsip_dialog *dlg, + const pjsip_generic_array_hdr *cap_hdr) +{ + pjsip_generic_array_hdr *hdr; + + /* Check arguments. */ + PJ_ASSERT_RETURN(dlg && cap_hdr, PJ_EINVAL); + + pjsip_dlg_inc_lock(dlg); + + /* Find the header. */ + hdr = (pjsip_generic_array_hdr*) + pjsip_dlg_get_remote_cap_hdr(dlg, cap_hdr->type, &cap_hdr->name); + + /* Quick compare if the capability is up to date */ + if (hdr && hdr->count == cap_hdr->count) { + unsigned i; + pj_bool_t uptodate = PJ_TRUE; + + for (i=0; i<hdr->count; ++i) { + if (pj_stricmp(&hdr->values[i], &cap_hdr->values[i])) + uptodate = PJ_FALSE; + } + + /* Capability is up to date, just return PJ_SUCCESS */ + if (uptodate) { + pjsip_dlg_dec_lock(dlg); + return PJ_SUCCESS; + } + } + + /* Remove existing capability header if any */ + if (hdr) + pj_list_erase(hdr); + + /* Add the new capability header */ + hdr = (pjsip_generic_array_hdr*) pjsip_hdr_clone(dlg->pool, cap_hdr); + hdr->type = cap_hdr->type; + pj_strdup(dlg->pool, &hdr->name, &cap_hdr->name); + pj_list_push_back(&dlg->rem_cap_hdr, hdr); + + pjsip_dlg_dec_lock(dlg); + + /* Done. */ + return PJ_SUCCESS; +} + +/* + * Remove a remote capability header. + */ +PJ_DEF(pj_status_t) pjsip_dlg_remove_remote_cap_hdr(pjsip_dialog *dlg, + int htype, + const pj_str_t *hname) +{ + pjsip_generic_array_hdr *hdr; + + /* Check arguments. */ + PJ_ASSERT_RETURN(dlg, PJ_EINVAL); + PJ_ASSERT_RETURN((htype != PJSIP_H_OTHER) || (hname && hname->slen), + PJ_EINVAL); + + pjsip_dlg_inc_lock(dlg); + + hdr = (pjsip_generic_array_hdr*) + pjsip_dlg_get_remote_cap_hdr(dlg, htype, hname); + if (!hdr) { + pjsip_dlg_dec_lock(dlg); + return PJ_ENOTFOUND; + } + + pj_list_erase(hdr); + + pjsip_dlg_dec_lock(dlg); + + return PJ_SUCCESS; +} diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index cc9eaad9..85db6d5f 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -1322,6 +1322,31 @@ PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id, return PJ_SUCCESS; } +/* + * Check if call remote peer support the specified capability. + */ +PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap( + pjsua_call_id call_id, + int htype, + const pj_str_t *hname, + const pj_str_t *token) +{ + pjsua_call *call; + pjsip_dialog *dlg; + pj_status_t status; + pjsip_dialog_cap_status cap_status; + + status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg); + if (status != PJ_SUCCESS) + return PJSIP_DIALOG_CAP_UNKNOWN; + + cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token); + + pjsip_dlg_dec_lock(dlg); + + return cap_status; +} + /* * Attach application specific data to the call. |