Added tel: uri and user-defined uri parser

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@82 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip/build/pjsip_core.dsp b/pjsip/build/pjsip_core.dsp
index 92a6353..8893fa0 100644
--- a/pjsip/build/pjsip_core.dsp
+++ b/pjsip/build/pjsip_core.dsp
@@ -41,7 +41,7 @@
 # PROP Intermediate_Dir ".\output\pjsip-core-i386-win32-vc6-release"

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c

-# ADD CPP /nologo /MD /W4 /Zi /O2 /Oy /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /c

+# ADD CPP /nologo /MD /W4 /Zi /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /c

 # SUBTRACT CPP /YX

 # ADD BASE RSC /l 0x409 /d "NDEBUG"

 # ADD RSC /l 0x409 /d "NDEBUG"

@@ -119,6 +119,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=..\src\pjsip\sip_tel_uri.c

+# End Source File

+# Begin Source File

+

 SOURCE=..\src\pjsip\sip_transaction.c

 # End Source File

 # Begin Source File

@@ -199,6 +203,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=..\include\pjsip\sip_tel_uri.h

+# End Source File

+# Begin Source File

+

 SOURCE=..\include\pjsip\sip_transaction.h

 # End Source File

 # Begin Source File

diff --git a/pjsip/include/pjsip/print_util.h b/pjsip/include/pjsip/print_util.h
index c72d1ab..0951612 100644
--- a/pjsip/include/pjsip/print_util.h
+++ b/pjsip/include/pjsip/print_util.h
@@ -55,7 +55,7 @@
 	  if (str2.slen) { \
 	    if (len1+str2.slen >= (endbuf-buf)) return -1; \
 	    pj_memcpy(buf,str1,len1); \
-	    printed=pj_strncpy2_escape(buf,&str2,(endbuf-buf-len1),&unres);\
+	    printed=pj_strncpy2_escape(buf+len1,&str2,(endbuf-buf-len1),&unres);\
 	    if (printed < 0) return -1; \
 	    buf += (printed+len1); \
 	  } \
diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
index 9844191..972a080 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -75,6 +75,12 @@
 /* Module related constants. */
 #define PJSIP_MAX_MODULE		8
 
+/* Maximum header types. */
+#define PJSIP_MAX_HEADER_TYPES		64
+
+/* Maximum URI types. */
+#define PJSIP_MAX_URI_TYPES		4
+
 /*****************************************************************************
  *  Default timeout settings, in miliseconds. 
  */
diff --git a/pjsip/include/pjsip/sip_parser.h b/pjsip/include/pjsip/sip_parser.h
index d6e46e5..b2e4969 100644
--- a/pjsip/include/pjsip/sip_parser.h
+++ b/pjsip/include/pjsip/sip_parser.h
@@ -59,7 +59,7 @@
 /**
  * Parser syntax error exception value.
  */
-#define PJSIP_SYN_ERR_EXCEPTION	1
+extern int PJSIP_SYN_ERR_EXCEPTION;
 
 /**
  * This structure is used to get error reporting from parser.
@@ -109,7 +109,8 @@
  * Most of the specification of header parser handler (pjsip_parse_hdr_func)
  * also applies here (except the separator part).
  */
-typedef void* (pjsip_parse_uri_func)(pj_scanner *scanner, pj_pool_t *pool);
+typedef void* (pjsip_parse_uri_func)(pj_scanner *scanner, pj_pool_t *pool,
+				     pj_bool_t parse_params);
 
 /**
  * Register header parser handler. The parser handler MUST follow the 
@@ -148,7 +149,7 @@
  *
  * @return		zero on success.
  */
-PJ_DECL(pj_status_t) pjsip_register_uri_parser( const char *scheme,
+PJ_DECL(pj_status_t) pjsip_register_uri_parser( char *scheme,
 					        pjsip_parse_uri_func *func);
 
 /**
diff --git a/pjsip/include/pjsip/sip_tel_uri.h b/pjsip/include/pjsip/sip_tel_uri.h
new file mode 100644
index 0000000..5ade09c
--- /dev/null
+++ b/pjsip/include/pjsip/sip_tel_uri.h
@@ -0,0 +1,71 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __PJSIP_TEL_URI_H__
+#define __PJSIP_TEL_URI_H__
+
+/**
+ * @file sip_tel_uri.h
+ * @brief Tel: URI
+ */
+
+#include <pjsip/sip_uri.h>
+
+
+PJ_BEGIN_DECL
+
+/**
+ * tel: URI.
+ */
+typedef struct pjsip_tel_uri
+{
+    pjsip_uri_vptr *vptr;	/**< Pointer to virtual function table.	*/
+    pj_str_t	    number;	/**< Global or local phone number	*/
+    pj_str_t	    context;	/**< Phone context (for local number).	*/
+    pj_str_t	    ext_param;	/**< Extension param.			*/
+    pj_str_t	    isub_param;	/**< ISDN sub-address param.		*/
+    pjsip_param	    other_param;/**< Other parameter.			*/
+} pjsip_tel_uri;
+
+
+/**
+ * Create a new tel: URI.
+ *
+ * @param pool	    The pool.
+ *
+ * @return	    New instance of tel: URI.
+ */
+PJ_DECL(pjsip_tel_uri*) pjsip_tel_uri_create(pj_pool_t *pool);
+
+/**
+ * This function compares two numbers for equality, according to rules as
+ * specified in RFC 3966.
+ *
+ * @param nb1	    The first number.
+ * @param nb2	    The second number.
+ *
+ * @return	    Zero if equal, -1 if nb1 is less than nb2, or +1 if
+ *		    nb1 is greater than nb2.
+ */
+PJ_DECL(int) pjsip_tel_nb_cmp(const pj_str_t *nb1, const pj_str_t *nb2);
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJSIP_TEL_URI_H__ */
diff --git a/pjsip/include/pjsip/sip_uri.h b/pjsip/include/pjsip/sip_uri.h
index 0e13b30..63f0dfe 100644
--- a/pjsip/include/pjsip/sip_uri.h
+++ b/pjsip/include/pjsip/sip_uri.h
@@ -27,6 +27,7 @@
 #include <pjsip/sip_types.h>
 #include <pjsip/sip_config.h>
 #include <pj/list.h>
+#include <pjlib-util/scanner.h>
 
 PJ_BEGIN_DECL
 
@@ -102,12 +103,16 @@
  * @param param_list	The parameter list.
  * @param buf		Buffer.
  * @param size		Size of buffer.
+ * @param pname_unres	Specification of allowed characters in pname.
+ * @param pvalue_unres	Specification of allowed characters in pvalue.
  * @param sep		Separator character (either ';' or ',').
  *
  * @return		The number of bytes printed, or -1 on errr.
  */
 PJ_DECL(pj_ssize_t) pjsip_param_print_on(const pjsip_param *param_list,
 					 char *buf, pj_size_t size,
+					 const pj_cis_t *pname_unres,
+					 const pj_cis_t *pvalue_unres,
 					 int sep);
 
 /**
diff --git a/pjsip/include/pjsip_core.h b/pjsip/include/pjsip_core.h
index 6ae1c2c..8ae286a 100644
--- a/pjsip/include/pjsip_core.h
+++ b/pjsip/include/pjsip_core.h
@@ -28,6 +28,7 @@
 #include <pjsip/sip_msg.h>
 #include <pjsip/sip_parser.h>
 #include <pjsip/sip_resolve.h>
+#include <pjsip/sip_tel_uri.h>
 #include <pjsip/sip_transaction.h>
 #include <pjsip/sip_transport.h>
 #include <pjsip/sip_uri.h>
diff --git a/pjsip/src/pjsip/sip_auth_msg.c b/pjsip/src/pjsip/sip_auth_msg.c
index 366bff0..84c41cc 100644
--- a/pjsip/src/pjsip/sip_auth_msg.c
+++ b/pjsip/src/pjsip/sip_auth_msg.c
@@ -18,6 +18,7 @@
  */
 #include <pjsip/sip_auth_msg.h>
 #include <pjsip/sip_auth_parser.h>
+#include <pjsip/sip_parser.h>
 #include <pj/pool.h>
 #include <pj/list.h>
 #include <pj/string.h>
@@ -79,7 +80,9 @@
     copy_advance_pair(buf, ", qop=", 6, cred->qop);
     copy_advance_pair(buf, ", nc=", 5, cred->nc);
     
-    printed = pjsip_param_print_on(&cred->other_param, buf, endbuf-buf, ',');
+    printed = pjsip_param_print_on(&cred->other_param, buf, endbuf-buf, 
+				   &pjsip_PARAM_CHAR_SPEC, 
+				   &pjsip_PARAM_CHAR_SPEC, ',');
     if (printed < 0)
 	return -1;
     buf += printed;
@@ -231,7 +234,9 @@
     copy_advance_pair(buf, ",algorithm=", 11, chal->algorithm);
     copy_advance_pair_quote_cond(buf, ",qop=", 5, chal->qop, '"', '"');
     
-    printed = pjsip_param_print_on(&chal->other_param, buf, endbuf-buf, ',');
+    printed = pjsip_param_print_on(&chal->other_param, buf, endbuf-buf, 
+				   &pjsip_PARAM_CHAR_SPEC, 
+				   &pjsip_PARAM_CHAR_SPEC, ',');
     if (printed < 0)
 	return -1;
     buf += printed;
diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c
index 011b335..dae3d5b 100644
--- a/pjsip/src/pjsip/sip_endpoint.c
+++ b/pjsip/src/pjsip/sip_endpoint.c
@@ -104,8 +104,13 @@
 static void endpt_transport_callback(pjsip_endpoint*, 
 				     pj_status_t, pjsip_rx_data*);
 
+/* Defined in sip_parser.c */
 void init_sip_parser(void);
 
+/* Defined in sip_tel_uri.c */
+pj_status_t pjsip_tel_uri_subsys_init(void);
+
+
 /*
  * This is the global handler for memory allocation failure, for pools that
  * are created by the endpoint (by default, all pools ARE allocated by 
@@ -377,6 +382,9 @@
     /* Init parser. */
     init_sip_parser();
 
+    /* Init tel: uri */
+    pjsip_tel_uri_subsys_init();
+
     /* Get name. */
     if (name != NULL) {
 	pj_str_t temp;
diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c
index 6f963ab..2dc83dd 100644
--- a/pjsip/src/pjsip/sip_msg.c
+++ b/pjsip/src/pjsip/sip_msg.c
@@ -17,6 +17,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  */
 #include <pjsip/sip_msg.h>
+#include <pjsip/sip_parser.h>
 #include <pjsip/print_util.h>
 #include <pj/string.h>
 #include <pj/pool.h>
@@ -927,7 +928,9 @@
 	    buf += printed + 9;
 	}
 
-	printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf,';');
+	printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf,
+				       &pjsip_PARAM_CHAR_SPEC,
+				       &pjsip_PARAM_CHAR_SPEC, ';');
 	if (printed < 0)
 	    return printed;
 	buf += printed;
@@ -1120,7 +1123,9 @@
 
     copy_advance_pair(buf, ";tag=", 5, hdr->tag);
 
-    printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, ';');
+    printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, 
+				   &pjsip_PARAM_CHAR_SPEC,
+				   &pjsip_PARAM_CHAR_SPEC, ';');
     if (printed < 0)
 	return -1;
     buf += printed;
@@ -1243,7 +1248,9 @@
 	return -1;
     buf += printed;
 
-    printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, ';');
+    printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, 
+				   &pjsip_PARAM_CHAR_SPEC, 
+				   &pjsip_PARAM_CHAR_SPEC, ';');
     if (printed < 0)
 	return -1;
     buf += printed;
@@ -1406,7 +1413,9 @@
     copy_advance_pair(buf, ";received=", 10, hdr->recvd_param);
     copy_advance_pair(buf, ";branch=", 8, hdr->branch_param);
     
-    printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, ';');
+    printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, 
+				   &pjsip_PARAM_CHAR_SPEC,
+				   &pjsip_PARAM_CHAR_SPEC, ';');
     if (printed < 0)
 	return -1;
     buf += printed;
diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c
index 1b430de..7b16357 100644
--- a/pjsip/src/pjsip/sip_parser.c
+++ b/pjsip/src/pjsip/sip_parser.c
@@ -49,14 +49,15 @@
 #define HDR_CHAR	    HNV_UNRESERVED UNRESERVED ESCAPED
 
 #define PJSIP_VERSION		"SIP/2.0"
-#define PJSIP_SYN_ERR_EXCEPTION	1
 
 #define UNREACHED(expr)
 
-//#define IS_NEWLINE(c)	((c)=='\r' || (c)=='\n')
-#define IS_NEWLINE(c)	((c)=='\r')
+#define IS_NEWLINE(c)	((c)=='\r' || (c)=='\n')
 #define IS_SPACE(c)	((c)==' ' || (c)=='\t')
 
+/*
+ * Header parser records.
+ */
 typedef struct handler_rec
 {
     char		  hname[PJSIP_MAX_HNAME_LEN+1];
@@ -65,13 +66,27 @@
     pjsip_parse_hdr_func *handler;
 } handler_rec;
 
-static handler_rec handler[127];
+static handler_rec handler[PJSIP_MAX_HEADER_TYPES];
 static unsigned handler_count;
 static int parser_is_initialized;
 
 /*
+ * URI parser records.
+ */
+typedef struct uri_parser_rec
+{
+    pj_str_t		     scheme;
+    pjsip_parse_uri_func    *parse;
+} uri_parser_rec;
+
+static uri_parser_rec uri_handler[PJSIP_MAX_URI_TYPES];
+static unsigned uri_handler_count;
+
+/*
  * Global vars (also extern).
  */
+int PJSIP_SYN_ERR_EXCEPTION;
+
 const pj_str_t  pjsip_USER_STR      = { "user", 4};
 const pj_str_t  pjsip_METHOD_STR    = { "method", 6};
 const pj_str_t  pjsip_TRANSPORT_STR = { "transport", 9};
@@ -246,6 +261,17 @@
 
     initialized = 1;
 
+    /*
+     * Syntax error exception number.
+     */
+    status = pj_exception_id_alloc("PJSIP: syntax error", 
+				   &PJSIP_SYN_ERR_EXCEPTION);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+    /*
+     * Init character input spec (cis)
+     */
+
     pj_cis_buf_init(&cis_buf);
 
     status = pj_cis_init(&cis_buf, &pjsip_DIGIT_SPEC);
@@ -309,6 +335,20 @@
     pj_cis_add_str( &pjsip_DISPLAY_SPEC, ":\r\n<");
     pj_cis_invert(&pjsip_DISPLAY_SPEC);
 
+    /*
+     * Register URI parsers.
+     */
+
+    status = pjsip_register_uri_parser("sip", &int_parse_sip_url);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+    status = pjsip_register_uri_parser("sips", &int_parse_sip_url);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+    /*
+     * Register header parsers.
+     */
+
     status = pjsip_register_hdr_parser( "Accept", NULL, &parse_hdr_accept);
     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
@@ -373,7 +413,10 @@
     status = pjsip_register_hdr_parser( "Via", NULL, &parse_hdr_via);
     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
-    /* Register auth parser. */
+    /* 
+     * Register auth parser. 
+     */
+
     status = pjsip_auth_init_parser();
 
     return status;
@@ -382,13 +425,10 @@
 void init_sip_parser(void)
 {
     if (!parser_is_initialized) {
-	/* Prevent race cond. */
-	pj_enter_critical_section();
 	if (!parser_is_initialized) {
 	    init_parser();
 	    parser_is_initialized = 1;
 	}
-	pj_leave_critical_section();
     }
 }
 
@@ -398,9 +438,9 @@
  * - >0 if handler is 'greater' than header name.
  */
 PJ_INLINE(int) compare_handler( const handler_rec *r1, 
-			    const char *name, 
-			    pj_size_t name_len,
-			    pj_uint32_t hash )
+				const char *name, 
+				pj_size_t name_len,
+				pj_uint32_t hash )
 {
     PJ_UNUSED_ARG(name_len);
 
@@ -538,6 +578,31 @@
     return comp==0 ? first->handler : NULL;
 }
 
+/* Find URI handler. */
+static pjsip_parse_uri_func* find_uri_handler(const pj_str_t *scheme)
+{
+    unsigned i;
+    for (i=0; i<uri_handler_count; ++i) {
+	if (pj_stricmp_alnum(&uri_handler[i].scheme, scheme)==0)
+	    return uri_handler[i].parse;
+    }
+    return NULL;
+}
+
+/* Register URI parser. */
+PJ_DEF(pj_status_t) pjsip_register_uri_parser( char *scheme,
+					       pjsip_parse_uri_func *func)
+{
+    if (uri_handler_count >= PJ_ARRAY_SIZE(uri_handler))
+	return PJ_ETOOMANY;
+
+    uri_handler[uri_handler_count].scheme = pj_str((char*)scheme);
+    uri_handler[uri_handler_count].parse = func;
+    ++uri_handler_count;
+
+    return PJ_SUCCESS;
+}
+
 /* Public function to parse SIP message. */
 PJ_DEF(pjsip_msg*) pjsip_parse_msg( pj_pool_t *pool, 
                                     char *buf, pj_size_t size,
@@ -547,8 +612,6 @@
     pj_scanner scanner;
     pjsip_parse_ctx context;
 
-    init_sip_parser();
-
     pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, 
                  &on_syntax_error);
 
@@ -569,8 +632,6 @@
     pj_scanner scanner;
     pjsip_parse_ctx context;
 
-    init_sip_parser();
-
     pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, 
                  &on_syntax_error);
 
@@ -625,8 +686,6 @@
 	    pj_scanner scanner;
 	    PJ_USE_EXCEPTION;
 
-	    init_sip_parser();
-
 	    pj_scan_init(&scanner, (char*)line, hdr_end-line, 
 			 PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error);
 
@@ -694,8 +753,6 @@
     pjsip_uri *uri = NULL;
     PJ_USE_EXCEPTION;
 
-    init_sip_parser();
-
     pj_scan_init(&scanner, buf, size, 0, &on_syntax_error);
 
     
@@ -871,6 +928,10 @@
 		} while (IS_SPACE(*scanner->curptr));
 	    }
 
+	    /* Restore flag. Flag may be set in int_parse_sip_url() */
+	    scanner->skip_ws = PJ_SCAN_AUTOSKIP_WS_HEADER;
+
+	    /* Continue parse next header, if any. */
 	    if (!pj_scan_is_eof(scanner) && !IS_NEWLINE(*scanner->curptr)) {
 		goto parse_headers;
 	    }
@@ -1018,19 +1079,17 @@
 
 	next_ch = pj_scan_peek( scanner, &pjsip_DISPLAY_SPEC, &scheme);
 
-	if (next_ch==':' && 
-            (parser_stricmp(scheme, pjsip_SIP_STR)==0 || 
-             parser_stricmp(scheme, pjsip_SIPS_STR)==0)) 
-	{
-	    uri = (pjsip_uri*)
-                int_parse_sip_url( scanner, pool, 
-				   (opt & PJSIP_PARSE_URI_IN_FROM_TO_HDR)== 0);
+	if (next_ch==':') {
+	    pjsip_parse_uri_func *func = find_uri_handler(&scheme);
 
-	} else if (next_ch==':') {
+	    if (func == NULL) {
+		/* Unsupported URI scheme */
+		PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+	    }
 
-	    /* Not supported. */
-	    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
-	    UNREACHED({return NULL; /* Not reached. */});
+	    uri = (*func)( scanner, pool, 
+			  (opt & PJSIP_PARSE_URI_IN_FROM_TO_HDR)== 0);
+
 
 	} else {
 	    uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
@@ -1062,6 +1121,7 @@
     } else {
 	pj_str_t scheme;
 	int colon;
+	pjsip_parse_uri_func *func;
 
 	/* Get scheme. */
 	colon = pj_scan_peek(scanner, &pjsip_TOKEN_SPEC, &scheme);
@@ -1069,16 +1129,12 @@
 	    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
 	}
 
-	if ((parser_stricmp(scheme, pjsip_SIP_STR)==0 || 
-            parser_stricmp(scheme, pjsip_SIPS_STR)==0)) 
-	{
-	    return (pjsip_uri*)int_parse_sip_url( scanner, pool, parse_params);
-
-	} else if (parser_stricmp(scheme, pjsip_TEL_STR)==0) {
-	    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
-	    UNREACHED({ return NULL; /* Not reached. */ })
+	func = find_uri_handler(&scheme);
+	if (func)  {
+	    return (pjsip_uri*)(*func)(scanner, pool, parse_params);
 
 	} else {
+	    /* Unsupported URI scheme */
 	    PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
 	    UNREACHED({ return NULL; /* Not reached. */ })
 	}
@@ -1773,8 +1829,6 @@
     pjsip_parse_ctx context;
     PJ_USE_EXCEPTION;
 
-    init_sip_parser();
-
     pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, 
                  &on_syntax_error);
 
diff --git a/pjsip/src/pjsip/sip_tel_uri.c b/pjsip/src/pjsip/sip_tel_uri.c
new file mode 100644
index 0000000..4563f6d
--- /dev/null
+++ b/pjsip/src/pjsip/sip_tel_uri.c
@@ -0,0 +1,416 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjsip/sip_tel_uri.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/print_util.h>
+#include <pj/pool.h>
+#include <pj/assert.h>
+#include <pj/string.h>
+#include <pj/ctype.h>
+#include <pj/except.h>
+#include <pjlib-util/string.h>
+#include <pjlib-util/scanner.h>
+
+#define ALPHA
+#define DIGITS		    "0123456789"
+#define HEX		    "aAbBcCdDeEfF"
+#define HEX_DIGITS	    DIGITS HEX
+#define VISUAL_SEP	    "-.()"
+#define PHONE_DIGITS	    DIGITS VISUAL_SEP
+#define GLOBAL_DIGITS	    "+" PHONE_DIGITS
+#define LOCAL_DIGITS	    HEX_DIGITS "*#" VISUAL_SEP
+#define NUMBER_SPEC	    LOCAL_DIGITS GLOBAL_DIGITS
+#define PHONE_CONTEXT	    ALPHA GLOBAL_DIGITS
+//#define RESERVED	    ";/?:@&=+$,"
+#define RESERVED	    "/:@&$,+"
+#define MARK		    "-_.!~*'()"
+#define UNRESERVED	    ALPHA DIGITS MARK
+#define ESCAPED		    "%"
+#define URIC		    RESERVED UNRESERVED ESCAPED "[]+"
+#define PARAM_UNRESERVED    "[]/:&+$"
+#define PARAM_CHAR	    PARAM_UNRESERVED UNRESERVED ESCAPED
+
+static pj_cis_buf_t cis_buf;
+static pj_cis_t pjsip_TEL_NUMBER_SPEC;
+static pj_cis_t pjsip_TEL_EXT_VALUE_SPEC;
+static pj_cis_t pjsip_TEL_PHONE_CONTEXT_SPEC;
+static pj_cis_t pjsip_TEL_URIC_SPEC;
+static pj_cis_t pjsip_TEL_VISUAL_SEP_SPEC;
+static pj_cis_t pjsip_TEL_PNAME_SPEC;
+static pj_cis_t pjsip_TEL_PVALUE_SPEC;
+static pj_cis_t pjsip_TEL_PARSING_PVALUE_SPEC;
+
+static pj_str_t pjsip_ISUB_STR = { "isub", 4 };
+static pj_str_t pjsip_EXT_STR = { "ext", 3 };
+static pj_str_t pjsip_PH_CTX_STR = { "phone-context", 13 };
+
+
+static const pj_str_t *tel_uri_get_scheme( const pjsip_tel_uri* );
+static void *tel_uri_get_uri( pjsip_tel_uri* );
+static int tel_uri_print( pjsip_uri_context_e context,
+			  const pjsip_tel_uri *url, 
+			  char *buf, pj_size_t size);
+static int tel_uri_cmp( pjsip_uri_context_e context,
+			const pjsip_tel_uri *url1, const pjsip_tel_uri *url2);
+static pjsip_tel_uri* tel_uri_clone(pj_pool_t *pool, const pjsip_tel_uri *rhs);
+static pjsip_tel_uri *tel_uri_parse( pj_scanner *scanner, pj_pool_t *pool,
+				     pj_bool_t parse_params);
+
+#ifdef __GNUC__
+#  define HAPPY_FLAG	(void*)
+#else
+#  define HAPPY_FLAG
+#endif
+
+static pjsip_uri_vptr tel_uri_vptr = 
+{
+    HAPPY_FLAG &tel_uri_get_scheme,
+    HAPPY_FLAG &tel_uri_get_uri,
+    HAPPY_FLAG &tel_uri_print,
+    HAPPY_FLAG &tel_uri_cmp,
+    HAPPY_FLAG &tel_uri_clone
+};
+
+
+PJ_DEF(pjsip_tel_uri*) pjsip_tel_uri_create(pj_pool_t *pool)
+{
+    pjsip_tel_uri *uri = pj_pool_zalloc(pool, sizeof(pjsip_tel_uri));
+    uri->vptr = &tel_uri_vptr;
+    pj_list_init(&uri->other_param);
+    return uri;
+}
+
+
+static const pj_str_t *tel_uri_get_scheme( const pjsip_tel_uri *uri )
+{
+    PJ_UNUSED_ARG(uri);
+    return &pjsip_TEL_STR;
+}
+
+static void *tel_uri_get_uri( pjsip_tel_uri *uri )
+{
+    return uri;
+}
+
+
+pj_status_t pjsip_tel_uri_subsys_init(void)
+{
+    pj_status_t status;
+
+    pj_cis_buf_init(&cis_buf);
+
+    status = pj_cis_init(&cis_buf, &pjsip_TEL_EXT_VALUE_SPEC);
+    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+    pj_cis_add_str(&pjsip_TEL_EXT_VALUE_SPEC, PHONE_DIGITS);
+
+    status = pj_cis_init(&cis_buf, &pjsip_TEL_NUMBER_SPEC);
+    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+    pj_cis_add_str(&pjsip_TEL_NUMBER_SPEC, NUMBER_SPEC);
+
+    status = pj_cis_init(&cis_buf, &pjsip_TEL_VISUAL_SEP_SPEC);
+    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+    pj_cis_add_str(&pjsip_TEL_VISUAL_SEP_SPEC, VISUAL_SEP);
+
+    status = pj_cis_init(&cis_buf, &pjsip_TEL_PHONE_CONTEXT_SPEC);
+    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+    pj_cis_add_alpha(&pjsip_TEL_PHONE_CONTEXT_SPEC);
+    pj_cis_add_num(&pjsip_TEL_PHONE_CONTEXT_SPEC);
+    pj_cis_add_str(&pjsip_TEL_PHONE_CONTEXT_SPEC, PHONE_CONTEXT);
+
+    status = pj_cis_init(&cis_buf, &pjsip_TEL_URIC_SPEC);
+    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+    pj_cis_add_alpha(&pjsip_TEL_URIC_SPEC);
+    pj_cis_add_num(&pjsip_TEL_URIC_SPEC);
+    pj_cis_add_str(&pjsip_TEL_URIC_SPEC, URIC);
+
+    status = pj_cis_init(&cis_buf, &pjsip_TEL_PNAME_SPEC);
+    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+    pj_cis_add_alpha(&pjsip_TEL_PNAME_SPEC);
+    pj_cis_add_num(&pjsip_TEL_PNAME_SPEC);
+    pj_cis_add_str(&pjsip_TEL_PNAME_SPEC, "-");
+
+    status = pj_cis_init(&cis_buf, &pjsip_TEL_PVALUE_SPEC);
+    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+    pj_cis_add_alpha(&pjsip_TEL_PVALUE_SPEC);
+    pj_cis_add_num(&pjsip_TEL_PVALUE_SPEC);
+    pj_cis_add_str(&pjsip_TEL_PVALUE_SPEC, PARAM_CHAR);
+
+    status = pj_cis_dup(&pjsip_TEL_PARSING_PVALUE_SPEC, &pjsip_TEL_URIC_SPEC);
+    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+    pj_cis_add_cis(&pjsip_TEL_PARSING_PVALUE_SPEC, &pjsip_TEL_PVALUE_SPEC);
+    pj_cis_add_str(&pjsip_TEL_PARSING_PVALUE_SPEC, "=");
+
+    status = pjsip_register_uri_parser("tel", &tel_uri_parse);
+    PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+
+    return PJ_SUCCESS;
+}
+
+/* Print tel: URI */
+static int tel_uri_print( pjsip_uri_context_e context,
+			  const pjsip_tel_uri *uri, 
+			  char *buf, pj_size_t size)
+{
+    int printed;
+    char *startbuf = buf;
+    char *endbuf = buf+size;
+
+    PJ_UNUSED_ARG(context);
+
+    /* Print scheme. */
+    copy_advance(buf, pjsip_TEL_STR);
+    *buf++ = ':';
+
+    /* Print number. */
+    copy_advance_escape(buf, uri->number, pjsip_TEL_NUMBER_SPEC);
+
+    /* ISDN sub-address or extension must appear first. */
+
+    /* Extension param. */
+    copy_advance_pair_escape(buf, ";ext=", 5, uri->ext_param, 
+			     pjsip_TEL_EXT_VALUE_SPEC);
+
+    /* ISDN sub-address. */
+    copy_advance_pair_escape(buf, ";isub=", 6, uri->isub_param, 
+			     pjsip_TEL_URIC_SPEC);
+
+    /* Followed by phone context, if present. */
+    copy_advance_pair_escape(buf, ";phone-context=", 15, uri->context, 
+			     pjsip_TEL_PHONE_CONTEXT_SPEC);
+
+
+    /* Print other parameters. */
+    printed = pjsip_param_print_on(&uri->other_param, buf, (endbuf-buf), 
+				   &pjsip_TEL_PNAME_SPEC, 
+				   &pjsip_TEL_PVALUE_SPEC, ';');
+    if (printed < 0)
+	return -1;
+    buf += printed;
+
+    return (buf-startbuf);
+}
+
+/* Compare two numbers, according to RFC 3966:
+ *  - both must be either local or global numbers.
+ *  - The 'global-number-digits' and the 'local-number-digits' must be
+ *    equal, after removing all visual separators.
+ */
+PJ_DEF(int) pjsip_tel_nb_cmp(const pj_str_t *number1, const pj_str_t *number2)
+{
+    const char *s1 = number1->ptr,
+	       *e1 = number1->ptr + number1->slen,
+	       *s2 = number2->ptr,
+	       *e2 = number2->ptr + number2->slen;
+
+    /* Compare each number, ignoreing visual separators. */
+    while (s1!=e1 && s2!=e2) {
+	int diff;
+
+	if (pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s1)) {
+	    ++s1;
+	    continue;
+	}
+	if (pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s2)) {
+	    ++s2;
+	    continue;
+	}
+
+	diff = pj_tolower(*s1) - pj_tolower(*s2);
+	if (!diff) {
+	    ++s1, ++s2;
+	    continue;
+	} else
+	    return diff;
+    }
+
+    /* Exhaust remaining visual separators. */
+    while (s1!=e1 && pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s1))
+	++s1;
+    while (s2!=e2 && pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s2))
+	++s2;
+
+    if (s1==e1 && s2==e2)
+	return 0;
+    else if (s1==e1)
+	return -1;
+    else
+	return 1;
+}
+
+/* Compare two tel: URI */
+static int tel_uri_cmp( pjsip_uri_context_e context,
+			const pjsip_tel_uri *url1, const pjsip_tel_uri *url2)
+{
+    int result;
+
+    PJ_UNUSED_ARG(context);
+
+    /* Scheme must match. */
+    if (url1->vptr != url2->vptr)
+	return -1;
+
+    /* Compare number. */
+    result = pjsip_tel_nb_cmp(&url1->number, &url2->number);
+    if (result != 0)
+	return result;
+
+    /* Compare phone-context as hostname or as as global nb. */
+    if (url1->context.slen) {
+	if (*url1->context.ptr != '+')
+	    result = pj_stricmp(&url1->context, &url2->context);
+	else
+	    result = pjsip_tel_nb_cmp(&url1->context, &url2->context);
+
+	if (result != 0)
+	    return result;
+
+    } else if (url2->context.slen)
+	return -1;
+
+    /* Compare extension. */
+    if (url1->ext_param.slen) {
+	result = pjsip_tel_nb_cmp(&url1->ext_param, &url2->ext_param);
+	if (result != 0)
+	    return result;
+    }
+
+    /* Compare isub bytes by bytes. */
+    if (url1->isub_param.slen) {
+	result = pj_stricmp(&url1->isub_param, &url2->isub_param);
+	if (result != 0)
+	    return result;
+    }
+
+    /* Other parameters are compared regardless of the order.
+     * If one URI has parameter not found in the other URI, the URIs are
+     * not equal.
+     */
+    if (url1->other_param.next != &url1->other_param) {
+	const pjsip_param *p1, *p2;
+	int cnt1 = 0, cnt2 = 0;
+
+	p1 = url1->other_param.next;
+	while (p1 != &url1->other_param) {
+	    p2 = pjsip_param_cfind(&url2->other_param, &p1->name);
+	    if (!p2 )
+		return 1;
+
+	    result = pj_stricmp(&p1->value, &p2->value);
+	    if (result != 0)
+		return result;
+
+	    p1 = p1->next;
+	    ++cnt1;
+	}
+
+	p2 = url2->other_param.next;
+	while (p2 != &url2->other_param)
+	    ++cnt2, p2 = p2->next;
+
+	if (cnt1 < cnt2)
+	    return -1;
+	else if (cnt1 > cnt2)
+	    return 1;
+
+    } else if (url2->other_param.next != &url2->other_param)
+	return -1;
+
+    /* Equal. */
+    return 0;
+}
+
+/* Clone tel: URI */
+static pjsip_tel_uri* tel_uri_clone(pj_pool_t *pool, const pjsip_tel_uri *rhs)
+{
+    pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
+
+    pj_strdup(pool, &uri->number, &rhs->number);
+    pj_strdup(pool, &uri->context, &rhs->context);
+    pj_strdup(pool, &uri->ext_param, &rhs->ext_param);
+    pj_strdup(pool, &uri->isub_param, &rhs->isub_param);
+    pjsip_param_clone(pool, &uri->other_param, &rhs->other_param);
+
+    return uri;
+}
+
+/* Parse tel: URI */
+static pjsip_tel_uri *tel_uri_parse( pj_scanner *scanner, pj_pool_t *pool,
+				     pj_bool_t parse_params)
+{
+    pjsip_tel_uri *uri;
+    pj_str_t token;
+    int skip_ws = scanner->skip_ws;
+
+    scanner->skip_ws = 0;
+
+    /* Parse scheme. */
+    pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &token);
+    if (pj_scan_get_char(scanner) != ':')
+	PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+    if (pj_stricmp_alnum(&token, &pjsip_TEL_STR) != 0)
+	PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+
+    /* Create URI */
+    uri = pjsip_tel_uri_create(pool);
+
+    /* Get the phone number. */
+    pj_scan_get(scanner, &pjsip_TEL_NUMBER_SPEC, &uri->number);
+    uri->number = pj_str_unescape(pool, &uri->number);
+
+    /* Get all parameters. */
+    if (parse_params && *scanner->curptr==';') {
+	pj_str_t pname, pvalue;
+
+	do {
+	    /* Eat the ';' separator. */
+	    pj_scan_get_char(scanner);
+
+	    /* Get pname. */
+	    pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, &pname);
+
+	    if (*scanner->curptr == '=') {
+		pj_scan_get_char(scanner);
+		pj_scan_get(scanner, &pjsip_TEL_PARSING_PVALUE_SPEC, &pvalue);
+	    } else {
+		pvalue.slen = 0;
+		pvalue.ptr = NULL;
+	    }
+
+	    /* Save the parameters. */
+	    if (pj_stricmp_alnum(&pname, &pjsip_ISUB_STR)==0) {
+		uri->isub_param = pvalue;
+	    } else if (pj_stricmp_alnum(&pname, &pjsip_EXT_STR)==0) {
+		uri->ext_param = pvalue;
+	    } else if (pj_stricmp_alnum(&pname, &pjsip_PH_CTX_STR)==0) {
+		uri->context = pvalue;
+	    } else {
+		pjsip_param *param = pj_pool_alloc(pool, sizeof(pjsip_param));
+		param->name = pname;
+		param->value = pvalue;
+		pj_list_insert_before(&uri->other_param, param);
+	    }
+
+	} while (*scanner->curptr==';');
+    }
+
+    scanner->skip_ws = skip_ws;
+    return uri;
+}
+
diff --git a/pjsip/src/pjsip/sip_uri.c b/pjsip/src/pjsip/sip_uri.c
index 6c4efb5..09fd8ef 100644
--- a/pjsip/src/pjsip/sip_uri.c
+++ b/pjsip/src/pjsip/sip_uri.c
@@ -87,6 +87,8 @@
 
 PJ_DEF(pj_ssize_t) pjsip_param_print_on( const pjsip_param *param_list,
 					 char *buf, pj_size_t size,
+					 const pj_cis_t *pname_spec,
+					 const pj_cis_t *pvalue_spec,
 					 int sep)
 {
     const pjsip_param *p;
@@ -103,10 +105,10 @@
 
     do {
 	*buf++ = (char)sep;
-	copy_advance_escape(buf, p->name, pjsip_PARAM_CHAR_SPEC);
+	copy_advance_escape(buf, p->name, (*pname_spec));
 	if (p->value.slen) {
 	    *buf++ = '=';
-	    copy_advance_escape(buf, p->value, pjsip_PARAM_CHAR_SPEC);
+	    copy_advance_escape(buf, p->value, (*pvalue_spec));
 	}
 	p = p->next;
     } while (p != param_list);
@@ -308,7 +310,9 @@
     }
 
     /* Other param. */
-    printed = pjsip_param_print_on(&url->other_param, buf, endbuf-buf, ';');
+    printed = pjsip_param_print_on(&url->other_param, buf, endbuf-buf, 
+				   &pjsip_PARAM_CHAR_SPEC, 
+				   &pjsip_PARAM_CHAR_SPEC, ';');
     if (printed < 0)
 	return -1;
     buf += printed;
@@ -448,8 +452,6 @@
 	     * the rule of each header. We'll just compare them string to
 	     * string..
 	     */
-	    PJ_TODO(MORE_COMPLIANT_HEADER_PARAM_COMPARISON_IN_URL);
-
 	    if (pj_stricmp(&p1->value, &p2->value) != 0)
 		return PJSIP_ECMPHEADERPARAM;
 	} else {
diff --git a/pjsip/src/test-pjsip/test.c b/pjsip/src/test-pjsip/test.c
index b940ffa..92e0c33 100644
--- a/pjsip/src/test-pjsip/test.c
+++ b/pjsip/src/test-pjsip/test.c
@@ -82,7 +82,7 @@
     PJ_LOG(3,("",""));
 
     DO_TEST(uri_test());
-    //DO_TEST(msg_test());
+    DO_TEST(msg_test());
 
 on_return:
 
diff --git a/pjsip/src/test-pjsip/uri.c b/pjsip/src/test-pjsip/uri.c
index 7afcbbe..29a16dd 100644
--- a/pjsip/src/test-pjsip/uri.c
+++ b/pjsip/src/test-pjsip/uri.c
@@ -57,6 +57,17 @@
 static pjsip_uri *create_uri15( pj_pool_t *pool );
 static pjsip_uri *create_uri16( pj_pool_t *pool );
 static pjsip_uri *create_uri17( pj_pool_t *pool );
+static pjsip_uri *create_uri25( pj_pool_t *pool );
+static pjsip_uri *create_uri26( pj_pool_t *pool );
+static pjsip_uri *create_uri27( pj_pool_t *pool );
+static pjsip_uri *create_uri28( pj_pool_t *pool );
+static pjsip_uri *create_uri29( pj_pool_t *pool );
+static pjsip_uri *create_uri30( pj_pool_t *pool );
+static pjsip_uri *create_uri31( pj_pool_t *pool );
+static pjsip_uri *create_uri32( pj_pool_t *pool );
+static pjsip_uri *create_uri33( pj_pool_t *pool );
+static pjsip_uri *create_uri34( pj_pool_t *pool );
+static pjsip_uri *create_uri35( pj_pool_t *pool );
 static pjsip_uri *create_dummy( pj_pool_t *pool );
 
 #define ERR_NOT_EQUAL	-1001
@@ -66,7 +77,8 @@
 {
     pj_status_t	     status;
     char	     str[PJSIP_MAX_URL_SIZE];
-    pjsip_uri *(*creator)(pj_pool_t *pool);
+    pjsip_uri	    *(*creator)(pj_pool_t *pool);
+    const char	    *printed;
     pj_size_t	     len;
 } uri_test_array[] = 
 {
@@ -226,7 +238,78 @@
 	ERR_SYNTAX_ERR,
 	"",
 	&create_dummy,
-    }
+    },
+    {
+	/* 25: Simple tel: URI with global context */
+	PJ_SUCCESS,
+	"tel:+1-201-555-0123",
+	&create_uri25,
+	"tel:+1-201-555-0123"
+    },
+    {
+	/* 26: Simple tel: URI with local context */
+	PJ_SUCCESS,
+	"tel:7042;phone-context=example.com",
+	&create_uri26,
+	"tel:7042;phone-context=example.com"
+    },
+    {
+	/* 27: Simple tel: URI with local context */
+	PJ_SUCCESS,
+	"tel:863-1234;phone-context=+1-914-555",
+	&create_uri27,
+	"tel:863-1234;phone-context=+1-914-555"
+    },
+    {
+	/* 28: Comparison between local and global number */
+	ERR_NOT_EQUAL,
+	"tel:+1",
+	&create_uri28,
+	"tel:+1"
+    },
+    {
+	/* 29: tel: with some visual chars and spaces */
+	PJ_SUCCESS,
+	"tel:(44).1234-*#+Deaf",
+	&create_uri29,
+	"tel:(44).1234-*#+Deaf"
+    },
+    {
+	/* 30: isub parameters */
+	PJ_SUCCESS,
+	"tel:+1;isub=/:@&$,-_.!~*'()[]/:&$aA1%21+=",
+	&create_uri30,
+	"tel:+1;isub=/:@&$,-_.!~*'()[]/:&$aA1%21+%3d"
+    },
+    {
+	/* 31: extension number parsing and encoding */
+	PJ_SUCCESS,
+	"tel:+1;ext=+123",
+	&create_uri31,
+	"tel:+1;ext=%2b123"
+    },
+    {
+	/* 32: context parameter parsing and encoding */
+	PJ_SUCCESS,
+	"tel:911;phone-context=+1-911",
+	&create_uri32,
+	"tel:911;phone-context=+1-911"
+    },
+    {
+	/* 33: case-insensitive comparison */
+	PJ_SUCCESS,
+	"tel:911;phone-context=emergency.example.com",
+	&create_uri33,
+	"tel:911;phone-context=emergency.example.com"
+    },
+    {
+	/* 34: parameter only appears in one URL */
+	ERR_NOT_EQUAL,
+	"tel:911;p1=p1;p2=p2",
+	&create_uri34,
+	"tel:911;p1=p1;p2=p2"
+    },
+    
 };
 
 static pjsip_uri *create_uri0(pj_pool_t *pool)
@@ -474,6 +557,110 @@
     return (pjsip_uri*)url;
 }
 
+
+static pjsip_uri *create_uri25(pj_pool_t *pool)
+{
+    /* "tel:+1-201-555-0123" */
+    pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
+
+    uri->number = pj_str("+1-201-555-0123");
+    return (pjsip_uri*)uri;
+}
+
+static pjsip_uri *create_uri26(pj_pool_t *pool)
+{
+    /* tel:7042;phone-context=example.com */
+    pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
+
+    uri->number = pj_str("7042");
+    uri->context = pj_str("example.com");
+    return (pjsip_uri*)uri;
+}
+
+static pjsip_uri *create_uri27(pj_pool_t *pool)
+{
+    /* "tel:863-1234;phone-context=+1-914-555" */
+    pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
+
+    uri->number = pj_str("863-1234");
+    uri->context = pj_str("+1-914-555");
+    return (pjsip_uri*)uri;
+}
+
+/* "tel:1" */
+static pjsip_uri *create_uri28(pj_pool_t *pool)
+{
+    pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
+
+    uri->number = pj_str("1");
+    return (pjsip_uri*)uri;
+}
+
+/* "tel:(44).1234-*#+Deaf" */
+static pjsip_uri *create_uri29(pj_pool_t *pool)
+{
+    pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
+
+    uri->number = pj_str("(44).1234-*#+Deaf");
+    return (pjsip_uri*)uri;    
+}
+
+/* "tel:+1;isub=/:@&$,-_.!~*'()[]/:&$aA1%21+=" */
+static pjsip_uri *create_uri30(pj_pool_t *pool)
+{
+    pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
+
+    uri->number = pj_str("+1");
+    uri->isub_param = pj_str("/:@&$,-_.!~*'()[]/:&$aA1%21+=");
+    return (pjsip_uri*)uri;    
+}
+
+/* "tel:+1;ext=+123" */
+static pjsip_uri *create_uri31(pj_pool_t *pool)
+{
+    pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
+
+    uri->number = pj_str("+1");
+    uri->ext_param = pj_str("+123");
+    return (pjsip_uri*)uri;    
+}
+
+/* "tel:911;phone-context=+1-911" */
+static pjsip_uri *create_uri32(pj_pool_t *pool)
+{
+    pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
+
+    uri->number = pj_str("911");
+    uri->context = pj_str("+1-911");
+    return (pjsip_uri*)uri;    
+}
+
+/* "tel:911;phone-context=emergency.example.com" */
+static pjsip_uri *create_uri33(pj_pool_t *pool)
+{
+    pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
+
+    uri->number = pj_str("911");
+    uri->context = pj_str("EMERGENCY.EXAMPLE.COM");
+    return (pjsip_uri*)uri;    
+}
+
+/* "tel:911;p1=p1;p2=p2" */
+static pjsip_uri *create_uri34(pj_pool_t *pool)
+{
+    pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
+    pjsip_param *p;
+
+    uri->number = pj_str("911");
+    
+    p = pj_pool_alloc(pool, sizeof(*p));
+    p->name = p->value = pj_str("p1");
+    pj_list_insert_before(&uri->other_param, p);
+
+    return (pjsip_uri*)uri;    
+}
+
+
 static pjsip_uri *create_dummy(pj_pool_t *pool)
 {
     PJ_UNUSED_ARG(pool);
@@ -572,9 +759,24 @@
     pj_add_timestamp(&cmp_time, &t2);
 
     /* Compare text. */
-    if (pj_strcmp(&s1, &s2) != 0) {
-	/* Not equal. */
-	status = -60;
+    if (entry->printed) {
+	if (pj_strcmp2(&s1, entry->printed) != 0) {
+	    /* Not equal. */
+	    PJ_LOG(3,("", "   uri print mismatch:\n"
+			  "    printed='%s'\n"
+			  "    expectd='%s'",
+			  s1.ptr, entry->printed));
+	    status = -60;
+	}
+    } else {
+	if (pj_strcmp(&s1, &s2) != 0) {
+	    /* Not equal. */
+	    PJ_LOG(3,("", "   uri print mismatch:\n"
+			  "    uri1='%s'\n"
+			  "    uri2='%s'",
+			  s1.ptr, s2.ptr));
+	    status = -70;
+	}
     }
 
 on_return: