Re #1395: Backport of PJSIP 1.x branch into PJSIP 2.0 trunk

* Backport of r3557:r3832

TODO: ticket #1268 (Option for automatic/manual sending of RTCP SDES/BYE for the stream) for video stream.



git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@3841 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip/src/pjsip-ua/sip_100rel.c b/pjsip/src/pjsip-ua/sip_100rel.c
index 5d49731..d2bb1d4 100644
--- a/pjsip/src/pjsip-ua/sip_100rel.c
+++ b/pjsip/src/pjsip-ua/sip_100rel.c
@@ -105,8 +105,10 @@
 /* UAC state */
 typedef struct uac_state_t
 {
-	pj_int32_t	cseq;
-	pj_uint32_t	rseq;	/* Initialized to -1 */
+    pj_str_t		tag;	/* To tag	     	*/
+    pj_int32_t		cseq;
+    pj_uint32_t		rseq;	/* Initialized to -1 	*/
+    struct uac_state_t *next;	/* next call leg	*/
 } uac_state_t;
 
 
@@ -115,7 +117,7 @@
 {
 	pjsip_inv_session	*inv;
 	uas_state_t		*uas_state;
-	uac_state_t		*uac_state;
+	uac_state_t		*uac_state_list;
 };
 
 
@@ -231,6 +233,8 @@
 					       pjsip_tx_data **p_tdata)
 {
     dlg_data *dd;
+    uac_state_t *uac_state = NULL;
+    const pj_str_t *to_tag = &rdata->msg_info.to->tag;
     pjsip_transaction *tsx;
     pjsip_msg *msg;
     pjsip_generic_string_hdr *rseq_hdr;
@@ -261,41 +265,51 @@
 	       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"));
+		 "Ignoring 100rel 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;
+    /* Find UAC state for the specified call leg */
+    uac_state = dd->uac_state_list;
+    while (uac_state) {
+	if (pj_strcmp(&uac_state->tag, to_tag)==0)
+	    break;
+	uac_state = uac_state->next;
     }
 
-    /* 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;
+    /* Create new UAC state if we don't have one */
+    if (uac_state == NULL) {
+	uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, uac_state_t);
+	uac_state->cseq = rdata->msg_info.cseq->cseq;
+	uac_state->rseq = rseq - 1;
+	pj_strdup(dd->inv->dlg->pool, &uac_state->tag, to_tag);
+	uac_state->next = dd->uac_state_list;
+	dd->uac_state_list = uac_state;
+    }
+
+    /* If this is from new INVITE transaction, reset UAC state. */
+    if (rdata->msg_info.cseq->cseq != uac_state->cseq) {
+	uac_state->cseq = rdata->msg_info.cseq->cseq;
+	uac_state->rseq = rseq - 1;
     }
 
     /* Ignore provisional response retransmission */
-    if (rseq <= dd->uac_state->rseq) {
+    if (rseq <= 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) {
+    } else if (rseq != uac_state->rseq + 1) {
 	PJ_LOG(4,(dd->inv->dlg->obj_name, 
-		 "Ignoring provisional response because RSeq jump "
+		 "Ignoring 100rel response because RSeq jump "
 		 "(expecting %u, got %u)",
-		 dd->uac_state->rseq+1, rseq));
+		 uac_state->rseq+1, rseq));
 	return PJ_EIGNORED;
     }
 
     /* Update our RSeq */
-    dd->uac_state->rseq = rseq;
+    uac_state->rseq = rseq;
 
     /* Create PRACK */
     status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method,
@@ -303,6 +317,26 @@
     if (status != PJ_SUCCESS)
 	return status;
 
+    /* If this response is a forked response from a different call-leg,
+     * update the req URI (https://trac.pjsip.org/repos/ticket/1364)
+     */
+    if (pj_strcmp(&uac_state->tag, &dd->inv->dlg->remote.info->tag)) {
+	const pjsip_contact_hdr *mhdr;
+
+	mhdr = (const pjsip_contact_hdr*)
+	       pjsip_msg_find_hdr(rdata->msg_info.msg,
+	                          PJSIP_H_CONTACT, NULL);
+	if (!mhdr || !mhdr->uri) {
+	    PJ_LOG(4,(dd->inv->dlg->obj_name,
+		     "Ignoring 100rel response with no or "
+		     "invalid Contact header"));
+	    pjsip_tx_data_dec_ref(tdata);
+	    return PJ_EIGNORED;
+	}
+	tdata->msg->line.req.uri = (pjsip_uri*)
+				   pjsip_uri_clone(tdata->pool, mhdr->uri);
+    }
+
     /* Create RAck header */
     rack.ptr = rack_buf;
     rack.slen = pj_ansi_snprintf(rack.ptr, sizeof(rack_buf),