Index: pjproject/trunk/pjlib/include/pj/config.h
===================================================================
--- pjproject/trunk/pjlib/include/pj/config.h	(revision 140)
+++ pjproject/trunk/pjlib/include/pj/config.h	(revision 141)
@@ -176,8 +176,8 @@
  * flag is set.
  *
- * Default: 800
+ * Default: 1500
  */
 #ifndef PJ_LOG_MAX_SIZE
-#  define PJ_LOG_MAX_SIZE	    800
+#  define PJ_LOG_MAX_SIZE	    1500
 #endif
 
Index: pjproject/trunk/pjlib/include/pj/log.h
===================================================================
--- pjproject/trunk/pjlib/include/pj/log.h	(revision 140)
+++ pjproject/trunk/pjlib/include/pj/log.h	(revision 141)
@@ -113,4 +113,5 @@
 /**
  * Default logging writer function used by front end logger function.
+ * This function will print the log message to stdout only.
  * Application normally should NOT need to call this function, but
  * rather use the PJ_LOG macro.
Index: pjproject/trunk/pjlib/src/pj/config.c
===================================================================
--- pjproject/trunk/pjlib/src/pj/config.c	(revision 140)
+++ pjproject/trunk/pjlib/src/pj/config.c	(revision 141)
@@ -22,5 +22,5 @@
 
 static const char *id = "config.c";
-const char *PJ_VERSION = "0.5";
+const char *PJ_VERSION = "0.5.1";
 
 PJ_DEF(void) pj_dump_config(void)
Index: pjproject/trunk/pjlib/src/pj/log.c
===================================================================
--- pjproject/trunk/pjlib/src/pj/log.c	(revision 140)
+++ pjproject/trunk/pjlib/src/pj/log.c	(revision 141)
@@ -74,5 +74,5 @@
     char log_buffer[PJ_LOG_MAX_SIZE];
 #endif
-    int len;
+    int len, print_len;
 
     PJ_CHECK_STACK();
@@ -138,5 +138,10 @@
 
     /* Print the whole message to the string log_buffer. */
-    len = len + vsnprintf(pre, sizeof(log_buffer)-len, format, marker);
+    print_len = vsnprintf(pre, sizeof(log_buffer)-len, format, marker);
+    if (print_len < 0) {
+	print_len = pj_snprintf(pre, sizeof(log_buffer)-len, 
+				"<logging error: msg too long>");
+    }
+    len = len + print_len;
     if (len > 0 && len < sizeof(log_buffer)-2) {
 	if (log_decor & PJ_LOG_HAS_CR) {
Index: pjproject/trunk/pjsip/build/pjsip_core.dsp
===================================================================
--- pjproject/trunk/pjsip/build/pjsip_core.dsp	(revision 140)
+++ pjproject/trunk/pjsip/build/pjsip_core.dsp	(revision 141)
@@ -180,5 +180,4 @@
 
 SOURCE=..\src\pjsip\sip_util_statefull.c
-# PROP Exclude_From_Build 1
 # End Source File
 # End Group
Index: pjproject/trunk/pjsip/build/pjsip_ua.dsp
===================================================================
--- pjproject/trunk/pjsip/build/pjsip_ua.dsp	(revision 140)
+++ pjproject/trunk/pjsip/build/pjsip_ua.dsp	(revision 141)
@@ -98,6 +98,4 @@
 !ELSEIF  "$(CFG)" == "pjsip_ua - Win32 Debug"
 
-# PROP Exclude_From_Build 1
-
 !ENDIF 
 
@@ -107,4 +105,8 @@
 
 # PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\include\pjsip_ua.h
+# End Source File
 # Begin Source File
 
Index: pjproject/trunk/pjsip/build/pjsua.dsp
===================================================================
--- pjproject/trunk/pjsip/build/pjsua.dsp	(revision 140)
+++ pjproject/trunk/pjsip/build/pjsua.dsp	(revision 141)
@@ -101,4 +101,15 @@
 !ELSEIF  "$(CFG)" == "pjsua - Win32 Debug"
 
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsua\main_old.c
+
+!IF  "$(CFG)" == "pjsua - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "pjsua - Win32 Debug"
+
 # PROP Exclude_From_Build 1
 
@@ -111,4 +122,12 @@
 # PROP Exclude_From_Build 1
 # End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsua\pjsua.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsua\pjsua_reg.c
+# End Source File
 # End Group
 # Begin Group "Header Files"
@@ -119,4 +138,8 @@
 SOURCE=..\src\pjsua\getopt.h
 # End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsua\pjsua.h
+# End Source File
 # End Group
 # Begin Group "Resource Files"
Index: pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h	(revision 140)
+++ pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h	(revision 141)
@@ -39,5 +39,4 @@
     PJSIP_INV_STATE_CONFIRMED,	    /**< After ACK is sent/received.	    */
     PJSIP_INV_STATE_DISCONNECTED,   /**< Session is terminated.		    */
-    PJSIP_INV_STATE_TERMINATED,	    /**< Session will be destroyed soon.    */
 };
 
Index: pjproject/trunk/pjsip/include/pjsip-ua/sip_regc.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip-ua/sip_regc.h	(revision 140)
+++ pjproject/trunk/pjsip/include/pjsip-ua/sip_regc.h	(revision 141)
@@ -27,5 +27,5 @@
 #include <pjsip/sip_types.h>
 #include <pjsip/sip_auth.h>
-#include <pjsip_mod_ua/sip_ua.h>
+//#include <pjsip/sip_ua.h>
 
 PJ_BEGIN_DECL
@@ -86,9 +86,11 @@
  * @param token	    A data to be associated with the client registration struct.
  * @param cb	    Pointer to callback function to receive registration status.
- *
- * @return	    client registration structure.
- */
-PJ_DECL(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
-				        pjsip_regc_cb *cb);
+ * @param p_regc    Pointer to receive client registration structure.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
+				        pjsip_regc_cb *cb, 
+					pjsip_regc **p_regc);
 
 
@@ -99,6 +101,8 @@
  *
  * @param regc	    The client registration structure.
- */
-PJ_DECL(void) pjsip_regc_destroy(pjsip_regc *regc);
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc);
 
 /**
@@ -138,9 +142,9 @@
  * Set authentication credentials to use by this registration.
  *
- * @param dlg		The registration structure.
- * @param count		Number of credentials in the array.
- * @param cred		Array of credentials.
- *
- * @return		Zero on success.
+ * @param dlg	    The registration structure.
+ * @param count	    Number of credentials in the array.
+ * @param cred	    Array of credentials.
+ *
+ * @return	    PJ_SUCCESS on success.
  */
 PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
@@ -158,8 +162,10 @@
  * @param autoreg   If non zero, the library will automatically refresh the
  *		    next registration until application unregister.
- *
- * @return	    SIP REGISTER request.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg);
+ * @param p_tdata   Pointer to receive the REGISTER request.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
+					 pjsip_tx_data **p_tdata);
 
 
@@ -168,8 +174,10 @@
  *
  * @param regc	    The client registration structure.
- *
- * @return	    SIP REGISTER request.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc);
+ * @param p_tdata   Pointer to receive the REGISTER request.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
+					   pjsip_tx_data **p_tdata);
 
 /**
@@ -202,6 +210,8 @@
  * @param regc	    The client registration structure.
  * @param tdata	    Transmit data.
- */
-PJ_DECL(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata);
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata);
 
 
Index: pjproject/trunk/pjsip/include/pjsip/sip_dialog.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip/sip_dialog.h	(revision 140)
+++ pjproject/trunk/pjsip/include/pjsip/sip_dialog.h	(revision 141)
@@ -68,4 +68,5 @@
 
     /* Dialog's session properties. */
+    pj_bool_t		established;/**< Dialog is established?		    */
     pjsip_uri	       *target;	    /**< Current target.		    */
     pjsip_dlg_party     local;	    /**< Local party info.		    */
Index: pjproject/trunk/pjsip/include/pjsip_ua.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip_ua.h	(revision 140)
+++ pjproject/trunk/pjsip/include/pjsip_ua.h	(revision 141)
@@ -21,4 +21,6 @@
 
 #include <pjsip-ua/sip_inv.h>
+#include <pjsip-ua/sip_regc.h>
+
 
 #endif	/* __PJSIP_UA_H__ */
Index: pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 140)
+++ pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 141)
@@ -107,9 +107,24 @@
 static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata)
 {
+    pjsip_dialog *dlg;
+
     /* Ignore requests outside dialog */
-    if (pjsip_rdata_get_dlg(rdata) == NULL)
+    dlg = pjsip_rdata_get_dlg(rdata);
+    if (dlg == NULL)
 	return PJ_FALSE;
 
-    /* Ignore all. */
+    /* Answer BYE with 200/OK. */
+    if (rdata->msg_info.msg->line.req.method.id == PJSIP_BYE_METHOD) {
+	pj_status_t status;
+	pjsip_tx_data *tdata;
+
+	status = pjsip_dlg_create_response(dlg, rdata, 200, NULL, &tdata);
+	if (status == PJ_SUCCESS)
+	    status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
+					     tdata);
+
+	return status==PJ_SUCCESS ? PJ_TRUE : PJ_FALSE;
+    }
+
     return PJ_FALSE;
 }
@@ -915,5 +930,4 @@
 
     case PJSIP_INV_STATE_DISCONNECTED:
-    case PJSIP_INV_STATE_TERMINATED:
 	/* No need to do anything. */
 	PJ_TODO(RETURN_A_PROPER_STATUS_CODE_HERE);
@@ -1067,8 +1081,9 @@
     pjsip_transaction *tsx = e->body.tsx_state.tsx;
     pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx);
+    pj_status_t status;
 
     PJ_ASSERT_ON_FAIL(tsx && dlg, return);
     
-    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+    if (tsx == s->invite_tsx) {
 
 	switch (tsx->state) {
@@ -1093,6 +1108,37 @@
 		inv_set_state(s, PJSIP_INV_STATE_CONNECTING, e);
 
+	    } else if (tsx->status_code==401 || tsx->status_code==407) {
+
+		/* Handle authentication failure:
+		 * Resend the request with Authorization header.
+		 */
+		pjsip_tx_data *tdata;
+
+		status = pjsip_auth_clt_reinit_req(&s->dlg->auth_sess, 
+						   e->body.tsx_state.src.rdata,
+						   tsx->last_tx,
+						   &tdata);
+
+		if (status != PJ_SUCCESS) {
+
+		    /* Does not have proper credentials. 
+		     * End the session.
+		     */
+		    inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
+
+		} else {
+
+		    /* Restart session. */
+		    s->state = PJSIP_INV_STATE_NULL;
+		    s->invite_tsx = NULL;
+
+		    /* Send the request. */
+		    status = pjsip_inv_send_msg(s, tdata, NULL );
+		}
+
 	    } else {
+
 		inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
+
 	    }
 	    break;
@@ -1116,5 +1162,4 @@
 	    } else  {
 		inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
-		inv_set_state(s, PJSIP_INV_STATE_TERMINATED, e);
 	    }
 	    break;
@@ -1136,5 +1181,5 @@
     PJ_ASSERT_ON_FAIL(tsx && dlg, return);
 
-    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+    if (tsx == s->invite_tsx) {
 	switch (tsx->state) {
 	case PJSIP_TSX_STATE_PROCEEDING:
@@ -1151,5 +1196,4 @@
 	    /* This happens on transport error */
 	    inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
-	    inv_set_state(s, PJSIP_INV_STATE_TERMINATED, e);
 	    break;
 	default:
@@ -1168,5 +1212,5 @@
     PJ_ASSERT_ON_FAIL(tsx && dlg, return);
 
-    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+    if (tsx == s->invite_tsx) {
 
 	switch (tsx->state) {
@@ -1207,5 +1251,4 @@
 	    } else  {
 		inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
-		inv_set_state(s, PJSIP_INV_STATE_TERMINATED, e);
 	    }
 	    break;
@@ -1244,4 +1287,10 @@
 	}
 
+    } else if (tsx->method.id == PJSIP_INVITE_METHOD) {
+
+	/* Ignore previously failed INVITE transaction event
+	 * (e.g. when rejected with 401/407)
+	 */
+
     } else {
 	pj_assert(!"Unexpected transaction type");
@@ -1256,5 +1305,5 @@
     PJ_ASSERT_ON_FAIL(tsx && dlg, return);
 
-    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+    if (tsx == s->invite_tsx) {
 
 	switch (tsx->state) {
@@ -1270,5 +1319,4 @@
 	    if (tsx->status_code/100 != 2) {
 		inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
-		inv_set_state(s, PJSIP_INV_STATE_TERMINATED, e);
 	    }
 	    break;
@@ -1297,5 +1345,5 @@
 	inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
 
-    } else if (tsx->method.id == PJSIP_INVITE_METHOD) {
+    } else if (tsx == s->invite_tsx) {
 	
 	switch (tsx->state) {
@@ -1308,4 +1356,7 @@
 	}
 
+    } else if (tsx->method.id == PJSIP_INVITE_METHOD) {
+
+	/* Re-INVITE */
 
     } else {
Index: pjproject/trunk/pjsip/src/pjsip-ua/sip_reg.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip-ua/sip_reg.c	(revision 140)
+++ pjproject/trunk/pjsip/src/pjsip-ua/sip_reg.c	(revision 141)
@@ -17,5 +17,5 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  */
-#include <pjsip_mod_ua/sip_reg.h>
+#include <pjsip-ua/sip_regc.h>
 #include <pjsip/sip_endpoint.h>
 #include <pjsip/sip_parser.h>
@@ -25,8 +25,11 @@
 #include <pjsip/sip_util.h>
 #include <pjsip/sip_auth_msg.h>
+#include <pjsip/sip_errno.h>
 #include <pj/pool.h>
 #include <pj/string.h>
 #include <pj/guid.h>
 #include <pj/log.h>
+#include <pj/assert.h>
+
 
 #define REFRESH_TIMER		1
@@ -42,10 +45,10 @@
     pjsip_endpoint	*endpt;
     pj_bool_t		 _delete_flag;
-    int			pending_tsx;
+    int			 pending_tsx;
 
     void		*token;
     pjsip_regc_cb	*cb;
 
-    pj_str_t		str_srv_url;
+    pj_str_t		 str_srv_url;
     pjsip_uri		*srv_url;
     pjsip_cid_hdr	*cid_hdr;
@@ -60,10 +63,6 @@
     pj_uint32_t		 expires;
 
-    /* Credentials. */
-    int			 cred_count;
-    pjsip_cred_info     *cred_info;
-    
     /* Authorization sessions. */
-    pjsip_auth_session	 auth_sess_list;
+    pjsip_auth_clt_sess	 auth_sess;
 
     /* Auto refresh registration. */
@@ -74,15 +73,19 @@
 
 
-PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
-				       pjsip_regc_cb *cb)
+PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
+				       pjsip_regc_cb *cb,
+				       pjsip_regc **p_regc)
 {
     pj_pool_t *pool;
     pjsip_regc *regc;
-
-    if (cb == NULL)
-	return NULL;
+    pj_status_t status;
+
+    /* Verify arguments. */
+    PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
 
     pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
-    regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc));
+    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+    regc = pj_pool_zalloc(pool, sizeof(struct pjsip_regc));
 
     regc->pool = pool;
@@ -93,11 +96,15 @@
     regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
 
-    pj_list_init(&regc->auth_sess_list);
-
-    return regc;
-}
-
-
-PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc)
+    status = pjsip_auth_clt_init(&regc->auth_sess, endpt, regc->pool, 0);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Done */
+    *p_regc = regc;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
 {
     if (regc->pending_tsx) {
@@ -105,6 +112,8 @@
 	regc->cb = NULL;
     } else {
-	pjsip_endpt_destroy_pool(regc->endpt, regc->pool);
-    }
+	pjsip_endpt_release_pool(regc->endpt, regc->pool);
+    }
+
+    return PJ_SUCCESS;
 }
 
@@ -118,6 +127,5 @@
 {
     if (expires != regc->expires) {
-	regc->expires_hdr = pjsip_expires_hdr_create(regc->pool);
-	regc->expires_hdr->ivalue = expires;
+	regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
     } else {
 	regc->expires_hdr = NULL;
@@ -137,5 +145,5 @@
     for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) {
 	if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) {
-	    return -1;
+	    return PJSIP_EURITOOLONG;
 	}
 	pj_memcpy(s, contact[i].ptr, contact[i].slen);
@@ -149,9 +157,11 @@
 
     /* Set "Contact" header. */
-    regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR);
+    regc->contact_hdr = pjsip_generic_string_hdr_create(regc->pool, 
+							&contact_STR,
+							NULL);
     regc->contact_hdr->hvalue.ptr = regc->contact_buf;
     regc->contact_hdr->hvalue.slen = (s - regc->contact_buf);
 
-    return 0;
+    return PJ_SUCCESS;
 }
 
@@ -166,4 +176,8 @@
 {
     pj_str_t tmp;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url && 
+		     contact_cnt && contact && expires, PJ_EINVAL);
 
     /* Copy server URL. */
@@ -174,5 +188,5 @@
     regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
     if (regc->srv_url == NULL) {
-	return -1;
+	return PJSIP_EINVALIDURI;
     }
 
@@ -183,6 +197,7 @@
 					  PJSIP_PARSE_URI_AS_NAMEADDR);
     if (!regc->from_hdr->uri) {
-	PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr));
-	return -1;
+	PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", 
+		  from_url->slen, from_url->ptr));
+	return PJSIP_EINVALIDURI;
     }
 
@@ -194,11 +209,12 @@
     if (!regc->to_hdr->uri) {
 	PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
-	return -1;
+	return PJSIP_EINVALIDURI;
     }
 
 
     /* Set "Contact" header. */
-    if (set_contact( regc, contact_cnt, contact) != 0)
-	return -1;
+    status = set_contact( regc, contact_cnt, contact);
+    if (status != PJ_SUCCESS)
+	return status;
 
     /* Set "Expires" header, if required. */
@@ -219,9 +235,8 @@
 
     /* Create "Expires" header used in unregistration. */
-    regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool);
-    regc->unreg_expires_hdr->ivalue = 0;
+    regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool, 0);
 
     /* Done. */
-    return 0;
+    return PJ_SUCCESS;
 }
 
@@ -230,57 +245,51 @@
 						const pjsip_cred_info cred[] )
 {
-    if (count > 0) {
-	regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info));
-	pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info));
-    }
-    regc->cred_count = count;
-    return 0;
-}
-
-static pjsip_tx_data *create_request(pjsip_regc *regc)
-{
+    PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
+    return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
+}
+
+static pj_status_t create_request(pjsip_regc *regc, 
+				  pjsip_tx_data **p_tdata)
+{
+    pj_status_t status;
     pjsip_tx_data *tdata;
+
+    PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
+
+    /* Create the request. */
+    status = pjsip_endpt_create_request_from_hdr( regc->endpt, 
+						  &pjsip_register_method,
+						  regc->srv_url,
+						  regc->from_hdr,
+						  regc->to_hdr,
+						  NULL,
+						  regc->cid_hdr,
+						  regc->cseq_hdr->cseq,
+						  NULL,
+						  &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Add cached authorization headers. */
+    pjsip_auth_clt_init_req( &regc->auth_sess, tdata );
+
+    /* Done. */
+    *p_tdata = tdata;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
+					pjsip_tx_data **p_tdata)
+{
     pjsip_msg *msg;
-
-    /* Create transmit data. */
-    tdata = pjsip_endpt_create_tdata(regc->endpt);
-    if (!tdata) {
-	return NULL;
-    }
-
-    /* Create request message. */
-    msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
-    tdata->msg = msg;
-
-    /* Initialize request line. */
-    pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD);
-    msg->line.req.uri = regc->srv_url;
-
-    /* Add headers. */
-    pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr);
-    pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr);
-    pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr);
-    pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr);
-    
-    /* Add cached authorization headers. */
-    pjsip_auth_init_req( regc->pool, tdata, &regc->auth_sess_list,
-			 regc->cred_count, regc->cred_info );
-
-    /* Add reference counter to transmit data. */
-    pjsip_tx_data_add_ref(tdata);
-
-    return tdata;
-}
-
-
-PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg)
-{
-    pjsip_msg *msg;
+    pj_status_t status;
     pjsip_tx_data *tdata;
 
-    tdata = create_request(regc);
-    if (!tdata)
-	return NULL;
-
+    status = create_request(regc, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Add Contact header. */
     msg = tdata->msg;
     pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr);
@@ -295,12 +304,16 @@
     regc->auto_reg = autoreg;
 
-    return tdata;
-}
-
-
-PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc)
+    /* Done */
+    *p_tdata = tdata;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
+					  pjsip_tx_data **p_tdata)
 {
     pjsip_tx_data *tdata;
     pjsip_msg *msg;
+    pj_status_t status;
 
     if (regc->timer.id != 0) {
@@ -309,7 +322,7 @@
     }
 
-    tdata = create_request(regc);
-    if (!tdata)
-	return NULL;
+    status = create_request(regc, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
 
     msg = tdata->msg;
@@ -317,5 +330,6 @@
     pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
 
-    return tdata;
+    *p_tdata = tdata;
+    return PJ_SUCCESS;
 }
 
@@ -333,5 +347,5 @@
 {
     set_expires( regc, expires );
-    return 0;
+    return PJ_SUCCESS;
 }
 
@@ -364,13 +378,15 @@
     pjsip_regc *regc = entry->user_data;
     pjsip_tx_data *tdata;
+    pj_status_t status;
     
-    PJ_UNUSED_ARG(timer_heap)
+    PJ_UNUSED_ARG(timer_heap);
 
     entry->id = 0;
-    tdata = pjsip_regc_register(regc, 1);
-    if (tdata) {
+    status = pjsip_regc_register(regc, 1, &tdata);
+    if (status == PJ_SUCCESS) {
 	pjsip_regc_send(regc, tdata);
     } else {
-	pj_str_t reason = pj_str("Unable to create txdata");
+	char errmsg[PJ_ERR_MSG_SIZE];
+	pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
 	call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
     }
@@ -379,6 +395,7 @@
 static void tsx_callback(void *token, pjsip_event *event)
 {
+    pj_status_t status;
     pjsip_regc *regc = token;
-    pjsip_transaction *tsx = event->obj.tsx;
+    pjsip_transaction *tsx = event->body.tsx_state.tsx;
     
     /* If registration data has been deleted by user then remove registration 
@@ -391,18 +408,18 @@
 	       tsx->status_code == PJSIP_SC_UNAUTHORIZED)
     {
-	pjsip_rx_data *rdata = event->src.rdata;
+	pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
 	pjsip_tx_data *tdata;
 
-	tdata = pjsip_auth_reinit_req( regc->endpt,
-				       regc->pool, &regc->auth_sess_list,
-				       regc->cred_count, regc->cred_info,
-				       tsx->last_tx, event->src.rdata );
-
-	if (tdata) {
+	status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
+					    rdata, 
+					    tsx->last_tx,  
+					    &tdata);
+
+	if (status == PJ_SUCCESS) {
 	    --regc->pending_tsx;
 	    pjsip_regc_send(regc, tdata);
 	    return;
 	} else {
-	    call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason,
+	    call_callback(regc, tsx->status_code, &rdata->msg_info.msg->line.status.reason,
 			  rdata, -1, 0, NULL);
 	    --regc->pending_tsx;
@@ -420,6 +437,6 @@
 	    pjsip_expires_hdr *expires;
 
-	    rdata = event->src.rdata;
-	    msg = rdata->msg;
+	    rdata = event->body.tsx_state.src.rdata;
+	    msg = rdata->msg_info.msg;
 	    hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
 	    while (hdr) {
@@ -460,5 +477,6 @@
 
 	} else {
-	    rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL;
+	    rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? 
+			event->body.tsx_state.src.rdata : NULL;
 	}
 
@@ -467,5 +485,5 @@
 	if (expiration == 0xFFFF) expiration = -1;
 	call_callback(regc, tsx->status_code, 
-		      (rdata ? &rdata->msg->line.status.reason 
+		      (rdata ? &rdata->msg_info.msg->line.status.reason 
 			: pjsip_get_status_text(tsx->status_code)),
 		      rdata, expiration, 
@@ -481,7 +499,7 @@
 }
 
-PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
-{
-    int status;
+PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
+{
+    pj_status_t status;
 
     /* Make sure we don't have pending transaction. */
@@ -490,5 +508,5 @@
 	call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
 	pjsip_tx_data_dec_ref( tdata );
-	return;
+	return PJ_EINVALIDOP;
     }
 
@@ -501,11 +519,14 @@
     /* Send. */
     status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback);
-    if (status==0)
+    if (status==PJ_SUCCESS)
 	++regc->pending_tsx;
     else {
-	pj_str_t reason = pj_str("Unable to send request.");
+	char errmsg[PJ_ERR_MSG_SIZE];
+	pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
 	call_callback(regc, status, &reason, NULL, -1, 0, NULL);
     }
-}
-
-
+
+    return status;
+}
+
+
Index: pjproject/trunk/pjsip/src/pjsip/sip_auth_client.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_auth_client.c	(revision 140)
+++ pjproject/trunk/pjsip/src/pjsip/sip_auth_client.c	(revision 141)
@@ -395,9 +395,13 @@
 						    const pjsip_cred_info *c)
 {
-    PJ_ASSERT_RETURN(sess && cred_cnt && c, PJ_EINVAL);
-
-    sess->cred_info = pj_pool_alloc(sess->pool, cred_cnt * sizeof(*c));
-    pj_memcpy(sess->cred_info, c, cred_cnt * sizeof(*c));
-    sess->cred_cnt = cred_cnt;
+    PJ_ASSERT_RETURN(sess && c, PJ_EINVAL);
+
+    if (cred_cnt == 0) {
+	sess->cred_cnt = 0;
+    } else {
+	sess->cred_info = pj_pool_alloc(sess->pool, cred_cnt * sizeof(*c));
+	pj_memcpy(sess->cred_info, c, cred_cnt * sizeof(*c));
+	sess->cred_cnt = cred_cnt;
+    }
 
     return PJ_SUCCESS;
@@ -522,4 +526,10 @@
 	    }
 	}
+
+#	if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && PJSIP_AUTH_AUTO_SEND_NEXT!=0
+	    if (hdr != cached_auth->last_chal) {
+		cached_auth->last_chal = pjsip_hdr_clone(sess_pool, hdr);
+	    }
+#	endif
     }
 #   endif
Index: pjproject/trunk/pjsip/src/pjsip/sip_dialog.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_dialog.c	(revision 140)
+++ pjproject/trunk/pjsip/src/pjsip/sip_dialog.c	(revision 141)
@@ -698,8 +698,10 @@
     }
 
-    /* Copy authorization headers. */
-    status = pjsip_auth_clt_init_req( &dlg->auth_sess, tdata );
-    if (status != PJ_SUCCESS)
-	return status;
+    /* Copy authorization headers, if request is not ACK or CANCEL. */
+    if (method->id != PJSIP_ACK_METHOD && method->id != PJSIP_CANCEL_METHOD) {
+	status = pjsip_auth_clt_init_req( &dlg->auth_sess, tdata );
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
 
     /* Done. */
@@ -1133,4 +1135,5 @@
 {
     unsigned i;
+    int res_code;
 
     /* Lock the dialog. */
@@ -1140,6 +1143,6 @@
     pj_assert(pjsip_rdata_get_dlg(rdata) == dlg);
 
-    /* Update the remote tag, if none is specified yet. */
-    if (dlg->remote.info->tag.slen == 0 && rdata->msg_info.to->tag.slen != 0) {
+    /* Update the remote tag if it is different. */
+    if (pj_strcmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag) != 0) {
 
 	pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag);
@@ -1148,10 +1151,41 @@
     }
 
-    /* Update remote target when receiving certain response messages. */
-    if (pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) &&
-	rdata->msg_info.msg->line.status.code/100 == 2)
+    /* Keep the response's status code */
+    res_code = rdata->msg_info.msg->line.status.code;
+
+    /* When we receive response that establishes dialog, update the route
+     * set and dialog target.
+     */
+    if (!dlg->established && 
+	pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) &&
+	(res_code > 100 && res_code < 300) &&
+	rdata->msg_info.to->tag.slen)
     {
+	/* 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 
+	 * preserving all URI parameters. If no Record-Route header field 
+	 * is present in the response, the route set MUST be set to the 
+	 * empty set. This route set, even if empty, overrides any pre-existing
+	 * route set for future requests in this dialog.
+	 */
+	pjsip_hdr *hdr, *end_hdr;
 	pjsip_contact_hdr *contact;
 
+	pj_list_init(&dlg->route_set);
+
+	end_hdr = &rdata->msg_info.msg->hdr;
+	for (hdr=rdata->msg_info.msg->hdr.prev; hdr!=end_hdr; hdr=hdr->prev) {
+	    if (hdr->type == PJSIP_H_RECORD_ROUTE) {
+		pjsip_route_hdr *r;
+		r = pjsip_hdr_clone(dlg->pool, hdr);
+		pjsip_routing_hdr_set_route(r);
+		pj_list_push_back(&dlg->route_set, r);
+	    }
+	}
+
+	/* The remote target MUST be set to the URI from the Contact header 
+	 * field of the response.
+	 */
 	contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, 
 				     NULL);
@@ -1160,5 +1194,24 @@
 	    dlg->target = dlg->remote.contact->uri;
 	}
-    }
+
+	dlg->established = 1;
+    }
+
+    /* Update remote target (again) when receiving 2xx response messages
+     * that's defined as target refresh. 
+     */
+    if (pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) &&
+	res_code/100 == 2)
+    {
+	pjsip_contact_hdr *contact;
+
+	contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, 
+				     NULL);
+	if (contact) {
+	    dlg->remote.contact = pjsip_hdr_clone(dlg->pool, contact);
+	    dlg->target = dlg->remote.contact->uri;
+	}
+    }
+
 
     /* Pass to dialog usages. */
Index: pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c	(revision 140)
+++ pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c	(revision 141)
@@ -104,4 +104,6 @@
 pj_status_t pjsip_tel_uri_subsys_init(void);
 
+/* Defined in sip_util_statefull.c */
+extern pjsip_module mod_stateful_util;
 
 /*
@@ -490,4 +492,9 @@
     /* Initialize capability header list. */
     pj_list_init(&endpt->cap_hdr);
+
+    /* Register mod_stateful_util module (sip_util_statefull.c) */
+    status = pjsip_endpt_register_module(endpt, &mod_stateful_util);
+    if (status != PJ_SUCCESS)
+	goto on_error;
 
     /* Done. */
Index: pjproject/trunk/pjsip/src/pjsip/sip_transaction.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_transaction.c	(revision 140)
+++ pjproject/trunk/pjsip/src/pjsip/sip_transaction.c	(revision 141)
@@ -2196,4 +2196,11 @@
 
     } else if (tsx->status_code >= 300 && tsx->status_code <= 699) {
+
+
+#if 0
+	/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+	/*
+	 * This is the old code; it's broken for authentication.
+	 */
 	pj_time_val timeout;
         pj_status_t status;
@@ -2243,4 +2250,60 @@
 	tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, 
                        PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+
+	/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+#endif
+
+	/* New code, taken from 0.2.9.x branch */
+	pj_time_val timeout;
+	pjsip_tx_data *ack_tdata = NULL;
+
+	/* Stop timer B. */
+	pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
+
+	/* Generate ACK now (for INVITE) but send it later because
+	 * dialog need to use last_tx.
+	 */
+	if (tsx->method.id == PJSIP_INVITE_METHOD) {
+	    pj_status_t status;
+
+	    status = pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx, 
+					     event->body.rx_msg.rdata,
+					     &ack_tdata);
+	    if (status != PJ_SUCCESS)
+		return status;
+	}
+
+	/* Inform TU. */
+	tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, 
+		       PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata);
+
+	/* Generate and send ACK for INVITE. */
+	if (tsx->method.id == PJSIP_INVITE_METHOD) {
+	    pj_status_t status;
+
+	    status = tsx_send_msg( tsx, ack_tdata);
+
+	    if (ack_tdata != tsx->last_tx) {
+		pjsip_tx_data_dec_ref(tsx->last_tx);
+		tsx->last_tx = ack_tdata;
+		pjsip_tx_data_add_ref(ack_tdata);
+	    }
+
+	    if (status != PJ_SUCCESS) {
+		return status;
+	    }
+	}
+
+	/* Start Timer D with TD/T4 timer if unreliable transport is used. */
+	if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) {
+	    if (tsx->method.id == PJSIP_INVITE_METHOD) {
+		timeout = td_timer_val;
+	    } else {
+		timeout = t4_timer_val;
+	    }
+	} else {
+	    timeout.sec = timeout.msec = 0;
+	}
+	pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout);
 
     } else {
Index: pjproject/trunk/pjsip/src/pjsip/sip_ua_layer.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_ua_layer.c	(revision 140)
+++ pjproject/trunk/pjsip/src/pjsip/sip_ua_layer.c	(revision 141)
@@ -618,7 +618,11 @@
      * Request will fork only for the initial INVITE request.
      */
-    if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
-	rdata->msg_info.cseq->cseq == dlg_set->dlg_list.next->local.first_cseq)
-    {
+
+    //This doesn't work when there is authentication challenge, since 
+    //first_cseq evaluation will yield false.
+    //if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
+    //	rdata->msg_info.cseq->cseq == dlg_set->dlg_list.next->local.first_cseq)
+
+    if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) {
 	pj_str_t *to_tag = &rdata->msg_info.to->tag;
 
@@ -627,4 +631,9 @@
 
 	dlg = dlg_set->dlg_list.next;
+
+	/* Forking handling is temporarily disabled. */
+	PJ_TODO(UA_LAYER_HANDLE_FORKING);
+
+#if 0
 	while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) {
 
@@ -670,4 +679,5 @@
 	    }
 	}
+#endif
 
 	/* Done with the dialog set. */
Index: pjproject/trunk/pjsip/src/pjsip/sip_util.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_util.c	(revision 140)
+++ pjproject/trunk/pjsip/src/pjsip/sip_util.c	(revision 141)
@@ -442,8 +442,9 @@
     const pjsip_cseq_hdr *cseq_hdr;
     const pjsip_hdr *hdr;
+    pjsip_hdr *via;
     pjsip_to_hdr *to;
     pj_status_t status;
 
-    /* rdata must be a final response. */
+    /* rdata must be a non-2xx final response. */
     pj_assert(rdata->msg_info.msg->type==PJSIP_RESPONSE_MSG &&
 	      rdata->msg_info.msg->line.status.code >= 300);
@@ -488,9 +489,12 @@
     pj_strdup(ack->pool, &to->tag, &rdata->msg_info.to->tag);
 
+
+    /* Clear Via headers in the new request. */
+    while ((via=pjsip_msg_find_hdr(ack->msg, PJSIP_H_VIA, NULL)) != NULL)
+	pj_list_erase(via);
+
     /* Must contain single Via, just as the original INVITE. */
     hdr = pjsip_msg_find_hdr( invite_msg, PJSIP_H_VIA, NULL);
-    if (hdr) {
-        pjsip_msg_insert_first_hdr( ack->msg, pjsip_hdr_clone(ack->pool,hdr) );
-    }
+    pjsip_msg_insert_first_hdr( ack->msg, pjsip_hdr_clone(ack->pool,hdr) );
 
     /* If the original INVITE has Route headers, those header fields MUST 
Index: pjproject/trunk/pjsip/src/pjsip/sip_util_statefull.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_util_statefull.c	(revision 140)
+++ pjproject/trunk/pjsip/src/pjsip/sip_util_statefull.c	(revision 141)
@@ -22,40 +22,47 @@
 #include <pjsip/sip_transaction.h>
 #include <pjsip/sip_event.h>
+#include <pjsip/sip_errno.h>
 #include <pj/pool.h>
+#include <pj/assert.h>
 
-struct aux_tsx_data
+struct tsx_data
 {
     void *token;
-    void (*cb)(void*,pjsip_event*);
+    void (*cb)(void*, pjsip_event*);
 };
 
-static void aux_tsx_handler( pjsip_transaction *tsx, pjsip_event *event );
+static void mod_util_on_tsx_state(pjsip_transaction*, pjsip_event*);
 
-pjsip_module aux_tsx_module = 
+/* This module will be registered in pjsip_endpt.c */
+
+pjsip_module mod_stateful_util = 
 {
-    NULL, NULL,				/* prev and next	*/
-    { "Aux-Tsx", 7},			/* Name.		*/
-    -1,					/* Id		*/
-    PJSIP_MOD_PRIORITY_APPLICATION-1,   /* Priority		*/
-    NULL,				/* User data.	*/
-    0,					/* Number of methods supported (=0). */
-    { 0 },				/* Array of methods (none) */
-    NULL,				/* load()		*/
-    NULL,				/* start()		*/
-    NULL,				/* stop()		*/
-    NULL,				/* unload()		*/
-    NULL,				/* on_rx_request()	*/
-    NULL,				/* on_rx_response()	*/
-    &aux_tsx_handler,			/* tsx_handler()	*/
+    NULL, NULL,			    /* prev, next.			*/
+    { "mod-stateful-util", 17 },    /* Name.				*/
+    -1,				    /* Id				*/
+    PJSIP_MOD_PRIORITY_APPLICATION, /* Priority				*/
+    NULL,			    /* User data.			*/
+    NULL,			    /* load()				*/
+    NULL,			    /* start()				*/
+    NULL,			    /* stop()				*/
+    NULL,			    /* unload()				*/
+    NULL,			    /* on_rx_request()			*/
+    NULL,			    /* on_rx_response()			*/
+    NULL,			    /* on_tx_request.			*/
+    NULL,			    /* on_tx_response()			*/
+    &mod_util_on_tsx_state,	    /* on_tsx_state()			*/
 };
 
-static void aux_tsx_handler( pjsip_transaction *tsx, pjsip_event *event )
+static void mod_util_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
 {
-    struct aux_tsx_data *tsx_data;
+    struct tsx_data *tsx_data;
 
     if (event->type != PJSIP_EVENT_TSX_STATE)
 	return;
-    if (tsx->module_data[aux_tsx_module.id] == NULL)
+
+    tsx_data = tsx->mod_data[mod_stateful_util.id];
+    if (tsx_data == NULL)
 	return;
+
     if (tsx->status_code < 200)
 	return;
@@ -64,6 +71,5 @@
      * by clearing the transaction's module_data.
      */
-    tsx_data = tsx->module_data[aux_tsx_module.id];
-    tsx->module_data[aux_tsx_module.id] = NULL;
+    tsx->mod_data[mod_stateful_util.id] = NULL;
 
     if (tsx_data->cb) {
@@ -80,29 +86,23 @@
 {
     pjsip_transaction *tsx;
-    struct aux_tsx_data *tsx_data;
+    struct tsx_data *tsx_data;
     pj_status_t status;
 
-    status = pjsip_endpt_create_tsx(endpt, &tsx);
-    if (!tsx) {
+    PJ_ASSERT_RETURN(endpt && tdata && (timeout==-1 || timeout>0), PJ_EINVAL);
+
+    status = pjsip_tsx_create_uac(&mod_stateful_util, tdata, &tsx);
+    if (status != PJ_SUCCESS) {
 	pjsip_tx_data_dec_ref(tdata);
-	return -1;
+	return status;
     }
 
-    tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data));
+    tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct tsx_data));
     tsx_data->token = token;
     tsx_data->cb = cb;
-    tsx->module_data[aux_tsx_module.id] = tsx_data;
+    tsx->mod_data[mod_stateful_util.id] = tsx_data;
 
-    if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
-	pjsip_endpt_destroy_tsx(endpt, tsx);
-	pjsip_tx_data_dec_ref(tdata);
-	return -1;
-    }
+    PJ_TODO(IMPLEMENT_TIMEOUT_FOR_SEND_REQUEST);
 
-    pjsip_endpt_register_tsx(endpt, tsx);
-    pjsip_tx_data_invalidate_msg(tdata);
-    pjsip_tsx_on_tx_msg(tsx, tdata);
-    pjsip_tx_data_dec_ref(tdata);
-    return 0;
+    return pjsip_tsx_send_msg(tsx, NULL);
 }
 
Index: pjproject/trunk/pjsip/src/pjsua/main.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua/main.c	(revision 140)
+++ pjproject/trunk/pjsip/src/pjsua/main.c	(revision 141)
@@ -18,4 +18,6 @@
  */
 #include "pjsua.h"
+#include "getopt.h"
+
 
 /* For debugging, disable threading. */
@@ -33,5 +35,5 @@
  * Notify UI when invite state has changed.
  */
-void ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
+void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
 {
     const char *state_names[] =
@@ -51,7 +53,5 @@
     PJ_LOG(3,(THIS_FILE, "INVITE session state changed to %s", state_names[inv->state]));
 
-    if (inv->state == PJSIP_INV_STATE_DISCONNECTED ||
-	inv->state == PJSIP_INV_STATE_TERMINATED)
-    {
+    if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
 	if (inv == inv_session)
 	    inv_session = NULL;
@@ -104,5 +104,5 @@
 	    }
 
-#if 0
+#if 1
 	    printf("Enter URL to call: ");
 	    fgets(buf, sizeof(buf), stdin);
@@ -122,7 +122,9 @@
 
 	    pjsua_invite(buf, &inv);
+
+#else
+
+	    pjsua_invite("sip:localhost:5061", &inv);
 #endif
-
-	    pjsua_invite("sip:localhost:5061", &inv);
 	    break;
 
@@ -163,4 +165,20 @@
 }
 
+
+/*****************************************************************************
+ * This is a very simple PJSIP module, whose sole purpose is to display
+ * incoming and outgoing messages to log. This module will have priority
+ * higher than transport layer, which means:
+ *
+ *  - incoming messages will come to this module first before reaching
+ *    transaction layer.
+ *
+ *  - outgoing messages will come to this module last, after the message
+ *    has been 'printed' to contiguous buffer by transport layer and
+ *    appropriate transport instance has been decided for this message.
+ *
+ */
+
+/* Notification on incoming messages */
 static pj_bool_t console_on_rx_msg(pjsip_rx_data *rdata)
 {
@@ -174,10 +192,18 @@
 			 rdata->msg_info.msg_buf));
     
-    /* Must return false for logger! */
+    /* Always return false, otherwise messages will not get processed! */
     return PJ_FALSE;
 }
 
+/* Notification on outgoing messages */
 static pj_status_t console_on_tx_msg(pjsip_tx_data *tdata)
 {
+    
+    /* Important note:
+     *	tp_info field is only valid after outgoing messages has passed
+     *	transport layer. So don't try to access tp_info when the module
+     *	has lower priority than transport layer.
+     */
+
     PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n"
 			 "%s\n"
@@ -189,7 +215,9 @@
 			 tdata->buf.start));
 
+    /* Always return success, otherwise message will not get sent! */
     return PJ_SUCCESS;
 }
 
+/* The module instance. */
 static pjsip_module console_msg_logger = 
 {
@@ -212,5 +240,425 @@
 
 
-int main()
+
+/*****************************************************************************
+ * Console application custom logging:
+ */
+
+
+static FILE *log_file;
+
+
+static void app_log_writer(int level, const char *buffer, int len)
+{
+    /* Write to both stdout and file. */
+
+    if (level <= pjsua.app_log_level)
+	pj_log_write(level, buffer, len);
+
+    if (log_file) {
+	fwrite(buffer, len, 1, log_file);
+	fflush(log_file);
+    }
+}
+
+
+void app_logging_init(void)
+{
+    /* Redirect log function to ours */
+
+    pj_log_set_log_func( &app_log_writer );
+
+    /* If output log file is desired, create the file: */
+
+    if (pjsua.log_filename)
+	log_file = fopen(pjsua.log_filename, "wt");
+}
+
+
+void app_logging_shutdown(void)
+{
+    /* Close logging file, if any: */
+
+    if (log_file) {
+	fclose(log_file);
+	log_file = NULL;
+    }
+}
+
+/*****************************************************************************
+ * Command line argument processing:
+ */
+
+
+/* Show usage */
+static void usage(void)
+{
+    puts("Usage:");
+    puts("  pjsua [options] [sip-url]");
+    puts("");
+    puts("  [sip-url]   Default URL to invite.");
+    puts("");
+    puts("General options:");
+    puts("  --config-file=file  Read the config/arguments from file.");
+    puts("  --log-file=fname    Log to filename (default stderr)");
+    puts("  --log-level=N       Set log max level to N (0(none) to 6(trace))");
+    puts("  --app-log-level=N   Set log max level for stdout display to N");
+    puts("  --help              Display this help screen");
+    puts("  --version           Display version info");
+    puts("");
+    puts("Media options:");
+    puts("  --null-audio        Use NULL audio device");
+    puts("");
+    puts("User Agent options:");
+    puts("  --auto-answer=sec   Auto-answer all incoming calls after sec seconds.");
+    puts("  --auto-hangup=sec   Auto-hangup all calls after sec seconds.");
+    puts("");
+    puts("SIP options:");
+    puts("  --local-port=port   Set TCP/UDP port");
+    puts("  --id=url            Set the URL of local ID (used in From header)");
+    puts("  --contact=url       Override the Contact information");
+    puts("  --proxy=url         Set the URL of proxy server");
+    puts("  --outbound=url      Set the URL of outbound proxy server");
+    puts("  --registrar=url     Set the URL of registrar server");
+    puts("  --reg-timeout=secs  Set registration interval to secs (default 3600)");
+    puts("");
+    puts("Authentication options:");
+    puts("  --realm=string      Set realm");
+    puts("  --username=string   Set authentication username");
+    puts("  --password=string   Set authentication password");
+    puts("");
+    puts("STUN options (all must be specified):");
+    puts("  --use-stun1=host[:port]");
+    puts("  --use-stun2=host[:port]  Use STUN and set host name and port of STUN servers");
+    puts("");
+    puts("SIMPLE options (may be specified more than once):");
+    puts("  --add-buddy url     Add the specified URL to the buddy list.");
+    puts("  --offer-x-ms-msg    Offer \"x-ms-message\" in outgoing INVITE");
+    puts("  --no-presence	Do not subscribe presence of buddies");
+    puts("");
+    fflush(stdout);
+}
+
+
+/*
+ * Verify that valid SIP url is given.
+ */
+static pj_status_t verify_sip_url(char *url)
+{
+    pjsip_uri *p;
+    pj_pool_t *pool;
+    int len = (url ? strlen(url) : 0);
+
+    if (!len) return -1;
+
+    pool = pj_pool_create(&pjsua.cp.factory, "check%p", 1024, 0, NULL);
+    if (!pool) return -1;
+
+    p = pjsip_parse_uri(pool, url, len, 0);
+    if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
+	p = NULL;
+
+    pj_pool_release(pool);
+    return p ? 0 : -1;
+}
+
+
+/*
+ * Read command arguments from config file.
+ */
+static int read_config_file(pj_pool_t *pool, const char *filename, 
+			    int *app_argc, char ***app_argv)
+{
+    int i;
+    FILE *fhnd;
+    char line[200];
+    int argc = 0;
+    char **argv;
+    enum { MAX_ARGS = 64 };
+
+    /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
+    argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
+    argv[argc++] = *app_argv[0];
+
+    /* Open config file. */
+    fhnd = fopen(filename, "rt");
+    if (!fhnd) {
+	printf("Unable to open config file %s\n", filename);
+	return -1;
+    }
+
+    /* Scan tokens in the file. */
+    while (argc < MAX_ARGS && !feof(fhnd)) {
+	char *token, *p = line;
+
+	if (fgets(line, sizeof(line), fhnd) == NULL) break;
+
+	for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS; 
+	     token = strtok(NULL, " \t\r\n"))
+	{
+	    int token_len;
+	    
+	    if (!token) break;
+	    if (*token == '#') break;
+
+	    token_len = strlen(token);
+	    if (!token_len)
+		continue;
+	    argv[argc] = pj_pool_alloc(pool, token_len+1);
+	    pj_memcpy(argv[argc], token, token_len+1);
+	    ++argc;
+	}
+    }
+
+    /* Copy arguments from command line */
+    for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
+	argv[argc++] = (*app_argv)[i];
+
+    if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
+	printf("Too many arguments specified in cmd line/config file\n");
+	fclose(fhnd);
+	return -1;
+    }
+
+    fclose(fhnd);
+
+    /* Assign the new command line back to the original command line. */
+    *app_argc = argc;
+    *app_argv = argv;
+    return 0;
+
+}
+
+
+/* Parse arguments. */
+static pj_status_t parse_args(int argc, char *argv[])
+{
+    int c;
+    int option_index;
+    enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, 
+	   OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
+	   OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR,
+	   OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT, 
+	   OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
+	   OPT_USE_STUN1, OPT_USE_STUN2, 
+	   OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
+	   OPT_AUTO_ANSWER, OPT_AUTO_HANGUP};
+    struct option long_options[] = {
+	{ "config-file",1, 0, OPT_CONFIG_FILE},
+	{ "log-file",	1, 0, OPT_LOG_FILE},
+	{ "log-level",	1, 0, OPT_LOG_LEVEL},
+	{ "app-log-level",1,0,OPT_APP_LOG_LEVEL},
+	{ "help",	0, 0, OPT_HELP},
+	{ "version",	0, 0, OPT_VERSION},
+	{ "null-audio", 0, 0, OPT_NULL_AUDIO},
+	{ "local-port", 1, 0, OPT_LOCAL_PORT},
+	{ "proxy",	1, 0, OPT_PROXY},
+	{ "outbound",	1, 0, OPT_OUTBOUND_PROXY},
+	{ "registrar",	1, 0, OPT_REGISTRAR},
+	{ "reg-timeout",1, 0, OPT_REG_TIMEOUT},
+	{ "id",		1, 0, OPT_ID},
+	{ "contact",	1, 0, OPT_CONTACT},
+	{ "realm",	1, 0, OPT_REALM},
+	{ "username",	1, 0, OPT_USERNAME},
+	{ "password",	1, 0, OPT_PASSWORD},
+	{ "use-stun1",  1, 0, OPT_USE_STUN1},
+	{ "use-stun2",  1, 0, OPT_USE_STUN2},
+	{ "add-buddy",  1, 0, OPT_ADD_BUDDY},
+	{ "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
+	{ "no-presence", 0, 0, OPT_NO_PRESENCE},
+	{ "auto-answer",1, 0, OPT_AUTO_ANSWER},
+	{ "auto-hangup",1, 0, OPT_AUTO_HANGUP},
+	{ NULL, 0, 0, 0}
+    };
+    pj_status_t status;
+    char *config_file = NULL;
+
+    /* Run getopt once to see if user specifies config file to read. */
+    while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+	switch (c) {
+	case OPT_CONFIG_FILE:
+	    config_file = optarg;
+	    break;
+	}
+	if (config_file)
+	    break;
+    }
+
+    if (config_file) {
+	status = read_config_file(pjsua.pool, config_file, &argc, &argv);
+	if (status != 0)
+	    return status;
+    }
+
+
+    /* Reinitialize and re-run getopt again, possibly with new arguments
+     * read from config file.
+     */
+    optind = 0;
+    while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+	char *p;
+	pj_str_t tmp;
+	long lval;
+
+	switch (c) {
+
+	case OPT_LOG_FILE:
+	    pjsua.log_filename = optarg;
+	    break;
+
+	case OPT_LOG_LEVEL:
+	    c = pj_strtoul(pj_cstr(&tmp, optarg));
+	    if (c < 0 || c > 6) {
+		printf("Error: expecting integer value 0-6 for --log-level\n");
+		return PJ_EINVAL;
+	    }
+	    pj_log_set_level( c );
+	    break;
+
+	case OPT_APP_LOG_LEVEL:
+	    pjsua.app_log_level = pj_strtoul(pj_cstr(&tmp, optarg));
+	    if (pjsua.app_log_level < 0 || pjsua.app_log_level > 6) {
+		printf("Error: expecting integer value 0-6 for --app-log-level\n");
+		return PJ_EINVAL;
+	    }
+	    break;
+
+	case OPT_HELP:
+	    usage();
+	    return PJ_EINVAL;
+
+	case OPT_VERSION:   /* version */
+	    pj_dump_config();
+	    return PJ_EINVAL;
+
+	case OPT_NULL_AUDIO:
+	    pjsua.null_audio = 1;
+	    break;
+
+	case OPT_LOCAL_PORT:   /* local-port */
+	    lval = pj_strtoul(pj_cstr(&tmp, optarg));
+	    if (lval < 1 || lval > 65535) {
+		printf("Error: expecting integer value for --local-port\n");
+		return PJ_EINVAL;
+	    }
+	    pjsua.sip_port = (pj_uint16_t)lval;
+	    break;
+
+	case OPT_PROXY:   /* proxy */
+	    if (verify_sip_url(optarg) != 0) {
+		printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg);
+		return PJ_EINVAL;
+	    }
+	    pjsua.proxy = pj_str(optarg);
+	    break;
+
+	case OPT_OUTBOUND_PROXY:   /* outbound proxy */
+	    if (verify_sip_url(optarg) != 0) {
+		printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg);
+		return PJ_EINVAL;
+	    }
+	    pjsua.outbound_proxy = pj_str(optarg);
+	    break;
+
+	case OPT_REGISTRAR:   /* registrar */
+	    if (verify_sip_url(optarg) != 0) {
+		printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg);
+		return PJ_EINVAL;
+	    }
+	    pjsua.registrar_uri = pj_str(optarg);
+	    break;
+
+	case OPT_REG_TIMEOUT:   /* reg-timeout */
+	    pjsua.reg_timeout = pj_strtoul(pj_cstr(&tmp,optarg));
+	    if (pjsua.reg_timeout < 1 || pjsua.reg_timeout > 3600) {
+		printf("Error: invalid value for --reg-timeout (expecting 1-3600)\n");
+		return PJ_EINVAL;
+	    }
+	    break;
+
+	case OPT_ID:   /* id */
+	    if (verify_sip_url(optarg) != 0) {
+		printf("Error: invalid SIP URL '%s' in local id argument\n", optarg);
+		return PJ_EINVAL;
+	    }
+	    pjsua.local_uri = pj_str(optarg);
+	    break;
+
+	case OPT_CONTACT:   /* contact */
+	    if (verify_sip_url(optarg) != 0) {
+		printf("Error: invalid SIP URL '%s' in contact argument\n", optarg);
+		return PJ_EINVAL;
+	    }
+	    pjsua.contact_uri = pj_str(optarg);
+	    break;
+
+	case OPT_USERNAME:   /* Default authentication user */
+	    if (!pjsua.cred_count) pjsua.cred_count = 1;
+	    pjsua.cred_info[0].username = pj_str(optarg);
+	    break;
+
+	case OPT_REALM:	    /* Default authentication realm. */
+	    if (!pjsua.cred_count) pjsua.cred_count = 1;
+	    pjsua.cred_info[0].realm = pj_str(optarg);
+	    break;
+
+	case OPT_PASSWORD:   /* authentication password */
+	    if (!pjsua.cred_count) pjsua.cred_count = 1;
+	    pjsua.cred_info[0].data_type = 0;
+	    pjsua.cred_info[0].data = pj_str(optarg);
+	    break;
+
+	case OPT_USE_STUN1:   /* STUN server 1 */
+	    p = pj_native_strchr(optarg, ':');
+	    if (p) {
+		*p = '\0';
+		pjsua.stun_srv1 = pj_str(optarg);
+		pjsua.stun_port1 = pj_strtoul(pj_cstr(&tmp, p+1));
+		if (pjsua.stun_port1 < 1 || pjsua.stun_port1 > 65535) {
+		    printf("Error: expecting port number with option --use-stun1\n");
+		    return PJ_EINVAL;
+		}
+	    } else {
+		pjsua.stun_port1 = 3478;
+		pjsua.stun_srv1 = pj_str(optarg);
+	    }
+	    break;
+
+	case OPT_USE_STUN2:   /* STUN server 2 */
+	    p = pj_native_strchr(optarg, ':');
+	    if (p) {
+		*p = '\0';
+		pjsua.stun_srv2 = pj_str(optarg);
+		pjsua.stun_port2 = pj_strtoul(pj_cstr(&tmp,p+1));
+		if (pjsua.stun_port2 < 1 || pjsua.stun_port2 > 65535) {
+		    printf("Error: expecting port number with option --use-stun2\n");
+		    return PJ_EINVAL;
+		}
+	    } else {
+		pjsua.stun_port2 = 3478;
+		pjsua.stun_srv2 = pj_str(optarg);
+	    }
+	    break;
+	}
+    }
+
+    if (optind != argc) {
+	printf("Error: unknown options %s\n", argv[optind]);
+	return PJ_EINVAL;
+    }
+
+    if (pjsua.reg_timeout == 0)
+	pjsua.reg_timeout = 3600;
+
+
+    return PJ_SUCCESS;
+}
+
+
+
+/*****************************************************************************
+ * main():
+ */
+int main(int argc, char *argv[])
 {
     /* Init default settings. */
@@ -224,10 +672,21 @@
 
 
-    /* Initialize pjsua.
-     * This will start worker thread, client registration, etc.
+    /* Initialize pjsua (to create pool etc).
      */
 
     if (pjsua_init() != PJ_SUCCESS)
 	return 1;
+
+
+    /* Parse command line arguments: */
+
+    if (parse_args(argc, argv) != PJ_SUCCESS)
+	return 1;
+
+
+    /* Init logging: */
+
+    app_logging_init();
+
 
     /* Register message logger to print incoming and outgoing
@@ -238,4 +697,13 @@
 
 
+    /* Start pjsua! */
+
+    if (pjsua_start() != PJ_SUCCESS) {
+
+	pjsua_destroy();
+	return 1;
+    }
+
+
     /* Sleep for a while, let any messages get printed to console: */
 
@@ -252,4 +720,10 @@
     pjsua_destroy();
 
+
+    /* Close logging: */
+
+    app_logging_shutdown();
+
+
     /* Exit... */
 
Index: pjproject/trunk/pjsip/src/pjsua/pjsua.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua/pjsua.c	(revision 140)
+++ pjproject/trunk/pjsip/src/pjsua/pjsua.c	(revision 141)
@@ -24,6 +24,5 @@
 
 
-#define PJSUA_LOCAL_URI	    "<sip:bennylp@192.168.0.7>"
-#define PJSUA_CONTACT_URI   "<sip:bennylp@192.168.0.7>"
+#define PJSUA_LOCAL_URI	    "<sip:user@127.0.0.1>"
 
 static char *PJSUA_DUMMY_SDP_OFFER = 
@@ -72,4 +71,8 @@
 
     pjsua.stun_port1 = pjsua.stun_port2 = 0;
+
+    /* Default URIs: */
+
+    pjsua.local_uri = pj_str(PJSUA_LOCAL_URI);
 }
 
@@ -130,5 +133,5 @@
 static void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
 {
-    ui_inv_on_state_changed(inv, e);
+    pjsua_ui_inv_on_state_changed(inv, e);
 }
 
@@ -397,4 +400,95 @@
 
 
+    /* Done */
+
+    return PJ_SUCCESS;
+
+
+on_error:
+    pjsip_endpt_destroy(pjsua.endpt);
+    pjsua.endpt = NULL;
+    return status;
+}
+
+
+static int PJ_THREAD_FUNC pjsua_worker_thread(void *arg)
+{
+    PJ_UNUSED_ARG(arg);
+
+    while (!pjsua.quit_flag) {
+	pj_time_val timeout = { 0, 10 };
+	pjsip_endpt_handle_events (pjsua.endpt, &timeout);
+    }
+
+    return 0;
+}
+
+/*
+ * Initialize pjsua application.
+ * This will initialize all libraries, create endpoint instance, and register
+ * pjsip modules.
+ */
+pj_status_t pjsua_init(void)
+{
+    pj_status_t status;
+
+    /* Init PJLIB logging: */
+
+    pj_log_set_level(pjsua.log_level);
+    pj_log_set_decor(pjsua.log_decor);
+
+
+    /* Init PJLIB: */
+
+    status = pj_init();
+    if (status != PJ_SUCCESS) {
+	pjsua_perror("pj_init() error", status);
+	return status;
+    }
+
+    /* Init memory pool: */
+
+    /* Init caching pool. */
+    pj_caching_pool_init(&pjsua.cp, &pj_pool_factory_default_policy, 0);
+
+    /* Create memory pool for application. */
+    pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL);
+
+
+    /* Init PJSIP and all the modules: */
+
+    status = init_stack();
+    if (status != PJ_SUCCESS) {
+	pj_caching_pool_destroy(&pjsua.cp);
+	pjsua_perror("Stack initialization has returned error", status);
+	return status;
+    }
+
+    /* Done. */
+    return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Start pjsua stack.
+ * This will start the registration process, if registration is configured.
+ */
+pj_status_t pjsua_start(void)
+{
+    int i;  /* Must be signed */
+    pjsip_transport *udp_transport;
+    pj_status_t status;
+
+    /* Init sockets (STUN etc): */
+
+    status = init_sockets();
+    if (status != PJ_SUCCESS) {
+	pj_caching_pool_destroy(&pjsua.cp);
+	pjsua_perror("init_sockets() has returned error", status);
+	return status;
+    }
+
+
     /* Add UDP transport: */
 
@@ -413,16 +507,82 @@
 
 	status = pjsip_udp_transport_attach( pjsua.endpt, pjsua.sip_sock,
-					     &addr_name, 1, NULL);
+					     &addr_name, 1, 
+					     &udp_transport);
 	if (status != PJ_SUCCESS) {
 	    pjsua_perror("Unable to start UDP transport", status);
-	    goto on_error;
-	}
-    }
-
-    /* Initialize local user info and contact: */
-
-    {
-	pj_strdup2(pjsua.pool, &pjsua.local_uri, PJSUA_LOCAL_URI);
-	pj_strdup2(pjsua.pool, &pjsua.contact_uri, PJSUA_CONTACT_URI);
+	    return status;
+	}
+    }
+
+    /* Initialize Contact URI, if one is not specified: */
+
+    if (pjsua.contact_uri.slen == 0 && pjsua.local_uri.slen) {
+
+	pjsip_uri *uri;
+	pjsip_sip_uri *sip_uri;
+	char contact[128];
+	int len;
+
+	/* The local Contact is the username@ip-addr, where
+	 *  - username is taken from the local URI,
+	 *  - ip-addr in UDP transport's address name (which may have been
+	 *    resolved from STUN.
+	 */
+	
+	/* Need to parse local_uri to get the elements: */
+
+	uri = pjsip_parse_uri(pjsua.pool, pjsua.local_uri.ptr, 
+			      pjsua.local_uri.slen, 0);
+	if (uri == NULL) {
+	    pjsua_perror("Invalid local URI", PJSIP_EINVALIDURI);
+	    return PJSIP_EINVALIDURI;
+	}
+
+
+	/* Local URI MUST be a SIP or SIPS: */
+
+	if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) {
+	    pjsua_perror("Invalid local URI", PJSIP_EINVALIDSCHEME);
+	    return PJSIP_EINVALIDSCHEME;
+	}
+
+
+	/* Get the SIP URI object: */
+
+	sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
+
+	
+	/* Build temporary contact string. */
+
+	if (sip_uri->user.slen) {
+
+	    /* With the user part. */
+	    len = pj_snprintf(contact, sizeof(contact),
+			      "<sip:%.*s@%.*s:%d>",
+			      sip_uri->user.slen,
+			      sip_uri->user.ptr,
+			      udp_transport->local_name.host.slen,
+			      udp_transport->local_name.host.ptr,
+			      udp_transport->local_name.port);
+	} else {
+
+	    /* Without user part */
+
+	    len = pj_snprintf(contact, sizeof(contact),
+			      "<sip:%.*s:%d>",
+			      udp_transport->local_name.host.slen,
+			      udp_transport->local_name.host.ptr,
+			      udp_transport->local_name.port);
+	}
+
+	if (len < 1 || len >= sizeof(contact)) {
+	    pjsua_perror("Invalid Contact", PJSIP_EURITOOLONG);
+	    return PJSIP_EURITOOLONG;
+	}
+
+	/* Duplicate Contact uri. */
+
+	pj_strdup2(pjsua.pool, &pjsua.contact_uri, contact);
+
     }
 
@@ -431,83 +591,4 @@
     PJ_TODO(INIT_GLOBAL_ROUTE_SET);
 
-
-    /* Start registration: */
-
-    PJ_TODO(START_REGISTRATION);
-
-    /* Done? */
-
-    return PJ_SUCCESS;
-
-
-on_error:
-    pjsip_endpt_destroy(pjsua.endpt);
-    pjsua.endpt = NULL;
-    return status;
-}
-
-
-static int PJ_THREAD_FUNC pjsua_worker_thread(void *arg)
-{
-    PJ_UNUSED_ARG(arg);
-
-    while (!pjsua.quit_flag) {
-	pj_time_val timeout = { 0, 10 };
-	pjsip_endpt_handle_events (pjsua.endpt, &timeout);
-    }
-
-    return 0;
-}
-
-/*
- * Initialize pjsua application.
- * This will start the registration process, if registration is configured.
- */
-pj_status_t pjsua_init(void)
-{
-    int i;  /* Must be signed */
-    pj_status_t status;
-
-    /* Init PJLIB logging: */
-
-    pj_log_set_level(pjsua.log_level);
-    pj_log_set_decor(pjsua.log_decor);
-
-
-    /* Init PJLIB: */
-
-    status = pj_init();
-    if (status != PJ_SUCCESS) {
-	pjsua_perror("pj_init() error", status);
-	return status;
-    }
-
-    /* Init memory pool: */
-
-    /* Init caching pool. */
-    pj_caching_pool_init(&pjsua.cp, &pj_pool_factory_default_policy, 0);
-
-    /* Create memory pool for application. */
-    pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL);
-
-
-    /* Init sockets (STUN etc): */
-
-    status = init_sockets();
-    if (status != PJ_SUCCESS) {
-	pj_caching_pool_destroy(&pjsua.cp);
-	pjsua_perror("init_sockets() has returned error", status);
-	return status;
-    }
-
-
-    /* Init PJSIP and all the modules: */
-
-    status = init_stack();
-    if (status != PJ_SUCCESS) {
-	pj_caching_pool_destroy(&pjsua.cp);
-	pjsua_perror("Stack initialization has returned error", status);
-	return status;
-    }
 
     /* Create worker thread(s), if required: */
@@ -527,5 +608,19 @@
     }
 
-    /* Done. */
+    /* Start registration: */
+
+    /* Create client registration session: */
+
+    status = pjsua_regc_init();
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Perform registration, if required. */
+    if (pjsua.regc) {
+	pjsua_regc_update(1);
+    }
+
+
+
     return PJ_SUCCESS;
 }
@@ -538,4 +633,14 @@
 {
     int i;
+
+    /* Unregister, if required: */
+    if (pjsua.regc) {
+
+	pjsua_regc_update(0);
+
+	/* Wait for some time to allow unregistration to complete: */
+
+	pj_thread_sleep(500);
+    }
 
     /* Signal threads to quit: */
@@ -611,7 +716,12 @@
 
 
+    /* Set dialog Route-Set: */
+
+    PJ_TODO(INIT_DIALOG_ROUTE_SET);
+
     /* Set credentials: */
 
-    PJ_TODO(SET_DIALOG_CREDENTIALS);
+    pjsip_auth_clt_set_credentials( &dlg->auth_sess, pjsua.cred_count, 
+				    pjsua.cred_info);
 
 
Index: pjproject/trunk/pjsip/src/pjsua/pjsua.h
===================================================================
--- pjproject/trunk/pjsip/src/pjsua/pjsua.h	(revision 140)
+++ pjproject/trunk/pjsip/src/pjsua/pjsua.h	(revision 141)
@@ -48,6 +48,26 @@
 
     /* User info: */
+
     pj_str_t	     local_uri;	    /**< Uri in From: header.		*/
     pj_str_t	     contact_uri;   /**< Uri in Contact: header.	*/
+
+    /* Proxy URLs: */
+
+    pj_str_t	     proxy;
+    pj_str_t	     outbound_proxy;
+
+    /* Registration: */
+
+    pj_str_t	     registrar_uri;
+    pjsip_regc	    *regc;
+    pj_int32_t	     reg_timeout;
+    pj_timer_entry   regc_timer;
+
+
+    /* Authentication credentials: */
+
+    int		     cred_count;
+    pjsip_cred_info  cred_info[4];
+
 
     /* Threading: */
@@ -77,4 +97,10 @@
 
 
+    /* Media stack: */
+
+    pj_bool_t	     null_audio;
+    pj_med_mgr_t    *mmgr;
+
+
     /* Misc: */
     
@@ -82,4 +108,5 @@
     int		     app_log_level; /**< stdout log verbosity.		*/
     unsigned	     log_decor;	    /**< Log decoration.		*/
+    char	    *log_filename;  /**< Log filename.			*/
 
 } pjsua;
@@ -103,8 +130,23 @@
 
 /**
- * Initialize pjsua application.
- * This will start the registration process, if registration is configured.
+ * Initialize pjsua application. Application can call this before parsing
+ * application settings.
+ *
+ * This will initialize all libraries, create endpoint instance, and register
+ * pjsip modules. Transport will NOT be created however.
+ *
+ * Application may register module after calling this function.
  */
 pj_status_t pjsua_init(void);
+
+
+/**
+ * Start pjsua stack. Application calls this after pjsua settings has been
+ * configured.
+ *
+ * This will start the transport, worker threads (if any), and registration 
+ * process, if registration is configured.
+ */
+pj_status_t pjsua_start(void);
 
 
@@ -123,4 +165,22 @@
 
 /*****************************************************************************
+ * PJSUA Client Registration API.
+ */
+
+/**
+ * Initialize client registration session.
+ *
+ * @param app_callback	Optional callback
+ */
+pj_status_t pjsua_regc_init(void);
+
+/**
+ * Update registration or perform unregistration. If renew argument is zero,
+ * this will start unregistration process.
+ */
+void pjsua_regc_update(pj_bool_t renew);
+
+
+/*****************************************************************************
  * User Interface API.
  * The UI API specifies functions that will be called by pjsua upon
@@ -131,5 +191,5 @@
  * Notify UI when invite state has changed.
  */
-void ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e);
+void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e);
 
 
