--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/email/imap4mtm/imapsession/src/cimapbodystructurebuilder.cpp Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,914 @@
+// 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 "cimapbodystructurebuilder.h"
+
+#include "cimapatom.h"
+#include "cimapatomwalker.h"
+#include "cimapatomparser.h"
+#include "cimapbodystructure.h"
+#include "cimapfetchresponse.h"
+#include "cimapsessionconsts.h"
+#include "cimapcommand.h"
+#include "imappaniccodes.h"
+
+
+CImapBodyStructureBuilder* CImapBodyStructureBuilder::NewL(CImapFetchResponse& aFetchResponse, TInt aLogId)
+// static method
+ {
+ CImapBodyStructureBuilder* self = new(ELeave)CImapBodyStructureBuilder(aFetchResponse, aLogId);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+CImapBodyStructureBuilder::CImapBodyStructureBuilder(CImapFetchResponse& aFetchResponse, TInt aLogId)
+ : iFetchResponse(aFetchResponse)
+ , iBodyStructureOwned(ETrue)
+ , iProcessBlockState(EWaitLine)
+ , iLogId(aLogId)
+ {}
+
+void CImapBodyStructureBuilder::ConstructL()
+ {
+ iAtomParser = CImapAtomParser::NewL(EFalse, iLogId);
+ iAtomWalker = CImapAtomWalker::NewL(iLogId);
+ }
+
+CImapBodyStructureBuilder::~CImapBodyStructureBuilder()
+ {
+ delete iAtomWalker;
+ delete iAtomParser;
+
+ // NOTE
+ //
+ // CImapBodyStructure is a tree data strucutre, where any CImapBodyStructure owns and
+ // is responssible for destroying its children.
+ //
+ // iBodyStructureStack[0] is the root bodystructure
+ // ownership of the which is usually passed to CImapFetchResponse object before we get here
+ // So destroying iBodyStructureStack[0] will cause all its children to be destroyed too.
+ //
+ // With the exception of the root bodystructure, iBodyStructureStack does not own any
+ // of the objects it points to, and MUST NOT destroy them.
+
+ if (iBodyStructureOwned)
+ {
+ if (iBodyStructureStack.Count() > 0)
+ {
+ // delete the root bodystructure.
+ delete iBodyStructureStack[0];
+ }
+ }
+ iBodyStructureStack.Close(); // And DO NOT destroy the data that is pointed to.
+ }
+
+/**
+Parses a block of incoming data from the session.
+ProcessBlockL() should be called repeatedly with more data until it returns EFalse to
+indicate that enough data has been received.
+This method parses the incoming data into an atom tree as the data is received.
+When the last block of data is received, the method will then parse the complete atom tree,
+populating iFetchResponse with a fully initialised CImapBodyStructure tree.
+@param aData either a line or literal block of data.
+@return Whether ProcessBlockL() expects to be called again with the next block of data from the session.
+*/
+TBool CImapBodyStructureBuilder::ProcessBlockL(const TDesC8& aData)
+ {
+ TBool wantMore = ETrue;
+ switch (iProcessBlockState)
+ {
+ case EWaitLine:
+ {
+ wantMore = iAtomParser->ProcessLineL(aData);
+
+ if (!wantMore)
+ {
+ iAtomWalker->SetRootL(iAtomParser->RootAtom());
+
+ // Get to the first "("
+ __ASSERT_ALWAYS(iAtomWalker->CurrentDes(EFalse).Length()==0, CImapCommand::CorruptDataL(iLogId));
+ __ASSERT_ALWAYS(iAtomWalker->PeekAcross() == NULL, CImapCommand::CorruptDataL(iLogId));
+
+ iAtomWalker->WalkDownL();
+ __ASSERT_ALWAYS(iAtomWalker->CurrentMatch(KImapTxtOpenBracket()), CImapCommand::CorruptDataL(iLogId));
+
+ iProcessBlockState = EParsing;
+ ParseLoopL();
+
+ TransferBufferOwnershipToFetchResponseL();
+ }
+ else
+ {
+ iProcessBlockState = EWaitLiteral;
+ }
+ }
+ break;
+ case EWaitLiteral:
+ {
+ iAtomParser->ProcessLiteralBlockL(aData);
+ iProcessBlockState = EWaitLine;
+ }
+ break;
+ default:
+ {
+ // This is an internal programming error.
+ __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EBodyStructureBuilderInvalidProcessBlockState));
+ wantMore = EFalse;
+ }
+ break;
+ }
+
+ return wantMore;
+ }
+
+/**
+Assigns the root bodystructure object, and its associated data to iFetchResponse.
+iFetchResponse takes ownership of the bodystructure and its data.
+*/
+void CImapBodyStructureBuilder::TransferBufferOwnershipToFetchResponseL()
+ {
+ // Check for internal programming errors.
+ __ASSERT_DEBUG(iBodyStructureOwned, TImapServerPanic::ImapPanic(TImapServerPanic::EBodyStructureBuilderRootNotOwned));
+ __ASSERT_DEBUG(iBodyStructureStack.Count() == 1, TImapServerPanic::ImapPanic(TImapServerPanic::EBodyStructureBuilderExpectedRootAtomOnlyOnStack));
+ __ASSERT_DEBUG(iBodyStructureStack[0] == iBodyStructure, TImapServerPanic::ImapPanic(TImapServerPanic::EBodyStructureBuilderCurrentAtomIsNotRoot));
+
+ // Prepare to transfer ownership of bodyStructureData from iAtomParser to iFetchResponse
+ // bodyStructureData will no longer be owned by iAtomParser
+ HBufC8* bodyStructureData = iAtomParser->DetachBuffer();
+
+ // Transfer ownership of iBodyStructure and bodyStructureData to iFetchResponse
+ iFetchResponse.SetBodyStructure(iBodyStructure, bodyStructureData);
+
+ // iBodyStructure is no longer owned by "this" CImapBodyStructureBuilder object.
+ iBodyStructureOwned = EFalse;
+ }
+
+/**
+Returns any data that was not parsed by ParseBlockL()
+ - i.e any data that follows the top level BODYSTRUCTURE.
+This will be a null string until ParseBlockL() has returned EFalse
+to indicate that it has finished parsing.
+When non-null, the returned pointer descriptor points into a section of the
+aData descriptor that was passed into ParseBlockL(). Consequently, it is
+only valid while the aData descriptor it points into is valid.
+@return a descriptor pointing to unparsed data.
+*/
+TPtrC8 CImapBodyStructureBuilder::UnparsedData()
+ {
+ return iAtomParser->UnparsedData();
+ }
+
+/**
+The main loop for parsing the bodystructure.
+The loop uses a state machine and bodystructure stack in order to handle
+embedded bodystructures without needing to use recursion.
+*/
+void CImapBodyStructureBuilder::ParseLoopL()
+ {
+ // Check for internal programming error
+ __ASSERT_DEBUG(iBodyStructureStack.Count() == 0, TImapServerPanic::ImapPanic(TImapServerPanic::EBodyStructureBuilderStackNotEmpty));
+
+ TParseStep parseStep = EParseNewBodyStructure;
+
+ while (parseStep != EParseComplete)
+ {
+ switch (parseStep)
+ {
+ case EParseNewBodyStructure:
+ {
+ // Either at the start of the root body structure,
+ // Or at the start of an embedded body structure.
+ PushNewBodyStructureL();
+ parseStep = ParseBodyStructureTypeL();
+ }
+ break;
+ case EParseBasic:
+ {
+ // Found a "basic" bodystructure - i.e. not text, rfc822 or multipart.
+ ParseBodyTypeBasicL();
+ ParseBodyExt1PartL();
+ parseStep = EParseSubStructureComplete;
+ }
+ break;
+ case EParseText:
+ {
+ // Found a "TEXT" body structure
+ ParseBodyTypeTextL();
+ ParseBodyExt1PartL();
+ parseStep = EParseSubStructureComplete;
+ }
+ break;
+ case EParseBodyTypeMessageRfc822:
+ {
+ // Found a "MESSAGE/RFC822" body structure.
+ // This contains an embedded bodystructure, so parse up to the structure,
+ // and then allow the loop to parse the embedded structure.
+ ParseBodyTypeMessageRfc822L();
+ parseStep = EParseNewBodyStructure;
+ }
+ break;
+ case EParseRemainderMessageRfc822:
+ {
+ // Just finished parsing the embedded bodystructure of a "MESSAGE/RFC822".
+ // Complete parsing the parent MESSAGE/RFC822 structure here.
+ ParseRemainderMessageRfc822L();
+ ParseBodyExt1PartL();
+ parseStep = EParseSubStructureComplete;
+ }
+ break;
+ case EParseRemainderMultipart:
+ {
+ // Just finished parsing the final embedded bodystructure of a MULTIPART structure.
+ // Complete parsing the parent MULTIPART structure here
+ ParseRemainderMultipartL();
+ parseStep = EParseSubStructureComplete;
+ }
+ break;
+ case EParseSubStructureComplete:
+ {
+ // Just finished parsing a bodystructure.
+ // If it is the root bodystructure then we are complete.
+ // Otherwise, let ParseSubStructureCompleteL() will find out whether it is
+ // * embedded in a MESSAGE/RFC822 structure - requiring the remainder to be parsed next: EParseRemainderMessageRfc822
+ // * embedded in a MULTIPART structure in which case
+ // > either there is another embedded structure next: EParseNewBodyStructure
+ // > or this is the last embedded structure, so we need to parse the multipart remainder: EParseRemainderMultipart
+
+ if (PopBodyStructureL())
+ {
+ // we were actually in the root, so we are fully complete.
+ parseStep = EParseComplete;
+ }
+ else
+ {
+ // we were in a genuine substructure, so need to walk up and decide what to do.
+ parseStep = ParseSubStructureCompleteL();
+ }
+ }
+ break;
+ default:
+ {
+ // This is an internal programming error.
+ __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EBodyStructureBuilderInvalidParseStep));
+ User::Leave(KErrGeneral); // avoid an infinite loop.
+ }
+ }
+ }
+ }
+
+/**
+When the parse loop comes across a new root or embedded bodystructure, it will use this method to...
+ > Create a new CImapBodyStructure object to represent the bodystructure
+ > Push the object onto the stack and make it "current"
+ > Associate an embedded bodystructure with its parent.
+
+*/
+void CImapBodyStructureBuilder::PushNewBodyStructureL()
+ {
+ // Going to create a new body structure.
+ // Need to be sure that something (either the stack root or its tree)
+ // is going to own and ultimatelty destroy it.
+ __ASSERT_DEBUG(iBodyStructureOwned, TImapServerPanic::ImapPanic(TImapServerPanic::EBodyStructureBuilderRootNotOwned));
+
+ CImapBodyStructure* bodyStructure = CImapBodyStructure::NewL();
+ CleanupStack::PushL(bodyStructure);
+
+ // root bodystructure is iBodyStructureStack[0]
+ if (iBodyStructure == NULL)
+ {
+ // Check for internal programming error
+ __ASSERT_DEBUG(iBodyStructureStack.Count() == 0, TImapServerPanic::ImapPanic(TImapServerPanic::EBodyStructureBuilderStackNotEmpty));
+
+ iBodyStructureStack.AppendL(bodyStructure);
+
+ // ownership is now transferred to the root bodystructure tree
+ CleanupStack::Pop(bodyStructure);
+ }
+ else
+ {
+ // Check for internal programming error
+ __ASSERT_DEBUG(iBodyStructureStack.Count() > 0, TImapServerPanic::ImapPanic(TImapServerPanic::EBodyStructureBuilderStackIsEmpty));
+
+ iBodyStructure->AppendEmbeddedBodyStructureL(*bodyStructure);
+
+ // ownership is now transferred to the root bodystructure tree
+ CleanupStack::Pop(bodyStructure);
+
+ iBodyStructureStack.AppendL(bodyStructure);
+ }
+
+ // This is now the bodystructure that we are parsing.
+ iBodyStructure = bodyStructure;
+ }
+
+/**
+Pops a bodystructure from the stack - except the root, which will not be popped.
+@return whether we were in the root already.
+*/
+TBool CImapBodyStructureBuilder::PopBodyStructureL()
+ {
+ TBool bRootAlready = EFalse;
+ TInt stackCount = iBodyStructureStack.Count();
+
+ if (stackCount > 1)
+ {
+ // Pop the bodystructure stack
+ --stackCount;
+ CImapBodyStructure* poppedBs = iBodyStructureStack[stackCount];
+ iBodyStructureStack.Remove(stackCount); // No need to destroy the bodystructure, as it is now owned by iBodyStructureStack[stackCount-1]
+
+ iBodyStructure = iBodyStructureStack[stackCount-1];
+ }
+ else
+ {
+ // Check for internal programming error: Not expecting a stack count of 0 or less
+ __ASSERT_DEBUG(stackCount == 1, TImapServerPanic::ImapPanic(TImapServerPanic::EBodyStructureBuilderExpectedRootAtomOnlyOnStack));
+ bRootAlready = ETrue;
+ }
+
+ return bRootAlready;
+ }
+
+/**
+Found the closing bracket of our substructure.
+Pop the stack, walk up and decide what to do next.
+If we've reached the root level, then parsing is complete.
+If our parent is a multipart, then check to see if we have a sibling.
+ If not then we have come to the end of our parent's structure too - handle this in a separate loop.
+If our parent is a Rfc822, then we need to parse the remainder of the Rfc822 structure.
+ If not then we have come to the end of our parent's structure too - handle this in a separate loop.
+@return
+*/
+CImapBodyStructureBuilder::TParseStep CImapBodyStructureBuilder::ParseSubStructureCompleteL()
+ {
+ TParseStep nextStep = EParseComplete;
+
+ iAtomWalker->WalkUpL();
+
+ if (iBodyStructure->BodyStructureType() == CImapBodyStructure::ETypeMultipart)
+ {
+ // Expecting either an open bracket for another bodystructure
+ // or the subtype.
+ CImapAtom* peekAcross = iAtomWalker->PeekAcross(); // peekAcross does not need to be destroyed as no ownership is transferred.
+ if (peekAcross == NULL)
+ {
+ // But not expecting "nothing".
+ CImapCommand::CorruptDataL(iLogId);
+ }
+
+ if (peekAcross->Match(KImapTxtOpenBracket()))
+ {
+ // position the atom walker on the open bracket, ready for ParseBodyStructureTypeL
+ // to walk down into it.
+ iAtomWalker->WalkAcrossL(ETrue);
+ nextStep = EParseNewBodyStructure;
+ }
+ else
+ {
+ // stay where we are, so that ParseRemainderMultipartL()
+ // can walk accross to the subtype, as any other Parse method would do
+ nextStep = EParseRemainderMultipart;
+ }
+ }
+ else if (iBodyStructure->BodyStructureType() == CImapBodyStructure::ETypeMessageRfc822)
+ {
+ // stay where we are, so that ParseRemainderMessageRfc822L()
+ // can walk accross to the body-fld-lines, as any other Parse method would do
+ nextStep = EParseRemainderMessageRfc822;
+ }
+ else
+ {
+ // No other bodystruct type has substructures.
+ // So getting here is an internal programming error.
+ __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EBodyStructureBuilderInvalidBodyStructureType));
+ User::Leave(KErrGeneral);
+ }
+
+ return nextStep;
+ }
+
+/**
+body = "(" (body-type-1part / body-type-mpart) ")"
+
+This method expects iAtomWalker to be positioned at the opening bracket of a body structure.
+The method works out the type of the bodystructure, and returns the appropriate next parse
+step to the parse loop.
+For multipart structures, iAtomWalker is left positioned at the opening bracket of the embedded structure.
+Foa all other structures, iAtomWalker is left positioned at the subtype field.
+
+@return The next step for the parse loop to take. This is one of
+ > EParseNewBodyStructure for MULTIPART structures
+ > EParseText for TEXT structures
+ > EParseBodyTypeMessageRfc822 for MESSAGE/RFC822
+ > EParseBasic for all other structures.
+*/
+CImapBodyStructureBuilder::TParseStep CImapBodyStructureBuilder::ParseBodyStructureTypeL()
+ {
+ // Start at the opening bracket
+ __ASSERT_ALWAYS(iAtomWalker->CurrentMatch(KImapTxtOpenBracket()), CImapCommand::CorruptDataL(iLogId));
+ iAtomWalker->WalkDownL();
+
+ // What kind of body type does this represent?
+ // Assume Basic, unless we find otherwise.
+ TParseStep nextStep = EParseBasic;
+
+ // Is it body-type-mpart? - check for opening bracket
+ // body-type-mpart = 1*body SP media-subtype [SP body-ext-mpart]
+ if (iAtomWalker->CurrentMatch(KImapTxtOpenBracket()))
+ {
+ // According to section 6.4.5 of RFC3501, "MULTIPART" is the correct Type string for
+ // multipart messages. This is an implicit value not directly available from the
+ // bodystructure input string. So we point the bodystructure object at a constant string instead.
+ iBodyStructure->SetType(KImapTxtMultipart());
+
+ iBodyStructure->SetBodyStructureType(CImapBodyStructure::ETypeMultipart);
+ nextStep = EParseNewBodyStructure;
+ }
+ else
+ {
+ // body-type-1part = (body-type-basic / body-type-msg / body-type-text) [SP body-ext-1part]
+ iBodyStructure->SetType(iAtomWalker->CurrentDes(EFalse)); // media-basic and variants is a string, not an nstring
+ __ASSERT_ALWAYS(iAtomWalker->PeekDown() == NULL, CImapCommand::CorruptDataL(iLogId));
+
+ iAtomWalker->WalkAcrossL(ETrue);
+ iBodyStructure->SetSubType(iAtomWalker->CurrentDes(EFalse)); // media-subtype and variants is a string, not an nstring
+
+ if (iBodyStructure->Type().CompareF(KImapTxtText())==0)
+ // body-type-text = media-text SP body-fields SP body-fld-lines
+ //
+ // media-text = DQUOTE "TEXT" DQUOTE SP media-subtype
+ // media-subtype = string
+ {
+ // we have media-text...
+ // ... so this is body-type-text
+ iBodyStructure->SetBodyStructureType(CImapBodyStructure::ETypeText);
+ nextStep = EParseText;
+ }
+ else if (iBodyStructure->Type().CompareF(KImapTxtMessage())==0 && iBodyStructure->SubType().CompareF(KImapTxtRfc822())==0)
+ // body-type-msg = media-message SP body-fields SP envelope SP body SP body-fld-lines
+ //
+ // media-message = DQUOTE "MESSAGE" DQUOTE SP DQUOTE "RFC822" DQUOTE
+ {
+ // we have media-message...
+ // ... so this is body-type-msg
+ iBodyStructure->SetBodyStructureType(CImapBodyStructure::ETypeMessageRfc822);
+ nextStep = EParseBodyTypeMessageRfc822;
+ }
+ else
+ {
+ // Not multipart, text or rfc822, so must be basic.
+ iBodyStructure->SetBodyStructureType(CImapBodyStructure::ETypeBasic);
+ }
+ }
+
+ return nextStep;
+ }
+
+/**
+body-type-basic = media-basic SP body-fields
+
+This method expects media-basict to have been parsed already, as part of ParseBodyStructureTypeL()
+It expects the atom walker to be positioned at the last atom of media-basic
+*/
+void CImapBodyStructureBuilder::ParseBodyTypeBasicL()
+ {
+ // body-fields
+ ParseBodyFieldsL();
+ }
+
+/**
+body-type-text = media-text SP body-fields SP body-fld-lines
+
+This method expects media-text to have been parsed already, as part of ParseBodyStructureTypeL()
+It expects the atom walker to be positioned at the last atom of media-text
+*/
+void CImapBodyStructureBuilder::ParseBodyTypeTextL()
+ {
+ // body-fields
+ ParseBodyFieldsL();
+
+ // body-fld-lines = number
+ iAtomWalker->WalkAcrossL(ETrue);
+ iBodyStructure->SetBodyLines(iAtomWalker->CurrentDes(EFalse));
+ }
+
+/**
+body-type-msg = media-message SP body-fields SP envelope SP body SP body-fld-lines
+
+This method parses up to and including envelope.
+It then returns, allowing the parse loop to parse the nested "body" that is next.
+Upon completion of the nested "body", ParseRemainderMessageRfc822L() will be called to finish parsing
+the message body type
+
+This method expects media-message to have been parsed already, as part of ParseBodyStructureTypeL()
+It expects the atom walker to be positioned at the last atom of media-text
+*/
+void CImapBodyStructureBuilder::ParseBodyTypeMessageRfc822L()
+ {
+ // body-fields SP SP body SP body-fld-lines
+ ParseBodyFieldsL();
+
+ // envelope
+ ParseEnvelopeL();
+
+ // Expect a body substructure next.
+ // Position iAtomWalker at the opening bracket, ready for ParseBodyStructureTypeL
+ iAtomWalker->WalkAcrossL(ETrue);
+ }
+
+/**
+body-type-msg = media-message SP body-fields SP envelope SP body SP body-fld-lines
+
+ParseBodyTypeMessageRfc822L processes up to and including body-fields.
+The ParseLoop processes the nested "body"
+This method processes the remainder - i.e. body-fld-lines
+It expects the atom walker to be positioned at the field just prior to body-fld-lines
+*/
+void CImapBodyStructureBuilder::ParseRemainderMessageRfc822L()
+ {
+ // body-fld-lines = number
+ iAtomWalker->WalkAcrossL(ETrue);
+ iBodyStructure->SetBodyLines(iAtomWalker->CurrentDes(EFalse));
+ }
+
+/**
+body-fields = body-fld-param SP body-fld-id SP body-fld-desc SP body-fld-enc SP body-fld-octets
+
+This method expects iAtomWalker to be positioned just prior to body-fld-param
+*/
+void CImapBodyStructureBuilder::ParseBodyFieldsL()
+ {
+ ParseBodyFieldParamsL(EFalse);
+
+ // body-fld-id = nstring
+ iAtomWalker->WalkAcrossL(EFalse);
+ iBodyStructure->SetBodyId(iAtomWalker->CurrentDes(ETrue));
+
+ // body-fld-desc = nstring
+ iAtomWalker->WalkAcrossL(EFalse);
+ iBodyStructure->SetBodyDescription(iAtomWalker->CurrentDes(ETrue));
+
+ // body-fld-enc = (DQUOTE ("7BIT" / "8BIT" / "BINARY" / "BASE64"/ "QUOTED-PRINTABLE") DQUOTE) / string
+ // i.e. it's a string that might be in quotes
+ iAtomWalker->WalkAcrossL(EFalse);
+ iBodyStructure->SetBodyEncoding(iAtomWalker->CurrentDes(EFalse));
+
+ // body-fld-octets = number
+ iAtomWalker->WalkAcrossL(EFalse);
+ iBodyStructure->SetBodySizeOctets(iAtomWalker->CurrentDes(EFalse));
+ }
+
+/**
+body-fld-param = "(" string SP string *(SP string SP string) ")" / nil
+
+This method expects iAtomWalker to be positioned just prior to the "(" or "NIL" atom.
+*/
+void CImapBodyStructureBuilder::ParseBodyFieldParamsL(TBool aStoreAsDisposition)
+
+ {
+ if (iAtomWalker->WalkAcrossToNilOrOpenL() == CImapAtomWalker::EAtomNil)
+ {
+ // there are no params
+ return;
+ }
+
+ // Consume the bracket
+ iAtomWalker->WalkDownL();
+
+ do
+ {
+ CImapBodyStructure::TAttributeValuePair pair;
+ pair.iAttribute.Set(iAtomWalker->CurrentDes(EFalse));
+
+ iAtomWalker->WalkAcrossL(ETrue);
+ pair.iValue.Set(iAtomWalker->CurrentDes(EFalse));
+
+ if (aStoreAsDisposition)
+ {
+ iBodyStructure->AppendExtDispositionParameterListL(pair);
+ }
+ else
+ {
+ iBodyStructure->AppendParameterListL(pair);
+ }
+ }
+ while (iAtomWalker->WalkAcrossL(EFalse));
+
+ iAtomWalker->WalkUpL();
+ }
+
+/**
+body-fld-dsp = "(" string SP body-fld-param ")" / nil
+
+This method expects iAtomWalker to be positioned just prior to the "(" or "NIL" atom.
+*/
+void CImapBodyStructureBuilder::ParseBodyFieldDispL()
+ {
+ if (iAtomWalker->WalkAcrossToNilOrOpenL() == CImapAtomWalker::EAtomNil)
+ {
+ // there are no params
+ return;
+ }
+
+ iAtomWalker->WalkDownL();
+
+ // string
+ iBodyStructure->SetExtDispositionName(iAtomWalker->CurrentDes(EFalse));
+
+ // body-fld-param
+ ParseBodyFieldParamsL(ETrue);
+
+ iAtomWalker->WalkUpL();
+ }
+
+/**
+body-fld-lang = nstring / "(" string *(SP string) ")"
+
+This method should only be called if body-fld-lang is expected.
+The caller should check first, using iAtomWalker->PeekAcross()
+*/
+void CImapBodyStructureBuilder::ParseBodyFieldLangL()
+ {
+ // Consume the bracket
+ iAtomWalker->WalkAcrossL(ETrue);
+
+ if (iAtomWalker->CurrentMatch(KImapTxtOpenBracket()))
+ {
+ // we have many strings
+ // "(" string *(SP string) ")"
+
+ iAtomWalker->WalkDownL();
+
+ do
+ {
+ iBodyStructure->AppendExtLanguageListL(iAtomWalker->CurrentDes(EFalse));
+
+ } while (iAtomWalker->WalkAcrossL(EFalse));
+
+ iAtomWalker->WalkUpL();
+ }
+ else
+ {
+ // we have a single nstring - only add it if it is non-empty
+ const TDesC8& language = iAtomWalker->CurrentDes(ETrue);
+ if (language.Length() > 0)
+ {
+ iBodyStructure->AppendExtLanguageListL(language);
+ }
+ }
+ }
+
+/**
+body-ext-1part = body-fld-md5 [SP body-fld-dsp [SP body-fld-lang [SP body-fld-loc *(SP body-extension)]]]
+
+This only appears in
+body-type-1part = (body-type-basic / body-type-msg / body-type-text) [SP body-ext-1part]
+So, this method treats the body-fld-md5 field as optional
+*/
+void CImapBodyStructureBuilder::ParseBodyExt1PartL()
+ {
+ // Return as soon as a field is not found.
+ if (iAtomWalker->WalkAcrossL(EFalse))
+ {
+ // body-fld-md5 = nstring
+ iBodyStructure->SetExtMD5(iAtomWalker->CurrentDes(ETrue));
+
+ // [SP body-fld-dsp [SP body-fld-lang [SP body-fld-loc *(SP body-extension)]]]
+ ParseCommonOptionalExtensionsL();
+ }
+ }
+
+/**
+body-type-mpart = 1*body SP media-subtype [SP body-ext-mpart]
+This method deals with media-subtype [SP body-ext-mpart]
+
+body-ext-mpart = body-fld-param [SP body-fld-dsp [SP body-fld-lang [SP body-fld-loc *(SP body-extension)]]]
+
+It expects iAtomWalker to be positioned just prior to media-subtype.
+*/
+void CImapBodyStructureBuilder::ParseRemainderMultipartL()
+ {
+ iAtomWalker->WalkAcrossL(ETrue);
+
+ // media-subtype = string
+ iBodyStructure->SetSubType(iAtomWalker->CurrentDes(EFalse));
+
+ // The remainder of items are optional. Return as soon as one is not found.
+
+ // body-fld-param
+ if (iAtomWalker->PeekAcross())
+ {
+ ParseBodyFieldParamsL(EFalse);
+
+ // [SP body-fld-dsp [SP body-fld-lang [SP body-fld-loc *(SP body-extension)]]]
+ ParseCommonOptionalExtensionsL();
+ }
+ }
+
+/**
+body-ext-1part = body-fld-md5 [SP body-fld-dsp [SP body-fld-lang [SP body-fld-loc *(SP body-extension)]]]
+body-ext-mpart = body-fld-param [SP body-fld-dsp [SP body-fld-lang [SP body-fld-loc *(SP body-extension)]]]
+Apart from the first parameter, body extensions for 1 and multi part are the same.
+So this method parses them in one place.
+*/
+void CImapBodyStructureBuilder::ParseCommonOptionalExtensionsL()
+ {
+ // Return as soon as a field is not found.
+
+ // body-fld-dsp
+ if (iAtomWalker->PeekAcross())
+ {
+ ParseBodyFieldDispL();
+
+ // body-fld-lang
+ if (iAtomWalker->PeekAcross())
+ {
+ ParseBodyFieldLangL();
+
+ // body-fld-loc = nstring
+ if (iAtomWalker->WalkAcrossL(EFalse))
+ {
+ iBodyStructure->SetExtLocation(iAtomWalker->CurrentDes(ETrue));
+
+ // *(SP body-extension)
+ if (iAtomWalker->PeekAcross())
+ {
+ ParseBodyExtensionL();
+ }
+ }
+ }
+ }
+ }
+
+/**
+body-extension = nstring / number / "(" body-extension *(SP body-extension) ")"
+RFC3501 says: "Future expansion. Client implementations
+ MUST accept body-extension fields. Server
+ implementations MUST NOT generate
+ body-extension fields except as defined by
+ future standard or standards-track
+ revisions of this specification.
+As body-extension is always at the end of a (sub)bodystructure, it is safe to ignore.
+This method provides a placeholder for extracting any body-extension data that we might
+be interested in, in the future.
+*/
+void CImapBodyStructureBuilder::ParseBodyExtensionL()
+ {}
+
+/**
+envelope = "(" env-date SP env-subject SP env-from SP env-sender SP env-reply-to SP env-to SP env-cc SP env-bcc SP env-in-reply-to SP env-message-id ")"
+
+This method expects iAtomWalker to be positioned at the opening bracket.
+*/
+void CImapBodyStructureBuilder::ParseEnvelopeL()
+ {
+ // Always expect an open bracket here, so allow atom walker to leave if there is one
+ iAtomWalker->WalkAcrossL(ETrue);
+ iAtomWalker->WalkDownL();
+
+ CImapEnvelope& envelope = iBodyStructure->GetRfc822EnvelopeStructureL();
+
+ // env-date = nstring
+ envelope.SetEnvDate(iAtomWalker->CurrentDes(ETrue));
+
+ // env-subject = nstring
+ iAtomWalker->WalkAcrossL(ETrue);
+ envelope.SetEnvSubject(iAtomWalker->CurrentDes(ETrue));
+
+ // This single address structure will be *copied* into various envelope address arrays
+ CImapEnvelope::TAddress address; // this will copied into many en
+
+ // env-from = "(" 1*address ")" / nil
+ if (iAtomWalker->WalkAcrossToNilOrOpenL() == CImapAtomWalker::EAtomOpen)
+ {
+ iAtomWalker->WalkDownL();
+ do
+ {
+ ParseAddressL(address);
+ envelope.AppendEnvFromL(address);
+
+ } while(iAtomWalker->WalkAcrossL(EFalse));
+
+ iAtomWalker->WalkUpL();
+ }
+
+ // env-sender = "(" 1*address ")" / nil
+ if (iAtomWalker->WalkAcrossToNilOrOpenL() == CImapAtomWalker::EAtomOpen)
+ {
+ iAtomWalker->WalkDownL();
+ do
+ {
+ ParseAddressL(address);
+ envelope.AppendEnvSenderL(address);
+
+ } while(iAtomWalker->WalkAcrossL(EFalse));
+
+ iAtomWalker->WalkUpL();
+ }
+ // env-reply-to = "(" 1*address ")" / nil
+ if (iAtomWalker->WalkAcrossToNilOrOpenL() == CImapAtomWalker::EAtomOpen)
+ {
+ iAtomWalker->WalkDownL();
+ do
+ {
+ ParseAddressL(address);
+ envelope.AppendEnvReplyToL(address);
+
+ } while(iAtomWalker->WalkAcrossL(EFalse));
+
+ iAtomWalker->WalkUpL();
+ }
+
+ // env-to = "(" 1*address ")" / nil
+ if (iAtomWalker->WalkAcrossToNilOrOpenL() == CImapAtomWalker::EAtomOpen)
+ {
+ iAtomWalker->WalkDownL();
+ do
+ {
+ ParseAddressL(address);
+ envelope.AppendEnvToL(address);
+
+ } while(iAtomWalker->WalkAcrossL(EFalse));
+
+ iAtomWalker->WalkUpL();
+ }
+
+ // env-cc = "(" 1*address ")" / nil
+ if (iAtomWalker->WalkAcrossToNilOrOpenL() == CImapAtomWalker::EAtomOpen)
+ {
+ iAtomWalker->WalkDownL();
+ do
+ {
+ ParseAddressL(address);
+ envelope.AppendEnvCcL(address);
+
+ } while(iAtomWalker->WalkAcrossL(EFalse));
+
+ iAtomWalker->WalkUpL();
+ }
+
+ // env-bcc = "(" 1*address ")" / nil
+ if (iAtomWalker->WalkAcrossToNilOrOpenL() == CImapAtomWalker::EAtomOpen)
+ {
+ iAtomWalker->WalkDownL();
+ do
+ {
+ ParseAddressL(address);
+ envelope.AppendEnvBccL(address);
+
+ } while(iAtomWalker->WalkAcrossL(EFalse));
+
+ iAtomWalker->WalkUpL();
+ }
+
+ // env-in-reply-to = nstring
+ iAtomWalker->WalkAcrossL(ETrue);
+ envelope.SetEnvInReplyTo(iAtomWalker->CurrentDes(ETrue));
+
+ // env-message-id = nstring
+ iAtomWalker->WalkAcrossL(ETrue);
+ envelope.SetEnvMessageId(iAtomWalker->CurrentDes(ETrue));
+
+ iAtomWalker->WalkUpL();
+ }
+
+/**
+address = "(" addr-name SP addr-adl SP addr-mailbox SP addr-host ")"
+
+This method expects iAtomWalker to be positioned at the opening bracket.
+*/
+void CImapBodyStructureBuilder::ParseAddressL(CImapEnvelope::TAddress& aAddress)
+ {
+ // Always expect an open bracket here, so allow atom walker to leave if there is one
+ iAtomWalker->WalkDownL();
+
+ // addr-name = nstring
+ aAddress.SetName(iAtomWalker->CurrentDes(ETrue));
+
+ // addr-adl = nstring
+ iAtomWalker->WalkAcrossL(ETrue);
+ aAddress.SetAdl(iAtomWalker->CurrentDes(ETrue));
+
+ // addr-mailbox = nstring
+ iAtomWalker->WalkAcrossL(ETrue);
+ aAddress.SetMailbox(iAtomWalker->CurrentDes(ETrue));
+
+ // addr-host = nstring
+ iAtomWalker->WalkAcrossL(ETrue);
+ aAddress.SetHost(iAtomWalker->CurrentDes(ETrue));
+
+ iAtomWalker->WalkUpL();
+ }