Index: pjproject/trunk/pjsip/src/pjsip/sip_util_proxy.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_util_proxy.c	(revision 974)
+++ pjproject/trunk/pjsip/src/pjsip/sip_util_proxy.c	(revision 1127)
@@ -18,23 +18,224 @@
  */
 #include <pjsip/sip_util.h>
+#include <pjsip/sip_endpoint.h>
 #include <pjsip/sip_errno.h>
+#include <pjsip/sip_msg.h>
 #include <pj/assert.h>
-
-PJ_DEF(pj_status_t) pjsip_endpt_create_request_fwd(  pjsip_endpoint *endpt,
-						     pjsip_rx_data *rdata, 
-						     const pjsip_uri *uri,
-						     const pj_str_t *branch,
-						     unsigned options,
-						     pjsip_tx_data **tdata)
-{
-    PJ_UNUSED_ARG(endpt);
-    PJ_UNUSED_ARG(rdata);
-    PJ_UNUSED_ARG(uri);
-    PJ_UNUSED_ARG(branch);
+#include <pj/ctype.h>
+#include <pj/except.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pjlib-util/md5.h>
+
+
+/**
+ * Clone the incoming SIP request or response message. A forwarding proxy
+ * typically would need to clone the incoming SIP message before processing
+ * the message.
+ *
+ * Once a transmit data is created, the reference counter is initialized to 1.
+ *
+ * @param endpt	    The endpoint instance.
+ * @param rdata	    The incoming SIP message.
+ * @param p_tdata   Pointer to receive the transmit data containing
+ *		    the duplicated message.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_clone_msg( pjsip_endpoint *endpt,
+					   const pjsip_rx_data *rdata,
+					   pjsip_tx_data **p_tdata)
+{
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+
+    status = pjsip_endpt_create_tdata(endpt, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    tdata->msg = pjsip_msg_clone(tdata->pool, rdata->msg_info.msg);
+
+    pjsip_tx_data_add_ref(tdata);
+    
+    *p_tdata = tdata;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Create new request message to be forwarded upstream to new destination URI 
+ * in uri. 
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_create_request_fwd(pjsip_endpoint *endpt,
+						   pjsip_rx_data *rdata, 
+						   const pjsip_uri *uri,
+						   const pj_str_t *branch,
+						   unsigned options,
+						   pjsip_tx_data **p_tdata)
+{
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+    PJ_USE_EXCEPTION;
+
+
+    PJ_ASSERT_RETURN(endpt && rdata && p_tdata, PJ_EINVAL);
+    PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, 
+		     PJSIP_ENOTREQUESTMSG);
+
     PJ_UNUSED_ARG(options);
-    PJ_UNUSED_ARG(tdata);
-
-    pj_assert(!"Not implemented yet");
-    return PJ_EBUG;
+
+
+    /* Request forwarding rule in RFC 3261 section 16.6:
+     *
+     * For each target, the proxy forwards the request following these
+     * steps:
+     * 
+     * 1.  Make a copy of the received request
+     * 2.  Update the Request-URI
+     * 3.  Update the Max-Forwards header field
+     * 4.  Optionally add a Record-route header field value
+     * 5.  Optionally add additional header fields
+     * 6.  Postprocess routing information
+     * 7.  Determine the next-hop address, port, and transport
+     * 8.  Add a Via header field value
+     * 9.  Add a Content-Length header field if necessary
+     * 10. Forward the new request
+     *
+     * Of these steps, we only do step 1-3, since the later will be
+     * done by application.
+     */
+
+    status = pjsip_endpt_create_tdata(endpt, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Always increment ref counter to 1 */
+    pjsip_tx_data_add_ref(tdata);
+
+    /* Duplicate the request */
+    PJ_TRY {
+	pjsip_msg *dst;
+	const pjsip_msg *src = rdata->msg_info.msg;
+	const pjsip_hdr *hsrc;
+
+	/* Create the request */
+	tdata->msg = dst = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
+
+	/* Duplicate request method */
+	pjsip_method_copy(tdata->pool, &tdata->msg->line.req.method,
+			  &src->line.req.method);
+
+	/* Set request URI */
+	if (uri) {
+	    dst->line.req.uri = pjsip_uri_clone(tdata->pool, uri);
+	} else {
+	    dst->line.req.uri = pjsip_uri_clone(tdata->pool, src->line.req.uri);
+	}
+
+	/* Clone ALL headers */
+	hsrc = src->hdr.next;
+	while (hsrc != &src->hdr) {
+
+	    pjsip_hdr *hdst;
+
+	    /* If this is the top-most Via header, insert our own before
+	     * cloning the header.
+	     */
+	    if (hsrc == (pjsip_hdr*)rdata->msg_info.via) {
+		pjsip_via_hdr *hvia;
+		hvia = pjsip_via_hdr_create(tdata->pool);
+		if (branch)
+		    pj_strdup(tdata->pool, &hvia->branch_param, branch);
+		else {
+		    pj_str_t new_branch = pjsip_calculate_branch_id(rdata);
+		    pj_strdup(tdata->pool, &hvia->branch_param, &new_branch);
+		}
+		pjsip_msg_add_hdr(dst, (pjsip_hdr*)hvia);
+
+	    }
+	    /* Skip Content-Type and Content-Length as these would be 
+	     * generated when the the message is printed.
+	     */
+	    else if (hsrc->type == PJSIP_H_CONTENT_LENGTH ||
+		     hsrc->type == PJSIP_H_CONTENT_TYPE) {
+
+		hsrc = hsrc->next;
+		continue;
+
+	    }
+#if 0
+	    /* If this is the top-most Route header and it indicates loose
+	     * route, remove the header.
+	     */
+	    else if (hsrc == (pjsip_hdr*)rdata->msg_info.route) {
+
+		const pjsip_route_hdr *hroute = (const pjsip_route_hdr*) hsrc;
+		const pjsip_sip_uri *sip_uri;
+
+		if (!PJSIP_URI_SCHEME_IS_SIP(hroute->name_addr.uri) &&
+		    !PJSIP_URI_SCHEME_IS_SIPS(hroute->name_addr.uri))
+		{
+		    /* This is a bad request! */
+		    status = PJSIP_EINVALIDHDR;
+		    goto on_error;
+		}
+
+		sip_uri = (pjsip_sip_uri*) hroute->name_addr.uri;
+
+		if (sip_uri->lr_param) {
+		    /* Yes lr param is present, skip this Route header */
+		    hsrc = hsrc->next;
+		    continue;
+		}
+	    }
+#endif
+
+	    /* Clone the header */
+	    hdst = pjsip_hdr_clone(tdata->pool, hsrc);
+
+	    /* If this is Max-Forward header, decrement the value */
+	    if (hdst->type == PJSIP_H_MAX_FORWARDS) {
+		pjsip_max_fwd_hdr *hmaxfwd = (pjsip_max_fwd_hdr*)hdst;
+		--hmaxfwd->ivalue;
+	    }
+
+	    /* Append header to new request */
+	    pjsip_msg_add_hdr(dst, hdst);
+
+
+	    hsrc = hsrc->next;
+	}
+
+	/* 16.6.3:
+	 * If the copy does not contain a Max-Forwards header field, the
+         * proxy MUST add one with a field value, which SHOULD be 70.
+	 */
+	if (rdata->msg_info.max_fwd == NULL) {
+	    pjsip_max_fwd_hdr *hmaxfwd = 
+		pjsip_max_fwd_hdr_create(tdata->pool, 70);
+	    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hmaxfwd);
+	}
+
+	/* Clone request body */
+	if (src->body) {
+	    dst->body = pjsip_msg_body_clone(tdata->pool, src->body);
+	}
+
+    }
+    PJ_CATCH_ANY {
+	status = PJ_ENOMEM;
+	goto on_error;
+    }
+    PJ_END
+
+
+    /* Done */
+    *p_tdata = tdata;
+    return PJ_SUCCESS;
+
+on_error:
+    pjsip_tx_data_dec_ref(tdata);
+    return status;
 }
 
@@ -43,13 +244,85 @@
 						     pjsip_rx_data *rdata, 
 						     unsigned options,
-						     pjsip_tx_data **tdata)
-{
-    PJ_UNUSED_ARG(endpt);
-    PJ_UNUSED_ARG(rdata);
+						     pjsip_tx_data **p_tdata)
+{
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+    PJ_USE_EXCEPTION;
+
     PJ_UNUSED_ARG(options);
-    PJ_UNUSED_ARG(tdata);
-
-    pj_assert(!"Not implemented yet");
-    return PJ_EBUG;
+
+    status = pjsip_endpt_create_tdata(endpt, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pjsip_tx_data_add_ref(tdata);
+
+    PJ_TRY {
+	pjsip_msg *dst;
+	const pjsip_msg *src = rdata->msg_info.msg;
+	const pjsip_hdr *hsrc;
+
+	/* Create the request */
+	tdata->msg = dst = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG);
+
+	/* Clone the status line */
+	dst->line.status.code = src->line.status.code;
+	pj_strdup(tdata->pool, &dst->line.status.reason, 
+		  &src->line.status.reason);
+
+	/* Duplicate all headers */
+	hsrc = src->hdr.next;
+	while (hsrc != &src->hdr) {
+	    
+	    /* Skip Content-Type and Content-Length as these would be 
+	     * generated when the the message is printed.
+	     */
+	    if (hsrc->type == PJSIP_H_CONTENT_LENGTH ||
+		hsrc->type == PJSIP_H_CONTENT_TYPE) {
+
+		hsrc = hsrc->next;
+		continue;
+
+	    }
+	    /* Remove the first Via header */
+	    else if (hsrc == (pjsip_hdr*) rdata->msg_info.via) {
+
+		hsrc = hsrc->next;
+		continue;
+	    }
+
+	    pjsip_msg_add_hdr(dst, pjsip_hdr_clone(tdata->pool, hsrc));
+
+	    hsrc = hsrc->next;
+	}
+
+	/* Clone message body */
+	if (src->body)
+	    dst->body = pjsip_msg_body_clone(tdata->pool, src->body);
+
+
+    }
+    PJ_CATCH_ANY {
+	status = PJ_ENOMEM;
+	goto on_error;
+    }
+    PJ_END;
+
+    *p_tdata = tdata;
+    return PJ_SUCCESS;
+
+on_error:
+    pjsip_tx_data_dec_ref(tdata);
+    return status;
+}
+
+
+static void digest2str(const unsigned char digest[], char *output)
+{
+    int i;
+    for (i = 0; i<16; ++i) {
+	pj_val_to_hex_digit(digest[i], output);
+	output += 2;
+    }
 }
 
@@ -57,10 +330,26 @@
 PJ_DEF(pj_str_t) pjsip_calculate_branch_id( pjsip_rx_data *rdata )
 {
-    pj_str_t empty_str = { NULL, 0 };
-
-    PJ_UNUSED_ARG(rdata);
-    pj_assert(!"Not implemented yet");
-    return empty_str;
-}
-
-
+    pj_md5_context ctx;
+    pj_uint8_t digest[16];
+    pj_str_t branch;
+
+    /* Create branch ID for new request by calculating MD5 hash
+     * of the branch parameter in top-most Via header.
+     */
+    pj_md5_init(&ctx);
+    pj_md5_update(&ctx, (pj_uint8_t*)rdata->msg_info.via->branch_param.ptr,
+		  rdata->msg_info.via->branch_param.slen);
+    pj_md5_final(&ctx, digest);
+
+    branch.ptr = pj_pool_alloc(rdata->tp_info.pool, 
+			       32 + PJSIP_RFC3261_BRANCH_LEN);
+    pj_memcpy(branch.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN);
+
+    digest2str(digest, branch.ptr+PJSIP_RFC3261_BRANCH_LEN);
+
+    branch.slen = 32 + PJSIP_RFC3261_BRANCH_LEN;
+
+    return branch;
+}
+
+
