/* 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#include <pjsip/sip_util.h>
#include <pjsip/sip_endpoint.h>
#include <pjsip/sip_errno.h>
#include <pjsip/sip_msg.h>
#include <pj/assert.h>
#include <pj/ctype.h>
#include <pj/except.h>
#include <pj/guid.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);


    /* 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*) 
                                pjsip_uri_clone(tdata->pool, uri);
        } else {
            dst->line.req.uri= (pjsip_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*) 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;
}


PJ_DEF(pj_status_t) pjsip_endpt_create_response_fwd( pjsip_endpoint *endpt,
                                                     pjsip_rx_data *rdata, 
                                                     unsigned options,
                                                     pjsip_tx_data **p_tdata)
{
    pjsip_tx_data *tdata;
    pj_status_t status;
    PJ_USE_EXCEPTION;

    PJ_UNUSED_ARG(options);

    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*)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;
    }
}


PJ_DEF(pj_str_t) pjsip_calculate_branch_id( pjsip_rx_data *rdata )
{
    pj_md5_context ctx;
    pj_uint8_t digest[16];
    pj_str_t branch;
    pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID, 
                               PJSIP_RFC3261_BRANCH_LEN};

    /* If incoming request does not have RFC 3261 branch value, create
     * a branch value from GUID .
     */
    if (pj_strnicmp(&rdata->msg_info.via->branch_param, 
                   &rfc3261_branch, PJSIP_RFC3261_BRANCH_LEN) != 0 ) 
    {
        pj_str_t tmp;

        branch.ptr = (char*)
                     pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_BRANCH_LEN);
        branch.slen = PJSIP_RFC3261_BRANCH_LEN;
        pj_memcpy(branch.ptr, PJSIP_RFC3261_BRANCH_ID, 
                  PJSIP_RFC3261_BRANCH_LEN);

        tmp.ptr = branch.ptr + PJSIP_RFC3261_BRANCH_LEN + 2;
        *(tmp.ptr-2) = (pj_int8_t)(branch.slen+73); 
        *(tmp.ptr-1) = (pj_int8_t)(branch.slen+99);
        pj_generate_unique_string( &tmp );

        branch.slen = PJSIP_MAX_BRANCH_LEN;
        return 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,
                  (unsigned)rdata->msg_info.via->branch_param.slen);
    pj_md5_final(&ctx, digest);

    branch.ptr = (char*)
                 pj_pool_alloc(rdata->tp_info.pool, 
                               34 + PJSIP_RFC3261_BRANCH_LEN);
    pj_memcpy(branch.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN);
    branch.slen = PJSIP_RFC3261_BRANCH_LEN;
    *(branch.ptr+PJSIP_RFC3261_BRANCH_LEN) = (pj_int8_t)(branch.slen+73);
    *(branch.ptr+PJSIP_RFC3261_BRANCH_LEN+1) = (pj_int8_t)(branch.slen+99);
    digest2str(digest, branch.ptr+PJSIP_RFC3261_BRANCH_LEN+2);
    branch.slen = 34 + PJSIP_RFC3261_BRANCH_LEN;

    return branch;
}


