/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */

#include "rtsp_par_com.h"
#include "oscl_string_utils.h"
#include "rtsp_range_utils.h"

OSCL_EXPORT_REF void
RTSPIncomingMessage::reset()
{
    RTSPGenericMessage::reset();

    playlistRangeField.setPtrLen("", 0);
    playlistRangeFieldIsSet = false;
    // need to figure out how to store all the error fields.. we may not actually need to
    playlistErrorFieldIsSet = false;
    playlistErrorFieldCount = 0;
    for (int ii = 0; ii < RTSP_MAX_NUMBER_OF_PLAYLIST_ERROR_ENTRIES; ++ii)
    {
        playlistErrorField[ii].setPtrLen("", 0);
    }

    supportedFieldIsSet = false;
    numOfSupportedEntries = 0;
    for (int jj = 0; jj < RTSP_MAX_NUMBER_OF_SUPPORTED_ENTRIES; ++jj)
    {
        supportedField[jj].setPtrLen("", 0);
    }

    playlistRangeUrl.setPtrLen("", 0);
    playlistRangeClipIndex = 0;
    playlistRangeClipOffset = 0;
    playlistRangeNptTime = 0;

    amMalformed = RTSPOk;
    rtspVersionString = "";
}

void
RTSPIncomingMessage::parseFirstFields()
{

    char * endOfString;

    // The first thing to do is to parse the status line;
    // the rest of the parsing (regular fields) can be parsed out by
    // parseNextPortion() method, since it has to know how to do that anyway.
    // We also have to find out if Content-length is there, etc.

    // so, let's set up the pointers first
    //
    secondaryBufferSpace = secondaryBuffer;
    if (CHAR_CR == *secondaryBufferSpace)
    {
        ++secondaryBufferSpace;
    }
    if (CHAR_LF == *secondaryBufferSpace)
    {
        ++secondaryBufferSpace;
    }

    if (secondaryBufferSizeUsed >= 4)
    {
        if (('H' != secondaryBufferSpace[0])
                || ('T' != secondaryBufferSpace[1])
                || ('T' != secondaryBufferSpace[2])
                || ('P' != secondaryBufferSpace[3])
           )
        {
            if ('H' == secondaryBufferSpace[0])
            {//HTTP_RESPONSE
                uint32 nOptionLen = secondaryBufferSpace[1];
                secondaryBufferSpace += (2 + nOptionLen);
            }
            /* TBD
            else if('r' == secondaryBufferSpace[0])
            {//HTTP_RV_RESPONSE
            }
            */
        }
    }

    numPtrFields = 0;

    char * wordPtr[3] = { NULL, NULL, NULL };
    char * endOfLine;
    int   wordCount;
    bool  wordCountTrigger;

    for (endOfLine = secondaryBufferSpace, wordCount = 0, wordCountTrigger = true;
            (endOfLine < secondaryBufferSpace + secondaryBufferSizeUsed)
            && (CHAR_CR != *endOfLine)
            && (CHAR_LF != *endOfLine);
            ++endOfLine
        )
    {
        switch (wordCountTrigger)
        {
            case true:  // if non-space, then it's a beginning of a new word
            {
                if ((*endOfLine >= 0x09 && *endOfLine <= 0x0D) || *endOfLine == 0x20)
                { // spaces
                }
                else
                { // word
                    if (3 > wordCount)
                    {
                        wordPtr[wordCount] = endOfLine;
                    }
                    ++wordCount;

                    // uncock the trigger
                    wordCountTrigger = false;
                }

                break;
            }

            case false: // if space, then cock the trigger
            {
                if ((*endOfLine >= 0x09 && *endOfLine <= 0x0D) || *endOfLine == 0x20)
                { // yeah, space
                    wordCountTrigger = true;
                }
                else
                { // still a word
                }
                break;
            }

            default:
                // ERROR: internal inconsistency, a switch on a boolean should not hit
                // this point
                ;;
        }
    }

    if (3 > wordCount)
    { // only two words in the status line - error

        amMalformed = RTSPErrorSyntax;
        return;
    }

    if (CHAR_CR == *endOfLine)
    {
        *(endOfLine++) = CHAR_NULL;
        if (CHAR_LF == *endOfLine)
        {
            *(endOfLine++) = CHAR_NULL;
        }
    }
    else if (CHAR_LF == *endOfLine)
    {
        *(endOfLine++) = CHAR_NULL;
    }
    else
    {
        // ran to the end of input, didn't find a single newline....
        // most probably the message was too big to handle, but parser still
        // created a message hoping that at least something can be found...
        //

        amMalformed = RTSPErrorSyntax;

        return;
    }



    // figure out the format of the message - request or response?
    //
    // make out the first word... don't forget about '$'
    //

////  endOfString = secondaryBufferSpace;
////  while( ! isspace(*endOfString) )
////    ++endOfString;

    endOfString = wordPtr[1];
    while (--endOfString >= secondaryBufferSpace
            && ((*endOfString >= 0x09 && *endOfString <= 0x0D) || *endOfString == 0x20)
          )
    {
        *endOfString = CHAR_NULL;
    }

////  *endOfString = CHAR_NULL;
////  StrCSumPtrLen firstWordPLSS = secondaryBufferSpace;
    StrCSumPtrLen firstWordPLSS = wordPtr[0];

    if (('R' == *(wordPtr[0]))
            && ('T' == *(wordPtr[0] + 1))
            && ('S' == *(wordPtr[0] + 2))
            && ('P' == *(wordPtr[0] + 3))
            && ('/' == *(wordPtr[0] + 4))
       )
    {
        // it has to be a response
        //
        msgType = RTSPResponseMsg;
        rtspVersionString = firstWordPLSS;

        // if there is a mismatch, let the Engine know about it and stop parsing
        if (rtspVersionString != RTSPVersionString)
        {
            amMalformed = RTSPErrorVersion;
            return;
        }

        // get the code
////      secondaryBufferSpace = endOfString+1;
////     while( isspace(*secondaryBufferSpace) )
////      {
////          ++secondaryBufferSpace;
////        }

////      statusCode = atoi( secondaryBufferSpace );
        uint32 atoi_tmp;
        PV_atoi(wordPtr[1], 'd', atoi_tmp);
        statusCode = (RTSPStatusCode)atoi_tmp;

        // get the reason string

////      while( ! isspace( *secondaryBufferSpace ) )
////        {
////          ++secondaryBufferSpace;
////        }

////      while( isspace( *secondaryBufferSpace ) )
////        {
////          ++secondaryBufferSpace;
////        }

////      endOfString = secondaryBufferSpace;
////      while(            (CHAR_CR != *endOfString)
////                                &&  (CHAR_LF != *endOfString)
////                                )
////        {
////          ++endOfString;
////        }

////      *endOfString = CHAR_NULL;
////      reasonString = secondaryBufferSpace;
        reasonString = wordPtr[2];

        // now, resync to the next line
////      secondaryBufferSpace = endOfString+1;
////      if( CHAR_LF == *secondaryBufferSpace )
////        {
////          ++secondaryBufferSpace;
////        }

        secondaryBufferSpace = endOfLine;
    }
    else if (('H' == *(wordPtr[0]))
             && ('T' == *(wordPtr[0] + 1))
             && ('T' == *(wordPtr[0] + 2))
             && ('P' == *(wordPtr[0] + 3))
             && ('/' == *(wordPtr[0] + 4))
            )
    {
        msgType = RTSPResponseMsg;
        rtspVersionString = firstWordPLSS;

        // if there is a mismatch, let the Engine know about it and stop parsing
        if ((rtspVersionString != HTTPVersion_1_0_String)
                && (rtspVersionString != HTTPVersion_1_1_String))
        {
            amMalformed = RTSPErrorVersion;
            return;
        }
        uint32 atoi_tmp;
        PV_atoi(wordPtr[1], 'd', atoi_tmp);
        statusCode = (RTSPStatusCode)atoi_tmp;
        reasonString = wordPtr[2];
        secondaryBufferSpace = endOfLine;
    }
    else
    {   // okay, it could be a request

        // but for requests there must be exactly three words on the
        // status line
        if (3 != wordCount)
        {
            amMalformed = RTSPErrorSyntax;
            return;
        }

        msgType = RTSPRequestMsg;

        methodString = firstWordPLSS;

        if (firstWordPLSS == RtspRequestMethodStringDescribe)
        {
            method = METHOD_DESCRIBE;
        }
        else if (firstWordPLSS == RtspRequestMethodStringGetParameter)
        {
            method = METHOD_GET_PARAMETER;
        }
        else if (firstWordPLSS == RtspRequestMethodStringOptions)
        {
            method = METHOD_OPTIONS;
        }
        else if (firstWordPLSS == RtspRequestMethodStringPause)
        {
            method = METHOD_PAUSE;
        }
        else if (firstWordPLSS == RtspRequestMethodStringPlay)
        {
            method = METHOD_PLAY;
        }
        else if (firstWordPLSS == RtspRequestMethodStringSetup)
        {
            method = METHOD_SETUP;
        }
        else if (firstWordPLSS == RtspRequestMethodStringRecord)
        {
            method = METHOD_RECORD;
        }
        else if (firstWordPLSS == RtspRequestMethodStringTeardown)
        {
            method = METHOD_TEARDOWN;
        }
        else if (firstWordPLSS == RtspRequestMethodStringEndOfStream)
        {
            method = METHOD_END_OF_STREAM;
        }
        /* TBD: add Announce, SetParameter and Redirect later
        else if( firstWordPLSS == RtspRequestMethodStringAnnounce )
          {
            method = METHOD_ANNOUNCE;
          }
        */
        else if (firstWordPLSS == RtspRequestMethodStringSetParameter)
        {
            method = METHOD_SET_PARAMETER;
        }

        else if (firstWordPLSS == RtspRequestMethodStringRedirect)  //SS
        {
            method = METHOD_REDIRECT; ////SS
        }
        // now, we could put binary data here, but it's not exactly the same
        // thing; since it requires special care, let's not confuse things by
        // putting generic-looking stuff here
        else
        {
            method = METHOD_UNRECOGNIZED;
        }

        // get the URI

////      secondaryBufferSpace = endOfString +1;
////      while( isspace( *secondaryBufferSpace ) )
////        {
////          ++secondaryBufferSpace;
////        }

////      endOfString = secondaryBufferSpace;
////      while( ! isspace( *endOfString ) )
////        {
////          ++endOfString;
////        }
////      *endOfString = CHAR_NULL;

////      originalURI = secondaryBufferSpace;
        endOfString = wordPtr[2];
        while ((--endOfString) >= wordPtr[1]
                && ((*endOfString >= 0x09 && *endOfString <= 0x0D) || *endOfString == 0x20)
              )
        {
            *endOfString = CHAR_NULL;
        }
        originalURI = wordPtr[1];

        // get the RTSP version
////      secondaryBufferSpace = endOfString +1;
////      while( isspace( *secondaryBufferSpace ) )
////        {
////          ++secondaryBufferSpace;
////        }
////      endOfString = secondaryBufferSpace;
////      while( !isspace(*endOfString) )
////        {
////          ++endOfString;
////        }

////      *endOfString = CHAR_NULL;
////      rtspVersionString = secondaryBufferSpace;
        endOfString = endOfLine;
        while (--endOfString >= wordPtr[2]
                && (((*endOfString >= 0x09 && *endOfString <= 0x0D) || *endOfString == 0x20)
                    ||  CHAR_NULL == *endOfString
                   )
              )
        {
            *endOfString = CHAR_NULL;
        }
        rtspVersionString = wordPtr[2];

        // if there is a mismatch, let the Engine know about it and stop parsing
        if (rtspVersionString != RTSPVersionString)
        {
            amMalformed = RTSPErrorVersion;
            return;
        }

        // now, resync to the next line
////      secondaryBufferSpace = endOfString + 1;
////      if( CHAR_LF == *secondaryBufferSpace )
////        {
////          ++secondaryBufferSpace;
////        }
        secondaryBufferSpace = endOfLine;

        // extra URI validation
        {
            if (1 == originalURI.length())
            { // Is it a star
                if (CHAR_STAR != originalURI.c_str()[0])
                {
                    amMalformed = RTSPErrorSyntax;
                    return;
                }
            }
            else
            { // it's a normal URI

                // let's validate the scheme
                char colonStr[2];
                colonStr[0] = CHAR_COLON;
                colonStr[1] = NULL_TERM_CHAR;
                const char * colonPtr = oscl_strstr(originalURI.c_str(), colonStr);
                if (NULL == colonPtr)
                {
                    // no colon
                    amMalformed = RTSPErrorSyntax;
                    return;
                }

                // now, the first character of the scheme is a character
                const char * ptr = originalURI.c_str();
                if (!((*ptr >= 'A' && *ptr <= 'Z') || (*ptr >= 'a' && *ptr <= 'z')))

                {
                    // the URI doesn't start with a character
                    amMalformed = RTSPErrorSyntax;
                    return;
                }

                // the rest of the characters must be either characters, or
                // plus/minus, or dots...
                for (++ptr; ptr < colonPtr; ++ptr)
                {
                    if (!(((*ptr >= 'A' && *ptr <= 'Z') || (*ptr >= 'a' && *ptr <= 'z'))
                            || (*ptr >= '0' || *ptr <= '9')
                            ||  CHAR_PLUS == *ptr
                            ||  CHAR_MINUS == *ptr
                            ||  CHAR_DOT == *ptr
                         ))
                    {
                        amMalformed = RTSPErrorSyntax;
                        return;
                    }
                }
            }
        }

        // finish off the URI processing

        {
            // look for the base
            uint32  ii;
            int32   ee;
            uint32 length = originalURI.length();

            for (ii = 0; ii < length; ++ii)
            {
                if (CHAR_SLASH == originalURI.c_str()[ii])
                {
                    if (CHAR_SLASH != originalURI.c_str()[++ii])
                    {
                        break;
                    }
                    else
                    { // no, it's a separator, skip over it, continue
                    }
                }
            }
            originalURIBase = originalURI.c_str() + ii;

            // look for the control candidate
            for (ee = originalURIBase.length() - 1;
                    ee >= 0;
                    --ee
                )
            {
                if ((CHAR_SLASH      ==  originalURIBase.c_str()[ee])
                        || (CHAR_SEMICOLON  ==  originalURIBase.c_str()[ee])
                   )
                {
                    break;
                }
            }
            originalURIControlCandidate = originalURIBase.c_str() + ee + 1;

        }
    }


    // determine the total number of fields remaining to be seen

    endOfString = secondaryBuffer + secondaryBufferSizeUsed;
    *(--endOfString) = CHAR_NULL;
    --secondaryBufferSizeUsed;
    if (CHAR_CR == *(endOfString - 1))
    {
        *(--endOfString) = CHAR_NULL;
        --secondaryBufferSizeUsed;
    }


    totalFields = 0;
    for (char * ptr = secondaryBufferSpace; ptr < endOfString; ++ptr)
    {
        if (CHAR_LF == *ptr)
        {
            ++totalFields;
        }
        else if (CHAR_CR == *ptr)
        {
            if (CHAR_LF != *(ptr + 1))
            {
                ++totalFields;
            }
        }
    }

    totalFieldsParsed = 0;
    parseNextPortion();

    if (getTotalFields() != getTotalFieldsParsed())
    {
        amMalformed = RTSPErrorTooManyFields;
    }
}

bool
RTSPIncomingMessage::parseNextPortion()
{
    if (totalFieldsParsed == totalFields)
    {
        return false;
    }

    char * endOfMessage = secondaryBuffer + secondaryBufferSizeUsed;
    char * ptr = secondaryBufferSpace;

    //  printf ("parcom::parseNextPortion: secondaryBuffer is \n %s \n",secondaryBuffer);


    for (numPtrFields = 0;
            (numPtrFields < RTSP_MAX_NUMBER_OF_FIELDS) && (ptr < endOfMessage);
            ++numPtrFields)
    {
        char *endOfValue = ptr;
        while (CHAR_LF != *(endOfValue)
                &&  CHAR_CR != *(endOfValue)
                &&  CHAR_NULL != *(endOfValue))
        {
            ++endOfValue;
        }

        if (CHAR_CR == *(endOfValue) && CHAR_LF == *(endOfValue + 1))
        {
            // need to increment endOfValue for CR-LF, because
            // it is needed later to step into the next field by shifting by 1
            *(endOfValue) = CHAR_NULL;
            *(++endOfValue) = CHAR_NULL;
        }
        else
        {
            *endOfValue = CHAR_NULL;
        }

        char * separator = ptr;

        while ((CHAR_COLON != *separator)   && (CHAR_NULL != *separator))
        {
            ++separator;
        }

        if (CHAR_COLON != *separator)
        {
            //amMalformed = RTSPErrorSyntax;
            //ignore the unknown lines
            ptr = endOfValue + 1;
            continue;
        }
        else
        {
            *separator = CHAR_NULL;

            // get name pointer
            char * namePtr = ptr;
            // eat through trailing whitespace
            for (char * wPtr1 = separator - 1;
                    (wPtr1 >= namePtr) && ((*wPtr1 >= 0x09 && *wPtr1 <= 0x0D) || *wPtr1 == 0x20);
                    --wPtr1)
            {
                *wPtr1 = CHAR_NULL;
            }
            // eat through leading whitespace
            while ((*namePtr >= 0x09 && *namePtr <= 0x0D) || *namePtr == 0x20)
            {
                ++namePtr;
            }

            // get value pointer
            char * valuePtr = separator + 1;
            // eat through trailing whitespace
            for (char * wPtr2 = endOfValue - 1;
                    (wPtr2 > separator)
                    && ((*wPtr2 >= 0x09 && *wPtr2 <= 0x0D) || *wPtr2 == 0x20);
                    --wPtr2)
            {
                *wPtr2 = CHAR_NULL;
            }
            //eat through leading whitespace
            while ((*valuePtr >= 0x09 && *valuePtr <= 0x0D) || *valuePtr == 0x20)
            {
                ++valuePtr;
            }

            // all right, set the pointers

            fieldKeys[ numPtrFields ] = namePtr;
            fieldVals[ numPtrFields ] = valuePtr;

            // determine if we are supposed to recognize this
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldSessionId))
            {
                {
                    StrPtrLen tmp = fieldVals[ numPtrFields ];
                    int Len = tmp.length();
                    char * beginPtr = (char*)tmp.c_str();
                    char * endPtr = beginPtr + Len;

                    while (beginPtr < endPtr)
                    {
                        if (*beginPtr == ';')
                        {//we got timeout field
                            while (beginPtr < endPtr)
                            {
                                if ((*beginPtr == 't') && (beginPtr[6] == 't'))
                                {//a little bit validation
                                    beginPtr += 8;
                                    PV_atoi(beginPtr, 'd', timeout);
                                    beginPtr = endPtr;
                                }
                                beginPtr++;
                            }
                        }
                        beginPtr++;
                    }
                }
                sessionId = fieldVals[ numPtrFields ];
                sessionIdIsSet = true;
            }
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldCSeq))
            {
                PV_atoi(valuePtr, 'd', cseq);
                cseqIsSet = true;
            }
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldBufferSize
                    ))
            {
                PV_atoi(valuePtr, 'd', bufferSize);
                bufferSizeIsSet = true;
            }
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldContentType))
            {
                contentType = fieldVals[ numPtrFields ];
                contentTypeIsSet = true;
            }
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldContentBase))
            {
                contentBase = fieldVals[ numPtrFields ];
                contentBaseMode = CONTENT_BASE_SET;
            }
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldContentLength))
            {
                PV_atoi(valuePtr, 'd', contentLength);
                contentLengthIsSet = true;
            }
            if (fieldKeys[ numPtrFields].isCIEquivalentTo(
                        RtspRecognizedFieldUserAgent))
            {
                userAgent = fieldVals[ numPtrFields ];
                userAgentIsSet = true;
            }
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldAccept))
            {
                accept = fieldVals[ numPtrFields ];
                acceptIsSet = true;
            }
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldRequire))
            {
                require = fieldVals[ numPtrFields ];
                requireIsSet = true;
            }
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldRTPInfo
                    ))
            {
                parseRTPInfo(numPtrFields);
            }

            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldRange))
            {
                parseRtspRange(fieldVals[ numPtrFields].c_str(), fieldVals[ numPtrFields].length(), range);
                rangeIsSet = true;
            }
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldTransport))
            {
                parseTransport(numPtrFields);
            }
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldSupported))
            {
                parseSupported(fieldVals[ numPtrFields].c_str(), fieldVals[ numPtrFields].length() + 1);
                supportedFieldIsSet = true;
            }
        }

        ptr = endOfValue + 1;
    }

    totalFieldsParsed += numPtrFields;

    secondaryBufferSpace = ptr;

    return true;
}

void
RTSPIncomingMessage::parseSupported(const char *supportedString, int length)
{
    const StrPtrLen methodEosString("method.eos");
    const StrPtrLen comPvPlaylistString("com.pv.server_playlist");

    const char *end = supportedString + length;

    char *sptr;//, *eptr;

    sptr = (char*)supportedString;

    while ((sptr < end) && (numOfSupportedEntries < RTSP_MAX_NUMBER_OF_SUPPORTED_ENTRIES))
    {
        char * separator = sptr;

        while ((CHAR_COMMA != *separator)
                && (CHAR_NULL != *separator)
                && (separator < end)
              )
        {
            ++separator;
        }

        if (CHAR_COMMA == *separator)
        {
            // make sure there is a terminating char
            // in the other case, there will already be one because its the end of the line
            // and the field parsing that called this function put one there
            *separator = CHAR_NULL;
        }

        // get name pointer
        char * namePtr = sptr;
        // eat through trailing whitespace
        for (char * wPtr1 = separator - 1;
                (wPtr1 >= namePtr)
                && ((*wPtr1 >= 0x09 && *wPtr1 <= 0x0D) || *wPtr1 == 0x20);
                --wPtr1)
        {
            *wPtr1 = CHAR_NULL;
        }
        // eat through leading whitespace
        while ((*namePtr >= 0x09 && *namePtr <= 0x0D) || *namePtr == 0x20)
        {
            ++namePtr;
        }

        supportedField[numOfSupportedEntries++] = namePtr;

        if (!oscl_strncmp(namePtr, methodEosString.c_str(), methodEosString.length()))
        {
            methodEosIsSet = true;
        }
        else if (!oscl_strncmp(namePtr, comPvPlaylistString.c_str(), comPvPlaylistString.length()))
        {
            comPvServerPlaylistIsSet = true;
        }

        sptr = separator + 1;

    }  // end while
}



// relevant constants
const int MIN_CHANNEL_VALUE = 0;
const int MAX_CHANNEL_VALUE = 255;
const int PORT_MIN_VALUE = 0;
const int PORT_MAX_VALUE = 65535;

#define interleaved_str "interleaved="
#define interleaved_str_len 12

#define client_port_str "client_port="
#define client_port_str_len 12

#define server_port_str "server_port="
#define server_port_str_len 12

#define ttl_str "ttl="
#define ttl_str_len 4

#define mode_str "mode="
#define mode_str_len 5

#define port_str "port="
#define port_str_len 5

#define layers_str "layers="
#define layers_str_len 7

#define ssrc_str "ssrc="
#define ssrc_str_len 5

#define destination_str "destination"
#define destination_str_len 11

void
RTSPIncomingMessage::parseTransport(uint16 fieldIdx)
{
    char * cPtr;
    char * finishPtr;

    cPtr = const_cast<char*>(fieldVals[ fieldIdx ].c_str());
    finishPtr = cPtr + fieldVals[ fieldIdx ].length();

    do
    {
        parseOneTransportEntry(cPtr, finishPtr);

    }
    while (RTSPOk == amMalformed
            &&  cPtr < finishPtr
          );
}


void
RTSPIncomingMessage::parseOneTransportEntry(char*& trans, char *final_end)
{
    const char *startPtr, *endPtr, *nxtPtr;
    char *transSepPtr;



    // check the counter
    //
    if (RTSP_MAX_NUMBER_OF_TRANSPORT_ENTRIES == numOfTransportEntries)
    { // limit reached
        amMalformed = RTSPErrorSyntax;  // should it be different?
        return;
    }

    RtspTransport* rtspTrans = transport + numOfTransportEntries;
    numOfTransportEntries++;

    startPtr = trans;

    if (*startPtr == ',')
    {
        // skip over any starting commas
        ++startPtr;
    }

    rtspTrans->appendIsSet = false;
    rtspTrans->channelIsSet = false;
    rtspTrans->client_portIsSet = false;
    rtspTrans->deliveryIsSet = false;
    rtspTrans->layersIsSet = false;
    rtspTrans->modeIsSet = false;
    rtspTrans->portIsSet = false;
    rtspTrans->profileIsSet = false;
    rtspTrans->protocolIsSet = false;
    rtspTrans->transportTypeIsSet = false;
    rtspTrans->server_portIsSet = false;
    rtspTrans->ttlIsSet = false;
    rtspTrans->destinationIsSet = false;



    // see if there is a tranport list separator
    transSepPtr = OSCL_CONST_CAST(char*, oscl_strstr(startPtr, ","));
    if (transSepPtr)
    {

        const char *quotePtr = oscl_strstr(startPtr, "\"");
        if (quotePtr && quotePtr < transSepPtr)
        {
            // this may be a comma in the mode list -- so find the end of the mode list
            const char *quote_end = oscl_strstr(quotePtr + 1, "\"");
            if (quote_end)
            {
                // look for another comma
                if (NULL != (transSepPtr = OSCL_CONST_CAST(char*, oscl_strstr(quote_end, ","))))
                {
                    // temporarily write a terminator to separate the transport specs.
                    *transSepPtr = '\0';
                }
            }
        }
        else
        {
            // temporarily write a terminator to separate the transport specs.
            *transSepPtr = '\0';
        }
    }


//  for (; startPtr < final_end && isspace(*startPtr); ++startPtr);
    for (; startPtr < final_end && ((*startPtr >= 0x09 && *startPtr <= 0x0D) || *startPtr == 0x20); ++startPtr);

    do
    {
        if (NULL == (endPtr = oscl_strstr(startPtr, "/")))
        {
            endPtr = final_end;
        }

        rtspTrans->protocolIsSet = true;

        if (!oscl_strncmp(startPtr, "RTP", endPtr - startPtr))
        {
            rtspTrans->protocol = RtspTransport::RTP_PROTOCOL;
        }
        else if (!oscl_strncmp(startPtr, "x-pn-tng", endPtr - startPtr))
        {
            rtspTrans->protocol = RtspTransport::RDT_PROTOCOL;
        }
        else
        {
            rtspTrans->protocol = RtspTransport::UNKNOWN_PROTOCOL;
        }

        if (endPtr == final_end)
        {
            break;
        }

        // increment the startPtr past the separator
        startPtr = endPtr + 1;

        // look for either the '/' or ';' separators
        endPtr = oscl_strstr(startPtr, "/");
        const char *endPtr2 = oscl_strstr(startPtr, ";");

        if (!endPtr)
        {
            endPtr = endPtr2;
        }

        if (endPtr2 && endPtr2 < endPtr)
        {
            endPtr = endPtr2;
        }

        if (!endPtr)
        {
            endPtr = final_end;
        }

        rtspTrans->profile = RtspTransport::UNKNOWN_PROFILE;
        rtspTrans->profileIsSet = true;
        if (!oscl_strncmp(startPtr, "AVP", endPtr - startPtr))
        {
            rtspTrans->profile = RtspTransport::AVP_PROFILE;
        }
        else if (!oscl_strncmp(startPtr, "tcp", endPtr - startPtr))
        {//RDT "x-pn-tng/tcp"
            rtspTrans->profile = RtspTransport::TCP_PROFILE;
        }

        if (*endPtr == '/')
        {
            // there is an optional transport spec
            startPtr = endPtr + 1;

            if (NULL == (endPtr = oscl_strstr(startPtr, ";")))
            {
                endPtr = final_end;
            }

            rtspTrans->transportType = RtspTransport::UNKNOWN_TRANSPORT;
            rtspTrans->transportTypeIsSet = true;

            if (!oscl_strncmp(startPtr, "UDP", endPtr - startPtr))
            {
                rtspTrans->transportType = RtspTransport::UDP_TRANSPORT;
            }
            else if (!oscl_strncmp(startPtr, "TCP", endPtr - startPtr))
            {
                rtspTrans->transportType = RtspTransport::TCP_TRANSPORT;
            }
        }

        if (endPtr == final_end)
        {
            break;
        }

        // increment the startPtr past the separator
        startPtr = endPtr + 1;

    }
    while (0); // only go through this once


    while (startPtr < final_end)
    {

        for (; startPtr < final_end && ((*startPtr >= 0x09 && *startPtr <= 0x0D) || *startPtr == 0x20); ++startPtr);

        char *sepPtr;
        // find the next separator
        if (NULL == (sepPtr = (char*)(endPtr = oscl_strstr(startPtr, ";"))))
        {
            endPtr = final_end;
        }

        if (sepPtr)
        {
            *sepPtr = '\0';
        }

        if (!oscl_strncmp(startPtr, "unicast", endPtr - startPtr))
        {
            rtspTrans->delivery = RtspTransport::UNICAST_DELIVERY;
            rtspTrans->deliveryIsSet = true;
        }
        if (!oscl_strncmp(startPtr, "multicast", endPtr - startPtr))
        {
            rtspTrans->delivery = RtspTransport::MULTICAST_DELIVERY;
            rtspTrans->deliveryIsSet = true;
        }
        else if (!oscl_strncmp(startPtr, "append", endPtr - startPtr))
        {
            rtspTrans->append = true;
            rtspTrans->appendIsSet = true;
        }
        else if (!oscl_strncmp(startPtr, destination_str,
                               destination_str_len))
        {
            nxtPtr = startPtr + destination_str_len;
            char *curEndPtr = OSCL_CONST_CAST(char*, oscl_strstr(nxtPtr, "="));
            if (curEndPtr)
            {
                nxtPtr = curEndPtr + 1;

                // skip leading whitespace
                for (; nxtPtr < endPtr && ((*nxtPtr >= 0x09 && *nxtPtr <= 0x0D) || *nxtPtr == 0x20); ++nxtPtr);

                // now find the end
                for (curEndPtr = (char*)nxtPtr; curEndPtr < endPtr && !((*curEndPtr >= 0x09 && *curEndPtr <= 0x0D) || *curEndPtr == 0x20);
                        ++curEndPtr);

                if (curEndPtr - nxtPtr > 0)
                {
                    if (curEndPtr < endPtr)
                    {
                        *curEndPtr = '\0';
                    }
                    else if (sepPtr)
                    {
                        // clear sepPtr so it won't overwrite the NULL character
                        sepPtr = NULL;
                    }
                    rtspTrans->destination.setPtrLen(nxtPtr,
                                                     curEndPtr - nxtPtr);
                    rtspTrans->destinationIsSet = true;
                }
            }

        }
        else if (!oscl_strncmp(startPtr, interleaved_str,
                               interleaved_str_len))
        {
            int tmp;
            nxtPtr = startPtr + interleaved_str_len;
            char *curEndPtr = OSCL_CONST_CAST(char*, oscl_strstr(nxtPtr, "-"));
            if (curEndPtr) *curEndPtr = '\0';
            uint32 atoi_tmp;
            PV_atoi(nxtPtr, 'd', atoi_tmp);
            tmp = atoi_tmp;
            if (tmp < MIN_CHANNEL_VALUE || tmp > MAX_CHANNEL_VALUE)
            {
                amMalformed = RTSPErrorSyntax;
                if (sepPtr)
                {
                    *sepPtr = ';';
                };
                break;
            }
            rtspTrans->channel1 = tmp;
            if (!curEndPtr)
            {
                if (rtspTrans->channel1 >= MAX_CHANNEL_VALUE)
                {
                    amMalformed = RTSPErrorSyntax;
                    if (sepPtr)
                    {
                        *sepPtr = ';';
                    };
                    break;
                }
                rtspTrans->channel2 = rtspTrans->channel1 + 1;
                rtspTrans->channelIsSet = true;
            }
            else
            {
                nxtPtr = curEndPtr + 1;
                *curEndPtr = '-';
                PV_atoi(nxtPtr, 'd', atoi_tmp);
                tmp = atoi_tmp;
                if (tmp < MIN_CHANNEL_VALUE || tmp > MAX_CHANNEL_VALUE)
                {
                    amMalformed = RTSPErrorSyntax;
                    if (sepPtr)
                    {
                        *sepPtr = ';';
                    };
                    break;
                }
                rtspTrans->channel2 = tmp;
                rtspTrans->channelIsSet = true;
            }
        }
        else if (!oscl_strncmp(startPtr, client_port_str,
                               client_port_str_len))
        {
            int tmp;
            uint32 atoi_tmp;
            nxtPtr = startPtr + client_port_str_len;
            char *curEndPtr = OSCL_CONST_CAST(char*, oscl_strstr(nxtPtr, "-"));
            if (curEndPtr) *curEndPtr = '\0';
            PV_atoi(nxtPtr, 'd', atoi_tmp);
            tmp = atoi_tmp;
            if (tmp < PORT_MIN_VALUE || tmp > PORT_MAX_VALUE)
            {
                amMalformed = RTSPErrorSyntax;
                if (sepPtr)
                {
                    *sepPtr = ';';
                };
                break;
            }
            rtspTrans->client_port1 = tmp;
            if (!curEndPtr)
            {
                if (rtspTrans->client_port1 >= PORT_MAX_VALUE)
                {
                    amMalformed = RTSPErrorSyntax;
                    if (sepPtr)
                    {
                        *sepPtr = ';';
                    };
                    break;
                }
                rtspTrans->client_port2 = rtspTrans->client_port1 + 1;
                rtspTrans->client_portIsSet = true;
            }
            else
            {
                nxtPtr = curEndPtr + 1;
                *curEndPtr = '-';
                PV_atoi(nxtPtr, 'd', atoi_tmp);
                tmp = atoi_tmp;
                if (tmp < PORT_MIN_VALUE || tmp > PORT_MAX_VALUE)
                {
                    amMalformed = RTSPErrorSyntax;
                    if (sepPtr)
                    {
                        *sepPtr = ';';
                    };
                    break;
                }
                rtspTrans->client_port2 = tmp;
                rtspTrans->client_portIsSet = true;
            }
        }
        else if (!oscl_strncmp(startPtr, server_port_str,
                               server_port_str_len))
        {
            int tmp;
            uint32 atoi_tmp;
            nxtPtr = startPtr + server_port_str_len;
            char *curEndPtr = OSCL_CONST_CAST(char*, oscl_strstr(nxtPtr, "-"));
            if (curEndPtr) *curEndPtr = '\0';
            PV_atoi(nxtPtr, 'd', atoi_tmp);
            tmp = atoi_tmp;
            if (tmp < PORT_MIN_VALUE || tmp > PORT_MAX_VALUE)
            {
                amMalformed = RTSPErrorSyntax;
                if (sepPtr)
                {
                    *sepPtr = ';';
                };
                break;
            }
            rtspTrans->server_port1 = tmp;
            if (!curEndPtr)
            {
                if (rtspTrans->server_port1 >= PORT_MAX_VALUE)
                {
                    amMalformed = RTSPErrorSyntax;
                    if (sepPtr)
                    {
                        *sepPtr = ';';
                    };
                    break;
                }
                rtspTrans->server_port2 = rtspTrans->server_port1 + 1;
                rtspTrans->server_portIsSet = true;
            }
            else
            {
                nxtPtr = curEndPtr + 1;
                *curEndPtr = '-';
                PV_atoi(nxtPtr, 'd', atoi_tmp);
                tmp = atoi_tmp;
                if (tmp < PORT_MIN_VALUE || tmp > PORT_MAX_VALUE)
                {
                    amMalformed = RTSPErrorSyntax;
                    if (sepPtr)
                    {
                        *sepPtr = ';';
                    };
                    break;
                }
                rtspTrans->server_port2 = tmp;
                rtspTrans->server_portIsSet = true;
            }
        }
        else if (!oscl_strncmp(startPtr, ttl_str, ttl_str_len))
        {
            nxtPtr = startPtr + ttl_str_len;
            int tmp;
            uint32 atoi_tmp;
            PV_atoi(nxtPtr, 'd', atoi_tmp);
            tmp = atoi_tmp;
            if (tmp < 0 || tmp > 255)
            {
                amMalformed = RTSPErrorSyntax;
                if (sepPtr)
                {
                    *sepPtr = ';';
                };
                break;
            }
            rtspTrans->ttl = tmp;
            rtspTrans->ttlIsSet = true;
        }
        else if (!oscl_strncmp(startPtr, mode_str, mode_str_len))
        {
            rtspTrans->mode.play_mode = false;
            rtspTrans->mode.record_mode = false;
            nxtPtr = startPtr + mode_str_len;
            rtspTrans->modeIsSet = true;
            char *quotePtr = NULL;
            const char *wordEndPtr;
            if (*nxtPtr == '\"')
            {
                ++nxtPtr;
                if (NULL != (quotePtr = OSCL_CONST_CAST(char*, oscl_strstr(nxtPtr, "\""))))
                {
                    *quotePtr = '\0';
                }
            }
            while (nxtPtr < endPtr)
            {
                if (NULL == (wordEndPtr = oscl_strstr(nxtPtr, ",")))
                {
                    wordEndPtr = endPtr;
                }

                if (!oscl_strncmp(nxtPtr, "PLAY", wordEndPtr - nxtPtr))
                {
                    rtspTrans->mode.play_mode = true;
                }
                else if (!oscl_strncmp(nxtPtr, "RECORD", wordEndPtr - nxtPtr))
                {
                    rtspTrans->mode.record_mode = true;
                }
                nxtPtr = wordEndPtr + 1;

            }

            if (quotePtr)
            {
                *quotePtr = '\"';
            }
        }
        else if (!oscl_strncmp(startPtr, port_str, port_str_len))
        {
            int tmp;
            uint32 atoi_tmp;
            nxtPtr = startPtr + port_str_len;
            char *curEndPtr = OSCL_CONST_CAST(char*, oscl_strstr(nxtPtr, "-"));
            if (curEndPtr) *curEndPtr = '\0';
            PV_atoi(nxtPtr, 'd', atoi_tmp);
            tmp = atoi_tmp;
            if (tmp < PORT_MIN_VALUE || tmp > PORT_MAX_VALUE)
            {
                amMalformed = RTSPErrorSyntax;
                if (sepPtr)
                {
                    *sepPtr = ';';
                };
                break;
            }
            rtspTrans->port1 = tmp;
            if (!curEndPtr)
            {
                if (rtspTrans->port1 >= PORT_MAX_VALUE)
                {
                    amMalformed = RTSPErrorSyntax;
                    if (sepPtr)
                    {
                        *sepPtr = ';';
                    };
                    break;
                }
                rtspTrans->port2 = rtspTrans->port1 + 1;
                rtspTrans->portIsSet = true;
            }
            else
            {
                nxtPtr = curEndPtr + 1;
                *curEndPtr = '-';
                PV_atoi(nxtPtr, 'd', atoi_tmp);
                tmp = atoi_tmp;
                if (tmp < PORT_MIN_VALUE || tmp > PORT_MAX_VALUE)
                {
                    amMalformed = RTSPErrorSyntax;
                    if (sepPtr)
                    {
                        *sepPtr = ';';
                    };
                    break;
                }
                rtspTrans->port2 = tmp;
                rtspTrans->portIsSet = true;
            }
        }
        else if (!oscl_strncmp(startPtr, layers_str, layers_str_len))
        {
            nxtPtr = startPtr + layers_str_len;
            PV_atoi(nxtPtr, 'd', rtspTrans->layers);
            rtspTrans->layersIsSet = true;
        }
        else if (!oscl_strncmp(startPtr, ssrc_str, ssrc_str_len))
        {
            nxtPtr = startPtr + ssrc_str_len;
            rtspTrans->ssrc = 0;
            rtspTrans->ssrcIsSet = false;
            if (!PV_atoi(nxtPtr, 'x', rtspTrans->ssrc))
            {
                amMalformed = RTSPErrorSyntax;
                if (sepPtr)
                {
                    *sepPtr = ';';
                };
                break;
            }
            rtspTrans->ssrcIsSet = true;
        }

        if (sepPtr)
        {
            *sepPtr = ';';
        }

        startPtr = endPtr + 1;

    }

    if (transSepPtr)
    {
        // put the separator character back
        *transSepPtr = ',';
        trans = transSepPtr;
    }
    else
    {
        trans = final_end;
    }

}

void
RTSPIncomingMessage::parseRTPInfo(uint16 fieldIdx)
{
    char * cPtr;
    char * finishPtr;

    cPtr = const_cast<char*>(fieldVals[ fieldIdx ].c_str());
    finishPtr = cPtr + fieldVals[ fieldIdx ].length();

    do
    {
        parseOneRTPInfoEntry(cPtr, finishPtr);

    }
    while (RTSPOk == amMalformed
            &&  cPtr < finishPtr
          );
}

void
RTSPIncomingMessage::parseOneRTPInfoEntry(char * & cPtr, char * finishPtr)
{
    bool   getOut;
    char * subFieldName;
    char * separator;
    char * endOfIt;

    // check the counter
    //
    if (RTSP_MAX_NUMBER_OF_RTP_INFO_ENTRIES == numOfRtpInfoEntries)
    { // limit reached
        amMalformed = RTSPErrorSyntax;  // should it be different?
        return;
    }
    ++numOfRtpInfoEntries;

    // skip comma, if necessary
    //
    if (CHAR_COMMA == *cPtr)
    {
        ++cPtr;
    }

    getOut = false;

    // set ssrcIsSet to false by default
    rtpInfo[numOfRtpInfoEntries - 1].ssrcIsSet = false;

    while ((!getOut) && cPtr < finishPtr && CHAR_COMMA != *cPtr)
    {
        // reset pointers

        subFieldName = NULL;
        separator = NULL;
        endOfIt = NULL;

        // skip whitespace

        while (cPtr < finishPtr
                && (CHAR_SPACE == *cPtr
                    || CHAR_TAB  == *cPtr
                    || CHAR_SEMICOLON == *cPtr
                    || CHAR_NULL == *cPtr
                   )
              )
        {
            ++cPtr;
        }
        if (cPtr >= finishPtr  || CHAR_COMMA  == *cPtr)
        {
            return;
        }

        // we see the beginning
        subFieldName = cPtr;

        // get the separator
        while (cPtr < finishPtr
                && CHAR_EQUAL != *cPtr
              )
        {
            ++cPtr;
        }
        if (cPtr >= finishPtr)
        {
            return;
        }
        // we see the separator
        separator = cPtr;

        // get the end of it
        while (cPtr < finishPtr
                && CHAR_SEMICOLON != *cPtr
                && CHAR_SPACE != *cPtr
                && CHAR_TAB != *cPtr
                && CHAR_COMMA != *cPtr
              )
        {
            ++cPtr;
        }

        endOfIt = cPtr;

        if (3 == separator - subFieldName)
        { // url?
            if (('u' == (subFieldName[0] | OSCL_ASCII_CASE_MAGIC_BIT))
                    && ('r' == (subFieldName[1] | OSCL_ASCII_CASE_MAGIC_BIT))
                    && ('l' == (subFieldName[2] | OSCL_ASCII_CASE_MAGIC_BIT))
               )
            {
                rtpInfo[ numOfRtpInfoEntries-1 ].url.setPtrLen(
                    separator + 1,
                    endOfIt - separator - 1
                );
                rtpInfo[ numOfRtpInfoEntries-1 ].urlIsSet = true;
                if (CHAR_COMMA == *endOfIt)
                {
                    getOut = true;
                }
                *endOfIt = CHAR_NULL;
                if (getOut)
                {
                    // skip ahead beyond the whitespace
                    // since we overwrote the comma with a NULL
                    while (cPtr < finishPtr
                            && (CHAR_SPACE == *cPtr
                                || CHAR_TAB  == *cPtr
                                || CHAR_SEMICOLON == *cPtr
                                || CHAR_NULL == *cPtr
                               )
                          )
                    {
                        ++cPtr;
                    }
                }
            }
            // or, maybe, seq?
            else if (('s' == (subFieldName[0] | OSCL_ASCII_CASE_MAGIC_BIT))
                     && ('e' == (subFieldName[1] | OSCL_ASCII_CASE_MAGIC_BIT))
                     && ('q' == (subFieldName[2] | OSCL_ASCII_CASE_MAGIC_BIT))
                    )
            {
                uint32 atoi_tmp;
                PV_atoi(separator + 1, 'd', atoi_tmp);
                rtpInfo[ numOfRtpInfoEntries-1 ].seq  = (RtpSeqType)atoi_tmp;

                rtpInfo[ numOfRtpInfoEntries-1 ].seqIsSet = true;
            }
            else
            {
                amMalformed = RTSPErrorSyntax;
                return;
            }
        }
        else if (7 == separator - subFieldName)
        { // rtptime?
            if (('r' == (subFieldName[0] | OSCL_ASCII_CASE_MAGIC_BIT))
                    && ('t' == (subFieldName[1] | OSCL_ASCII_CASE_MAGIC_BIT))
                    && ('p' == (subFieldName[2] | OSCL_ASCII_CASE_MAGIC_BIT))
                    && ('t' == (subFieldName[3] | OSCL_ASCII_CASE_MAGIC_BIT))
                    && ('i' == (subFieldName[4] | OSCL_ASCII_CASE_MAGIC_BIT))
                    && ('m' == (subFieldName[5] | OSCL_ASCII_CASE_MAGIC_BIT))
                    && ('e' == (subFieldName[6] | OSCL_ASCII_CASE_MAGIC_BIT))
               )
            {
                uint32 rtptime = 0;
                PV_atoi((separator + 1), 'd', rtptime);
                rtpInfo[ numOfRtpInfoEntries-1 ].rtptime = rtptime;
                rtpInfo[ numOfRtpInfoEntries-1 ].rtptimeIsSet = true;
            }
            else
            {
                amMalformed = RTSPErrorSyntax;
                return;
            }
        }
        else if (4 == separator - subFieldName)
        { // rtptime?
            if (('s' == (subFieldName[0] | OSCL_ASCII_CASE_MAGIC_BIT))
                    && ('s' == (subFieldName[1] | OSCL_ASCII_CASE_MAGIC_BIT))
                    && ('r' == (subFieldName[2] | OSCL_ASCII_CASE_MAGIC_BIT))
                    && ('c' == (subFieldName[3] | OSCL_ASCII_CASE_MAGIC_BIT))
               )
            {
                uint32 ssrc = 0;
                PV_atoi((separator + 1), 'x', ssrc);

                rtpInfo[ numOfRtpInfoEntries-1 ].ssrc = ssrc;
                rtpInfo[ numOfRtpInfoEntries-1 ].ssrcIsSet = true;
            }
            else
            {
                amMalformed = RTSPErrorSyntax;
                return;
            }
        }



        else
        {
            amMalformed = RTSPErrorSyntax;
            return;
        }

        cPtr = endOfIt;
    }
}

OSCL_EXPORT_REF bool
RTSPIncomingMessage::parseEntityBody(RTSPEntityBody * entityBody)
{
    char * endOfMessage = (char *) entityBody->ptr + entityBody->len;
    char * ptr;

    for (ptr = (char *) entityBody->ptr;
            ptr < endOfMessage ;
            ++numPtrFields
        )
    {
        char * endOfValue = ptr;
        while (CHAR_LF != *(endOfValue)
                &&  CHAR_CR != *(endOfValue)
                &&  CHAR_NULL != *(endOfValue))
        {
            ++endOfValue;
        }

        if (CHAR_CR == *(endOfValue) && CHAR_LF == *(endOfValue + 1))
        {
            // need to increment endOfValue for CR-LF, because
            // it is needed later to step into the next field by shifting by 1
            *(endOfValue) = CHAR_NULL;
            *(++endOfValue) = CHAR_NULL;
        }
        else
        {
            *endOfValue = CHAR_NULL;
        }

        char * separator = ptr;

        while ((CHAR_COLON != *separator)
                && (CHAR_NULL != *separator)
              )
        {
            ++separator;
        }

        if (CHAR_COLON != *separator)
        {
            amMalformed = RTSPErrorSyntax;
        }
        else
        {   // field is pretty much normal

            *separator = CHAR_NULL;

            // get name pointer
            char * namePtr = ptr;
            // eat through trailing whitespace
            for (char * wPtr1 = separator - 1;
                    (wPtr1 >= namePtr)
                    && ((*wPtr1 >= 0x09 && *wPtr1 <= 0x0D) || *wPtr1 == 0x20);
                    --wPtr1)
            {
                *wPtr1 = CHAR_NULL;
            }
            // eat through leading whitespace
            while ((*namePtr >= 0x09 && *namePtr <= 0x0D) || *namePtr == 0x20)
            {
                ++namePtr;
            }

            // get value pointer
            char * valuePtr = separator + 1;
            // eat through trailing whitespace
            for (char * wPtr2 = endOfValue - 1;
                    (wPtr2 > separator)
                    && ((*wPtr2 >= 0x09 && *wPtr2 <= 0x0D) || *wPtr2 == 0x20);
                    --wPtr2)
            {
                *wPtr2 = CHAR_NULL;
            }
            //eat through leading whitespace
            while ((*valuePtr >= 0x09 && *valuePtr <= 0x0D) || *valuePtr == 0x20)
            {
                ++valuePtr;
            }

            // set the pointers

            fieldKeys[ numPtrFields ] = namePtr;
            fieldVals[ numPtrFields ] = valuePtr;

            // now, figure out if we are supposed to recognize this
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldSessionId))
            {
                sessionId = fieldVals[ numPtrFields ];
                sessionIdIsSet = true;
            }
//RTSP_PAR_COM_CONSTANTS_H_
#define RtspRecognizedFieldEOF "EOF"

            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldEOF))
            {
                eofField = fieldVals[ numPtrFields ];
                eofFieldIsSet = true;
            }
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldPlaylistRange))
            {
                playlistRangeField = fieldVals[ numPtrFields ];  // not sure we really need to store this, but do it for now anyway
                playlistRangeFieldIsSet = true;
                // now parse the entry
                /*playlistRangeField.setPtrLen("",0);
                playlistRangeFieldIsSet = false;
                playlistErrorFieldIsSet = false;
                playlistErrorFieldCount = 0;
                playlistRangeUrl.setPtrLen("",0);
                playlistRangeClipIndex=0;
                playlistRangeClipOffset=0;
                playlistRangeNptTime=0;*/
                char* curr = valuePtr; // set curr to the start of the entry after the colon, whitespace was already removed
                StrPtrLen eStr;

                eStr = curr;
                // look for playlist_play_time
                const StrPtrLen RtspPlaylistPlayTimeStr = "playlist_play_time";
                if (0 && RtspPlaylistPlayTimeStr.isCIPrefixOf(eStr))
                {
                    curr += 18; // move past "playlist_play_time"
                    // eat through leading whitespace
                    while ((*curr >= 0x09 && *curr <= 0x0D) || *curr == 0x20)
                    {
                        ++curr;
                    }
                    if (*curr != CHAR_EQUAL)
                    {
                        // problem
                    }
                    // could be more whitespace
                    while ((*curr >= 0x09 && *curr <= 0x0D) || *curr == 0x20)
                    {
                        ++curr;
                    }
                    if (*curr != CHAR_LT)
                    {
                        // problem
                    }
                    ++curr; // move past '<'
                    // could be even more whitespace
                    while ((*curr >= 0x09 && *curr <= 0x0D) || *curr == 0x20)
                    {
                        ++curr;
                    }
                    // now comes the url
                    // find the end first
                    char* end = curr;
                    while (*end != CHAR_COMMA)
                    {
                        ++end;
                    }
                    char* endtmp = end;
                    // may have trailing whitespace
                    if (*(endtmp - 1) == 0x09 || *(endtmp - 1) == 0x0D || *(endtmp - 1) == 0x20)
                    {
                        --endtmp;
                        while ((*endtmp >= 0x09 && *endtmp <= 0x0D) || *endtmp == 0x20)
                        {
                            --endtmp;
                        }
                        // will be on a non space char, move it over
                        ++endtmp;
                    }
                    // to do - not sure if we need a '\0' here..
                    playlistRangeUrl.setPtrLen(curr, endtmp - curr);

                    curr = end + 1;
                    // next should be the clip index
                    // of course, could be more whitespace
                    while ((*curr >= 0x09 && *curr <= 0x0D) || *curr == 0x20)
                    {
                        ++curr;
                    }
                    // find next comma
                    end = curr;
                    while (*end != CHAR_COMMA)
                    {
                        ++end;
                    }
                    // may have trailing whitespace
                    // dont worry about it for now  atoi should be ok
                    /*if(*(end-1) == 0x09 ||*(end-1) == 0x0D ||*(end-1) == 0x20)
                    {
                    --end;
                    while( (*end >= 0x09 && *end <= 0x0D) || *end == 0x20 )
                    {
                    --end;
                    }
                    // will be on a non space char, move it over
                    ++end;
                    }*/
                    // finally get the value
                    uint32 atoi_tmp;
                    PV_atoi(curr, 'd', atoi_tmp);
                    playlistRangeClipIndex = atoi_tmp;

                    curr = end + 1;
                    // next should be the clip offset
                    // of course, could be more whitespace
                    while ((*curr >= 0x09 && *curr <= 0x0D) || *curr == 0x20)
                    {
                        ++curr;
                    }
                    // find next comma
                    end = curr;
                    while (*end != CHAR_GT)
                    {
                        ++end;
                    }
                    // may have trailing whitespace
                    // dont worry about it for now  atoi should be ok
                    /*if(*(end-1) == 0x09 ||*(end-1) == 0x0D ||*(end-1) == 0x20)
                    {
                    --end;
                    while( (*end >= 0x09 && *end <= 0x0D) || *end == 0x20 )
                    {
                    --end;
                    }
                    // will be on a non space char, move it over
                    ++end;
                    }*/
                    // finally get the value
                    PV_atoi(curr, 'd', atoi_tmp);
                    playlistRangeClipOffset = atoi_tmp;

                }
                else
                {
                    // problem
                }
            }
            if (fieldKeys[ numPtrFields ].isCIEquivalentTo(
                        RtspRecognizedFieldPlaylistError))
            {
                if (playlistErrorFieldCount < RTSP_MAX_NUMBER_OF_PLAYLIST_ERROR_ENTRIES)
                {
                    playlistErrorField[playlistErrorFieldCount++] = fieldVals[ numPtrFields ];  // this needs to be an array since the number can be unbounded
                    playlistErrorFieldIsSet = true;
                }
            }
        }
        ptr = endOfValue + 1;
    }
    return true;
}

