/* ------------------------------------------------------------------
 * 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 "oscl_dll.h"
// Define entry point for this DLL
OSCL_DLL_ENTRY_POINT_DEFAULT()

// This function searches for a substring in a string, just like strstr(), but
// with a couple of specific features:
//  1. It is case-insensitive, so "Ces" can be found in the word "success";
//  2. It is looking for substrings left-to-right, and if the string starts
//  matching, the state of search gets increased; if the whole string does not
//  match, this function does NOT step back; therefore, the string "KoKoMo"
//  will not be found correctly in the string "KoKoKoMo"; however, it will
//  work for substrings that have unique beginnings that don't repeat
//  themselves, such as "Content-length" ('c' is never repeated). Therefore,
//  such a search works for "Content-length", which is all that RtspParser
//  needs.
//
// This function is not part of OSCL precisely because of its limitations;
// however, these limitations are ok here because they contribute to the
// overall performance by not doing extra steps.
//
static inline char *
ci_local_strstr(char * bigString, int32 bigStringSize,
                const StrPtrLen & subString
               )
{
    uint32 state = 0;
    uint32 targetState = subString.length();
    char * pp;
    //char * eos = bigString + oscl_strlen(bigString);
    char * eos = bigString + bigStringSize;

    for (pp = bigString;
            pp != eos;
            ++pp
        )
    {
        if (OSCL_ASCII_CASE_MAGIC_BIT
                == (((*pp) ^(subString.c_str()[state])) | OSCL_ASCII_CASE_MAGIC_BIT)
           )
        {
            ++state;
            if (state == targetState)
            {
                return pp - targetState + 1;
            }
        }
        else
        {
            state = 0;
        }

    }

    return NULL;
}


static inline bool
isspaceNotNL(char ch)
{
    if ((CHAR_CR == ch) || (CHAR_LF == ch) || (CHAR_NULL == ch))
    {
        return false;
    }

    return (0 != ((ch >= 0x09 && ch <= 0x0D) || ch == 0x20));
}

OSCL_EXPORT_REF RTSPParser::ParserState
RTSPParser::getState()
{
    ParserState stateToReturn;

    switch (internalState)
    {
        case    IS_WAITING_FOR_REQUEST_MEMORY:
            stateToReturn = WAITING_FOR_REQUEST_MEMORY;
            break;

        case    IS_LOOKING_FOR_END_OF_REQUEST:
        case  IS_CONTINUING_TO_FILL_OUT_ENTITY_BODY:
        case  IS_SKIPPING_OVER_ENTITY_BODY:
        case  IS_LOOKING_FOR_RESYNC:
        case  IS_CONTINUING_TO_FILL_OUT_EMBEDDED_DATA:
        case  IS_SKIPPING_OVER_EMBEDDED_DATA:
            stateToReturn = WAITING_FOR_DATA;
            break;

        case  IS_START_LOOKING_FOR_RESYNC:
            stateToReturn = ERROR_REQUEST_TOO_BIG;
            internalState = IS_LOOKING_FOR_RESYNC;
            break;

        case  IS_WAITING_FOR_ENTITY_BODY_MEMORY:
            stateToReturn = WAITING_FOR_ENTITY_BODY_MEMORY;
            break;

        case IS_WAITING_FOR_EMBEDDED_DATA_MEMORY:
            stateToReturn = WAITING_FOR_EMBEDDED_DATA_MEMORY;
            break;

        case  IS_ERROR_REQUEST_TOO_BIG:
        case    IS_REQUEST_IS_READY:
            // both states are processed the same way,
            // except that the state returned is different
            //
            stateToReturn = (IS_REQUEST_IS_READY == internalState) ?
                            REQUEST_IS_READY : ERROR_REQUEST_TOO_BIG;

            // take care of the entity bodies
            if (0 == ebFullSizeExpected)
            { // no eb expected
                internalState = IS_WAITING_FOR_REQUEST_MEMORY;
            }
            else
            { // check to see the nature of the entity body. if it
                // is embedded data, change internal state appropriately
                if (requestStruct->method != METHOD_BINARY_DATA)
                {
                    internalState = IS_WAITING_FOR_ENTITY_BODY_MEMORY;
                }
                else
                {
                    internalState = IS_WAITING_FOR_EMBEDDED_DATA_MEMORY;
                }
            }
            continueProcessing();

            break;

        case  IS_ENTITY_BODY_IS_READY:
            stateToReturn = ENTITY_BODY_IS_READY;
            internalState = IS_WAITING_FOR_REQUEST_MEMORY;
            break;

        case IS_EMBEDDED_DATA_IS_READY:
            stateToReturn = EMBEDDED_DATA_IS_READY;
            internalState = IS_WAITING_FOR_REQUEST_MEMORY;
            break;

        default:

            // now, this is an internal error
            internalState = IS_INTERNAL_ERROR;
            stateToReturn = INTERNAL_ERROR;
    }

    return stateToReturn;
}

OSCL_EXPORT_REF RTSPParser::RTSPParser()
{
    flush();
};

void
RTSPParser::continueProcessing()
{
    switch (internalState)
    {
        case IS_WAITING_FOR_REQUEST_MEMORY:
            break;

        case IS_LOOKING_FOR_END_OF_REQUEST:
            lookForEndOfRequest();
            break;

        case IS_START_LOOKING_FOR_RESYNC:
        case IS_LOOKING_FOR_RESYNC:
            lookForResync();
            break;

        case IS_WAITING_FOR_ENTITY_BODY_MEMORY:
            // nothing to do, really
            break;

        case IS_SKIPPING_OVER_ENTITY_BODY:
            skipOverEntityBody();
            break;

        case IS_STARTING_TO_FILL_OUT_ENTITY_BODY:
            startFillingOutEntityBody();
            break;

        case IS_CONTINUING_TO_FILL_OUT_ENTITY_BODY:
            break;

        case IS_WAITING_FOR_EMBEDDED_DATA_MEMORY:
            break;

        case IS_SKIPPING_OVER_EMBEDDED_DATA:
            skipOverEmbeddedData();
            break;

        case IS_STARTING_TO_FILL_OUT_EMBEDDED_DATA:
            startFillingOutEmbeddedData();
            break;

        case IS_CONTINUING_TO_FILL_OUT_EMBEDDED_DATA:
            break;


        default:
            internalState = IS_INTERNAL_ERROR;
    }
}

OSCL_EXPORT_REF bool
RTSPParser::registerNewRequestStruct(RTSPIncomingMessage * newRequestStruct)
{
    if (IS_WAITING_FOR_REQUEST_MEMORY != internalState)
    {
        return false;
    }

    requestStruct = newRequestStruct;

    internalState = IS_LOOKING_FOR_END_OF_REQUEST;

    continueProcessing();

    return true;
}

OSCL_EXPORT_REF const StrPtrLen *
RTSPParser::getDataBufferSpec()
{
    if (internalState == IS_LOOKING_FOR_END_OF_REQUEST
            ||  internalState == IS_START_LOOKING_FOR_RESYNC
            ||  internalState == IS_LOOKING_FOR_RESYNC
       )
    {
        // normal case - just put stuff into the buffer
        dataBufferSpec.setPtrLen(mainBufferSpace,
                                 mainBuffer + RTSP_PARSER_BUFFER_SIZE - mainBufferSpace);
    }

    else if (internalState == IS_CONTINUING_TO_FILL_OUT_ENTITY_BODY
             || internalState == IS_CONTINUING_TO_FILL_OUT_EMBEDDED_DATA
            )
    {

        // the whole entity body arithmetic is done here, not in
        // registerDataWritten(), so here it comes
        while (entityBody[ebCurrentIndex].len == ebCurrentOffset)
        {
            ++ebCurrentIndex;
            ebCurrentOffset = 0;
        }

        // so, we are at the appropriate fragment;
        // figure out which is smaller
        //
        uint32 sizePossibleToWrite =
            entityBody[ebCurrentIndex].len - ebCurrentOffset;
        uint32 sizeLeftToWrite = ebFullSizeExpected - ebSizeCoveredSoFar;

        // set the data buffer spec
        dataBufferSpec.setPtrLen(
            static_cast<char*>(entityBody[ebCurrentIndex].ptr) + ebCurrentOffset,
            (sizePossibleToWrite < sizeLeftToWrite) ?
            sizePossibleToWrite : sizeLeftToWrite
        );
    }

    else if (IS_SKIPPING_OVER_ENTITY_BODY == internalState)
    {
        mainBufferEntry = mainBuffer;
        mainBufferSpace = mainBuffer;

        dataBufferSpec.setPtrLen(mainBufferSpace, RTSP_PARSER_BUFFER_SIZE);
    }

    else
    {
        // it's some kind of inconsistency on the Engine's part, or Parser's
        // internal error

        return NULL;
    }

    return & dataBufferSpec;
}

void
RTSPParser::lookForEndOfRequest()
{
    // eorptr = mainBufferEntry;
    uint32   newMessageSize;

    *mainBufferSpace = CHAR_NULL;

    bool  shouldMoveOverToBeginning = false;

    // now, it's either a binary data thing, or a regular message
    if (CHAR_DOLLAR == *mainBufferEntry)
    {

        if (mainBufferSpace - mainBufferEntry < 4)
        { // not a complete message
            shouldMoveOverToBeginning = true;
        }
        else
        { // it is a complete thing
            requestStruct->msgType = RTSPRequestMsg;
            requestStruct->method = METHOD_BINARY_DATA;

            requestStruct->contentLength =
                ((static_cast<uint16>(
                      (*(reinterpret_cast<unsigned char*>(mainBufferEntry + 2))
                      ))) << 8)
                + static_cast<uint16>(
                    *(reinterpret_cast<unsigned char*>(mainBufferEntry + 3))
                );
            requestStruct->contentLengthIsSet = true;

            *(mainBufferEntry + 2) = CHAR_NULL;
            requestStruct->contentType = mainBufferEntry + 1;
            requestStruct->contentTypeIsSet = true;

            requestStruct->channelID = static_cast<uint8>(
                                           *(reinterpret_cast<unsigned char*>(mainBufferEntry + 1)));

            mainBufferEntry += 4;

            eorptr = mainBufferEntry;

            ebFullSizeExpected = requestStruct->contentLength;

            internalState = IS_REQUEST_IS_READY;

            return;
        }
    }

    else
    { // it's a normal message
        bool found = false;

        for (/*eorptr = mainBufferEntry*/; eorptr < mainBufferSpace - 1; ++eorptr)
        {
            if (CHAR_LF == *eorptr || CHAR_CR == *eorptr)
            {   // it's a possible

                // is it two newlines?
                if (*eorptr == *(eorptr + 1))
                {   // yes, CR-CR or LF-LF format

                    found = true;
                    eorptr += 2;
                    break;
                }
                else if ((eorptr <= mainBufferSpace - 4)
                         && (CHAR_CR == *(eorptr)) && (CHAR_LF == *(eorptr + 1))
                         && (CHAR_CR == *(eorptr + 2)) && (CHAR_LF == *(eorptr + 3))
                        )
                {   // yes, MS-WINDOWS format

                    found = true;
                    eorptr += 4;
                    break;
                }
                // else, continue on
            }
        }

        if (found)
        {
            // transfer the buffer, if necessary

            newMessageSize = eorptr - mainBufferEntry;

            // quickly take a peek at content-length
            char * cl = ci_local_strstr(mainBufferEntry, newMessageSize,
                                        RtspRecognizedFieldContentLength);
            if (NULL == cl)
            { // nothing visible
                ebFullSizeExpected = 0;
            }
            else if (cl >= mainBufferEntry + newMessageSize)
            { // it's not part of this particular message
                ebFullSizeExpected = 0;
            }
            else
            {
                cl += oscl_strlen(RtspRecognizedFieldContentLength);
                while (cl < eorptr && (isspaceNotNL(*cl) || (CHAR_COLON == *cl)))
                {
                    ++cl;
                }

                uint32 atoi_tmp;
                PV_atoi(cl, 'd', atoi_tmp);
                ebFullSizeExpected = atoi_tmp;
            }

            // now, on with the moving around ...

            if (RTSP_MAX_FULL_REQUEST_SIZE < newMessageSize)
            {
                // request too big

                oscl_memcpy(requestStruct->secondaryBuffer, mainBufferEntry,
                            RTSP_MAX_FULL_REQUEST_SIZE);
                requestStruct->secondaryBuffer[RTSP_MAX_FULL_REQUEST_SIZE] = CHAR_NULL;

                requestStruct->secondaryBufferSizeUsed = newMessageSize;

                requestStruct->amMalformed = RTSPErrorTooBig;

                mainBufferEntry += newMessageSize;

                eorptr = mainBufferEntry;

                internalState = IS_ERROR_REQUEST_TOO_BIG;
            }
            else
            {
                // everything is ok

                oscl_memcpy(requestStruct->secondaryBuffer, mainBufferEntry,
                            newMessageSize);
                requestStruct->secondaryBuffer[ newMessageSize ] = CHAR_NULL;

                requestStruct->secondaryBufferSizeUsed = newMessageSize;

                mainBufferEntry += newMessageSize;

                eorptr = mainBufferEntry;

                internalState = IS_REQUEST_IS_READY;
            }

            dealWithLineContinuations(requestStruct);

            requestStruct->parseFirstFields();

        }
        else
        {
            shouldMoveOverToBeginning = true;
        }
    }

    if (shouldMoveOverToBeginning)
    {
        // i.e. end of request was not found

        int sizeUsedSoFar = mainBufferSpace - mainBufferEntry;

        if (RTSP_PARSER_BUFFER_SIZE == sizeUsedSoFar)
        {   // we hit the parser's buffer size
            internalState = IS_START_LOOKING_FOR_RESYNC;
            continueProcessing();

            return;
        }

        if (mainBufferEntry != mainBuffer)
        {
            oscl_memmove(mainBuffer, mainBufferEntry, sizeUsedSoFar);
            mainBufferEntry = mainBuffer;
            eorptr = mainBufferEntry;
            mainBufferSpace = mainBufferEntry + sizeUsedSoFar;
        }
        else
        { // rewind eorptr

            eorptr -= 4;

            eorptr = (eorptr < mainBufferEntry) ? mainBufferEntry : eorptr;
        }
    }
}

OSCL_EXPORT_REF bool
RTSPParser::registerDataBufferWritten(uint32 sizeWritten)
{
    // take care of entity bodies and stuff

    if (IS_LOOKING_FOR_END_OF_REQUEST == internalState)
    { // the memory being filled out is Parser's
        mainBufferSpace += sizeWritten;

        continueProcessing();

        return true;
    }
    else if (IS_CONTINUING_TO_FILL_OUT_ENTITY_BODY == internalState
             || IS_CONTINUING_TO_FILL_OUT_EMBEDDED_DATA == internalState
            )
    { // the memory being filled out is Engine's entity body
        ebCurrentOffset += sizeWritten;
        ebSizeCoveredSoFar += sizeWritten;

        if (ebSizeCoveredSoFar == ebFullSizeExpected)
        {
            eorptr = mainBufferEntry;
            if (internalState == IS_CONTINUING_TO_FILL_OUT_ENTITY_BODY)
            {
                internalState = IS_ENTITY_BODY_IS_READY;
            }
            else // internalState=IS_CONTINUING_TO_FILL_OUT_EMBEDDED_DATA
            {
                internalState = IS_EMBEDDED_DATA_IS_READY;
            }
        }

        return true;
    }

    else if (IS_SKIPPING_OVER_ENTITY_BODY == internalState)
    {
        mainBufferSpace += sizeWritten;
        continueProcessing();
    }

    else if (IS_START_LOOKING_FOR_RESYNC == internalState
             ||  IS_LOOKING_FOR_RESYNC == internalState
            )
    {
        mainBufferSpace += sizeWritten;
        continueProcessing();
    }

    else
    {
        // some kind of error on Engine's part, or Parser's internal inconsistency
        return false;
    }

    return false; // to appease compiler
}

OSCL_EXPORT_REF bool
RTSPParser::registerEntityBody(RTSPEntityBody * newBody)
{
    if (IS_WAITING_FOR_ENTITY_BODY_MEMORY != internalState)
    {
        return false;
    }

    entityBody = newBody;

    if (NULL == entityBody)
    {
        internalState = IS_SKIPPING_OVER_ENTITY_BODY;
        ebSizeCoveredSoFar = 0;
        ebCurrentIndex = 0;
        ebCurrentOffset = 0;
    }
    else
    {
        internalState = IS_STARTING_TO_FILL_OUT_ENTITY_BODY;
        ebSizeCoveredSoFar = 0;
        ebCurrentIndex = 0;
        ebCurrentOffset = 0;
    }

    continueProcessing();

    return true;
}

void
RTSPParser::skipOverEntityBody()
{
    uint32 sizeNeededToSkip = ebFullSizeExpected - ebSizeCoveredSoFar;
    uint32 sizePossibleToSkip = mainBufferSpace - mainBufferEntry;

    uint32 sizeSkipped = (sizeNeededToSkip < sizePossibleToSkip) ?
                         sizeNeededToSkip : sizePossibleToSkip;

    ebSizeCoveredSoFar += sizeSkipped;
    mainBufferEntry += sizeSkipped;

    if (ebSizeCoveredSoFar == ebFullSizeExpected)
    {
        eorptr = mainBufferEntry;
        internalState = IS_WAITING_FOR_REQUEST_MEMORY;
    }
}

void
RTSPParser::startFillingOutEntityBody()
{
    while (ebFullSizeExpected != ebSizeCoveredSoFar)
    {
        // find an appropriate fragment
        if (entityBody[ebCurrentIndex].len == ebCurrentOffset)
        {
            ++ebCurrentIndex;
            ebCurrentOffset = 0;
            continue;
        }

        uint32 fragmentSizeAvailable = entityBody[ebCurrentIndex].len - ebCurrentOffset;

        // decide, which is smaller - size left in the main buffer, or size left
        // in the current target buffer
        uint32 sizeAvailableForWriting = mainBufferSpace - mainBufferEntry;
        uint32 sizeActuallyToBeWritten =
            (fragmentSizeAvailable < sizeAvailableForWriting) ?
            fragmentSizeAvailable : sizeAvailableForWriting;

        // now decide, which is smaller - the previous result or the size actually
        // left to fill out
        uint32  sizeLeftToWrite = ebFullSizeExpected - ebSizeCoveredSoFar;
        sizeActuallyToBeWritten = (sizeActuallyToBeWritten < sizeLeftToWrite) ?
                                  sizeActuallyToBeWritten : sizeLeftToWrite;

        if (0 == sizeActuallyToBeWritten)
        { // ran out of available data

            // now, further filling out will actually be done by the Engine, we'll
            // just direct it to memory within entity body;
            //
            // this means a change of states
            //
            internalState = IS_CONTINUING_TO_FILL_OUT_ENTITY_BODY;

            return;
        }

        // memory comes from different sources, so we can copy it without fear of
        // overlaps

        oscl_memcpy(static_cast<char*>(entityBody[ebCurrentIndex].ptr) + ebCurrentOffset,
                    mainBufferEntry,
                    sizeActuallyToBeWritten
                   );

        ebCurrentOffset += sizeActuallyToBeWritten;
        mainBufferEntry += sizeActuallyToBeWritten;

        ebSizeCoveredSoFar += sizeActuallyToBeWritten;

    }

    // at this point, either we covered the whole thing, or we didn't

    // if we covered the whole thing, we jumped out from the loop according to
    // the loop invariant
    //
    // if, however, we did not fill out the whole thing, then we have already
    // returned from within the loop, switching to hungry state
    //
    // in other words, at this point we could ONLY succeed

    eorptr = mainBufferEntry;

    internalState = IS_ENTITY_BODY_IS_READY;
}

OSCL_EXPORT_REF bool
RTSPParser::registerEmbeddedDataMemory(RTSPEntityBody * newBody)
{
    if (IS_WAITING_FOR_EMBEDDED_DATA_MEMORY != internalState)
    {
        return false;
    }

    entityBody = newBody;

    if (NULL == entityBody)
    {
        internalState = IS_SKIPPING_OVER_EMBEDDED_DATA;
        ebSizeCoveredSoFar = 0;
        ebCurrentIndex = 0;
        ebCurrentOffset = 0;
    }
    else
    {
        internalState = IS_STARTING_TO_FILL_OUT_EMBEDDED_DATA;
        ebSizeCoveredSoFar = 0;
        ebCurrentIndex = 0;
        ebCurrentOffset = 0;
    }

    continueProcessing();

    return true;
}

void
RTSPParser::skipOverEmbeddedData()
{
    uint32 sizeNeededToSkip = ebFullSizeExpected - ebSizeCoveredSoFar;
    uint32 sizePossibleToSkip = mainBufferSpace - mainBufferEntry;

    uint32 sizeSkipped = (sizeNeededToSkip < sizePossibleToSkip) ?
                         sizeNeededToSkip : sizePossibleToSkip;

    ebSizeCoveredSoFar += sizeSkipped;
    mainBufferEntry += sizeSkipped;

    if (ebSizeCoveredSoFar == ebFullSizeExpected)
    {
        eorptr = mainBufferEntry;
        internalState = IS_WAITING_FOR_REQUEST_MEMORY;
    }
}

void
RTSPParser::startFillingOutEmbeddedData()
{
    while (ebFullSizeExpected != ebSizeCoveredSoFar)
    {    // find an appropriate fragment
        if (entityBody[ebCurrentIndex].len == ebCurrentOffset)
        {
            ++ebCurrentIndex;
            ebCurrentOffset = 0;
            continue;
        }

        uint32 fragmentSizeAvailable = entityBody[ebCurrentIndex].len - ebCurrentOffset;

        // decide, which is smaller - size left in the main buffer, or size left
        // in the current target buffer
        //
        uint32 sizeAvailableForWriting = mainBufferSpace - mainBufferEntry;
        uint32 sizeActuallyToBeWritten =
            (fragmentSizeAvailable < sizeAvailableForWriting) ?
            fragmentSizeAvailable : sizeAvailableForWriting;

        // now decide, which is smaller - the previous result or the size actually
        // left to fill out
        uint32  sizeLeftToWrite = ebFullSizeExpected - ebSizeCoveredSoFar;
        sizeActuallyToBeWritten = (sizeActuallyToBeWritten < sizeLeftToWrite) ?
                                  sizeActuallyToBeWritten : sizeLeftToWrite;

        if (0 == sizeActuallyToBeWritten)
        { // ran out of available data

            // now, further filling out will actually be done by the Engine, we'll
            // just direct it to memory within entity body;
            //
            // this means a change of states
            //
            internalState = IS_CONTINUING_TO_FILL_OUT_EMBEDDED_DATA;

            return;
        }

        // memory comes from different sources, so we can copy it without fear of
        // overlaps

        oscl_memcpy(static_cast<char*>(entityBody[ebCurrentIndex].ptr) + ebCurrentOffset,
                    mainBufferEntry,
                    sizeActuallyToBeWritten
                   );

        ebCurrentOffset += sizeActuallyToBeWritten;
        mainBufferEntry += sizeActuallyToBeWritten;

        ebSizeCoveredSoFar += sizeActuallyToBeWritten;

    }

    // at this point, either we covered the whole thing, or we didn't

    // if we covered the whole thing, we jumped out from the loop according to
    // the loop invariant
    //
    // if, however, we did not fill out the whole thing, then we have already
    // returned from within the loop, switching to hungry state
    //
    // in other words, at this point we could ONLY succeed

    eorptr = mainBufferEntry;

    internalState = IS_EMBEDDED_DATA_IS_READY;
}

OSCL_EXPORT_REF void
RTSPParser::flush(void)
{
    internalState = IS_WAITING_FOR_REQUEST_MEMORY;
    mainBufferEntry = mainBuffer;
    mainBufferSpace = mainBuffer;
    mainBufferSizeUsed = 0;
    eorptr = mainBuffer;
    mainBuffer[ RTSP_PARSER_BUFFER_SIZE ] = CHAR_NULL;
    mainBuffer[ RTSP_PARSER_BUFFER_SIZE+1 ] = CHAR_NULL;
}

void
RTSPParser::dealWithLineContinuations(RTSPIncomingMessage * theStruct)
{
    char * cPtr;      // current pointer
    char * nlEnd;     // new-line end pointer
    char * finishPtr; // end-of-buffer pointer

    finishPtr = theStruct->secondaryBuffer + theStruct->secondaryBufferSizeUsed;

    for (cPtr = theStruct->secondaryBuffer;
            cPtr < finishPtr;
        )
    {
        // check if this point is suspicious
        //
        if (CHAR_CR == *cPtr)
        {
            if (cPtr < finishPtr - 1      // there's room for CR-LF
                    &&  CHAR_LF == *(cPtr + 1)  // next char is LF
               )
            {
                nlEnd = cPtr + 1;

                // newline, CR-LF
            }
            else
            {
                nlEnd = cPtr;

                // newline, CR
            }
        }
        else if (CHAR_LF == *cPtr)
        {
            nlEnd = cPtr;

            // newline, LF
        }
        else
        { // not a newline
            ++cPtr;
            continue;
        }

        // it was a newline

        // now, is there a whitespace after it?
        if (nlEnd >= finishPtr      // could be no room for a whitespace
                || (CHAR_SPACE != *(nlEnd + 1)      // could be a non-whitespace
                    &&  CHAR_TAB   != *(nlEnd + 1)
                   )
           )
        {
            cPtr = nlEnd + 1;
            continue;
        }

        char * sPtr;
        for (sPtr = nlEnd + 1;
                sPtr < finishPtr
                && (CHAR_SPACE == *sPtr
                    ||  CHAR_TAB   == *sPtr
                   );
                ++sPtr
            )
        { // nothing
            ;;;;
        }

        size_t sizeToMove = finishPtr - sPtr + 1;
        size_t sizeCut = sPtr - cPtr - 1;

        // set a space
        * cPtr = CHAR_SPACE;

        // move the rest, careful with overlaps
        oscl_memmove(cPtr + 1, sPtr, sizeToMove);

        // reset the length
        theStruct->secondaryBufferSizeUsed -= sizeCut;

        // reiterate
        finishPtr -= sizeCut;
        ++cPtr; // safe enough, still one whitespace has been written

        continue;
    }
}


void
RTSPParser::dealWithFieldRepetitions(RTSPIncomingMessage * theStruct)
{
    char * cPtr;
    char * finishPtr =
        theStruct->secondaryBuffer + theStruct->secondaryBufferSizeUsed;
    char * nlEnd;

    for (cPtr = theStruct->secondaryBuffer;
            cPtr < finishPtr;
            ++cPtr
        )
    {
        if (CHAR_CR == *cPtr)
        {
            if (cPtr < finishPtr - 1
                    &&  CHAR_LF == *(cPtr + 1)
               )
            {
                nlEnd = cPtr + 1;
            }
            else
            {
                nlEnd = cPtr;
            }
        }
        else if (CHAR_LF == *cPtr)
        {
            nlEnd = cPtr;
        }
        else
        {
            continue;
        }

        char * fieldNamePtr = nlEnd + 1;

        if (fieldNamePtr >= finishPtr
                ||  CHAR_CR     == *fieldNamePtr
                ||  CHAR_LF     == *fieldNamePtr
                ||  CHAR_SPACE  == *fieldNamePtr
                ||  CHAR_TAB    == *fieldNamePtr
           )
        {
            // not a field, an end of message or an empty string

            continue;
        }

        // we have a field
    }
}


void
RTSPParser::lookForResync()
{
    bool found = false;

    for (; eorptr < mainBufferSpace - 1; ++eorptr)
    {
        if (CHAR_LF == *eorptr || CHAR_CR == *eorptr)
        {
            if (*eorptr == *(eorptr + 1))
            {
                found = true;
                eorptr += 2;
                break;
            }
            else if ((eorptr <= mainBufferSpace - 4)
                     && (CHAR_CR == *(eorptr)) && (CHAR_LF == *(eorptr + 1))
                     && (CHAR_CR == *(eorptr + 2)) && (CHAR_LF == *(eorptr + 3))
                    )
            {
                found = true;
                eorptr += 4;
                break;
            }
        }
    }

    if (found)
    {
        mainBufferEntry = eorptr;

        if (mainBufferEntry == mainBufferSpace)
        {
            mainBufferEntry = mainBuffer;
            eorptr = mainBufferEntry;
            mainBufferSpace = mainBufferEntry;
        }

        internalState = IS_WAITING_FOR_REQUEST_MEMORY;

        continueProcessing();
    }
    else
    {
        int sizeToMove = (RTSP_RESYNC_PRESERVE_SIZE < (mainBufferSpace - mainBufferEntry)) ?
                         RTSP_RESYNC_PRESERVE_SIZE : (mainBufferSpace - mainBufferEntry);

        oscl_memmove(mainBuffer, mainBufferSpace - sizeToMove, sizeToMove);
        mainBufferEntry = mainBuffer;
        mainBufferSpace = mainBufferEntry + sizeToMove;
        eorptr = mainBuffer;
    }

    return;
}
