Ticket 5: Support for SIP UPDATE (RFC 3311) and fix the offer/answer negotiation

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@1469 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip/src/pjsip-ua/sip_100rel.c b/pjsip/src/pjsip-ua/sip_100rel.c
index 4879825..cb8852d 100644
--- a/pjsip/src/pjsip-ua/sip_100rel.c
+++ b/pjsip/src/pjsip-ua/sip_100rel.c
@@ -28,17 +28,21 @@
 #include <pj/pool.h>
 #include <pj/rand.h>
 
-#if defined(PJSIP_HAS_100REL) && PJSIP_HAS_100REL!=0
-
 #define THIS_FILE	"sip_100rel.c"
 
+/* PRACK method */
+PJ_DEF_DATA(const pjsip_method) pjsip_prack_method =
+{
+    PJSIP_OTHER_METHOD,
+    { "PRACK", 5 }
+};
+
 typedef struct dlg_data dlg_data;
 
 /*
  * Static prototypes.
  */
 static pj_status_t mod_100rel_load(pjsip_endpoint *endpt);
-static void	   mod_100rel_on_tsx_state(pjsip_transaction*, pjsip_event*);
 
 static void handle_incoming_prack(dlg_data *dd, pjsip_transaction *tsx,
 				  pjsip_event *e);
@@ -48,13 +52,6 @@
 			  struct pj_timer_entry *entry);
 
 
-/* PRACK method */
-const pjsip_method pjsip_prack_method =
-{
-	PJSIP_OTHER_METHOD,
-	{ "PRACK", 5 }
-};
-
 const pj_str_t tag_100rel = { "100rel", 6 };
 const pj_str_t RSEQ = { "RSeq", 4 };
 const pj_str_t RACK = { "RAck", 4 };
@@ -80,7 +77,7 @@
 	NULL,				    /* on_rx_response()		*/
 	NULL,				    /* on_tx_request.		*/
 	NULL,				    /* on_tx_response()		*/
-	&mod_100rel_on_tsx_state,	    /* on_tsx_state()		*/
+	NULL,				    /* on_tsx_state()		*/
     }
 
 };
@@ -132,339 +129,62 @@
  */
 static pj_status_t mod_100rel_load(pjsip_endpoint *endpt)
 {
-	mod_100rel.endpt = endpt;
-	pjsip_endpt_add_capability(endpt, &mod_100rel.mod, 
-				   PJSIP_H_ALLOW, NULL,
-				   1, &pjsip_prack_method.name);
-	pjsip_endpt_add_capability(endpt, &mod_100rel.mod, 
-				   PJSIP_H_SUPPORTED, NULL,
-				   1, &tag_100rel);
+    mod_100rel.endpt = endpt;
+    pjsip_endpt_add_capability(endpt, &mod_100rel.mod, 
+			       PJSIP_H_ALLOW, NULL,
+			       1, &pjsip_prack_method.name);
+    pjsip_endpt_add_capability(endpt, &mod_100rel.mod, 
+			       PJSIP_H_SUPPORTED, NULL,
+			       1, &tag_100rel);
 
-	return PJ_SUCCESS;
+    return PJ_SUCCESS;
 }
 
 static pjsip_require_hdr *find_req_hdr(pjsip_msg *msg)
 {
-	pjsip_require_hdr *hreq;
+    pjsip_require_hdr *hreq;
+
+    hreq = (pjsip_require_hdr*)
+	    pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, NULL);
+
+    while (hreq) {
+	unsigned i;
+	for (i=0; i<hreq->count; ++i) {
+	    if (!pj_stricmp(&hreq->values[i], &tag_100rel)) {
+		return hreq;
+	    }
+	}
+
+	if ((void*)hreq->next == (void*)&msg->hdr)
+	    return NULL;
 
 	hreq = (pjsip_require_hdr*)
-		pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, NULL);
+		pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, hreq->next);
 
-	while (hreq) {
-		unsigned i;
-		for (i=0; i<hreq->count; ++i) {
-			if (!pj_stricmp(&hreq->values[i], &tag_100rel)) {
-				return hreq;
-			}
-		}
+    }
 
-		if ((void*)hreq->next == (void*)&msg->hdr)
-			return NULL;
-
-		hreq = (pjsip_require_hdr*)
-			pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, hreq->next);
-
-	}
-
-	return NULL;
-}
-
-static void mod_100rel_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
-{
-	pjsip_dialog *dlg;
-	dlg_data *dd;
-
-	dlg = pjsip_tsx_get_dlg(tsx);
-	if (!dlg)
-		return;
-
-	dd = (dlg_data*) dlg->mod_data[mod_100rel.mod.id];
-	if (!dd)
-		return;
-
-	if (tsx->role == PJSIP_ROLE_UAS &&
-	    tsx->state == PJSIP_TSX_STATE_TRYING &&
-	    pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0)
-	{
-		/* 
-		 * Handle incoming PRACK request.
-		 */
-		handle_incoming_prack(dd, tsx, e);
-
-	} else if (tsx->role == PJSIP_ROLE_UAC &&
-		   tsx->method.id == PJSIP_INVITE_METHOD &&
-		   e->type == PJSIP_EVENT_TSX_STATE &&
-		   e->body.tsx_state.type == PJSIP_EVENT_RX_MSG && 
-		   e->body.tsx_state.src.rdata->msg_info.msg->line.status.code > 100 &&
-		   e->body.tsx_state.src.rdata->msg_info.msg->line.status.code < 200 &&
-		   e->body.tsx_state.src.rdata->msg_info.require != NULL)
-	{
-		/*
-		 * Handle incoming provisional response which wants to 
-		 * be PRACK-ed
-		 */
-
-		if (find_req_hdr(e->body.tsx_state.src.rdata->msg_info.msg)) {
-			/* Received provisional response which needs to be 
-			 * PRACK-ed.
-			 */
-			handle_incoming_response(dd, tsx, e);
-		}
-
-	} else if (tsx->role == PJSIP_ROLE_UAC &&
-		   tsx->state == PJSIP_TSX_STATE_COMPLETED &&
-		   pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0)
-	{
-		/*
-		 * Handle the status of outgoing PRACK request.
-		 */
-		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)
-		{
-			/* These are fatal errors which should terminate
-			 * the session AND dialog!
-			 */
-			PJ_TODO(TERMINATE_SESSION_ON_481);
-		}
-
-	} else if (tsx == dd->inv->invite_tsx &&
-		   tsx->role == PJSIP_ROLE_UAS &&
-		   tsx->state == PJSIP_TSX_STATE_TERMINATED)
-	{
-		/* Make sure we don't have pending transmission */
-		if (dd->uas_state) {
-			pj_assert(!dd->uas_state->retransmit_timer.id);
-			pj_assert(pj_list_empty(&dd->uas_state->tx_data_list));
-		}
-	}
-}
-
-static void parse_rack(const pj_str_t *rack,
-		       pj_uint32_t *p_rseq, pj_int32_t *p_seq,
-		       pj_str_t *p_method)
-{
-	const char *p = rack->ptr, *end = p + rack->slen;
-	pj_str_t token;
-
-	token.ptr = (char*)p;
-	while (p < end && pj_isdigit(*p))
-		++p;
-	token.slen = p - token.ptr;
-	*p_rseq = pj_strtoul(&token);
-
-	++p;
-	token.ptr = (char*)p;
-	while (p < end && pj_isdigit(*p))
-		++p;
-	token.slen = p - token.ptr;
-	*p_seq = pj_strtoul(&token);
-
-	++p;
-	if (p < end) {
-		p_method->ptr = (char*)p;
-		p_method->slen = end - p;
-	} else {
-		p_method->ptr = NULL;
-		p_method->slen = 0;
-	}
-}
-
-/* Clear all responses in the transmission list */
-static void clear_all_responses(dlg_data *dd)
-{
-	tx_data_list_t *tl;
-
-	tl = dd->uas_state->tx_data_list.next;
-	while (tl != &dd->uas_state->tx_data_list) {
-		pjsip_tx_data_dec_ref(tl->tdata);
-		tl = tl->next;
-	}
-	pj_list_init(&dd->uas_state->tx_data_list);
-}
-
-
-static void handle_incoming_prack(dlg_data *dd, pjsip_transaction *tsx,
-				  pjsip_event *e)
-{
-	pjsip_rx_data *rdata;
-	pjsip_msg *msg;
-	pjsip_generic_string_hdr *rack_hdr;
-	pjsip_tx_data *tdata;
-	pj_uint32_t rseq;
-	pj_int32_t cseq;
-	pj_str_t method;
-	pj_status_t status;
-
-
-	rdata = e->body.tsx_state.src.rdata;
-	msg = rdata->msg_info.msg;
-
-	/* Always reply with 200/OK for PRACK */
-	status = pjsip_endpt_create_response(tsx->endpt, rdata, 
-					     200, NULL, &tdata);
-	if (status == PJ_SUCCESS)
-		pjsip_tsx_send_msg(tsx, tdata);
-
-	/* Ignore if we don't have pending transmission */
-	if (dd->uas_state == NULL ||
-	    pj_list_empty(&dd->uas_state->tx_data_list))
-	{
-		PJ_LOG(4,(dd->inv->dlg->obj_name, 
-			  "PRACK ignored - no pending response"));
-		return;
-	}
-
-	/* Find RAck header */
-	rack_hdr = (pjsip_generic_string_hdr*)
-		   pjsip_msg_find_hdr_by_name(msg, &RACK, NULL);
-	if (!rack_hdr) {
-		/* RAck header not found */
-		PJ_LOG(4,(dd->inv->dlg->obj_name, "No RAck header"));
-		return;
-	}
-	parse_rack(&rack_hdr->hvalue, &rseq, &cseq, &method);
-
-	/* Match RAck against outgoing transmission */
-	if (rseq == dd->uas_state->tx_data_list.next->rseq &&
-	    cseq == dd->uas_state->cseq)
-	{
-		tx_data_list_t *tl = dd->uas_state->tx_data_list.next;
-
-		/* Yes it match! */
-		if (dd->uas_state->retransmit_timer.id) {
-			pjsip_endpt_cancel_timer(dd->inv->dlg->endpt,
-						 &dd->uas_state->retransmit_timer);
-			dd->uas_state->retransmit_timer.id = PJ_FALSE;
-		}
-
-		/* Remove from the list */
-		if (tl != &dd->uas_state->tx_data_list) {
-			pj_list_erase(tl);
-
-			/* Destroy the response */
-			pjsip_tx_data_dec_ref(tl->tdata);
-		}
-
-		/* Schedule next packet */
-		dd->uas_state->retransmit_count = 0;
-		if (!pj_list_empty(&dd->uas_state->tx_data_list)) {
-			on_retransmit(NULL, &dd->uas_state->retransmit_timer);
-		}
-
-	} else {
-		/* No it doesn't match */
-		PJ_LOG(4,(dd->inv->dlg->obj_name, 
-			 "Rx PRACK with no matching reliable response"));
-	}
+    return NULL;
 }
 
 
 /*
- * Handle incoming provisional response with 100rel requirement.
- * In this case we shall transmit PRACK request.
+ * Get PRACK method constant. 
  */
-static void handle_incoming_response(dlg_data *dd, pjsip_transaction *tsx,
-				     pjsip_event *e)
+PJ_DEF(const pjsip_method*) pjsip_get_prack_method(void)
 {
-	pjsip_rx_data *rdata;
-	pjsip_msg *msg;
-	pjsip_generic_string_hdr *rseq_hdr;
-	pjsip_generic_string_hdr *rack_hdr;
-	unsigned rseq;
-	pj_str_t rack;
-	char rack_buf[80];
-	pjsip_tx_data *tdata;
-	pj_status_t status;
-
-	rdata = e->body.tsx_state.src.rdata;
-	msg = rdata->msg_info.msg;
-
-	/* Check our assumptions */
-	pj_assert( tsx->role == PJSIP_ROLE_UAC &&
-		   tsx->method.id == PJSIP_INVITE_METHOD &&
-		   e->type == PJSIP_EVENT_TSX_STATE &&
-		   e->body.tsx_state.type == PJSIP_EVENT_RX_MSG && 
-		   msg->line.status.code > 100 &&
-		   msg->line.status.code < 200);
-
-
-	/* Get the RSeq header */
-	rseq_hdr = (pjsip_generic_string_hdr*)
-		   pjsip_msg_find_hdr_by_name(msg, &RSEQ, NULL);
-	if (rseq_hdr == NULL) {
-		PJ_LOG(4,(dd->inv->dlg->obj_name, 
-			 "Ignoring provisional response with no RSeq header"));
-		return;
-	}
-	rseq = (pj_uint32_t) pj_strtoul(&rseq_hdr->hvalue);
-
-	/* Create new UAC state if we don't have one */
-	if (dd->uac_state == NULL) {
-		dd->uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool,
-						 uac_state_t);
-		dd->uac_state->cseq = rdata->msg_info.cseq->cseq;
-		dd->uac_state->rseq = rseq - 1;
-	}
-
-	/* If this is from new INVITE transaction, reset UAC state */
-	if (rdata->msg_info.cseq->cseq != dd->uac_state->cseq) {
-		dd->uac_state->cseq = rdata->msg_info.cseq->cseq;
-		dd->uac_state->rseq = rseq - 1;
-	}
-
-	/* Ignore provisional response retransmission */
-	if (rseq <= dd->uac_state->rseq) {
-		/* This should have been handled before */
-		return;
-
-	/* Ignore provisional response with out-of-order RSeq */
-	} else if (rseq != dd->uac_state->rseq + 1) {
-		PJ_LOG(4,(dd->inv->dlg->obj_name, 
-			 "Ignoring provisional response because RSeq jump "
-			 "(expecting %u, got %u)",
-			 dd->uac_state->rseq+1, rseq));
-		return;
-	}
-
-	/* Update our RSeq */
-	dd->uac_state->rseq = rseq;
-
-	/* Create PRACK */
-	status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method,
-					  -1, &tdata);
-	if (status != PJ_SUCCESS) {
-		PJ_LOG(4,(dd->inv->dlg->obj_name, 
-			 "Error creating PRACK request (status=%d)", status));
-		return;
-	}
-
-	/* Create RAck header */
-	rack.ptr = rack_buf;
-	rack.slen = pj_ansi_snprintf(rack.ptr, sizeof(rack_buf),
-				     "%u %u %.*s",
-				     rseq, rdata->msg_info.cseq->cseq,
-				     (int)tsx->method.name.slen,
-				     tsx->method.name.ptr);
-	PJ_ASSERT_ON_FAIL(rack.slen > 0 && rack.slen < (int)sizeof(rack_buf),
-			{ pjsip_tx_data_dec_ref(tdata); return; });
-	rack_hdr = pjsip_generic_string_hdr_create(tdata->pool, &RACK, &rack);
-	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) rack_hdr);
-
-	/* Send PRACK */
-	pjsip_dlg_send_request(dd->inv->dlg, tdata, 
-			       mod_100rel.mod.id, (void*) dd);
-
+    return &pjsip_prack_method;
 }
 
 
 /*
- * API: init module
+ * init module
  */
 PJ_DEF(pj_status_t) pjsip_100rel_init_module(pjsip_endpoint *endpt)
 {
-	return pjsip_endpt_register_module(endpt, &mod_100rel.mod);
+    if (mod_100rel.mod.id != -1)
+	return PJ_SUCCESS;
+
+    return pjsip_endpt_register_module(endpt, &mod_100rel.mod);
 }
 
 
@@ -474,19 +194,304 @@
  */
 PJ_DEF(pj_status_t) pjsip_100rel_attach(pjsip_inv_session *inv)
 {
-	dlg_data *dd;
+    dlg_data *dd;
 
-	/* Check that 100rel module has been initialized */
-	PJ_ASSERT_RETURN(mod_100rel.mod.id >= 0, PJ_EINVALIDOP);
+    /* Check that 100rel module has been initialized */
+    PJ_ASSERT_RETURN(mod_100rel.mod.id >= 0, PJ_EINVALIDOP);
 
-	/* Create and attach as dialog usage */
-	dd = PJ_POOL_ZALLOC_T(inv->dlg->pool, dlg_data);
-	dd->inv = inv;
-	pjsip_dlg_add_usage(inv->dlg, &mod_100rel.mod, (void*)dd);
+    /* Create and attach as dialog usage */
+    dd = PJ_POOL_ZALLOC_T(inv->dlg->pool, dlg_data);
+    dd->inv = inv;
+    pjsip_dlg_add_usage(inv->dlg, &mod_100rel.mod, (void*)dd);
 
-	PJ_LOG(5,(dd->inv->dlg->obj_name, "100rel module attached"));
+    PJ_LOG(5,(dd->inv->dlg->obj_name, "100rel module attached"));
 
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Check if incoming response has reliable provisional response feature.
+ */
+PJ_DEF(pj_bool_t) pjsip_100rel_is_reliable(pjsip_rx_data *rdata)
+{
+    pjsip_msg *msg = rdata->msg_info.msg;
+
+    PJ_ASSERT_RETURN(msg->type == PJSIP_RESPONSE_MSG, PJ_FALSE);
+
+    return msg->line.status.code > 100 && msg->line.status.code < 200 &&
+	   rdata->msg_info.require != NULL &&
+	   find_req_hdr(msg) != NULL;
+}
+
+
+/*
+ * Create PRACK request for the incoming reliable provisional response.
+ */
+PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv,
+					       pjsip_rx_data *rdata,
+					       pjsip_tx_data **p_tdata)
+{
+    dlg_data *dd;
+    pjsip_transaction *tsx;
+    pjsip_msg *msg;
+    pjsip_generic_string_hdr *rseq_hdr;
+    pjsip_generic_string_hdr *rack_hdr;
+    unsigned rseq;
+    pj_str_t rack;
+    char rack_buf[80];
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+
+    *p_tdata = NULL;
+
+    dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
+    PJ_ASSERT_RETURN(dd != NULL, PJSIP_ENOTINITIALIZED);
+
+    tsx = pjsip_rdata_get_tsx(rdata);
+    msg = rdata->msg_info.msg;
+
+    /* Check our assumptions */
+    pj_assert( tsx->role == PJSIP_ROLE_UAC &&
+	       tsx->method.id == PJSIP_INVITE_METHOD &&
+	       msg->line.status.code > 100 &&
+	       msg->line.status.code < 200);
+
+
+    /* Get the RSeq header */
+    rseq_hdr = (pjsip_generic_string_hdr*)
+	       pjsip_msg_find_hdr_by_name(msg, &RSEQ, NULL);
+    if (rseq_hdr == NULL) {
+	PJ_LOG(4,(dd->inv->dlg->obj_name, 
+		 "Ignoring provisional response with no RSeq header"));
+	return PJSIP_EMISSINGHDR;
+    }
+    rseq = (pj_uint32_t) pj_strtoul(&rseq_hdr->hvalue);
+
+    /* Create new UAC state if we don't have one */
+    if (dd->uac_state == NULL) {
+	dd->uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool,
+					 uac_state_t);
+	dd->uac_state->cseq = rdata->msg_info.cseq->cseq;
+	dd->uac_state->rseq = rseq - 1;
+    }
+
+    /* If this is from new INVITE transaction, reset UAC state */
+    if (rdata->msg_info.cseq->cseq != dd->uac_state->cseq) {
+	dd->uac_state->cseq = rdata->msg_info.cseq->cseq;
+	dd->uac_state->rseq = rseq - 1;
+    }
+
+    /* Ignore provisional response retransmission */
+    if (rseq <= dd->uac_state->rseq) {
+	/* This should have been handled before */
+	return PJ_EIGNORED;
+
+    /* Ignore provisional response with out-of-order RSeq */
+    } else if (rseq != dd->uac_state->rseq + 1) {
+	PJ_LOG(4,(dd->inv->dlg->obj_name, 
+		 "Ignoring provisional response because RSeq jump "
+		 "(expecting %u, got %u)",
+		 dd->uac_state->rseq+1, rseq));
+	return PJ_EIGNORED;
+    }
+
+    /* Update our RSeq */
+    dd->uac_state->rseq = rseq;
+
+    /* Create PRACK */
+    status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method,
+				      -1, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Create RAck header */
+    rack.ptr = rack_buf;
+    rack.slen = pj_ansi_snprintf(rack.ptr, sizeof(rack_buf),
+				 "%u %u %.*s",
+				 rseq, rdata->msg_info.cseq->cseq,
+				 (int)tsx->method.name.slen,
+				 tsx->method.name.ptr);
+    rack_hdr = pjsip_generic_string_hdr_create(tdata->pool, &RACK, &rack);
+    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) rack_hdr);
+
+    /* Done */
+    *p_tdata = tdata;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Send PRACK request.
+ */
+PJ_DEF(pj_status_t) pjsip_100rel_send_prack( pjsip_inv_session *inv,
+					     pjsip_tx_data *tdata)
+{
+    dlg_data *dd;
+
+    dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
+    PJ_ASSERT_ON_FAIL(dd != NULL, 
+    {pjsip_tx_data_dec_ref(tdata); return PJSIP_ENOTINITIALIZED; });
+
+    return pjsip_dlg_send_request(inv->dlg, tdata, 
+				  mod_100rel.mod.id, (void*) dd);
+
+}
+
+
+/*
+ * Notify 100rel module that the invite session has been disconnected.
+ */
+PJ_DEF(pj_status_t) pjsip_100rel_end_session(pjsip_inv_session *inv)
+{
+    dlg_data *dd;
+
+    dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
+    if (!dd)
 	return PJ_SUCCESS;
+
+    /* Make sure we don't have pending transmission */
+    if (dd->uas_state) {
+	pj_assert(!dd->uas_state->retransmit_timer.id);
+	pj_assert(pj_list_empty(&dd->uas_state->tx_data_list));
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+static void parse_rack(const pj_str_t *rack,
+		       pj_uint32_t *p_rseq, pj_int32_t *p_seq,
+		       pj_str_t *p_method)
+{
+    const char *p = rack->ptr, *end = p + rack->slen;
+    pj_str_t token;
+
+    token.ptr = (char*)p;
+    while (p < end && pj_isdigit(*p))
+	++p;
+    token.slen = p - token.ptr;
+    *p_rseq = pj_strtoul(&token);
+
+    ++p;
+    token.ptr = (char*)p;
+    while (p < end && pj_isdigit(*p))
+	++p;
+    token.slen = p - token.ptr;
+    *p_seq = pj_strtoul(&token);
+
+    ++p;
+    if (p < end) {
+	p_method->ptr = (char*)p;
+	p_method->slen = end - p;
+    } else {
+	p_method->ptr = NULL;
+	p_method->slen = 0;
+    }
+}
+
+/* Clear all responses in the transmission list */
+static void clear_all_responses(dlg_data *dd)
+{
+    tx_data_list_t *tl;
+
+    tl = dd->uas_state->tx_data_list.next;
+    while (tl != &dd->uas_state->tx_data_list) {
+	pjsip_tx_data_dec_ref(tl->tdata);
+	tl = tl->next;
+    }
+    pj_list_init(&dd->uas_state->tx_data_list);
+}
+
+
+/*
+ * Handle incoming PRACK request.
+ */
+PJ_DEF(pj_status_t) pjsip_100rel_on_rx_prack( pjsip_inv_session *inv,
+					      pjsip_rx_data *rdata)
+{
+    dlg_data *dd;
+    pjsip_transaction *tsx;
+    pjsip_msg *msg;
+    pjsip_generic_string_hdr *rack_hdr;
+    pjsip_tx_data *tdata;
+    pj_uint32_t rseq;
+    pj_int32_t cseq;
+    pj_str_t method;
+    pj_status_t status;
+
+    dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
+    PJ_ASSERT_RETURN(dd != NULL, PJSIP_ENOTINITIALIZED);
+
+    tsx = pjsip_rdata_get_tsx(rdata);
+    pj_assert(tsx != NULL);
+
+    msg = rdata->msg_info.msg;
+
+    /* Always reply with 200/OK for PRACK */
+    status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
+    if (status == PJ_SUCCESS) {
+	status = pjsip_dlg_send_response(inv->dlg, tsx, tdata);
+    }
+
+    /* Ignore if we don't have pending transmission */
+    if (dd->uas_state == NULL || pj_list_empty(&dd->uas_state->tx_data_list)) {
+	PJ_LOG(4,(dd->inv->dlg->obj_name, 
+		  "PRACK ignored - no pending response"));
+	return PJ_EIGNORED;
+    }
+
+    /* Find RAck header */
+    rack_hdr = (pjsip_generic_string_hdr*)
+	       pjsip_msg_find_hdr_by_name(msg, &RACK, NULL);
+    if (!rack_hdr) {
+	/* RAck header not found */
+	PJ_LOG(4,(dd->inv->dlg->obj_name, "No RAck header"));
+	return PJSIP_EMISSINGHDR;
+    }
+
+    /* Parse RAck header */
+    parse_rack(&rack_hdr->hvalue, &rseq, &cseq, &method);
+
+
+    /* Match RAck against outgoing transmission */
+    if (rseq == dd->uas_state->tx_data_list.next->rseq &&
+	cseq == dd->uas_state->cseq)
+    {
+	/* 
+	 * Yes this PRACK matches outgoing transmission.
+	 */
+	tx_data_list_t *tl = dd->uas_state->tx_data_list.next;
+
+	if (dd->uas_state->retransmit_timer.id) {
+	    pjsip_endpt_cancel_timer(dd->inv->dlg->endpt,
+				     &dd->uas_state->retransmit_timer);
+	    dd->uas_state->retransmit_timer.id = PJ_FALSE;
+	}
+
+	/* Remove from the list */
+	if (tl != &dd->uas_state->tx_data_list) {
+	    pj_list_erase(tl);
+
+	    /* Destroy the response */
+	    pjsip_tx_data_dec_ref(tl->tdata);
+	}
+
+	/* Schedule next packet */
+	dd->uas_state->retransmit_count = 0;
+	if (!pj_list_empty(&dd->uas_state->tx_data_list)) {
+	    on_retransmit(NULL, &dd->uas_state->retransmit_timer);
+	}
+
+    } else {
+	/* No it doesn't match */
+	PJ_LOG(4,(dd->inv->dlg->obj_name, 
+		 "Rx PRACK with no matching reliable response"));
+	return PJ_EIGNORED;
+    }
+
+    return PJ_SUCCESS;
 }
 
 
@@ -497,136 +502,138 @@
 static void on_retransmit(pj_timer_heap_t *timer_heap,
 			  struct pj_timer_entry *entry)
 {
-	dlg_data *dd;
-	tx_data_list_t *tl;
-	pjsip_tx_data *tdata;
-	pj_bool_t final;
-	pj_time_val delay;
+    dlg_data *dd;
+    tx_data_list_t *tl;
+    pjsip_tx_data *tdata;
+    pj_bool_t final;
+    pj_time_val delay;
 
-	PJ_UNUSED_ARG(timer_heap);
+    PJ_UNUSED_ARG(timer_heap);
 
-	dd = (dlg_data*) entry->user_data;
+    dd = (dlg_data*) entry->user_data;
 
-	entry->id = PJ_FALSE;
+    entry->id = PJ_FALSE;
 
-	++dd->uas_state->retransmit_count;
-	if (dd->uas_state->retransmit_count >= 7) {
-		/* If a reliable provisional response is retransmitted for
-		   64*T1 seconds  without reception of a corresponding PRACK,
-		   the UAS SHOULD reject the original request with a 5xx 
-		   response.
-		*/
-		pj_str_t reason = pj_str("Reliable response timed out");
-		pj_status_t status;
+    ++dd->uas_state->retransmit_count;
+    if (dd->uas_state->retransmit_count >= 7) {
+	/* If a reliable provisional response is retransmitted for
+	   64*T1 seconds  without reception of a corresponding PRACK,
+	   the UAS SHOULD reject the original request with a 5xx 
+	   response.
+	*/
+	pj_str_t reason = pj_str("Reliable response timed out");
+	pj_status_t status;
 
-		/* Clear all pending responses */
-		clear_all_responses(dd);
+	/* Clear all pending responses */
+	clear_all_responses(dd);
 
-		/* Send 500 response */
-		status = pjsip_inv_end_session(dd->inv, 500, &reason, &tdata);
-		if (status == PJ_SUCCESS) {
-			pjsip_dlg_send_response(dd->inv->dlg, 
-						dd->inv->invite_tsx,
-						tdata);
-		}
-		return;
+	/* Send 500 response */
+	status = pjsip_inv_end_session(dd->inv, 500, &reason, &tdata);
+	if (status == PJ_SUCCESS) {
+	    pjsip_dlg_send_response(dd->inv->dlg, 
+				    dd->inv->invite_tsx,
+				    tdata);
 	}
+	return;
+    }
 
-	pj_assert(!pj_list_empty(&dd->uas_state->tx_data_list));
-	tl = dd->uas_state->tx_data_list.next;
-	tdata = tl->tdata;
+    pj_assert(!pj_list_empty(&dd->uas_state->tx_data_list));
+    tl = dd->uas_state->tx_data_list.next;
+    tdata = tl->tdata;
 
-	pjsip_tx_data_add_ref(tdata);
-	final = tdata->msg->line.status.code >= 200;
+    pjsip_tx_data_add_ref(tdata);
+    final = tdata->msg->line.status.code >= 200;
 
-	if (dd->uas_state->retransmit_count == 1) {
-		pjsip_tsx_send_msg(dd->inv->invite_tsx, tdata);
-	} else {
-		pjsip_tsx_retransmit_no_state(dd->inv->invite_tsx, tdata);
-	}
+    if (dd->uas_state->retransmit_count == 1) {
+	pjsip_tsx_send_msg(dd->inv->invite_tsx, tdata);
+    } else {
+	pjsip_tsx_retransmit_no_state(dd->inv->invite_tsx, tdata);
+    }
 
-	if (final) {
-		/* This is final response, which will be retransmitted by
-		 * UA layer. There's no more task to do, so clear the
-		 * transmission list and bail out.
-		 */
-		clear_all_responses(dd);
-		return;
-	}
+    if (final) {
+	/* This is final response, which will be retransmitted by
+	 * UA layer. There's no more task to do, so clear the
+	 * transmission list and bail out.
+	 */
+	clear_all_responses(dd);
+	return;
+    }
 
-	/* Schedule next retransmission */
-	if (dd->uas_state->retransmit_count < 6) {
-		delay.sec = 0;
-		delay.msec = (1 << dd->uas_state->retransmit_count) * 
-			     PJSIP_T1_TIMEOUT;
-		pj_time_val_normalize(&delay);
-	} else {
-		delay.sec = 1;
-		delay.msec = 500;
-	}
+    /* Schedule next retransmission */
+    if (dd->uas_state->retransmit_count < 6) {
+	delay.sec = 0;
+	delay.msec = (1 << dd->uas_state->retransmit_count) * 
+		     PJSIP_T1_TIMEOUT;
+	pj_time_val_normalize(&delay);
+    } else {
+	delay.sec = 1;
+	delay.msec = 500;
+    }
 
 
-	pjsip_endpt_schedule_timer(dd->inv->dlg->endpt, 
-				   &dd->uas_state->retransmit_timer,
-				   &delay);
+    pjsip_endpt_schedule_timer(dd->inv->dlg->endpt, 
+			       &dd->uas_state->retransmit_timer,
+			       &delay);
 
-	entry->id = PJ_TRUE;
+    entry->id = PJ_TRUE;
 }
 
+
 /* Clone response. */
 static pjsip_tx_data *clone_tdata(dlg_data *dd,
 				  const pjsip_tx_data *src)
 {
-	pjsip_tx_data *dst;
-	const pjsip_hdr *hsrc;
-	pjsip_msg *msg;
-	pj_status_t status;
+    pjsip_tx_data *dst;
+    const pjsip_hdr *hsrc;
+    pjsip_msg *msg;
+    pj_status_t status;
 
-	status = pjsip_endpt_create_tdata(dd->inv->dlg->endpt, &dst);
-	if (status != PJ_SUCCESS)
-		return NULL;
+    status = pjsip_endpt_create_tdata(dd->inv->dlg->endpt, &dst);
+    if (status != PJ_SUCCESS)
+	return NULL;
 
-	msg = pjsip_msg_create(dst->pool, PJSIP_RESPONSE_MSG);
-	dst->msg = msg;
-	pjsip_tx_data_add_ref(dst);
+    msg = pjsip_msg_create(dst->pool, PJSIP_RESPONSE_MSG);
+    dst->msg = msg;
+    pjsip_tx_data_add_ref(dst);
 
-	/* Duplicate status line */
-	msg->line.status.code = src->msg->line.status.code;
-	pj_strdup(dst->pool, &msg->line.status.reason, 
-		  &src->msg->line.status.reason);
+    /* Duplicate status line */
+    msg->line.status.code = src->msg->line.status.code;
+    pj_strdup(dst->pool, &msg->line.status.reason, 
+	      &src->msg->line.status.reason);
 
-	/* Duplicate all headers */
-	hsrc = src->msg->hdr.next;
-	while (hsrc != &src->msg->hdr) {
-		pjsip_hdr *h = (pjsip_hdr*) pjsip_hdr_clone(dst->pool, hsrc);
-		pjsip_msg_add_hdr(msg, h);
-		hsrc = hsrc->next;
-	}
+    /* Duplicate all headers */
+    hsrc = src->msg->hdr.next;
+    while (hsrc != &src->msg->hdr) {
+	pjsip_hdr *h = (pjsip_hdr*) pjsip_hdr_clone(dst->pool, hsrc);
+	pjsip_msg_add_hdr(msg, h);
+	hsrc = hsrc->next;
+    }
 
-	/* Duplicate message body */
-	if (src->msg->body)
-		msg->body = pjsip_msg_body_clone(dst->pool, src->msg->body);
+    /* Duplicate message body */
+    if (src->msg->body)
+	msg->body = pjsip_msg_body_clone(dst->pool, src->msg->body);
 
-	PJ_LOG(5,(dd->inv->dlg->obj_name,
-		 "Reliable response %s created",
-		 pjsip_tx_data_get_info(dst)));
+    PJ_LOG(5,(dd->inv->dlg->obj_name,
+	     "Reliable response %s created",
+	     pjsip_tx_data_get_info(dst)));
 
-	return dst;
+    return dst;
 }
 
-/* Check if pending response has SDP */
+
+/* Check if any pending response in transmission list has SDP */
 static pj_bool_t has_sdp(dlg_data *dd)
 {
-	tx_data_list_t *tl;
+    tx_data_list_t *tl;
 
-	tl = dd->uas_state->tx_data_list.next;
-	while (tl != &dd->uas_state->tx_data_list) {
-		if (tl->tdata->msg->body)
-			return PJ_TRUE;
-		tl = tl->next;
-	}
+    tl = dd->uas_state->tx_data_list.next;
+    while (tl != &dd->uas_state->tx_data_list) {
+	    if (tl->tdata->msg->body)
+		    return PJ_TRUE;
+	    tl = tl->next;
+    }
 
-	return PJ_FALSE;
+    return PJ_FALSE;
 }
 
 
@@ -634,212 +641,221 @@
 PJ_DEF(pj_status_t) pjsip_100rel_tx_response(pjsip_inv_session *inv,
 					     pjsip_tx_data *tdata)
 {
-	pjsip_cseq_hdr *cseq_hdr;
-	pjsip_generic_string_hdr *rseq_hdr;
-	pjsip_require_hdr *req_hdr;
-	int status_code;
-	dlg_data *dd;
-	pjsip_tx_data *old_tdata;
-	pj_status_t status;
+    pjsip_cseq_hdr *cseq_hdr;
+    pjsip_generic_string_hdr *rseq_hdr;
+    pjsip_require_hdr *req_hdr;
+    int status_code;
+    dlg_data *dd;
+    pjsip_tx_data *old_tdata;
+    pj_status_t status;
+    
+    PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_RESPONSE_MSG,
+		     PJSIP_ENOTRESPONSEMSG);
+    
+    status_code = tdata->msg->line.status.code;
+    
+    /* 100 response doesn't need PRACK */
+    if (status_code == 100)
+	return pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata);
+    
 
-	PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_RESPONSE_MSG,
-			 PJ_EINVALIDOP);
+    /* Get the 100rel data attached to this dialog */
+    dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
+    PJ_ASSERT_RETURN(dd != NULL, PJ_EINVALIDOP);
+    
+    
+    /* Clone tdata.
+     * We need to clone tdata because we may need to keep it in our
+     * retransmission list, while the original dialog may modify it
+     * if it wants to send another response.
+     */
+    old_tdata = tdata;
+    tdata = clone_tdata(dd, old_tdata);
+    pjsip_tx_data_dec_ref(old_tdata);
+    
 
-	status_code = tdata->msg->line.status.code;
+    /* Get CSeq header, and make sure this is INVITE response */
+    cseq_hdr = (pjsip_cseq_hdr*)
+	        pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+    PJ_ASSERT_RETURN(cseq_hdr != NULL, PJ_EBUG);
+    PJ_ASSERT_RETURN(cseq_hdr->method.id == PJSIP_INVITE_METHOD, 
+	PJ_EINVALIDOP);
+    
+    /* Remove existing Require header */
+    req_hdr = find_req_hdr(tdata->msg);
+    if (req_hdr) {
+	pj_list_erase(req_hdr);
+    }
+    
+    /* Remove existing RSeq header */
+    rseq_hdr = (pjsip_generic_string_hdr*)
+	pjsip_msg_find_hdr_by_name(tdata->msg, &RSEQ, NULL);
+    if (rseq_hdr)
+	pj_list_erase(rseq_hdr);
+    
+    /* Different treatment for provisional and final response */
+    if (status_code/100 == 2) {
+	
+	/* RFC 3262 Section 3: UAS Behavior:
+    
+	  The UAS MAY send a final response to the initial request 
+	  before having received PRACKs for all unacknowledged 
+	  reliable provisional responses, unless the final response 
+	  is 2xx and any of the unacknowledged reliable provisional 
+	  responses contained a session description.  In that case, 
+	  it MUST NOT send a final response until those provisional 
+	  responses are acknowledged.
+	*/
+	
+	if (dd->uas_state && has_sdp(dd)) {
+	    /* Yes we have transmitted 1xx with SDP reliably.
+	     * In this case, must queue the 2xx response.
+	     */
+	    tx_data_list_t *tl;
+	    
+	    tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t);
+	    tl->tdata = tdata;
+	    tl->rseq = (pj_uint32_t)-1;
+	    pj_list_push_back(&dd->uas_state->tx_data_list, tl);
+	    
+	    /* Will send later */
+	    status = PJ_SUCCESS;
+	    
+	    PJ_LOG(4,(dd->inv->dlg->obj_name, 
+		      "2xx response will be sent after PRACK"));
+	    
+	} else if (dd->uas_state) {
+	    /* 
+	    RFC 3262 Section 3: UAS Behavior:
 
-	/* 100 response doesn't need PRACK */
-	if (status_code == 100)
-		return pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata);
-
-	/* Get the dialog data */
-	dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
-	PJ_ASSERT_RETURN(dd != NULL, PJ_EINVALIDOP);
-
-
-	/* Clone tdata */
-	old_tdata = tdata;
-	tdata = clone_tdata(dd, old_tdata);
-	pjsip_tx_data_dec_ref(old_tdata);
-
-	/* Get CSeq header */
-	cseq_hdr = (pjsip_cseq_hdr*)
-		   pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
-	PJ_ASSERT_RETURN(cseq_hdr != NULL, PJ_EBUG);
-	PJ_ASSERT_RETURN(cseq_hdr->method.id == PJSIP_INVITE_METHOD, 
-			 PJ_EINVALIDOP);
-
-	/* Remove existing Require header */
-	req_hdr = find_req_hdr(tdata->msg);
-	if (req_hdr) {
-		pj_list_erase(req_hdr);
-	}
-
-	/* Remove existing RSeq header */
-	rseq_hdr = (pjsip_generic_string_hdr*)
-		   pjsip_msg_find_hdr_by_name(tdata->msg, &RSEQ, NULL);
-	if (rseq_hdr)
-		pj_list_erase(rseq_hdr);
-
-	/* Different treatment for provisional and final response */
-	if (status_code/100 == 2) {
-
-		/* RFC 3262 Section 3: UAS Behavior:
-
-		The UAS MAY send a final response to the initial request 
-		before having received PRACKs for all unacknowledged 
-		reliable provisional responses, unless the final response 
-		is 2xx and any of the unacknowledged reliable provisional 
-		responses contained a session description.  In that case, 
-		it MUST NOT send a final response until those provisional 
-		responses are acknowledged.
-		*/
-
-		if (dd->uas_state && has_sdp(dd)) {
-			/* Yes we have transmitted 1xx with SDP reliably.
-			 * In this case, must queue the 2xx response.
-			 */
-			tx_data_list_t *tl;
-
-			tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t);
-			tl->tdata = tdata;
-			tl->rseq = (pj_uint32_t)-1;
-			pj_list_push_back(&dd->uas_state->tx_data_list, tl);
-
-			/* Will send later */
-			status = PJ_SUCCESS;
-
-			PJ_LOG(4,(dd->inv->dlg->obj_name, 
-				  "2xx response will be sent after PRACK"));
-
-		} else if (dd->uas_state) {
-			/* 
-			If the UAS does send a final response when reliable
-			responses are still unacknowledged, it SHOULD NOT 
-			continue to retransmit the unacknowledged reliable
-			provisional responses, but it MUST be prepared to 
-			process PRACK requests for those outstanding 
-			responses.
-			*/
-			
-			PJ_LOG(4,(dd->inv->dlg->obj_name, 
-				  "No SDP sent so far, sending 2xx now"));
-
-			/* Cancel the retransmit timer */
-			if (dd->uas_state->retransmit_timer.id) {
-				pjsip_endpt_cancel_timer(dd->inv->dlg->endpt,
-							 &dd->uas_state->retransmit_timer);
-				dd->uas_state->retransmit_timer.id = PJ_FALSE;
-			}
-
-			/* Clear all pending responses (drop 'em) */
-			clear_all_responses(dd);
-
-			/* And transmit the 2xx response */
-			status=pjsip_dlg_send_response(inv->dlg, 
-						       inv->invite_tsx, tdata);
-
-		} else {
-			/* We didn't send any reliable provisional response */
-
-			/* Transmit the 2xx response */
-			status=pjsip_dlg_send_response(inv->dlg, 
-						       inv->invite_tsx, tdata);
-		}
-
-	} else if (status_code >= 300) {
-
-		/* 
-		If the UAS does send a final response when reliable
-		responses are still unacknowledged, it SHOULD NOT 
-		continue to retransmit the unacknowledged reliable
-		provisional responses, but it MUST be prepared to 
-		process PRACK requests for those outstanding 
-		responses.
-		*/
-
-		/* Cancel the retransmit timer */
-		if (dd->uas_state && dd->uas_state->retransmit_timer.id) {
-			pjsip_endpt_cancel_timer(dd->inv->dlg->endpt,
-						 &dd->uas_state->retransmit_timer);
-			dd->uas_state->retransmit_timer.id = PJ_FALSE;
-
-			/* Clear all pending responses (drop 'em) */
-			clear_all_responses(dd);
-		}
-
-		/* And transmit the 2xx response */
-		status=pjsip_dlg_send_response(inv->dlg, 
-					       inv->invite_tsx, tdata);
-
+	    If the UAS does send a final response when reliable
+	    responses are still unacknowledged, it SHOULD NOT 
+	    continue to retransmit the unacknowledged reliable
+	    provisional responses, but it MUST be prepared to 
+	    process PRACK requests for those outstanding 
+	    responses.
+	    */
+	    
+	    PJ_LOG(4,(dd->inv->dlg->obj_name, 
+		      "No SDP sent so far, sending 2xx now"));
+	    
+	    /* Cancel the retransmit timer */
+	    if (dd->uas_state->retransmit_timer.id) {
+		pjsip_endpt_cancel_timer(dd->inv->dlg->endpt,
+					 &dd->uas_state->retransmit_timer);
+		dd->uas_state->retransmit_timer.id = PJ_FALSE;
+	    }
+	    
+	    /* Clear all pending responses (drop 'em) */
+	    clear_all_responses(dd);
+	    
+	    /* And transmit the 2xx response */
+	    status=pjsip_dlg_send_response(inv->dlg, 
+					   inv->invite_tsx, tdata);
+	    
 	} else {
-		/*
-		 * This is provisional response.
-		 */
-		char rseq_str[32];
-		pj_str_t rseq;
-		tx_data_list_t *tl;
-
-		/* Create UAS state if we don't have one */
-		if (dd->uas_state == NULL) {
-			dd->uas_state = PJ_POOL_ZALLOC_T(inv->dlg->pool,
-							 uas_state_t);
-			dd->uas_state->cseq = cseq_hdr->cseq;
-			dd->uas_state->rseq = pj_rand() % 0x7FFF;
-			pj_list_init(&dd->uas_state->tx_data_list);
-			dd->uas_state->retransmit_timer.user_data = dd;
-			dd->uas_state->retransmit_timer.cb = &on_retransmit;
-		}
-
-		/* Check that CSeq match */
-		PJ_ASSERT_RETURN(cseq_hdr->cseq == dd->uas_state->cseq,
-				 PJ_EINVALIDOP);
-
-		/* Add Require header */
-		req_hdr = pjsip_require_hdr_create(tdata->pool);
-		req_hdr->count = 1;
-		req_hdr->values[0] = tag_100rel;
-		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)req_hdr);
-
-		/* Add RSeq header */
-		pj_ansi_snprintf(rseq_str, sizeof(rseq_str), "%u",
-				 dd->uas_state->rseq);
-		rseq = pj_str(rseq_str);
-		rseq_hdr = pjsip_generic_string_hdr_create(tdata->pool, 
-							   &RSEQ, &rseq);
-		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)rseq_hdr);
-
-		/* Create list entry for this response */
-		tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t);
-		tl->tdata = tdata;
-		tl->rseq = dd->uas_state->rseq++;
-
-		/* Add to queue if there's pending response, otherwise
-		 * transmit immediately.
-		 */
-		if (!pj_list_empty(&dd->uas_state->tx_data_list)) {
-			
-			int code = tdata->msg->line.status.code;
-
-			/* Will send later */
-			pj_list_push_back(&dd->uas_state->tx_data_list, tl);
-			status = PJ_SUCCESS;
-
-			PJ_LOG(4,(dd->inv->dlg->obj_name, 
-				  "Reliable %d response enqueued (%d pending)", 
-				  code, pj_list_size(&dd->uas_state->tx_data_list)));
-
-		} else {
-			pj_list_push_back(&dd->uas_state->tx_data_list, tl);
-
-			dd->uas_state->retransmit_count = 0;
-			on_retransmit(NULL, &dd->uas_state->retransmit_timer);
-			status = PJ_SUCCESS;
-		}
-
+	    /* We didn't send any reliable provisional response */
+	    
+	    /* Transmit the 2xx response */
+	    status=pjsip_dlg_send_response(inv->dlg, 
+					   inv->invite_tsx, tdata);
 	}
+	
+    } else if (status_code >= 300) {
+	
+	/* 
+	RFC 3262 Section 3: UAS Behavior:
 
-	return status;
+	If the UAS does send a final response when reliable
+	responses are still unacknowledged, it SHOULD NOT 
+	continue to retransmit the unacknowledged reliable
+	provisional responses, but it MUST be prepared to 
+	process PRACK requests for those outstanding 
+	responses.
+	*/
+	
+	/* Cancel the retransmit timer */
+	if (dd->uas_state && dd->uas_state->retransmit_timer.id) {
+	    pjsip_endpt_cancel_timer(dd->inv->dlg->endpt,
+				     &dd->uas_state->retransmit_timer);
+	    dd->uas_state->retransmit_timer.id = PJ_FALSE;
+	    
+	    /* Clear all pending responses (drop 'em) */
+	    clear_all_responses(dd);
+	}
+	
+	/* And transmit the 2xx response */
+	status=pjsip_dlg_send_response(inv->dlg, 
+				       inv->invite_tsx, tdata);
+	
+    } else {
+	/*
+	 * This is provisional response.
+	 */
+	char rseq_str[32];
+	pj_str_t rseq;
+	tx_data_list_t *tl;
+	
+	/* Create UAS state if we don't have one */
+	if (dd->uas_state == NULL) {
+	    dd->uas_state = PJ_POOL_ZALLOC_T(inv->dlg->pool,
+					     uas_state_t);
+	    dd->uas_state->cseq = cseq_hdr->cseq;
+	    dd->uas_state->rseq = pj_rand() % 0x7FFF;
+	    pj_list_init(&dd->uas_state->tx_data_list);
+	    dd->uas_state->retransmit_timer.user_data = dd;
+	    dd->uas_state->retransmit_timer.cb = &on_retransmit;
+	}
+	
+	/* Check that CSeq match */
+	PJ_ASSERT_RETURN(cseq_hdr->cseq == dd->uas_state->cseq,
+			 PJ_EINVALIDOP);
+	
+	/* Add Require header */
+	req_hdr = pjsip_require_hdr_create(tdata->pool);
+	req_hdr->count = 1;
+	req_hdr->values[0] = tag_100rel;
+	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)req_hdr);
+	
+	/* Add RSeq header */
+	pj_ansi_snprintf(rseq_str, sizeof(rseq_str), "%u",
+			 dd->uas_state->rseq);
+	rseq = pj_str(rseq_str);
+	rseq_hdr = pjsip_generic_string_hdr_create(tdata->pool, 
+						   &RSEQ, &rseq);
+	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)rseq_hdr);
+	
+	/* Create list entry for this response */
+	tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t);
+	tl->tdata = tdata;
+	tl->rseq = dd->uas_state->rseq++;
+	
+	/* Add to queue if there's pending response, otherwise
+	 * transmit immediately.
+	 */
+	if (!pj_list_empty(&dd->uas_state->tx_data_list)) {
+	    
+	    int code = tdata->msg->line.status.code;
+	    
+	    /* Will send later */
+	    pj_list_push_back(&dd->uas_state->tx_data_list, tl);
+	    status = PJ_SUCCESS;
+	    
+	    PJ_LOG(4,(dd->inv->dlg->obj_name, 
+		      "Reliable %d response enqueued (%d pending)", 
+		      code, pj_list_size(&dd->uas_state->tx_data_list)));
+	    
+	} else {
+	    pj_list_push_back(&dd->uas_state->tx_data_list, tl);
+	    
+	    dd->uas_state->retransmit_count = 0;
+	    on_retransmit(NULL, &dd->uas_state->retransmit_timer);
+	    status = PJ_SUCCESS;
+	}
+	
+    }
+    
+    return status;
 }
 
 
-#endif	/* PJSIP_HAS_100REL */