Index: pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 1463)
+++ pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 1469)
@@ -32,4 +32,26 @@
 #include <pj/log.h>
 
+/* 
+ * Note on offer/answer:
+ *
+ * The offer/answer framework in this implementation assumes the occurence
+ * of SDP in a particular request/response according to this table:
+
+		  offer   answer    Note:
+    ========================================================================
+    INVITE	    X		    INVITE may contain offer
+    18x/INVITE	    X	    X	    Response may contain offer or answer
+    2xx/INVITE	    X	    X	    Response may contain offer or answer
+    ACK			    X	    ACK may contain answer
+
+    PRACK		    X	    PRACK can only contain answer
+    2xx/PRACK	    		    Response may not have offer nor answer
+
+    UPDATE	    X		    UPDATE may only contain offer
+    2xx/UPDATE		    X	    Response may only contain answer
+    ========================================================================
+
+  *
+  */
 
 #define THIS_FILE	"sip_inv.c"
@@ -47,4 +69,11 @@
 };
 
+/* UPDATE method */
+const pjsip_method pjsip_update_method =
+{
+    PJSIP_OTHER_METHOD,
+    { "UPDATE", 6 }
+};
+
 /*
  * Static prototypes.
@@ -67,4 +96,7 @@
 						  pjsip_transaction *tsx,
 						  pjsip_rx_data *rdata);
+static pj_status_t inv_negotiate_sdp( pjsip_inv_session *inv );
+static pjsip_msg_body *create_sdp_body(pj_pool_t *pool,
+				       const pjmedia_sdp_session *c_sdp);
 static pj_status_t process_answer( pjsip_inv_session *inv,
 				   int st_code,
@@ -120,8 +152,9 @@
 static pj_status_t mod_inv_load(pjsip_endpoint *endpt)
 {
-    pj_str_t allowed[] = {{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}};
+    pj_str_t allowed[] = {{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6},
+			    { "UPDATE", 6}};
     pj_str_t accepted = { "application/sdp", 15 };
 
-    /* Register supported methods: INVITE, ACK, BYE, CANCEL */
+    /* Register supported methods: INVITE, ACK, BYE, CANCEL, UPDATE */
     pjsip_endpt_add_capability(endpt, &mod_inv.mod, PJSIP_H_ALLOW, NULL,
 			       PJ_ARRAY_SIZE(allowed), allowed);
@@ -187,4 +220,9 @@
 	prev_state != PJSIP_INV_STATE_DISCONNECTED) 
     {
+	if (inv->last_ack) {
+	    pjsip_tx_data_dec_ref(inv->last_ack);
+	    inv->last_ack = NULL;
+	}
+	pjsip_100rel_end_session(inv);
 	pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod);
     }
@@ -210,4 +248,54 @@
 
 
+/*
+ * Check if outgoing request needs to have SDP answer.
+ * This applies for both ACK and PRACK requests.
+ */
+static pjmedia_sdp_session *inv_has_pending_answer(pjsip_inv_session *inv,
+						   pjsip_transaction *tsx)
+{
+    pjmedia_sdp_neg_state neg_state;
+    pjmedia_sdp_session *sdp = NULL;
+    pj_status_t status;
+
+    /* If SDP negotiator is ready, start negotiation. */
+
+    /* Start nego when appropriate. */
+    neg_state = inv->neg ? pjmedia_sdp_neg_get_state(inv->neg) :
+		PJMEDIA_SDP_NEG_STATE_NULL;
+
+    if (neg_state == PJMEDIA_SDP_NEG_STATE_DONE) {
+
+	/* Nothing to do */
+
+    } else if (neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO &&
+	       pjmedia_sdp_neg_has_local_answer(inv->neg) )
+    {
+	struct tsx_inv_data *tsx_inv_data;
+
+	/* Get invite session's transaction data */
+	tsx_inv_data = (struct tsx_inv_data*) tsx->mod_data[mod_inv.mod.id];
+
+	status = inv_negotiate_sdp(inv);
+	if (status != PJ_SUCCESS)
+	    return NULL;
+	
+	/* Mark this transaction has having SDP offer/answer done. */
+	tsx_inv_data->sdp_done = 1;
+
+	status = pjmedia_sdp_neg_get_active_local(inv->neg, &sdp);
+
+    } else {
+	/* This remark is only valid for ACK.
+	PJ_LOG(4,(inv->dlg->obj_name,
+		  "FYI, the SDP negotiator state (%s) is in a mess "
+		  "when sending this ACK/PRACK request",
+		  pjmedia_sdp_neg_state_str(neg_state)));
+	 */
+    }
+
+    return sdp;
+}
+
 
 /*
@@ -216,5 +304,4 @@
 static pj_status_t inv_send_ack(pjsip_inv_session *inv, pjsip_rx_data *rdata)
 {
-    pjsip_tx_data *tdata;
     pj_status_t status;
 
@@ -222,13 +309,40 @@
 	      pjsip_rx_data_get_info(rdata)));
 
-    status = pjsip_dlg_create_request(inv->dlg, pjsip_get_ack_method(), 
-				      rdata->msg_info.cseq->cseq, &tdata);
-    if (status != PJ_SUCCESS) {
-	/* Better luck next time */
-	pj_assert(!"Unable to create ACK!");
-	return status;
-    }
-
-    status = pjsip_dlg_send_request(inv->dlg, tdata, -1, NULL);
+    /* Check if we have cached ACK request */
+    if (inv->last_ack && rdata->msg_info.cseq->cseq == inv->last_ack_cseq) {
+	pjsip_tx_data_add_ref(inv->last_ack);
+    } else {
+	pjmedia_sdp_session *sdp = NULL;
+
+	/* Destroy last_ack */
+	if (inv->last_ack) {
+	    pjsip_tx_data_dec_ref(inv->last_ack);
+	    inv->last_ack = NULL;
+	}
+
+	/* Create new ACK request */
+	status = pjsip_dlg_create_request(inv->dlg, pjsip_get_ack_method(), 
+					  rdata->msg_info.cseq->cseq, 
+					  &inv->last_ack);
+	if (status != PJ_SUCCESS) {
+	    /* Better luck next time */
+	    pj_assert(!"Unable to create ACK!");
+	    return status;
+	}
+
+	/* See if we have pending SDP answer to send */
+	sdp = inv_has_pending_answer(inv, inv->invite_tsx);
+	if (sdp) {
+	    inv->last_ack->msg->body=create_sdp_body(inv->last_ack->pool, sdp);
+	}
+
+
+	/* Keep this for subsequent response retransmission */
+	inv->last_ack_cseq = rdata->msg_info.cseq->cseq;
+	pjsip_tx_data_add_ref(inv->last_ack);
+    }
+
+    /* Send ACK */
+    status = pjsip_dlg_send_request(inv->dlg, inv->last_ack, -1, NULL);
     if (status != PJ_SUCCESS) {
 	/* Better luck next time */
@@ -493,12 +607,4 @@
 	options |= PJSIP_INV_SUPPORT_100REL;
 
-#if !PJSIP_HAS_100REL
-    /* options cannot specify 100rel if 100rel is disabled */
-    PJ_ASSERT_RETURN(
-	(options & (PJSIP_INV_REQUIRE_100REL | PJSIP_INV_SUPPORT_100REL))==0,
-	PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EXTENSION));
-    
-#endif
-
     if (options & PJSIP_INV_REQUIRE_TIMER)
 	options |= PJSIP_INV_SUPPORT_TIMER;
@@ -539,8 +645,6 @@
     pjsip_dlg_inc_session(dlg, &mod_inv.mod);
 
-#if PJSIP_HAS_100REL
     /* Create 100rel handler */
     pjsip_100rel_attach(inv);
-#endif
 
     /* Done */
@@ -944,12 +1048,4 @@
 	options |= PJSIP_INV_SUPPORT_100REL;
 
-#if !PJSIP_HAS_100REL
-    /* options cannot specify 100rel if 100rel is disabled */
-    PJ_ASSERT_RETURN(
-	(options & (PJSIP_INV_REQUIRE_100REL | PJSIP_INV_SUPPORT_100REL))==0,
-	PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EXTENSION));
-    
-#endif
-
     if (options & PJSIP_INV_REQUIRE_TIMER)
 	options |= PJSIP_INV_SUPPORT_TIMER;
@@ -1021,10 +1117,8 @@
     inv->invite_tsx->mod_data[mod_inv.mod.id] = tsx_inv_data;
 
-#if PJSIP_HAS_100REL
     /* Create 100rel handler */
     if (inv->options & PJSIP_INV_REQUIRE_100REL) {
 	    pjsip_100rel_attach(inv);
     }
-#endif
 
     /* Done */
@@ -1240,5 +1334,5 @@
 
 /*
- * Negotiate SDP.
+ * Initiate SDP negotiation in the SDP negotiator.
  */
 static pj_status_t inv_negotiate_sdp( pjsip_inv_session *inv )
@@ -1782,14 +1876,82 @@
 PJ_DEF(pj_status_t) pjsip_inv_update (	pjsip_inv_session *inv,
 					const pj_str_t *new_contact,
-					const pjmedia_sdp_session *new_offer,
+					const pjmedia_sdp_session *offer,
 					pjsip_tx_data **p_tdata )
 {
-    PJ_UNUSED_ARG(inv);
-    PJ_UNUSED_ARG(new_contact);
-    PJ_UNUSED_ARG(new_offer);
-    PJ_UNUSED_ARG(p_tdata);
-
-    PJ_TODO(CREATE_UPDATE_REQUEST);
-    return PJ_ENOTSUP;
+    pjsip_contact_hdr *contact_hdr = NULL;
+    pjsip_tx_data *tdata = NULL;
+    pjmedia_sdp_session *sdp_copy;
+    pj_status_t status = PJ_SUCCESS;
+
+    /* Verify arguments. */
+    PJ_ASSERT_RETURN(inv && p_tdata && offer, PJ_EINVAL);
+
+    /* Dialog must have been established */
+    PJ_ASSERT_RETURN(inv->dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED,
+		     PJ_EINVALIDOP);
+
+    /* Invite session must not have been disconnected */
+    PJ_ASSERT_RETURN(inv->state < PJSIP_INV_STATE_DISCONNECTED,
+		     PJ_EINVALIDOP);
+
+    /* Lock dialog. */
+    pjsip_dlg_inc_lock(inv->dlg);
+
+    /* Process offer */
+    if (pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) {
+	PJ_LOG(4,(inv->dlg->obj_name, 
+		  "Invalid SDP offer/answer state for UPDATE"));
+	status = PJ_EINVALIDOP;
+	goto on_error;
+    }
+
+    status = pjmedia_sdp_neg_modify_local_offer(inv->pool,inv->neg,
+						offer);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+
+    /* Update Contact if required */
+    if (new_contact) {
+	pj_str_t tmp;
+	const pj_str_t STR_CONTACT = { "Contact", 7 };
+
+	pj_strdup_with_null(inv->dlg->pool, &tmp, new_contact);
+	contact_hdr = (pjsip_contact_hdr*)
+		      pjsip_parse_hdr(inv->dlg->pool, &STR_CONTACT, 
+				      tmp.ptr, tmp.slen, NULL);
+	if (!contact_hdr) {
+	    status = PJSIP_EINVALIDURI;
+	    goto on_error;
+	}
+
+	inv->dlg->local.contact = contact_hdr;
+    }
+
+    /* Create request */
+    status = pjsip_dlg_create_request(inv->dlg, &pjsip_update_method,
+				      -1, &tdata);
+    if (status != PJ_SUCCESS)
+	    goto on_error;
+
+    /* Attach SDP body */
+    sdp_copy = pjmedia_sdp_session_clone(tdata->pool, offer);
+    pjsip_create_sdp_body(tdata->pool, sdp_copy, &tdata->msg->body);
+
+    /* Unlock dialog. */
+    pjsip_dlg_dec_lock(inv->dlg);
+
+    *p_tdata = tdata;
+
+    return PJ_SUCCESS;
+
+on_error:
+    if (tdata)
+	pjsip_tx_data_dec_ref(tdata);
+
+    /* Unlock dialog. */
+    pjsip_dlg_dec_lock(inv->dlg);
+
+    return status;
 }
 
@@ -1833,11 +1995,9 @@
 			 PJ_EINVALIDOP);
 
-#if PJSIP_HAS_100REL
 	if (inv->options & PJSIP_INV_REQUIRE_100REL) {
-		status = pjsip_100rel_tx_response(inv, tdata);
+	    status = pjsip_100rel_tx_response(inv, tdata);
 	} else 
-#endif
 	{
-		status = pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata);
+	    status = pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata);
 	}
 
@@ -1988,4 +2148,186 @@
 
 /*
+ * Respond to incoming UPDATE request.
+ */
+static void inv_respond_incoming_update(pjsip_inv_session *inv,
+					pjsip_rx_data *rdata)
+{
+    pjmedia_sdp_neg_state neg_state;
+    pj_status_t status;
+    pjsip_tx_data *tdata = NULL;
+
+    neg_state = pjmedia_sdp_neg_get_state(inv->neg);
+
+    /* Send 491 if we receive UPDATE while we're waiting for an answer */
+    if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) {
+	status = pjsip_dlg_create_response(inv->dlg, rdata, 
+					   PJSIP_SC_REQUEST_PENDING, NULL,
+					   &tdata);
+    }
+    /* Send 500 with Retry-After header set randomly between 0 and 10 if we 
+     * receive UPDATE while we haven't sent answer.
+     */
+    else if (neg_state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER ||
+	     neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) {
+	status = pjsip_dlg_create_response(inv->dlg, rdata, 
+					   PJSIP_SC_INTERNAL_SERVER_ERROR,
+					   NULL, &tdata);
+
+    } else {
+	/* We receive new offer from remote */
+	inv_check_sdp_in_incoming_msg(inv, pjsip_rdata_get_tsx(rdata), rdata);
+
+	/* Application MUST have supplied the answer by now.
+	 * If so, negotiate the SDP.
+	 */
+	neg_state = pjmedia_sdp_neg_get_state(inv->neg);
+	if (neg_state != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO ||
+	    (status=inv_negotiate_sdp(inv)) != PJ_SUCCESS)
+	{
+	    /* Negotiation has failed */
+	    status = pjsip_dlg_create_response(inv->dlg, rdata, 
+					       PJSIP_SC_NOT_ACCEPTABLE_HERE,
+					       NULL, &tdata);
+	} else {
+	    /* New media has been negotiated successfully, send 200/OK */
+	    status = pjsip_dlg_create_response(inv->dlg, rdata, 
+					       PJSIP_SC_OK, NULL, &tdata);
+	    if (status == PJ_SUCCESS) {
+		pjmedia_sdp_session *sdp;
+		status = pjmedia_sdp_neg_get_active_local(inv->neg, &sdp);
+		if (status == PJ_SUCCESS)
+		    tdata->msg->body = create_sdp_body(tdata->pool, sdp);
+	    }
+	}
+    }
+
+    if (status != PJ_SUCCESS) {
+	if (tdata != NULL) {
+	    pjsip_tx_data_dec_ref(tdata);
+	    tdata = NULL;
+	}
+	return;
+    }
+
+    pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata), tdata);
+}
+
+
+/*
+ * Handle incoming response to UAC UPDATE request.
+ */
+static void inv_handle_update_response( pjsip_inv_session *inv,
+					pjsip_event *e)
+{
+    pjsip_transaction *tsx = e->body.tsx_state.tsx;
+    struct tsx_inv_data *tsx_inv_data = NULL;
+    pj_status_t status = -1;
+
+    /* Process 2xx response */
+    if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
+	tsx->status_code/100 == 2 &&
+	e->body.tsx_state.src.rdata->msg_info.msg->body)
+    {
+	status = inv_check_sdp_in_incoming_msg(inv, tsx, 
+					    e->body.tsx_state.src.rdata);
+
+    } else {
+	/* Get/attach invite session's transaction data */
+	tsx_inv_data = (struct tsx_inv_data*)tsx->mod_data[mod_inv.mod.id];
+	if (tsx_inv_data == NULL) {
+	    tsx_inv_data=PJ_POOL_ZALLOC_T(tsx->pool, struct tsx_inv_data);
+	    tsx_inv_data->inv = inv;
+	    tsx->mod_data[mod_inv.mod.id] = tsx_inv_data;
+	}
+    }
+
+    /* Otherwise if we don't get successful response, cancel
+     * our negotiator.
+     */
+    if (status != PJ_SUCCESS &&
+	pjmedia_sdp_neg_get_state(inv->neg)==PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER &&
+	tsx_inv_data && tsx_inv_data->sdp_done == PJ_FALSE) 
+    {
+	pjmedia_sdp_neg_cancel_offer(inv->neg);
+
+	/* Prevent from us cancelling different offer! */
+	tsx_inv_data->sdp_done = PJ_TRUE;
+    }
+}
+
+
+/*
+ * Handle incoming reliable response.
+ */
+static void inv_handle_incoming_reliable_response(pjsip_inv_session *inv,
+						  pjsip_rx_data *rdata)
+{
+    pjsip_tx_data *tdata;
+    pjmedia_sdp_session *sdp;
+    pj_status_t status;
+
+    /* Create PRACK */
+    status = pjsip_100rel_create_prack(inv, rdata, &tdata);
+    if (status != PJ_SUCCESS)
+	return;
+
+    /* See if we need to attach SDP answer on the PRACK request */
+    sdp = inv_has_pending_answer(inv, pjsip_rdata_get_tsx(rdata));
+    if (sdp) {
+	tdata->msg->body = create_sdp_body(tdata->pool, sdp);
+    }
+
+    /* Send PRACK (must be using 100rel module!) */
+    pjsip_100rel_send_prack(inv, tdata);
+}
+
+
+/*
+ * Handle incoming PRACK.
+ */
+static void inv_respond_incoming_prack(pjsip_inv_session *inv,
+				       pjsip_rx_data *rdata)
+{
+    pj_status_t status;
+
+    /* Run through 100rel module to see if we can accept this
+     * PRACK request. The 100rel will send 200/OK to PRACK request.
+     */
+    status = pjsip_100rel_on_rx_prack(inv, rdata);
+    if (status != PJ_SUCCESS)
+	return;
+
+    /* Now check for SDP answer in the PRACK request */
+    if (rdata->msg_info.msg->body) {
+	status = inv_check_sdp_in_incoming_msg(inv, 
+					pjsip_rdata_get_tsx(rdata), rdata);
+    } else {
+	/* No SDP body */
+	status = -1;
+    }
+
+    /* If SDP negotiation has been successful, also mark the
+     * SDP negotiation flag in the invite transaction to be
+     * done too.
+     */
+    if (status == PJ_SUCCESS && inv->invite_tsx) {
+	struct tsx_inv_data *tsx_inv_data;
+
+	/* Get/attach invite session's transaction data */
+	tsx_inv_data = (struct tsx_inv_data*) 
+		       inv->invite_tsx->mod_data[mod_inv.mod.id];
+	if (tsx_inv_data == NULL) {
+	    tsx_inv_data = PJ_POOL_ZALLOC_T(inv->invite_tsx->pool, 
+					    struct tsx_inv_data);
+	    tsx_inv_data->inv = inv;
+	    inv->invite_tsx->mod_data[mod_inv.mod.id] = tsx_inv_data;
+	}
+	
+	tsx_inv_data->sdp_done = PJ_TRUE;
+    }
+}
+
+
+/*
  * State NULL is before anything is sent/received.
  */
@@ -2073,4 +2415,9 @@
 					      e->body.tsx_state.src.rdata);
 
+		if (pjsip_100rel_is_reliable(e->body.tsx_state.src.rdata)) {
+		    inv_handle_incoming_reliable_response(
+			inv, e->body.tsx_state.src.rdata);
+		}
+
 	    } else {
 		/* Ignore 100 (Trying) response, as it doesn't change
@@ -2167,10 +2514,7 @@
 	}
 
-    } else if (inv->role == PJSIP_ROLE_UAC &&
-	       tsx->role == PJSIP_ROLE_UAC &&
-	       tsx->method.id == PJSIP_CANCEL_METHOD)
-    {
+    } else if (tsx->role == PJSIP_ROLE_UAC) {
 	/*
-	 * Handle case when outgoing CANCEL is answered with 481 (Call/
+	 * Handle case when outgoing request is answered with 481 (Call/
 	 * Transaction Does Not Exist), 408, or when it's timed out. In these
 	 * cases, disconnect session (i.e. dialog usage only).
@@ -2285,4 +2629,9 @@
 		inv_check_sdp_in_incoming_msg(inv, tsx, 
 					      e->body.tsx_state.src.rdata);
+
+		if (pjsip_100rel_is_reliable(e->body.tsx_state.src.rdata)) {
+		    inv_handle_incoming_reliable_response(
+			inv, e->body.tsx_state.src.rdata);
+		}
 	    }
 	    break;
@@ -2354,10 +2703,36 @@
 	inv_respond_incoming_cancel(inv, tsx, e->body.tsx_state.src.rdata);
 
-    } else if (inv->role == PJSIP_ROLE_UAC &&
-	       tsx->role == PJSIP_ROLE_UAC &&
-	       tsx->method.id == PJSIP_CANCEL_METHOD)
+    } else if (tsx->role == PJSIP_ROLE_UAS &&
+	       tsx->state == PJSIP_TSX_STATE_TRYING &&
+	       pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0)
     {
 	/*
-	 * Handle case when outgoing CANCEL is answered with 481 (Call/
+	 * Handle incoming UPDATE
+	 */
+	inv_respond_incoming_update(inv, e->body.tsx_state.src.rdata);
+
+
+    } else if (tsx->role == PJSIP_ROLE_UAC &&
+	       (tsx->state == PJSIP_TSX_STATE_COMPLETED ||
+	        tsx->state == PJSIP_TSX_STATE_TERMINATED) &&
+	       pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0)
+    {
+	/*
+	 * Handle response to outgoing UPDATE request.
+	 */
+	inv_handle_update_response(inv, e);
+
+    } else if (tsx->role == PJSIP_ROLE_UAS &&
+	       tsx->state == PJSIP_TSX_STATE_TRYING &&
+	       pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0)
+    {
+	/*
+	 * Handle incoming PRACK
+	 */
+	inv_respond_incoming_prack(inv, e->body.tsx_state.src.rdata);
+
+    } else if (tsx->role == PJSIP_ROLE_UAC) {
+	/*
+	 * Handle case when outgoing request is answered with 481 (Call/
 	 * Transaction Does Not Exist), 408, or when it's timed out. In these
 	 * cases, disconnect session (i.e. dialog usage only).
@@ -2467,5 +2842,49 @@
 	if (status != PJ_SUCCESS) return;
 
-    }
+    } else if (tsx->role == PJSIP_ROLE_UAS &&
+	       tsx->state == PJSIP_TSX_STATE_TRYING &&
+	       pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0)
+    {
+	/*
+	 * Handle incoming UPDATE
+	 */
+	inv_respond_incoming_update(inv, e->body.tsx_state.src.rdata);
+
+
+    } else if (tsx->role == PJSIP_ROLE_UAC &&
+	       (tsx->state == PJSIP_TSX_STATE_COMPLETED ||
+	        tsx->state == PJSIP_TSX_STATE_TERMINATED) &&
+	       pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0)
+    {
+	/*
+	 * Handle response to outgoing UPDATE request.
+	 */
+	inv_handle_update_response(inv, e);
+
+    } else if (tsx->role == PJSIP_ROLE_UAS &&
+	       tsx->state == PJSIP_TSX_STATE_TRYING &&
+	       pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0)
+    {
+	/*
+	 * Handle incoming PRACK
+	 */
+	inv_respond_incoming_prack(inv, e->body.tsx_state.src.rdata);
+
+    } else if (tsx->role == PJSIP_ROLE_UAC) {
+	/*
+	 * Handle case when outgoing request is answered with 481 (Call/
+	 * Transaction Does Not Exist), 408, or when it's timed out. In these
+	 * cases, disconnect session (i.e. dialog usage only).
+	 */
+	if (tsx->status_code == PJSIP_SC_CALL_TSX_DOES_NOT_EXIST ||
+	    tsx->status_code == PJSIP_SC_REQUEST_TIMEOUT ||
+	    tsx->status_code == PJSIP_SC_TSX_TIMEOUT ||
+	    tsx->status_code == PJSIP_SC_TSX_TRANSPORT_ERROR)
+	{
+	    inv_set_cause(inv, tsx->status_code, &tsx->status_text);
+	    inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
+	}
+    }
+
 }
 
@@ -2731,5 +3150,49 @@
 	    }
 	}
-    }
+
+    } else if (tsx->role == PJSIP_ROLE_UAS &&
+	       tsx->state == PJSIP_TSX_STATE_TRYING &&
+	       pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0)
+    {
+	/*
+	 * Handle incoming UPDATE
+	 */
+	inv_respond_incoming_update(inv, e->body.tsx_state.src.rdata);
+
+    } else if (tsx->role == PJSIP_ROLE_UAC &&
+	       (tsx->state == PJSIP_TSX_STATE_COMPLETED ||
+	        tsx->state == PJSIP_TSX_STATE_TERMINATED) &&
+	       pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0)
+    {
+	/*
+	 * Handle response to outgoing UPDATE request.
+	 */
+	inv_handle_update_response(inv, e);
+
+    } else if (tsx->role == PJSIP_ROLE_UAS &&
+	       tsx->state == PJSIP_TSX_STATE_TRYING &&
+	       pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0)
+    {
+	/*
+	 * Handle strandled incoming PRACK
+	 */
+	inv_respond_incoming_prack(inv, e->body.tsx_state.src.rdata);
+
+    } else if (tsx->role == PJSIP_ROLE_UAC) {
+	/*
+	 * Handle case when outgoing request is answered with 481 (Call/
+	 * Transaction Does Not Exist), 408, or when it's timed out. In these
+	 * cases, disconnect session (i.e. dialog usage only).
+	 */
+	if (tsx->status_code == PJSIP_SC_CALL_TSX_DOES_NOT_EXIST ||
+	    tsx->status_code == PJSIP_SC_REQUEST_TIMEOUT ||
+	    tsx->status_code == PJSIP_SC_TSX_TIMEOUT ||
+	    tsx->status_code == PJSIP_SC_TSX_TRANSPORT_ERROR)
+	{
+	    inv_set_cause(inv, tsx->status_code, &tsx->status_text);
+	    inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
+	}
+    }
+
 }
 
