email/imap4mtm/imapsession/src/cimapfetchbodystructurebase.cpp
changeset 0 72b543305e3a
equal deleted inserted replaced
-1:000000000000 0:72b543305e3a
       
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "cimapfetchbodystructurebase.h"
       
    17 #include "cimapfetchresponse.h"
       
    18 #include "cimapbodystructurebuilder.h"
       
    19 #include "cimaprfc822headerfieldsparser.h"
       
    20 #include "moutputstream.h"
       
    21 #include "cimapsessionconsts.h"
       
    22 #include "imappaniccodes.h"
       
    23 #include "cimaplogger.h"
       
    24 
       
    25 _LIT8(KCommandFetch, "%d UID FETCH %S (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (%S)])\r\n");
       
    26 const TInt KCommandFetchFormatLength=6;
       
    27 
       
    28 CImapFetchBodyStructureBase::CImapFetchBodyStructureBase(CImapFolderInfo* aSelectedFolderInfo, TInt aLogId, const TDesC8& aHeaderFields)
       
    29 	: CImapCommand(aSelectedFolderInfo, aLogId)
       
    30 	, iHeaderFields(aHeaderFields)
       
    31 	, iState(EStateDataItemLine)
       
    32 	{
       
    33 	}
       
    34 
       
    35 CImapFetchBodyStructureBase::~CImapFetchBodyStructureBase()
       
    36 	{
       
    37 	delete iBodyStructureBuilder;
       
    38 	delete iHeaderFieldsParser;
       
    39 	}
       
    40 
       
    41 /**
       
    42 @param aTagId Command sequence id which will be send along with the IMAP command.
       
    43 @param aStream A wrapper for the outbound stream of a connected socket, using which 
       
    44 the command will be send to the server
       
    45 */
       
    46 void CImapFetchBodyStructureBase::SendMessageL(TInt aTagId, MOutputStream& aStream)
       
    47 	{
       
    48 	iTagId = aTagId;
       
    49 
       
    50 	TInt bufLen = KCommandFetch().Length() - KCommandFetchFormatLength;
       
    51 	bufLen += TagLength(aTagId);
       
    52 	bufLen += iSequenceSet->Length();
       
    53 	bufLen += iHeaderFields.Length();
       
    54 
       
    55 	__ASSERT_DEBUG(iOutputBuffer==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandOutputBufferNotNull));
       
    56 	iOutputBuffer = HBufC8::NewL(bufLen);
       
    57 	iOutputBuffer->Des().Format(KCommandFetch, aTagId, iSequenceSet, &iHeaderFields);
       
    58 
       
    59 	//send the command to the server
       
    60 	aStream.SendDataReq(*iOutputBuffer);
       
    61 	}
       
    62 
       
    63 /**
       
    64 @param aData Will contain a single line of response from the server for LOGIN command without \r\n.
       
    65 @return will be any one of this
       
    66 	1) If the next expected chunk is a literal block, ParseMessageL() will return the size of the block it expects.
       
    67 	2) If the next expected chunk is a line, ParseMessageL() will return 0, and Result() will return EImapResponseNone.
       
    68 	3) If no further data is expected (e.g. the OK or error tag has been received) then ParseMessageL() will return 0, 
       
    69 	   and Result() will return one of EImapResponseOk, EImapResponseNo or EImapResponseBad.
       
    70 */
       
    71 CImapCommand::TParseBlockResult CImapFetchBodyStructureBase::DoParseBlockL(const TDesC8& aData)
       
    72 	{
       
    73 	CImapCommand::TParseBlockResult resultCode(ECompleteUntagged);
       
    74 
       
    75 	switch (iState)
       
    76 		{
       
    77 		case EStateDataItemLine:
       
    78 			{
       
    79 			// We are the beginning of a new response, so we can't have found any UID data items yet.
       
    80 			// So we need to reset the flag here.
       
    81 			iUidDataItemFoundInResponse = EFalse;
       
    82 			resultCode = ProcessStartL();
       
    83 			
       
    84 			__ASSERT_DEBUG(iState == EStateDataItemLine, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState));
       
    85 			
       
    86 			// If we get EResponseIncomplete then allow the current EStateDataItemLine state to
       
    87 			// drop through to ProcessDataItems()
       
    88 			// otherwise, EStateComplete will take us straight to the return.
       
    89 			if (resultCode != EResponseIncomplete)
       
    90 				{
       
    91 				iState = EStateComplete;
       
    92 				}
       
    93 			}
       
    94 			break;
       
    95 		case EStateBodyStructureLiteral:
       
    96 		case EStateBodyStructureLine:
       
    97 			{
       
    98 			ProcessBodyStructureL(aData);
       
    99 			resultCode = EResponseIncomplete; // always expect more data after bodystructure.
       
   100 			}
       
   101 			break;
       
   102 		case EStateHeaderFieldsLiteral:
       
   103 			{
       
   104 			ProcessHeaderFieldsL(aData);
       
   105 			resultCode = EResponseIncomplete; // always expect more data after header field literal.
       
   106 			}
       
   107 			break;
       
   108 		case EStateFetchNextDataItemLine:
       
   109 			{
       
   110 			// Fetch is over.  Get ready to process next data item.
       
   111 			iUnparsedData.Set(aData);
       
   112 			if(GetAndStoreNextPart())
       
   113 				{
       
   114 						iState = EStateDataItemLine;
       
   115 				}
       
   116 			}
       
   117 			break;
       
   118 		default:
       
   119 			{
       
   120 			// unexpected state
       
   121 			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState));
       
   122 			}
       
   123 		}
       
   124 
       
   125 	// The ProcessXxxL() methods above can change the state.
       
   126 	// Now we need to check if there are data items to process...
       
   127 	if (iState == EStateDataItemLine)
       
   128 		{
       
   129 		resultCode = ProcessDataItemsL();
       
   130 		}
       
   131 	else if (iState == EStateComplete)
       
   132 		{
       
   133 		// this response is complete, so the next data (if any) will be a data item line,
       
   134 		iState = EStateDataItemLine;
       
   135 		}
       
   136 
       
   137 	// For complete untagged responses, check whether the UID data item was received.
       
   138 	// If it was, then this was a genuine response to the UID FETCH command.
       
   139 	// If it was not received, then this was an unsolicited server event, and the data should be discarded.
       
   140 	if (resultCode == ECompleteUntagged)
       
   141 		{
       
   142 		if (iUidDataItemFoundInResponse)
       
   143 			{
       
   144 			// Genuine UID FETCH response - copy UID and FLAG info into the response object
       
   145 			iFetchResponse->SetMessageFlagInfo(iMessageFlagInfo);
       
   146 			
       
   147 			// Note: iUidDataItemFoundInResponse is NOT reset here as its value is needed by CImapFetchMultiBodyStructures::DoParseBlockL()
       
   148 			// 		 Instead iUidDataItemFoundInResponse is set to EFalse just prior to calling ProcessStartL() - i.e. at the start of a new response.
       
   149 			}
       
   150 		else
       
   151 			{
       
   152 			// Record that an unsolicited FETCH was received - so that a sync will be triggered after this command.
       
   153 			__LOG_TEXT(iLogId, "CImapFetchBodyStructureBase::DoParseBlockL() - Found unsolicited FETCH FLAGS");
       
   154 			SetMessageFlagsChanged();
       
   155 			}
       
   156 		}
       
   157 
       
   158 	return resultCode;
       
   159 	}
       
   160 
       
   161 CImapCommand::TParseBlockResult CImapFetchBodyStructureBase::ProcessStartL()
       
   162 	{
       
   163 	TParseBlockResult result = ENotRecognised;
       
   164 
       
   165 	TInt tagId = 0;	
       
   166 	TTagType tagged = GetTagTypeL(tagId);
       
   167 	switch(tagged)
       
   168 		{
       
   169 		case ETagged:
       
   170 			{
       
   171 			// Check the tag id
       
   172 	 		if (tagId != iTagId)
       
   173 	 			{
       
   174 	 			//Unexpected Tagid	
       
   175 	 			CorruptDataL();
       
   176 	 			}
       
   177 	 		
       
   178  			// Fetch and check the response code
       
   179 			iResponseCode = GetResponseStateCode();
       
   180 			if (iResponseCode == EImapResponseNone)
       
   181 				{
       
   182 				// Was expecting one of OK/NO/BAD, but didn't get it.  This is a parse error.
       
   183 				CorruptDataL();
       
   184 				}
       
   185 	 		
       
   186 			result = ECompleteTagged;	
       
   187 			}
       
   188 			break;			
       
   189 		case EUntagged:
       
   190 			{
       
   191 			// Is this a FETCH response?
       
   192 			// Check for Sequence Number followed by "FETCH"
       
   193 			
       
   194 			TPtrC8 part1 = GetNextPart(); // returns KNullDesC8 if there is no part available
       
   195 			TPtrC8 part2 = GetNextPart(); // returns KNullDesC8 if there is no part available
       
   196 			
       
   197 			// Is part1 a Sequence Number?
       
   198 			TInt sequenceNumber = 0;
       
   199 			TLex8 lex(part1);
       
   200 			if (lex.Val(sequenceNumber) == KErrNone)
       
   201 				{
       
   202 				// part1 is a Sequence Number.  Now check part2 - is it "FETCH"?
       
   203 
       
   204 				if(part2.CompareF(KImapTxtFetch) == 0)
       
   205 					{
       
   206 					if (GetAndStoreNextPart())
       
   207 						{
       
   208 						if (iCurrentPart[0] == '(')
       
   209 							{
       
   210 							iCurrentPart.Set(iCurrentPart.Mid(1));
       
   211 							}
       
   212 						else
       
   213 							{
       
   214 							// was expecting a bracket, got something else
       
   215 							CorruptDataL();
       
   216 							}
       
   217 						}
       
   218 					else
       
   219 						{
       
   220 						// was expecting a bracket, got nothing
       
   221 						CorruptDataL();
       
   222 						}
       
   223 
       
   224 					result = EResponseIncomplete;
       
   225 					}
       
   226 				}
       
   227 			}
       
   228 			break;
       
   229 		case EContinuation:
       
   230 		default:
       
   231 			{
       
   232 			CorruptDataL();
       
   233 			}
       
   234 			break;
       
   235 		}
       
   236 
       
   237 	// result will be ENotRecognised if tagged not found or untagged FETCH not found.
       
   238 	return result;
       
   239 	}
       
   240 
       
   241 CImapCommand::TParseBlockResult CImapFetchBodyStructureBase::ProcessDataItemsL()
       
   242 	{
       
   243 	CImapCommand::TParseBlockResult resultCode = EResponseIncomplete;
       
   244 	
       
   245 	TBool foundPart = ETrue;
       
   246 	while (iState == EStateDataItemLine && foundPart)
       
   247 		{
       
   248 		if (iCurrentPart.CompareF(KImapTxtUid) == 0)
       
   249 			{
       
   250 			ProcessUidL();
       
   251 			}
       
   252 		else if (iCurrentPart.CompareF(KImapTxtFlags) == 0)
       
   253 			{
       
   254 			ProcessFlagsL();
       
   255 			}
       
   256 		else if (iCurrentPart.CompareF(KImapTxtBodyStructure) == 0)
       
   257 			{
       
   258 			ProcessBodyStructureL();
       
   259 			}
       
   260 		else if (iCurrentPart.CompareF(KImapTxtBodyHeaderFields) == 0)
       
   261 			{
       
   262 			ProcessHeaderFieldsL();
       
   263 			}
       
   264 		// Ignore anything else at the moment until we reach 
       
   265 		// a valid dataItem on the next line.
       
   266 				
       
   267 		
       
   268 		// Only fetch the next part if we're still searching for data items.
       
   269 		if (iState == EStateDataItemLine)
       
   270 			{
       
   271 			foundPart = GetAndStoreNextPart();
       
   272 			}
       
   273 		}
       
   274 		
       
   275 	if (!foundPart && iState == EStateDataItemLine)
       
   276 		{
       
   277 		resultCode = ECompleteUntagged;
       
   278 		}
       
   279 		
       
   280 	return resultCode;
       
   281 	}
       
   282 
       
   283 void CImapFetchBodyStructureBase::ProcessFlagsL()
       
   284 	{
       
   285 	iUnparsedData.Set(iMessageFlagInfo.ParseFlagsL(iUnparsedData));
       
   286 	}
       
   287 	
       
   288 void CImapFetchBodyStructureBase::ProcessUidL()
       
   289 	{
       
   290 	if (GetAndStoreNextPart())
       
   291 		{
       
   292 		TInt err = iMessageFlagInfo.SetMessageUid(iCurrentPart);
       
   293 		if (err == KErrNone)
       
   294 			{
       
   295 			iUidDataItemFoundInResponse = ETrue;
       
   296 			}
       
   297 		else
       
   298 			{
       
   299 			// expected iCurrentPart to be a number representing a UID.
       
   300 			// but we did not get a number.
       
   301 			CorruptDataL();
       
   302 			}
       
   303 		}
       
   304 	}
       
   305 	
       
   306 void CImapFetchBodyStructureBase::ProcessBodyStructureL()
       
   307 	{
       
   308 	iBodyStructureBuilder = CImapBodyStructureBuilder::NewL(*iFetchResponse, iLogId);
       
   309 
       
   310 	ProcessBodyStructureL(iUnparsedData);
       
   311 	}
       
   312 
       
   313 void CImapFetchBodyStructureBase::ProcessBodyStructureL(const TDesC8& aData)
       
   314 	{
       
   315 	TBool needsMore = iBodyStructureBuilder->ProcessBlockL(aData);
       
   316 
       
   317 	if (iState == EStateBodyStructureLiteral)
       
   318 		{
       
   319 		// Bodystructure should always request a line after a literal
       
   320 		__ASSERT_DEBUG(needsMore, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureExpectedRequestForLine));
       
   321 		
       
   322 		iState = EStateBodyStructureLine;
       
   323 		}		
       
   324 	else if (needsMore)
       
   325 		{
       
   326 		// Check the previous state was one we expected
       
   327 		__ASSERT_DEBUG(iState == EStateDataItemLine || iState == EStateBodyStructureLine, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState));
       
   328 		
       
   329 		iState = EStateBodyStructureLiteral;
       
   330 		}
       
   331 	else
       
   332 		{
       
   333 		// Check the previous state was one we expected
       
   334 		__ASSERT_DEBUG(iState == EStateDataItemLine || iState == EStateBodyStructureLine, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState));
       
   335 
       
   336 		iState = EStateDataItemLine;
       
   337 		
       
   338 		iUnparsedData.Set(iBodyStructureBuilder->UnparsedData());
       
   339 		
       
   340 		delete iBodyStructureBuilder;
       
   341 		iBodyStructureBuilder = NULL;		
       
   342 		}
       
   343 	}
       
   344 
       
   345 void CImapFetchBodyStructureBase::ProcessHeaderFieldsL()
       
   346 	{
       
   347 	// Skip past the field titles
       
   348 	while (GetAndStoreNextPart())
       
   349 		{
       
   350 		if (iCurrentPart[iCurrentPart.Length() - 1] == ']')
       
   351 			{
       
   352 			break;
       
   353 			}
       
   354 		}
       
   355 
       
   356 	iHeaderFieldsParser = CImapRfc822HeaderFieldsParser::NewL(*iFetchResponse, iLogId);
       
   357 
       
   358 	return ProcessHeaderFieldsL(iUnparsedData);
       
   359 	}
       
   360 
       
   361 void CImapFetchBodyStructureBase::ProcessHeaderFieldsL(const TDesC8& aData)
       
   362 	{
       
   363 	TBool needsMore = iHeaderFieldsParser->ProcessBlockL(aData);
       
   364 
       
   365 	if (iState == EStateDataItemLine)
       
   366 		{
       
   367 		// Always expect a literal for the Header Fields data item
       
   368 		if (!needsMore)
       
   369 			{
       
   370 			CorruptDataL();
       
   371 			}
       
   372 		
       
   373 		iState = EStateHeaderFieldsLiteral;
       
   374 		}
       
   375 	else if (iState == EStateHeaderFieldsLiteral)
       
   376 		{
       
   377 		// Parser should not be expecting more data after a literal
       
   378 		if (needsMore)
       
   379 			{
       
   380 			CorruptDataL();
       
   381 			}
       
   382 		
       
   383 		iState = EStateFetchNextDataItemLine;
       
   384 		
       
   385 		iUnparsedData.Set(iHeaderFieldsParser->UnparsedData());
       
   386 		__ASSERT_DEBUG(iUnparsedData.Length() == 0, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnparsedDataExists));
       
   387 		
       
   388 		delete iHeaderFieldsParser;
       
   389 		iHeaderFieldsParser = NULL;		
       
   390 		}
       
   391 	else
       
   392 		{
       
   393 		// Recovery from this would depend on the state
       
   394 		__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState));
       
   395 		}
       
   396 	}
       
   397  
       
   398 /**
       
   399 Move to the next part
       
   400 @return whether a part was found
       
   401 */
       
   402 TBool CImapFetchBodyStructureBase::GetAndStoreNextPart()
       
   403 	{
       
   404 	iCurrentPart.Set(GetNextPart());
       
   405 	return (iCurrentPart.Length() > 0) ? ETrue : EFalse;
       
   406 	}
       
   407 
       
   408 
       
   409 TBool CImapFetchBodyStructureBase::UidDataItemFoundInResponse()
       
   410 	{
       
   411 	return iUidDataItemFoundInResponse;
       
   412 	}