email/imap4mtm/imapsession/src/cimapcommand.cpp
changeset 31 ebfee66fde93
child 47 5b14749788d7
equal deleted inserted replaced
30:6a20128ce557 31:ebfee66fde93
       
     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 
       
    17 #include "cimapcommand.h"
       
    18 #include "cimapsessionconsts.h"
       
    19 #include "cimapfolderinfo.h"
       
    20 #include "cimaputils.h"
       
    21 #include "cimapcharconv.h"
       
    22 #include "cimaplogger.h"
       
    23 
       
    24 static const TInt KDefaultNumberOfTaggedResponsesExpected = 1;
       
    25 
       
    26 CImapCommand::CImapCommand(CImapFolderInfo* aSelectedFolderData, TInt aLogId)
       
    27 	: iSelectedFolderData(aSelectedFolderData)
       
    28 	, iLogId(aLogId)
       
    29 	{}
       
    30 
       
    31 CImapCommand::~CImapCommand()
       
    32 	{
       
    33 	delete iOutputBuffer;
       
    34 	}
       
    35 
       
    36 void CImapCommand::SendDataCnfL()
       
    37 	{}
       
    38 
       
    39 CImapCommand::TParseState CImapCommand::ParseBlockL(const TDesC8& aData)
       
    40 	{
       
    41 #ifdef __IMAP_LOGGING
       
    42 	// Log the first few bytes only - but enough to tell the difference between untagged FETCH's
       
    43 	TPtrC8 truncatedData = aData.Left(30);
       
    44 	__LOG_FORMAT((iLogId, "CImapCommand::ParseBlockL(): [%S] %d octets", &truncatedData, aData.Length()));
       
    45 #endif //__IMAP_LOGGING
       
    46 
       
    47 	__ASSERT_DEBUG(iParseState != ECommandComplete, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState2));
       
    48 	iUnparsedData.Set(aData);
       
    49 	iLiteralBytesExpected = 0; // Initialise to zero, and only peek in states that read a line.
       
    50 	
       
    51 	switch (iParseState)
       
    52 		{
       
    53 		case EWaitStartResponse:
       
    54 			{
       
    55 			if (Flushing())
       
    56 				{
       
    57 				ParseStartResponseFlushL(aData);
       
    58 				}
       
    59 			else
       
    60 				{
       
    61 				ParseLineL(aData);
       
    62 				}
       
    63 			break;
       
    64 			}
       
    65 		case EWaitLineParse:
       
    66 			{
       
    67 			ParseLineL(aData);
       
    68 			}
       
    69 			break;
       
    70 		case EWaitLiteralParse:
       
    71 			{
       
    72 			ParseLiteralL(aData);
       
    73 			}
       
    74 			break;		
       
    75 		case EWaitLiteralIngore:
       
    76 			{
       
    77 			__LOG_TEXT(iLogId, "CImapCommand Ignore Literal");
       
    78 			// Ignore the literal.
       
    79 			// A literal is always followed by a line (as part of the same response)
       
    80 			SetParseState(EWaitLineIgnore);
       
    81 			}
       
    82 			break;
       
    83 		case EWaitLineIgnore:
       
    84 			{
       
    85 			__LOG_TEXT(iLogId, "CImapCommand Ignore Line following Literal");
       
    86 			// Ignore the line, but check whether it indicates that another literal is on its way.
       
    87 			PeekLiteralRequest(aData);
       
    88 			
       
    89 			TParseState newParseState = (iLiteralBytesExpected > 0) 
       
    90 				? EWaitLiteralIngore	// a literal has been indicated on the line.  We need to ignore that too!
       
    91 				: EWaitStartResponse;	// we are not expecting any more data for this response, so wait for the next one.
       
    92 			SetParseState(newParseState);
       
    93 			}
       
    94 			break;
       
    95 		default:
       
    96 			{
       
    97 			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState3));
       
    98 			}
       
    99 		}
       
   100 
       
   101 	if (iCompleteOnAnyResponse && iParseState==EWaitStartResponse)
       
   102 		{
       
   103 		__LOG_TEXT(iLogId, "CImapCommand - Complete on Any Response");
       
   104 		__ASSERT_DEBUG(iResponseCode == EImapResponseNone, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidResponseCode));
       
   105 		
       
   106 		// Simulate command completion so that the session closes this command.		
       
   107 		iResponseCode = EImapResponseOk;
       
   108 		SetParseState(ECommandComplete);
       
   109 		}
       
   110 
       
   111 	// check some post-conditions: is session getting consistent data?
       
   112 	
       
   113 	// (iParseState == ECommandComplete) implies (iResponseCode != EImapResponseNone)
       
   114 	__ASSERT_DEBUG(iParseState == ECommandComplete ? iResponseCode != EImapResponseNone : ETrue, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandMismatchedParseStateAndResponseCode));
       
   115 	
       
   116 	// (iParseState != ECommandComplete) implies (iResponseCode == EImapResponseNone)
       
   117 	__ASSERT_DEBUG(iParseState != ECommandComplete ? iResponseCode == EImapResponseNone : ETrue, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandMismatchedParseStateAndResponseCode));
       
   118 				
       
   119 	return iParseState;
       
   120 	}
       
   121 
       
   122 /**
       
   123 Parses incoming line data.
       
   124 @param aLine the line to parse.
       
   125 */
       
   126 void CImapCommand::ParseLineL(const TDesC8& aLine)
       
   127 	{
       
   128 
       
   129 	// Should only be calling this method when parsing a line.
       
   130 	__ASSERT_DEBUG(iParseState == EWaitStartResponse || iParseState == EWaitLineParse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState4));
       
   131 	
       
   132 	PeekLiteralRequest(aLine);
       
   133 				
       
   134 	TParseBlockResult parseBlockResult = DoParseBlockL(aLine);
       
   135 	
       
   136 	if(parseBlockResult ==  ECompleteUntagged && iLiteralBytesExpected != 0)
       
   137 		return;
       
   138 	
       
   139 	switch (parseBlockResult)
       
   140 		{
       
   141 		case ECompleteTagged:
       
   142 			// No more data expected
       
   143 			__ASSERT_DEBUG(iLiteralBytesExpected == 0, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandResponseLiteralDataNotZero));
       
   144 			__ASSERT_DEBUG(iResponseCode != EImapResponseNone, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidResponseCode));
       
   145 			
       
   146 			SetParseState(ECommandComplete);
       
   147 			break;
       
   148 		case ECompleteUntagged:
       
   149 			// Wait for the next response line to be received
       
   150 			__ASSERT_DEBUG(iLiteralBytesExpected == 0, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandResponseLiteralDataNotZero));
       
   151 			
       
   152 			SetParseState(EWaitStartResponse);
       
   153 			break;
       
   154 		case EResponseIncomplete:
       
   155 			// Literal data is expected - this needs to be delivered to the subclass
       
   156 			__ASSERT_DEBUG(iLiteralBytesExpected > 0, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandResponseLiteralDataNotNonZero));
       
   157 			
       
   158 			SetParseState(EWaitLiteralParse);
       
   159 			break;
       
   160 		case ENotRecognised:
       
   161 			if (iParseState == EWaitLineParse)
       
   162 				{
       
   163 				__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState5));
       
   164 				}
       
   165 			else // EWaitStartResponse
       
   166 				{
       
   167 				// The line was not understood by the subclass, so try the generic parsing
       
   168 				parseBlockResult = ParseUnhandledBlockL(aLine);
       
   169 				__ASSERT_DEBUG(parseBlockResult != EResponseIncomplete, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseBlockResult)); // Not expected or currently supported
       
   170 				
       
   171 				// If we're swallowing an unsupported response, then we may need to swallow a literal block next
       
   172 				TParseState newParseState = (iLiteralBytesExpected > 0) 
       
   173 					? EWaitLiteralIngore	// a literal has been indicated on the line.  We need to ignore that too!
       
   174 					: EWaitStartResponse;	// we are not expecting any more data for this response	
       
   175 				SetParseState(newParseState);
       
   176 				}
       
   177 			break;
       
   178 		default:
       
   179 			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseBlockResult));
       
   180 			break;
       
   181 		}
       
   182 	}
       
   183 	
       
   184 /**
       
   185 Parses incoming literal data.
       
   186 @param aLiteralBlock the literal block to parse.
       
   187 */
       
   188 void CImapCommand::ParseLiteralL(const TDesC8& aLiteralBlock)
       
   189 	{
       
   190 	// Should only be calling this method when parsing a literal blcok.
       
   191 	__ASSERT_DEBUG(iParseState == EWaitLiteralParse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState6));
       
   192 	
       
   193 	TParseBlockResult parseBlockResult = DoParseBlockL(aLiteralBlock);
       
   194 	switch (parseBlockResult)
       
   195 		{
       
   196 		case ECompleteTagged:					
       
   197 		case ECompleteUntagged:
       
   198 			// Literals are always followed by a line, so the response can't be complete yet.
       
   199 			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseBlockResult));
       
   200 			break;
       
   201 		case EResponseIncomplete:
       
   202 			SetParseState(EWaitLineParse);
       
   203 			break;
       
   204 		case ENotRecognised: 
       
   205 			// Subclass asked for the literal block, so it ought to have parsed it.
       
   206 			// Fall through to default __ASSERT_DEBUG below.
       
   207 		default:
       
   208 			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseBlockResult));
       
   209 			break;
       
   210 		}
       
   211 	}
       
   212 
       
   213 /**
       
   214 During cancel this method is called whenever the start of a new response is received.
       
   215 It doesn't do any parsing, other than to detect the closing tagged response, 
       
   216 which completes the command and means there is no more data to receive.
       
   217 @param aLine the line containing the start of a new response.
       
   218 */
       
   219 void CImapCommand::ParseStartResponseFlushL(const TDesC8& aLine)
       
   220 	{
       
   221 	__LOG_TEXT(iLogId, "CImapCommand::ParseStartResponseFlushL()");
       
   222 	
       
   223 	// should only call this method when parsing an incoming response but not cancelling
       
   224 	__ASSERT_DEBUG(iParseState == EWaitStartResponse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState7));
       
   225 	__ASSERT_DEBUG(iTaggedResponsesToFlush > 0, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandNoTaggedResponsesToFlush));
       
   226 
       
   227 	TInt tagId = 0;
       
   228 	switch (GetTagTypeL(tagId))
       
   229 		{
       
   230 		case ETagged:
       
   231 			{
       
   232 			--iTaggedResponsesToFlush;
       
   233 			if (iTaggedResponsesToFlush == 0)
       
   234 				{
       
   235 				// Don't check the tagId during cancel.
       
   236 				iResponseCode = GetResponseStateCode();
       
   237 				SetParseState(ECommandComplete);	
       
   238 				}
       
   239 			else
       
   240 				{
       
   241 				__LOG_FORMAT((iLogId, "CImapCommand - Still have %d tagged response(s) to flush", iTaggedResponsesToFlush));
       
   242 				}			
       
   243 			}
       
   244 			break;
       
   245 		case EUntagged:
       
   246 			{
       
   247 			// Ignore the untagged response, but check whether it indicates that another literal is on its way.
       
   248 			PeekLiteralRequest(aLine);
       
   249 			TParseState newParseState = (iLiteralBytesExpected > 0) 
       
   250 				? EWaitLiteralIngore	// a literal has been indicated on the line.  We need to ignore that too!
       
   251 				: EWaitStartResponse;	// we are not expecting any more data for this response, so wait for the next one.
       
   252 			SetParseState(newParseState);
       
   253 			}
       
   254 			break;
       
   255 		case EContinuation:
       
   256 			{
       
   257 			// Not exepecting a continuation response
       
   258 			CorruptDataL(); 
       
   259 			}
       
   260 			break;
       
   261 		}
       
   262 	}
       
   263 
       
   264 /**
       
   265 Looks for {nnn} at the end of the line, determine whether literal bytes are expected.
       
   266 If literal bytes are expected, then iLiteralBytesExpected is updated to store the
       
   267 number of bytes expected.
       
   268 @return KErrNone if literal bytes are expected, otherwise a system-wide error code.
       
   269 */
       
   270 TInt CImapCommand::PeekLiteralRequest(const TDesC8& aLine)
       
   271 	{
       
   272 	// Minimum of 3 chars needed for literal specifier: {n}
       
   273 	TInt lineLength = aLine.Length();
       
   274 	if (lineLength < 3)
       
   275 		{
       
   276 		return KErrNotFound; // no request found.
       
   277 		}
       
   278 		
       
   279 	// Last char MUST be a }
       
   280 	if (aLine[lineLength-1] != '}')
       
   281 		{
       
   282 		return KErrNotFound; // no request found.
       
   283 		}
       
   284 	
       
   285 	// Reverse-Find matching '{'
       
   286 	TPtrC8 ptrLiteralRequest;
       
   287 	for (TInt i=lineLength-1; i>=0; --i)
       
   288 		{
       
   289 		if (aLine[i] == '{')
       
   290 			{
       
   291 			// Found matching brace.  Point at its contents only!
       
   292 			ptrLiteralRequest.Set(aLine.Mid(i+1, lineLength-i-2));
       
   293 			break; // from for loop
       
   294 			}
       
   295 		}
       
   296 	
       
   297 	if (ptrLiteralRequest.Length() == 0)
       
   298 		{
       
   299 		return KErrNotFound; // No matching brace found
       
   300 		}
       
   301 	
       
   302 	// Extract the value
       
   303 	TLex8 lex(ptrLiteralRequest);
       
   304 	TInt err = lex.Val(iLiteralBytesExpected);
       
   305 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)			
       
   306 	//	If message body is downloaded using FETCH BINARY. The number of bytes received for previous fetch is recorded here.
       
   307 	//	This valued is used to make sure that there are no extra body fetch command are issued 
       
   308 	iPreviousLiteralBytesReceived = iLiteralBytesExpected;
       
   309 #endif
       
   310 	
       
   311 #ifdef __IMAP_LOGGING
       
   312 	if (iLiteralBytesExpected > 0)
       
   313 		{
       
   314 		__LOG_FORMAT((iLogId, "CImapCommand - Found Literal Request: %d", iLiteralBytesExpected));
       
   315 		}	
       
   316 #endif //__IMAP_LOGGING
       
   317 
       
   318 	return err;
       
   319 	}
       
   320 
       
   321 /**
       
   322 This is called when the session has processed all the blocks in its input buffer, but the 
       
   323 command is not yet complete (so it is expecting more data).
       
   324 This allows subclasses of CImapCommand to commit any bulk operations while waiting for the next set of data.
       
   325 By default, this method does nothing.
       
   326 */
       
   327 void CImapCommand::WaitingForMoreDataL()
       
   328 	{}
       
   329 
       
   330 /**
       
   331 Call this method to stop fully parsing the command.
       
   332 Instead, discard all incoming server data until no more data is expected for this command.
       
   333 By default this means that the tagged response has been received.
       
   334 This method can be overridden.
       
   335 */
       
   336 void CImapCommand::Cancel()
       
   337 	{
       
   338 	__LOG_TEXT(iLogId, "CImapCommand::Cancel()");
       
   339 	EnterFlushingState();
       
   340 	}
       
   341 
       
   342 void CImapCommand::EnterFlushingState()
       
   343 	{
       
   344 	iFlushing = ETrue;
       
   345 	iTaggedResponsesToFlush = NumberOfTaggedResponsesExpected();
       
   346 	
       
   347 	__LOG_FORMAT((iLogId, "CImapCommand::EnterFlushingState() - %d tagged response(s) to flush", iTaggedResponsesToFlush));
       
   348 	
       
   349 	if (iParseState == EWaitLiteralParse)
       
   350 		{
       
   351 		SetParseState(EWaitLiteralIngore);
       
   352 		}
       
   353 	else if (iParseState == EWaitLineParse)
       
   354 		{
       
   355 		SetParseState(EWaitLineIgnore);
       
   356 		}
       
   357 	}
       
   358 
       
   359 TBool CImapCommand::Flushing() const
       
   360 	{
       
   361 	return iFlushing;
       
   362 	}
       
   363 
       
   364 /**
       
   365 This method tells the session whether the Flush operation is able to complete
       
   366 the command straight away.
       
   367 By default, a command that has not already been completed (and so destoyed)
       
   368 cannot be completed early by Flush, as it is still waiting for the tagged
       
   369 response.
       
   370 This method can be overridden by commands that can be completed earlier, such
       
   371 as commands that do not wait for a tagged response.
       
   372 @return EFalse.
       
   373 */
       
   374 TBool CImapCommand::CanCompleteFlushNow() const
       
   375 	{
       
   376 	// Any command that has completed should have been destroyed before this
       
   377 	// method is called.
       
   378 	__ASSERT_DEBUG(iParseState != ECommandComplete, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState8));
       
   379 	__ASSERT_DEBUG(Flushing(), TImapServerPanic::ImapPanic(TImapServerPanic::ECommandNotFlushing));
       
   380 	
       
   381 	return EFalse;
       
   382 	}
       
   383 
       
   384 /**
       
   385 Returns the number of tagged responses that are currently expected.
       
   386 This default implementation always returns one, as most commands on send one request
       
   387 and expect one tagged response.
       
   388 Pipelined commands that send more than one request at a time should override this method.
       
   389 @return one.
       
   390 */
       
   391 TInt CImapCommand::NumberOfTaggedResponsesExpected() const
       
   392 	{
       
   393 	return KDefaultNumberOfTaggedResponsesExpected;
       
   394 	}
       
   395 
       
   396 /**
       
   397 This API will return the result returned by the remote server to the issued IMAP command.
       
   398 
       
   399 @return The IMAP result code extracted from the incoming response or EImapResponseNone 
       
   400 		if the response has not yet been parsed.
       
   401 */ 
       
   402 CImapCommand::TResponseCode CImapCommand::ResponseCode() const
       
   403 	{
       
   404 	return iResponseCode;
       
   405 	}
       
   406 
       
   407 /**
       
   408 @return the number of bytes 
       
   409 */
       
   410 TInt CImapCommand::LiteralBytesExpected() const
       
   411 	{
       
   412 	return iLiteralBytesExpected;
       
   413 	}
       
   414 
       
   415 /**
       
   416 Static method that should a be called by all classes internal to CImapSession whenever they
       
   417 detect corrupt data - e.g. missing fields - that means that they can no longer process the 
       
   418 incoming data.
       
   419 
       
   420 This method will always leave with KErrCorrupt.
       
   421 */
       
   422 #ifdef __IMAP_LOGGING
       
   423 void CImapCommand::CorruptDataL(TInt aLogId)
       
   424 #else
       
   425 void CImapCommand::CorruptDataL(TInt /*aLogId*/)
       
   426 #endif //__IMAP_LOGGING
       
   427 // static method
       
   428 	{
       
   429 	__LOG_TEXT(aLogId, "CImapCommand::CorruptDataL() !!!");
       
   430 	User::Leave(KErrImapCorrupt);
       
   431 	}
       
   432 
       
   433 /**
       
   434 Non-static version of CorruptDataL() that provides the current log id
       
   435 */
       
   436 void CImapCommand::CorruptDataL()
       
   437 	{
       
   438 	CorruptDataL(iLogId);
       
   439 	}
       
   440 
       
   441 /**
       
   442 Static method that stores each part of the supplied data into an array based on 
       
   443 the delimiter value supplied in the aDelimiter argument.
       
   444 
       
   445 @param aDelimiter 	The character that delimits the end of one part and the begining of the next.
       
   446 @param aData 		The data that is to be split.
       
   447 @param aOutDelimitedPartsList Output array that recieves the list of parts.
       
   448 @param aMaxParts 	If greater than zero, this stops the search when aOutDelimitedPartsList 
       
   449 					has the specified number of parts.
       
   450 */
       
   451 void CImapCommand::GetDelimitedPartsL(TChar aDelimiter, const TDesC8& aData, RDesParts& aOutDelimitedPartsList, TInt aMaxParts /*=0*/)
       
   452 // static method
       
   453 	{
       
   454 	aOutDelimitedPartsList.Reset();
       
   455 	TInt dataLength = aData.Length();
       
   456 	
       
   457 	if (dataLength == 0)
       
   458 		{
       
   459 		// no data to parse, so nothing to do.
       
   460 		return;
       
   461 		}
       
   462 	
       
   463 	TInt currentPos = 0;
       
   464 	TPtrC8 currentSegment(aData);
       
   465 	
       
   466 	for(;;)
       
   467 		{
       
   468 		TInt found = currentSegment.Locate(aDelimiter);
       
   469 		if (found == KErrNotFound)
       
   470 			{
       
   471 			aOutDelimitedPartsList.AppendL(currentSegment);
       
   472 			break;
       
   473 			}
       
   474 		
       
   475 		currentSegment.Set(currentSegment.Left(found));
       
   476 		aOutDelimitedPartsList.AppendL(currentSegment);
       
   477 		
       
   478 		// Check if whether have enough parts yet
       
   479 		if (aMaxParts > 0 && aOutDelimitedPartsList.Count() >= aMaxParts)
       
   480 			{
       
   481 			break;
       
   482 			}
       
   483 		
       
   484 		// Move past the delimiter and set next segment
       
   485 		currentPos += (found+1);
       
   486 		if (currentPos<dataLength)
       
   487 			{
       
   488 			currentSegment.Set(aData.Mid(currentPos));
       
   489 			}
       
   490 		else
       
   491 			{
       
   492 			// No more data
       
   493 			break;
       
   494 			}	
       
   495 		}
       
   496 	}
       
   497 
       
   498 /**
       
   499 Static method that calculates the length in characters of the given tag id
       
   500 
       
   501 @param aTagId The tag id whose length in characters is to be calculated
       
   502 @return The length in characters of the given tag id
       
   503 */
       
   504 TInt CImapCommand::TagLength(TInt aTagId)
       
   505 // static method
       
   506 	{
       
   507 	// Count how many characters would be needed to represent aTagId as a string.
       
   508 	// Do this by count how many times aTagId can be divided by 10.
       
   509 	
       
   510 	TInt tagLength = 1; // values 0 to 9 have a length of 1 character
       
   511 	
       
   512 	while (aTagId > 9)
       
   513 		{
       
   514 		aTagId = aTagId / 10;
       
   515 		++tagLength;
       
   516 		}
       
   517 		
       
   518 	return tagLength;
       
   519 	}
       
   520 
       
   521 /**
       
   522 Encodes a Unicode mailbox name by:
       
   523 	- Converting it into ImapUtf7 format
       
   524 	- Quoting it if necessary
       
   525 @param aMailboxName the mailbox name in Unicode format.
       
   526 @return the mailbox name in ImapUtf7, quoted if necessary.
       
   527 */
       
   528 HBufC8* CImapCommand::EncodeMailboxNameForSendL(const TDesC16& aMailboxName)
       
   529 // static method
       
   530 	{
       
   531 	// According to section 9 of RFC3501, mailbox is an astring.
       
   532 	// 	
       
   533 	// That means that if it cannot be represented as an atom, then it must be 
       
   534 	// represented as either a quoted string or literal.
       
   535 	// We can rule out literal as explained below.
       
   536 	//
       
   537 	// An atom is a string that contains any 7-bit character other than "atom-specials"
       
   538 	//
       
   539 	// atom-specials consists of 
       
   540 	//
       
   541 	// 	"(" ")" "{" "%" "*" DQUOTE "\" "]" SP CTL
       
   542 	//
       
   543 	//
       
   544 	// The ImapUtf7 encoding has the following characteristics (from RFC3501).
       
   545 	//
       
   546 	// "printable US-ASCII characters, except for "&", represent 
       
   547 	// themselves; that is, characters with octet values 0x20-0x25 and 0x27-0x7e.
       
   548 	// All other characters (octet values 0x00-0x1f and 0x7f-0xff) are
       
   549 	// represented in modified BASE64, with a further modification from
       
   550 	// [UTF-7] that "," is used instead of "/". Modified BASE64 MUST NOT be
       
   551 	// used to represent any printing US-ASCII character which can represent
       
   552 	// itself.""
       
   553 	//
       
   554 	// "&" means start "Modified BASE64" block
       
   555 	// "-" means end "Modified BASE64" block
       
   556 	// "&-" represents the & character
       
   557 	//
       
   558 	// "Modified BASE64" can contain the following characters
       
   559 	// "a" to "z", "A" to "Z", "0" to "9" and then "+" "," and "=".  The last is the pad character.
       
   560 	//
       
   561 	//
       
   562 	// So an Imap-UTF7 mailbox string can always be sent as quoted (and hence no need for literal)
       
   563 	// because it conforms to the RFC3501 definition of a quoted string, which 
       
   564 	// may only contain 7-bit US-ASCII characters other than NUL, CR and LF (which are encoded as Modified BASE64)
       
   565 	//
       
   566 	//
       
   567 	// To decide whether to send the encoded mailbox as an atom or as quoted, we need to test whether
       
   568 	// the encoded string contains printable atom-specials (CTL characters including CR and LF have 
       
   569 	// already been dealt with by the ImapUtf7 encoding)
       
   570 	//
       
   571 	// Usefully, the set of atom-specials and the set of characters used in Modified BASE64 
       
   572 	// DO NOT intersect.  That makes it safe to search for (and escape) atom-specials in an 
       
   573 	// encoded ImapUtf7 string without inadvertantly finding or modifying a BASE64 encoding.
       
   574 	//
       
   575 	// Note that section 5.1 of RFC3501 allows mailbox names to include all the atom-specials, including
       
   576 	// list wildcards such as "*" and "%".  So we need to search for all printable atom-specials, not a subset.
       
   577 
       
   578 	if (aMailboxName.Length()==0)
       
   579 	    {
       
   580 	    // aMailboxName is an empty string so just return an empty string.
       
   581 	    _LIT8(KEmptyString, "\"\"");
       
   582 	    HBufC8* hBuf = KEmptyString().AllocL();
       
   583 	    return hBuf;
       
   584 	    }
       
   585 
       
   586 	// Convert into ImapUtf7.
       
   587 	HBufC8* mailboxNameUtf7 = CImapUtils::GetRef().Charconv().ConvertFromUnicodeToImapUtf7L(aMailboxName);
       
   588 	CleanupStack::PushL(mailboxNameUtf7);
       
   589 	
       
   590 	// Search for printable atom-specials and quote the mailbox if any are found
       
   591 	TInt countQuotedSpecials = 0;
       
   592 	if (CheckForPrintableAtomSpecial(*mailboxNameUtf7, countQuotedSpecials))
       
   593 		{		
       
   594 		// Make enough room to escape each of the quoted-specials and to insert the start and end quote
       
   595 		TInt mailboxNameLength = mailboxNameUtf7->Length() + countQuotedSpecials + 2;
       
   596 		HBufC8 *expandedBuffer = mailboxNameUtf7->ReAllocL(mailboxNameLength);
       
   597 		
       
   598 		// Been moved due to realloc?
       
   599 		if (expandedBuffer != mailboxNameUtf7)
       
   600 			{
       
   601 			// Update mailboxNameUtf7 with the new address
       
   602 			mailboxNameUtf7 = expandedBuffer;
       
   603 			expandedBuffer = NULL;
       
   604 			
       
   605 			// Update the cleanup stack with the new address of mailboxNameUtf7
       
   606 			CleanupStack::Pop();
       
   607 			CleanupStack::PushL(mailboxNameUtf7);
       
   608 			}
       
   609 
       
   610 		// Insert the opening DQUOTE
       
   611 		mailboxNameUtf7->Des().Insert(0, KImapTxtDoubleQuote);
       
   612 		
       
   613 		// Perform escaping if there are any quoted-specials
       
   614 		if (countQuotedSpecials > 0)
       
   615 			{
       
   616 			// Use the length of the newly expanded buffer, 
       
   617 			// but do not include the first or last characters, which are non-escaped DQUOTE's
       
   618 			// Hence start at index 1, and reduce the length by 2
       
   619 			mailboxNameLength -= 2;
       
   620 			for(TInt i=1; i<mailboxNameLength; ++i)
       
   621 				{
       
   622 				if ((*mailboxNameUtf7)[i]=='\\' || (*mailboxNameUtf7)[i]=='\"')
       
   623 					{
       
   624 					// We have found one of the quoted-specials, this needs to be escaped
       
   625 					mailboxNameUtf7->Des().Insert(i, KImapTxtEscape);
       
   626 					++i;
       
   627 					}
       
   628 				}	
       
   629 			}
       
   630 			
       
   631 		// Append the closing DQUOTE
       
   632 		mailboxNameUtf7->Des().Append(KImapTxtDoubleQuote);
       
   633 		}
       
   634 	
       
   635 	CleanupStack::Pop(mailboxNameUtf7);
       
   636 	return mailboxNameUtf7;
       
   637 	}
       
   638 
       
   639 /**
       
   640 Checks whether the given string contains printable atom-specials.
       
   641 An atom special is one of "(" ")" "{" "%" "*" DQUOTE "\" "]" SP
       
   642 @param aString The string that is to be checked
       
   643 @param aCountQuotedSpecials Output parameter that counts the number of quoted-specials characters that need escaping
       
   644 @return ETrue is any of the atom-specials are present in the string
       
   645 */
       
   646 TBool CImapCommand::CheckForPrintableAtomSpecial(const TDesC8& aString, TInt& aCountQuotedSpecials)
       
   647 // static method
       
   648 	{
       
   649 	aCountQuotedSpecials = 0;
       
   650 	TBool foundPrintableAtomSpecial = EFalse;
       
   651 
       
   652 	TInt stringLength = aString.Length();
       
   653 	for(TInt i=0; i<stringLength; ++i)
       
   654 		{
       
   655 		if (aString[i]=='(' || aString[i]==')' || aString[i]=='{' || aString[i]==' ' ||	// <<< specific printable atom-specials
       
   656 			aString[i]=='%' || aString[i]=='*' || 										// <<< list-wildcards
       
   657 			aString[i]==']')															// <<< resp-specials
       
   658 			{
       
   659 			foundPrintableAtomSpecial = ETrue;
       
   660 			}
       
   661 		else if (aString[i]=='\\' || aString[i]=='\"')									// <<< quoted-specials
       
   662 			{
       
   663 			foundPrintableAtomSpecial = ETrue;
       
   664 			++aCountQuotedSpecials;
       
   665 			}
       
   666 		}
       
   667 		
       
   668 	return foundPrintableAtomSpecial;
       
   669 	}
       
   670 
       
   671 /**
       
   672 Helper that is to be called by subclasses when they receive some data that is not specific to the class.
       
   673 This indicates that the data is probably unilateral notification data, such as EXISTS and RECENT
       
   674 This method will parse and handle the notification.
       
   675 @param aLine The line to be parsed and handled (not including CRLF).
       
   676 @return Whether the data was successfully parsed, recognised, etc
       
   677 */
       
   678 CImapCommand::TParseBlockResult CImapCommand::ParseUnhandledBlockL(const TDesC8& aLine)
       
   679 	{
       
   680 	TParseBlockResult result = ENotRecognised;	
       
   681 	
       
   682 	iUnparsedData.Set(aLine);
       
   683 	
       
   684 	// Check the tag type.  This method should only be called for untagged resposnes.
       
   685 	TInt tagId = 0;
       
   686 	TTagType tagType = GetTagTypeL(tagId);
       
   687 	__ASSERT_DEBUG(tagType == EUntagged, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidTagType));
       
   688 	
       
   689 	// Looking for one of
       
   690 	//
       
   691 	// number "EXISTS"
       
   692 	// number "RECENT"
       
   693 	// nz-number "EXPUNGE"
       
   694 	//
       
   695 	// Note that untagged "BYE" is ignored.
       
   696 	// This can occur during LOGOUT or prior to the server terminating the connection.
       
   697 	// For LOGOUT, we wait for the tagged OK.
       
   698 	// And transport handler detects server termination - so we don't need to respond to the early warning.
       
   699 	
       
   700 	TPtrC8 numberPart = GetNextPart();
       
   701 	TLex8 desToInt(numberPart);
       
   702 	
       
   703 	TInt messageCountOrId = 0; // This is a message count for all except EXPUNGE where it is a message id
       
   704 	TInt err = desToInt.Val(messageCountOrId);
       
   705 	
       
   706 	if (err == KErrNone)
       
   707 		{
       
   708 		TPtrC8 secondPart = GetNextPart();
       
   709 		
       
   710 		if((secondPart.CompareF(KImapTxtExpunge) == 0))
       
   711 			{
       
   712 			__LOG_FORMAT((iLogId, "CImapCommand::ParseUnhandledBlockL() - Found EXPUNGE: %d", messageCountOrId));
       
   713 			if (iSelectedFolderData != NULL && iSelectedFolderData->Exists())
       
   714 				{
       
   715 				iSelectedFolderData->AddExpungedMessageL(messageCountOrId);
       
   716 				}
       
   717 			result = ECompleteUntagged;
       
   718 			}
       
   719 		else if((secondPart.CompareF(KImapTxtExists) == 0))
       
   720 			{
       
   721 			__LOG_FORMAT((iLogId, "CImapCommand::ParseUnhandledBlockL() - Found EXISTS: %d", messageCountOrId));
       
   722 			if (iSelectedFolderData != NULL)
       
   723 				{
       
   724 				iSelectedFolderData->SetExists(messageCountOrId);	
       
   725 				}			
       
   726 			result = ECompleteUntagged;
       
   727 			}
       
   728 		else if((secondPart.CompareF(KImapTxtRecent) == 0))
       
   729 			{
       
   730 			__LOG_FORMAT((iLogId, "CImapCommand::ParseUnhandledBlockL() - Found RECENT: %d", messageCountOrId));
       
   731 			if (iSelectedFolderData != NULL)
       
   732 				{
       
   733 				iSelectedFolderData->SetRecent(messageCountOrId);
       
   734 				}			
       
   735 			result = ECompleteUntagged;
       
   736 			}
       
   737 		else if ((secondPart.CompareF(KImapTxtFetch) == 0))
       
   738 			{
       
   739 			// If a FETCH has been requested, then the FETCH response will be handled by the subclass.
       
   740 			// So getting here means that the FETCH was unsolicited.
       
   741 			// When means that it is LIKELY that this is a FETCH FLAGS response.
       
   742 			// We don't need to parse the resposne fully (a complex task), but instead, 
       
   743 			// just record the notification that flags have changed for a message.
       
   744 			// This will cause a resync of flags when the Protocol Controller next attempts to go IDLE.
       
   745 			__LOG_FORMAT((iLogId, "CImapCommand::ParseUnhandledBlockL() - Found unsolicited FETCH: %d", messageCountOrId));
       
   746 			SetMessageFlagsChanged();
       
   747 			result = ECompleteUntagged;
       
   748 			}
       
   749 		}
       
   750 	
       
   751 #ifdef __IMAP_LOGGING
       
   752 	if (result == ENotRecognised)
       
   753 		{
       
   754 		__LOG_TEXT(iLogId, "CImapCommand::ParseUnhandledBlockL() - Ignoring Unhandled Response");
       
   755 		}	
       
   756 #endif //__IMAP_LOGGING
       
   757 
       
   758 	return result;
       
   759 	}
       
   760 
       
   761 /**
       
   762 Helper function to return the tag of the response
       
   763 @param aTagId Will contain the Tag if response is tagged.
       
   764 @return Returns the tag type as an enumeration.
       
   765 */
       
   766 CImapCommand::TTagType CImapCommand::GetTagTypeL(TInt& aTagId)
       
   767 	{
       
   768 	TPtrC8 nextPart = GetNextPart() ;
       
   769 	if(nextPart.Find(KImapTxtContinuation) >= 0)
       
   770 	 	{
       
   771 	 	__LOG_TEXT(iLogId, "CImapCommand::GetTagTypeL() - Found Continuation");
       
   772 	 	return EContinuation ;
       
   773 	 	}
       
   774 	else if(nextPart.Find(KImapTxtUntagged) >= 0)
       
   775 		{
       
   776 		// Don't log this, as it's the most common, and causes bloat!
       
   777 		return EUntagged;
       
   778 		}
       
   779 	else 
       
   780 		{
       
   781 		TLex8 desToInt(nextPart);
       
   782 		if (desToInt.Val(aTagId) != KErrNone)
       
   783 			{
       
   784 			// We were expecting a numeric tag id here.
       
   785 			__LOG_TEXT(iLogId, "CImapCommand::GetTagTypeL() - Non-numeric tag id");
       
   786 			CorruptDataL();
       
   787 			}
       
   788 		__LOG_FORMAT((iLogId, "CImapCommand::GetTagTypeL() - Found Tagged: %d", aTagId));
       
   789 		}
       
   790 	return ETagged;
       
   791 	}
       
   792 
       
   793 /**
       
   794 Helper method to return the next part of the response from server.
       
   795 @return nextpart from the iUnparsedData
       
   796 		KNullDesC8 if no more data to return.
       
   797 */
       
   798 TPtrC8 CImapCommand::GetNextPart()
       
   799 	{
       
   800 	TPtrC8 nextPart = iUnparsedData;
       
   801 	
       
   802 	// If the first character is a space, then skip it.	
       
   803 	if (iUnparsedData.Length() > 0)
       
   804 		{
       
   805 		if (iUnparsedData[0] == ' ')
       
   806 			{
       
   807 			iUnparsedData.Set(iUnparsedData.Mid(1));
       
   808 			}
       
   809 		}
       
   810 		
       
   811 	if (iUnparsedData.Length() > 0)
       
   812 		{
       
   813 		TInt length = iUnparsedData.Length();
       
   814 		// Some server may send n FETCH (UID n BODY[1]<0> "Message Body" BODY[1.MIME] {nnn}
       
   815 		// So extract the body from this response.
       
   816 		if(iUnparsedData[0] == '"')
       
   817 			{
       
   818 			for (TInt i=length-1; i>=1; --i)
       
   819 				{
       
   820 				if (iUnparsedData[i] == '"')
       
   821 					{
       
   822 					// Found matching brace.  Point at its contents only!
       
   823 					nextPart.Set(iUnparsedData.Mid(0, i + 1));
       
   824 					iUnparsedData.Set(iUnparsedData.Mid(i+1));
       
   825 					if(iUnparsedData.Length() > 0)
       
   826 						{
       
   827 						if (iUnparsedData[0] == ' ')
       
   828 							{
       
   829 							iUnparsedData.Set(iUnparsedData.Mid(1));
       
   830 							}
       
   831 						}
       
   832 					break; // from for loop
       
   833 					}
       
   834 				}
       
   835 			}
       
   836 		else
       
   837 			{
       
   838 			TInt offset = iUnparsedData.Locate(' ');
       
   839 			if (offset != KErrNotFound)
       
   840 				{
       
   841 				nextPart.Set(iUnparsedData.Left(offset));
       
   842 				if(iUnparsedData.Length() > offset + 1)
       
   843 					{
       
   844 					iUnparsedData.Set(iUnparsedData.Mid(offset + 1));
       
   845 					}
       
   846 				else
       
   847 					{
       
   848 					iUnparsedData.Set(KNullDesC8);
       
   849 					}
       
   850 				}
       
   851 			else
       
   852 				{
       
   853 				iUnparsedData.Set(KNullDesC8);
       
   854 				}
       
   855 			}
       
   856 		}
       
   857 	return 	nextPart;
       
   858 	}
       
   859 
       
   860 /**
       
   861 Helper method to return the next part of the response from server.
       
   862 The Unparsed Data pointer is not updated by this routine
       
   863 @return nextpart from the iUnparsedData
       
   864 		KNullDesC8 if no more data to return.
       
   865 */
       
   866 TPtrC8 CImapCommand::PeekNextPart()
       
   867 	{
       
   868 	TPtrC8 nextPart = iUnparsedData;
       
   869 
       
   870 	while ((nextPart.Length() > 0) && (nextPart[0] == ' '))
       
   871 		{
       
   872 		nextPart.Set(nextPart.Mid(1));
       
   873 		}
       
   874 
       
   875 	if (nextPart.Length() > 0)
       
   876 		{
       
   877 		TInt offset = nextPart.Locate(' ');
       
   878 		if (offset != KErrNotFound)
       
   879 			{
       
   880 			nextPart.Set(nextPart.Left(offset));
       
   881 			}
       
   882 		}
       
   883 	else
       
   884 		{
       
   885 		nextPart.Set(KNullDesC8);
       
   886 		}
       
   887 
       
   888 	return nextPart;
       
   889 	}
       
   890 
       
   891 /**
       
   892 Remainder()
       
   893 @return the remaining unparsed data
       
   894 */
       
   895 TPtrC8 CImapCommand::Remainder()
       
   896 	{
       
   897 	return iUnparsedData;
       
   898 	}
       
   899 
       
   900 /**
       
   901 Helper method to parse the "["resp-text-code"]" type response from server.
       
   902 iUnparsedData is updated to next part of response only if [] is found.
       
   903 @return resp-text-code without the square brackets([]).
       
   904         KNullDesC8 if "[" not found.
       
   905 */
       
   906 TPtrC8 CImapCommand::GetResponseTextCodeL()
       
   907 	{
       
   908 	if(iUnparsedData.Length()==0)
       
   909 		{
       
   910 		iUnparsedData.Set(KNullDesC8);
       
   911 		return iUnparsedData;	
       
   912 		}
       
   913 		
       
   914 	TPtrC8 response = iUnparsedData;
       
   915 	if(response[0] == '[')
       
   916 		{
       
   917 		TInt offset = iUnparsedData.Locate(']');
       
   918 		// No closing "]" ,possibly data may be corrupt.
       
   919 		if(offset== KErrNotFound)
       
   920 			{
       
   921 			__LOG_TEXT(iLogId, "CImapCommand::GetResponseTextCodeL() - No Closing ']'");
       
   922 			CorruptDataL();
       
   923 			}
       
   924 		
       
   925 		response.Set(iUnparsedData.Mid(1,offset-1));
       
   926 		if (iUnparsedData.Length() > offset + 1)
       
   927 			{
       
   928 			// Move to next non-space part after "]".
       
   929 			iUnparsedData.Set(iUnparsedData.Mid(offset+1));
       
   930 			}
       
   931 		else	
       
   932 			{
       
   933 			iUnparsedData.Set(KNullDesC8);
       
   934 			}
       
   935 			
       
   936 		__LOG_FORMAT((iLogId, "CImapCommand::GetResponseTextCodeL() - Found %S", &response));
       
   937 		}
       
   938 		
       
   939 		//Case where there is no [ ] for Server Response from servers like tuukka
       
   940 		//(ie) * OK PERMANENTFLAGS (\seen \answered \flagged \deleted \draft \priority)  
       
   941 		//we would be returning iUnparsedData, without editing the data
       
   942 
       
   943 	return response;	
       
   944 	}
       
   945 	
       
   946 /**
       
   947 Helper method to return the state of the server response.
       
   948 iUnparsedData is updated to next part of response only if OK/NO/BAD is found.
       
   949 @return EImapResponseOk if OK is returned by server
       
   950 		EImapResponseNo if NO is returned by server
       
   951 		EImapResponseBad if BAD is returned by server
       
   952 		EImapResponseNone in all other cases. 
       
   953 */	
       
   954 CImapCommand::TResponseCode CImapCommand::GetResponseStateCode()	
       
   955 	{
       
   956 	TResponseCode result = EImapResponseNone;
       
   957 	TInt offset = 0;
       
   958 	TBool update = EFalse;
       
   959 	offset = iUnparsedData.Locate(' ');
       
   960 
       
   961 	TPtrC8 nextPart = iUnparsedData;
       
   962 	if (offset != KErrNotFound)
       
   963 		{
       
   964 		nextPart.Set(iUnparsedData.Left(offset));
       
   965 		}
       
   966 		
       
   967 	if(nextPart.CompareF(KImapTxtOk) == 0) 
       
   968 		{
       
   969 		__LOG_TEXT(iLogId, "CImapCommand::GetResponseStateCode() - Found \"OK\"");
       
   970 	 	result = EImapResponseOk ;
       
   971 	 	update = ETrue;
       
   972 	 	}
       
   973 	else if(nextPart.CompareF(KImapTxtNo) == 0) 
       
   974 		{
       
   975 		__LOG_TEXT(iLogId, "CImapCommand::GetResponseStateCode() - Found \"NO\"");
       
   976 		result = EImapResponseNo;
       
   977 		update = ETrue;
       
   978 		}
       
   979 	else if(nextPart.CompareF(KImapTxtBad) == 0) 
       
   980 		{
       
   981 		__LOG_TEXT(iLogId, "CImapCommand::GetResponseStateCode() - Found \"BAD\"");
       
   982 	 	result = EImapResponseBad;
       
   983 	 	update = ETrue;
       
   984 		}
       
   985 		
       
   986 	if(update)
       
   987 		{
       
   988 		if(iUnparsedData.Length() > offset + 1)
       
   989 			{
       
   990 			iUnparsedData.Set(iUnparsedData.Mid(offset+1));		
       
   991 			}
       
   992 		else
       
   993 			{
       
   994 			iUnparsedData.Set(KNullDesC8);
       
   995 			}
       
   996 			
       
   997 		}
       
   998 	return result;
       
   999 	}
       
  1000 
       
  1001 void CImapCommand::SetParseState(TParseState aParseState)
       
  1002 	{
       
  1003 	if (aParseState == iParseState)
       
  1004 		{
       
  1005 		// Nothing to do.
       
  1006 		return;
       
  1007 		}
       
  1008 		
       
  1009 #ifdef __IMAP_LOGGING
       
  1010 
       
  1011 	_LIT8(KTxtWaitStartResponse, "EWaitStartResponse");
       
  1012 	_LIT8(KTxtWaitLiteralParse, "EWaitLiteralParse");
       
  1013 	_LIT8(KTxtWaitLineParse, "EWaitLineParse");
       
  1014 	_LIT8(KTxtWaitLiteralIngore, "EWaitLiteralIngore");
       
  1015 	_LIT8(KTxtWaitLineIgnore, "EWaitLineIgnore");
       
  1016 	_LIT8(KTxtCommandComplete, "ECommandComplete");
       
  1017 	_LIT8(KTxtParseStateUnknown, "Unknown");
       
  1018 	
       
  1019 	TPtrC8 ptrOldCommandParseState(KTxtParseStateUnknown);
       
  1020 	TPtrC8 ptrNewCommandParseState(KTxtParseStateUnknown);
       
  1021 	
       
  1022 	switch(iParseState)
       
  1023 		{
       
  1024 		case EWaitStartResponse:	ptrOldCommandParseState.Set(KTxtWaitStartResponse);		break;
       
  1025 		case EWaitLiteralParse:		ptrOldCommandParseState.Set(KTxtWaitLiteralParse);		break;
       
  1026 		case EWaitLineParse:		ptrOldCommandParseState.Set(KTxtWaitLineParse);			break;
       
  1027 		case EWaitLiteralIngore:	ptrOldCommandParseState.Set(KTxtWaitLiteralIngore);		break;
       
  1028 		case EWaitLineIgnore:		ptrOldCommandParseState.Set(KTxtWaitLineIgnore);		break;
       
  1029 		case ECommandComplete:		ptrOldCommandParseState.Set(KTxtCommandComplete);		break;
       
  1030 		}
       
  1031 		
       
  1032 	switch(aParseState)
       
  1033 		{
       
  1034 		case EWaitStartResponse:	ptrNewCommandParseState.Set(KTxtWaitStartResponse);		break;
       
  1035 		case EWaitLiteralParse:		ptrNewCommandParseState.Set(KTxtWaitLiteralParse);		break;
       
  1036 		case EWaitLineParse:		ptrNewCommandParseState.Set(KTxtWaitLineParse);			break;
       
  1037 		case EWaitLiteralIngore:	ptrNewCommandParseState.Set(KTxtWaitLiteralIngore);		break;
       
  1038 		case EWaitLineIgnore:		ptrNewCommandParseState.Set(KTxtWaitLineIgnore);		break;
       
  1039 		case ECommandComplete:		ptrNewCommandParseState.Set(KTxtCommandComplete);		break;
       
  1040 		}
       
  1041 	
       
  1042 	_LIT8(KLogFormat, "CImapCommand::iParseState %S ==> %S");
       
  1043 	__LOG_FORMAT((iLogId, KLogFormat, &ptrOldCommandParseState, &ptrNewCommandParseState));
       
  1044 	
       
  1045 #endif //__IMAP_LOGGING
       
  1046 
       
  1047 	iParseState = aParseState;
       
  1048 	}
       
  1049 
       
  1050 CImapCommand::TParseState CImapCommand::ParseState() const
       
  1051 	{
       
  1052 	return iParseState;
       
  1053 	}
       
  1054 
       
  1055 void CImapCommand::SetMessageFlagsChanged()
       
  1056 	{
       
  1057 	if (iSelectedFolderData != NULL)
       
  1058 		{
       
  1059 		__LOG_TEXT(iLogId, "CImapCommand - SetMessageFlagsChanged");
       
  1060 		iSelectedFolderData->SetMessageFlagsChanged(ETrue);
       
  1061 		}
       
  1062 	}
       
  1063 
       
  1064 //
       
  1065 //
       
  1066 //
       
  1067 
       
  1068 CImapCommandEx::CImapCommandEx(CImapFolderInfo* aSelectedFolderData, TInt aLogId)
       
  1069 	: CImapCommand(aSelectedFolderData, aLogId)
       
  1070 	{}
       
  1071 
       
  1072 CImapCommand::TParseBlockResult CImapCommandEx::DoParseBlockL(const TDesC8& /*aData*/)
       
  1073 	{
       
  1074 	TParseBlockResult result = ENotRecognised;
       
  1075 	
       
  1076 	switch (ParseState())
       
  1077 		{
       
  1078 		case EWaitStartResponse:
       
  1079 			{
       
  1080 			result = ParseStartResponseL();
       
  1081 			}
       
  1082 			break;
       
  1083 		case EWaitLiteralParse:
       
  1084 			{
       
  1085 			__LOG_TEXT(iLogId, "CImapCommand - calling ParseLiteralBlockL()");
       
  1086 			ParseLiteralBlockL();
       
  1087 			result = EResponseIncomplete;
       
  1088 			}
       
  1089 			break;
       
  1090 		case EWaitLineParse:
       
  1091 			{
       
  1092 			__LOG_TEXT(iLogId, "CImapCommand - calling ParseLineFollowingLiteralL()");
       
  1093 			TBool wantMoreData = ParseLineFollowingLiteralL();
       
  1094 			result = wantMoreData ? EResponseIncomplete : ECompleteUntagged;
       
  1095 			}
       
  1096 			break;
       
  1097 		default:
       
  1098 			{
       
  1099 			// This method should not be called for any other parse state
       
  1100 			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState9));
       
  1101 			}
       
  1102 			break;
       
  1103 		}
       
  1104 		
       
  1105 	return result;
       
  1106 	}
       
  1107 	
       
  1108 CImapCommand::TParseBlockResult CImapCommandEx::ParseStartResponseL()
       
  1109 	{
       
  1110 	TParseBlockResult result = ENotRecognised;
       
  1111 	
       
  1112 	TInt tagId = 0;
       
  1113 	switch (GetTagTypeL(tagId))
       
  1114 		{
       
  1115 		case ETagged:
       
  1116 			{
       
  1117 			__LOG_TEXT(iLogId, "CImapCommand - calling ParseTaggedResponseL()");
       
  1118 			TBool commandComplete = ParseTaggedResponseL(tagId);
       
  1119 			
       
  1120 			// If this tagged response does not complete the command (e.g. during fetch body)
       
  1121 			// then treat it as if it were an untagged resopnse.
       
  1122 			result = commandComplete ? ECompleteTagged : ECompleteUntagged;
       
  1123 			}
       
  1124 			break;
       
  1125 		case EUntagged:
       
  1126 			{
       
  1127 			__LOG_TEXT(iLogId, "CImapCommand - calling ParseUntaggedResponseL()");
       
  1128 			result = ParseUntaggedResponseL();
       
  1129 			}
       
  1130 			break;
       
  1131 		case EContinuation:
       
  1132 			{
       
  1133 			__LOG_TEXT(iLogId, "CImapCommand - calling ParseContinuationResponseL()");
       
  1134 			ParseContinuationResponseL();
       
  1135 			result = ECompleteUntagged;
       
  1136 			}
       
  1137 			break;
       
  1138 		default:
       
  1139 			{
       
  1140 			// unexpected tag type value
       
  1141 			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidTagType));
       
  1142 			CorruptDataL();
       
  1143 			break;
       
  1144 			}
       
  1145 		}
       
  1146 		
       
  1147 	return result;
       
  1148 	}
       
  1149 	
       
  1150 /**
       
  1151 Calls the Default handler with no further processing.
       
  1152 @see BaseParseTaggedResponseL()
       
  1153 @param aTagId The incoming tag id.
       
  1154 @return ETrue - indicating that the command is completed by this tagged response.
       
  1155 */
       
  1156 TBool CImapCommandEx::ParseTaggedResponseL(TInt aTagId)
       
  1157 	{
       
  1158 	BaseParseTaggedResponseL(aTagId);
       
  1159 	return ETrue;
       
  1160 	}
       
  1161 
       
  1162 /**
       
  1163 Default handler for a tagged response.
       
  1164 Records the response status code (OK/NO/BAD)
       
  1165 And checks that the incoming tag id matches the sent tag id.
       
  1166 Can be called by subclasses.
       
  1167 @param aTagId The incoming tag id.
       
  1168 */	
       
  1169 void CImapCommandEx::BaseParseTaggedResponseL(TInt aTagId)
       
  1170 	{
       
  1171 	// Check the tag id
       
  1172 	if (aTagId != iTagId)
       
  1173 		{
       
  1174 		// Unexpected tag id
       
  1175 		CorruptDataL();
       
  1176 		}
       
  1177 
       
  1178 	// Fetch and check the response code
       
  1179 	iResponseCode = GetResponseStateCode();
       
  1180 	if (iResponseCode == EImapResponseNone)
       
  1181 		{
       
  1182 		// Was expecting one of OK/NO/BAD, but didn't get it.  This is a parse error.
       
  1183 		CorruptDataL();
       
  1184 		}
       
  1185 	}
       
  1186 
       
  1187 /**
       
  1188 Default handler for a continuation response.
       
  1189 Always leaves with KErrImapCorrupt, because by default commands do not expect continuation responses.
       
  1190 */
       
  1191 void CImapCommandEx::ParseContinuationResponseL()
       
  1192 	{
       
  1193 	CorruptDataL();
       
  1194 	}
       
  1195 
       
  1196 /**
       
  1197 Default handler for an untagged response.
       
  1198 Always treats the response as unrecognised, causing ParseUnhandledBlockL() to be called later.
       
  1199 This default is used by commands that expect no specific responses.
       
  1200 @return ENotRecognised
       
  1201 */
       
  1202 CImapCommand::TParseBlockResult CImapCommandEx::ParseUntaggedResponseL()
       
  1203 	{	
       
  1204 	return ENotRecognised;
       
  1205 	}
       
  1206 
       
  1207 /**
       
  1208 Default handler for a block of literal data.
       
  1209 Always leaves with KErrImapCorrupt, because by default commands do not expect literal data.
       
  1210 */
       
  1211 void CImapCommandEx::ParseLiteralBlockL()
       
  1212 	{
       
  1213 	CorruptDataL();
       
  1214 	}
       
  1215 
       
  1216 /**
       
  1217 Default handler for a line following a literal block.
       
  1218 Always leaves with KErrImapCorrupt, because by default commands do not expect literal data, let alone lines following literal data.
       
  1219 @return because this implementation of the method always leaves, no value can ever be returned.
       
  1220 */
       
  1221 TBool CImapCommandEx::ParseLineFollowingLiteralL()
       
  1222 	{
       
  1223 	CorruptDataL();
       
  1224 	return EFalse;
       
  1225 	}
       
  1226 
       
  1227 /***
       
  1228 To get the present TagId of the Command object.
       
  1229 @return the TagId of the Command object.
       
  1230 */	
       
  1231 TInt CImapCommand::GetTagId() 
       
  1232 	{
       
  1233 	return iTagId ;
       
  1234 	}
       
  1235