Index: pjproject/trunk/pjnath/src/pjnath/stun_msg.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 1852)
+++ pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 1877)
@@ -21,5 +21,4 @@
 #include <pjlib-util/crc32.h>
 #include <pjlib-util/hmac_sha1.h>
-#include <pjlib-util/md5.h>
 #include <pj/assert.h>
 #include <pj/log.h>
@@ -91,4 +90,5 @@
     pj_status_t (*encode_attr)(const void *a, pj_uint8_t *buf, 
 			       unsigned len, unsigned *printed);
+    void*       (*clone_attr)(pj_pool_t *pool, const void *src);
 };
 
@@ -102,4 +102,5 @@
 				       unsigned len, 
 				       unsigned *printed);
+static void*       clone_sockaddr_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_string_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf, 
@@ -107,4 +108,5 @@
 static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf, 
 				      unsigned len, unsigned *printed);
+static void*       clone_string_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_msgint_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf,
@@ -112,4 +114,5 @@
 static pj_status_t encode_msgint_attr(const void *a, pj_uint8_t *buf, 
 				      unsigned len, unsigned *printed);
+static void*       clone_msgint_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_errcode_attr(pj_pool_t *pool, 
 				       const pj_uint8_t *buf,
@@ -117,4 +120,5 @@
 static pj_status_t encode_errcode_attr(const void *a, pj_uint8_t *buf, 
 				       unsigned len, unsigned *printed);
+static void*       clone_errcode_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_unknown_attr(pj_pool_t *pool, 
 				       const pj_uint8_t *buf, 
@@ -122,4 +126,5 @@
 static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, 
 				       unsigned len, unsigned *printed);
+static void*       clone_unknown_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_uint_attr(pj_pool_t *pool, 
 				    const pj_uint8_t *buf, 
@@ -127,4 +132,5 @@
 static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf, 
 				    unsigned len, unsigned *printed);
+static void*       clone_uint_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_uint64_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf, 
@@ -132,4 +138,5 @@
 static pj_status_t encode_uint64_attr(const void *a, pj_uint8_t *buf, 
 				      unsigned len, unsigned *printed);
+static void*       clone_uint64_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_binary_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf,
@@ -137,4 +144,5 @@
 static pj_status_t encode_binary_attr(const void *a, pj_uint8_t *buf, 
 				      unsigned len, unsigned *printed);
+static void*       clone_binary_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_empty_attr(pj_pool_t *pool, 
 				     const pj_uint8_t *buf, 
@@ -142,5 +150,5 @@
 static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf, 
 				     unsigned len, unsigned *printed);
-
+static void*       clone_empty_attr(pj_pool_t *pool, const void *src);
 
 static struct attr_desc mandatory_attr_desc[] = 
@@ -148,4 +156,5 @@
     {
 	/* type zero */
+	NULL,
 	NULL,
 	NULL,
@@ -156,5 +165,6 @@
 	"MAPPED-ADDRESS",
 	&decode_sockaddr_attr,
-	&encode_sockaddr_attr
+	&encode_sockaddr_attr,
+	&clone_sockaddr_attr
     },
     {
@@ -162,5 +172,6 @@
 	"RESPONSE-ADDRESS",
 	&decode_sockaddr_attr,
-	&encode_sockaddr_attr
+	&encode_sockaddr_attr,
+	&clone_sockaddr_attr
     },
     {
@@ -168,5 +179,6 @@
 	"CHANGE-REQUEST",
 	&decode_uint_attr,
-	&encode_uint_attr
+	&encode_uint_attr,
+	&clone_uint_attr
     },
     {
@@ -174,5 +186,6 @@
 	"SOURCE-ADDRESS",
 	&decode_sockaddr_attr,
-	&encode_sockaddr_attr
+	&encode_sockaddr_attr,
+	&clone_sockaddr_attr
     },
     {
@@ -180,5 +193,6 @@
 	"CHANGED-ADDRESS",
 	&decode_sockaddr_attr,
-	&encode_sockaddr_attr
+	&encode_sockaddr_attr,
+	&clone_sockaddr_attr
     },
     {
@@ -186,5 +200,6 @@
 	"USERNAME",
 	&decode_string_attr,
-	&encode_string_attr
+	&encode_string_attr,
+	&clone_string_attr
     },
     {
@@ -192,5 +207,6 @@
 	"PASSWORD",
 	&decode_string_attr,
-	&encode_string_attr
+	&encode_string_attr,
+	&clone_string_attr
     },
     {
@@ -198,5 +214,6 @@
 	"MESSAGE-INTEGRITY",
 	&decode_msgint_attr,
-	&encode_msgint_attr
+	&encode_msgint_attr,
+	&clone_msgint_attr
     },
     {
@@ -204,5 +221,6 @@
 	"ERROR-CODE",
 	&decode_errcode_attr,
-	&encode_errcode_attr
+	&encode_errcode_attr,
+	&clone_errcode_attr
     },
     {
@@ -210,5 +228,6 @@
 	"UNKNOWN-ATTRIBUTES",
 	&decode_unknown_attr,
-	&encode_unknown_attr
+	&encode_unknown_attr,
+	&clone_unknown_attr
     },
     {
@@ -216,5 +235,6 @@
 	"REFLECTED-FROM",
 	&decode_sockaddr_attr,
-	&encode_sockaddr_attr
+	&encode_sockaddr_attr,
+	&clone_sockaddr_attr
     },
     {
@@ -222,5 +242,6 @@
 	"CHANNEL-NUMBER",
 	&decode_uint_attr,
-	&encode_uint_attr
+	&encode_uint_attr,
+	&clone_uint_attr
     },
     {
@@ -228,8 +249,10 @@
 	"LIFETIME",
 	&decode_uint_attr,
-	&encode_uint_attr
+	&encode_uint_attr,
+	&clone_uint_attr
     },
     {
 	/* ID 0x000E is not assigned */
+	NULL,
 	NULL,
 	NULL,
@@ -240,5 +263,6 @@
 	"MAGIC-COOKIE",
 	&decode_uint_attr,
-	&encode_uint_attr
+	&encode_uint_attr,
+	&clone_uint_attr
     },
     {
@@ -246,8 +270,10 @@
 	"BANDWIDTH",
 	&decode_uint_attr,
-	&encode_uint_attr
+	&encode_uint_attr,
+	&clone_uint_attr
     },
     {
 	/* ID 0x0011 is not assigned */
+	NULL,
 	NULL,
 	NULL,
@@ -258,5 +284,6 @@
 	"PEER-ADDRESS",
 	&decode_xored_sockaddr_attr,
-	&encode_sockaddr_attr
+	&encode_sockaddr_attr,
+	&clone_sockaddr_attr
     },
     {
@@ -264,5 +291,6 @@
 	"DATA",
 	&decode_binary_attr,
-	&encode_binary_attr
+	&encode_binary_attr,
+	&clone_binary_attr
     },
     {
@@ -270,5 +298,6 @@
 	"REALM",
 	&decode_string_attr,
-	&encode_string_attr
+	&encode_string_attr,
+	&clone_string_attr
     },
     {
@@ -276,5 +305,6 @@
 	"NONCE",
 	&decode_string_attr,
-	&encode_string_attr
+	&encode_string_attr,
+	&clone_string_attr
     },
     {
@@ -282,5 +312,6 @@
 	"RELAY-ADDRESS",
 	&decode_xored_sockaddr_attr,
-	&encode_sockaddr_attr
+	&encode_sockaddr_attr,
+	&clone_sockaddr_attr
     },
     {
@@ -288,5 +319,6 @@
 	"REQUESTED-ADDRESS-TYPE",
 	&decode_uint_attr,
-	&encode_uint_attr
+	&encode_uint_attr,
+	&clone_uint_attr
     },
     {
@@ -294,5 +326,6 @@
 	"REQUESTED-PROPS",
 	&decode_uint_attr,
-	&encode_uint_attr
+	&encode_uint_attr,
+	&clone_uint_attr
     },
     {
@@ -300,5 +333,6 @@
 	"REQUESTED-TRANSPORT",
 	&decode_uint_attr,
-	&encode_uint_attr
+	&encode_uint_attr,
+	&clone_uint_attr
     },
     {
@@ -306,4 +340,5 @@
 	NULL,
 	NULL,
+	NULL,
 	NULL
     },
@@ -312,4 +347,5 @@
 	NULL,
 	NULL,
+	NULL,
 	NULL
     },
@@ -318,4 +354,5 @@
 	NULL,
 	NULL,
+	NULL,
 	NULL
     },
@@ -324,4 +361,5 @@
 	NULL,
 	NULL,
+	NULL,
 	NULL
     },
@@ -330,8 +368,10 @@
 	NULL,
 	NULL,
+	NULL,
 	NULL
     },
     {
 	/* ID 0x001F is not assigned */
+	NULL,
 	NULL,
 	NULL,
@@ -342,5 +382,6 @@
 	"XOR-MAPPED-ADDRESS",
 	&decode_xored_sockaddr_attr,
-	&encode_sockaddr_attr
+	&encode_sockaddr_attr,
+	&clone_sockaddr_attr
     },
     {
@@ -348,5 +389,6 @@
 	"TIMER-VAL",
 	&decode_uint_attr,
-	&encode_uint_attr
+	&encode_uint_attr,
+	&clone_uint_attr
     },
     {
@@ -354,5 +396,6 @@
 	"RESERVATION-TOKEN",
 	&decode_uint64_attr,
-	&encode_uint64_attr
+	&encode_uint64_attr,
+	&clone_uint64_attr
     },
     {
@@ -360,5 +403,6 @@
 	"XOR-REFLECTED-FROM",
 	&decode_xored_sockaddr_attr,
-	&encode_sockaddr_attr
+	&encode_sockaddr_attr,
+	&clone_sockaddr_attr
     },
     {
@@ -366,5 +410,6 @@
 	"PRIORITY",
 	&decode_uint_attr,
-	&encode_uint_attr
+	&encode_uint_attr,
+	&clone_uint_attr
     },
     {
@@ -372,5 +417,6 @@
 	"USE-CANDIDATE",
 	&decode_empty_attr,
-	&encode_empty_attr
+	&encode_empty_attr,
+	&clone_empty_attr
     },
     {
@@ -378,5 +424,6 @@
 	"XOR-INTERNAL-ADDRESS",
 	&decode_xored_sockaddr_attr,
-	&encode_sockaddr_attr
+	&encode_sockaddr_attr,
+	&clone_sockaddr_attr
     },
 
@@ -386,4 +433,5 @@
 	NULL,
 	NULL,
+	NULL,
 	NULL
     }
@@ -394,4 +442,5 @@
     {
 	/* ID 0x8021 is not assigned */
+	NULL,
 	NULL,
 	NULL,
@@ -402,5 +451,6 @@
 	"SERVER",
 	&decode_string_attr,
-	&encode_string_attr
+	&encode_string_attr,
+	&clone_string_attr
     },
     {
@@ -408,5 +458,6 @@
 	"ALTERNATE-SERVER",
 	&decode_sockaddr_attr,
-	&encode_sockaddr_attr
+	&encode_sockaddr_attr,
+	&clone_sockaddr_attr
     },
     {
@@ -414,5 +465,6 @@
 	"REFRESH-INTERVAL",
 	&decode_uint_attr,
-	&encode_uint_attr
+	&encode_uint_attr,
+	&clone_uint_attr
     },
     {
@@ -420,4 +472,5 @@
 	NULL,
 	NULL,
+	NULL,
 	NULL
     },
@@ -426,8 +479,10 @@
 	NULL,
 	NULL,
+	NULL,
 	NULL
     },
     {
 	/* CACHE-TIMEOUT, 0x8027 */
+	NULL,
 	NULL,
 	NULL,
@@ -438,5 +493,6 @@
 	"FINGERPRINT",
 	&decode_uint_attr,
-	&encode_uint_attr
+	&encode_uint_attr,
+	&clone_uint_attr
     },
     {
@@ -444,5 +500,6 @@
 	"ICE-CONTROLLED",
 	&decode_uint64_attr,
-	&encode_uint64_attr
+	&encode_uint64_attr,
+	&clone_uint64_attr
     },
     {
@@ -450,5 +507,6 @@
 	"ICE-CONTROLLING",
 	&decode_uint64_attr,
-	&encode_uint64_attr
+	&encode_uint64_attr,
+	&clone_uint64_attr
     }
 };
@@ -827,4 +885,11 @@
 
 
+static void* clone_sockaddr_attr(pj_pool_t *pool, const void *src)
+{
+    pj_stun_sockaddr_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_sockaddr_attr);
+    pj_memcpy(dst, src, sizeof(pj_stun_sockaddr_attr));
+    return (void*)dst;
+}
+
 //////////////////////////////////////////////////////////////////////////////
 /*
@@ -935,4 +1000,15 @@
 
 
+static void* clone_string_attr(pj_pool_t *pool, const void *src)
+{
+    const pj_stun_string_attr *asrc = (const pj_stun_string_attr*)src;
+    pj_stun_string_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_string_attr);
+
+    pj_memcpy(dst, src, sizeof(pj_stun_attr_hdr));
+    pj_strdup(pool, &dst->value, &asrc->value);
+
+    return (void*)dst;
+}
+
 //////////////////////////////////////////////////////////////////////////////
 /*
@@ -1018,4 +1094,13 @@
 }
 
+
+static void* clone_empty_attr(pj_pool_t *pool, const void *src)
+{
+    pj_stun_empty_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_empty_attr);
+
+    pj_memcpy(dst, src, sizeof(pj_stun_empty_attr));
+
+    return (void*) dst;
+}
 
 //////////////////////////////////////////////////////////////////////////////
@@ -1104,4 +1189,14 @@
 }
 
+
+static void* clone_uint_attr(pj_pool_t *pool, const void *src)
+{
+    pj_stun_uint_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_uint_attr);
+
+    pj_memcpy(dst, src, sizeof(pj_stun_uint_attr));
+
+    return (void*)dst;
+}
+
 //////////////////////////////////////////////////////////////////////////////
 
@@ -1190,4 +1285,14 @@
 
 
+static void* clone_uint64_attr(pj_pool_t *pool, const void *src)
+{
+    pj_stun_uint64_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_uint64_attr);
+
+    pj_memcpy(dst, src, sizeof(pj_stun_uint64_attr));
+
+    return (void*)dst;
+}
+
+
 //////////////////////////////////////////////////////////////////////////////
 /*
@@ -1270,4 +1375,14 @@
 
     return PJ_SUCCESS;
+}
+
+
+static void* clone_msgint_attr(pj_pool_t *pool, const void *src)
+{
+    pj_stun_msgint_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_msgint_attr);
+
+    pj_memcpy(dst, src, sizeof(pj_stun_msgint_attr));
+
+    return (void*) dst;
 }
 
@@ -1382,4 +1497,16 @@
 }
 
+
+static void* clone_errcode_attr(pj_pool_t *pool, const void *src)
+{
+    const pj_stun_errcode_attr *asrc = (const pj_stun_errcode_attr*)src;
+    pj_stun_errcode_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_errcode_attr);
+
+    pj_memcpy(dst, src, sizeof(pj_stun_errcode_attr));
+    pj_strdup(pool, &dst->reason, &asrc->reason);
+
+    return (void*)dst;
+}
+
 //////////////////////////////////////////////////////////////////////////////
 /*
@@ -1500,4 +1627,13 @@
 
 
+static void* clone_unknown_attr(pj_pool_t *pool, const void *src)
+{
+    pj_stun_unknown_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_unknown_attr);
+
+    pj_memcpy(dst, src, sizeof(pj_stun_unknown_attr));
+    
+    return (void*)dst;
+}
+
 //////////////////////////////////////////////////////////////////////////////
 /*
@@ -1597,4 +1733,19 @@
 }
 
+
+static void* clone_binary_attr(pj_pool_t *pool, const void *src)
+{
+    const pj_stun_binary_attr *asrc = (const pj_stun_binary_attr*)src;
+    pj_stun_binary_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_binary_attr);
+
+    pj_memcpy(dst, src, sizeof(pj_stun_binary_attr));
+
+    if (asrc->length) {
+	dst->data = (pj_uint8_t*) pj_pool_alloc(pool, asrc->length);
+	pj_memcpy(dst->data, asrc->data, asrc->length);
+    }
+
+    return (void*)dst;
+}
 
 //////////////////////////////////////////////////////////////////////////////
@@ -1638,4 +1789,29 @@
 
     return PJ_SUCCESS;
+}
+
+
+/*
+ * Clone a STUN message with all of its attributes.
+ */
+PJ_DEF(pj_stun_msg*) pj_stun_msg_clone( pj_pool_t *pool,
+					const pj_stun_msg *src)
+{
+    pj_stun_msg *dst;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(pool && src, NULL);
+
+    dst = PJ_POOL_ZALLOC_T(pool, pj_stun_msg);
+    pj_memcpy(dst, src, sizeof(pj_stun_msg));
+
+    /* Duplicate the attributes */
+    for (i=0, dst->attr_count=0; i<src->attr_count; ++i) {
+	dst->attr[dst->attr_count] = pj_stun_attr_clone(pool, src->attr[i]);
+	if (dst->attr[dst->attr_count])
+	    ++dst->attr_count;
+    }
+
+    return dst;
 }
 
@@ -1982,77 +2158,4 @@
 }
 
-/* Calculate HMAC-SHA1 key for long term credential, by getting
- * MD5 digest of username, realm, and password. 
- */
-static void calc_md5_key(pj_uint8_t digest[16],
-			 const pj_str_t *realm,
-			 const pj_str_t *username,
-			 const pj_str_t *passwd)
-{
-    /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking
-     * the MD5 hash of the result of concatenating the following five
-     * fields: (1) The username, with any quotes and trailing nulls
-     * removed, (2) A single colon, (3) The realm, with any quotes and
-     * trailing nulls removed, (4) A single colon, and (5) The 
-     * password, with any trailing nulls removed.
-     */
-    pj_md5_context ctx;
-    pj_str_t s;
-
-    pj_md5_init(&ctx);
-
-#define REMOVE_QUOTE(s)	if (s.slen && *s.ptr=='"') \
-			    s.ptr++, s.slen--; \
-			if (s.slen && s.ptr[s.slen-1]=='"') \
-			    s.slen--;
-
-    /* Add username */
-    s = *username;
-    REMOVE_QUOTE(s);
-    pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen);
-
-    /* Add single colon */
-    pj_md5_update(&ctx, (pj_uint8_t*)":", 1);
-
-    /* Add realm */
-    s = *realm;
-    REMOVE_QUOTE(s);
-    pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen);
-
-#undef REMOVE_QUOTE
-
-    /* Another colon */
-    pj_md5_update(&ctx, (pj_uint8_t*)":", 1);
-
-    /* Add password */
-    pj_md5_update(&ctx, (pj_uint8_t*)passwd->ptr, passwd->slen);
-
-    /* Done */
-    pj_md5_final(&ctx, digest);
-}
-
-
-/*
- * Create authentication key to be used for encoding the message with
- * MESSAGE-INTEGRITY. 
- */
-PJ_DEF(void) pj_stun_create_key(pj_pool_t *pool,
-				pj_str_t *key,
-				const pj_str_t *realm,
-				const pj_str_t *username,
-				const pj_str_t *passwd)
-{
-    PJ_ASSERT_ON_FAIL(pool && key && username && passwd, return);
-
-    if (realm && realm->slen) {
-	key->ptr = (char*) pj_pool_alloc(pool, 16);
-	calc_md5_key((pj_uint8_t*)key->ptr, realm, username, passwd);
-	key->slen = 16;
-    } else {
-	pj_strdup(pool, key, passwd);
-    }
-}
-
-
 /*
 static char *print_binary(const pj_uint8_t *data, unsigned data_len)
@@ -2208,5 +2311,5 @@
 	 * the last attribute before FINGERPRINT.
 	 */
-	if (i < msg->attr_count-2) {
+	if (msg->attr_count>1 && i < msg->attr_count-2) {
 	    /* Should not happen for message generated by us */
 	    pj_assert(PJ_FALSE);
@@ -2299,2 +2402,19 @@
 }
 
+
+/*
+ * Clone a STUN attribute.
+ */
+PJ_DEF(pj_stun_attr_hdr*) pj_stun_attr_clone( pj_pool_t *pool,
+					      const pj_stun_attr_hdr *attr)
+{
+    const struct attr_desc *adesc;
+
+    /* Get the attribute descriptor */
+    adesc = find_attr_desc(attr->type);
+    PJ_ASSERT_RETURN(adesc != NULL, NULL);
+
+    return (pj_stun_attr_hdr*) (*adesc->clone_attr)(pool, attr);
+}
+
+
