diff -r 000000000000 -r 72b543305e3a email/imap4mtm/imapsession/src/cimapfetchbodystructurebase.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/imap4mtm/imapsession/src/cimapfetchbodystructurebase.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,412 @@ +// Copyright (c) 2006-2009 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: +// + +#include "cimapfetchbodystructurebase.h" +#include "cimapfetchresponse.h" +#include "cimapbodystructurebuilder.h" +#include "cimaprfc822headerfieldsparser.h" +#include "moutputstream.h" +#include "cimapsessionconsts.h" +#include "imappaniccodes.h" +#include "cimaplogger.h" + +_LIT8(KCommandFetch, "%d UID FETCH %S (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (%S)])\r\n"); +const TInt KCommandFetchFormatLength=6; + +CImapFetchBodyStructureBase::CImapFetchBodyStructureBase(CImapFolderInfo* aSelectedFolderInfo, TInt aLogId, const TDesC8& aHeaderFields) + : CImapCommand(aSelectedFolderInfo, aLogId) + , iHeaderFields(aHeaderFields) + , iState(EStateDataItemLine) + { + } + +CImapFetchBodyStructureBase::~CImapFetchBodyStructureBase() + { + delete iBodyStructureBuilder; + delete iHeaderFieldsParser; + } + +/** +@param aTagId Command sequence id which will be send along with the IMAP command. +@param aStream A wrapper for the outbound stream of a connected socket, using which +the command will be send to the server +*/ +void CImapFetchBodyStructureBase::SendMessageL(TInt aTagId, MOutputStream& aStream) + { + iTagId = aTagId; + + TInt bufLen = KCommandFetch().Length() - KCommandFetchFormatLength; + bufLen += TagLength(aTagId); + bufLen += iSequenceSet->Length(); + bufLen += iHeaderFields.Length(); + + __ASSERT_DEBUG(iOutputBuffer==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandOutputBufferNotNull)); + iOutputBuffer = HBufC8::NewL(bufLen); + iOutputBuffer->Des().Format(KCommandFetch, aTagId, iSequenceSet, &iHeaderFields); + + //send the command to the server + aStream.SendDataReq(*iOutputBuffer); + } + +/** +@param aData Will contain a single line of response from the server for LOGIN command without \r\n. +@return will be any one of this + 1) If the next expected chunk is a literal block, ParseMessageL() will return the size of the block it expects. + 2) If the next expected chunk is a line, ParseMessageL() will return 0, and Result() will return EImapResponseNone. + 3) If no further data is expected (e.g. the OK or error tag has been received) then ParseMessageL() will return 0, + and Result() will return one of EImapResponseOk, EImapResponseNo or EImapResponseBad. +*/ +CImapCommand::TParseBlockResult CImapFetchBodyStructureBase::DoParseBlockL(const TDesC8& aData) + { + CImapCommand::TParseBlockResult resultCode(ECompleteUntagged); + + switch (iState) + { + case EStateDataItemLine: + { + // We are the beginning of a new response, so we can't have found any UID data items yet. + // So we need to reset the flag here. + iUidDataItemFoundInResponse = EFalse; + resultCode = ProcessStartL(); + + __ASSERT_DEBUG(iState == EStateDataItemLine, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState)); + + // If we get EResponseIncomplete then allow the current EStateDataItemLine state to + // drop through to ProcessDataItems() + // otherwise, EStateComplete will take us straight to the return. + if (resultCode != EResponseIncomplete) + { + iState = EStateComplete; + } + } + break; + case EStateBodyStructureLiteral: + case EStateBodyStructureLine: + { + ProcessBodyStructureL(aData); + resultCode = EResponseIncomplete; // always expect more data after bodystructure. + } + break; + case EStateHeaderFieldsLiteral: + { + ProcessHeaderFieldsL(aData); + resultCode = EResponseIncomplete; // always expect more data after header field literal. + } + break; + case EStateFetchNextDataItemLine: + { + // Fetch is over. Get ready to process next data item. + iUnparsedData.Set(aData); + if(GetAndStoreNextPart()) + { + iState = EStateDataItemLine; + } + } + break; + default: + { + // unexpected state + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState)); + } + } + + // The ProcessXxxL() methods above can change the state. + // Now we need to check if there are data items to process... + if (iState == EStateDataItemLine) + { + resultCode = ProcessDataItemsL(); + } + else if (iState == EStateComplete) + { + // this response is complete, so the next data (if any) will be a data item line, + iState = EStateDataItemLine; + } + + // For complete untagged responses, check whether the UID data item was received. + // If it was, then this was a genuine response to the UID FETCH command. + // If it was not received, then this was an unsolicited server event, and the data should be discarded. + if (resultCode == ECompleteUntagged) + { + if (iUidDataItemFoundInResponse) + { + // Genuine UID FETCH response - copy UID and FLAG info into the response object + iFetchResponse->SetMessageFlagInfo(iMessageFlagInfo); + + // Note: iUidDataItemFoundInResponse is NOT reset here as its value is needed by CImapFetchMultiBodyStructures::DoParseBlockL() + // Instead iUidDataItemFoundInResponse is set to EFalse just prior to calling ProcessStartL() - i.e. at the start of a new response. + } + else + { + // Record that an unsolicited FETCH was received - so that a sync will be triggered after this command. + __LOG_TEXT(iLogId, "CImapFetchBodyStructureBase::DoParseBlockL() - Found unsolicited FETCH FLAGS"); + SetMessageFlagsChanged(); + } + } + + return resultCode; + } + +CImapCommand::TParseBlockResult CImapFetchBodyStructureBase::ProcessStartL() + { + TParseBlockResult result = ENotRecognised; + + TInt tagId = 0; + TTagType tagged = GetTagTypeL(tagId); + switch(tagged) + { + case ETagged: + { + // Check the tag id + if (tagId != iTagId) + { + //Unexpected Tagid + CorruptDataL(); + } + + // Fetch and check the response code + iResponseCode = GetResponseStateCode(); + if (iResponseCode == EImapResponseNone) + { + // Was expecting one of OK/NO/BAD, but didn't get it. This is a parse error. + CorruptDataL(); + } + + result = ECompleteTagged; + } + break; + case EUntagged: + { + // Is this a FETCH response? + // Check for Sequence Number followed by "FETCH" + + TPtrC8 part1 = GetNextPart(); // returns KNullDesC8 if there is no part available + TPtrC8 part2 = GetNextPart(); // returns KNullDesC8 if there is no part available + + // Is part1 a Sequence Number? + TInt sequenceNumber = 0; + TLex8 lex(part1); + if (lex.Val(sequenceNumber) == KErrNone) + { + // part1 is a Sequence Number. Now check part2 - is it "FETCH"? + + if(part2.CompareF(KImapTxtFetch) == 0) + { + if (GetAndStoreNextPart()) + { + if (iCurrentPart[0] == '(') + { + iCurrentPart.Set(iCurrentPart.Mid(1)); + } + else + { + // was expecting a bracket, got something else + CorruptDataL(); + } + } + else + { + // was expecting a bracket, got nothing + CorruptDataL(); + } + + result = EResponseIncomplete; + } + } + } + break; + case EContinuation: + default: + { + CorruptDataL(); + } + break; + } + + // result will be ENotRecognised if tagged not found or untagged FETCH not found. + return result; + } + +CImapCommand::TParseBlockResult CImapFetchBodyStructureBase::ProcessDataItemsL() + { + CImapCommand::TParseBlockResult resultCode = EResponseIncomplete; + + TBool foundPart = ETrue; + while (iState == EStateDataItemLine && foundPart) + { + if (iCurrentPart.CompareF(KImapTxtUid) == 0) + { + ProcessUidL(); + } + else if (iCurrentPart.CompareF(KImapTxtFlags) == 0) + { + ProcessFlagsL(); + } + else if (iCurrentPart.CompareF(KImapTxtBodyStructure) == 0) + { + ProcessBodyStructureL(); + } + else if (iCurrentPart.CompareF(KImapTxtBodyHeaderFields) == 0) + { + ProcessHeaderFieldsL(); + } + // Ignore anything else at the moment until we reach + // a valid dataItem on the next line. + + + // Only fetch the next part if we're still searching for data items. + if (iState == EStateDataItemLine) + { + foundPart = GetAndStoreNextPart(); + } + } + + if (!foundPart && iState == EStateDataItemLine) + { + resultCode = ECompleteUntagged; + } + + return resultCode; + } + +void CImapFetchBodyStructureBase::ProcessFlagsL() + { + iUnparsedData.Set(iMessageFlagInfo.ParseFlagsL(iUnparsedData)); + } + +void CImapFetchBodyStructureBase::ProcessUidL() + { + if (GetAndStoreNextPart()) + { + TInt err = iMessageFlagInfo.SetMessageUid(iCurrentPart); + if (err == KErrNone) + { + iUidDataItemFoundInResponse = ETrue; + } + else + { + // expected iCurrentPart to be a number representing a UID. + // but we did not get a number. + CorruptDataL(); + } + } + } + +void CImapFetchBodyStructureBase::ProcessBodyStructureL() + { + iBodyStructureBuilder = CImapBodyStructureBuilder::NewL(*iFetchResponse, iLogId); + + ProcessBodyStructureL(iUnparsedData); + } + +void CImapFetchBodyStructureBase::ProcessBodyStructureL(const TDesC8& aData) + { + TBool needsMore = iBodyStructureBuilder->ProcessBlockL(aData); + + if (iState == EStateBodyStructureLiteral) + { + // Bodystructure should always request a line after a literal + __ASSERT_DEBUG(needsMore, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureExpectedRequestForLine)); + + iState = EStateBodyStructureLine; + } + else if (needsMore) + { + // Check the previous state was one we expected + __ASSERT_DEBUG(iState == EStateDataItemLine || iState == EStateBodyStructureLine, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState)); + + iState = EStateBodyStructureLiteral; + } + else + { + // Check the previous state was one we expected + __ASSERT_DEBUG(iState == EStateDataItemLine || iState == EStateBodyStructureLine, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState)); + + iState = EStateDataItemLine; + + iUnparsedData.Set(iBodyStructureBuilder->UnparsedData()); + + delete iBodyStructureBuilder; + iBodyStructureBuilder = NULL; + } + } + +void CImapFetchBodyStructureBase::ProcessHeaderFieldsL() + { + // Skip past the field titles + while (GetAndStoreNextPart()) + { + if (iCurrentPart[iCurrentPart.Length() - 1] == ']') + { + break; + } + } + + iHeaderFieldsParser = CImapRfc822HeaderFieldsParser::NewL(*iFetchResponse, iLogId); + + return ProcessHeaderFieldsL(iUnparsedData); + } + +void CImapFetchBodyStructureBase::ProcessHeaderFieldsL(const TDesC8& aData) + { + TBool needsMore = iHeaderFieldsParser->ProcessBlockL(aData); + + if (iState == EStateDataItemLine) + { + // Always expect a literal for the Header Fields data item + if (!needsMore) + { + CorruptDataL(); + } + + iState = EStateHeaderFieldsLiteral; + } + else if (iState == EStateHeaderFieldsLiteral) + { + // Parser should not be expecting more data after a literal + if (needsMore) + { + CorruptDataL(); + } + + iState = EStateFetchNextDataItemLine; + + iUnparsedData.Set(iHeaderFieldsParser->UnparsedData()); + __ASSERT_DEBUG(iUnparsedData.Length() == 0, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnparsedDataExists)); + + delete iHeaderFieldsParser; + iHeaderFieldsParser = NULL; + } + else + { + // Recovery from this would depend on the state + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState)); + } + } + +/** +Move to the next part +@return whether a part was found +*/ +TBool CImapFetchBodyStructureBase::GetAndStoreNextPart() + { + iCurrentPart.Set(GetNextPart()); + return (iCurrentPart.Length() > 0) ? ETrue : EFalse; + } + + +TBool CImapFetchBodyStructureBase::UidDataItemFoundInResponse() + { + return iUidDataItemFoundInResponse; + }