email/imap4mtm/imapsession/src/cimapatomparser.cpp
changeset 0 72b543305e3a
equal deleted inserted replaced
-1:000000000000 0:72b543305e3a
       
     1 // Copyright (c) 1998-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 "cimapatomparser.h"
       
    17 #include "cimapatom.h"
       
    18 #include "cimapcommand.h"
       
    19 #include "imappaniccodes.h"
       
    20 
       
    21 // Initial parser buffer size & granularity
       
    22 const TInt KIOBufferSize=1280;
       
    23 const TInt KIOBufferGranularity=256;
       
    24 
       
    25 
       
    26 CImapAtomParser::CImapAtomParser(TBool aAllowParseAtTopLevel, TInt aLogId)
       
    27 	: iAllowParseAtTopLevel(aAllowParseAtTopLevel)
       
    28 	, iBufferSize(KIOBufferSize)
       
    29 	, iLogId(aLogId)
       
    30 	{}
       
    31 
       
    32 CImapAtomParser::~CImapAtomParser()
       
    33 	{
       
    34 	// Dispose of buffer
       
    35 	delete iBuffer;
       
    36 
       
    37 	// Delete any atom tree that may still exist
       
    38 	delete iRootAtom;
       
    39 
       
    40 	// delete the atom array
       
    41 	iAtomArray.ResetAndDestroy();
       
    42 	iAtomStack.Reset();
       
    43 	}
       
    44 
       
    45 // Create and call non-trivial contructor
       
    46 CImapAtomParser *CImapAtomParser::NewL(TBool aAllowParseAtTopLevel, TInt aLogId)
       
    47 	{
       
    48 	CImapAtomParser* self=new (ELeave) CImapAtomParser(aAllowParseAtTopLevel, aLogId);
       
    49 	CleanupStack::PushL(self);
       
    50 	self->ConstructL();
       
    51 	CleanupStack::Pop();
       
    52 	return self;
       
    53 	}
       
    54 
       
    55 // The non-trivial constructor
       
    56 void CImapAtomParser::ConstructL()
       
    57 	{
       
    58 	// Get initial buffer
       
    59 	iBuffer=HBufC8::NewL(iBufferSize);
       
    60 	
       
    61 	iRootAtom=CImapAtom::NewLC();
       
    62 	CleanupStack::Pop(iRootAtom);
       
    63 	
       
    64 	iAtom=iRootAtom;
       
    65 	iNextIsChild=ETrue;
       
    66 	}
       
    67 
       
    68 // Return the root atom
       
    69 CImapAtom* CImapAtomParser::RootAtom()
       
    70 	{
       
    71 	// Return it
       
    72 	return(iRootAtom);
       
    73 	}
       
    74 
       
    75 /**
       
    76 @return a descriptor that points at the unparsed portion of the last line to be processed.
       
    77 This pointer descriptor is only valid while the descriptor it points at is valid
       
    78 This pointer descriptor will only be set after ProcessLineL() has returned
       
    79 EFalse to indicate that no more data is expected.
       
    80 */
       
    81 const TPtrC8& CImapAtomParser::UnparsedData()
       
    82 	{
       
    83 	return iUnparsedData;
       
    84 	}
       
    85 	
       
    86 /**
       
    87 Returns the internal data buffer, transferring ownership of the buffer to the caller.
       
    88 The internal data buffer pointer is set to NULL.
       
    89 This method should only be called after parsing is complete.
       
    90 @return the data buffer.
       
    91 */
       
    92 HBufC8*	CImapAtomParser::DetachBuffer()
       
    93 // Note that the iBuffer pointer can change during parsing (via ReAllocL)
       
    94 // So we only want to expose iBuffer after parsing.
       
    95 	{
       
    96 	// Check for internal programming error
       
    97 	__ASSERT_DEBUG(iParserState == EStateParseComplete, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserInvalidParserState));
       
    98 	
       
    99 	HBufC8* detachedBuffer = iBuffer;
       
   100 	iBuffer = NULL;
       
   101 	
       
   102 	return detachedBuffer;
       
   103 	}
       
   104 
       
   105 // Deal with the atom stack: this is used when building the atom tree
       
   106 void CImapAtomParser::PushL(CImapAtom* aAtom)
       
   107 	{
       
   108 	iAtomStack.AppendL(aAtom);
       
   109 	}
       
   110 
       
   111 CImapAtom* CImapAtomParser::PopL()
       
   112 	{
       
   113 	if (iAtomStack.Count() <= 0)
       
   114 		{
       
   115 		// Mismatched bracket
       
   116 		CImapCommand::CorruptDataL(iLogId);
       
   117 		}
       
   118 
       
   119 	TInt count = iAtomStack.Count();
       
   120 	CImapAtom* atom = iAtomStack[count-1];
       
   121 	iAtomStack.Remove(count-1);
       
   122 	return(atom);
       
   123 	}
       
   124 
       
   125 // Add to the parsed buffer
       
   126 void CImapAtomParser::BufferAppendL(const TChar aChar)
       
   127 	{
       
   128 	// Check for internal programming error
       
   129 	__ASSERT_DEBUG(iBuffer != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserBufferIsNull));
       
   130 	
       
   131 	// Does buffer need extending?
       
   132 	if (iBuffer->Length()==iBufferSize)
       
   133 		{
       
   134 		HBufC8 *oldbuffer=iBuffer;
       
   135 		const TText8 *oldbufptr=iBuffer->Ptr();
       
   136 
       
   137 		// Extend by granularity amount
       
   138 		iBufferSize+=KIOBufferGranularity;
       
   139 		iBuffer=iBuffer->ReAllocL(iBufferSize);
       
   140 
       
   141 		// Buffer moved?
       
   142 		if (iBuffer!=oldbuffer)
       
   143 			{
       
   144 			// Fixup buffer tree pointers
       
   145 			iRootAtom->FixupL(iBuffer,oldbufptr);
       
   146 			}
       
   147 		}
       
   148 
       
   149 	// Append the data
       
   150 	iBuffer->Des().Append(aChar);
       
   151 	}
       
   152 	
       
   153 // Add the last atom appended to the buffer to the tree.
       
   154 void CImapAtomParser::AddAtomL()
       
   155 	{
       
   156 	// Check for internal programming error
       
   157 	__ASSERT_DEBUG(iBuffer != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserBufferIsNull));
       
   158 	
       
   159 	TPtrC8 bufptr(iBuffer->Ptr()+iAtomStart, iBuffer->Length()-iAtomStart);
       
   160 
       
   161 	// Make a new current atom
       
   162 	CImapAtom* newAtom=CImapAtom::NewLC();
       
   163 	iAtomArray.AppendL(newAtom);
       
   164 	CleanupStack::Pop(newAtom);
       
   165 	
       
   166 	// Set pointers in it
       
   167 	newAtom->Set(bufptr, iParserQuoted);
       
   168 
       
   169 	// Add it as a child/sibling to the current atom
       
   170 	if (iNextIsChild)
       
   171 		{
       
   172 		iAtom->AddChild(newAtom);
       
   173 		}
       
   174 	else
       
   175 		{
       
   176 		iAtom->AddNext(newAtom);
       
   177 		}
       
   178 
       
   179 	// The next item should be a sibling
       
   180 	iNextIsChild=EFalse;
       
   181 
       
   182 	// Make new current
       
   183 	iAtom=newAtom;
       
   184 	}
       
   185 
       
   186 /**
       
   187 Receives a line of data.
       
   188 If parsing completes before the end of the line, then aLine is set to point 
       
   189 at the octet immediately after the last parsed octet
       
   190 @return ETrue if more data is expected.
       
   191 */
       
   192 TBool CImapAtomParser::ProcessLineL(const TDesC8& aLine)
       
   193 	{
       
   194 	// Check for internal programming errors
       
   195 	__ASSERT_DEBUG(iParserState == EStateAtomWait, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserInvalidParserState));
       
   196 	__ASSERT_DEBUG(iBuffer != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserBufferIsNull));
       
   197 	
       
   198 	// Process the buffer
       
   199 	TChar octet;
       
   200 
       
   201 	TInt lineLength = aLine.Length();
       
   202 	
       
   203 	TBool bReachedTopLevel = EFalse;
       
   204 
       
   205 	TInt pos; // used after the loop
       
   206 	for(pos=0; pos<lineLength; ++pos)
       
   207 		{
       
   208 		// Octet to process
       
   209 		octet=aLine[pos];
       
   210 
       
   211 		switch(iParserState)
       
   212 			{
       
   213 		case EStateAtomWait:
       
   214 			switch(octet)
       
   215 				{
       
   216 			case '(':
       
   217 				{
       
   218 				// Make a new current atom
       
   219 				CImapAtom* newAtom=CImapAtom::NewLC();				
       
   220 				iAtomArray.AppendL(newAtom);
       
   221 				CleanupStack::Pop(newAtom);
       
   222 				
       
   223 				// Add it as a sibling to the current atom
       
   224 				if (iNextIsChild)
       
   225 					{
       
   226 					iAtom->AddChild(newAtom);
       
   227 					}
       
   228 				else
       
   229 					{
       
   230 					iAtom->AddNext(newAtom);	
       
   231 					}
       
   232 
       
   233 				// The next item should be a child
       
   234 				iNextIsChild=ETrue;
       
   235 
       
   236 				// Push current atom onto atom stack, make new current
       
   237 				iAtom=newAtom;
       
   238 				PushL(iAtom);
       
   239 
       
   240 				// Store the open bracket in the buffer, so we can tell what it is
       
   241 				BufferAppendL(octet);
       
   242 				iAtom->Set(iBuffer->Right(1), EFalse);
       
   243 
       
   244 				break;
       
   245 				}
       
   246 
       
   247 			case ')':
       
   248 				// End of this nesting level: pop last atom off stack and
       
   249 				// make it the new current one
       
   250 				iAtom=PopL();
       
   251 				if (iAtomStack.Count() == 0)
       
   252 					{
       
   253 					bReachedTopLevel = ETrue;
       
   254 					}
       
   255 
       
   256 				// Any new atoms will be siblings, not children
       
   257 				iNextIsChild=EFalse;
       
   258 
       
   259 				break;
       
   260 
       
   261 			case '{':
       
   262 				// Start of a literal length
       
   263 				iLiteralLength=0;
       
   264 				iParserQuoted=EFalse;
       
   265 				iParserState=EStateLiteralLength;
       
   266 				break;
       
   267 
       
   268 			case ' ':
       
   269 			case '\r':
       
   270 				// Whitespace. Ignore! This state only happens with whitespace
       
   271 				// after a close )] or a endquote "
       
   272 				// Note that the CRLF will already have been stripped out by CImapSession
       
   273 				// so we are treating a CR on its own as whitespace
       
   274 				break;
       
   275 			case '\"':
       
   276 				// Quotes: we don't keep them, so the atom starts at the next
       
   277 				// character.
       
   278 				iAtomStart=iBuffer->Length();
       
   279 				iParserState=EStateInAtom;
       
   280 				iParserQuoted=ETrue;
       
   281 				iGotEscape=EFalse;
       
   282 				break;
       
   283 
       
   284 			default:
       
   285 				// Start new atom in buffer
       
   286 				iAtomStart=iBuffer->Length();
       
   287 				BufferAppendL(octet);
       
   288 				iParserState=EStateInAtom;
       
   289 				iParserQuoted=EFalse;
       
   290 				break;
       
   291 				}
       
   292 			break;
       
   293 
       
   294 		case EStateInAtom:
       
   295 			if (iParserQuoted)
       
   296 				{
       
   297 				// Look for another quote
       
   298 				if (octet=='\"')
       
   299 					{
       
   300 					// Just had an escape character?
       
   301 					if (iGotEscape)
       
   302 						{
       
   303 						// Add the character
       
   304 						BufferAppendL(octet);
       
   305 						iGotEscape=EFalse;
       
   306 						}
       
   307 					else
       
   308 						{
       
   309 						// It's the terminator: Add the atom, minus the quotes
       
   310 						AddAtomL();
       
   311 						iParserState=EStateAtomWait;
       
   312 						}
       
   313 					}
       
   314 				else
       
   315 					{
       
   316 					// Escape character?
       
   317 					if (!iGotEscape && octet=='\\')
       
   318 						{
       
   319 						// Got one
       
   320 						iGotEscape=ETrue;
       
   321 						}
       
   322 					else
       
   323 						{
       
   324 						// Add to buffer
       
   325 						BufferAppendL(octet);
       
   326 						iGotEscape=EFalse;
       
   327 						}
       
   328 					}
       
   329 				}
       
   330 			else
       
   331 				{
       
   332 				switch(octet)
       
   333 					{
       
   334 					case ' ':
       
   335 					case '\r':
       
   336 						// Whitespace: end of atom
       
   337 						// Note that the CRLF will already have been stripped out by CImapSession
       
   338 						// so we are treating a CR on its own as whitespace
       
   339 						{
       
   340 						AddAtomL();
       
   341 					
       
   342 						// Either go back to looking for an atom, or a LF
       
   343 						//iParserState=(octet=='\r')?EStateWaitLF:EStateAtomWait;
       
   344 						iParserState = EStateAtomWait;
       
   345 						}
       
   346 						break;
       
   347 					case '(':
       
   348 						{
       
   349 						// Add this atom
       
   350 						AddAtomL();
       
   351 
       
   352 						// Make a new current atom
       
   353 						CImapAtom* newAtom=CImapAtom::NewLC();
       
   354 						iAtomArray.AppendL(newAtom);
       
   355 						CleanupStack::Pop(newAtom);
       
   356 
       
   357 						// Add it as a sibling to the current atom
       
   358 						if (iNextIsChild)
       
   359 							{
       
   360 							iAtom->AddChild(newAtom);
       
   361 							}
       
   362 						else
       
   363 							{
       
   364 							iAtom->AddNext(newAtom);
       
   365 							}						
       
   366 
       
   367 						// The next item should be a child
       
   368 						iNextIsChild=ETrue;
       
   369 
       
   370 						// Push current atom onto atom stack, make new current
       
   371 						iAtom=newAtom;
       
   372 						PushL(iAtom);
       
   373 
       
   374 						// Store the open bracket in the buffer, so we can tell what it is
       
   375 						BufferAppendL(octet);
       
   376 						iAtom->Set(iBuffer->Right(1), EFalse);
       
   377 
       
   378 						iParserState=EStateAtomWait;
       
   379 						}
       
   380 						break;
       
   381 					case ')':
       
   382 						{
       
   383 						// Although these bytes usually indicate the end of an atom,
       
   384 						// they can also legitimately appear in a text field.
       
   385 						// If this is the end of an atom, then it must be a child or
       
   386 						// sibling atom in which case there will be an entry on the atom
       
   387 						// stack. If there is no entry on the atom stack, then this must
       
   388 						// be a text field so just add the octet to the buffer.
       
   389 						if (iAtomStack.Count() > 0)
       
   390 							{
       
   391 							// Add this atom
       
   392 							AddAtomL();
       
   393 
       
   394 							// End of this nesting level: pop last atom off stack and
       
   395 							// make it the new current one
       
   396 							iAtom=PopL();
       
   397 							if (iAtomStack.Count() == 0)
       
   398 								{
       
   399 								bReachedTopLevel = ETrue;
       
   400 								}
       
   401 
       
   402 							// Any new atoms will be siblings, not children
       
   403 							iNextIsChild=EFalse;
       
   404 
       
   405 							iParserState=EStateAtomWait;
       
   406 							}
       
   407 						else
       
   408 							{
       
   409 							BufferAppendL(octet);
       
   410 							}
       
   411 						}
       
   412 						break;
       
   413 					default:
       
   414 						{
       
   415 						// Add to buffer
       
   416 						BufferAppendL(octet);
       
   417 						}
       
   418 						break;
       
   419 					}
       
   420 				}
       
   421 			break;
       
   422 		case EStateLiteralLength:
       
   423 			// Digit?
       
   424 			if (octet.IsDigit())
       
   425 				{
       
   426 				// Add it to the total
       
   427 				iLiteralLength=(iLiteralLength*10)+(octet-(TChar)'0');
       
   428 				if (iLiteralLength <0)
       
   429 					{
       
   430 					User::Leave(KErrCorrupt);
       
   431 					}					
       
   432 				}
       
   433 			else if (octet=='}')
       
   434 				{
       
   435 				// Need to skip CR, LF
       
   436 				iLiteralSkip=2;
       
   437 				iParserState=EStateLiteralSkip;
       
   438 
       
   439 				// Add the atom (with the length we know, but no data) to the
       
   440 				// structure now, so that the partial structure can be parsed.
       
   441 				iAtomStart=iBuffer->Length();
       
   442 				}
       
   443 			break;
       
   444 		case EStateLiteralSkip:
       
   445 			// Skipping...
       
   446 			if (--iLiteralSkip==0)
       
   447 				{
       
   448 				// Is literal 0 bytes long?
       
   449 				if (iLiteralLength==0)
       
   450 					{
       
   451 					// Nothing to follow
       
   452 					iParserState=EStateAtomWait;
       
   453 					}
       
   454 				else
       
   455 					{
       
   456 					// In some cases {nnn} would be present in the middle of FETCH header info ALSO  
       
   457 					// so need to process the array further
       
   458 					// Eg: *43 FETCH (FLAGS (\Seen $LuukkuClean $NotJunk JunkRecorded) UID 6285 BODYSTRUCTURE (("TEXT" "PLAIN" ("CHARSET" "ISO-8859-1") NIL NIL "7BIT" 4 0 NIL NIL NIL)("APPLICATION" "PDF" ("NAME" {25}
       
   459 					// Kopio_paperikirjeestä.pdf) NIL NIL "BASE64" 187346 NIL ("ATTACHMENT" ("FILENAME" {25} 
       
   460 					// Kopio_paperikirjeestä.pdf)) NIL) "MIXED" ("BOUNDARY" "----=_Part_7226_1401000605.1221158570821") NIL NIL) BODY[HEADER.FIELDS (Received Date Subject From Priority X-MSMail-Priority X-Priority Importance)] {977}
       
   461 					
       
   462 					// Checking for pos>=2, so that array is not out of bounds
       
   463 					if ((pos>=2) && (aLine[pos-2] != ']'))
       
   464 						{
       
   465 						 iParserState=EStateAtomWait;
       
   466 						}
       
   467 					else 
       
   468 						{ 
       
   469 						iParserState=EStateLiteralFetch;
       
   470 						// Add the atom (with the length we know, but no data) to the
       
   471 						// structure now, so that the partial structure can be parsed.
       
   472 						iAtomStart=iBuffer->Length();
       
   473 						AddAtomL();
       
   474 						}								
       
   475 					}
       
   476 				}
       
   477 			break;
       
   478 		case EStateLiteralFetch:
       
   479 			// Fetching
       
   480 				{
       
   481 				// Internal programming error:
       
   482 				// This state should only be handled in ProcessLiteralBlockL()
       
   483 				__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserInvalidParserState));
       
   484 				}
       
   485 			break;
       
   486 			} // switch(iParserState)
       
   487 			
       
   488 		if (bReachedTopLevel && !iAllowParseAtTopLevel)
       
   489 			{
       
   490 			break; // from for loop
       
   491 			}
       
   492 		}// for(...pos...)
       
   493 
       
   494 	// Exited early.  Record the un-parsed data
       
   495 	if (bReachedTopLevel)
       
   496 		{
       
   497 		// skip the close-bracket that caused us to reach the top
       
   498 		++pos;
       
   499 		// and record anything that remains
       
   500 		if (pos < lineLength)
       
   501 			{
       
   502 			iUnparsedData.Set(aLine.Right(lineLength-pos));
       
   503 			}		
       
   504 		}
       
   505 
       
   506 	// The whole buffer has been read now
       
   507 	if (iParserState == EStateInAtom)
       
   508 		{
       
   509 		AddAtomL();
       
   510 		iParserState = EStateAtomWait;
       
   511 		}
       
   512 	else if (iParserState == EStateLiteralLength)
       
   513 		{
       
   514 		// the line contained a '{' but did not have a matching  '}'
       
   515 		CImapCommand::CorruptDataL(iLogId);
       
   516 		}
       
   517 	
       
   518 	// Check for internal programming error
       
   519 	__ASSERT_DEBUG(iParserState == EStateAtomWait || iParserState == EStateLiteralFetch, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserInvalidParserState));
       
   520 		
       
   521 	TBool bWantMoreData = ETrue;
       
   522 	
       
   523 	if (iParserState == EStateAtomWait)
       
   524 		{
       
   525 		bWantMoreData = EFalse;
       
   526 		iParserState = EStateParseComplete;
       
   527 		}	
       
   528 	
       
   529 	return bWantMoreData;
       
   530 	}
       
   531 
       
   532 void CImapAtomParser::ProcessLiteralBlockL(const TDesC8& aLiteralBlock)
       
   533 	{
       
   534 	// Check for internal programming errors
       
   535 	__ASSERT_DEBUG(iParserState == EStateLiteralFetch, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserInvalidParserState));
       
   536 	__ASSERT_DEBUG(aLiteralBlock.Length() == iLiteralLength, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserLiteralBlockLengthMismatch));
       
   537 	__ASSERT_DEBUG(iBuffer != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserBufferIsNull));
       
   538 
       
   539 	// BufferAppendL() appends a character, whereas this appends a whole string
       
   540 	if (iBuffer->Length() + iLiteralLength > iBufferSize)
       
   541 		{
       
   542 		HBufC8 *oldbuffer=iBuffer;
       
   543 		const TText8 *oldbufptr=iBuffer->Ptr();
       
   544 	
       
   545 		// Extend by extra amount + round up by KIOBufferGranularity
       
   546 		iBufferSize += iLiteralLength;
       
   547 		iBufferSize += (KIOBufferGranularity - (iBufferSize % KIOBufferGranularity));
       
   548 		iBuffer=iBuffer->ReAllocL(iBufferSize);
       
   549 
       
   550 		// Buffer moved?
       
   551 		if (iBuffer!=oldbuffer)
       
   552 			{
       
   553 			// Fixup buffer tree pointers
       
   554 			iRootAtom->FixupL(iBuffer,oldbufptr);
       
   555 			}
       
   556 		}
       
   557 		
       
   558 	iBuffer->Des().Append(aLiteralBlock);
       
   559 	AddAtomL();
       
   560 
       
   561 	// literal is always followed by a line, so we need to be waiting for the next atom
       
   562 	iParserState=EStateAtomWait;
       
   563 	}
       
   564