--- /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;
+ }