email/imap4mtm/imapsession/src/cimapfetchbody.cpp
changeset 0 72b543305e3a
child 26 ebe688cedc25
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/imap4mtm/imapsession/src/cimapfetchbody.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,1072 @@
+// 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 "cimapfetchbody.h"
+#include "moutputstream.h"
+#include "cimapsession.h"
+#include "cimapsessionconsts.h"
+#include "cimapsettings.h"
+#include "cimapmimeheaderfieldsparser.h"
+#include "cimapmimeheaderfields.h"
+#include "cfetchbodyinfo.h"
+#include "cimapfetchbodyresponse.h"
+#include "cimaplogger.h"
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+#include "cimapcapabilityinfo.h"
+#endif
+
+const TInt KMaxTagIdSize = 8;
+const TInt KDefaultMaxFetchSize = 20480;
+const TInt KOutstandingRequests = 2;
+const TInt KOutstandingBinaryFetchRequests = 1;
+
+_LIT8(KCommandFetch, "%S UID FETCH %d (BODY[%S]<%d.%d>)\r\n");
+_LIT8(KCommandFetchWithMime, "%S UID FETCH %d (BODY[%S]<%d.%d> BODY[%S.MIME])\r\n");
+_LIT8(KCommandFetchPeek, "%S UID FETCH %d (BODY.PEEK[%S]<%d.%d>)\r\n");
+_LIT8(KCommandFetchPeekWithMime, "%S UID FETCH %d (BODY.PEEK[%S]<%d.%d> BODY.PEEK[%S.MIME])\r\n");
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+_LIT8(KCommandFetchBinary, "%S UID FETCH %d (BINARY[%S]<%d.%d>)\r\n");
+_LIT8(KCommandFetchBinaryPeek, "%S UID FETCH %d (BINARY.PEEK[%S]<%d.%d>)\r\n");
+#endif
+_LIT8(KStartSection,"[");
+_LIT8(KEndSection,"]");
+_LIT8(KStartLiteral,"{");
+_LIT8(KEndLiteral,"}");
+_LIT8(KStartOrigin,"<");
+_LIT8(KEndOrigin,">");
+ 
+CImapFetchBody* CImapFetchBody::NewL(CImapFolderInfo* aSelectedFolderData, TInt aLogId, TUint aMessageUid,TBool aPeek, CFetchBodyInfo& aFetchBodyInfo, CImapFetchBodyResponse& aFetchBodyResponse, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, CImapSession& aParent)
+	{
+	CImapFetchBody* self = new (ELeave) CImapFetchBody(aSelectedFolderData, aLogId, aMessageUid, aPeek, aFetchBodyInfo, aFetchBodyResponse, aImapSettings, aImapMailStore, aParent);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+CImapFetchBody::CImapFetchBody(CImapFolderInfo* aSelectedFolderData, TInt aLogId, TUint aMessageUid, TBool aPeek, CFetchBodyInfo& aFetchBodyInfo, CImapFetchBodyResponse& aFetchBodyResponse, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, CImapSession& aParent)
+	: CImapCommand(aSelectedFolderData, aLogId),
+	iMessageUid(aMessageUid),
+	iPeek(aPeek),
+	iSizeToFetch(aFetchBodyInfo.SizeToFetch()),
+	iImapSettings(aImapSettings),
+	iFetchBodyInfo(aFetchBodyInfo),
+	iImapMailStore(aImapMailStore),
+	iParent(aParent),
+	iFetchBodyResponse(aFetchBodyResponse),
+	iSendFetch(ETrue)
+	{
+	}
+	
+CImapFetchBody::~CImapFetchBody()
+	{
+	// ensure that iImapMailStore will not call StoreOperationComplete() 
+	// on this object now that it is being destoyed.
+	
+	delete iHeaderFieldsParser;
+	delete iBuf;
+	iTagIds.Reset();
+	}
+
+/**
+Overrides CImapCommand::Cancel() by cancelling any outstanding mail store operation.
+*/
+void CImapFetchBody::Cancel()
+	{
+	__LOG_TEXT(iLogId, "CImapFetchBody::Cancel()"); // Overrides CImapCommand::Cancel()
+	
+	iImapMailStore.CancelRequest(*this);
+	CImapCommand::Cancel();
+	}
+
+void CImapFetchBody::StoreOperationComplete(TMsvId /*aId*/,TInt aErrorCode)
+	{
+	__LOG_FORMAT((iLogId, "CImapFetchBody::StoreOperationComplete aErrorCode = %d",aErrorCode));
+	iStoreComplete = ETrue;
+	
+	// Complete early if there is an error
+	if (aErrorCode != KErrNone)
+		{
+		__LOG_TEXT(iLogId, "CImapFetchBody::StoreOperationComplete - ERROR: Completing Early");
+		iParent.FetchBodyOperationComplete(aErrorCode);
+		}
+	// Otherwise complete only if the last tagged OK has been received.
+	else if(ParseState() == ECommandComplete)
+		{
+		// Call the session to let it know we are done
+		__ASSERT_DEBUG(iRequestCount == iTotalRequests && iOutstandingRequests == 0, TImapServerPanic::ImapPanic(TImapServerPanic::EStoreOperationCompleteWithPendingRequest) );
+		iParent.FetchBodyOperationComplete(aErrorCode);
+		}
+	}
+	
+TBool CImapFetchBody::IsStoreOperationComplete()
+	{
+	return iStoreComplete;
+	}
+	
+void CImapFetchBody::ConstructL()
+	{
+	// obtain
+	iImapSettings.GetTransportBufferSizesL(iMaxFetchSize, iMaxOutstandingRequests);
+
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)		
+	// check for BINARY capability
+	const CImapCapabilityInfo& capabilityInfo = iParent.CapabilityInfo();
+	TBool binaryCapExist = capabilityInfo.QueryFlag(CImapCapabilityInfo::EBinaryCap);		
+#endif
+	
+	// if either parameter is undefined, resort to using the defaults.
+	if(iMaxFetchSize==0)
+		{
+		iMaxFetchSize=KDefaultMaxFetchSize;	
+		}
+	
+	if(iMaxOutstandingRequests==0)
+		{
+		iMaxOutstandingRequests=KOutstandingRequests;	
+		}
+
+	//calculate the number of chunks that will be needed
+	iTotalRequests=TotalRequestsRequired(iSizeToFetch);
+		
+	__LOG_FORMAT((iLogId, "CImapFetchBody::CImapFetchBody iTotalRequests = %d",iTotalRequests));
+	
+	
+	if(iFetchBodyInfo.IsText())
+		{
+		// check if chunk storage mechanism is enabled.
+		if(iImapSettings.StorePlainText())
+			{
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+			if(binaryCapExist && iFetchBodyInfo.IsText())
+				{
+				iImapMailStore.InitialiseStorePlainBodyTextL(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this,*this, ETrue);
+				}
+			else
+#endif
+				{
+				iImapMailStore.InitialiseStorePlainBodyTextL(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this,*this);
+				}			
+			}
+		else
+			{
+			if(iImapSettings.Store8BitData())
+				{
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+				if(binaryCapExist && iFetchBodyInfo.IsText())
+					{
+					iImapMailStore.InitialiseStoreBody8L(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this,ETrue);
+					}
+				else
+#endif
+					{
+					iImapMailStore.InitialiseStoreBody8L(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this);
+					}						
+				}
+			else
+				{
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+				if(binaryCapExist && iFetchBodyInfo.IsText())
+					{
+					iImapMailStore.InitialiseStoreBody16L(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this,ETrue);		
+					}
+				else			
+#endif
+					{	
+					iImapMailStore.InitialiseStoreBody16L(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this);		
+					}
+				}
+			}
+		}
+	else
+		{
+		iImapMailStore.InitialiseStoreAttachmentL(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this);	
+		}
+	}
+
+TInt CImapFetchBody::TotalRequestsRequired(TInt aSize)
+	{
+	
+	TInt chunkNumber = aSize / iMaxFetchSize;
+	
+	if( (aSize % iMaxFetchSize)>0)
+		{
+		//add a chunk for the last bit of data
+		++chunkNumber;
+		}	
+
+	return chunkNumber;	
+	}
+
+
+TInt CImapFetchBody::CalculateChunk(TInt aPos)
+	{
+	return aPos / iMaxFetchSize;
+	}
+
+/** 
+Formats and sends the IMAP UID FETCH command.
+@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 CImapFetchBody::SendMessageL(TInt aTagId, MOutputStream& aStream)
+	{	
+	iOutStream=&aStream;
+	
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)		
+	// check for BINARY capability
+	const CImapCapabilityInfo& capabilityInfo = iParent.CapabilityInfo();
+	TBool binaryCapExist = capabilityInfo.QueryFlag(CImapCapabilityInfo::EBinaryCap);		
+	if(binaryCapExist && iFetchBodyInfo.IsText())
+		{
+		// if message body part is downloaded using FETCH BINARY, then there should be only one
+		// Outstanding Request
+		iOutstandingRequests = KOutstandingBinaryFetchRequests;
+		}
+	else
+#endif
+		{
+		//iOutstandingRequests is the smaller of iMaxOutstandingRequests and the total chunk count
+		iOutstandingRequests = iTotalRequests>iMaxOutstandingRequests ? iMaxOutstandingRequests : iTotalRequests;
+		}
+	// SendMessageL will increment the tag ID as the first thing it does, so we
+	// should set it to 1 lower than the fist tag we want to send.
+	iTagId = aTagId - 1;
+
+	SendMessageL();	
+	}
+	
+void CImapFetchBody::SendMessageL()
+	{
+	// Set the tag ID to use in the next message
+	++iTagId;
+
+	_LIT8(KTagFormatId, "%d");
+	TBuf8<KMaxTagIdSize>tagIdBuffer;
+	tagIdBuffer.Format(KTagFormatId,iTagId);
+	
+	iTagIds.InsertInOrder(iTagId);
+	
+	
+	//create fetch command based on settings	
+	//the offset from which we want to fetch data
+	TInt offset = iRequestCount*iMaxFetchSize;
+	
+	// calclulate the size to fetch in this request,
+	// default to max fetch size.
+	TUint sizeToFetch = iMaxFetchSize;
+	
+	TInt bufLength = 0;
+	bufLength += iFetchBodyInfo.RelativePath()->Length();
+	bufLength += TagLength(offset);
+	bufLength += TagLength(sizeToFetch);
+	bufLength += tagIdBuffer.Length();
+
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)	
+	// check for BINARY capability
+	// Issue binary fetch for plain/text part only
+	const CImapCapabilityInfo& capabilityInfo = iParent.CapabilityInfo();
+	TBool binaryCapExist = capabilityInfo.QueryFlag(CImapCapabilityInfo::EBinaryCap);	
+#endif
+	if (iRequestCount == 0)
+		{
+		bufLength += iFetchBodyInfo.RelativePath()->Length();
+		
+		if (iPeek)
+			{
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)	
+			if(binaryCapExist && iFetchBodyInfo.IsText())
+				{
+				bufLength += KCommandFetchBinaryPeek().Length();
+				}
+			else
+#endif
+				{
+				bufLength += KCommandFetchPeekWithMime().Length();	
+				}
+			}
+		else
+			{
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)	
+			if(binaryCapExist && iFetchBodyInfo.IsText())
+				{	
+				bufLength += KCommandFetchBinary().Length();
+				}
+			else
+#endif
+				{
+				bufLength += KCommandFetchWithMime().Length();
+				}			
+			}
+		}
+	else
+		{
+		if(iPeek)
+			{
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)	
+			if(binaryCapExist && iFetchBodyInfo.IsText())
+				{
+				bufLength += KCommandFetchBinaryPeek().Length();
+				}
+			else
+#endif
+				{
+				bufLength += KCommandFetchPeek().Length();	
+				}			
+			}
+		else
+			{
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)	
+			if(binaryCapExist && iFetchBodyInfo.IsText())
+				{
+				bufLength += KCommandFetchBinary().Length();							
+				}
+			else
+#endif
+				{
+				bufLength += KCommandFetch().Length();			
+				}			
+			}
+		}
+
+	//now create the command	
+	HBufC8* buffer = HBufC8::NewL(bufLength);				
+	delete iOutputBuffer;
+	iOutputBuffer=buffer;
+
+	if (iRequestCount == 0)
+		{
+		if(iPeek)
+			{
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)	
+			if(binaryCapExist && iFetchBodyInfo.IsText())
+				{
+				iOutputBuffer->Des().Format(KCommandFetchBinaryPeek, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, sizeToFetch, iFetchBodyInfo.RelativePath());				
+				}
+			else
+#endif
+				{
+				iOutputBuffer->Des().Format(KCommandFetchPeekWithMime, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, sizeToFetch, iFetchBodyInfo.RelativePath());
+				}			
+			}
+		else
+			{
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)	
+			if(binaryCapExist && iFetchBodyInfo.IsText())
+				{
+				iOutputBuffer->Des().Format(KCommandFetchBinary, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, sizeToFetch, iFetchBodyInfo.RelativePath());	
+				}
+			else
+#endif
+				{
+				iOutputBuffer->Des().Format(KCommandFetchWithMime, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, sizeToFetch, iFetchBodyInfo.RelativePath());	
+				}
+			}
+		}
+	else
+		{
+		if(iPeek)
+			{
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)	
+			if(binaryCapExist && iFetchBodyInfo.IsText())
+				{
+				iOutputBuffer->Des().Format(KCommandFetchBinaryPeek, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, sizeToFetch);	
+				}
+			else
+#endif
+				{
+				iOutputBuffer->Des().Format(KCommandFetchPeek, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, sizeToFetch);	
+				}			
+			}
+		else
+			{
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)	
+			if(binaryCapExist && iFetchBodyInfo.IsText())
+				{
+				iOutputBuffer->Des().Format(KCommandFetchBinary, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, iMaxFetchSize);								
+				}
+			else
+#endif
+				{
+				iOutputBuffer->Des().Format(KCommandFetch, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, iMaxFetchSize);				
+				}			
+			}
+		}
+
+	delete iBuf;
+	iBuf = NULL;
+	iReceivedMimeHeaders = EFalse;
+
+	//send the command to the server
+	iOutStream->SendDataReq(*iOutputBuffer);
+	++iRequestCount;
+	}
+
+/**
+@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 CImapFetchBody::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(iState == EStateDataItemLine);
+			
+			// 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 EStateBodyLiteral:
+			{
+			// Bump progress: bytesdone is *encoded* length, so we just use the encoded length
+			iFetchBodyInfo.IncrementBytesFetched(aData.Length());
+			
+			__ASSERT_DEBUG(aData.Length() == iLiteralSize, TImapServerPanic::ImapPanic(TImapServerPanic::ETParseBlockResultInvalidLiteralSize) );
+
+			ProcessBodyLiteralL(aData);
+			resultCode = EResponseIncomplete; // always expect more data after a literal
+			}
+			break;
+		case EStateMime:
+			{
+			ProcessRestOfMimeL(iUnparsedData);
+			resultCode = EResponseIncomplete;
+			}
+			break;
+		case EStateFetchNextDataItemLine:
+			{
+			// Fetch is over.  Get ready to process next data item.
+			iUnparsedData.Set(aData);
+			GetAndStoreNextPart();
+			
+			iState = EStateDataItemLine;
+			}
+			break;
+		default:
+			{
+			// unexpected state
+			ASSERT(EFalse);
+			}
+		}
+
+	// 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)
+		{
+		//if we still have more requests to issue send the next one now
+		
+		if(resultCode == ECompleteTagged)
+			{
+			if (iResponseCode == EImapResponseOk)
+				{
+				++iResponseCount;
+				// If we received some MIME headers, we may need to store them
+				// with the CAF framework
+				if ((iReceivedMimeHeaders) && (iFetchBodyInfo.Caf() != NULL) && (iFetchBodyInfo.Caf()->Registered()))
+					{
+					WriteMimeHeadersToCafL();
+					}
+
+				// Store the body data if we received it
+				if (iBuf != NULL)
+					{
+					TInt extraFetchRequestCount = 0;
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)					
+					// check for BINARY capability
+					const CImapCapabilityInfo& capabilityInfo = iParent.CapabilityInfo();
+					TBool binaryCapExist = capabilityInfo.QueryFlag(CImapCapabilityInfo::EBinaryCap);					
+					if(binaryCapExist && iFetchBodyInfo.IsText() && iPreviousLiteralBytesReceived < KDefaultMaxFetchSize)
+						{
+						extraFetchRequestCount = iTotalRequests - iRequestCount;
+						// iTotalRequests, iRequestCount and iResponseCount should be same at this stage
+						// iResponseCount will be same as iRequestCount
+						iTotalRequests=iRequestCount;
+						//reset iPreviousLiteralBytesReceived to zero
+						iPreviousLiteralBytesReceived=0;										
+						}
+#endif
+						
+					StoreBodyDataL(extraFetchRequestCount);										
+					
+					if (iRequestCount<iTotalRequests)
+						{
+						// if there are outstanding requests already then just add this request to the count
+						++iOutstandingRequests;
+						
+						// If iOutstandingRequests is greater than one, this means a write operation is in progress
+						// and the message will be sent when SendDataCnf() is called.
+						if (iSendFetch && (iOutstandingRequests == 1))
+							{
+							SendMessageL();
+							}
+
+						resultCode=ECompleteUntagged;
+						iResponseCode=EImapResponseNone;
+						}
+					// if there are remaining requests yet to be received then tell the session were not finished.
+					else if(iResponseCount<iTotalRequests)
+						{
+						resultCode = ECompleteUntagged;	
+						iResponseCode=EImapResponseNone;	
+						}
+					}
+				// if there is no body part to be stored but server sent a OK responsethen set 
+				// iStoreComplete to ETrue and Cancel the Request.If more than one FETCH was sent
+				// then Flush the state so that all incoming server data is discarded.
+				else
+					{
+					iStoreComplete = ETrue;
+					iImapMailStore.CancelRequest(*this);	
+					
+					// Check the tag id
+					if(iTagIds.Count())
+						{
+						EnterFlushingState();
+						resultCode = ECompleteUntagged;
+						iResponseCode = EImapResponseNone;
+						}		
+					}
+				}
+			else
+				{
+				iImapMailStore.CancelRequest(*this);
+				}
+			}
+			
+		// 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
+			iFetchBodyResponse.SetMessageFlagInfo(iMessageFlagInfo);
+			
+			// Note: iUidDataItemFoundInResponse is NOT reset here
+			// 		 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, "CImapFetchBody::DoParseBlockL() - Found unsolicited FETCH FLAGS");
+			SetMessageFlagsChanged();
+			}
+		}
+
+	return resultCode;
+	}
+
+/**
+Returns the number of tagged responses that are currently expected.
+Because CImapFetchBody uses pipelining - with many simulataneous request running at once
+it will be expect a tagged response for each request that is still running on the server.
+@return the number of tagged responses that are currently expected.
+*/
+TInt CImapFetchBody::NumberOfTaggedResponsesExpected() const
+	{
+	return iTagIds.Count();
+	}
+	
+CImapCommand::TParseBlockResult CImapFetchBody::ProcessStartL()
+	{
+	TParseBlockResult result = ENotRecognised;
+
+	TInt tagId = 0;	
+	TTagType tagged = GetTagTypeL(tagId);
+	switch(tagged)
+		{
+		case ETagged:
+			{
+			// Check the tag id
+			TInt tagPos=iTagIds.FindInOrder(tagId);
+			if(tagPos!=KErrNotFound)
+				{
+				iTagIds.Remove(tagPos);	
+				}
+			else
+				{
+				//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 CImapFetchBody::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();
+			}
+	
+		// check if the part starts 'BODY['	
+		else if (iCurrentPart.Find(KImapTxtBody)==0)
+			{
+			//is it the body or the body.mime?
+			if(iCurrentPart.Find(KImapTxtMime) != KErrNotFound )
+				{
+				ProcessStartOfMimeL();
+				}
+			else
+				{
+				ProcessBodyL();	
+				}
+			}
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+		// check if the part starts 'BINARY['	
+		else if (iCurrentPart.Find(KImapTxtBinary)==0)
+			{
+			// no mime to process for fetch binary
+			ProcessBodyL();	
+			}
+#endif
+	
+		// Only fetch the next part if we're still searching for data items.
+		if (iState == EStateDataItemLine)
+			{
+			foundPart = GetAndStoreNextPart();
+			}
+		}
+		
+	if (!foundPart && iState == EStateDataItemLine)
+		{
+		if(iBuf && iUnexpectedFormat)
+			{
+			iState = EStateFetchNextDataItemLine;
+			iUnexpectedFormat = EFalse;
+			}
+		resultCode = ECompleteUntagged;
+		}
+	iUnexpectedFormat = EFalse;	
+	return resultCode;
+	}
+
+
+void CImapFetchBody::ProcessStartOfMimeL()
+	{
+	//look for the body section that is being returned 
+	TInt secStart=iCurrentPart.Find(KStartSection);
+	TInt secEnd=iCurrentPart.Find(KImapTxtMime);
+
+	if(secStart==KErrNotFound || secEnd==KErrNotFound)
+		{
+		CorruptDataL();
+		}
+
+	TPtrC8 section = iCurrentPart.Mid(secStart + 1, secEnd - secStart - 1);
+
+	//check the section is what we asked for
+	if(section.CompareF(*iFetchBodyInfo.RelativePath())  != 0)
+		{
+		CorruptDataL();
+		}
+
+	// Peek the next part. We don't want to consume it as we may need
+	// to pass it to the header fields parser.
+	iCurrentPart.Set(PeekNextPart());
+	if (iCurrentPart.Length() == 0)
+		{
+		CorruptDataL();
+		}
+
+	iReceivedMimeHeaders = ETrue;
+	
+	// If the last character is ')' then we're at the last data item in the list.
+	// Consume the character so that the rest of the data item can be interpreted.
+	if (iCurrentPart.Right(1).CompareF(KImapTxtCloseBracket) == 0)
+		{
+		iCurrentPart.Set(iCurrentPart.Left(iCurrentPart.Length() - 1));
+		}
+	
+	// Check if data part is NIL or "" for empty string
+	if (iCurrentPart.CompareF(KImapTxtNil) == 0 || iCurrentPart.CompareF(KImapTxtEmptyStringAsDoubleQuotePair) == 0)
+		{
+		// Consume the NIL part
+		GetAndStoreNextPart();
+
+		// Create empty MIME header fields
+		CImapMimeHeaderFields* fields = CImapMimeHeaderFields::NewL();
+		iFetchBodyResponse.SetHeaderFields(fields);
+
+		// May be more data items coming up
+		iState = EStateDataItemLine;
+		}
+	else
+		{
+		// Pass the rest of the line to the header fields parser
+		iHeaderFieldsParser = CImapMimeHeaderFieldsParser::NewL(iFetchBodyResponse, iLogId);
+		iState = EStateMime;
+		ProcessRestOfMimeL(iUnparsedData);
+		}
+	}
+
+void CImapFetchBody::ProcessRestOfMimeL(const TDesC8& aData)
+	{
+	TBool wantsMore = iHeaderFieldsParser->ProcessBlockL(aData);
+
+	if (!wantsMore)
+		{
+		delete iHeaderFieldsParser;
+		iHeaderFieldsParser = NULL;
+		iState = EStateFetchNextDataItemLine;
+		}
+	}
+
+void CImapFetchBody::ProcessBodyL()
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+	{//BODY[1]<0> or BINARY[1]<0>
+#else
+	{//BODY[1]<0>
+#endif	
+	//look for the body section that is being returned 
+	TInt secStart=iCurrentPart.Find(KStartSection);
+	TInt secEnd=iCurrentPart.Find(KEndSection);
+	
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+	const CImapCapabilityInfo& capabilityInfo = iParent.CapabilityInfo();
+	if(capabilityInfo.QueryFlag(CImapCapabilityInfo::EBinaryCap) && iFetchBodyInfo.IsText())
+		{
+		if(secStart!=KImapTxtBinary().Length() - 1  || secEnd <= secStart + 1)
+			{
+			CorruptDataL();
+			}
+		}
+	else
+#endif 
+		{
+		if(secStart!=KImapTxtBody().Length() - 1  || secEnd <= secStart + 1)
+			{
+			CorruptDataL();
+			}						
+		}
+			
+	TPtrC8 section=iCurrentPart.Mid(secStart+1,secEnd-secStart-1);
+	
+	//check the section is what we asked for
+	if(section.CompareF(*iFetchBodyInfo.RelativePath())  != 0)
+		{
+		CorruptDataL();
+		}
+	
+	//get the origin octet of the form <origin>, this may not exist, if its not
+		//then the origin is at the start of the data
+	TInt originStart=iCurrentPart.Find(KStartOrigin);
+	if(originStart==KErrNotFound)
+		{
+		//the origin octet will be 0, the data will  go in the first chunk
+		iCurrentChunk=0;		
+		}
+	else
+		{
+		if(originStart != secEnd + 1)
+			{
+			CorruptDataL();
+			}
+		TInt originEnd=iCurrentPart.Find(KEndOrigin);
+		if(originEnd==KErrNotFound || originEnd != iCurrentPart.Length() - 1 )
+			{
+			CorruptDataL();	
+			}
+		if(originEnd <= originStart + 1)
+			{
+			CorruptDataL();	
+			}
+			
+		TPtrC8 originPtr=iCurrentPart.Mid(originStart+1,originEnd-originStart-1);
+		TLex8 originToInt(originPtr);
+	
+		TInt origin=0;
+		TInt err = originToInt.Val(origin);
+		if (err != KErrNone)
+			{
+			// Was expecting originPtr to be a number
+			CorruptDataL();
+			}
+		
+		//set the chunk number
+		//if the origin was blank ie. <> then this is set to 0, the start of the data 
+		iCurrentChunk = CalculateChunk(origin);
+		}
+		
+	//now look for the size of the literal
+	TBool foundPart = GetAndStoreNextPart();
+	if(!foundPart)
+		{
+		CorruptDataL();	
+		}
+		
+	TInt litStart=iCurrentPart.Find(KStartLiteral);
+	TInt litEnd=iCurrentPart.Find(KEndLiteral);
+	
+	if(litStart==KErrNotFound && litEnd==KErrNotFound)
+		{
+		// This may be the data item
+		ProcessBodyLiteralL(iCurrentPart);
+		iState = EStateDataItemLine;
+		iUnexpectedFormat = ETrue;
+		}
+	else
+		{
+		if(litStart==KErrNotFound || litEnd==KErrNotFound)
+			{
+			CorruptDataL();	
+			}
+
+		if(litEnd <= litStart + 1)
+			{
+			CorruptDataL();	
+			}
+
+		TPtrC8 litPtr=iCurrentPart.Mid(litStart+1,litEnd-litStart-1);
+		TLex8 litSizeToInt(litPtr);
+		
+		TInt err = litSizeToInt.Val(iLiteralSize);
+		if (err != KErrNone)
+			{
+			// Was expecting litPtr to be a number
+			CorruptDataL();
+			}
+		
+		if(GetAndStoreNextPart())
+			{
+			ProcessBodyLiteralL(iCurrentPart);
+			iState = EStateDataItemLine;
+			iUnexpectedFormat = ETrue;
+			}
+		else
+			{
+			//now wait for the litereral
+			iState = EStateBodyLiteral;
+			}
+		}
+	}
+	
+void CImapFetchBody::ProcessBodyLiteralL(const TDesC8& aData)
+	{
+	delete iBuf;
+	iBuf = NULL;
+	iBuf = aData.AllocL();
+	
+	//now wait for the line that always follows a literal
+	iState = EStateFetchNextDataItemLine;
+	}
+
+/**
+Move to the next part
+@return whether a part was found
+*/
+TBool CImapFetchBody::GetAndStoreNextPart()
+	{
+	iCurrentPart.Set(GetNextPart());
+	return (iCurrentPart.Length() > 0) ? ETrue : EFalse;
+	}
+
+void CImapFetchBody::ProcessFlagsL()
+	{
+	iUnparsedData.Set(iMessageFlagInfo.ParseFlagsL(iUnparsedData));
+	}
+
+void CImapFetchBody::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 CImapFetchBody::WriteMimeHeadersToCafL()
+	{
+	CImapMimeHeaderFields* fields = iFetchBodyResponse.HeaderFields();
+	if (fields != NULL)
+		{
+		TPtrC8 name;
+		TPtrC8 value;
+
+		fields->RestartGetNextField();
+		while (fields->GetNextField(name, value))
+			{
+			__LOG_FORMAT((iLogId, "Add CAF metadata: %S %S", &name, &value));
+			iFetchBodyInfo.Caf()->AddToMetaDataL(name, value);
+			}
+		}
+	}
+
+void CImapFetchBody::StoreBodyDataL(TBool aExtraFetchRequestCount)
+	{
+	// We are going to pass the buffer to the mail store, so set our
+	// buffer to NULL so that we don't try to delete it if the store
+	// routine leaves.
+	HBufC8* buf(iBuf);
+	iBuf = NULL;
+
+	if(iFetchBodyInfo.IsText())
+		{
+		if(iImapSettings.StorePlainText())
+			{
+			iSendFetch = iImapMailStore.StorePlainBodyTextL(buf,iFetchBodyInfo.PartId(),iCurrentChunk,aExtraFetchRequestCount);
+			}
+		else
+			{
+			if(iImapSettings.Store8BitData())
+				{
+				iImapMailStore.StoreBodyChunk8L(buf,iFetchBodyInfo.PartId(),iCurrentChunk,aExtraFetchRequestCount);
+				}
+			else
+				{
+				iImapMailStore.StoreBodyChunk16L(buf,iFetchBodyInfo.PartId(),iCurrentChunk,aExtraFetchRequestCount);	
+				}
+			}
+		}
+	else //attachments
+		{
+		iImapMailStore.StoreAttachmentChunkL(buf,iFetchBodyInfo.PartId(),iCurrentChunk);
+		}
+	}
+
+/**
+If pipelining is enabled then this method will send the next fetch request to the server after confirmation of the last request having been sent.
+@return void
+*/ 
+
+
+void CImapFetchBody::SendDataCnfL()
+	{
+	ASSERT(iOutstandingRequests>0);
+	--iOutstandingRequests;
+	//if we want more requests outstanding then send the next one now
+	if(iOutstandingRequests>0 && iSendFetch)
+		{
+		SendMessageL();	
+		}
+	}
+/**
+This method will enable the FETCH command to be send to the server if it was
+disabled by CImapMailStore due to reciept of out-of-order chunks.
+@return void
+*/	
+void CImapFetchBody::EnableSendFetch()
+	{
+	iSendFetch = ETrue;
+	}