From 62f1ba21e766b422e3b49ad89601a30742b48a45 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 3 Jun 2010 10:41:32 +0000 Subject: Re #1089: - Added a feature in dialog to store and retrieve remote capabilities dug from the remote messages. - Added few APIs in dialog to query and update remote capabilities, also added an API in pjsua_call to query whether a capability is supported by remote. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3196 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/include/pjsip/sip_dialog.h | 128 +++++++++++++++++++++ pjsip/include/pjsua-lib/pjsua.h | 26 +++++ pjsip/src/pjsip/sip_dialog.c | 238 ++++++++++++++++++++++++++++++++++++++- pjsip/src/pjsua-lib/pjsua_call.c | 25 ++++ 4 files changed, 415 insertions(+), 2 deletions(-) (limited to 'pjsip') diff --git a/pjsip/include/pjsip/sip_dialog.h b/pjsip/include/pjsip/sip_dialog.h index 6ac44c30..5f954946 100644 --- a/pjsip/include/pjsip/sip_dialog.h +++ b/pjsip/include/pjsip/sip_dialog.h @@ -89,6 +89,26 @@ typedef enum pjsip_dialog_state } pjsip_dialog_state; +/** + * Dialog capability status. + */ +typedef enum pjsip_dialog_cap_status +{ + /** Capability is unsupported. */ + PJSIP_DIALOG_CAP_UNSUPPORTED = 0, + + /** Capability is supported */ + PJSIP_DIALOG_CAP_SUPPORTED = 1, + + /** + * Unknown capability status. This is usually because we lack the + * capability info which is retrieved from capability header specified + * in the dialog messages. + */ + PJSIP_DIALOG_CAP_UNKNOWN = 2 +} pjsip_dialog_cap_status; + + /** * This structure describes the dialog structure. Application MUST NOT * try to SET the values here directly, but instead it MUST use the @@ -127,6 +147,7 @@ struct pjsip_dialog pjsip_hdr inv_hdr; /**< Headers from hparam in dest URL */ pjsip_dlg_party local; /**< Local party info. */ pjsip_dlg_party remote; /**< Remote party info. */ + pjsip_hdr rem_cap_hdr;/**< List of remote capability header. */ pjsip_role_e role; /**< Initial role. */ pj_bool_t uac_has_2xx;/**< UAC has received 2xx response? */ pj_bool_t secure; /**< Use secure transport? */ @@ -611,6 +632,113 @@ PJ_DECL(pj_status_t) pjsip_dlg_respond( pjsip_dialog *dlg, const pjsip_msg_body *body ); +/** + * Check if remote peer have the specified capability as published + * in the dialog messages from remote peer. + * + * Notes: + * - The capability \a token lookup will apply exact match, but not + * case-sensitive, for example: "text/html" will not match + * "text / html" (notice the spaces). + * + * @param dlg The dialog. + * @param htype The header type to be checked, which value may be: + * - PJSIP_H_ACCEPT + * - PJSIP_H_ALLOW + * - PJSIP_H_SUPPORTED + * @param hname If htype specifies PJSIP_H_OTHER, then the header name + * must be supplied in this argument. Otherwise the value + * must be set to NULL. + * @param token The capability token to check. For example, if \a htype + * is PJSIP_H_ALLOW, then \a token specifies the method + * names; if \a htype is PJSIP_H_SUPPORTED, then \a token + * specifies the extension names such as "100rel". + * + * @return PJSIP_DIALOG_CAP_SUPPORTED if the specified capability + * is explicitly supported, see @pjsip_dialog_cap_status + * for more info. + */ +PJ_DECL(pjsip_dialog_cap_status) pjsip_dlg_remote_has_cap( + pjsip_dialog *dlg, + int htype, + const pj_str_t *hname, + const pj_str_t *token); + +/** + * Get the specified capability header from the remote capability headers + * stored in the dialog. + * + * @param dlg The dialog. + * @param htype The header type to be retrieved, which value may be: + * - PJSIP_H_ACCEPT + * - PJSIP_H_ALLOW + * - PJSIP_H_SUPPORTED + * @param hname If htype specifies PJSIP_H_OTHER, then the header name + * must be supplied in this argument. Otherwise the value + * must be set to NULL. + * + * @return The appropriate header, or NULL if the header is not + * available. + */ +PJ_DECL(const pjsip_hdr*) pjsip_dlg_get_remote_cap_hdr(pjsip_dialog *dlg, + int htype, + const pj_str_t *hname); + +/** + * Set remote capability from a SIP header containing array of capability + * tags/values. + * + * @param dlg The dialog. + * @param cap_hdr The SIP header. + * + * @return PJ_SUCCESS when successful, otherwise the appropriate + * error code will be returned. + */ +PJ_DECL(pj_status_t) pjsip_dlg_set_remote_cap_hdr( + pjsip_dialog *dlg, + const pjsip_generic_array_hdr *cap_hdr); + +/** + * Remove a remote capability header. + * + * @param dlg The dialog. + * @param htype The header type to be removed, which value may be: + * - PJSIP_H_ACCEPT + * - PJSIP_H_ALLOW + * - PJSIP_H_SUPPORTED + * @param hname If htype specifies PJSIP_H_OTHER, then the header name + * must be supplied in this argument. Otherwise the value + * must be set to NULL. + * + * @return PJ_SUCCESS when successful, otherwise the appropriate + * error code will be returned. + */ +PJ_DECL(pj_status_t) pjsip_dlg_remove_remote_cap_hdr(pjsip_dialog *dlg, + int htype, + const pj_str_t *hname); + +/** + * Update remote capabilities from a received message. The header types + * to be updated from the message will only be \a PJSIP_H_ACCEPT, + * \a PJSIP_H_ALLOW, and \a PJSIP_H_SUPPORTED. + * + * @param dlg The dialog. + * @param msg The received message. + * @param strict If this is set to PJ_TRUE, any header types missing + * from the message will cause removal of existing + * header types in the capability list. Otherwise, the + * capability list will not be modified when any header + * type is missing. + * + * @return PJ_SUCCESS when successful, otherwise the appropriate + * error code will be returned. + */ +PJ_DECL(pj_status_t) pjsip_dlg_update_remote_cap(pjsip_dialog *dlg, + const pjsip_msg *msg, + pj_bool_t strict); + + + /** * @} */ diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 58574e92..eb1beb8c 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -2820,6 +2820,32 @@ PJ_DECL(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id); PJ_DECL(pj_status_t) pjsua_call_get_info(pjsua_call_id call_id, pjsua_call_info *info); +/** + * Check if remote peer support the specified capability. + * + * @param call_id Call identification. + * @param htype The header type to be checked, which value may be: + * - PJSIP_H_ACCEPT + * - PJSIP_H_ALLOW + * - PJSIP_H_SUPPORTED + * @param hname If htype specifies PJSIP_H_OTHER, then the header + * name must be supplied in this argument. Otherwise the + * value must be set to NULL. + * @param token The capability token to check. For example, if \a + * htype is PJSIP_H_ALLOW, then \a token specifies the + * method names; if \a htype is PJSIP_H_SUPPORTED, then + * \a token specifies the extension names such as + * "100rel". + * + * @return PJSIP_DIALOG_CAP_SUPPORTED if the specified capability + * is explicitly supported, see @pjsip_dialog_cap_status + * for more info. + */ +PJ_DECL(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); /** * Attach application specific data to the call. Application can then 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; iusage_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; icount; ++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; jcount && + tmp_hdr.countvalues[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; icount; ++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. -- cgit v1.2.3