Index: /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 779)
+++ /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 780)
@@ -38,4 +38,5 @@
     pjsua_logging_config    log_cfg;
     pjsua_media_config	    media_cfg;
+    pj_bool_t		    no_refersub;
     pj_bool_t		    no_tcp;
     pj_bool_t		    no_udp;
@@ -161,4 +162,5 @@
     puts  ("  --thread-cnt=N      Number of worker threads (default:1)");
     puts  ("  --duration=SEC      Set maximum call duration (default:no limit)");
+    puts  ("  --norefersub        Suppress event subscription when transfering calls");
 
     puts  ("");
@@ -288,4 +290,5 @@
 	   OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS, 
 	   OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT,
+	   OPT_NOREFERSUB,
     };
     struct pj_getopt_option long_options[] = {
@@ -302,4 +305,5 @@
 	{ "no-tcp",     0, 0, OPT_NO_TCP},
 	{ "no-udp",     0, 0, OPT_NO_UDP},
+	{ "norefersub", 0, 0, OPT_NOREFERSUB},
 	{ "proxy",	1, 0, OPT_PROXY},
 	{ "outbound",	1, 0, OPT_OUTBOUND_PROXY},
@@ -457,4 +461,8 @@
 	    break;
 
+	case OPT_NOREFERSUB: /* norefersub */
+	    cfg->no_refersub = PJ_TRUE;
+	    break;
+
 	case OPT_NO_TCP: /* no-tcp */
 	    if (cfg->no_udp) {
@@ -948,4 +956,13 @@
     }
 
+    /* No TCP ? */
+    if (config->no_tcp) {
+	pj_strcat2(&cfg, "--no-tcp\n");
+    }
+
+    /* No UDP ? */
+    if (config->no_udp) {
+	pj_strcat2(&cfg, "--no-udp\n");
+    }
 
     /* STUN */
@@ -1090,4 +1107,10 @@
     }
 
+    /* norefersub ? */
+    if (config->no_refersub) {
+	pj_strcat2(&cfg, "--norefersub\n");
+    }
+
+
     pj_strcat2(&cfg, "\n#\n# Buddies:\n#\n");
 
@@ -1471,4 +1494,28 @@
 	      (int)from->slen, from->ptr,
 	      (is_typing?"is typing..":"has stopped typing")));
+}
+
+
+/**
+ * Call transfer request status.
+ */
+static void on_call_transfer_status(pjsua_call_id call_id,
+				    int status_code,
+				    const pj_str_t *status_text,
+				    pj_bool_t final,
+				    pj_bool_t *p_cont)
+{
+    PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s",
+	      call_id, status_code,
+	      (int)status_text->slen, status_text->ptr,
+	      (final ? "[final]" : "")));
+
+    if (status_code/100 == 2) {
+	PJ_LOG(3,(THIS_FILE, 
+	          "Call %d: call transfered successfully, disconnecting call",
+		  call_id));
+	pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL);
+	*p_cont = PJ_FALSE;
+    }
 }
 
@@ -2148,10 +2195,11 @@
 		}
 
-		/* Add Refer-Sub: false in outgoing REFER request */
 		pjsua_msg_data_init(&msg_data);
-		pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
-					       &STR_FALSE);
-		pj_list_push_back(&msg_data.hdr_list, &refer_sub);
-
+		if (app_config.no_refersub) {
+		    /* Add Refer-Sub: false in outgoing REFER request */
+		    pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
+						   &STR_FALSE);
+		    pj_list_push_back(&msg_data.hdr_list, &refer_sub);
+		}
 		if (result.nb_result != NO_NB) {
 		    if (result.nb_result == -1 || result.nb_result == 0)
@@ -2168,7 +2216,4 @@
 		    pjsua_call_xfer( current_call, &tmp, &msg_data);
 		}
-
-		/* Hangup call regardless of xfer status */
-		pjsua_call_hangup(current_call, PJSIP_SC_GONE, NULL, NULL);
 	    }
 	    break;
@@ -2462,4 +2507,5 @@
     app_config.cfg.cb.on_pager = &on_pager;
     app_config.cfg.cb.on_typing = &on_typing;
+    app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
 
     /* Initialize pjsua */
Index: /pjproject/trunk/pjsip/include/pjsip/sip_msg.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_msg.h	(revision 779)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_msg.h	(revision 780)
@@ -432,7 +432,7 @@
     PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606,
 
-    PJSIP_SC_TSX_TIMEOUT = 701,
+    PJSIP_SC_TSX_TIMEOUT = PJSIP_SC_REQUEST_TIMEOUT,
     /*PJSIP_SC_TSX_RESOLVE_ERROR = 702,*/
-    PJSIP_SC_TSX_TRANSPORT_ERROR = 703
+    PJSIP_SC_TSX_TRANSPORT_ERROR = PJSIP_SC_SERVICE_UNAVAILABLE
 
 } pjsip_status_code;
Index: /pjproject/trunk/pjsip/include/pjsip/sip_parser.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_parser.h	(revision 779)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_parser.h	(revision 780)
@@ -25,5 +25,5 @@
  */
 
-#include <pjsip/sip_types.h>
+#include <pjsip/sip_msg.h>
 #include <pjlib-util/scanner.h>
 #include <pj/list.h>
@@ -190,4 +190,17 @@
 				     char *buf, pj_size_t size,
 				     unsigned options);
+
+/**
+ * Parse SIP status line.
+ *
+ * @param buf		Text buffer to parse.
+ * @param size		The size of the buffer.
+ * @param status_line	Structure to receive the parsed elements.
+ *
+ * @return		PJ_SUCCESS if a status line is parsed successfully.
+ */
+PJ_DECL(pj_status_t) pjsip_parse_status_line(char *buf, pj_size_t size,
+					     pjsip_status_line *status_line);
+
 
 /**
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 779)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 780)
@@ -310,7 +310,30 @@
      * transfer.
      */
-    void (*on_call_transfered)(pjsua_call_id call_id,
-			       const pj_str_t *dst,
-			       pjsip_status_code *code);
+    void (*on_call_transfer_request)(pjsua_call_id call_id,
+				     const pj_str_t *dst,
+				     pjsip_status_code *code);
+
+    /**
+     * Notify application of the status of previously sent call
+     * transfer request. Application can monitor the status of the
+     * call transfer request, for example to decide whether to 
+     * terminate existing call.
+     *
+     * @param call_id	    Call ID.
+     * @param status_code   Status progress of the transfer request.
+     * @param status_text   Status progress text.
+     * @param final	    If non-zero, no further notification will
+     *			    be reported. The status_code specified in
+     *			    this callback is the final status.
+     * @param p_cont	    Initially will be set to non-zero, application
+     *			    can set this to FALSE if it no longer wants
+     *			    to receie further notification (for example,
+     *			    after it hangs up the call).
+     */
+    void (*on_call_transfer_status)(pjsua_call_id call_id,
+				    int status_code,
+				    const pj_str_t *status_text,
+				    pj_bool_t final,
+				    pj_bool_t *p_cont);
 
     /**
Index: /pjproject/trunk/pjsip/src/pjsip/sip_dialog.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_dialog.c	(revision 779)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_dialog.c	(revision 780)
@@ -794,7 +794,21 @@
     for (index=0; index<dlg->usage_cnt; ++index) {
 	if (dlg->usage[index] == mod) {
-	    pj_assert(!"This module is already registered");
+	    /* Module may be registered more than once in the same dialog.
+	     * For example, when call transfer fails, application may retry
+	     * call transfer on the same dialog.
+	     * So return PJ_SUCCESS here.
+	     */
+	    PJ_LOG(4,(dlg->obj_name, 
+		      "Module %.*s already registered as dialog usage, "
+		      "updating the data %p",
+		      (int)mod->name.slen, mod->name.ptr, mod_data));
+	    dlg->mod_data[mod->id] = mod_data;
+
 	    pjsip_dlg_dec_lock(dlg);
-	    return PJSIP_ETYPEEXISTS;
+	    return PJ_SUCCESS;
+
+	    //pj_assert(!"This module is already registered");
+	    //pjsip_dlg_dec_lock(dlg);
+	    //return PJSIP_ETYPEEXISTS;
 	}
 
Index: /pjproject/trunk/pjsip/src/pjsip/sip_parser.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_parser.c	(revision 779)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_parser.c	(revision 780)
@@ -1472,4 +1472,34 @@
 }
 
+
+/*
+ * Public API to parse SIP status line.
+ */
+PJ_DEF(pj_status_t) pjsip_parse_status_line( char *buf, pj_size_t size,
+					     pjsip_status_line *status_line)
+{
+    pj_scanner scanner;
+    PJ_USE_EXCEPTION;
+
+    pj_bzero(status_line, sizeof(*status_line));
+    pj_scan_init(&scanner, buf, size, 0, &on_syntax_error);
+
+    PJ_TRY {
+	int_parse_status_line(&scanner, status_line);
+    } 
+    PJ_CATCH_ANY {
+	/* Tolerate the error if it is caused only by missing newline */
+	if (status_line->code == 0 && status_line->reason.slen == 0) {
+	    pj_scan_fini(&scanner);
+	    return PJSIP_EINVALIDMSG;
+	}
+    }
+    PJ_END;
+
+    pj_scan_fini(&scanner);
+    return PJ_SUCCESS;
+}
+
+
 /* Parse ending of header. */
 static void parse_hdr_end( pj_scanner *scanner )
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 779)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 780)
@@ -72,5 +72,17 @@
  * has changed.
  */
-static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
+static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
+static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
+
+/*
+ * Callback called by event framework when NOTIFY is received for outgoing
+ * REFER subscription.
+ */
+static void xfer_on_rx_notify(pjsip_evsub *sub, 
+			      pjsip_rx_data *rdata,
+			      int *p_st_code,
+			      pj_str_t **p_st_text,
+			      pjsip_hdr *res_hdr,
+			      pjsip_msg_body **p_body);
 
 /*
@@ -1079,5 +1091,5 @@
     /* Create xfer client subscription. */
     pj_bzero(&xfer_cb, sizeof(xfer_cb));
-    xfer_cb.on_evsub_state = &xfer_on_evsub_state;
+    xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
 
     status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
@@ -1087,4 +1099,7 @@
 	return status;
     }
+
+    /* Associate this call with the client subscription */
+    pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
 
     /*
@@ -2145,5 +2160,161 @@
  * has changed.
  */
-static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
+static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
+{
+    
+    PJ_UNUSED_ARG(event);
+
+    /*
+     * When subscription is accepted (got 200/OK to REFER), check if 
+     * subscription suppressed.
+     */
+    if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
+
+	pjsip_rx_data *rdata;
+	pjsip_generic_string_hdr *refer_sub;
+	const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
+	pjsua_call *call;
+
+	call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
+
+	/* Must be receipt of response message */
+	pj_assert(event->type == PJSIP_EVENT_TSX_STATE && 
+		  event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
+	rdata = event->body.tsx_state.src.rdata;
+
+	/* Find Refer-Sub header */
+	refer_sub = (pjsip_generic_string_hdr*)
+		    pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, 
+					       &REFER_SUB, NULL);
+
+	/* Check if subscription is suppressed */
+	if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
+	    /* Since no subscription is desired, assume that call has been
+	     * transfered successfully.
+	     */
+	    if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
+		const pj_str_t ACCEPTED = { "Accepted", 8 };
+		pj_bool_t cont = PJ_FALSE;
+		(*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index, 
+							       200,
+							       &ACCEPTED,
+							       PJ_TRUE,
+							       &cont);
+	    }
+
+	    /* Yes, subscription is suppressed.
+	     * Terminate our subscription now.
+	     */
+	    PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
+				 "event subcription..."));
+	    pjsip_evsub_terminate(sub, PJ_TRUE);
+
+	} else {
+	    /* Notify application about call transfer progress. 
+	     * Initially notify with 100/Accepted status.
+	     */
+	    if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
+		const pj_str_t ACCEPTED = { "Accepted", 8 };
+		pj_bool_t cont = PJ_FALSE;
+		(*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index, 
+							       100,
+							       &ACCEPTED,
+							       PJ_FALSE,
+							       &cont);
+	    }
+	}
+    }
+    /*
+     * On incoming NOTIFY, notify application about call transfer progress.
+     */
+    else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
+	     pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) 
+    {
+	pjsua_call *call;
+	pjsip_msg *msg;
+	pjsip_msg_body *body;
+	pjsip_status_line status_line;
+	pj_bool_t is_last;
+	pj_bool_t cont;
+	pj_status_t status;
+
+	call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
+
+	/* When subscription is terminated, clear the xfer_sub member of 
+	 * the inv_data.
+	 */
+	if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+	    pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
+	    PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
+
+	}
+
+	if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
+	    /* Application is not interested with call progress status */
+	    return;
+	}
+
+	/* This better be a NOTIFY request */
+	if (event->type == PJSIP_EVENT_TSX_STATE &&
+	    event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
+	{
+	    pjsip_rx_data *rdata;
+
+	    rdata = event->body.tsx_state.src.rdata;
+
+	    /* Check if there's body */
+	    msg = rdata->msg_info.msg;
+	    body = msg->body;
+	    if (!body) {
+		PJ_LOG(4,(THIS_FILE, 
+			  "Warning: received NOTIFY without message body"));
+		return;
+	    }
+
+	    /* Check for appropriate content */
+	    if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
+		pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
+	    {
+		PJ_LOG(4,(THIS_FILE, 
+			  "Warning: received NOTIFY with non message/sipfrag "
+			  "content"));
+		return;
+	    }
+
+	    /* Try to parse the content */
+	    status = pjsip_parse_status_line(body->data, body->len, 
+					     &status_line);
+	    if (status != PJ_SUCCESS) {
+		PJ_LOG(4,(THIS_FILE, 
+			  "Warning: received NOTIFY with invalid "
+			  "message/sipfrag content"));
+		return;
+	    }
+
+	} else {
+	    status_line.code = 500;
+	    status_line.reason = *pjsip_get_status_text(500);
+	}
+
+	/* Notify application */
+	is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
+	cont = !is_last;
+	(*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index, 
+						       status_line.code,
+						       &status_line.reason,
+						       is_last, &cont);
+
+	if (!cont) {
+	    pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
+	}
+    }
+}
+
+
+/*
+ * Callback called by event framework when the xfer subscription state
+ * has changed.
+ */
+static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
 {
     
@@ -2164,36 +2335,5 @@
 	call->xfer_sub = NULL;
 
-	PJ_LOG(4,(THIS_FILE, "Xfer subscription terminated"));
-
-    }
-    /*
-     * When subscription is accepted (got 200/OK to REFER), check if 
-     * subscription suppressed.
-     */
-    else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
-
-	pjsip_rx_data *rdata;
-	pjsip_generic_string_hdr *refer_sub;
-	const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
-
-	/* Must be receipt of response message */
-	pj_assert(event->type == PJSIP_EVENT_TSX_STATE && 
-		  event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
-	rdata = event->body.tsx_state.src.rdata;
-
-	/* Find Refer-Sub header */
-	refer_sub = (pjsip_generic_string_hdr*)
-		    pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, 
-					       &REFER_SUB, NULL);
-
-	/* Check if subscription is suppressed */
-	if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
-	    /* Yes, subscription is suppressed.
-	     * Terminate our subscription now.
-	     */
-	    PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
-				 "event subcription..."));
-	    pjsip_evsub_terminate(sub, PJ_TRUE);
-	}
+	PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
     }
 }
@@ -2247,7 +2387,8 @@
     /* Notify callback */
     code = PJSIP_SC_OK;
-    if (pjsua_var.ua_cfg.cb.on_call_transfered)
-	(*pjsua_var.ua_cfg.cb.on_call_transfered)(existing_call->index,
-						  &refer_to->hvalue, &code);
+    if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
+	(*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
+							&refer_to->hvalue, 
+							&code);
 
     if (code < 200)
@@ -2305,5 +2446,5 @@
 	/* Init callback */
 	pj_bzero(&xfer_cb, sizeof(xfer_cb));
-	xfer_cb.on_evsub_state = &xfer_on_evsub_state;
+	xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
 
 	/* Init additional header list to be sent with REFER response */
