email/pop3andsmtpmtm/imapservermtm/src/IMAPIO.CPP
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 03 May 2010 12:29:07 +0300
changeset 25 84d9eb65b26f
permissions -rw-r--r--
Revision: 201015 Kit: 201018

// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// This deals with connection to the server, sending commands, and parsing the
// returned server data. The data returned is buffered in iBuffer: this is
// simply back-to-back data, with a tree which describes the structure
// containing TPtrC's which identify the data belonging to each atom.
// This class owns both the buffer and the tree, and deals with destroying them
// as necessary. The client can request the base of the tree for it to parse
// when a line has been received.
// 980708 - modified to store flavour of ([ in parent atom
// 980802 - partial read behavior modified (see release.txt)
// 990112 - added cancel debug printing
// 990304 - Tweaks to parsing of escape characters
// 990415 - Now happy with ('s in the middle on non-quoted atoms.
// This only generally happens with non-parsable text
// (eg, after an OK response).
// 
//

#include "imapio.h"
#include "impspan.h"
#include <imsk.h>
#include <imcvtext.h>
#include <msvstore.h>
#include "imapsess.h"

#define IMAPLOG

#ifdef _DEBUG
#define DBG(a) a
#else
#define DBG(a)
#endif

// Initial parser buffer size & granularity
const TInt KIOBufferSize=1280;
const TInt KIOBufferGranularity=256;

// Specifies how long a socket is allowed to be inactive before we close it
// down. This handles the situation where the mail server closes down the
// connection, but we don't receive any indication of that. It has been seen
// when connected using GPRS, and a long telephone call is then made.
const TInt KImapSendInactivityTimeMinutes = 30;
const TInt KImapReceiveInactivityTimeMinutes = 30;

// The CImapAtom class contains a TPtrC which points to the data 'owned' by 
// this atom in the buffer. It also contains next (sibling) and child pointers,
// with which the tree is constructed.
CImapAtom::CImapAtom()
	{
	__DECLARE_NAME(_S("CImapAtom"));
	}

CImapAtom::~CImapAtom()
	{
	}

void CImapAtom::Set(const TDesC8& aAtom)
	{
	// Save this atom in here
	iAtom.Set(aAtom);
	}

void CImapAtom::AddChild(CImapAtom *aNewChild)
	{
	// Set child pointer
	iChild=aNewChild;
	}

void CImapAtom::AddNext(CImapAtom *aNewNext)
	{
	// Set next pointer
	iNext=aNewNext;
	}

CImapAtom *CImapAtom::Child()
	{
	return(iChild);
	}

CImapAtom *CImapAtom::Next()
	{
	return(iNext);
	}

// ToChildL and ToNextL provide clients with a neat way of traversing the tree
// in a direction that *should* exist: if it doesn't, a Leave occurs.
CImapAtom *CImapAtom::ToChildL()
	{
	if (!iChild)
		User::Leave(KErrNotFound);
	return(iChild);
	}

CImapAtom *CImapAtom::ToNextL()
	{
	if (!iNext)
		User::Leave(KErrNotFound);
	return(iNext);
	}

// Makes things look neater: do a CompareF
TBool CImapAtom::Compare(const TDesC8& aVal)
	{
	// Compare and return result
	return(iAtom.CompareF(aVal)==0);
	}

// Compare the right hand side of the atom with the match string.
TBool CImapAtom::CompareTail(const TDesC8& aVal)
	{
	if (aVal.Length() > iAtom.Length())
		return EFalse;
	TPtrC8 ptr = iAtom.Right(aVal.Length());
	return(ptr.CompareF(aVal)==0);
	}

// Makes things look neater: lex a decimal atom to a TUint
TInt CImapAtom::Value(TUint& aVal)
	{
	// Turn it into a value
	TLex8 lex(iAtom);

	return(lex.Val(aVal));
	}

// Makes things look neater: lex a decimal atom to a TInt
TInt CImapAtom::Value(TInt& aVal)
	{
	// Turn it into a value
	TLex8 lex(iAtom);

	return(lex.Val(aVal));
	}

// Makes things look neater: lex a decimal atom to a TInt32
TInt CImapAtom::Value(TInt32& aVal)
	{
	// Turn it into a value
	TLex8 lex(iAtom);

	return(lex.Val(aVal));
	}

// Return descriptor
TPtrC8 CImapAtom::Atom()
	{
	return(iAtom);
	}

// Fixup descriptor pointers
void CImapAtom::FixupL(const HBufC8 *aNewBuffer, const TText8 *aOldBuffer)
	{
   // Fixup descriptor pointers
	CArrayFixFlat<CImapAtom*>* atomStack = new (ELeave) CArrayFixFlat<CImapAtom*>(10);
	CleanupStack::PushL(atomStack);

	atomStack->AppendL(this);
	CImapAtom* currentAtom;
	while (atomStack->Count() != 0)
   		{
		// Pop the top atom off of the stack
		currentAtom = (*atomStack)[atomStack->Count() - 1];
 		atomStack->ResizeL(atomStack->Count() - 1);
 
		// Fix up the current atom
		if (currentAtom->Atom().Length()>0)
			{
			// Find offset from start of old buffer
			TInt start=(currentAtom->Atom().Ptr()-aOldBuffer);

 			// Make new descriptor & assign it
			TPtrC8 bufptr(aNewBuffer->Ptr()+start,currentAtom->Atom().Length());
			currentAtom->iAtom.Set(bufptr); // Note that we are setting the real iAtom not the copy returned by Atom()
			}
 
		// Add the first sibling to the stack,
		// subsequent siblings are added when this sibling is visited
		CImapAtom* siblingAtom = currentAtom->Next();
		if (siblingAtom)
			atomStack->AppendL(siblingAtom);

   
		// Add child to the stack
		CImapAtom* childAtom = currentAtom->Child();
		if (childAtom)
			atomStack->AppendL(childAtom);
   		}
   
	CleanupStack::PopAndDestroy(atomStack);
   	}

// The IO processing class
CImapIO::CImapIO(TInt aId) : CMsgActive(1), iBufferSize(KIOBufferSize), iAtomStack(8), iID(aId),iPrimaryTextServerSession(NULL)
	{
	__DECLARE_NAME(_S("CImapIO"));
	}

CImapIO::~CImapIO()
	{
	// Make sure we're cancelled
	Disconnect();

	delete iSession;

	// Dispose of buffer
	delete iBuffer;

	// Delete any atom tree that may still exist
	delete iRootAtom;

	// Get rid of log
	delete iImapLog;
	// delete the atom array
	iAtomArray.ResetAndDestroy();
	}

// Create and call non-trivial contructor
CImapIO *CImapIO::NewLC(TInt aId)
	{
	CImapIO* self=new (ELeave) CImapIO(aId);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

CImapIO *CImapIO::NewL(TInt aId)
	{
	CImapIO* self=NewLC(aId);
	CleanupStack::Pop();
	return self;
	}

// The non-trivial constructor
void CImapIO::ConstructL()
	{
	// Get initial buffer
	iBuffer=HBufC8::NewL(iBufferSize);
	
	// We're an active object...
	CActiveScheduler::Add(this);
	}

// Called when an asynchronous child exits with status>=0
void CImapIO::DoRunL()
	{
	DBG((LogText(_L8("CImapIO::DoRunL(state=%d, status=%d)"),iState,iStatus.Int())));

	switch(iState)
		{
	case EIOStateConnectQueued:
		// Connect has finished: OK?
		if (iStatus.Int()==KErrNone)
			{
			// Done! We're connected
			iState=EIOStateConnected;
			}
		else
			{
			// Close connection and report fail
			iSession->Disconnect();
			}
		break;

	case EIOStateReadQueued:
		// Process it
		iState=EIOStateConnected;
		iRXbytes+=iReceive.Length();
		ProcessBufferL();
		break;

	case EIOStateWriteQueued:
		// Sent! Return error code directly
		iState=EIOStateConnected;
		break;

	case EIOStateConnected:
	case EIOStateDisconnected:
	case EIOStateDummyQueued:
		// Never happens, but pleases the compiler
		break;
		}
	}

// Called when an asynchronous child exits with status <KErrNone
void CImapIO::DoComplete(TInt& /*aStatus*/)
	{
	// note this message might not get reported if the socket has closed

	// pass all status codes up as given. KErrNone and KErrEOL (1)
	// will be ignored. All others will cause a disconnect either in
	// CImImap4Session's IdleReadError(), DummyComplete() or DoComplete().
	}	

// Called when parent wants to cancel an operation
void CImapIO::DoCancel()
	{

	DBG((LogText(_L8("CImapIO::DoCancel(state=%d)"),iState)));
	
	if (iSession)
		{
		// Cancel actions
		switch(iState)
			{
		case EIOStateConnectQueued:
			// Cancel session and disconnect
			iSession->Cancel();
			iState=EIOStateDisconnected;
			break;

		case EIOStateReadQueued:
		case EIOStateWriteQueued:
			// Cancel it at the session level: this will return the cancelled error
			// upwards, which should cause the above DoComplete to return
			iSession->Cancel();
			iState=EIOStateConnected;
			break;

		case EIOStateConnected:
		case EIOStateDisconnected:
			// Never happens, but pleases the compiler
			break;
		case EIOStateDummyQueued:
			break;
			}
		}
	else
		iState=EIOStateDisconnected;

	// Get msgactive to finish up
	CMsgActive::DoCancel();

	DBG((LogText(_L8("CImapIO::DoCancel done"))));

	}

// Return the root atom
CImapAtom *CImapIO::RootAtom()
	{
	// Return it
	return(iRootAtom);
	}

// Deal with the atom stack: this is used when building the atom tree
void CImapIO::PushL(CImapAtom *aAtom)
	{
	iAtomStack.AppendL(aAtom);
	}

CImapAtom *CImapIO::PopL()
	{
	// Check it isn't empty
	if (iAtomStack.Count()==0)
		User::Leave(KErrUnderflow);

	TInt count = iAtomStack.Count();
	CImapAtom* atom = iAtomStack[count-1];
	iAtomStack.Delete(count-1);
	return(atom);
	}

// Add to the parsed buffer
void CImapIO::BufferAppendL(const TChar aChar)
	{
	// Does buffer need extending?
	if (iBuffer->Length()==iBufferSize)
		{
		HBufC8 *oldbuffer=iBuffer;
		const TText8 *oldbufptr=iBuffer->Ptr();

		// Extend by granularity amount
		iBufferSize+=KIOBufferGranularity;
		iBuffer=iBuffer->ReAllocL(iBufferSize);

		// Buffer moved?
		if (iBuffer!=oldbuffer)
			{
			// Fixup buffer tree pointers
			iRootAtom->FixupL(iBuffer,oldbufptr);
			}
		}

	// Append the data
	iBuffer->Des().Append(aChar);
	}

// Add the last atom appended to the buffer to the tree.
void CImapIO::AddAtomL()
	{
	// Add it
	AddAtomL(iBuffer->Length()-iAtomStart);
	}

// Add the last atom, given a length
void CImapIO::AddAtomL(const TInt aLength)
	{
	// Note buffer position in an atom
	TPtrC8 bufptr(iBuffer->Ptr()+iAtomStart,aLength);

	// Make a new current atom
	CImapAtom *newAtom=new (ELeave) CImapAtom();

	iAtomArray.AppendL(newAtom);
	// Set pointers in it
	newAtom->Set(bufptr);

	// Add it as a child/sibling to the current atom
	if (iNextIsChild)
		iAtom->AddChild(newAtom);
	else
		iAtom->AddNext(newAtom);

	// The next item should be a sibling
	iNextIsChild=EFalse;

	// Make new current
	iAtom=newAtom;
	}

// Process the received buffer, creating atoms as we go
void CImapIO::ProcessBufferL()
	{
	// Process the buffer
	TChar byte;

	DBG((LogText(_L8("CImapIO::ProcessBuffer(iParserState=%d, Length=%d)"),iParserState,iReceive.Length())));

	for(TInt pos=0;pos<iReceive.Length();pos++)
		{
		// Note that we've processed stuff
		iBytesRead++;

		// Byte to process
		byte=iReceive[pos];

		switch(iParserState)
			{
		case EIOStateAtomWait:
			switch(byte)
				{
			case '(':
			case '[':
			case '<':
				{
				// Make a new current atom
				CImapAtom *newAtom=new (ELeave) CImapAtom();

				iAtomArray.AppendL(newAtom);
				// Add it as a sibling to the current atom
				if (iNextIsChild)
					iAtom->AddChild(newAtom);
				else
					iAtom->AddNext(newAtom);

				// The next item should be a child
				iNextIsChild=ETrue;

				// Push current atom onto atom stack, make new current
				iAtom=newAtom;
				PushL(iAtom);

				// Store the open bracket in the buffer, so we can tell what it is
				TPtrC8 bufptr(iBuffer->Ptr()+iBuffer->Length(),1);
				BufferAppendL(byte);
				iAtom->Set(bufptr);

				break;
				}

			case ')':
			case ']':
				// End of this nesting level: pop last atom off stack and
				// make it the new current one
				iAtom=PopL();

				// Any new atoms will be siblings, not children
				iNextIsChild=EFalse;

				break;

			case '{':
				// Start of a literal length
				iLiteralLength=0;
				iParserState=EIOStateLiteralLength;
				break;

			case ' ':
				// Whitespace. Ignore! This state only happens with whitespace
				// after a close )] or a endquote "
				break;

			case '\r':
				// Newline after a close )] or endquote "
				iParserState=EIOStateWaitLF;
				break;

			case '\"':
				// Quotes: we don't keep them, so the atom starts at the next
				// character.
				iAtomStart=iBuffer->Length();
				iParserState=EIOStateInAtom;
				iParserQuoted=ETrue;
				iGotEscape=EFalse;
				break;

			default:
				// Start new atom in buffer
				iAtomStart=iBuffer->Length();
				BufferAppendL(byte);
				iParserState=EIOStateInAtom;
				iParserQuoted=EFalse;
				break;
				}
			break;

		case EIOStateInAtom:
			if (iParserQuoted)
				{
				// Look for another quote
				if (byte=='\"')
					{
					// Just had an escape character?
					if (iGotEscape)
						{
						// Add the character
						BufferAppendL(byte);
						iGotEscape=EFalse;
						}
					else
						{
						// It's the terminator: Add the atom, minus the quotes
						AddAtomL();
						iParserState=EIOStateAtomWait;
						}
					}
				// fix for INC51597 and DEF053082:if a " has been missed out by the server, this will end the atom at a \r
				else if(!iGotEscape && byte == '\r')
					{
					AddAtomL();
					iParserState = EIOStateWaitLF;
					}
				else
					{
					// Escape character?
					if (!iGotEscape && byte=='\\')
						{
						// Got one
						iGotEscape=ETrue;
						}
					else
						{
						// Add to buffer
						BufferAppendL(byte);
						iGotEscape=EFalse;
						}
					}
				}
			else
				{
				if (byte==' ' || byte=='\r')
					{
					AddAtomL();
				
					// Either go back to looking for an atom, or a LF
					iParserState=(byte=='\r')?EIOStateWaitLF:EIOStateAtomWait;
					}
				else if (byte=='(' || byte=='[')
					{
					// Add this atom
					AddAtomL();

					// Make a new current atom
					CImapAtom *newAtom=new (ELeave) CImapAtom();
					iAtomArray.AppendL(newAtom);

					// Add it as a sibling to the current atom
					if (iNextIsChild)
						iAtom->AddChild(newAtom);
					else
						iAtom->AddNext(newAtom);

					// The next item should be a child
					iNextIsChild=ETrue;

					// Push current atom onto atom stack, make new current
					iAtom=newAtom;
					PushL(iAtom);

					// Store the open bracket in the buffer, so we can tell what it is
					TPtrC8 bufptr(iBuffer->Ptr()+iBuffer->Length(),1);
					BufferAppendL(byte);
					iAtom->Set(bufptr);

					iParserState=EIOStateAtomWait;
					}
				else if (byte==')' || byte==']' || byte == '>')
					{
					// Although these bytes usually indicate the end of an atom,
					// they can also legitimately appear in a text field.
					// If this is the end of an atom, then it must be a child or
					// sibling atom in which case there will be an entry on the atom
					// stack. If there is no entry on the atom stack, then this must
					// be a text field so just add the byte to the buffer.
					if (iAtomStack.Count() > 0)
						{
						// Add this atom
						AddAtomL();

						// End of this nesting level: pop last atom off stack and
						// make it the new current one
						iAtom=PopL();

						// Any new atoms will be siblings, not children
						iNextIsChild=EFalse;

						iParserState=EIOStateAtomWait;
						}
					else
						{
						BufferAppendL(byte);
						}
					}
				else
					{
					// Add to buffer
					BufferAppendL(byte);
					}
				}
			break;

		case EIOStateWaitLF:
			// After LF, this is end of line, finish!
			if (byte=='\n')
				{
				// Remove everything from the buffer, we've finished
				iReceive.Delete(0,pos+1);

				// Reset bytes read count & complete
				iBytesRead=0;

				// Complete with KErrFoundEOL
				DBG((LogText(_L8("CImapIO::ProcessBuffer: Complete in EIOStateWaitLF"))));
				Complete(KErrFoundEOL);
				return;
				}
			break;

		case EIOStateLiteralLength:
			// Digit?
			if (byte.IsDigit())
				{
				// Add it to the total
				iLiteralLength=(iLiteralLength*10)+(byte-(TChar)'0');
				if (iLiteralLength <0)
					User::Leave(KErrCorrupt);
				}
			else if (byte=='}')
				{
				// Need to skip CR, LF
				iLiteralSkip=2;
				iParserState=EIOStateLiteralSkip;

				// Add the atom (with the length we know, but no data) to the
				// structure now, so that the partial structure can be parsed.
				iAtomStart=iBuffer->Length();
				AddAtomL(iLiteralLength);
				}
			break;

		case EIOStateLiteralSkip:
			// Skipping...
			if (--iLiteralSkip==0)
				{
				// Is literal 0 bytes long?
				if (iLiteralLength==0)
					{
					// Nothing to follow
					iParserState=EIOStateAtomWait;
					}
				else
					{
					// Non-empty literal: go into fetch state
					iParserState=EIOStateLiteralFetch;
					}
				}
			break;

		case EIOStateLiteralFetch:
			// Fetching
			
			TInt fetchLength(0);
			if(KReceiveBuffer<iLiteralLength)
				{
				fetchLength = KReceiveBuffer;
				}
			else
				{
				fetchLength = iLiteralLength;
				}
			
			if(fetchLength > iReceive.Length()-pos)
				{
				fetchLength = iReceive.Length()-pos;
				}
// 			need to extend buffer ?
			DBG((LogText(_L8(">>> CImapIO::ProcessBufferL(1):buflen=%d:buffsize=%d:litlen=%d:fetchlen=%d"), iBuffer->Length(),iBufferSize, iLiteralLength,fetchLength)));
			if (iBuffer->Length() + iLiteralLength > iBufferSize)
				{
				HBufC8 *oldbuffer=iBuffer;
				const TText8 *oldbufptr=iBuffer->Ptr();
			
				// Extend by extra amount + round up by KIOBufferGranularity
				iBufferSize += iLiteralLength;
				iBufferSize += (KIOBufferGranularity - (iBufferSize % KIOBufferGranularity));
				iBuffer=iBuffer->ReAllocL(iBufferSize);

				// Buffer moved?
				if (iBuffer!=oldbuffer)
					{
					// Fixup buffer tree pointers
					iRootAtom->FixupL(iBuffer,oldbufptr);
					}
				DBG((LogText(_L8(">>> CImapIO::ProcessBufferL(2):buflen=%d:buffsize=%d:litlen=%d:fetchlen=%d"), iBuffer->Length(),iBufferSize, iLiteralLength,fetchLength)));
				}
			iBuffer->Des().Append(iReceive.Mid(pos,fetchLength));
			// adjust loop to account for data copy
			pos+=fetchLength-1;
			iLiteralLength-=fetchLength; 			
			DBG((LogText(_L8(">>> CImapIO::ProcessBufferL(3):buflen=%d:buffsize=%d:litlen=%d:fetchlen=%d"), iBuffer->Length(),iBufferSize, iLiteralLength,fetchLength)));
			if (iLiteralLength==0)
				{
				// Atom is already saved (we add literal atoms before they
				// are stored)
				iParserState=EIOStateAtomWait;
				}
			break;
			}
		}
	
	iReceive.Zero();
	// At start of line, or if we're not doing a partial return, we need to
	// queue another read here
	if (iBytesRead==0 || !iReturnPartialLine)
		{
		// We've processed this buffer: queue a read. We only complete (above)
		// when we've had a whole reply, including terminating CRLF.
		DBG((LogText(_L8("CImapIO::ProcessBufferL(): queuing read, iBytesRead=%d, iReturnPartialLine is %d"), iBytesRead, iReturnPartialLine)));
                QueueRead();
		}
	else
		{
		// Have we got 'enough' of the partial line?
		if (iBytesRead<iBytesToRead)
			{
			// Not enough yet: queue another partial read, for the remainder
			iBytesToRead-=iBytesRead;
			QueueRead();
			}
		else
			{
			// Partial line parsed: return success (client will requeue)
			DBG((LogText(_L8("CImapIO::ProcessBuffer: Complete on partial line"))));
			Complete(KErrNone);
			}
		}
	}

void CImapIO::QueueRead()
	{
	// Fill buffer
	iLen=iBytesToRead;
	iSession->ReceiveBinaryData(iStatus,iReceive,iLen);
	iState=EIOStateReadQueued;
	SetActive();
	}

// Close connection
void CImapIO::Disconnect()
	{
	DBG((LogText(_L8("CImapIO::Disconnect(state=%d)"),iState)));

	// Reset state
	iState=EIOStateDisconnected;

	// Delete session to stop timeouts (this will disconnect too)
	delete iSession;
	iSession=NULL;
	}

void CImapIO::SetTLSResponseL()
	{
	DBG((LogText(_L8("CImapIO::SetTLSResponseL"))));

	iSession->SetSSLTLSResponseL(KIMAP_OK);
	}

/**
	@fn				TInt GetIAPValue(TUint32& aIap)
	Intended Usage	:	Gets the value of the currently connecting/connected IAP
	@param			aIap will be value of the currently connecting/connected IAP or KErrNotFound if an error occurs 
	@return			KErrNone if succesful, KErrNotFound is the session does not exist or a system-wide error code.
	
	*/
TInt CImapIO::GetIAPValue(TUint32& aIap)
	{
	return (iSession) ? (iSession->GetIAPValue(aIap)) : (KErrNotFound);
	}

/**
	@fn				TInt GetRConnectionName(TName &aName)
	Intended Usage	:	On return, the unique name of the RConnection.
	@since			9.1
	@return			KErrNone if succesful, or another of the system-wide error codes. 
	*/
TInt CImapIO::GetRConnectionName(TName &aName)
	{
	return (iSession) ? (iSession->GetRConnectionName(aName)) : (KErrNotFound);
	}

// Returns the last socket activity timeout value for the session connection
TInt CImapIO::GetLastSocketActivityTimeout(TUint32& aTimeout)
	{
	return (iSession) ? (iSession->GetLastSocketActivityTimeout(aTimeout)) : (KErrNotFound);
	}

/**
	@fn				TInt GetConnectionStage()
	Intended Usage	:	Gets the stage of the connection process as defined in nifvar.h and csdprog.h
	@since			7.0s
	@return         The current connection stage, KErrNotFound is the session does not exist or a system-wide error code

	*/
TInt CImapIO::GetConnectionStage()
	{
	return (iSession) ? (iSession->GetConnectionStage()) : (KErrNotFound);
	}


// Parent wants to queue a connection
void CImapIO::ConnectL(TRequestStatus& aStatus, const TDesC& aHost, const TUint aPortNum, const CImIAPPreferences& aPrefs, TBool aSSLWrappedSocket)
	{
	// Have to be disconnected to connect...
	__ASSERT_DEBUG(iState==EIOStateDisconnected,gPanic(EConnectWhenConnected));
	if(!(iState==EIOStateDisconnected))
		{
		User::LeaveIfError(KErrInUse);// Connect when connected
		}
	// Queue request
	Queue(aStatus);

	// Delete current session (there shouldn't be one really)
	delete iSession;
	iSession=NULL;

	// Reconstruct it
	iSession=CImTextServerSession::NewL(KImapSendInactivityTimeMinutes, KImapReceiveInactivityTimeMinutes);
	
	// check local textseverssion is active.
	if(iPrimaryTextServerSession)
		{
		// Providing primarysession's textserversession
		// Going to be set on the secondary session
		iSession->SetPrimaryTextServerSession(iPrimaryTextServerSession);	
		}

	// Ask session to connect (it does the resolving bit)
	if(aSSLWrappedSocket)
		{
		iSession->SSLQueueConnectL(iStatus,aHost,aPortNum,aPrefs);
		}
	else
		{
		iSession->QueueConnectL(iStatus,aHost,aPortNum,aPrefs);
		}

	SetActive();
	iState=EIOStateConnectQueued;
	}

// Parent wants the next line, all nicely parsed. When this completes, the
// parent calls RootAtom() to get the root of the parse tree.
TInt CImapIO::GetReply(TRequestStatus& aStatus)
	{
	// Call with maximum buffer size: no partial returns
	return(GetReply(aStatus,KReceiveBuffer,EFalse));
	}
	
TInt CImapIO::GetReply(TRequestStatus& aStatus, const TInt aFetchSize, const TBool aPartialReturn)
	{
	DBG((LogText(_L8("CImapIO::GetReply (aFetchSize=%d, aPartialReturn=%d)"),aFetchSize,aPartialReturn)));

	// Disconnected?
	if (iState==EIOStateDisconnected)
		return(KErrDisconnected);

	// Have to be connected & not busy
	__ASSERT_ALWAYS(iState==EIOStateConnected,gPanic(EIOWhenNotReady));
	
	// Queue request
	Queue(aStatus);

	// Save max number of bytes to read in next request
	iBytesToRead=aFetchSize;

	// Partial return required? (ie return tree in as-is state when read
	// completes)
	iReturnPartialLine=aPartialReturn;

	// Still busy (last request cancelled?), or still building tree?
	if (iBytesRead)
		{
		// Carry on with this one, return it when ready
		}
	else
		{
		// New line

		// Blank output buffer
		iBuffer->Des().Zero();

		// Delete existing tree
		if (iRootAtom!=NULL)
			{

			// remove atoms from atom tree
			iAtomArray.ResetAndDestroy();
			
			delete iRootAtom;
			iRootAtom=NULL;
			}

		// Reset (empty) atom stack
		iAtomStack.Reset();

		// Reset state
		iParserState=EIOStateAtomWait;

		// First created atom will be child of root
		iRootAtom=new CImapAtom();
		iAtom=iRootAtom;
		iNextIsChild=ETrue;
		}

	// Anything in receive buffer? Deal with it if we can
	TRAPD(err,ProcessBufferL());
	if (err!=KErrNone)
		{
		// An error has occurred: return this
		DBG((LogText(_L8("CImapIO::ProcessBuffer: Complete in GetReply err %d"),err)));
		Complete(KErrGeneral);
		}

	return(KErrNone);
	}

// Send with structure
TInt CImapIO::Send(TRequestStatus &aStatus, TRefByValue<const TDesC8> aFmt,...)
	{
	VA_LIST list;
	VA_START(list,aFmt);
	iTransmit.Zero();
	iTransmit.AppendFormatList(aFmt,list);

	return(Send(aStatus, iTransmit));
	}

// Send with structure
void CImapIO::SendL(TRequestStatus &aStatus, TRefByValue<const TDesC8> aFmt,...)
	{
	VA_LIST list;
	VA_START(list,aFmt);
	iTransmit.Zero();
	iTransmit.AppendFormatList(aFmt,list);

	User::LeaveIfError(Send(aStatus, iTransmit));
	}

// Send with structure, specifying the transport-idle timeout to be used.
void CImapIO::SendWithTimeoutL(TRequestStatus &aStatus, TInt aTimeout, TRefByValue<const TDesC8> aFmt,...)
	{
	VA_LIST list;
	VA_START(list,aFmt);
	iTransmit.Zero();
	iTransmit.AppendFormatList(aFmt,list);

	User::LeaveIfError(SendWithTimeout(aStatus, aTimeout, iTransmit));
	}

// Send a string
TInt CImapIO::Send(TRequestStatus& aStatus, const TDesC8& aLine)
	{
	// Disconnected?
	if (iState==EIOStateDisconnected)
		return(KErrDisconnected);

	if (iState!=EIOStateConnected)
		{
		return(KErrNotReady);
		}
		
	// We're queuing a send
	iState=EIOStateWriteQueued;

	Queue(aStatus);
	iTXbytes+=aLine.Length();
	iSession->Send(iStatus,aLine);
	SetActive();

	return(KErrNone);
	}

// Send a string, specifying the transport-idle timeout to be used
TInt CImapIO::SendWithTimeout(TRequestStatus& aStatus, TInt aTimeout, const TDesC8& aLine)
	{
	// Disconnected?
	if (iState==EIOStateDisconnected)
		return(KErrDisconnected);

	if (iState!=EIOStateConnected)
		{
		return(KErrNotReady);
		}
		
	// We're queuing a send
	iState=EIOStateWriteQueued;

	Queue(aStatus);
	iTXbytes+=aLine.Length();
	iSession->SendWithTimeout(iStatus, aTimeout, aLine);
	SetActive();

	return(KErrNone);
	}

// Logging calls: passed through to IMSK
void CImapIO::LogText(const TDesC8& aString)
	{
	// Log the text
	if (iSession)
		iSession->LogText(aString);
	else
		{
#if defined(IMAPLOG)
		// Log it to secondary log file (so that we get logging after
		// connection close)
		if (!iImapLog)
			{
			TBuf<64> buf;
			buf.AppendFormat(_L("c:\\logs\\Email\\imaplog%d.txt"),iID);
			TRAP_IGNORE(iImapLog=CImLog::NewL(buf, EAppend));
			}
		
		if (iImapLog)
			iImapLog->AppendComment(aString);
#endif
		}
	}

void CImapIO::LogText(TRefByValue<const TDesC8> aFmt,...)
	{
	VA_LIST list;
	VA_START(list,aFmt);
	TBuf8<1024> aBuf;

	aBuf.AppendFormatList(aFmt,list);
	LogText(aBuf);
	}

// Pass Logging onto the Session
void CImapIO::PerformLogging(TBool aLogging)
	{
	iSession->PerformLogging(aLogging);
	}

// Bytes in/out
TInt CImapIO::RXbytes(const TBool aReset)
	{
	TInt b=iRXbytes;
	if (aReset) iRXbytes=0;
	return(b);
	}

TInt CImapIO::TXbytes(const TBool aReset)
	{
	TInt b=iTXbytes;
	if (aReset) iTXbytes=0;
	return(b);
	}

// Setting of current primaryTextServerSession, going to be use on the secondary session
void CImapIO::SetPrimaryTextServerSession(CImTextServerSession* aPrimaryTextServerSession)
	{
	iPrimaryTextServerSession=aPrimaryTextServerSession;
	}

// Retruns the current textserversession
CImTextServerSession* CImapIO::GetTextServerSession()
	{
	return iSession;
	}

// Return descriptor, without any enclosing brackets < >
TPtrC8 CImapAtom::AtomNoAngleBrackets()
	{
	TPtrC8 atom = iAtom;
	TInt len = atom.Length();

	if (len>2 && atom[0]==KImcvLeftChevron && atom[len-1]==KImcvRightChevron)
		{
		atom.Set(atom.Mid(1,len-2));
		}
	return(atom);
	}