summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2010-06-03 10:41:32 +0000
committerNanang Izzuddin <nanang@teluu.com>2010-06-03 10:41:32 +0000
commit62f1ba21e766b422e3b49ad89601a30742b48a45 (patch)
treeda0707f46ebad43e858dc453f859d3293f22002f /pjsip
parentf5a2d53060169500258cdc3bdebd76fe1481ebaf (diff)
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
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/include/pjsip/sip_dialog.h128
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h26
-rw-r--r--pjsip/src/pjsip/sip_dialog.c238
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c25
4 files changed, 415 insertions, 2 deletions
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
@@ -90,6 +90,26 @@ typedef enum 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
* appropriate dialog API. The dialog declaration only needs to be made
@@ -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? */
@@ -612,6 +633,113 @@ PJ_DECL(pj_status_t) pjsip_dlg_respond( pjsip_dialog *dlg,
/**
+ * 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: <tt>"text/html"</tt> will not match
+ * <tt>"text / html"</tt> (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; 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.