Index: /pjproject/trunk/pjsip/include/pjsip/sip_dialog.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_dialog.h	(revision 3195)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_dialog.h	(revision 3196)
@@ -91,4 +91,24 @@
 
 /**
+ * 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
@@ -128,4 +148,5 @@
     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?	    */
@@ -613,4 +634,111 @@
 
 /**
+ * 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);
+
+
+
+/**
  * @}
  */
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 3195)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 3196)
@@ -2821,4 +2821,30 @@
 					 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);
 
 /**
Index: /pjproject/trunk/pjsip/src/pjsip/sip_dialog.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_dialog.c	(revision 3195)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_dialog.c	(revision 3196)
@@ -47,4 +47,5 @@
 static const pj_str_t HCONTACT = { "Contact", 7 };
 
+
 PJ_DEF(pj_bool_t) pjsip_method_creates_dialog(const pjsip_method *m)
 {
@@ -90,4 +91,5 @@
 
     pj_list_init(&dlg->inv_hdr);
+    pj_list_init(&dlg->rem_cap_hdr);
 
     status = pj_mutex_create_recursive(pool, dlg->obj_name, &dlg->mutex_);
@@ -523,4 +525,7 @@
     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. */
@@ -1775,8 +1780,15 @@
 	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:
@@ -1860,5 +1872,4 @@
 	dlg_update_routeset(dlg, rdata);
     }
-
 
     /* Pass to dialog usages. */
@@ -1963,2 +1974,225 @@
 }
 
+
+/*
+ * 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;
+}
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 3195)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 3196)
@@ -1321,4 +1321,29 @@
 
     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;
 }
 
