Msrp/MsrpServer/src/CMSRPMsgParser.cpp
author Petteri Saari <petteri.saari@digia.com>
Thu, 25 Nov 2010 13:59:42 +0200
branchMSRP_FrameWork
changeset 58 cdb720e67852
parent 25 505ad3f0ce5c
child 60 7634585a4347
permissions -rw-r--r--
This release addresses the following issues: 1. The crash bug fix when receiving file 2. Now the sending is based on MSRP messages, there is no longer file receiving or sending. Client sends data as MSRP was designed. 3. Soma MSRP stack was created so that the client told the correct session-id, Symbian stack generated it by itself. This is not allowed, it was changed so that clients tell the session-id (same as used in SIP INVITE). 4. Unnecessary division of data to chunks removed when there is no need to interrupt sending. The message is sent in as few chunks as possible. 5. Stack can now receive files and chunks with ?unlimited? size. Old stack wrote the incoming data to memory and did not utilize disk space until the end of chunk was reached (large chunks from another client crashed it). 6. Now when writing the incoming data to file, it will take into account the byte-range header values. So, this complies with the RFC4975 requirements that stack must be able to handle chunks that come in any sequence. 7. Some buffering changes to outgoing/incoming data. 8. The outgoing data is now checked that it does not contain the created transaction-id before sending the data. 9. MSRP success reports are now implemented and tested against servers. 10. Progress report system fixed so progress is now visible on client (all the way to 100%). 11. Message Cancel receiving / Cancel sending now corrected and made to work as rfc4975 requires. (termination from sender and error code from receiver when cancelling). 12. Bug correction related to messages received not belonging to any session, old stack implementation did send error response, but after response was written it did give the buffer to client anyway. Now corrected.

/*
* Copyright (c) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html."
* Initial Contributors:
* Nokia Corporation - initial contribution.
* Contributors:
*
* Description:
* MSRP Implementation
*
*/

// INCLUDES

// CLASS HEADER
#include "CMSRPMsgParser.h"
#include "MSRPCommon.h"

#include "CMSRPMessage.h"
#include "CMSRPToPathHeader.h"

// -----------------------------------------------------------------------------
// CMSRPMsgParser::NewL
// Static constructor
// -----------------------------------------------------------------------------
//
/*static*/CMSRPMsgParser* CMSRPMsgParser::NewL(MMSRPParserObserver& aConnection)
    {
    MSRPLOG( "CMSRPMsgParser::NewL enter" )
    CMSRPMsgParser* self = new (ELeave) CMSRPMsgParser(aConnection );
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    MSRPLOG( "CMSRPMsgParser::NewL exit" )
    return self;
    }

// -----------------------------------------------------------------------------
// CMSRPMsgParser::CMSRPMsgParser
// Constructor
// -----------------------------------------------------------------------------
//
CMSRPMsgParser::CMSRPMsgParser(MMSRPParserObserver& aConnection)        
    : iConnection( aConnection )
    {  
    
    }

// -----------------------------------------------------------------------------
// CMSRPMsgParser::~CMSRPMsgParser
// Destructor
// -----------------------------------------------------------------------------
//
CMSRPMsgParser::~CMSRPMsgParser()
    {
    MSRPLOG( "CMSRPMsgParser::~CMSRPMsgParser enter" )
    delete iLocalBuf;
    delete iEndToken;
    delete iMessage;
    iParseBuffers.ResetAndDestroy();
    iParseBuffers.Close();    
    iIncomingMessageChunks.ResetAndDestroy();
    iIncomingMessageChunks.Close();
    MSRPLOG( "CMSRPMsgParser::~CMSRPMsgParser exit" )
    }

// -----------------------------------------------------------------------------
// CMSRPMsgParser::ConstructL
// 2nd phase constructor
// -----------------------------------------------------------------------------
//
void CMSRPMsgParser::ConstructL()
    {
    iState = ETitleLine;
    //to avoid leak on newl leave
    RMsrpBuf buf(iBufPool.ProvideBufferL());
    iLocalBuf = new (ELeave) RMsrpBuf(buf);
    //iLocalBuf = new (ELeave) RMsrpBuf(iBufPool.ProvideBufferL());
    iEndToken = HBufC8::NewL(KMSRPEndTokenMax);   
    }

// -----------------------------------------------------------------------------
// CMSRPMsgParser::ParseL
// -----------------------------------------------------------------------------
//
TBool CMSRPMsgParser::ParseL()
    {
    TMatchType match = EFullMatch;
    TBool ret = TRUE;
    iByteRangeHeaderFound = EFalse;

    /*parse until a parse element spans buffers. 
      if parse element completes exactly at buffer boundaries,
      might end up parsing greater than bufsize+1_element in one runl. 
      to avoid, use transitioned flag (set in handle full match)*/
    while (match == EFullMatch) 
    //while (ret == TRUE)/*parse all parseable data*/
        {    
        MSRPLOG( "CMSRPMsgParser::ParseL enter" )    
        if(!iParseBuffers.Count())
		{
			MSRPLOG( "CMSRPMsgParser::ParseL exit" ) 
            return FALSE;
		}
                               
        TPtrC8 token(KCRAndLF());        
        if (iState == EBody)
            {
            token.Set(iEndToken->Des());                
            }   
        
        TInt matchPos( 0 );
        match = FindToken(iParseBuffers[0]->Ptr(),token,matchPos);    
        if(match == EFullMatch)
            {
            ret = HandleFullMatchL(token, matchPos);
            }
        else if (match == EPartialMatch)
            {
            ret = HandlePartialMatchL(token, matchPos);    
            }
        else //if (match == ENoMatch)        
            {              
            ret = HandleNoMatchL(token, matchPos);
            }
        }
    MSRPLOG( "CMSRPMsgParser::ParseL exit" )
    return ret;
    }

// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleFullMatchL
// -----------------------------------------------------------------------------
//
TBool CMSRPMsgParser::HandleFullMatchL(TPtrC8& aToken, TInt aMatchPos, TBool aCopyToLocal)
    {
    MSRPLOG( "CMSRPMsgParser::HandleFullMatchL enter" )
    
    TPtrC8 buf = iParseBuffers[0]->Ptr().Left(aMatchPos + aToken.Length());
    if(aCopyToLocal)
        {        
        iLocalBuf->Append(buf);
        }
    else
        {
        HandleStateL(buf, aMatchPos);
        }
    
    if((aMatchPos + aToken.Length()) != iParseBuffers[0]->Ptr().Length())
        {
        iParseBuffers[0]->MsrpMidTPtr(aMatchPos + aToken.Length());
        }
    else
        {
        //transitioned
        RMsrpBuf* headBuf = iParseBuffers[0];
        iParseBuffers.Remove(0);
        delete headBuf;
        if(!iParseBuffers.Count())
            return FALSE;
        }
    MSRPLOG( "CMSRPMsgParser::HandleFullMatchL exit" )
    return TRUE;
    }
    
    

// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandlePartialMatchL
// -----------------------------------------------------------------------------
//
TBool CMSRPMsgParser::HandlePartialMatchL(TPtrC8& aToken, TInt aCurBufMatchPos)
    {
    MSRPLOG( "CMSRPMsgParser::HandlePartialMatchL enter" )
    TBool ret = TRUE;
    TInt matchTokenLen = iParseBuffers[0]->Ptr().Length()- aCurBufMatchPos;
    TPtrC8 matchToken = aToken.Left(matchTokenLen);
    TPtrC8 remainingToken = aToken.Mid(matchTokenLen);
    TPtrC8 emptyToken;
    
    if(iParseBuffers.Count() < 2)
        {
        if (iState == EBody && aCurBufMatchPos)
            //if some bytes to return in body, return now (xcluding token part) rather than parse again 
            {            
            ret = HandleFullMatchL(emptyToken, aCurBufMatchPos);//parse true return nxt parse false
            }
        return FALSE;//buffer count greater than 0 but content size smaller than complete lexical element  
        }
    
    //Look to complete token
    TInt nxtBufMatchPos;
    TMatchType match;                      
    TPtrC8 headOfNxtBuf = iParseBuffers[1]->Ptr().Left(remainingToken.Length());                    
    match = FindToken(headOfNxtBuf, remainingToken, nxtBufMatchPos);                       
    
    if(nxtBufMatchPos!=0) //nxt buf does not complete token
        {
        if (iState != EBody)
            {
            User::LeaveIfError(KErrCorrupt);
            }
        //false partial alarm for body, dont retain token part
        //need to update curbuf as false tokenpart also needs to be returned
        aCurBufMatchPos += matchToken.Length();
        //ret = HandleFullMatchL(matchToken, aCurBufMatchPos);//parse true return true
        ret = HandleFullMatchL(emptyToken, aCurBufMatchPos);
        }
    else
        {
        if(match == EPartialMatch) //completes part token but bytes insufficient
            {
            if (iState == EBody && aCurBufMatchPos)
                {
                ret = HandleFullMatchL(emptyToken, aCurBufMatchPos);//parse true ret false                                   
                }            
            return FALSE;
            }
        else //complete token
            {  
            TBool addToLocal = FALSE;
            if(iState != EBody)
                {
                addToLocal = TRUE;                
                }

            ret = HandleFullMatchL(matchToken, aCurBufMatchPos, addToLocal);
            ret = HandleFullMatchL(remainingToken, nxtBufMatchPos, addToLocal);//if token.length() and matches state token upto length, transition state
            //ret cud be false, if nxtbuf exactly completes token

            if(addToLocal == TRUE)
                {
                HandleStateL(*iLocalBuf, aCurBufMatchPos+nxtBufMatchPos);//nxtBuf==0, just token
                iLocalBuf->Zero();
                }            
            }
        }
    MSRPLOG( "CMSRPMsgParser::HandlePartialMatchL exit" )
    return ret;
    }



// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleNoMatchL
// -----------------------------------------------------------------------------
//
TBool CMSRPMsgParser::HandleNoMatchL(TPtrC8& aToken, TInt aCurBufMatchPos)
    {
    MSRPLOG( "CMSRPMsgParser::HandleNoMatchL enter" )

    TPtrC8 emptyToken;    
    TBool ret = FALSE;
    
    aCurBufMatchPos = iParseBuffers[0]->Ptr().Length();
    
    if (iState == EBody /*&& aCurBufMatchPos*/)
        {        
        ret = HandleFullMatchL(emptyToken, aCurBufMatchPos);        
        }        
    else if(iParseBuffers.Count() >= 2)
        {
        //if header find token in nxt buf
        TInt nxtBufMatchPos;
        TMatchType match;                      
        match = FindToken(iParseBuffers[1]->Ptr(),aToken,nxtBufMatchPos);
        
        if(match == EFullMatch)
            {       
            ret = HandleFullMatchL(emptyToken, aCurBufMatchPos, TRUE);
            ret = HandleFullMatchL(aToken, nxtBufMatchPos, TRUE);
            HandleStateL(*iLocalBuf, aCurBufMatchPos+nxtBufMatchPos);
            iLocalBuf->Zero();
            }
        else if (iParseBuffers.Count()>2)
            {
            User::LeaveIfError(KErrCorrupt);        
            }
        //else count==2, bytes insufficient in nxt buf, return false        
        }
    MSRPLOG( "CMSRPMsgParser::HandleNoMatchL exit" )
    return ret;
    }

// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleStateL
// -----------------------------------------------------------------------------
//
void CMSRPMsgParser::HandleStateL(const TDesC8& aString, TInt aMatchPos)
    {
    MSRPLOG( "CMSRPMsgParser::HandleStateL enter" )
    switch(iState)
         {
         case ETitleLine:
             HandleTitleLineL(aString, aMatchPos);             
             break;
         
         case EToPath:
         case EFromPath:    
         //case EMandatoryHeader:
             HandleHeaderL(aString, aMatchPos);            
             break;
                          
         case EOptionalHeaders:
             HandleOptionalHeaderL(aString, aMatchPos);
             break;
                                      
         case EExtraCRLF:
             HandleXtraCrlfL(aString, aMatchPos);
             break;
             
         case EBody:
             HandleBodyL(aString, aMatchPos);
             break;             
             
         case EEndofEndLine:
             HandleEndofEndLineL(aString, aMatchPos);
             break;
                          
         default:
             User::LeaveIfError(KErrUnknown);
             break;        
         }   
    MSRPLOG( "CMSRPMsgParser::HandleStateL exit" )
    }


// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleTitleLineL
// -----------------------------------------------------------------------------
//
void CMSRPMsgParser::HandleTitleLineL(const TDesC8& aString, TInt /*aMatchPos*/)
    {
    MSRPLOG( "CMSRPMsgParser::HandleTitleLineL enter" )
    
    TPtrC8 msrp(aString.Left(KMSRP().Length()));
    if(msrp.Compare(KMSRP()))
        User::LeaveIfError(KErrCorrupt);
    if(aString[KMSRP().Length()] != ' ')
        User::LeaveIfError(KErrCorrupt);
    
    TPtrC8 trans_id = aString.Mid(KMSRP().Length()+1);    
    TInt pos = trans_id.Locate(MSRP_SPACE);
    /*if(pos == KErrNotFound)
        User::LeaveIfError(KErrCorrupt);*/    
    if (pos < KMSRPIDLenMin || pos > KMSRPIDLenMax)
        User::LeaveIfError(KErrCorrupt);           
    trans_id.Set(trans_id.Left(pos));
    
    TPtr8 ptr = iEndToken->Des();
    ptr.Zero();
    ptr.Append(KCRAndLF);
    ptr.Append(KDashLine());
    ptr.Append(trans_id);
    
    TPtrC8 method = aString.Mid(KMSRP().Length()+1+trans_id.Length()+1);
    pos = method.Locate(MSRP_SPACE);
    
    if(pos == KErrNotFound)
        {
        pos = method.Locate(MSRP_CARRIAGE);        
    
        if(pos == KErrNotFound)
            User::LeaveIfError(KErrCorrupt);
        
        method.Set(method.Left(pos));
                
        delete iMessage;
        iMessage = NULL;
         if (!method.Compare(KMSRPSend()))
             {
             iMessage = CMSRPMessageHandler::NewL(MMSRPIncomingMessage::EMSRPMessage);
             MSRPLOG2( "CMSRPMsgParser::HandleTitleLineL new message = %d", iMessage )
             }
         else if ( !method.Compare( KMSRPReport() ) )
             {
             iMessage = CMSRPMessageHandler::NewL( MMSRPIncomingMessage::EMSRPReport );
             MSRPLOG2( "CMSRPMsgParser::HandleTitleLineL new report = %d", iMessage )
             }
         else //extn
             {
             iMessage = CMSRPMessageHandler::NewL(MMSRPIncomingMessage::EMSRPNotDefined);
             MSRPLOG2( "CMSRPMsgParser::HandleTitleLineL new not defined = %d", iMessage )
             }        
        }
    else //response
        {
        TPtrC8 null;
        method.Set(method.Left(pos));
        delete iMessage;
        iMessage = NULL;
        iMessage = CMSRPMessageHandler::NewL(MMSRPIncomingMessage::EMSRPResponse);
        MSRPLOG2( "CMSRPMsgParser::HandleTitleLineL new response = %d", iMessage )
        iMessage->SetStatusOfResponseL(method, null);
        }    
    iMessage->SetTransactionId(trans_id);
    iState = EToPath;    
    MSRPLOG( "CMSRPMsgParser::HandleTitleLineL exit" )
    }

#if 0
// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleTitleLineL
// -----------------------------------------------------------------------------
//
void CMSRPMsgParser::HandleTitleLineL(const TDesC8& aString, TInt /*aMatchPos*/)
    {
    MSRPLOG( "CMSRPMsgParser::HandleTitleLineL enter" )
    TLex8 lex(aString);

    //MSRP
    lex.Mark();
    lex.SkipCharacters();
    if(lex.TokenLength() != 4)
        {   
        User::LeaveIfError(KErrCorrupt);
        }    
    TPtrC8 first_token = lex.MarkedToken();        
    if (first_token.Compare(KMSRP()))
        {
        User::LeaveIfError(KErrCorrupt);
        }
    
    //SP
    if(lex.Peek() != MSRP_SPACE)
    {
    User::LeaveIfError(KErrCorrupt);
    }
    lex.Inc();
    
    //trans-id
    lex.Mark();
    lex.SkipCharacters();    
    if (lex.TokenLength() < KMSRPIDLenMin || lex.TokenLength() > KMSRPIDLenMax)
        {
        User::LeaveIfError(KErrCorrupt);
        }
    
    //SP
    if(lex.Peek() != MSRP_SPACE)
    {
    User::LeaveIfError(KErrCorrupt);
    }
    lex.Inc();
    
    TPtrC8 trans_id = lex.MarkedToken();
    TPtr8 ptr = iEndToken->Des();
    ptr.Zero();
    ptr.Append(kEndMarker());
    ptr.Append(trans_id);
    
    //method
    lex.Mark();
    lex.SkipCharacters();
    if(lex.TokenLength() != 0)
        {   
        User::LeaveIfError(KErrCorrupt);
        }      
    TPtrC8 method = lex.MarkedToken();      
    
    //create msg
    //set trans_id
    if (!method.Compare(KMSRPSend()))
        {
        //type = ESend        
        //set type
        }
    else if (!method.Compare(KMSRPReport()))
        {
        //type = EReport        
        //set type
        }
    else //if 3 digit string
        { 
        //type = EResponse
        //set type
        //set status code
        
        //remaining SP comment\r\n
        /*ignore comment*/
                     
        }
    //else extn method
    
    //remaining \r\n
    //TODO: comment, extn method and check remaining bytes is as xpected
        
    iState = EToPath;
    
    MSRPLOG( "CMSRPMsgParser::HandleTitleLineL exit" )
    }
#endif

// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleHeaderL
// -----------------------------------------------------------------------------
//
void CMSRPMsgParser::HandleHeaderL(const TDesC8& aString, TInt /*aMatchPos*/)
    {
    MSRPLOG( "CMSRPMsgParser::HandleHeaderL enter" )
    TInt pos = aString.Locate(MSRP_COLON);
    if(pos == KErrNotFound)
        User::LeaveIfError(KErrCorrupt);
    
    TPtrC8 header_name = aString.Left(pos);
    TUint8 nxtChar = aString[pos+1];
       
    if(nxtChar != MSRP_SPACE)
        User::LeaveIfError(KErrCorrupt);
    
    TPtrC8 header_val = aString.Mid(pos+2); 
    header_val.Set(header_val.Left(header_val.Length()-2));
    
    if(!header_val.Length())
        User::LeaveIfError(KErrCorrupt);

    MMSRPMessageHandler::TMsrpHeaderType headerType = MMSRPMessageHandler::EHeaderUnknown;
    if(iState == EToPath)
        {        
        if(header_name.Compare(KMSRPToPath()))
            User::LeaveIfError(KErrCorrupt);
        headerType = MMSRPMessageHandler::EToPath;
        iState = EFromPath;
        }
    else if(iState == EFromPath)
        {
        headerType = MMSRPMessageHandler::EFromPath;
        if(header_name.Compare(KMSRPFromPath()))
            User::LeaveIfError(KErrCorrupt);  
        iState = EOptionalHeaders;
        }
    else //find header id
        {
        if(!header_name.Compare(KMSRPMessageID()))
            {            
            headerType = MMSRPMessageHandler::EMessageId;
            }
        else if(!header_name.Compare(KMSRPByteRange()))
            {
            iByteRangeHeaderFound = ETrue;
            headerType = MMSRPMessageHandler::EByteRange;
            }
        else if(!header_name.Compare(KMSRPSuccessReport()))
            {
            headerType = MMSRPMessageHandler::ESuccessReport;
            }            
        else if(!header_name.Compare(KMSRPFailureReport()))
            {
            headerType = MMSRPMessageHandler::EFailureReport;
            }            
        else if(!header_name.Compare(KMSRPStatus()))
            {
            headerType = MMSRPMessageHandler::EStatus;
            }
        else if(!header_name.Compare(KMSRPContentType()))
            {
            headerType = MMSRPMessageHandler::EContentType;
            iState = EExtraCRLF;             
            }           
        }      
    
    //message->add_header
    TPtrC8 fullHeader(aString);
    iMessage->AddHeaderL(headerType, header_val, fullHeader);
    if ( headerType == MMSRPMessageHandler::EMessageId )
        {
        // let's check if this message chunk belong to one
        // of the chunks already received. If so, combine
        // the messages
        CheckMessageChunkL( );
        }
    //TODO: //only for to-path from-path add_header error
    //switch iState = EBody, mode = EError //essentially start looking for end token
    //if mode is error don't issue callbacks, on transition from EndofEndLine,
    //delete message and switch to normal mode
    
    MSRPLOG( "CMSRPMsgParser::HandleHeaderL exit" )
        
    }
    
// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleOptionalHeaderL
// -----------------------------------------------------------------------------
//
void CMSRPMsgParser::CheckMessageChunkL( )
    {
    MSRPLOG( "-> CMSRPMsgParser::HandleHeaderL" )
    for ( TInt i = 0; i < iIncomingMessageChunks.Count(); i++ )
        {
        if ( iMessage->CheckMessageChunkL( *iIncomingMessageChunks[ i ] ) )
            {
            delete iMessage;
            iMessage = iIncomingMessageChunks[ i ];
            iIncomingMessageChunks.Remove( i );
            break;
            }
        }
    MSRPLOG( "<- CMSRPMsgParser::HandleHeaderL" )
    }

// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleOptionalHeaderL
// -----------------------------------------------------------------------------
//
void CMSRPMsgParser::HandleOptionalHeaderL(const TDesC8& aString, TInt aMatchPos)
    {
    MSRPLOG( "CMSRPMsgParser::HandleOptionalHeaderL enter" )
    TPtrC8 endTokenwoStartCrlf = (*iEndToken).Mid(KCRAndLF().Length()); 
    TPtrC8 fullToken(aString.Left(endTokenwoStartCrlf.Length()));
    if (fullToken.Compare(endTokenwoStartCrlf))
        {
        HandleHeaderL(aString,aMatchPos);
        return;
        }
    //iState = EEndOfEndLine;
    aMatchPos -= fullToken.Length();
    HandleEndofEndLineL(aString.Mid(fullToken.Length()),aMatchPos);
    MSRPLOG( "CMSRPMsgParser::HandleOptionalHeaderL exit" )
    }

// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleXtraCrlfL
// -----------------------------------------------------------------------------
//
void CMSRPMsgParser::HandleXtraCrlfL(const TDesC8& /*aString*/, TInt aMatchPos)
    {
    MSRPLOG( "CMSRPMsgParser::HandleXtraCrlfL enter" )
    if(aMatchPos)
        User::LeaveIfError(KErrCorrupt);
    iState = EBody;
    MSRPLOG( "CMSRPMsgParser::HandleXtraCrlfL exit" )
    }

// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleBodyL
// -----------------------------------------------------------------------------
//
void CMSRPMsgParser::HandleBodyL(const TDesC8& aString, TInt aMatchPos)
    {
    MSRPLOG( "CMSRPMsgParser::HandleBodyL enter" )
    TPtrC8 token = aString.Mid(aMatchPos);
    
    //if(!aString.Length())
     //   return;
    
    if(aMatchPos!=0)
        {
        TPtrC8 content(aString.Left(aMatchPos));
        if ( !iMessage->IsTransmissionTerminated() )
            {
            MSRPLOG2( "CMSRPMsgParser::HandleBodyL instance = %d", iMessage )
            iMessage->AddContentL( content, iByteRangeHeaderFound ); 
            iConnection.ReportReceiveprogressL( iMessage );
            }
        }

    //partial or full match
    if(token.Length())
        {   
        MSRPLOG( "CMSRPMsgParser::HandleBodyL enter partial/token" )
         
        //if(aMatchPos == 0)
            {
            //TPtrC8 firstPart = (*iEndToken).Left(token.Length());
            TPtrC8 lastPart = (*iEndToken).Right(token.Length());
            
            //if(firstPart == lastPart)
            /*Solns: set iFlagFirstPartMatch on first match and then transition                      
              false transition to endline and transition back to body on endline parse error
              add initial \r\n (disallowed in trans_id) to searchEndToken on transition to body - 
              for optional header, token has no init crlf*/                    
            
            
            
            if (!token.Compare(lastPart))
                {
                iState = EEndofEndLine;
                }
            /*else if(!token.Compare(firstPart)); 
            else User::LeaveIfError(KErrCorrupt);*/                                                        
            }
        //else //full token matches
        }
    MSRPLOG( "CMSRPMsgParser::HandleBodyL exit" )
    return; 
    }

// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleEndofEndLineL
// -----------------------------------------------------------------------------
//
void CMSRPMsgParser::HandleEndofEndLineL(const TDesC8& aString, TInt aMatchPos)
    {
    MSRPLOG2( "CMSRPMsgParser::HandleEndofEndLineL enter, char = %d", aString[0] )
    if(aMatchPos != 1)
        User::LeaveIfError(KErrCorrupt);
    
    MMSRPMessageHandler::TMsrpMsgEndStatus endType = MMSRPMessageHandler::EMessageEnd;  
    if(aString[0] == MSRP_END)
        endType = MMSRPMessageHandler::EMessageEnd;        
    else if (aString[0] == MSRP_CONTINUE)
        endType = MMSRPMessageHandler::EMessageContinues;
    else if (aString[0] == MSRP_TERMINATE)
        endType = MMSRPMessageHandler::EMessageTerminated;
    else
        User::LeaveIfError(KErrCorrupt);
    
    if ( !iMessage->IsTransmissionTerminated() )
        {
        MSRPLOG2( "CMSRPMsgParser::HandleEndofEndLineL instance = %d", iMessage )
        iMessage->EndOfMessageL( endType );
        TInt status = iConnection.ParseStatusL( iMessage, KErrNone );
        if ( endType == MMSRPMessageHandler::EMessageContinues &&
            status == MMSRPParserObserver::EParseStatusMessageHandled ) 
            {
            iIncomingMessageChunks.AppendL( iMessage );
            }
        else if ( status == MMSRPParserObserver::EParseStatusError )
            {
            delete iMessage;
            iMessage = NULL;
            }
        iMessage = NULL;
        }
        
    iState = ETitleLine;
    MSRPLOG( "CMSRPMsgParser::HandleEndofEndLineL exit" )    
    }


// -----------------------------------------------------------------------------
// CMSRPMsgParser::FindToken
// -----------------------------------------------------------------------------
//
CMSRPMsgParser::TMatchType CMSRPMsgParser::FindToken( const TDesC8& aString, const TDesC8& aToken, TInt& aMatchPos)
    {
    MSRPLOG( "CMSRPMsgParser::FindToken enter" )
    TMatchType ret = ENoMatch;
    aMatchPos = KErrNotFound;
    if(!aString.Length() || !aToken.Length())
        return ret;
    aMatchPos = aString.Find(aToken);
    
    if(aMatchPos == KErrNotFound)
        {
        TUint8 lastChar = aString[aString.Length()-1];
        TPtrC8 tempToken;
        tempToken.Set(aToken);
        
        TInt tokenPos = tempToken.LocateReverse(lastChar);
        while (tokenPos != KErrNotFound)
            {
            TPtrC8 headPartofToken = tempToken.Left(tokenPos+1);
            TPtrC8 tailPartofString = aString.Right(tokenPos+1);
             if(!headPartofToken.Compare(tailPartofString))
                 break;            
             tempToken.Set(tempToken.Left(tokenPos)); //if 0
             tokenPos = tempToken.LocateReverse(lastChar);
            }
        if(tokenPos != KErrNotFound)
            {
            ret = EPartialMatch;
            aMatchPos = aString.Length() - (tokenPos+1);
            }
        return ret;
        }    
    MSRPLOG( "CMSRPMsgParser::FindToken exit" )
    return EFullMatch;    
    }

//test cases
//shyamprasad, prasad, xyz, add, dan, shyamprasad
//pr, prasad

#if 0
// -----------------------------------------------------------------------------
// CMSRPMsgParser::ExecuteStateL
// -----------------------------------------------------------------------------
//
CMSRPMsgParser::TDataCheck CMSRPMsgParser::ExecuteStateL()
    {         
    TDataCheck ret = EWaitForDataPost; 
    
    switch(iState)
        {
        case ETitleLine:
            ret = HandleTitleLineL();
            break;
        
        case EToPath:
        case EFromPath:    
        case EMandatoryHeader:
            ret = HandleMandatoryHeaderL();            
            break;
            
            
        case EOptionalHeaders:
            //ret = HandleOptionalHeaderL();
            break;
            
            
        case EBody:
            
            break;
            
        default:
            User::LeaveIfError(KErrUnknown);
            break;        
        }    
      return ret;     
    }

// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleTitleLineL
// -----------------------------------------------------------------------------
//
CMSRPMsgParser::TDataCheck CMSRPMsgParser::HandleTitleLineL()
    {
    TDataCheck ret;
    
    VARS(lex, transitioned, base_offset, lex_bytesdone);
    SET_LEX(lex);
        
    TOKEN(first_token);            
    ret = GetNextToken(lex, transitioned, first_token, base_offset, lex_bytesdone);
    RET_CHECK(ret,lex,MSRP_SPACE);//if transitioned, set mode local, being done above based on local buf length
    LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);
    
    
    if(first_token.iLength == 4)
        {        
        FETCH_TOKENSTRING(first_string, first_token, transitioned);
        if (first_string.Compare(KMSRP()))
            {
            User::LeaveIfError(KErrCorrupt);
            }        
        }        
    else
        {
        User::LeaveIfError(KErrCorrupt);
        }
    
    TOKEN(trans_id);
    ret =  GetNextToken(lex, transitioned, trans_id, base_offset, lex_bytesdone);
    RET_CHECK(ret,lex,MSRP_SPACE);
    LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);
    
    if (trans_id.iLength < KMSRPIDLenMin || trans_id.iLength > KMSRPIDLenMax)
        {
        User::LeaveIfError(KErrCorrupt);
        }
    
    TOKEN(method);
    ret =  GetNextToken(lex, transitioned, method, base_offset, lex_bytesdone);
    FETCH_TOKENSTRING(method_string, method, transitioned);
      
    if (!method_string.Compare(KMSRPSend()))
        {
        RET_CHECK(ret,lex,MSRP_CARRIAGE);
        LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);
        //create msg with correct type
        }
    else if (!method_string.Compare(KMSRPReport))
        {
        RET_CHECK(ret,lex,MSRP_CARRIAGE);
        LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);
        //create msg with correct type
        }
    else //if 3 digit string
        {        
        RET_CHECK_LENIENT(ret,lex,MSRP_SPACE,val);
        if (val)
            {
            LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);
            TOKEN(reason);
            ret =  GetNextToken(lex, transitioned, reason, base_offset, lex_bytesdone);
            }
        RET_CHECK(ret,lex,MSRP_CARRIAGE);
        LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);
        //create msg with correct type
        //set status code == method_string
        }
    //else extn method, TODO
    
    //set trans_id
        
    //EOL data sufficiency and increment
    ret = IsDataSufficient(lex, transitioned, base_offset, lex_bytesdone);
    RET_CHECK(ret,lex,MSRP_NEWLINE);
    LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);
             
    STATE_COMPLETE(EToPath, lex_bytesdone);    
    return ret;    
    }


// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleMandatoryHeaderL
// -----------------------------------------------------------------------------
//
CMSRPMsgParser::TDataCheck CMSRPMsgParser::HandleMandatoryHeaderL()
    {
    TDataCheck ret;    
    VARS(lex, transitioned, base_offset, lex_bytesdone);
    SET_LEX(lex);
        
    TOKEN(header);            
    ret = GetNextToken(lex, transitioned, header, base_offset, lex_bytesdone);
    RET_CHECK(ret,lex,MSRP_SPACE);
    LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);
    
    TOKEN(header_val);
    ret =  GetNextToken(lex, transitioned, header_val, base_offset, lex_bytesdone);
    RET_CHECK(ret,lex,MSRP_CARRIAGE);
    LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);

    //EOL data sufficiency and increment
    ret = IsDataSufficient(lex, transitioned, base_offset, lex_bytesdone);
    RET_CHECK(ret,lex,MSRP_NEWLINE);
    LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);
    
    FETCH_TOKENSTRING(header_string, header, transitioned);
    FETCH_TOKENSTRING(val_string, header_val, transitioned);
    
    //check last char is colon and reduce string len by 1
    //HEADER_CHECK(header_string);
            
    switch (iState)
        {
        case EToPath:
            if (header_string.Compare(KMSRPToPath()))
                {
                User::LeaveIfError(KErrCorrupt);
                }
            //AddHeader callback
            STATE_COMPLETE(EFromPath, lex_bytesdone);            
            break;
            
        case EFromPath:
            if (header_string.Compare(KMSRPFromPath()))
                {
                User::LeaveIfError(KErrCorrupt);
                }               
            //AddHeader callback
            STATE_COMPLETE(EMandatoryHeader, lex_bytesdone); 
            break;
            
        case EMandatoryHeader:
            //Findheaderid and give appropriate callback
            STATE_COMPLETE(EOptionalHeaders, lex_bytesdone);
            break;
            
        default:
            User::LeaveIfError(KErrUnknown);
            break;
        }       
    return ret; 
    }

// -----------------------------------------------------------------------------
// CMSRPMsgParser::HandleOptionalHeaderL
// -----------------------------------------------------------------------------
//
#if 0
CMSRPMsgParser::TDataCheck CMSRPMsgParser::HandleOptionalHeaderL()
    {
    //get first token compare against endline
    //if yes msg done and handover bodiless msg to conn
    //if not, get next token and give header callback
    
    TDataCheck ret;    
    VARS(lex, transitioned, base_offset, lex_bytesdone);
    SET_LEX(lex);
        
    TOKEN(header);            
    ret = GetNextToken(lex, transitioned, header, base_offset, lex_bytesdone);
    RET_CHECK(ret,lex,MSRP_SPACE);
    LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);
    
    TOKEN(header_val);
    ret =  GetNextToken(lex, transitioned, header_val, base_offset, lex_bytesdone);
    RET_CHECK(ret,lex,MSRP_CARRIAGE);
    LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);

    //EOL data sufficiency and increment
    ret = IsDataSufficient(lex, transitioned, base_offset, lex_bytesdone);
    RET_CHECK(ret,lex,MSRP_NEWLINE);
    LEX_INC_AVAILABLE(lex, lex_bytesdone, transitioned);
    
    FETCH_TOKENSTRING(header_string, header, transitioned);
    FETCH_TOKENSTRING(val_string, header_val, transitioned);
    
    //check last char is colon and reduce string len by 1
    HEADER_CHECK(header_string);
            
    switch (iState)
        {
        case EToPath:
            if (header_string.Compare(KMSRPToPath()))
                {
                User::LeaveIfError(KErrCorrupt);
                }
            //AddHeader callback
            STATE_COMPLETE(EFromPath, lex_bytesdone);            
            break;
            
        case EFromPath:
            if (header_string.Compare(KMSRPFromPath()))
                {
                User::LeaveIfError(KErrCorrupt);
                }               
            //AddHeader callback
            STATE_COMPLETE(EMandatoryHeader, lex_bytesdone); 
            break;
            
        case EMandatoryHeader:
            //Findheaderid and give appropriate callback
            STATE_COMPLETE(EOptionalHeaders, lex_bytesdone);
            break;
            
        default:
            User::LeaveIfError(KErrUnknown);
            break;
        }       
    return ret; 
    }

#endif

    
// -----------------------------------------------------------------------------
// CMSRPMsgParser::GetNextToken
// -----------------------------------------------------------------------------
//
//TBD: convert to macros
CMSRPMsgParser::TDataCheck CMSRPMsgParser::GetNextToken(TLex8& aLex, TBool& aTransitioned, TToken& aToken, TInt& aBaseOffset, TInt& aLexBytesDone)
    {
    TDataCheck ret = EContinueNormal;
    
    aLex.Mark();
    aToken.iOffset += aBaseOffset + aLex.MarkedOffset();
    do
        {
        aLex.SkipCharacters();        
        if (aLex.TokenLength() > 0)
            {    
            aLexBytesDone += aLex.TokenLength();
            aToken.iLength += aLex.TokenLength();
            TPtrC8 tokenString = aLex.MarkedToken();
            if(aTransitioned)
                {
                //TODO: leave or parse error if append not possible 
                iLocalBuf->Ptr().Append(tokenString);
                }                        
            }            
        ret = IsDataSufficient(aLex, aTransitioned, aBaseOffset, aLexBytesDone); 
        //if(ret == EContinueTransitioned) aBytesParsed = 0;        
        } while (ret == EContinueTransitioned);
        
    return ret;
    }


// -----------------------------------------------------------------------------
// CMSRPMsgParser::IsDataSufficient
// not called if in Body, only header data sufficiency check
// -----------------------------------------------------------------------------
//TBD: convert to macro
CMSRPMsgParser::TDataCheck CMSRPMsgParser::IsDataSufficient(TLex8& aLex, TBool& aTransitioned, TInt& aBaseOffset, TInt& aLexBytesDone)
    {
    if(aLex.Peek() == 0)
        {  
        /*iParseBuffers[0] being parsed*/
        aBaseOffset += iParseBuffers[0]->Ptr().Length();
        aLexBytesDone = 0;
        if(aTransitioned)
            {
            delete iParseBuffers[0];
            }
        else
            {
            if (iMode == EQueueBuf)
                {
                /*non collated buffer existence means spanning across buf pools*/  
                if (iParseBuffers.Count() < 2)
                    return EWaitForDataPost; //aLexBytesDone =0, nothing copied to local buf
                else if (iSthgParsed)
                    return EContinueInNextParse; //aLexBytesDone =0
                else
                    iLocalBuf->Ptr().Append(*iParseBuffers[0]);
                }    
            /*in case of localbuf, we are about to begin parsing first buf and copy tokens*/
            aTransitioned = TRUE;
            }
        
#if 0        
        /*for queuebuf avoid copying till bufpool spanning of a parse element can be confirmed*/
        if (iMode == EQueueBuf)
            {
            if (!aTransitioned)
                {
                /*non collated buffer existence means spanning across buf pools*/  
                if (iParseBuffers.Count() < 2)
                    return EWaitForDataPost; //aLexBytesDone =0, nothing copied to local buf
                else if (iSthgParsed)
                    return EContinueInNextParse; //aLexBytesDone =0
                else
                    {                    
                    //copy to local
                    aTransitioned = TRUE;
                    iLocalBuf->Ptr().Append(*iParseBuffers[0]);
                    aBaseOffset += iParseBuffers[0]->Ptr().Length();
                    aLexBytesDone = 0;
                    delete iParseBuffers[0];                    
                    }
                }
            else //once transitioned tokens are being copied,iParseBuffers[0] being parsed, hence no existence check            
                {
                //only free
                aBaseOffset += iParseBuffers[0]->Ptr().Length();
                aLexBytesDone = 0;
                delete iParseBuffers[0];                    
                }            
            }
        else //if (iMode == ELocalBuf)
            {
            if (!aTransitioned)
               {
               /*sthg parsed is always false for local buf*/
               /*also if not transitioned in this pass, means count of buffers in queue is atleast 1*/               
               /*if (iParseBuffers.Count() < 1)
                   return EWaitForDataPost;
               else if (iSthgParsed)
                   return EContinueInNextParse;*/
               //no action  
               aTransitioned = TRUE;
               aBaseOffset += iParseBuffers[0]->Ptr().Length();
               aLexBytesDone = 0;
               }
           else //once transitioned tokens are being copied,iParseBuffers[0] being parsed, hence no existence check
               {
               //only free  
               aBaseOffset += iParseBuffers[0]->Ptr().Length();
               aLexBytesDone = 0;
               //delete iParseBuffers[0];
               }                    
            }
#endif                       
        if(!iParseBuffers.Count())
            return EWaitForDataPost; //aLexBytesDone = 0 //no rbuf to delete
                                                        
        //reassign lex to zeroth
        aLex.Assign(*iParseBuffers[0]);
        
        return EContinueTransitioned;
        }
    return EContinueNormal;
    }
    
#endif