email/imap4mtm/imapsession/src/cimapfetchbodystructurebase.cpp
changeset 0 72b543305e3a
--- /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;
+	}