email/pop3andsmtpmtm/imapservermtm/src/IMAPIO.CPP
changeset 25 84d9eb65b26f
equal deleted inserted replaced
23:238255e8b033 25:84d9eb65b26f
       
     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 // This deals with connection to the server, sending commands, and parsing the
       
    15 // returned server data. The data returned is buffered in iBuffer: this is
       
    16 // simply back-to-back data, with a tree which describes the structure
       
    17 // containing TPtrC's which identify the data belonging to each atom.
       
    18 // This class owns both the buffer and the tree, and deals with destroying them
       
    19 // as necessary. The client can request the base of the tree for it to parse
       
    20 // when a line has been received.
       
    21 // 980708 - modified to store flavour of ([ in parent atom
       
    22 // 980802 - partial read behavior modified (see release.txt)
       
    23 // 990112 - added cancel debug printing
       
    24 // 990304 - Tweaks to parsing of escape characters
       
    25 // 990415 - Now happy with ('s in the middle on non-quoted atoms.
       
    26 // This only generally happens with non-parsable text
       
    27 // (eg, after an OK response).
       
    28 // 
       
    29 //
       
    30 
       
    31 #include "imapio.h"
       
    32 #include "impspan.h"
       
    33 #include <imsk.h>
       
    34 #include <imcvtext.h>
       
    35 #include <msvstore.h>
       
    36 #include "imapsess.h"
       
    37 
       
    38 #define IMAPLOG
       
    39 
       
    40 #ifdef _DEBUG
       
    41 #define DBG(a) a
       
    42 #else
       
    43 #define DBG(a)
       
    44 #endif
       
    45 
       
    46 // Initial parser buffer size & granularity
       
    47 const TInt KIOBufferSize=1280;
       
    48 const TInt KIOBufferGranularity=256;
       
    49 
       
    50 // Specifies how long a socket is allowed to be inactive before we close it
       
    51 // down. This handles the situation where the mail server closes down the
       
    52 // connection, but we don't receive any indication of that. It has been seen
       
    53 // when connected using GPRS, and a long telephone call is then made.
       
    54 const TInt KImapSendInactivityTimeMinutes = 30;
       
    55 const TInt KImapReceiveInactivityTimeMinutes = 30;
       
    56 
       
    57 // The CImapAtom class contains a TPtrC which points to the data 'owned' by 
       
    58 // this atom in the buffer. It also contains next (sibling) and child pointers,
       
    59 // with which the tree is constructed.
       
    60 CImapAtom::CImapAtom()
       
    61 	{
       
    62 	__DECLARE_NAME(_S("CImapAtom"));
       
    63 	}
       
    64 
       
    65 CImapAtom::~CImapAtom()
       
    66 	{
       
    67 	}
       
    68 
       
    69 void CImapAtom::Set(const TDesC8& aAtom)
       
    70 	{
       
    71 	// Save this atom in here
       
    72 	iAtom.Set(aAtom);
       
    73 	}
       
    74 
       
    75 void CImapAtom::AddChild(CImapAtom *aNewChild)
       
    76 	{
       
    77 	// Set child pointer
       
    78 	iChild=aNewChild;
       
    79 	}
       
    80 
       
    81 void CImapAtom::AddNext(CImapAtom *aNewNext)
       
    82 	{
       
    83 	// Set next pointer
       
    84 	iNext=aNewNext;
       
    85 	}
       
    86 
       
    87 CImapAtom *CImapAtom::Child()
       
    88 	{
       
    89 	return(iChild);
       
    90 	}
       
    91 
       
    92 CImapAtom *CImapAtom::Next()
       
    93 	{
       
    94 	return(iNext);
       
    95 	}
       
    96 
       
    97 // ToChildL and ToNextL provide clients with a neat way of traversing the tree
       
    98 // in a direction that *should* exist: if it doesn't, a Leave occurs.
       
    99 CImapAtom *CImapAtom::ToChildL()
       
   100 	{
       
   101 	if (!iChild)
       
   102 		User::Leave(KErrNotFound);
       
   103 	return(iChild);
       
   104 	}
       
   105 
       
   106 CImapAtom *CImapAtom::ToNextL()
       
   107 	{
       
   108 	if (!iNext)
       
   109 		User::Leave(KErrNotFound);
       
   110 	return(iNext);
       
   111 	}
       
   112 
       
   113 // Makes things look neater: do a CompareF
       
   114 TBool CImapAtom::Compare(const TDesC8& aVal)
       
   115 	{
       
   116 	// Compare and return result
       
   117 	return(iAtom.CompareF(aVal)==0);
       
   118 	}
       
   119 
       
   120 // Compare the right hand side of the atom with the match string.
       
   121 TBool CImapAtom::CompareTail(const TDesC8& aVal)
       
   122 	{
       
   123 	if (aVal.Length() > iAtom.Length())
       
   124 		return EFalse;
       
   125 	TPtrC8 ptr = iAtom.Right(aVal.Length());
       
   126 	return(ptr.CompareF(aVal)==0);
       
   127 	}
       
   128 
       
   129 // Makes things look neater: lex a decimal atom to a TUint
       
   130 TInt CImapAtom::Value(TUint& aVal)
       
   131 	{
       
   132 	// Turn it into a value
       
   133 	TLex8 lex(iAtom);
       
   134 
       
   135 	return(lex.Val(aVal));
       
   136 	}
       
   137 
       
   138 // Makes things look neater: lex a decimal atom to a TInt
       
   139 TInt CImapAtom::Value(TInt& aVal)
       
   140 	{
       
   141 	// Turn it into a value
       
   142 	TLex8 lex(iAtom);
       
   143 
       
   144 	return(lex.Val(aVal));
       
   145 	}
       
   146 
       
   147 // Makes things look neater: lex a decimal atom to a TInt32
       
   148 TInt CImapAtom::Value(TInt32& aVal)
       
   149 	{
       
   150 	// Turn it into a value
       
   151 	TLex8 lex(iAtom);
       
   152 
       
   153 	return(lex.Val(aVal));
       
   154 	}
       
   155 
       
   156 // Return descriptor
       
   157 TPtrC8 CImapAtom::Atom()
       
   158 	{
       
   159 	return(iAtom);
       
   160 	}
       
   161 
       
   162 // Fixup descriptor pointers
       
   163 void CImapAtom::FixupL(const HBufC8 *aNewBuffer, const TText8 *aOldBuffer)
       
   164 	{
       
   165    // Fixup descriptor pointers
       
   166 	CArrayFixFlat<CImapAtom*>* atomStack = new (ELeave) CArrayFixFlat<CImapAtom*>(10);
       
   167 	CleanupStack::PushL(atomStack);
       
   168 
       
   169 	atomStack->AppendL(this);
       
   170 	CImapAtom* currentAtom;
       
   171 	while (atomStack->Count() != 0)
       
   172    		{
       
   173 		// Pop the top atom off of the stack
       
   174 		currentAtom = (*atomStack)[atomStack->Count() - 1];
       
   175  		atomStack->ResizeL(atomStack->Count() - 1);
       
   176  
       
   177 		// Fix up the current atom
       
   178 		if (currentAtom->Atom().Length()>0)
       
   179 			{
       
   180 			// Find offset from start of old buffer
       
   181 			TInt start=(currentAtom->Atom().Ptr()-aOldBuffer);
       
   182 
       
   183  			// Make new descriptor & assign it
       
   184 			TPtrC8 bufptr(aNewBuffer->Ptr()+start,currentAtom->Atom().Length());
       
   185 			currentAtom->iAtom.Set(bufptr); // Note that we are setting the real iAtom not the copy returned by Atom()
       
   186 			}
       
   187  
       
   188 		// Add the first sibling to the stack,
       
   189 		// subsequent siblings are added when this sibling is visited
       
   190 		CImapAtom* siblingAtom = currentAtom->Next();
       
   191 		if (siblingAtom)
       
   192 			atomStack->AppendL(siblingAtom);
       
   193 
       
   194    
       
   195 		// Add child to the stack
       
   196 		CImapAtom* childAtom = currentAtom->Child();
       
   197 		if (childAtom)
       
   198 			atomStack->AppendL(childAtom);
       
   199    		}
       
   200    
       
   201 	CleanupStack::PopAndDestroy(atomStack);
       
   202    	}
       
   203 
       
   204 // The IO processing class
       
   205 CImapIO::CImapIO(TInt aId) : CMsgActive(1), iBufferSize(KIOBufferSize), iAtomStack(8), iID(aId),iPrimaryTextServerSession(NULL)
       
   206 	{
       
   207 	__DECLARE_NAME(_S("CImapIO"));
       
   208 	}
       
   209 
       
   210 CImapIO::~CImapIO()
       
   211 	{
       
   212 	// Make sure we're cancelled
       
   213 	Disconnect();
       
   214 
       
   215 	delete iSession;
       
   216 
       
   217 	// Dispose of buffer
       
   218 	delete iBuffer;
       
   219 
       
   220 	// Delete any atom tree that may still exist
       
   221 	delete iRootAtom;
       
   222 
       
   223 	// Get rid of log
       
   224 	delete iImapLog;
       
   225 	// delete the atom array
       
   226 	iAtomArray.ResetAndDestroy();
       
   227 	}
       
   228 
       
   229 // Create and call non-trivial contructor
       
   230 CImapIO *CImapIO::NewLC(TInt aId)
       
   231 	{
       
   232 	CImapIO* self=new (ELeave) CImapIO(aId);
       
   233 	CleanupStack::PushL(self);
       
   234 	self->ConstructL();
       
   235 	return self;
       
   236 	}
       
   237 
       
   238 CImapIO *CImapIO::NewL(TInt aId)
       
   239 	{
       
   240 	CImapIO* self=NewLC(aId);
       
   241 	CleanupStack::Pop();
       
   242 	return self;
       
   243 	}
       
   244 
       
   245 // The non-trivial constructor
       
   246 void CImapIO::ConstructL()
       
   247 	{
       
   248 	// Get initial buffer
       
   249 	iBuffer=HBufC8::NewL(iBufferSize);
       
   250 	
       
   251 	// We're an active object...
       
   252 	CActiveScheduler::Add(this);
       
   253 	}
       
   254 
       
   255 // Called when an asynchronous child exits with status>=0
       
   256 void CImapIO::DoRunL()
       
   257 	{
       
   258 	DBG((LogText(_L8("CImapIO::DoRunL(state=%d, status=%d)"),iState,iStatus.Int())));
       
   259 
       
   260 	switch(iState)
       
   261 		{
       
   262 	case EIOStateConnectQueued:
       
   263 		// Connect has finished: OK?
       
   264 		if (iStatus.Int()==KErrNone)
       
   265 			{
       
   266 			// Done! We're connected
       
   267 			iState=EIOStateConnected;
       
   268 			}
       
   269 		else
       
   270 			{
       
   271 			// Close connection and report fail
       
   272 			iSession->Disconnect();
       
   273 			}
       
   274 		break;
       
   275 
       
   276 	case EIOStateReadQueued:
       
   277 		// Process it
       
   278 		iState=EIOStateConnected;
       
   279 		iRXbytes+=iReceive.Length();
       
   280 		ProcessBufferL();
       
   281 		break;
       
   282 
       
   283 	case EIOStateWriteQueued:
       
   284 		// Sent! Return error code directly
       
   285 		iState=EIOStateConnected;
       
   286 		break;
       
   287 
       
   288 	case EIOStateConnected:
       
   289 	case EIOStateDisconnected:
       
   290 	case EIOStateDummyQueued:
       
   291 		// Never happens, but pleases the compiler
       
   292 		break;
       
   293 		}
       
   294 	}
       
   295 
       
   296 // Called when an asynchronous child exits with status <KErrNone
       
   297 void CImapIO::DoComplete(TInt& /*aStatus*/)
       
   298 	{
       
   299 	// note this message might not get reported if the socket has closed
       
   300 
       
   301 	// pass all status codes up as given. KErrNone and KErrEOL (1)
       
   302 	// will be ignored. All others will cause a disconnect either in
       
   303 	// CImImap4Session's IdleReadError(), DummyComplete() or DoComplete().
       
   304 	}	
       
   305 
       
   306 // Called when parent wants to cancel an operation
       
   307 void CImapIO::DoCancel()
       
   308 	{
       
   309 
       
   310 	DBG((LogText(_L8("CImapIO::DoCancel(state=%d)"),iState)));
       
   311 	
       
   312 	if (iSession)
       
   313 		{
       
   314 		// Cancel actions
       
   315 		switch(iState)
       
   316 			{
       
   317 		case EIOStateConnectQueued:
       
   318 			// Cancel session and disconnect
       
   319 			iSession->Cancel();
       
   320 			iState=EIOStateDisconnected;
       
   321 			break;
       
   322 
       
   323 		case EIOStateReadQueued:
       
   324 		case EIOStateWriteQueued:
       
   325 			// Cancel it at the session level: this will return the cancelled error
       
   326 			// upwards, which should cause the above DoComplete to return
       
   327 			iSession->Cancel();
       
   328 			iState=EIOStateConnected;
       
   329 			break;
       
   330 
       
   331 		case EIOStateConnected:
       
   332 		case EIOStateDisconnected:
       
   333 			// Never happens, but pleases the compiler
       
   334 			break;
       
   335 		case EIOStateDummyQueued:
       
   336 			break;
       
   337 			}
       
   338 		}
       
   339 	else
       
   340 		iState=EIOStateDisconnected;
       
   341 
       
   342 	// Get msgactive to finish up
       
   343 	CMsgActive::DoCancel();
       
   344 
       
   345 	DBG((LogText(_L8("CImapIO::DoCancel done"))));
       
   346 
       
   347 	}
       
   348 
       
   349 // Return the root atom
       
   350 CImapAtom *CImapIO::RootAtom()
       
   351 	{
       
   352 	// Return it
       
   353 	return(iRootAtom);
       
   354 	}
       
   355 
       
   356 // Deal with the atom stack: this is used when building the atom tree
       
   357 void CImapIO::PushL(CImapAtom *aAtom)
       
   358 	{
       
   359 	iAtomStack.AppendL(aAtom);
       
   360 	}
       
   361 
       
   362 CImapAtom *CImapIO::PopL()
       
   363 	{
       
   364 	// Check it isn't empty
       
   365 	if (iAtomStack.Count()==0)
       
   366 		User::Leave(KErrUnderflow);
       
   367 
       
   368 	TInt count = iAtomStack.Count();
       
   369 	CImapAtom* atom = iAtomStack[count-1];
       
   370 	iAtomStack.Delete(count-1);
       
   371 	return(atom);
       
   372 	}
       
   373 
       
   374 // Add to the parsed buffer
       
   375 void CImapIO::BufferAppendL(const TChar aChar)
       
   376 	{
       
   377 	// Does buffer need extending?
       
   378 	if (iBuffer->Length()==iBufferSize)
       
   379 		{
       
   380 		HBufC8 *oldbuffer=iBuffer;
       
   381 		const TText8 *oldbufptr=iBuffer->Ptr();
       
   382 
       
   383 		// Extend by granularity amount
       
   384 		iBufferSize+=KIOBufferGranularity;
       
   385 		iBuffer=iBuffer->ReAllocL(iBufferSize);
       
   386 
       
   387 		// Buffer moved?
       
   388 		if (iBuffer!=oldbuffer)
       
   389 			{
       
   390 			// Fixup buffer tree pointers
       
   391 			iRootAtom->FixupL(iBuffer,oldbufptr);
       
   392 			}
       
   393 		}
       
   394 
       
   395 	// Append the data
       
   396 	iBuffer->Des().Append(aChar);
       
   397 	}
       
   398 
       
   399 // Add the last atom appended to the buffer to the tree.
       
   400 void CImapIO::AddAtomL()
       
   401 	{
       
   402 	// Add it
       
   403 	AddAtomL(iBuffer->Length()-iAtomStart);
       
   404 	}
       
   405 
       
   406 // Add the last atom, given a length
       
   407 void CImapIO::AddAtomL(const TInt aLength)
       
   408 	{
       
   409 	// Note buffer position in an atom
       
   410 	TPtrC8 bufptr(iBuffer->Ptr()+iAtomStart,aLength);
       
   411 
       
   412 	// Make a new current atom
       
   413 	CImapAtom *newAtom=new (ELeave) CImapAtom();
       
   414 
       
   415 	iAtomArray.AppendL(newAtom);
       
   416 	// Set pointers in it
       
   417 	newAtom->Set(bufptr);
       
   418 
       
   419 	// Add it as a child/sibling to the current atom
       
   420 	if (iNextIsChild)
       
   421 		iAtom->AddChild(newAtom);
       
   422 	else
       
   423 		iAtom->AddNext(newAtom);
       
   424 
       
   425 	// The next item should be a sibling
       
   426 	iNextIsChild=EFalse;
       
   427 
       
   428 	// Make new current
       
   429 	iAtom=newAtom;
       
   430 	}
       
   431 
       
   432 // Process the received buffer, creating atoms as we go
       
   433 void CImapIO::ProcessBufferL()
       
   434 	{
       
   435 	// Process the buffer
       
   436 	TChar byte;
       
   437 
       
   438 	DBG((LogText(_L8("CImapIO::ProcessBuffer(iParserState=%d, Length=%d)"),iParserState,iReceive.Length())));
       
   439 
       
   440 	for(TInt pos=0;pos<iReceive.Length();pos++)
       
   441 		{
       
   442 		// Note that we've processed stuff
       
   443 		iBytesRead++;
       
   444 
       
   445 		// Byte to process
       
   446 		byte=iReceive[pos];
       
   447 
       
   448 		switch(iParserState)
       
   449 			{
       
   450 		case EIOStateAtomWait:
       
   451 			switch(byte)
       
   452 				{
       
   453 			case '(':
       
   454 			case '[':
       
   455 			case '<':
       
   456 				{
       
   457 				// Make a new current atom
       
   458 				CImapAtom *newAtom=new (ELeave) CImapAtom();
       
   459 
       
   460 				iAtomArray.AppendL(newAtom);
       
   461 				// Add it as a sibling to the current atom
       
   462 				if (iNextIsChild)
       
   463 					iAtom->AddChild(newAtom);
       
   464 				else
       
   465 					iAtom->AddNext(newAtom);
       
   466 
       
   467 				// The next item should be a child
       
   468 				iNextIsChild=ETrue;
       
   469 
       
   470 				// Push current atom onto atom stack, make new current
       
   471 				iAtom=newAtom;
       
   472 				PushL(iAtom);
       
   473 
       
   474 				// Store the open bracket in the buffer, so we can tell what it is
       
   475 				TPtrC8 bufptr(iBuffer->Ptr()+iBuffer->Length(),1);
       
   476 				BufferAppendL(byte);
       
   477 				iAtom->Set(bufptr);
       
   478 
       
   479 				break;
       
   480 				}
       
   481 
       
   482 			case ')':
       
   483 			case ']':
       
   484 				// End of this nesting level: pop last atom off stack and
       
   485 				// make it the new current one
       
   486 				iAtom=PopL();
       
   487 
       
   488 				// Any new atoms will be siblings, not children
       
   489 				iNextIsChild=EFalse;
       
   490 
       
   491 				break;
       
   492 
       
   493 			case '{':
       
   494 				// Start of a literal length
       
   495 				iLiteralLength=0;
       
   496 				iParserState=EIOStateLiteralLength;
       
   497 				break;
       
   498 
       
   499 			case ' ':
       
   500 				// Whitespace. Ignore! This state only happens with whitespace
       
   501 				// after a close )] or a endquote "
       
   502 				break;
       
   503 
       
   504 			case '\r':
       
   505 				// Newline after a close )] or endquote "
       
   506 				iParserState=EIOStateWaitLF;
       
   507 				break;
       
   508 
       
   509 			case '\"':
       
   510 				// Quotes: we don't keep them, so the atom starts at the next
       
   511 				// character.
       
   512 				iAtomStart=iBuffer->Length();
       
   513 				iParserState=EIOStateInAtom;
       
   514 				iParserQuoted=ETrue;
       
   515 				iGotEscape=EFalse;
       
   516 				break;
       
   517 
       
   518 			default:
       
   519 				// Start new atom in buffer
       
   520 				iAtomStart=iBuffer->Length();
       
   521 				BufferAppendL(byte);
       
   522 				iParserState=EIOStateInAtom;
       
   523 				iParserQuoted=EFalse;
       
   524 				break;
       
   525 				}
       
   526 			break;
       
   527 
       
   528 		case EIOStateInAtom:
       
   529 			if (iParserQuoted)
       
   530 				{
       
   531 				// Look for another quote
       
   532 				if (byte=='\"')
       
   533 					{
       
   534 					// Just had an escape character?
       
   535 					if (iGotEscape)
       
   536 						{
       
   537 						// Add the character
       
   538 						BufferAppendL(byte);
       
   539 						iGotEscape=EFalse;
       
   540 						}
       
   541 					else
       
   542 						{
       
   543 						// It's the terminator: Add the atom, minus the quotes
       
   544 						AddAtomL();
       
   545 						iParserState=EIOStateAtomWait;
       
   546 						}
       
   547 					}
       
   548 				// fix for INC51597 and DEF053082:if a " has been missed out by the server, this will end the atom at a \r
       
   549 				else if(!iGotEscape && byte == '\r')
       
   550 					{
       
   551 					AddAtomL();
       
   552 					iParserState = EIOStateWaitLF;
       
   553 					}
       
   554 				else
       
   555 					{
       
   556 					// Escape character?
       
   557 					if (!iGotEscape && byte=='\\')
       
   558 						{
       
   559 						// Got one
       
   560 						iGotEscape=ETrue;
       
   561 						}
       
   562 					else
       
   563 						{
       
   564 						// Add to buffer
       
   565 						BufferAppendL(byte);
       
   566 						iGotEscape=EFalse;
       
   567 						}
       
   568 					}
       
   569 				}
       
   570 			else
       
   571 				{
       
   572 				if (byte==' ' || byte=='\r')
       
   573 					{
       
   574 					AddAtomL();
       
   575 				
       
   576 					// Either go back to looking for an atom, or a LF
       
   577 					iParserState=(byte=='\r')?EIOStateWaitLF:EIOStateAtomWait;
       
   578 					}
       
   579 				else if (byte=='(' || byte=='[')
       
   580 					{
       
   581 					// Add this atom
       
   582 					AddAtomL();
       
   583 
       
   584 					// Make a new current atom
       
   585 					CImapAtom *newAtom=new (ELeave) CImapAtom();
       
   586 					iAtomArray.AppendL(newAtom);
       
   587 
       
   588 					// Add it as a sibling to the current atom
       
   589 					if (iNextIsChild)
       
   590 						iAtom->AddChild(newAtom);
       
   591 					else
       
   592 						iAtom->AddNext(newAtom);
       
   593 
       
   594 					// The next item should be a child
       
   595 					iNextIsChild=ETrue;
       
   596 
       
   597 					// Push current atom onto atom stack, make new current
       
   598 					iAtom=newAtom;
       
   599 					PushL(iAtom);
       
   600 
       
   601 					// Store the open bracket in the buffer, so we can tell what it is
       
   602 					TPtrC8 bufptr(iBuffer->Ptr()+iBuffer->Length(),1);
       
   603 					BufferAppendL(byte);
       
   604 					iAtom->Set(bufptr);
       
   605 
       
   606 					iParserState=EIOStateAtomWait;
       
   607 					}
       
   608 				else if (byte==')' || byte==']' || byte == '>')
       
   609 					{
       
   610 					// Although these bytes usually indicate the end of an atom,
       
   611 					// they can also legitimately appear in a text field.
       
   612 					// If this is the end of an atom, then it must be a child or
       
   613 					// sibling atom in which case there will be an entry on the atom
       
   614 					// stack. If there is no entry on the atom stack, then this must
       
   615 					// be a text field so just add the byte to the buffer.
       
   616 					if (iAtomStack.Count() > 0)
       
   617 						{
       
   618 						// Add this atom
       
   619 						AddAtomL();
       
   620 
       
   621 						// End of this nesting level: pop last atom off stack and
       
   622 						// make it the new current one
       
   623 						iAtom=PopL();
       
   624 
       
   625 						// Any new atoms will be siblings, not children
       
   626 						iNextIsChild=EFalse;
       
   627 
       
   628 						iParserState=EIOStateAtomWait;
       
   629 						}
       
   630 					else
       
   631 						{
       
   632 						BufferAppendL(byte);
       
   633 						}
       
   634 					}
       
   635 				else
       
   636 					{
       
   637 					// Add to buffer
       
   638 					BufferAppendL(byte);
       
   639 					}
       
   640 				}
       
   641 			break;
       
   642 
       
   643 		case EIOStateWaitLF:
       
   644 			// After LF, this is end of line, finish!
       
   645 			if (byte=='\n')
       
   646 				{
       
   647 				// Remove everything from the buffer, we've finished
       
   648 				iReceive.Delete(0,pos+1);
       
   649 
       
   650 				// Reset bytes read count & complete
       
   651 				iBytesRead=0;
       
   652 
       
   653 				// Complete with KErrFoundEOL
       
   654 				DBG((LogText(_L8("CImapIO::ProcessBuffer: Complete in EIOStateWaitLF"))));
       
   655 				Complete(KErrFoundEOL);
       
   656 				return;
       
   657 				}
       
   658 			break;
       
   659 
       
   660 		case EIOStateLiteralLength:
       
   661 			// Digit?
       
   662 			if (byte.IsDigit())
       
   663 				{
       
   664 				// Add it to the total
       
   665 				iLiteralLength=(iLiteralLength*10)+(byte-(TChar)'0');
       
   666 				if (iLiteralLength <0)
       
   667 					User::Leave(KErrCorrupt);
       
   668 				}
       
   669 			else if (byte=='}')
       
   670 				{
       
   671 				// Need to skip CR, LF
       
   672 				iLiteralSkip=2;
       
   673 				iParserState=EIOStateLiteralSkip;
       
   674 
       
   675 				// Add the atom (with the length we know, but no data) to the
       
   676 				// structure now, so that the partial structure can be parsed.
       
   677 				iAtomStart=iBuffer->Length();
       
   678 				AddAtomL(iLiteralLength);
       
   679 				}
       
   680 			break;
       
   681 
       
   682 		case EIOStateLiteralSkip:
       
   683 			// Skipping...
       
   684 			if (--iLiteralSkip==0)
       
   685 				{
       
   686 				// Is literal 0 bytes long?
       
   687 				if (iLiteralLength==0)
       
   688 					{
       
   689 					// Nothing to follow
       
   690 					iParserState=EIOStateAtomWait;
       
   691 					}
       
   692 				else
       
   693 					{
       
   694 					// Non-empty literal: go into fetch state
       
   695 					iParserState=EIOStateLiteralFetch;
       
   696 					}
       
   697 				}
       
   698 			break;
       
   699 
       
   700 		case EIOStateLiteralFetch:
       
   701 			// Fetching
       
   702 			
       
   703 			TInt fetchLength(0);
       
   704 			if(KReceiveBuffer<iLiteralLength)
       
   705 				{
       
   706 				fetchLength = KReceiveBuffer;
       
   707 				}
       
   708 			else
       
   709 				{
       
   710 				fetchLength = iLiteralLength;
       
   711 				}
       
   712 			
       
   713 			if(fetchLength > iReceive.Length()-pos)
       
   714 				{
       
   715 				fetchLength = iReceive.Length()-pos;
       
   716 				}
       
   717 // 			need to extend buffer ?
       
   718 			DBG((LogText(_L8(">>> CImapIO::ProcessBufferL(1):buflen=%d:buffsize=%d:litlen=%d:fetchlen=%d"), iBuffer->Length(),iBufferSize, iLiteralLength,fetchLength)));
       
   719 			if (iBuffer->Length() + iLiteralLength > iBufferSize)
       
   720 				{
       
   721 				HBufC8 *oldbuffer=iBuffer;
       
   722 				const TText8 *oldbufptr=iBuffer->Ptr();
       
   723 			
       
   724 				// Extend by extra amount + round up by KIOBufferGranularity
       
   725 				iBufferSize += iLiteralLength;
       
   726 				iBufferSize += (KIOBufferGranularity - (iBufferSize % KIOBufferGranularity));
       
   727 				iBuffer=iBuffer->ReAllocL(iBufferSize);
       
   728 
       
   729 				// Buffer moved?
       
   730 				if (iBuffer!=oldbuffer)
       
   731 					{
       
   732 					// Fixup buffer tree pointers
       
   733 					iRootAtom->FixupL(iBuffer,oldbufptr);
       
   734 					}
       
   735 				DBG((LogText(_L8(">>> CImapIO::ProcessBufferL(2):buflen=%d:buffsize=%d:litlen=%d:fetchlen=%d"), iBuffer->Length(),iBufferSize, iLiteralLength,fetchLength)));
       
   736 				}
       
   737 			iBuffer->Des().Append(iReceive.Mid(pos,fetchLength));
       
   738 			// adjust loop to account for data copy
       
   739 			pos+=fetchLength-1;
       
   740 			iLiteralLength-=fetchLength; 			
       
   741 			DBG((LogText(_L8(">>> CImapIO::ProcessBufferL(3):buflen=%d:buffsize=%d:litlen=%d:fetchlen=%d"), iBuffer->Length(),iBufferSize, iLiteralLength,fetchLength)));
       
   742 			if (iLiteralLength==0)
       
   743 				{
       
   744 				// Atom is already saved (we add literal atoms before they
       
   745 				// are stored)
       
   746 				iParserState=EIOStateAtomWait;
       
   747 				}
       
   748 			break;
       
   749 			}
       
   750 		}
       
   751 	
       
   752 	iReceive.Zero();
       
   753 	// At start of line, or if we're not doing a partial return, we need to
       
   754 	// queue another read here
       
   755 	if (iBytesRead==0 || !iReturnPartialLine)
       
   756 		{
       
   757 		// We've processed this buffer: queue a read. We only complete (above)
       
   758 		// when we've had a whole reply, including terminating CRLF.
       
   759 		DBG((LogText(_L8("CImapIO::ProcessBufferL(): queuing read, iBytesRead=%d, iReturnPartialLine is %d"), iBytesRead, iReturnPartialLine)));
       
   760                 QueueRead();
       
   761 		}
       
   762 	else
       
   763 		{
       
   764 		// Have we got 'enough' of the partial line?
       
   765 		if (iBytesRead<iBytesToRead)
       
   766 			{
       
   767 			// Not enough yet: queue another partial read, for the remainder
       
   768 			iBytesToRead-=iBytesRead;
       
   769 			QueueRead();
       
   770 			}
       
   771 		else
       
   772 			{
       
   773 			// Partial line parsed: return success (client will requeue)
       
   774 			DBG((LogText(_L8("CImapIO::ProcessBuffer: Complete on partial line"))));
       
   775 			Complete(KErrNone);
       
   776 			}
       
   777 		}
       
   778 	}
       
   779 
       
   780 void CImapIO::QueueRead()
       
   781 	{
       
   782 	// Fill buffer
       
   783 	iLen=iBytesToRead;
       
   784 	iSession->ReceiveBinaryData(iStatus,iReceive,iLen);
       
   785 	iState=EIOStateReadQueued;
       
   786 	SetActive();
       
   787 	}
       
   788 
       
   789 // Close connection
       
   790 void CImapIO::Disconnect()
       
   791 	{
       
   792 	DBG((LogText(_L8("CImapIO::Disconnect(state=%d)"),iState)));
       
   793 
       
   794 	// Reset state
       
   795 	iState=EIOStateDisconnected;
       
   796 
       
   797 	// Delete session to stop timeouts (this will disconnect too)
       
   798 	delete iSession;
       
   799 	iSession=NULL;
       
   800 	}
       
   801 
       
   802 void CImapIO::SetTLSResponseL()
       
   803 	{
       
   804 	DBG((LogText(_L8("CImapIO::SetTLSResponseL"))));
       
   805 
       
   806 	iSession->SetSSLTLSResponseL(KIMAP_OK);
       
   807 	}
       
   808 
       
   809 /**
       
   810 	@fn				TInt GetIAPValue(TUint32& aIap)
       
   811 	Intended Usage	:	Gets the value of the currently connecting/connected IAP
       
   812 	@param			aIap will be value of the currently connecting/connected IAP or KErrNotFound if an error occurs 
       
   813 	@return			KErrNone if succesful, KErrNotFound is the session does not exist or a system-wide error code.
       
   814 	
       
   815 	*/
       
   816 TInt CImapIO::GetIAPValue(TUint32& aIap)
       
   817 	{
       
   818 	return (iSession) ? (iSession->GetIAPValue(aIap)) : (KErrNotFound);
       
   819 	}
       
   820 
       
   821 /**
       
   822 	@fn				TInt GetRConnectionName(TName &aName)
       
   823 	Intended Usage	:	On return, the unique name of the RConnection.
       
   824 	@since			9.1
       
   825 	@return			KErrNone if succesful, or another of the system-wide error codes. 
       
   826 	*/
       
   827 TInt CImapIO::GetRConnectionName(TName &aName)
       
   828 	{
       
   829 	return (iSession) ? (iSession->GetRConnectionName(aName)) : (KErrNotFound);
       
   830 	}
       
   831 
       
   832 // Returns the last socket activity timeout value for the session connection
       
   833 TInt CImapIO::GetLastSocketActivityTimeout(TUint32& aTimeout)
       
   834 	{
       
   835 	return (iSession) ? (iSession->GetLastSocketActivityTimeout(aTimeout)) : (KErrNotFound);
       
   836 	}
       
   837 
       
   838 /**
       
   839 	@fn				TInt GetConnectionStage()
       
   840 	Intended Usage	:	Gets the stage of the connection process as defined in nifvar.h and csdprog.h
       
   841 	@since			7.0s
       
   842 	@return         The current connection stage, KErrNotFound is the session does not exist or a system-wide error code
       
   843 
       
   844 	*/
       
   845 TInt CImapIO::GetConnectionStage()
       
   846 	{
       
   847 	return (iSession) ? (iSession->GetConnectionStage()) : (KErrNotFound);
       
   848 	}
       
   849 
       
   850 
       
   851 // Parent wants to queue a connection
       
   852 void CImapIO::ConnectL(TRequestStatus& aStatus, const TDesC& aHost, const TUint aPortNum, const CImIAPPreferences& aPrefs, TBool aSSLWrappedSocket)
       
   853 	{
       
   854 	// Have to be disconnected to connect...
       
   855 	__ASSERT_DEBUG(iState==EIOStateDisconnected,gPanic(EConnectWhenConnected));
       
   856 	if(!(iState==EIOStateDisconnected))
       
   857 		{
       
   858 		User::LeaveIfError(KErrInUse);// Connect when connected
       
   859 		}
       
   860 	// Queue request
       
   861 	Queue(aStatus);
       
   862 
       
   863 	// Delete current session (there shouldn't be one really)
       
   864 	delete iSession;
       
   865 	iSession=NULL;
       
   866 
       
   867 	// Reconstruct it
       
   868 	iSession=CImTextServerSession::NewL(KImapSendInactivityTimeMinutes, KImapReceiveInactivityTimeMinutes);
       
   869 	
       
   870 	// check local textseverssion is active.
       
   871 	if(iPrimaryTextServerSession)
       
   872 		{
       
   873 		// Providing primarysession's textserversession
       
   874 		// Going to be set on the secondary session
       
   875 		iSession->SetPrimaryTextServerSession(iPrimaryTextServerSession);	
       
   876 		}
       
   877 
       
   878 	// Ask session to connect (it does the resolving bit)
       
   879 	if(aSSLWrappedSocket)
       
   880 		{
       
   881 		iSession->SSLQueueConnectL(iStatus,aHost,aPortNum,aPrefs);
       
   882 		}
       
   883 	else
       
   884 		{
       
   885 		iSession->QueueConnectL(iStatus,aHost,aPortNum,aPrefs);
       
   886 		}
       
   887 
       
   888 	SetActive();
       
   889 	iState=EIOStateConnectQueued;
       
   890 	}
       
   891 
       
   892 // Parent wants the next line, all nicely parsed. When this completes, the
       
   893 // parent calls RootAtom() to get the root of the parse tree.
       
   894 TInt CImapIO::GetReply(TRequestStatus& aStatus)
       
   895 	{
       
   896 	// Call with maximum buffer size: no partial returns
       
   897 	return(GetReply(aStatus,KReceiveBuffer,EFalse));
       
   898 	}
       
   899 	
       
   900 TInt CImapIO::GetReply(TRequestStatus& aStatus, const TInt aFetchSize, const TBool aPartialReturn)
       
   901 	{
       
   902 	DBG((LogText(_L8("CImapIO::GetReply (aFetchSize=%d, aPartialReturn=%d)"),aFetchSize,aPartialReturn)));
       
   903 
       
   904 	// Disconnected?
       
   905 	if (iState==EIOStateDisconnected)
       
   906 		return(KErrDisconnected);
       
   907 
       
   908 	// Have to be connected & not busy
       
   909 	__ASSERT_ALWAYS(iState==EIOStateConnected,gPanic(EIOWhenNotReady));
       
   910 	
       
   911 	// Queue request
       
   912 	Queue(aStatus);
       
   913 
       
   914 	// Save max number of bytes to read in next request
       
   915 	iBytesToRead=aFetchSize;
       
   916 
       
   917 	// Partial return required? (ie return tree in as-is state when read
       
   918 	// completes)
       
   919 	iReturnPartialLine=aPartialReturn;
       
   920 
       
   921 	// Still busy (last request cancelled?), or still building tree?
       
   922 	if (iBytesRead)
       
   923 		{
       
   924 		// Carry on with this one, return it when ready
       
   925 		}
       
   926 	else
       
   927 		{
       
   928 		// New line
       
   929 
       
   930 		// Blank output buffer
       
   931 		iBuffer->Des().Zero();
       
   932 
       
   933 		// Delete existing tree
       
   934 		if (iRootAtom!=NULL)
       
   935 			{
       
   936 
       
   937 			// remove atoms from atom tree
       
   938 			iAtomArray.ResetAndDestroy();
       
   939 			
       
   940 			delete iRootAtom;
       
   941 			iRootAtom=NULL;
       
   942 			}
       
   943 
       
   944 		// Reset (empty) atom stack
       
   945 		iAtomStack.Reset();
       
   946 
       
   947 		// Reset state
       
   948 		iParserState=EIOStateAtomWait;
       
   949 
       
   950 		// First created atom will be child of root
       
   951 		iRootAtom=new CImapAtom();
       
   952 		iAtom=iRootAtom;
       
   953 		iNextIsChild=ETrue;
       
   954 		}
       
   955 
       
   956 	// Anything in receive buffer? Deal with it if we can
       
   957 	TRAPD(err,ProcessBufferL());
       
   958 	if (err!=KErrNone)
       
   959 		{
       
   960 		// An error has occurred: return this
       
   961 		DBG((LogText(_L8("CImapIO::ProcessBuffer: Complete in GetReply err %d"),err)));
       
   962 		Complete(KErrGeneral);
       
   963 		}
       
   964 
       
   965 	return(KErrNone);
       
   966 	}
       
   967 
       
   968 // Send with structure
       
   969 TInt CImapIO::Send(TRequestStatus &aStatus, TRefByValue<const TDesC8> aFmt,...)
       
   970 	{
       
   971 	VA_LIST list;
       
   972 	VA_START(list,aFmt);
       
   973 	iTransmit.Zero();
       
   974 	iTransmit.AppendFormatList(aFmt,list);
       
   975 
       
   976 	return(Send(aStatus, iTransmit));
       
   977 	}
       
   978 
       
   979 // Send with structure
       
   980 void CImapIO::SendL(TRequestStatus &aStatus, TRefByValue<const TDesC8> aFmt,...)
       
   981 	{
       
   982 	VA_LIST list;
       
   983 	VA_START(list,aFmt);
       
   984 	iTransmit.Zero();
       
   985 	iTransmit.AppendFormatList(aFmt,list);
       
   986 
       
   987 	User::LeaveIfError(Send(aStatus, iTransmit));
       
   988 	}
       
   989 
       
   990 // Send with structure, specifying the transport-idle timeout to be used.
       
   991 void CImapIO::SendWithTimeoutL(TRequestStatus &aStatus, TInt aTimeout, TRefByValue<const TDesC8> aFmt,...)
       
   992 	{
       
   993 	VA_LIST list;
       
   994 	VA_START(list,aFmt);
       
   995 	iTransmit.Zero();
       
   996 	iTransmit.AppendFormatList(aFmt,list);
       
   997 
       
   998 	User::LeaveIfError(SendWithTimeout(aStatus, aTimeout, iTransmit));
       
   999 	}
       
  1000 
       
  1001 // Send a string
       
  1002 TInt CImapIO::Send(TRequestStatus& aStatus, const TDesC8& aLine)
       
  1003 	{
       
  1004 	// Disconnected?
       
  1005 	if (iState==EIOStateDisconnected)
       
  1006 		return(KErrDisconnected);
       
  1007 
       
  1008 	if (iState!=EIOStateConnected)
       
  1009 		{
       
  1010 		return(KErrNotReady);
       
  1011 		}
       
  1012 		
       
  1013 	// We're queuing a send
       
  1014 	iState=EIOStateWriteQueued;
       
  1015 
       
  1016 	Queue(aStatus);
       
  1017 	iTXbytes+=aLine.Length();
       
  1018 	iSession->Send(iStatus,aLine);
       
  1019 	SetActive();
       
  1020 
       
  1021 	return(KErrNone);
       
  1022 	}
       
  1023 
       
  1024 // Send a string, specifying the transport-idle timeout to be used
       
  1025 TInt CImapIO::SendWithTimeout(TRequestStatus& aStatus, TInt aTimeout, const TDesC8& aLine)
       
  1026 	{
       
  1027 	// Disconnected?
       
  1028 	if (iState==EIOStateDisconnected)
       
  1029 		return(KErrDisconnected);
       
  1030 
       
  1031 	if (iState!=EIOStateConnected)
       
  1032 		{
       
  1033 		return(KErrNotReady);
       
  1034 		}
       
  1035 		
       
  1036 	// We're queuing a send
       
  1037 	iState=EIOStateWriteQueued;
       
  1038 
       
  1039 	Queue(aStatus);
       
  1040 	iTXbytes+=aLine.Length();
       
  1041 	iSession->SendWithTimeout(iStatus, aTimeout, aLine);
       
  1042 	SetActive();
       
  1043 
       
  1044 	return(KErrNone);
       
  1045 	}
       
  1046 
       
  1047 // Logging calls: passed through to IMSK
       
  1048 void CImapIO::LogText(const TDesC8& aString)
       
  1049 	{
       
  1050 	// Log the text
       
  1051 	if (iSession)
       
  1052 		iSession->LogText(aString);
       
  1053 	else
       
  1054 		{
       
  1055 #if defined(IMAPLOG)
       
  1056 		// Log it to secondary log file (so that we get logging after
       
  1057 		// connection close)
       
  1058 		if (!iImapLog)
       
  1059 			{
       
  1060 			TBuf<64> buf;
       
  1061 			buf.AppendFormat(_L("c:\\logs\\Email\\imaplog%d.txt"),iID);
       
  1062 			TRAP_IGNORE(iImapLog=CImLog::NewL(buf, EAppend));
       
  1063 			}
       
  1064 		
       
  1065 		if (iImapLog)
       
  1066 			iImapLog->AppendComment(aString);
       
  1067 #endif
       
  1068 		}
       
  1069 	}
       
  1070 
       
  1071 void CImapIO::LogText(TRefByValue<const TDesC8> aFmt,...)
       
  1072 	{
       
  1073 	VA_LIST list;
       
  1074 	VA_START(list,aFmt);
       
  1075 	TBuf8<1024> aBuf;
       
  1076 
       
  1077 	aBuf.AppendFormatList(aFmt,list);
       
  1078 	LogText(aBuf);
       
  1079 	}
       
  1080 
       
  1081 // Pass Logging onto the Session
       
  1082 void CImapIO::PerformLogging(TBool aLogging)
       
  1083 	{
       
  1084 	iSession->PerformLogging(aLogging);
       
  1085 	}
       
  1086 
       
  1087 // Bytes in/out
       
  1088 TInt CImapIO::RXbytes(const TBool aReset)
       
  1089 	{
       
  1090 	TInt b=iRXbytes;
       
  1091 	if (aReset) iRXbytes=0;
       
  1092 	return(b);
       
  1093 	}
       
  1094 
       
  1095 TInt CImapIO::TXbytes(const TBool aReset)
       
  1096 	{
       
  1097 	TInt b=iTXbytes;
       
  1098 	if (aReset) iTXbytes=0;
       
  1099 	return(b);
       
  1100 	}
       
  1101 
       
  1102 // Setting of current primaryTextServerSession, going to be use on the secondary session
       
  1103 void CImapIO::SetPrimaryTextServerSession(CImTextServerSession* aPrimaryTextServerSession)
       
  1104 	{
       
  1105 	iPrimaryTextServerSession=aPrimaryTextServerSession;
       
  1106 	}
       
  1107 
       
  1108 // Retruns the current textserversession
       
  1109 CImTextServerSession* CImapIO::GetTextServerSession()
       
  1110 	{
       
  1111 	return iSession;
       
  1112 	}
       
  1113 
       
  1114 // Return descriptor, without any enclosing brackets < >
       
  1115 TPtrC8 CImapAtom::AtomNoAngleBrackets()
       
  1116 	{
       
  1117 	TPtrC8 atom = iAtom;
       
  1118 	TInt len = atom.Length();
       
  1119 
       
  1120 	if (len>2 && atom[0]==KImcvLeftChevron && atom[len-1]==KImcvRightChevron)
       
  1121 		{
       
  1122 		atom.Set(atom.Mid(1,len-2));
       
  1123 		}
       
  1124 	return(atom);
       
  1125 	}
       
  1126 
       
  1127 
       
  1128