/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 *  Functions that parse and create HTTP/1.1-like messages(RFC 2068). Basically
 *  code that converts from network(ie text) form to a usable structure
 *  and vice-versa.
 */

#include <errno.h>
#include <limits.h>

#include "plstr.h"
#include "cpr_types.h"
#include "cpr_stdio.h"
#include "cpr_stdlib.h"
#include "cpr_string.h"
#include "httpish.h"
#include "ccsip_protocol.h"
#include "phone_debug.h"
#include "ccsip_core.h"

//#define HTTPISH_DEBUG if (1)
#define MSG_DELIMIT_SIZE   80
#define TMP_BODY_BUF_SIZE 200
#define CMPC_HEADER_SIZE  256

extern sip_header_t sip_cached_headers[];
httpishMsg_t *
httpish_msg_create (void)
{
    int i;
    httpishMsg_t *msg;

    msg = (httpishMsg_t *) cpr_calloc(1, sizeof(httpishMsg_t));
    if (!msg) {
        return NULL;
    }

    msg->headers = (queuetype *) cpr_calloc(1, sizeof(queuetype));

    if (!msg->headers) {
        cpr_free(msg);
        return NULL;
    }

    msg->retain_flag = FALSE;
    msg->mesg_line = NULL;
    msg->content_length = 0;
    msg->is_complete = FALSE;
    msg->headers_read = FALSE;

    for (i = 0; i < HTTPISH_MAX_BODY_PARTS; i++) {
        msg->mesg_body[i].msgContentType = NULL;
        msg->mesg_body[i].msgBody = NULL;
        msg->mesg_body[i].msgLength = 0;
        msg->mesg_body[i].msgContentId = NULL;
        msg->mesg_body[i].msgContentEnc = SIP_CONTENT_ENCODING_IDENTITY_VALUE;
        msg->mesg_body[i].msgContentDisp = SIP_CONTENT_DISPOSITION_SESSION_VALUE;
        msg->mesg_body[i].msgRequiredHandling = TRUE;
        msg->mesg_body[i].msgContentTypeValue = SIP_CONTENT_TYPE_UNKNOWN_VALUE;
    }

    msg->num_body_parts = 0;
    msg->raw_body = NULL;

    queue_init(msg->headers, 0);

    return msg;
}

int
httpish_strncasecmp(const char *s1, const char* s2, size_t len)
{
  /*This routine is an enhanced version of strncasecmp().
   *It ensures that the two strings being compared for size "len"
   *don't have trailing characters beyond "len" chars.
   *The trailing whitespaces beyond "len" chars is ignored.
   */
    const unsigned char *us1 = (const unsigned char *) s1;
    const unsigned char *us2 = (const unsigned char *) s2;

    /* No match if only one ptr is NULL */
    if ((!s1 && s2) || (s1 && !s2))
        return ((int) (s1 - s2));

    if ((len == 0) || (s1 == s2))
        return 0;

    while (len-- > 0 && toupper(*us1) == toupper(*us2)) {
        if (len == 0 || *us1 == '\0' || *us2 == '\0')
            break;
        us1++;
        us2++;
    }

   if (len == 0 && toupper(*us1) == toupper(*us2)) {
       //all "len" chars are compared, need to look for trailing
       //chars beyond "len" string size. Ignore white spaces.
      while (*(++us1) != '\0') {
          if (*us1 != ' ' && *us1 != '\t') {
               break;
          }
      }

      while (*(++us2) != '\0') {
          if (*us2 != ' ' && *us2 != '\t') {
               break;
          }
      }
   }


   return (toupper(*us1) - toupper(*us2));
}

void
httpish_msg_free (httpishMsg_t *msg)
{
    int i;

    if ((!msg) || (msg->retain_flag == TRUE)) {
        return;
    }

    UTILFREE(msg->mesg_line);

    // Free all body parts
    for (i = 0; i < HTTPISH_MAX_BODY_PARTS; i++) {
        UTILFREE(msg->mesg_body[i].msgContentType);
        UTILFREE(msg->mesg_body[i].msgBody);
        UTILFREE(msg->mesg_body[i].msgContentId);
    }
    UTILFREE(msg->raw_body);

    if (msg->headers) {
        httpish_header *this_header;

        this_header = (httpish_header *) dequeue(msg->headers);
        while (this_header != NULL) {
            UTILFREE(this_header->header);
            UTILFREE(this_header);
            this_header = (httpish_header *) dequeue(msg->headers);
        }
    }

    UTILFREE(msg->headers);
    msg->headers = NULL;

    /* Free the header cache */
    for (i = 0; i < HTTPISH_HEADER_CACHE_SIZE; ++i) {
        if (msg->hdr_cache[i].hdr_start) {
            cpr_free(msg->hdr_cache[i].hdr_start);
        }
    }

    /* Free the httpishMsg_t struct itself */
    cpr_free(msg);
}

boolean
httpish_msg_is_request (httpishMsg_t *msg,
                        const char *schema,
                        int schema_len)
{
    char *loc;

    loc = msg->mesg_line;

    if (!msg->is_complete || !msg->mesg_line) {
        return FALSE;
    }

    /*
     *  There might be a couple of leading spaces. Not allowed,
     *  but still be friendly
     */
    while ((*loc == ' ') && (*loc != '\0')) {
        loc++;
    }

    if (strncmp(loc, schema, schema_len)) {
        return TRUE;
    } else {
        return FALSE;
    }
}


boolean
httpish_msg_is_complete (httpishMsg_t *msg)
{
    return msg->is_complete;
}


hStatus_t
httpish_msg_add_reqline (httpishMsg_t *msg,
                         const char *method,
                         const char *url,
                         const char *version)
{

    uint32_t linesize = 0;

    if (!msg || !method || !url || !version) {
        return HSTATUS_FAILURE;
    }

    if (msg->mesg_line) {
        cpr_free(msg->mesg_line);
    }

    linesize = strlen(method) + 1 + strlen(url) + 1 + strlen(version) + 1;

    msg->mesg_line = (char *) cpr_malloc(linesize * sizeof(char));
    if (!msg->mesg_line) {
        return HSTATUS_FAILURE;
    }

    snprintf(msg->mesg_line, linesize, "%s %s %s", method, url, version);

    return HSTATUS_SUCCESS;
}

hStatus_t
httpish_msg_add_respline (httpishMsg_t *msg,
                          const char *version,
                          uint16_t status_code,
                          const char *reason_phrase)
{
    uint32_t linesize = 0;

    if (!msg || !reason_phrase || !version ||
        (status_code < HTTPISH_MIN_STATUS_CODE)) {
        return HSTATUS_FAILURE;
    }

    if (msg->mesg_line) {
        cpr_free(msg->mesg_line);
    }

    /* Assumes status codes are max 6 characters long */
    linesize = strlen(version) + 1 + 6 + 1 + strlen(reason_phrase) + 1;

    msg->mesg_line = (char *) cpr_malloc(linesize * sizeof(char));
    if (!msg->mesg_line) {
        return HSTATUS_FAILURE;
    }

    snprintf(msg->mesg_line, linesize, "%s %d %s",
             version, status_code, reason_phrase);

    return HSTATUS_SUCCESS;
}


httpishReqLine_t *
httpish_msg_get_reqline (httpishMsg_t *msg)
{
    char *this_token;
    char *msgline;
    httpishReqLine_t *hreq = NULL;
    char *strtok_state;

    if (!msg || !msg->mesg_line || !(msgline = cpr_strdup(msg->mesg_line))) {
        return NULL;
    }

    hreq = (httpishReqLine_t *) cpr_malloc(sizeof(httpishReqLine_t));
    if (!hreq) {
        cpr_free(msgline);
        return NULL;
    }

    this_token = PL_strtok_r(msgline, " ", &strtok_state);

    if (!this_token) {
        cpr_free(hreq);
        cpr_free(msgline);
        return NULL;
    }

    hreq->method = cpr_strdup(this_token);

    this_token = PL_strtok_r(NULL, " ", &strtok_state);

    if (!this_token) {
        cpr_free(hreq->method);
        cpr_free(hreq);
        cpr_free(msgline);
        return NULL;
    }

    hreq->url = cpr_strdup(this_token);

    this_token = PL_strtok_r(NULL, " ", &strtok_state);

    if (!this_token) {
        cpr_free(hreq->method);
        cpr_free(hreq->url);
        cpr_free(hreq);
        cpr_free(msgline);
        return NULL;
    }

    hreq->version = cpr_strdup(this_token);
    cpr_free(msgline);
    return hreq;
}


httpishRespLine_t *
httpish_msg_get_respline (httpishMsg_t *msg)
{
    char *this_token;
    char *msgline;
    httpishRespLine_t *hrsp = NULL;
    char *strtok_state;
    unsigned long strtoul_result;
    char *strtoul_end;

    if (!msg || !msg->mesg_line) {
        return NULL;
    }

    msgline = cpr_strdup(msg->mesg_line);
    if (!msgline) {
        return NULL;
    }

    hrsp = (httpishRespLine_t *) cpr_malloc(sizeof(httpishRespLine_t));

    if (!hrsp) {
        cpr_free(msgline);
        return NULL;
    }

    this_token = PL_strtok_r(msgline, " ", &strtok_state);

    if (!this_token) {
        cpr_free(hrsp);
        cpr_free(msgline);
        return NULL;
    }

    hrsp->version = cpr_strdup(this_token);

    this_token = PL_strtok_r(NULL, " ", &strtok_state);

    if (!this_token) {
        cpr_free(hrsp->version);
        cpr_free(hrsp);
        cpr_free(msgline);
        return NULL;
    }

    errno = 0;
    strtoul_result = strtoul(this_token, &strtoul_end, 10);

    if (errno || this_token == strtoul_end || strtoul_result > USHRT_MAX) {
        cpr_free(hrsp->version);
        cpr_free(hrsp);
        cpr_free(msgline);
        return NULL;
    }

    hrsp->status_code = (uint16_t) strtoul_result;

    this_token = PL_strtok_r(NULL, " ", &strtok_state);

    /* reason phrase is optional */
    if (this_token) {
        hrsp->reason_phrase = cpr_strdup(this_token);
    } else {
        hrsp->reason_phrase = NULL;
    }

    cpr_free(msgline);
    return (hrsp);
}



void
httpish_msg_free_reqline (httpishReqLine_t *rqline)
{
    if (!rqline) {
        return;
    }

    UTILFREE(rqline->method);
    UTILFREE(rqline->url);
    UTILFREE(rqline->version);
}


void
httpish_msg_free_respline (httpishRespLine_t *rspline)
{
    if (!rspline) {
        return;
    }

    UTILFREE(rspline->reason_phrase);
    UTILFREE(rspline->version);
}

hStatus_t
httpish_msg_add_text_header (httpishMsg_t *msg,
                             const char *hname,
                             const char *hval)
{
    uint32_t        linesize = 0;
    httpish_header *this_header = NULL;
    char           *header_line = NULL;

    if (!msg || !hname || !hval) {
        return HSTATUS_FAILURE;
    }

    linesize = strlen(hname) + 2 + strlen(hval) + 1;

    header_line = (char *) cpr_malloc(linesize * sizeof(char));
    if (!header_line)
        return HSTATUS_FAILURE;

    this_header = (httpish_header *) cpr_malloc(sizeof(httpish_header));
    if (!this_header) {
        cpr_free(header_line);
        return HSTATUS_FAILURE;
    }

    snprintf(header_line, linesize, "%s: %s", hname, hval);

    this_header->header = header_line;
    this_header->next = NULL;

    enqueue(msg->headers, (void *) this_header);

    return HSTATUS_SUCCESS;
}


hStatus_t
httpish_msg_add_int_header (httpishMsg_t *msg,
                            const char *hname,
                            int32_t hval)
{
    uint32_t        linesize = 0;
    char           *header_line = NULL;
    httpish_header *this_header = NULL;

    if (!msg || !hname) {
        return HSTATUS_FAILURE;
    }

    /* Assumes the int is less than 10 characters */
    linesize = strlen(hname) + 2 + 10 + 1;

    header_line = (char *) cpr_malloc(linesize * sizeof(char));
    if (!header_line) {
        return HSTATUS_FAILURE;
    }

    this_header = (httpish_header *) cpr_malloc(sizeof(httpish_header));
    if (!this_header) {
        cpr_free(header_line);
        return HSTATUS_FAILURE;
    }

    snprintf(header_line, linesize, "%s: %d", hname, hval);

    this_header->header = header_line;
    this_header->next = NULL;

    enqueue(msg->headers, (void *) this_header);

    return HSTATUS_SUCCESS;
}

const char *
httpish_msg_get_cached_header_val (httpishMsg_t *msg,
                                   int cache_index)
{
    return msg->hdr_cache[cache_index].val_start;
}

int
compact_hdr_cmp (char *this_line,
                 const char *c_hname)
{
    char cmpct_hdr[CMPC_HEADER_SIZE];

    if (c_hname) {
        sstrncpy(cmpct_hdr, c_hname, CMPC_HEADER_SIZE);
        return cpr_strcasecmp(this_line, cmpct_hdr);
    }
    return -1;
}

int
httpish_header_name_val (char *sipHeaderName,  char *this_line)
{
    unsigned int x = 0;
    boolean  nameFound = FALSE;

    if (!sipHeaderName || !this_line) {
       return (SIP_ERROR);
    }

    sipHeaderName[0] = '\0';

    /* Remove the leading white spaces  eg: ......From:  or .....From....: */
    while ((*this_line==' ' || *this_line=='\t') ) {
        this_line++;
    }

     /* Copy the allowed characters for header field name */
    while ((*this_line > 32) && (*this_line < 127) && (x < HTTPISH_HEADER_NAME_SIZE)) {
        if (*this_line == ':') {
            nameFound = TRUE;
            sipHeaderName[x] = '\0';
            break;
        }
        sipHeaderName[x] = *this_line;
        this_line++;
        x++;
    }

    /* Remove trailing white spaces */
     if (nameFound == FALSE && x < HTTPISH_HEADER_NAME_SIZE) {
         while ((*this_line == ' ' || *this_line=='\t') ){
             this_line++;
             if (*this_line == ':') {
                 nameFound = TRUE;
                 sipHeaderName[x] = '\0';
                 break;
             }
         }
     }
     sipHeaderName[HTTPISH_HEADER_NAME_SIZE-1] = '\0';

     if (nameFound) {
         return (SIP_OK);
     } else {
         return (SIP_ERROR);
     }
}

boolean
httpish_msg_header_present (httpishMsg_t *msg,
                            const char *hname)
{
    nexthelper *p;
    char       *this_line = NULL;

    /*
     * To allow case-insensitive compact headers, we need to compare 2
     * characters before we can decide, what the header name is.
     * e.g. Call-ID and Content-Type both start with C.
     * For now assume there is no white space between header name and ":"
     */

    if (!msg || !hname || (msg->headers->count == 0)) {
        return FALSE;
    }

    p = (nexthelper *) msg->headers->qhead;
    while (p) {
        this_line = ((httpish_header *)p)->header;
        if (this_line) {
            /* Remove leading spaces */
            while ((*this_line == ' ') && (*this_line != '\0'))
                this_line++;
            if ((strlen(this_line) >= strlen(hname)) &&
                (cpr_strncasecmp(this_line, hname, strlen(hname))) == 0) {
                return TRUE;
            }
        }
        p = p->next;
    }

    return FALSE;
}
const char *
httpish_msg_get_header_val (httpishMsg_t *msg,
                            const char *hname,
                            const char *c_hname)
{
    static const char fname[] = "httpish_msg_get_header_val";
    nexthelper *p;
    char       *this_line = NULL;
    char       headerName[HTTPISH_HEADER_NAME_SIZE];

    headerName[0] = '\0';

    /*
     * To allow case-insensitive compact headers, we need to compare 2
     * characters before we can decide, what the header name is.
     * e.g. Call-ID and Content-Type both start with C.
     * For now assume there is no white space between header name and ":"
     */

    if (!msg || !hname || (msg->headers->count == 0)) {
        return NULL;
    }

    p = (nexthelper *) msg->headers->qhead;
    while (p) {
        this_line = ((httpish_header *)p)->header;

        if (httpish_header_name_val(headerName, this_line)) {
            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Invalid Header Passed %s\n", DEB_F_PREFIX_ARGS(HTTPISH, fname), this_line);
            return (NULL);
        }

        if (this_line) {
            if ((cpr_strcasecmp(headerName, hname) == 0 ||
                 compact_hdr_cmp(headerName, c_hname) == 0)) {
                this_line = strchr(this_line, ':');
                if (this_line) {
                    this_line++;
                    /* Remove leading spaces */
                    while ((*this_line == ' ') && (*this_line != '\0'))
                        this_line++;
                    if (*this_line == '\0')
                        return (NULL);
                    else
                        return ((const char *) this_line);
                }
            }
        }
        p = p->next;
    }
    return NULL;
}

int32_t
httpish_msg_get_content_length (httpishMsg_t *msg)
{
    return msg->content_length;
}

static boolean
httpish_msg_to_wstream (pmhWstream_t *ws,
                        httpishMsg_t *msg)
{
    nexthelper *p;
    char        tmp_body_buf[TMP_BODY_BUF_SIZE];
    int         buf_len, total_length = 0, i, boundary_size;

    if (!pmhutils_wstream_write_line(ws, msg->mesg_line)) {
        return (FALSE);
    }

    p = (nexthelper *) msg->headers->qhead;
    while (p) {
        if (!pmhutils_wstream_write_line(ws,
                (char *) (((httpish_header *)p)->header))) {
            return (FALSE);
        }
        p = p->next;
    }
    if (msg->num_body_parts > 0) {
        if (msg->num_body_parts > 1) {
            // Write out the special Content-Type header and the
            // Mime-Version header and the aggregate Content-Length header
            // followed by the unique boundary
            buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE,
                               "%s: multipart/mixed; boundary=%s\r\n",
                               HTTPISH_HEADER_CONTENT_TYPE, uniqueBoundary);

            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
                return (FALSE);
            }

            buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "%s: 1.0\r\n",
                               HTTPISH_HEADER_MIME_VERSION);
            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
                return (FALSE);
            }

            // Traverse the list and calculate the total size of the
            // body
            boundary_size = strlen("\r\n--\r\n") + strlen(uniqueBoundary);
            for (i = 0; i < msg->num_body_parts; i++) {
                total_length += boundary_size;
                total_length += msg->mesg_body[i].msgLength;
                total_length += sizeof(HTTPISH_HEADER_CONTENT_TYPE) + 1;
                switch (msg->mesg_body[i].msgContentTypeValue) {
                default:
                case SIP_CONTENT_TYPE_UNKNOWN_VALUE:
                    total_length += strlen(msg->mesg_body[i].msgContentType) + 2;
                    break;
                case SIP_CONTENT_TYPE_SDP_VALUE:
                    total_length += sizeof(SIP_CONTENT_TYPE_SDP) + 1;
                    break;
                case SIP_CONTENT_TYPE_SIPFRAG_VALUE:
                    total_length += sizeof(SIP_CONTENT_TYPE_SIPFRAG) + 1;
                    break;
                case SIP_CONTENT_TYPE_DIALOG_VALUE:
                    total_length += sizeof(SIP_CONTENT_TYPE_DIALOG) + 1;
                    break;
                case SIP_CONTENT_TYPE_KPML_REQUEST_VALUE:
                    total_length += sizeof(SIP_CONTENT_TYPE_KPML_REQUEST) + 1;
                    break;
                case SIP_CONTENT_TYPE_KPML_RESPONSE_VALUE:
                    total_length += sizeof(SIP_CONTENT_TYPE_KPML_RESPONSE) + 1;
                    break;
                case SIP_CONTENT_TYPE_REMOTECC_REQUEST_VALUE:
                    total_length += sizeof(SIP_CONTENT_TYPE_REMOTECC_REQUEST) + 1;
                    break;
                case SIP_CONTENT_TYPE_REMOTECC_RESPONSE_VALUE:
                    total_length += sizeof(SIP_CONTENT_TYPE_REMOTECC_RESPONSE) + 1;
                    break;
                case SIP_CONTENT_TYPE_CTI_VALUE:
                    total_length += sizeof(SIP_CONTENT_TYPE_CTI) + 1;
                    break;
                case SIP_CONTENT_TYPE_CMXML_VALUE:
                    total_length += sizeof(SIP_CONTENT_TYPE_CMXML) + 1;
                    break;
                case SIP_CONTENT_TYPE_TEXT_PLAIN_VALUE:
                    total_length += sizeof(SIP_CONTENT_TYPE_TEXT_PLAIN) + 1;
                    break;
                case SIP_CONTENT_TYPE_PRESENCE_VALUE:
                    total_length += sizeof(SIP_CONTENT_TYPE_PRESENCE) + 1;
                    break;
                }
                // Now estimate size of Content-Disposition header
                total_length += sizeof(SIP_HEADER_CONTENT_DISP) + 1;
                switch (msg->mesg_body[i].msgContentDisp) {
                case SIP_CONTENT_DISPOSITION_RENDER_VALUE:
                    total_length += sizeof(SIP_CONTENT_DISPOSITION_RENDER) - 1;
                    break;
                case SIP_CONTENT_DISPOSITION_SESSION_VALUE:
                default:
                    total_length += sizeof(SIP_CONTENT_DISPOSITION_SESSION) - 1;
                    break;
                case SIP_CONTENT_DISPOSITION_ICON_VALUE:
                    total_length += sizeof(SIP_CONTENT_DISPOSITION_ICON) - 1;
                    break;
                case SIP_CONTENT_DISPOSITION_ALERT_VALUE:
                    total_length += sizeof(SIP_CONTENT_DISPOSITION_ALERT) - 1;
                    break;
                case SIP_CONTENT_DISPOSITION_PRECONDITION_VALUE:
                    total_length += sizeof(SIP_CONTENT_DISPOSITION_PRECONDITION) - 1;
                    break;
                }
                // Now the handling attribute
                total_length += sizeof(";handling=") - 1;
                if (msg->mesg_body[i].msgRequiredHandling) {
                    total_length += sizeof("required") + 1;
                } else {
                    total_length += sizeof("optional") + 1;
                }
                // Now the content id
                if (msg->mesg_body[i].msgContentId) {
                    total_length += sizeof(HTTPISH_HEADER_CONTENT_ID) + 1 +
                        strlen(msg->mesg_body[i].msgContentId) + 2;

                }
            }
            // Now account for the closing boundary which is 2+boundary_size
            total_length += 2 + boundary_size + 2;

            // Now write it out
            buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "%s: %d\r\n",
                               HTTPISH_HEADER_CONTENT_LENGTH, total_length);

            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
                return (FALSE);
            }

            buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "\r\n--%s\r\n", uniqueBoundary);

            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
                return (FALSE);
            }

        } else {
            // Write out Content-Length for the first body
            total_length = msg->mesg_body[0].msgLength;
            buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "%s: %d\r\n",
                               HTTPISH_HEADER_CONTENT_LENGTH, total_length);

            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
                return (FALSE);
            }
        }
        for (i = 0; i < msg->num_body_parts; i++) {
            if (i > 0) {
                // If there is another body to come, write the unique boundary
                buf_len = snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "\r\n--%s\r\n", uniqueBoundary);
                if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
                    return (FALSE);
                }
            }
            snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "Content-Type: %s\r\n",
                    msg->mesg_body[i].msgContentType);
            sstrncat(tmp_body_buf, "Content-Disposition: ",
                    sizeof(tmp_body_buf) - strlen(tmp_body_buf));

            switch (msg->mesg_body[i].msgContentDisp) {
            case SIP_CONTENT_DISPOSITION_RENDER_VALUE:
                sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_RENDER,
                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
                break;
            case SIP_CONTENT_DISPOSITION_SESSION_VALUE:
            default:
                sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_SESSION,
                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
                break;
            case SIP_CONTENT_DISPOSITION_ICON_VALUE:
                sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_ICON,
                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
                break;
            case SIP_CONTENT_DISPOSITION_ALERT_VALUE:
                sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_ALERT,
                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
                break;
            case SIP_CONTENT_DISPOSITION_PRECONDITION_VALUE:
                sstrncat(tmp_body_buf, SIP_CONTENT_DISPOSITION_PRECONDITION,
                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
                break;
            }
            if (msg->mesg_body[i].msgRequiredHandling) {
                sstrncat(tmp_body_buf, ";handling=required\r\n",
                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
            } else {
                sstrncat(tmp_body_buf, ";handling=optional\r\n",
                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
            }
            if (msg->mesg_body[i].msgContentId) {
                sstrncat(tmp_body_buf, "Content-Id: ",
                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
                sstrncat(tmp_body_buf, msg->mesg_body[i].msgContentId,
                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
                sstrncat(tmp_body_buf, "\r\n",
                        sizeof(tmp_body_buf) - strlen(tmp_body_buf));
            }
            sstrncat(tmp_body_buf, "\r\n",
                     sizeof(tmp_body_buf) - strlen(tmp_body_buf));
            buf_len = strlen(tmp_body_buf);
            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
                return (FALSE);
            }

            // Now write the body
            if (!pmhutils_wstream_write_bytes(ws, msg->mesg_body[i].msgBody,
                                              msg->mesg_body[i].msgLength)) {
                return (FALSE);
            }
        }
        // After writing out the last body part, write out the last unique
        // boundary line
        if (msg->num_body_parts > 1) {
            snprintf(tmp_body_buf, TMP_BODY_BUF_SIZE, "\r\n--%s--\r\n", uniqueBoundary);
            buf_len = strlen(tmp_body_buf);
            if (!pmhutils_wstream_write_bytes(ws, tmp_body_buf, buf_len)) {
                return (FALSE);
            }
        }
    } else {
        if (!pmhutils_wstream_write_byte(ws, '\r')) {
            return (FALSE);
        }
        if (!pmhutils_wstream_write_byte(ws, '\n')) {
            return (FALSE);
        }
    }

    return (TRUE);
}

hStatus_t
httpish_msg_write (httpishMsg_t *msg,
                   char *buf,
                   uint32_t *nbytes)
{
    pmhWstream_t *ws = NULL;

    ws = pmhutils_wstream_create_with_buf(buf, *nbytes);
    if (!ws) {
        return (HSTATUS_FAILURE);
    }

    if (!httpish_msg_to_wstream(ws, msg)) {
        pmhutils_wstream_delete(ws, FALSE);
        cpr_free(ws);
        return (HSTATUS_FAILURE);
    }

    *nbytes = pmhutils_wstream_get_length(ws);
    pmhutils_wstream_delete(ws, FALSE);
    cpr_free(ws);
    return HSTATUS_SUCCESS;
}

int
httpish_cache_header_val (httpishMsg_t *hmsg,
                          char *this_line)
{
    static const char fname[] = "httpish_cache_header_val";
    char *hdr_start;
    httpish_cache_t *hdr_cache;
    int i;
    char headerName[HTTPISH_HEADER_NAME_SIZE];

    headerName[0] = '\0';

    hdr_cache = hmsg->hdr_cache;
    hdr_start = this_line;

    if (httpish_header_name_val(headerName, this_line)) {
        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Invalid Header %s\n", DEB_F_PREFIX_ARGS(HTTPISH, fname), this_line);
        return (SIP_ERROR);
    }

    for (i = 0; i < HTTPISH_HEADER_CACHE_SIZE; ++i) {
        sip_header_t *tmp = sip_cached_headers + i;

        if (cpr_strcasecmp(headerName, tmp->hname) == 0 ||
            compact_hdr_cmp(headerName, tmp->c_hname) == 0) {
            this_line = strchr(this_line, ':');
            if (this_line) {
                this_line++; /* Skip the ':' */

                /* Remove leading spaces */
                while (*this_line == ' ' || *this_line == '\t') {
                    this_line++;
                }
                if (*this_line) {
                    if (hdr_cache[i].hdr_start) {
                        int org_len, offset;
                        int size;
                        char *newbuf;

                        /* Multiple instances of a header, concatenate the
                         * header values with a ','
                         */
                        org_len = strlen(hdr_cache[i].hdr_start);
                        offset = hdr_cache[i].val_start - hdr_cache[i].hdr_start;
                        size = org_len + 2 + strlen(this_line);
                        newbuf = (char *) cpr_realloc(hdr_cache[i].hdr_start,
                                                      size);
                        if (newbuf == NULL) {
                            cpr_free(hdr_cache[i].hdr_start);
                            hdr_cache[i].hdr_start = NULL;
                            break;
                        }
                        hdr_cache[i].hdr_start = newbuf;
                        hdr_cache[i].val_start = hdr_cache[i].hdr_start + offset;
                        hdr_cache[i].hdr_start[org_len] = ',';
                        sstrncpy(hdr_cache[i].hdr_start + org_len + 1, this_line,
                                size - org_len - 1);
                        cpr_free(hdr_start);
                    } else {
                        hdr_cache[i].hdr_start = hdr_start;
                        hdr_cache[i].val_start = this_line;
                    }
                } else { // this line is blank
                    cpr_free(hdr_start);
                }
            } else { // this line does not have a ':'
                cpr_free(hdr_start);
            }
            return 0;
        }
    }
    return -1;
}

/*
 * Return -1 for invalid/empty content-length value
 * else return the content length
 */
int32_t
get_content_length (httpishMsg_t *hmsg)
{
    int i;
    const char *hdr_val;
    long strtol_result;
    char *strtol_end;

    hdr_val = httpish_msg_get_cached_header_val(hmsg, CONTENT_LENGTH);
    if (hdr_val == NULL) {
        return -1;
    }

    for (i = 0; hdr_val[i]; ++i) {
        if (!isdigit((int) hdr_val[i])) {
            return -1;
        }
    }

    /* If the string was empty then the content length is still invalid */
    if (!i) {
        return -1;
    }

    errno = 0;
    strtol_result = strtol(hdr_val, &strtol_end, 10);

    if (errno || hdr_val == strtol_end || strtol_result > INT_MAX) {
        return -1;
    } else {
        return (int) strtol_result;
    }
}

uint8_t
get_content_type_value (const char *content_type)
{
    if (!content_type) {
        return SIP_CONTENT_TYPE_UNKNOWN_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_SDP,
                                sizeof(SIP_CONTENT_TYPE_SDP) - 1)) {
        return SIP_CONTENT_TYPE_SDP_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_SIPFRAG,
                                sizeof(SIP_CONTENT_TYPE_SIPFRAG) - 1)) {
        return SIP_CONTENT_TYPE_SIPFRAG_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_TEXT_PLAIN,
                                sizeof(SIP_CONTENT_TYPE_TEXT_PLAIN) - 1)) {
        return SIP_CONTENT_TYPE_TEXT_PLAIN_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_SIP,
                                sizeof(SIP_CONTENT_TYPE_SIP) - 1)) {
        return SIP_CONTENT_TYPE_SIP_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_MWI,
                                sizeof(SIP_CONTENT_TYPE_MWI) - 1)) {
        return SIP_CONTENT_TYPE_MWI_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_MULTIPART_MIXED,
                                sizeof(SIP_CONTENT_TYPE_MULTIPART_MIXED) - 1)) {
        return SIP_CONTENT_TYPE_MULTIPART_MIXED_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE,
                                sizeof(SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE) - 1)) {
        return SIP_CONTENT_TYPE_MULTIPART_ALTERNATIVE_VALUE;
    }
    else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_DIALOG,
                              sizeof(SIP_CONTENT_TYPE_DIALOG) - 1)) {
        return SIP_CONTENT_TYPE_DIALOG_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_KPML_REQUEST,
                                sizeof(SIP_CONTENT_TYPE_KPML_REQUEST) - 1)) {
        return SIP_CONTENT_TYPE_KPML_REQUEST_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_KPML_RESPONSE,
                                sizeof(SIP_CONTENT_TYPE_KPML_RESPONSE) - 1)) {
        return SIP_CONTENT_TYPE_KPML_RESPONSE_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_REMOTECC_REQUEST,
                                sizeof(SIP_CONTENT_TYPE_REMOTECC_REQUEST) - 1)) {
        return SIP_CONTENT_TYPE_REMOTECC_REQUEST_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_CONFIGAPP,
                                sizeof(SIP_CONTENT_TYPE_CONFIGAPP) - 1)) {
        return SIP_CONTENT_TYPE_CONFIGAPP_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_REMOTECC_RESPONSE,
                                sizeof(SIP_CONTENT_TYPE_REMOTECC_RESPONSE) - 1)) {
        return SIP_CONTENT_TYPE_REMOTECC_RESPONSE_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_PRESENCE,
                                sizeof(SIP_CONTENT_TYPE_PRESENCE) - 1)) {
        return SIP_CONTENT_TYPE_PRESENCE_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_CMXML,
                                sizeof(SIP_CONTENT_TYPE_CMXML) - 1)) {
        return SIP_CONTENT_TYPE_CMXML_VALUE;
    } else if (!httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_CTI,
                                sizeof(SIP_CONTENT_TYPE_CTI) - 1)) {
        return SIP_CONTENT_TYPE_CTI_VALUE;
    }
    return SIP_CONTENT_TYPE_UNKNOWN_VALUE;
}

/******************************************************************
 * msg_process_one_body
 * This function will process one of the body parts of the whole body
 * Headers not understood will be ignored
 * - msg_start should be pointing at the first header of the body part
 * - msg_end at its last character
 ******************************************************************/
int
msg_process_one_body (httpishMsg_t *hmsg,
                      char *msg_start,
                      char *msg_end,
                      int current_body_part)
{
    static const char fname[] = "msg_process_one_body";
    pmhRstream_t *rstream = NULL;
    char         *content_type = NULL, *content_disp = NULL;
    char         *line = NULL, *body = NULL;
    char         *content_enc = NULL;
    char         *content_id = NULL;  int nbytes = 0;
    boolean       body_read = FALSE;

    // Adjust msg_start to point to the first valid character
    while (*msg_start == '\r' || *msg_start == '\n') {
        msg_start++;
    }

    // Convert this chunk of data into a more parse-able format
    rstream = pmhutils_rstream_create(msg_start, (uint32_t)(msg_end - msg_start));

    while (!body_read) {
        if (rstream) {
            line = pmhutils_rstream_read_line(rstream);
        }

        if (line) {
            // Look for the kind of line it is
            if (!cpr_strncasecmp(line, HTTPISH_HEADER_CONTENT_TYPE,
                                 sizeof(HTTPISH_HEADER_CONTENT_TYPE) - 1)) {
                // Its Content-Type, read in the value
                // XXX what if the line looks like "Content-Type-Garbage: some-value"?
                content_type = line + sizeof(HTTPISH_HEADER_CONTENT_TYPE);
                // XXX what if the line looks like "Content-Type :  some-value"?
                while (*content_type == ' ') {
                    content_type++;
                }
                hmsg->mesg_body[current_body_part].msgContentTypeValue = get_content_type_value(content_type);
                nbytes = strlen(content_type) + 1;
                hmsg->mesg_body[current_body_part].msgContentType =
                             (char *) cpr_malloc((nbytes)*sizeof(char));
                if (hmsg->mesg_body[current_body_part].msgContentType == NULL) {
                   CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc failed\n", fname);
                } else {
                    memcpy(hmsg->mesg_body[current_body_part].msgContentType,
                           content_type, nbytes);
                }
            } else if (!cpr_strncasecmp(line, HTTPISH_HEADER_CONTENT_ID,
                                        sizeof(HTTPISH_HEADER_CONTENT_ID) - 1)) {
                //Its Content-Id, read the value
                content_id = line + sizeof(HTTPISH_HEADER_CONTENT_ID);
                while(*content_id == ' ') {
                    content_id++;
                }
                nbytes = strlen(content_id) + 1;
                hmsg->mesg_body[current_body_part].msgContentId =
                             (char *) cpr_malloc((nbytes)*sizeof(char));
                if (hmsg->mesg_body[current_body_part].msgContentId == NULL) {
                   CCSIP_DEBUG_ERROR(SIP_F_PREFIX"malloc failed\n", fname);
                }
                memcpy(hmsg->mesg_body[current_body_part].msgContentId,
                          content_id, nbytes);
            } else if (!cpr_strncasecmp(line, SIP_HEADER_CONTENT_DISP,
                                        sizeof(SIP_HEADER_CONTENT_DISP) - 1)) {
                // Its Content-Disposition, read in the value
                content_disp = line + sizeof(SIP_HEADER_CONTENT_DISP);
                while (*content_disp == ' ') {
                    content_disp++;
                }
                if (!cpr_strncasecmp(content_disp, SIP_CONTENT_DISPOSITION_SESSION,
                                     sizeof(SIP_CONTENT_DISPOSITION_SESSION) - 1)) {
                    hmsg->mesg_body[current_body_part].msgContentDisp =
                        SIP_CONTENT_DISPOSITION_SESSION_VALUE;
                    content_disp += sizeof(SIP_CONTENT_DISPOSITION_SESSION) - 1;
                } else {
                    hmsg->mesg_body[current_body_part].msgContentDisp =
                        SIP_CONTENT_DISPOSITION_UNKNOWN_VALUE;
                    content_disp = strchr(line, ';');
                }
                if (content_disp && *content_disp == ';') {
                    content_disp++;
                    if (!cpr_strncasecmp(content_disp, "handling", 8)) {
                        content_disp += 9;
                        if (!cpr_strncasecmp(content_disp, "required", 8)) {
                            hmsg->mesg_body[current_body_part].msgRequiredHandling = TRUE;
                        } else if (!cpr_strncasecmp(content_disp, "optional", 8)) {
                            hmsg->mesg_body[current_body_part].msgRequiredHandling = FALSE;
                        }
                    }
                }
            } else if (!cpr_strncasecmp(line, HTTPISH_HEADER_CONTENT_ENCODING,
                                        sizeof(HTTPISH_HEADER_CONTENT_ENCODING) - 1)) {
                content_enc = line + sizeof(HTTPISH_HEADER_CONTENT_ENCODING);
                while (*content_enc == ' ') {
                    content_enc++;
                }
                if (!cpr_strcasecmp(content_enc, SIP_CONTENT_ENCODING_IDENTITY)) {
                    hmsg->mesg_body[current_body_part].msgContentEnc =
                        SIP_CONTENT_ENCODING_IDENTITY_VALUE;
                } else {
                    hmsg->mesg_body[current_body_part].msgContentEnc =
                        SIP_CONTENT_ENCODING_UNKNOWN_VALUE;
                }
            } else if (!(*line)) {
                body_read = TRUE;
                // The rest of the bytes are the body
                hmsg->mesg_body[current_body_part].msgLength =
                    rstream->nbytes - rstream->bytes_read;
                body = pmhutils_rstream_read_bytes(rstream, rstream->nbytes - rstream->bytes_read);
                if (body) {
                    hmsg->mesg_body[current_body_part].msgBody = body;
                }
            } else {
                // Unhandled header type
                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Unrecognized header in body\n", DEB_F_PREFIX_ARGS(HTTPISH, fname));
            }
            // Free the read line
            cpr_free(line);
            line = NULL;
        } else {
            body_read = TRUE;
        }
    }
    // Free the created stream structure
    pmhutils_rstream_delete(rstream, FALSE);
    cpr_free(rstream);
    return 0;
}

/******************************************************************
 * msg_process_multiple_bodies
 * This function will process multiple body parts of the whole body
 * boundary should point to the delimiter string, raw body points to
 * the beginning of the whole body structure in the message
 *
 * The algorithm here is to find the beginning and the end of each
 * body part and send it off for further header and body extraction
 ******************************************************************/
int
msg_process_multiple_bodies (httpishMsg_t *hmsg,
                             char *boundary,
                             char *raw_body)
{
    char    msg_delimit[MSG_DELIMIT_SIZE];
    int     i, body_part = 0;
    boolean end_of_proc = FALSE;
    char   *msg_start, *msg_end;

    // First copy the boundary in an array
    msg_delimit[0] = '-';
    msg_delimit[1] = '-';
    for (i = 0; boundary[i] != '\r' && boundary[i] != '\n' &&
         boundary[i] != ';' && boundary[i] != '\0'; i++) {
         if (i + 2 >= MSG_DELIMIT_SIZE) {
             return body_part;
         }
        msg_delimit[i + 2] = boundary[i];
    }
    msg_delimit[i + 2] = '\0';

    // Loop through the body segments
    while (!end_of_proc && body_part < HTTPISH_MAX_BODY_PARTS) {
        msg_start = strstr(raw_body, msg_delimit);
        if (msg_start) {
            msg_start += strlen(msg_delimit) + 1;
            msg_end = strstr(msg_start, msg_delimit);
            if (msg_end) {
                (void) msg_process_one_body(hmsg, msg_start, msg_end - 1,
                                            body_part);
            } else {
                // No boundary found for message end, return error
                end_of_proc = TRUE;
                continue;
            }
        } else {
            // No boundary found for message start, return error
            end_of_proc = TRUE;
            continue;
        }
        raw_body = msg_end;
        // Check if this is the last boundary
        if (*(raw_body + strlen(msg_delimit)) == '-') {
            end_of_proc = TRUE;
        }
        body_part++;
    }
    return body_part;
}

hStatus_t
httpish_msg_process_network_msg (httpishMsg_t *hmsg,
                                 char *nmsg,
                                 uint32_t *nbytes)
{
    static const char fname[] = "httpish_msg_process_network_msg";
    pmhRstream_t *rs = NULL;
    int32_t       bytes_remaining, delta;
    char         *mline;
    hStatus_t     retval;
    char         *raw_body = NULL;
    const char   *content_type = NULL;
    const char   *content_id = NULL;
    int32_t      contentid_len;
    int32_t      contenttype_len;

    if (!hmsg || !nmsg || (*nbytes <= 0)) {
        return HSTATUS_FAILURE;
    }

    if (hmsg->is_complete == TRUE) {
        *nbytes = 0;
        return HSTATUS_SUCCESS;
    }

    if ((rs = pmhutils_rstream_create(nmsg, *nbytes)) == NULL)
        return HSTATUS_FAILURE;

    /* Try to read message line */
    while (!hmsg->mesg_line) {
        mline = pmhutils_rstream_read_line(rs);
        if (!mline) {
            *nbytes = rs->bytes_read;
            if (rs->eof == TRUE) {
                retval = HSTATUS_SUCCESS;
                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Msg line read failure due to RS->EOF\n", fname);
            } else {
                retval = HSTATUS_FAILURE;
                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Msg line read failure\n", fname);
            }
            pmhutils_rstream_delete(rs, FALSE);
            cpr_free(rs);
            return retval;
        }
        if (!(*mline)) {
            cpr_free(mline);
        } else {
            hmsg->mesg_line = mline;
        }
    }

    /* There is a message line. We could have a header or a message body */
    while (!hmsg->headers_read) {
        char *this_header;

        this_header = pmhutils_rstream_read_line(rs);
        if (!this_header) {
            *nbytes = rs->bytes_read;
            if (rs->eof == TRUE) {
                retval = HSTATUS_SUCCESS;
                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Header line read failure due to RS->EOF\n", DEB_F_PREFIX_ARGS(HTTPISH, fname));
            } else {
                retval = HSTATUS_FAILURE;
                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Header line read failure\n", fname);
            }
            pmhutils_rstream_delete(rs, FALSE);
            cpr_free(rs);
            return retval;
        }
        if (!(*this_header)) {
            cpr_free(this_header);
            hmsg->headers_read = TRUE;
        } else {
            httpish_header *h;

            if (httpish_cache_header_val(hmsg, this_header) == -1) {
                /*
                 * For a non-cacheable header or error in caching routine,
                 * use the header linked list.
                 */
                h = (httpish_header *) cpr_malloc(sizeof(httpish_header));
                if (!h) {
                    *nbytes = rs->bytes_read;
                    pmhutils_rstream_delete(rs, FALSE);
                    cpr_free(rs);
                    cpr_free(this_header);
                    return HSTATUS_FAILURE;
                }

                h->next = NULL;
                h->header = this_header;
                enqueue(hmsg->headers, (void *)h);
            }

        }
    }

    /* Calculate the bytes remaining in the read stream */
    bytes_remaining = rs->nbytes - rs->bytes_read;

    /* Now get the content length header value */
    hmsg->content_length = get_content_length(hmsg);
    if (hmsg->content_length == -1) {
        /* Bad or missing content-length header
         * For UDP, assume remaining msg is message body.
         * For TCP, message is not complete without content-length header
         * but we will ignore this possibility for now (assume content-length will always be there)
         * since we don't know if we have a complete message or a fragmented one
         */
        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Content-Length header not received\n", fname);
        hmsg->content_length = bytes_remaining;
    }

    delta = bytes_remaining - hmsg->content_length;
    if (delta) {
        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX "Content Length %d, Bytes Remaining %d.\n", DEB_F_PREFIX_ARGS(HTTPISH, fname),
                            hmsg->content_length, bytes_remaining);
    }
    if (delta < 0) {
        /* We have fewer bytes than specified by Content-Length header */
        hmsg->content_length = bytes_remaining;
        hmsg->is_complete = FALSE;
        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Partial body received\n", DEB_F_PREFIX_ARGS(HTTPISH, fname));
    } else {
        hmsg->is_complete = TRUE;
    }

    if (hmsg->content_length > 0) {
        raw_body = pmhutils_rstream_read_bytes(rs, hmsg->content_length);
        if (!raw_body) {
            pmhutils_rstream_delete(rs, FALSE);
            cpr_free(rs);
            return HSTATUS_FAILURE;
        }
    }

    // If message is not complete we should not parse the message any more
    if (!hmsg->is_complete) {
        *nbytes = rs->bytes_read;
        pmhutils_rstream_delete(rs, FALSE);
        cpr_free(rs);
        UTILFREE(raw_body);
        return HSTATUS_SUCCESS;
    }

    // Figure out whether more parsing of the received body is necessary
    content_type = httpish_msg_get_cached_header_val(hmsg, CONTENT_TYPE);
    if (content_type && (hmsg->content_length > 0)) {
        if (!cpr_strncasecmp(content_type, "multipart/mixed", 15)) {

            char *boundary = NULL;
            int num_bodies = 0;

            // find the boundary tag
            boundary = strchr(content_type, '=');
            if (boundary) {
                boundary++;
                if (raw_body) {
                    num_bodies = msg_process_multiple_bodies(hmsg, boundary, raw_body);
                }

                if (num_bodies == 0) {
                    CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in decoding multipart messages\n", fname);
                } else {
                    hmsg->num_body_parts = (uint8_t) num_bodies;
                }
            } else {
                // No boundary specified!
                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error in decoding multipart messages: No body delimiter\n", fname);
            }
            hmsg->raw_body = raw_body;

        } else {
            // All of the body is of a single type
            // hmsg->mesg_body[0].msgBody = raw_body;
            hmsg->mesg_body[0].msgBody = (char *)
                cpr_malloc(hmsg->content_length + 1);
            if (hmsg->mesg_body[0].msgBody == NULL) {
                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to get memory\n", fname);
                pmhutils_rstream_delete(rs, FALSE);
                cpr_free(rs);
                cpr_free(raw_body);
                return HSTATUS_FAILURE;
            }

            if (raw_body) {
                memcpy(hmsg->mesg_body[0].msgBody, raw_body,
                       hmsg->content_length + 1);
            }

            content_id = httpish_msg_get_header_val(hmsg, HTTPISH_HEADER_CONTENT_ID, NULL);
            if (content_id) {
                contentid_len = strlen(content_id) + 1;
                hmsg->mesg_body[0].msgContentId = (char *)
                       cpr_malloc(contentid_len * sizeof(char));
                if (hmsg->mesg_body[0].msgContentId == NULL) {
                   CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to get memory\n", fname);
                    pmhutils_rstream_delete(rs, FALSE);
                    cpr_free(rs);
                    cpr_free(raw_body);
                    return HSTATUS_FAILURE;
                }
	        memcpy(hmsg->mesg_body[0].msgContentId,
			content_id, contentid_len);
            }

            hmsg->mesg_body[0].msgContentTypeValue = get_content_type_value(content_type);
            contenttype_len = strlen(content_type) + 1;
            hmsg->mesg_body[0].msgContentType = (char *)
                   cpr_malloc(contenttype_len * sizeof(char));
            if (hmsg->mesg_body[0].msgContentType == NULL) {
                CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to get memory\n", fname);
                pmhutils_rstream_delete(rs, FALSE);
                cpr_free(rs);
                cpr_free(raw_body);
                return HSTATUS_FAILURE;
            }
            memcpy(hmsg->mesg_body[0].msgContentType,
                   content_type, contenttype_len);

            hmsg->mesg_body[0].msgContentDisp =
                SIP_CONTENT_DISPOSITION_SESSION_VALUE;
            hmsg->mesg_body[0].msgRequiredHandling = TRUE;
            hmsg->mesg_body[0].msgLength = hmsg->content_length;
            hmsg->num_body_parts = 1;
            hmsg->raw_body = raw_body;
        }
    } else if (hmsg->content_length > 0) {
        // No content-type specified but there is a body present. Treat this
        // as an error
        hmsg->is_complete = FALSE;
        hmsg->content_length = -1;
        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Body found without content-type\n", fname);
        UTILFREE(raw_body);
        pmhutils_rstream_delete(rs, FALSE);
        cpr_free(rs);
        return HSTATUS_SUCCESS;
    }

    *nbytes = rs->bytes_read;
    pmhutils_rstream_delete(rs, FALSE);
    cpr_free(rs);
    return HSTATUS_SUCCESS;
}


hStatus_t
httpish_msg_add_body (httpishMsg_t *msg,
                      char *body,
                      uint32_t nbytes,
                      const char *content_type,
                      uint8_t msg_disposition,
                      boolean required,
                      char *content_id)
{
    static const char fname[] = "httpish_msg_add_body";
    uint8_t current_body_part;
    uint32_t contenttype_len;

    if (!msg || !body || (nbytes == 0)) {
        return HSTATUS_FAILURE;
    }

    if (msg->num_body_parts == HTTPISH_MAX_BODY_PARTS) {
        return HSTATUS_FAILURE;
    }

    // Add body at the next available index
    current_body_part = msg->num_body_parts;

    contenttype_len = strlen(content_type) + 1;
    msg->mesg_body[current_body_part].msgContentType = (char *)
            cpr_malloc(contenttype_len * sizeof(char));
    if (msg->mesg_body[current_body_part].msgContentType == NULL) {
        CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Unable to get memory\n", fname);
        return HSTATUS_FAILURE;
    }

    msg->mesg_body[current_body_part].msgBody = body;
    memcpy(msg->mesg_body[current_body_part].msgContentType,
           content_type, contenttype_len);
    msg->mesg_body[current_body_part].msgContentTypeValue = get_content_type_value(content_type);
    msg->mesg_body[current_body_part].msgContentDisp = msg_disposition;
    msg->mesg_body[current_body_part].msgRequiredHandling = required;
    msg->mesg_body[current_body_part].msgLength = nbytes;
    msg->mesg_body[current_body_part].msgContentId = content_id;

    msg->num_body_parts++;

    return HSTATUS_SUCCESS;
}


httpishStatusCodeClass_t
httpish_msg_get_code_class (uint16_t statusCode)
{
    httpishStatusCodeClass_t retval;
    int fc = statusCode / 100;

    switch (fc) {
    case 1:
        retval = codeClass1xx;
        break;
    case 2:
        retval = codeClass2xx;
        break;
    case 3:
        retval = codeClass3xx;
        break;
    case 4:
        retval = codeClass4xx;
        break;
    case 5:
        retval = codeClass5xx;
        break;
    case 6:
        retval = codeClass6xx;
        break;
    default:
        retval = codeClassInvalid;
        break;
    }

    return retval;
}


uint16_t
httpish_msg_get_num_particular_headers (httpishMsg_t *msg,
                                        const char *hname,
                                        const char *c_hname,
                                        char *header_val[],
                                        uint16_t max_headers)
{
    nexthelper *p;
    char       *this_line = NULL;
    uint16_t    found = 0;

    if (!msg || !hname) {
        return 0;
    }

    p = (nexthelper *) msg->headers->qhead;

    while (p && found < max_headers) {
        this_line = ((httpish_header *)p)->header;
        if (this_line) {
            /* Remove leading spaces */
            while ((*this_line == ' ') && (*this_line != '\0'))
                this_line++;
            if ((strlen(this_line) > strlen(hname) + 1) &&
                (cpr_strncasecmp(this_line, hname, strlen(hname)) == 0 ||
                 compact_hdr_cmp(this_line, c_hname) == 0)) {
                this_line = strchr(this_line, ':');
                if (this_line) {
                    this_line++;
                    /* Remove leading spaces */
                    while ((*this_line == ' ') && (*this_line != '\0'))
                        this_line++;
                    if (*this_line == '\0') {
                        p = p->next;
                        continue;
                    } else {
                        header_val[found] = this_line;
                        found++;
                    }
                }
            }
        }
        p = p->next;
    }
    return found;
}


