email/imap4mtm/imapsession/src/cimapsession.cpp
changeset 0 72b543305e3a
child 29 7e4e4bcc75b6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/imap4mtm/imapsession/src/cimapsession.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,2298 @@
+// Copyright (c) 2006-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:
+//
+
+
+#include "cimapsession.h"
+
+#include "moutputstream.h"
+#include "minputstream.h"
+
+#include "cimapsessionconsts.h"
+
+#include "cimapidle.h"
+#include "cimapservereventwait.h"
+#include "cimapservergreeting.h"
+#include "cimapservergreetinginfo.h"
+#include "cimapcapability.h"
+#include "cimapcapabilityinfo.h"
+#include "cimapnoop.h"
+#include "cimaplogout.h"
+#include "cimaplogin.h"
+#include "cimapstarttls.h"
+#include "cimapselect.h"
+#include "cimapexamine.h"
+#include "cimapcreate.h"
+#include "cimapdelete.h"
+#include "cimaprename.h"
+#include "cimapsubscribe.h"
+#include "cimapunsubscribe.h"
+#include "cimaplist.h"
+#include "cimaplsub.h"
+#include "cimapstatus.h"
+#include "cimapappend.h"
+#include "cimapsearch.h"
+#include "cimapclose.h"
+#include "cimapstore.h"
+#include "cimapcopy.h"
+#include "cimapexpunge.h"
+#include "cimapfetchflags.h"
+#include "cimapfetchbody.h"
+#include "cimapfetchsinglebodystructure.h"
+#include "cimapfetchmultibodystructures.h"
+#include "cimapfetchbodyresponse.h"
+#include "cimaplogger.h"
+#include "imappaniccodes.h"
+
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+	#include "cimapauthhelpers.h"
+#endif
+/**
+An RBuf buffer is used to create the sequence set string.
+This specifies by how the buffer should grow each time it needs to be expanded.
+*/
+const TInt KSequenceSetGranularity = 32;
+/**
+A CBufFlat buffer is used to store incoming string data whenever incoming data needs to be spliced
+together in order to form a contiguous line or literal block.
+This specifies by how the buffer should grow each time it needs to be expanded.
+*/
+const TInt KInputCacheGranularity = 1024;
+/**
+During a flush (after cancelling a command),
+time out if data is not received within 10 seconds of requesting it.
+*/
+const TInt KFlushTimeoutPeriod = 10*1000*1000;
+
+#ifdef __IMAP_LOGGING
+_LIT(KLogFileName, "imlog%d");
+#endif //__IMAP_LOGGING
+
+/**
+The factory constructor. Part of two phased construction.
+*/
+EXPORT_C CImapSession* CImapSession::NewL(CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, MInputStream& aInputStream, MOutputStream& aOutputStream)
+// static method
+	{
+	CImapSession* self = new(ELeave) CImapSession(aImapSettings, aImapMailStore, aInputStream, aOutputStream);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CImapSession::CImapSession(CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, MInputStream& aInputStream, MOutputStream& aOutputStream)
+	: iImapSettings(aImapSettings), iImapMailStore(aImapMailStore) 
+	, iInputStream(&aInputStream), iOutputStream(&aOutputStream)
+	{}
+
+void CImapSession::ConstructL()
+	{
+	__ASSERT_DEBUG(iInputStream != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNullInputStream));
+	__ASSERT_DEBUG(iOutputStream != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNullOutputStream));
+
+#ifdef __IMAP_LOGGING	
+	//Getting the server port number for creating the IMAP log file
+	TInt portNum = 143;
+	TBuf<10> fileName;
+	fileName.AppendFormat(KLogFileName, portNum);
+	__LOG_CREATE_STANDARD(fileName, iLogId);
+#endif //__IMAP_LOGGING	
+
+	iServerGreetingInfo = CImapServerGreetingInfo::NewL();
+	iCapabilityInfo = CImapCapabilityInfo::NewL();
+	iInputCache = CBufFlat::NewL(KInputCacheGranularity);
+
+	TCallBack callBack(CImapSession::AsyncCallBack, this);
+	iAsyncCallBack = new(ELeave)CAsyncCallBack(callBack, CActive::EPriorityStandard);
+	
+	iFlushCommandTimeout = CImapObservableTimer::NewL(*this);
+	
+	iInputStream->Bind(*this, iLogId);
+	iOutputStream->Bind(*this, iLogId);
+	}
+	
+CImapSession::~CImapSession()
+	{
+	Cancel();
+
+	// Close the streams if they are not already closed. Just closing the
+	// output stream will also close the input stream.
+	if (iOutputStream != NULL)
+		{
+		iOutputStream->Close();
+		}
+
+	delete iFlushCommandTimeout;
+	delete iAsyncCallBack;
+	
+	delete iCurrentCommand;
+	
+	delete iSelectedFolderInfo;
+	delete iCapabilityInfo;	
+	delete iServerGreetingInfo;
+	
+	delete iInputCache;
+
+	__LOG_CLOSE(iLogId);
+	}
+
+/**
+Returns the current server state.
+EServerStateNone indicates that the server greeting has not yet been received.
+CImapSession will panic if an attempt is made to call invoke an IMAP command when the IMAP server is 
+not in the correct state for the command.
+This method can be called to check the state before invoking such a command.
+@return The current server state.
+*/
+EXPORT_C CImapSession::TImapServerState CImapSession::ServerState()
+	{
+	return iServerState;
+	}
+
+/**
+Returns inforamtion about the currently selected folder.
+If no folder is currently selected, then NULL will be returned.
+@return information about the currently selected folder or NULL if no folder is currently selected.
+*/
+EXPORT_C CImapFolderInfo* CImapSession::SelectedFolderInfo()
+	{
+	return iSelectedFolderInfo;
+	}
+
+/** 
+Returns the server greeting information object.
+The data in the object will only be valid after a greeting has been received from the server.
+This must be requested using the ReadServerGreetingL() method immediately after connection has been made.
+@return The server greeting information object.
+*/
+EXPORT_C const CImapServerGreetingInfo& CImapSession::ServerGreetingInfo()
+	{
+	return *iServerGreetingInfo;
+	}
+
+/** 
+Returns the server capability information object.
+The data in the object will only be valid after a capability response has been received from the server.
+This can be requested using the CapabilityL() method.
+@return The server capability information object.
+*/
+EXPORT_C const CImapCapabilityInfo& CImapSession::CapabilityInfo()
+	{
+	return *iCapabilityInfo;
+	}
+
+/** 
+@return ETrue if IMAP IDLE mode is supported by the server.
+*/
+EXPORT_C TBool CImapSession::ImapIdleSupported() const
+	{
+	return iCapabilityInfo->QueryFlag(CImapCapabilityInfo::EIdle);
+	}
+
+/** 
+Request to enter IMAP IDLE mode.
+This method will complete when confirmation of entering IDLE mode has been received 
+from the server, via the '+' continuation response.
+The server may send untagged status information BEFORE sending the continuation response.
+This will be recorded and can be accessed via SelectedFolderInfo().
+Upon completion of EnterIdleL(), 
+WaitForIdleEvent() should be called in order to wait for the next status information event, 
+or DoneIdle() should be called to stop receiving further status information.
+@param aStatus Signals the receipt of the continuation response.
+*/
+EXPORT_C void CImapSession::EnterIdleL(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::EnterIdleL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	StartCommandL(aStatus, CImapIdle::NewL(iSelectedFolderInfo, iLogId));
+	
+	SetPendingOperation(EOpIdleEnter);
+	}
+	
+/**
+Request to be notified of an IMAP IDLE event.
+This method will complete when the next line of status information is received.
+The updated information can be accessed via SelectedFolderInfo().
+However, the information might not be complete as further lines of status information may 
+have been sent but not yet received.
+Upon completion of WaitForIdleEvent(), 
+WaitForIdleEvent() should be called again in order to receive the next event
+or DoneIdle() should be called to receive complete status information 
+and to stop receiving further information.
+This method will panic if the session is not currently in IDLE mode.
+Use EnterIdleL() to enter IDLE mode.
+@param aStatus Signals the receipt of a line of status information.
+*/
+EXPORT_C void CImapSession::WaitForIdleEvent(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::WaitForIdleEvent()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleWaitWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpIdleEnter || iPendingOperation == EOpIdleWait, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleWaitWhenOperationPendingNotIdle));
+	__ASSERT_DEBUG(iSessionState == ESessionNormal, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleWaitWhenSessionNonNormal));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	SetPendingOperation(EOpIdleWait);
+		
+	Queue(aStatus);
+	
+	// Restart the ProcessInputBufferL() loop asynchronously
+	ProcessInputBufferAsync();
+	}
+	
+/** 
+Request to exit IMAP IDLE mode.
+This will cause the session to send a DONE message to the server.
+The method will complete when a tagged response is received.
+Upon completion, up-to-date status information can be accessed via SelectedFolderInfo().
+This method will panic if the session is not currently in IDLE mode.
+Use EnterIdleL() to enter IDLE mode.
+@param aStatus Signals completion of the request.
+*/
+EXPORT_C void CImapSession::DoneIdle(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::DoneIdle()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleDoneWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpIdleEnter || iPendingOperation == EOpIdleWait, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleDoneWhenWhenInvalidOperationPending));
+	__ASSERT_DEBUG(iSessionState == ESessionNormal, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleDoneWhenSessionNonNormal));
+	__ASSERT_DEBUG(iCurrentCommand != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNullCurrentCommand));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	CImapIdle* idleCommand = static_cast<CImapIdle*>(iCurrentCommand);
+
+	SetPendingOperation(EOpIdleDone); // Treat as a normal command.
+	SetSessionState(ESessionNormal); // If called after cancel, then set the session back to normal state.
+	
+	// iOutputStream must be non-null because CompleteIfStreamsClosed() returned EFalse.
+	idleCommand->SendDone(*iOutputStream);
+	
+	Queue(aStatus);
+	
+	// Restart the ProcessInputBufferL() loop asynchronously
+	ProcessInputBufferAsync();
+	}
+
+/**
+This method does not send a command to the server, 
+but simply waits on the input stream for any unsolicited response message
+that the server might decide to send.  See section 5.3 of RFC3501.
+This method should be used in place of the IDLE methods when no command is
+being processed.
+To cancel this method, call Cancel() followed by FlushCancelledCommand() to 
+ensure that partial responses are not left on the input stream before 
+the issuing the next command
+@param aStatus Signals that an unsolicited response has been received.
+*/
+EXPORT_C void CImapSession::WaitForServerEventL(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::WaitForServerEventL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::EWaitForServerEventWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EWaitForServerEventWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	StartCommandL(aStatus, CImapServerEventWait::NewL(iSelectedFolderInfo, iLogId));
+	}
+
+/** 
+Reads and parses the server greeting from the input stream.
+This method must (and must only) be called immediately after making a connection to the server.
+Once this method has completed succesfully, the server greeting information will be accessible 
+from the ServerGreetingInfo() method of this object.
+@return The server greeting information object.
+*/
+EXPORT_C void CImapSession::ReadServerGreetingL(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::ReadServerGreetingL()");
+	__ASSERT_DEBUG(iServerState == EServerStateNone, TImapServerPanic::ImapPanic(TImapServerPanic::EReadServerGreetingWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EReadServerGreetingWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	SetPendingOperation(EOpServerGreeting); // Next server state depends on ServerGreeting result: could be PreAuth
+	StartCommandL(aStatus, CImapServerGreeting::NewL(iSelectedFolderInfo, iLogId, *iServerGreetingInfo));
+	}
+
+/** 
+Issue the IMAP capability command to gather IMAP capability information from the remote server.
+Once this method has completed successfully, the capability information will be accessible 
+from the CapabilityInfo() method of this object.
+@param aStatus Signals completion of the request
+@see CapabilityInfo()
+*/
+EXPORT_C void CImapSession::CapabilityL(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::CapabilityL()");
+	__ASSERT_DEBUG(iServerState != EServerStateNone, TImapServerPanic::ImapPanic(TImapServerPanic::ECapabilityWhenServerStateUnknown));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ECapabilityWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	StartCommandL(aStatus, CImapCapability::NewL(iSelectedFolderInfo, iLogId, *iCapabilityInfo));
+	}
+/** 
+Issue the IMAP noop command.
+When in selected state, and upon completions, the caller should 
+check SelectedFolderInfo() for any updates.
+@param aStatus 		Signals completion of the request
+*/
+EXPORT_C void CImapSession::NoopL(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::NoopL()");
+	__ASSERT_DEBUG(iServerState != EServerStateNone, TImapServerPanic::ImapPanic(TImapServerPanic::ENoopWhenServerStateUnknown));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ENoopWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	StartCommandL(aStatus, CImapNoop::NewL(iSelectedFolderInfo, iLogId));
+	}
+
+/**
+Issue the IMAP logout command.
+@param aStatus Signals completion of the request
+*/
+EXPORT_C void CImapSession::LogoutL(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::LogoutL()");
+	__ASSERT_DEBUG(iServerState != EServerStateNone, TImapServerPanic::ImapPanic(TImapServerPanic::ELogOutWhenNotSelected));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ELogOutWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	SetPendingOperation(EOpLogout); // Server state becomes None (i.e. disconnected)
+	StartCommandL(aStatus, CImapLogout::NewL(iSelectedFolderInfo, iLogId));
+	}
+	
+/**
+Issue the IMAP login command.
+@param aStatus Signals completion of the request
+@param aUserName A user name supplied by the user
+@param aPassword A password supplied by the user
+*/
+EXPORT_C void CImapSession::LoginL(TRequestStatus& aStatus, const TDesC8& aUserName, const TDesC8& aPassword)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::LoginL()");
+	__ASSERT_DEBUG(iServerState == EServerStateNotAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::ELoginWhenServerStateUnknown));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ELoginWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	SetPendingOperation(EOpLogin); // << Server state becomes Authenticated
+	StartCommandL(aStatus, CImapLogin::NewL(iSelectedFolderInfo, iLogId, aUserName, aPassword, *iCapabilityInfo));
+	}
+	
+/** 
+Issue the IMAP StartTLS command
+@param aStatus Signals completion of the request
+*/
+EXPORT_C void CImapSession::StartTlsL(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::StartTlsL()");
+	__ASSERT_DEBUG(iServerState == EServerStateNotAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::EStartTLSWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EStartTLSWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	StartCommandL(aStatus, CImapStartTls::NewL(iSelectedFolderInfo, iLogId));
+	}
+
+/**
+Issue the IMAP select command.
+Opens the folder identified by aFolderInfo.Name() for read-write operations.
+@param aStatus Signals completion of the request
+@param aFolderInfo The folder object that represents the mailbox.  CImapSession takes ownership of this object.
+The folder object initially contains the name of the folder to select.
+The folder object will be updated upon successful completion of the command.
+The folder object will be updated when any subsequent command receives folder information from the server.
+*/
+EXPORT_C void CImapSession::SelectL(TRequestStatus& aStatus, CImapFolderInfo* aFolderInfo, TBool aSelectBox)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::SelectL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::ESelectWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ESelectWhenInvalidOperationPending));
+	__ASSERT_DEBUG(aFolderInfo != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionImapFolderInfoIsNull));
+	
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		// Attempt to select while the stream is closed.
+		// This failed select effectively puts us into the authenticated server state, 
+		// where iSelectedFolderInfo should be NULL.
+		delete iSelectedFolderInfo;
+		iSelectedFolderInfo = NULL;		
+
+		delete aFolderInfo; // No need to set to NULL as we're returning.
+		return;
+		}
+			// Folder is already SELECTed so dont select again.. Hope this saves users bandwidth 
+	if(aSelectBox)		
+	{
+		if(iSelectedFolderInfo && iSelectedFolderInfo->MsvId() == aFolderInfo->MsvId())
+			{
+			Queue(aStatus);
+			Complete(KErrNone);
+			delete aFolderInfo;
+			return;
+			}	
+	}
+	CleanupStack::PushL(aFolderInfo); // Take ownership but don't assign as a data member yet
+	
+	SetPendingOperation(EOpSelect); // Server state becomes Selected if successful, otherwise becomes Authenticated - and selected folder info becomes NULL
+	StartCommandL(aStatus, CImapSelect::NewL(aFolderInfo, iLogId));
+	
+	// Overwrite any existing folder info, prior to performing the select
+	// If select succeeds, iSelectedFolderInfo will be correct.
+	// If select fails then iSelectedFolderInfo will be deleted and set to NULL later, as the state will 
+	//   revert from Selected to Authenticated.
+	
+	CleanupStack::Pop(aFolderInfo); // Now we can assign aFolderInfo as a data member
+	
+	delete iSelectedFolderInfo;
+	iSelectedFolderInfo = aFolderInfo;
+	}
+
+/**
+Issue the IMAP examine command.
+Opens the folder identified by aFolderInfo.Name() for read-only operations.
+@param aStatus Signals completion of the request
+@param aFolderInfo The folder object that represents the mailbox.  CImapSession takes ownership of this object.
+The folder object initially contains the name of the folder to examine.
+The folder object will be updated upon successful completion of the command.
+The folder object will be updated when any subsequent command receives folder information from the server.
+*/
+EXPORT_C void CImapSession::ExamineL(TRequestStatus& aStatus, CImapFolderInfo* aFolderInfo)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::ExamineL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::EExamineWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EExamineWhenInvalidOperationPending));
+	__ASSERT_DEBUG(aFolderInfo != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionImapFolderInfoIsNull));
+
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		// Attempt to select while the stream is closed.
+		// This failed select effectively puts us into the authenticated server state, 
+		// where iSelectedFolderInfo should be NULL.
+		delete iSelectedFolderInfo;
+		iSelectedFolderInfo = NULL;		
+		
+		delete aFolderInfo; // No need to set to NULL as we're returning.		
+		return;
+		}
+	
+	CleanupStack::PushL(aFolderInfo); // Take ownership but don't assign as a data member yet
+	
+	SetPendingOperation(EOpSelect); // Server state becomes Selected if successful, otherwise becomes Authenticated - and selected folder info becomes NULL
+	StartCommandL(aStatus, CImapExamine::NewL(aFolderInfo, iLogId));
+	
+	// Overwrite any existing folder info, prior to performing the select
+	// If select succeeds, iSelectedFolderInfo will be correct.
+	// If select fails then iSelectedFolderInfo will be deleted and set to NULL later, as the state will 
+	//   revert from Selected to Authenticated.
+	
+	CleanupStack::Pop(aFolderInfo); // Now we can assign aFolderInfo as a data member
+	
+	delete iSelectedFolderInfo;
+	iSelectedFolderInfo = aFolderInfo;
+	}
+
+/**
+Issue the IMAP create command
+Creates a remote mailbox with the name given by aMailboxName
+@param aStatus Signals completion of the request
+@param aMailboxName The name to be given to the new mailbox.
+					This is the FULL PATH to the mailbox, as understood by the remote server.
+*/
+EXPORT_C void CImapSession::CreateL(TRequestStatus& aStatus, const TDesC& aMailboxName)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::CreateL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::ECreateWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ECreateWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	StartCommandL(aStatus, CImapCreate::NewL(iSelectedFolderInfo, iLogId, aMailboxName));
+	}
+
+/** 
+Issue the IMAP delete command
+Deletes the folder identified by aMailboxName.
+@param aStatus Signals completion of the request.
+@param aMailboxName The name of the mailbox that is to be selected.
+					This is the FULL PATH to the mailbox, as understood by the remote server.
+*/
+EXPORT_C void CImapSession::DeleteL(TRequestStatus& aStatus, const TDesC& aMailboxName)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::DeleteL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::EDeleteWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EDeleteWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+		
+	StartCommandL(aStatus, CImapDelete::NewL(iSelectedFolderInfo, iLogId, aMailboxName));
+	}
+
+/**
+Issue the IMAP rename command.
+Renames the folder identified by aExistingMailboxName to aNewMailboxName.
+@param aStatus Signals completion of the request.
+@param aExistingMailboxName The current name of the mailbox that is to be renamed
+@param aNewMailboxName The new name to be given to the mailbox
+*/
+EXPORT_C void CImapSession::RenameL(TRequestStatus& aStatus, const TDesC& aExistingMailboxName, const TDesC& aNewMailboxName)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::RenameL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::ERenameWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ERenameWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+		
+	StartCommandL(aStatus, CImapRename::NewL(iSelectedFolderInfo, iLogId, aExistingMailboxName, aNewMailboxName));
+	}
+
+/** 
+Issue the IMAP subscribe command.
+Subscribes to the folder identified by aMailboxName.
+@param aStatus Signals completion of the request.
+@param aMailboxName The name of the mailbox that is to be subscribed to.
+					This is the FULL PATH to the mailbox, as understood by the remote server.
+*/
+EXPORT_C void CImapSession::SubscribeL(TRequestStatus& aStatus, const TDesC& aMailboxName)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::SubscribeL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::ESubscribeWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ESubscribeWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+		
+	StartCommandL(aStatus, CImapSubscribe::NewL(iSelectedFolderInfo, iLogId, aMailboxName));
+	}
+
+/**
+Issue the IMAP unsubscribe command
+Unsubscribes from the folder identified by aMailboxName.
+@param aStatus Signals completion of the request.
+@param aMailboxName The name of the mailbox that is to be unsubscribed from.
+					This is the FULL PATH to the mailbox, as understood by the remote server.
+*/
+EXPORT_C void CImapSession::UnsubscribeL(TRequestStatus& aStatus, const TDesC& aMailboxName)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::UnsubscribeL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::EUnSubscribeWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EUnSubscribeWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+		
+	StartCommandL(aStatus, CImapUnsubscribe::NewL(iSelectedFolderInfo, iLogId, aMailboxName));
+	}
+
+/**
+Issue the IMAP list command
+Populates aFolderList with information about the requested mailbox.
+@param aStatus Signals completion of the request.
+@param aReferenceName	The reference name can be used to qualify the path of the mailbox.  See section 6.3.8 of RFC 3501
+@param aMailboxName		Specifies which mailbox to list.  Only subfolders of the specified mailbox will be listed.
+						This is the FULL PATH to the mailbox, as understood by the remote server.
+@param aFolderList An array which will be populated with the list of folders upon successful completion of the command.
+*/
+EXPORT_C void CImapSession::ListL(TRequestStatus& aStatus, const TDesC& aReferenceName, const TDesC& aMailboxName, RArrayImapListFolderInfo& aFolderList)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::ListL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::EListWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EListWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+		
+	StartCommandL(aStatus, CImapList::NewL(iSelectedFolderInfo, iLogId, aReferenceName, aMailboxName, aFolderList, iImapSettings));
+	}
+
+/**
+Issue the IMAP lsub command
+Populates aFolderList with information about the requested remotely subscribed mailbox.
+@param aStatus Signals completion of the request.
+@param aReferenceName	The reference name can be used to qualify the path of the mailbox.  See section 6.3.9 of RFC 3501
+@param aMailboxName		Specifies which mailbox to list.  Only subscribed subfolders of the specified mailbox will be listed.
+						This is the FULL PATH to the mailbox, as understood by the remote server.
+@param aFolderList		An array which will be populated with the list of folders upon successful completion of the command.
+*/
+EXPORT_C void CImapSession::LsubL(TRequestStatus& aStatus, const TDesC& aReferenceName, const TDesC& aMailboxName, RArrayImapListFolderInfo& aFolderList)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::LsubL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::ELsubWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ELsubWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+		
+	StartCommandL(aStatus, CImapLsub::NewL(iSelectedFolderInfo, iLogId, aReferenceName, aMailboxName, aFolderList, iImapSettings));
+	}
+
+/**
+Issue the IMAP status command
+Populates aFolderInfo with information about the folder identified by aMailboxName
+This method SHOULD NOT be used to access information on this session's currently selected mailbox.
+@param aStatus Signals completion of the request.
+@param aMailboxName The name of the mailbox whose status information is to be fetched.
+					This is the FULL PATH to the mailbox, as understood by the remote server.
+@param aDataItemNames Specifies what information about the folder to request.
+@param aFolderInfo The folder object that represents the mailbox that whose status information is to be fetched.
+The folder object will be updated upon successful completion of the command.
+Ownership of aFolderInfo will NOT be taken by CImapSession.
+*/
+EXPORT_C void CImapSession::StatusL(TRequestStatus& aStatus, const TDesC& aMailboxName, const TDesC8& aDataItemNames, CImapFolderInfo& aFolderInfo)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::StatusL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::EStatusWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EStatusWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+		
+	StartCommandL(aStatus, CImapStatus::NewL(iSelectedFolderInfo, iLogId, aMailboxName, aDataItemNames, aFolderInfo));
+	}
+
+/**
+Issue the IMAP append command
+@param aStatus Signals completion of the request.
+@param aSource An object from which the message source can be obtained
+@param aDestinationMailboxName The name of the mailbox to which the message is to be appended
+*/
+EXPORT_C void CImapSession::AppendL(TRequestStatus& aStatus, CImSendMessage& aSource, const TDesC& aDestinationMailboxName)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::AppendL()");
+	__ASSERT_DEBUG(iServerState >= EServerStateAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::EAppendWhenServerUnAuthenticated));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EAppendWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+
+	StartCommandL(aStatus, CImapAppend::NewL(iSelectedFolderInfo, iLogId, aSource, aDestinationMailboxName, *this));
+	}
+
+/**
+Issue the IMAP close command
+@param aStatus Signals completion of the request.
+*/
+EXPORT_C void CImapSession::CloseL(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::CloseL()");
+	__ASSERT_DEBUG(iServerState == EServerStateSelected, TImapServerPanic::ImapPanic(TImapServerPanic::ECloseWhenNotSelected));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ECloseWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+		
+	SetPendingOperation(EOpClose); // Server state becomes Authenticated, iSelectedFolderInfo becomes NULL
+	StartCommandL(aStatus, CImapClose::NewL(iSelectedFolderInfo, iLogId));
+	}
+
+/**
+Issue the IMAP search command
+This returns a list of id's of messages that match the specified search critera.
+@param aStatus Signals completion of the request.
+@param aSearchCriteria Specifies the set of message id's to return.
+@param aMatchingMessageIds An array of message id's that will receive the result of the search.
+The array will be populated upon successful completion of the command.
+*/
+EXPORT_C void CImapSession::SearchL(TRequestStatus& aStatus, const TDesC8& aSearchCriteria, RArray<TUint>& aMatchingMessageIds)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::SearchL()");
+	__ASSERT_DEBUG(iServerState == EServerStateSelected, TImapServerPanic::ImapPanic(TImapServerPanic::ESearchWhenNotSelected));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ESearchInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+		
+	StartCommandL(aStatus, CImapSearch::NewL(iSelectedFolderInfo, iLogId, aSearchCriteria, aMatchingMessageIds));
+	}
+
+/**
+Issues the IMAP fetch command with FLAGS as the only message data item.
+The flags for a range of messages may be asked for in a single request.
+@param aStatus Signals completion of the request.
+@param aSequenceSet Specifies the messages to fetch
+@param aOutFlagInfo An array which will be populated with the list of messages and their flags upon successful completion of the command.
+*/
+EXPORT_C void CImapSession::FetchFlagsL(TRequestStatus& aStatus, const TDesC8& aSequenceSet, RArrayMessageFlagInfo& aOutFlagInfo)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::FetchFlagsL()");
+	__ASSERT_DEBUG(iServerState == EServerStateSelected, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchFlagsWhenNotSelected));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchFlagsInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+		
+	StartCommandL(aStatus, CImapFetchFlags::NewL(iSelectedFolderInfo, iLogId, aSequenceSet, aOutFlagInfo));
+	}
+	
+/**
+Issues the IMAP fetch command with a BODY (or BODY.PEEK) as the only message data item.
+Only one message body may be fetched at a time, although the implementaton may transparently pipeline the fetch.
+The caller only provides the <section> information for the BODY.  The rest of the request is built up by the method.
+The BODY response data will be stored in the mailstore by the session.
+The sessison will use the supplied TMsvId to instruct the mailstore which entry to populate.
+@param aStatus Signals completion of the request.
+@param aMessageUid Specifies the message to fetch.  This is the IMAP message UID.
+@param aPeek Whether the \\Seen flag of the message should be updated by the IMAP server.
+@param aFetchBodyInfo container class with extra information about the part being fetched. 
+*/	
+EXPORT_C void CImapSession::FetchBodyL(TRequestStatus& aStatus, TUint aMessageUid, TBool aPeek, CFetchBodyInfo& aFetchBodyInfo, CImapFetchBodyResponse& aFetchBodyResponse)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::FetchBodyL()");
+	__ASSERT_DEBUG(iServerState == EServerStateSelected, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyWhenNotSelected));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	SetPendingOperation(EOpWaitForBodyOperationComplete);
+	StartCommandL(aStatus, CImapFetchBody::NewL(iSelectedFolderInfo, iLogId, aMessageUid, aPeek, aFetchBodyInfo, aFetchBodyResponse, iImapSettings, iImapMailStore, *this));
+	}
+	
+/**
+Issues the IMAP fetch command with the FLAGS, BODYSTRUCTURE and BODY.PEEK[HEADER.FIELDS(<fields>)] data items set.
+Only one message may be fetched with this method.  An override exists for a range of messages.
+@param aStatus Signals completion of the request.
+@param aMessageUid Specifies the message to fetch.  This is the IMAP message UID.
+@param aFieldNames A space separated list of header fields that are to be fetched by this method.
+@param aOutFetchResponse An object that will be populated with all requested structural information upon successful completion of the command.
+*/	
+EXPORT_C void CImapSession::FetchBodyStructureAndHeadersL(TRequestStatus& aStatus, TUint aMessageUid, const TDesC8& aFieldNames, CImapFetchResponse& aOutFetchResponse)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::FetchBodyStructureAndHeadersL()-single");
+	__ASSERT_DEBUG(iServerState == EServerStateSelected, TImapServerPanic::ImapPanic(TImapServerPanic::FetchBodyStructureSingleWhenNotSelected));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::FetchBodyStructureSingleWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	StartCommandL(aStatus, CImapFetchSingleBodyStructure::NewL(iSelectedFolderInfo, iLogId, aMessageUid, aFieldNames, aOutFetchResponse));
+	}
+
+/**
+Issues the IMAP fetch command with the FLAGS, BODYSTRUCTURE and BODY.PEEK[HEADER.FIELDS(<fields>)] data items set.
+A range of messages can be fetched with this method.  An override exists for single message fetches.
+@param aStatus Signals completion of the request.
+@param aMessageUid Specifies the message to fetch.  This is the IMAP message UID.
+@param aFieldNames A space separated list of header fields that are to be fetched by this method.
+@param aObserver The implementor of this interface will be called each time a fetch response has been received and parsed.
+*/	
+EXPORT_C void CImapSession::FetchBodyStructureAndHeadersL(TRequestStatus& aStatus, const TDesC8& aSequenceSet, const TDesC8& aFieldNames, MImapFetchStructureObserver& aObserver)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::FetchBodyStructureAndHeadersL()-multi");
+	__ASSERT_DEBUG(iServerState == EServerStateSelected, TImapServerPanic::ImapPanic(TImapServerPanic::FetchBodyStructureMultiWhenNotSelected));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::FetchBodyStructureMultiWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	StartCommandL(aStatus, CImapFetchMultiBodyStructures::NewL(iSelectedFolderInfo, iLogId, aSequenceSet, aFieldNames, aObserver));
+	}
+
+/**
+Issues the IMAP store command
+aSequenceSet, aMessageDataItem and aValue are the STORE parameters defined in RFC3501 - see section 6.4.6
+If aUseSilent is true, then the .SILENT is appended to end of the aMessageDataItem specifier.
+@param aStatus Signals completion of the request.
+@param aSequenceSet Specifies the range of messages to modify.
+@param aMessageDataItem Specifies the name of the message data item to be stored.
+@param aValue Provides the new value of the message item.
+@param aUseSilent Whether the server should return the updated data value.  
+If ETrue, then .SILENT is appended to end of the aMessageDataItem specifier.
+*/
+EXPORT_C void CImapSession::StoreL(TRequestStatus& aStatus, const TDesC8& aSequenceSet, const TDesC8& aMessageDataItem, const TDesC8& aValue, TBool aUseSilent, RArrayMessageFlagInfo& aOutMessageFlagInfo)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::StoreL()");
+	__ASSERT_DEBUG(iServerState == EServerStateSelected, TImapServerPanic::ImapPanic(TImapServerPanic::EStoreWhenNotSelected));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EStoreWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	StartCommandL(aStatus, CImapStore::NewL(iSelectedFolderInfo, iLogId, aSequenceSet, aMessageDataItem, aValue, aUseSilent, aOutMessageFlagInfo));
+	}
+
+/**
+Issue the IMAP copy command
+This instructs the server to copy the messages identified by aSequence from the currently 
+selected folder to the folder identified by aDestinationMailbox.
+@param aStatus Signals completion of the request.
+@param aSequenceSet Specifies the range of messages to copy.
+@param aDestinationMailboxName The name of the mailbox to which the message(s) is(are) to be copied.
+*/
+EXPORT_C void CImapSession::CopyL(TRequestStatus& aStatus, const TDesC8& aSequenceSet, const TDesC& aDestinationMailboxName)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::CopyL()");
+	__ASSERT_DEBUG(iServerState == EServerStateSelected, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyWhenNotSelected));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	StartCommandL(aStatus, CImapCopy::NewL(iSelectedFolderInfo, iLogId, aSequenceSet, aDestinationMailboxName));
+	}
+
+/**
+Issues the IMAP expunge command.
+@param aStatus Signals completion of the request.
+*/
+EXPORT_C void CImapSession::ExpungeL(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::ExpungeL()");
+	__ASSERT_DEBUG(iServerState == EServerStateSelected, TImapServerPanic::ImapPanic(TImapServerPanic::EExpungeWhenNotSelected));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::EExpungeWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	StartCommandL(aStatus, CImapExpunge::NewL(iSelectedFolderInfo, iLogId));
+	}
+
+/**
+Produces an efficient sequence-set string for the given set of message uids.
+For instance, the set (3 4 5 6 21) would produce the string 3:6,21
+Methods that take the aSequenceSet parameter can also accept wildcard sequence sets such as "24:*"
+which cannot be produced by this method (it would make no sense from finite set of uids).
+@param aMessageUids the set of messages to include in the sequence set
+@return the sequence set that represents the message uids.  The caller takes ownership of the returned object.
+*/
+EXPORT_C HBufC8* CImapSession::CreateSequenceSetLC(RArray<TUint>& aMessageUids)
+// static method
+	{
+	_LIT8(KFormatFirstUid, "%d");
+	_LIT8(KFormatEndRangeAndNextUid, ":%d,%d");
+	_LIT8(KFormatNextUid, ",%d");
+	_LIT8(KFormatFinalEndRangeUid, ":%d");
+	
+	aMessageUids.Sort();
+	
+	TBool inRange = EFalse;
+	TUint prevUid = 0;
+	
+	TImapOverflowDetector overflowHandler;
+	
+	RBuf8 sequenceSet;
+	sequenceSet.CleanupClosePushL();
+	sequenceSet.CreateL(KSequenceSetGranularity);
+	
+	TInt countUids = aMessageUids.Count();
+	for (TInt i=0; i<countUids; i++)
+		{
+		// First (and lowest) uid always gets written
+		if (i==0)
+			{
+			prevUid = aMessageUids[0];
+			AppendAndGrowFormatL(sequenceSet, KFormatFirstUid(), &overflowHandler, prevUid);
+			}
+		// Avoid duplicate uids
+		else if (aMessageUids[i] > prevUid)
+			{
+			if (inRange && aMessageUids[i] > prevUid + 1 )
+				{
+				// Next uid is more than 1 greater than the last.  The range has ended.
+				AppendAndGrowFormatL(sequenceSet, KFormatEndRangeAndNextUid(), &overflowHandler, prevUid, aMessageUids[i]);
+				inRange = EFalse;
+				}
+			else if (!inRange)
+				{
+				if (aMessageUids[i] == prevUid + 1)
+					{
+					// One greater than the last uid means a range has started.  
+					// Wait until the end of the range before outputing anything.
+					inRange = ETrue;
+					}
+				else
+					{
+					// This one is on its own.
+					AppendAndGrowFormatL(sequenceSet, KFormatNextUid(), &overflowHandler, aMessageUids[i]);
+					}			
+				}
+			}
+			
+		prevUid = aMessageUids[i];
+		}
+	
+	// Output the final (range ending) value as it was skipped earlier.
+	if (inRange)
+		{
+		AppendAndGrowFormatL(sequenceSet, KFormatFinalEndRangeUid(), &overflowHandler, prevUid);
+		}
+	
+	// Allocate on the heap.
+	HBufC8* sequenceSetOnHeap = sequenceSet.AllocL();
+	// Rbuf no longer needed...
+	CleanupStack::PopAndDestroy(&sequenceSet);
+	
+	// But heap version needs to be put onto the cleanup stack.
+	CleanupStack::PushL(sequenceSetOnHeap);
+	return sequenceSetOnHeap;
+	}
+
+/**
+@return A pointer to the input stream
+@internalComponent
+*/
+MInputStream* CImapSession::InputStream()
+	{
+	return iInputStream;
+	}
+
+/**
+@return A pointer to the output stream
+@internalComponent
+*/
+MOutputStream* CImapSession::OutputStream()
+	{
+	return iOutputStream;
+	}
+
+void CImapSession::StartCommandL(TRequestStatus& aStatus, CImapCommand* aCommand)
+	{
+	__ASSERT_DEBUG(iCurrentCommand == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNotNullCurrentCommand));
+	__ASSERT_DEBUG(iOutputStream != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNullOutputStream));
+	__ASSERT_DEBUG(iInputStream != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNullInputStream));
+	__ASSERT_DEBUG(!iInputStream->IsReading(), TImapServerPanic::ImapPanic(TImapServerPanic::ESessionBadInputStreamState));
+	__ASSERT_DEBUG(iSessionState == ESessionNormal, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInvalidSessionState));
+	
+	iCurrentCommand = aCommand; // Take ownership immediately before there's a chance of leaving...
+		
+	iCurrentCommand->SendMessageL(++iNextTagId, *iOutputStream);
+	
+	// Start supplying Imap Server data to the command.
+	if (iInputBuffer.Length() > 0)
+		{
+		// Use data that was previously delivered by the input stream
+		// but which has not yet been consumed.
+		ProcessInputBufferAsync();
+		}
+	else
+		{
+		RequestReadData();
+		}
+			
+	// Queue should always be the last method, so as to avoid leaving!
+	Queue(aStatus);
+	}
+
+/**
+Requests data from the input stream
+*/
+void CImapSession::RequestReadData()
+	{
+	// Check that there is no asynchronous call to ProcessInputBuffer outstanding
+	__ASSERT_DEBUG(iAsyncCallBackOpCode != EAsyncCallBackOpProcessInputBuffer, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionAsyncCallBackOpInvalid));
+	__ASSERT_DEBUG(iInputStream!=NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNullInputStream));
+	
+	__LOG_TEXT(iLogId, "CImapSession::RequestReadData()");
+	
+	if (iInputStream != NULL)
+		{
+		if( iPendingOperation == EOpIdleDone)
+			{
+			__LOG_TEXT(iLogId, "CImapSession iInputStream->ReadReq() for DONE");
+			SetPendingOperation(EOpNone);
+			iInputStream->ReadReq(10);
+			}
+		else 
+			{
+			__LOG_TEXT(iLogId, "CImapSession iInputStream->ReadReq()");
+			iInputStream->ReadReq();
+			}
+		
+		if (iSessionState == ESessionFlushing)
+			{
+			__LOG_TEXT(iLogId, "CImapSession Start FlushTimeout");
+			iFlushCommandTimeout->After(KFlushTimeoutPeriod);
+			}
+		}
+	}
+
+/**
+Stops reading server data and completes any Queued operation with KErrCancel.
+Call FlushCancelledCommand() to read and discard any data associated with the command
+Or call DoneIdle() to complete an idle command.
+*/
+EXPORT_C void CImapSession::Cancel()
+	{
+	__LOG_TEXT(iLogId, "CImapSession::Cancel() - START");
+	DoCancel();
+	__LOG_TEXT(iLogId, "CImapSession::Cancel() - done DoCancel");
+	Complete(KErrCancel);
+	__LOG_TEXT(iLogId, "CImapSession::Cancel() - END");
+	}
+
+/**
+Stops reading server data, but does not complete any Queued operation.
+This should either be called by Cancel() itself - which will complete the queued operation with KErrCancel
+or from methods internal to CImapSession.  
+Internal methods that call DoCancel() are reponsible for ensuring that any queued operation will get completed.
+ - either by completing with some error code just before or after calling this method.
+ - or by initiating a new async step that will complete the queued operation later
+*/
+void CImapSession::DoCancel()
+	{
+	__LOG_FORMAT((iLogId, "CImapSession::DoCancel() - START - iSessionState: %d, iPendingOperation: %d, iCurrentCommand: 0x%08x", iSessionState, iPendingOperation, iCurrentCommand ));
+	// Stop processing server data
+	if (iInputStream != NULL)
+		{
+		__LOG_TEXT(iLogId, "CImapSession::DoCancel() - iInputStream->CancelReadReq()");
+		iInputStream->CancelReadReq();
+		__LOG_TEXT(iLogId, "CImapSession::DoCancel() - iInputStream->CancelReadReq() cancelled");
+		}
+		
+	iAsyncCallBack->Cancel();
+	iAsyncCallBackOpCode = EAsyncCallBackOpNone;
+	__LOG_TEXT(iLogId, "CImapSession::DoCancel() - iAsyncCallBackOpCode = EAsyncCallBackOpNone");
+	
+	iFlushCommandTimeout->Cancel();
+	__LOG_TEXT(iLogId, "CImapSession::DoCancel() - iFlushCommandTimeout cancelled");
+	
+	if (iSessionState == ESessionNormal && iCurrentCommand != NULL)
+		{
+		__LOG_TEXT(iLogId, "CImapSession::DoCancel() - cancelling the current command");
+		
+		iCurrentCommand->Cancel();
+		DoPendingOperationForCancel();
+		
+		// Most commands need to flush after cancel, but commands such as Idle may have different behaviours.
+		if (iCurrentCommand->Flushing())
+			{
+			SetSessionState(ESessionFlushing);
+			}
+		}
+	__LOG_FORMAT((iLogId, "CImapSession::DoCancel() - END - iSessionState: %d, iCurrentCommand: 0x%08x", iSessionState, iCurrentCommand ));
+	}
+
+/**
+This method reads and discards incoming data from the IMAP server until 
+no more data is expected.
+It can be called after calling Cancel() on any asynchronous method other 
+than EnterIdleL() or WaitForIdleEvent().
+It usually will complete when all the data for the cancelled command has been received.
+The method will also complete after a 10 second timeout - indicating that the 
+expected data has not been received from the server.
+@param aStatus Signals completion of the request or a timeout.
+*/
+EXPORT_C void CImapSession::FlushCancelledCommand(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(iLogId, "CImapSession::FlushCancelledCommand()");
+	
+	// Complete immediately if the streams are closed.
+	// The caller receives the KErrImapClosed status code.
+	if (CompleteIfStreamsClosed(aStatus))
+		{
+		return;
+		}
+	
+	Queue(aStatus);
+		
+	// Complete immediately if there is no command to flush.
+	// The caller will receive the KErrNone status code - so will know that the streams are open.
+	// This makes it possible to call Cancel() followed by FlushCancelledCommand() when there is no command operating.
+	if (iCurrentCommand == NULL)
+		{
+		__ASSERT_DEBUG(iSessionState == ESessionNormal, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInvalidSessionState));
+		__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInvalidPendingOp));
+		
+		__LOG_TEXT(iLogId, "CImapSession No command to flush");
+		Complete(KErrNone);
+		return;
+		}
+		
+	// If we got this far, then a command is in progress.
+	// The following ASSERTS ensure that it is approriate to be Flushing the command at this time.
+	__ASSERT_DEBUG(iCurrentCommand->Flushing(), TImapServerPanic::ImapPanic(TImapServerPanic::ESessionCommandNotFlushing));
+	__ASSERT_DEBUG(iSessionState == ESessionFlushing, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInvalidSessionState));
+
+	// Complete immediately if the command can be closed early
+	if (iCurrentCommand->CanCompleteFlushNow())
+		{
+		CompleteAndDestroyCommand(KErrNone, EFalse);
+		SetSessionState(ESessionNormal);
+		return;
+		}
+	
+	// Continue supplying Imap Server data to the command.
+	if (iInputBuffer.Length() > 0)
+		{
+		// Use data that was previously delivered by the input stream
+		// but which has not yet been consumed.
+		ProcessInputBufferAsync();
+		}
+	else
+		{
+		RequestReadData();
+		}
+	}
+
+/**
+This is called when the FlushCancelledCommand() operation times out.
+It cancels the operation and completes with the code KErrImapFlushTimeout.
+@param aSourceTimer a referecne to the iFlushCommandTimeout object.
+*/
+#ifdef _DEBUG
+void CImapSession::OnTimerL(const CImapObservableTimer& aSourceTimer)
+#else // _DEBUG
+void CImapSession::OnTimerL(const CImapObservableTimer& /*aSourceTimer*/)
+#endif // _DEBUG
+	{
+	__LOG_TEXT(iLogId, "CImapSession::OnTimerL()");
+	__ASSERT_DEBUG(&aSourceTimer == iFlushCommandTimeout, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionUnknownTimer));
+	
+	__ASSERT_DEBUG(iCurrentCommand->Flushing(), TImapServerPanic::ImapPanic(TImapServerPanic::ESessionCommandNotFlushing));
+	__ASSERT_DEBUG(iSessionState == ESessionFlushing, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInvalidSessionState));
+	
+	DoCancel();	
+	Complete(KErrImapFlushTimeout);
+	}
+
+/**
+Commands (such as CImapAppend) that send their data asynchronously
+should call this method if the send fails (e.g. due to lack of resources)
+*/
+void CImapSession::AsyncSendFailed(TInt aErr)
+	{
+	__LOG_FORMAT((iLogId, "CImapSession::AsyncSendFailed(%d)", aErr));
+	// Report the error.  
+	// Assert that there is a TRequestStatus to report to.
+	// This should always be the case, a command's send/receive is always paired with a call to Queue.
+	__ASSERT_DEBUG(iReport != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNullReportStatus));
+	CompleteAndDestroyCommand(aErr, ETrue);
+	
+	SetSessionState(ESessionUnrecoverable);
+	}
+
+/**
+Call this last when an asynch operation has been requested.
+@param aStatus	The request status of an active object that will be notified 
+				when the async operation has completed.
+*/
+void CImapSession::Queue(TRequestStatus& aStatus)
+    {
+    if(iReport !=NULL)
+    	return;
+	__ASSERT_DEBUG(iReport==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionReportStatusAlreadyQueued));
+    aStatus=KRequestPending;
+    iReport=&aStatus;
+    }
+
+/**
+Call this last when an asynch operation has been completed.
+@param aStatus the return error/status code for the completed operation.
+*/
+void CImapSession::Complete(TInt aStatus)
+	{
+	__LOG_FORMAT((iLogId, "CImapSession::Complete() - aStatus: %d, iReport: 0x%08x", aStatus, iReport));
+	if (iReport)
+	    {
+	    // only complete properly once.
+	    // this allows a derived class to complete and then cancel, reporting the desired state instead of KErrCancel
+	    DoComplete(aStatus);
+	    User::RequestComplete(iReport,aStatus);
+		}
+	}
+
+/**
+Implements CMsgActive::DoComplete()
+Does nothing except logging the call.
+*/
+#ifdef __IMAP_LOGGING
+void CImapSession::DoComplete(TInt& aStatus)
+#else //__IMAP_LOGGING
+void CImapSession::DoComplete(TInt& /*aStatus*/)
+#endif //__IMAP_LOGGING
+	{
+	__LOG_FORMAT((iLogId, "CImapSession::DoComplete() - completing observers status with error: %d", aStatus));
+	}
+
+TInt CImapSession::AsyncCallBack(TAny* aSelf)
+// static method
+	{
+	CImapSession* self = static_cast<CImapSession*>(aSelf);
+	self->AsyncCallBack();
+	
+	return EFalse; // Tell the OS not to call this method again without being requested.
+	}
+
+void CImapSession::AsyncCallBack()
+	{
+	TRAPD(err, DoAsyncCallBackL());
+	if (err != KErrNone)
+		{
+		__LOG_FORMAT((iLogId, "CImapSession Leave in DoAsyncCallBackL(): %d", err));
+		// Report the error.  
+		// Assert that there is a TRequestStatus to report to.
+		// This should always be the case, as async callbacks requests are paired with calls to Queue.
+		__ASSERT_DEBUG(iReport != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNullReportStatus));
+		CompleteAndDestroyCommand(err, ETrue);
+		
+		SetSessionState(ESessionUnrecoverable);
+		}
+	}
+	
+void CImapSession::DoAsyncCallBackL()
+	{
+	__LOG_TEXT(iLogId, "CImapSession::DoAsyncCallBackL()");
+	
+	// Allow methods called below to check that no async operation is queued,
+	// and even to queue one up, by caching the current op code and nulling it.
+	TAsyncCallBackOpCode cachedOpCode = iAsyncCallBackOpCode;
+	iAsyncCallBackOpCode = EAsyncCallBackOpNone;
+	
+	__LOG_TEXT(iLogId, "CImapSession::iAsyncCallBackOpCode = EAsyncCallBackOpNone");
+	
+	switch (cachedOpCode)
+		{
+		case EAsyncCallBackOpProcessInputBuffer:
+			{
+			__LOG_TEXT(iLogId, "CImapSession cachedOpCode = EAsyncCallBackOpNone");
+			ProcessInputBufferL();
+			}	
+			break;
+		case EAsyncCallBackOpNone:
+		default:
+			{
+			// No valid op code was specified.
+			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionAsyncCallBackOpInvalid));
+			break;
+			}			
+		}
+	}
+
+/**
+Calls ProcessInputBufferL() at a later stage.
+*/
+void CImapSession::ProcessInputBufferAsync()
+	{
+	__LOG_TEXT(iLogId, "CImapSession::ProcessInputBufferAsync()");
+	
+	// if we have an input stream, assert that it is not reading.
+	__ASSERT_DEBUG((iInputStream == NULL) ? !iInputStream->IsReading() : ETrue, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionBadInputStreamState));
+	__ASSERT_DEBUG(iAsyncCallBackOpCode == EAsyncCallBackOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionAsyncCallBackOpInvalid));
+		
+	iAsyncCallBack->CallBack();	
+	iAsyncCallBackOpCode = EAsyncCallBackOpProcessInputBuffer;
+	__LOG_TEXT(iLogId, "CImapSession::iAsyncCallBackOpCode = EAsyncCallBackOpProcessInputBuffer");
+	}
+
+/**
+Notifies the observer that data has been received from the 
+connected host.The supplied buffer will remain valid until 
+the input stream is notified that it is no longer needed.
+
+@param	aBuffer	A buffer containing the received data.
+@see MInputStreamObserver
+*/
+void CImapSession::ReceivedDataIndL(const TDesC8& aBuffer)
+// Implements MInputStreamObserver
+	{	
+	__LOG_TEXT(iLogId, "CImapSession::ReceivedDataIndL()");
+	
+	// Should not be requesting an async callback and data from the transport handler at the same time.
+	__ASSERT_DEBUG(iAsyncCallBackOpCode != EAsyncCallBackOpProcessInputBuffer, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionAsyncCallBackOpInvalid));
+
+	// Should not have any data remaining in the input buffer.
+	// If we have then the stream was restarted too soon.
+	__ASSERT_DEBUG(iInputBuffer.Length()==0, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInputBufferNotEmpty));
+
+	iInputBuffer.Set(aBuffer);
+	
+	iFlushCommandTimeout->Cancel();
+	
+	ProcessInputBufferL();
+	}
+	
+/**
+Processes data in iInputBuffer
+*/
+void CImapSession::ProcessInputBufferL()
+	{
+	// Should have a command ready to process the input buffer.
+	__ASSERT_DEBUG(iCurrentCommand != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNullCurrentCommand));
+	__ASSERT_DEBUG(iSessionState == ESessionNormal || iSessionState == ESessionFlushing, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInvalidSessionState));
+
+	TPtrC8 dataToDeliver(KNullDesC8());
+	
+	TBool continueReadingData = ETrue;	
+	TBool commandComplete = EFalse;
+	TBool deliveredSomeData = EFalse;
+	CImapCommand::TParseState commandParseState = CImapCommand::EWaitStartResponse;
+				
+	while(iInputBuffer.Length() > 0 && continueReadingData)
+		{
+		// The following TryFindXxx methods will remove data from iInputBuffer,
+		//   returning it to dataToDeliver
+		//   and/or copying it the iInputCache
+			
+		TBool foundBlock = EFalse;	
+		if (iCommandLiteralRequestSize == 0)
+			{
+			foundBlock = TryFindLineL(dataToDeliver);
+			}
+		else
+			{
+			foundBlock = TryFindLiteralBlockL(dataToDeliver);
+			}
+			
+		if (foundBlock)
+			{
+			// Reset expected command literal size, now that we are delivering a whole block.
+			iCommandLiteralRequestSize = 0;
+			
+			// Deliver the data to the command
+			TRAPD(err, 
+				commandParseState = iCurrentCommand->ParseBlockL(dataToDeliver);
+				);
+				
+			// Record that at least one block of data was delivered
+			deliveredSomeData = ETrue;
+				
+			// Cleanup the cache
+			iInputCache->Reset();
+			dataToDeliver.Set(KNullDesC8()); // defensive: dataToDeliver might be invalid if it was pointing at the cache
+				
+			if (err != KErrNone)
+				{
+				// As well as completing, destroy the command to help free up resources.
+				CompleteAndDestroyCommand(err, ETrue);
+
+				// Don't allow the session to be called again.
+				SetSessionState(ESessionUnrecoverable);
+				
+				return;
+				}
+				
+			// If a literal block is expected next, then find out how big it is expected to be.
+			switch (commandParseState)
+				{
+				case CImapCommand::EWaitStartResponse:
+					{
+					// This state means that a response been completely parsed,
+					// but another response is expected.for this command.
+					// Usually this means the last response was untagged * or +,
+					// but it can also happen during multi-fetch where more than one tagged OK is expected.
+
+					continueReadingData = DoPendingOperationForIntermediateResponse();
+					}
+					break;
+				case CImapCommand::EWaitLiteralParse:
+				case CImapCommand::EWaitLiteralIngore:
+					{
+					iCommandLiteralRequestSize = iCurrentCommand->LiteralBytesExpected();
+					__ASSERT_DEBUG(iCommandLiteralRequestSize > 0, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionLiteralSizeZero));
+					}
+					break;
+				case CImapCommand::ECommandComplete:
+					{
+					commandComplete = ETrue;
+					continueReadingData = EFalse;
+					}
+					break;
+				default:
+					break;
+				}				
+			}
+		else // block not found
+			{
+			// All the data should have been added to the cache
+			__ASSERT_DEBUG(iInputBuffer.Length() == 0, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInputBufferNotEmpty));
+			}
+			
+		} // end of while loop
+		
+	// The current block of data has been parsed.  Do we need any more, or are we finished?
+	if (commandComplete)
+		{
+		CommandComplete();
+		}
+	else if (continueReadingData)
+		{
+		RequestReadData();
+		
+		if (deliveredSomeData)
+			{
+			// Inform the command that no more data is available from the input buffer.
+			// This allows the command to commit any bulk operations while waiting for the next set of data.
+			__ASSERT_DEBUG(iCurrentCommand != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNullCurrentCommand));
+			iCurrentCommand->WaitingForMoreDataL();
+			}
+		}
+	}
+
+void CImapSession::CommandComplete()
+	{
+	switch (iCurrentCommand->ResponseCode())
+		{
+		case CImapCommand::EImapResponseOk:
+			{
+			if (iSessionState == ESessionNormal)
+				{
+				// Complete unless deferred by pending operation.
+				if (DoPendingOperationForOk())
+					{
+					// Command has completed successfully
+					CompleteAndDestroyCommand(KErrNone, EFalse);
+					}
+				}
+			else
+				{
+				// We're flushing the command, so always complete straight away.
+				__ASSERT_DEBUG(iSessionState == ESessionFlushing, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInvalidSessionState));
+				CompleteAndDestroyCommand(KErrNone, EFalse);
+				SetSessionState(ESessionNormal);
+				}				
+						
+			// NOTE: No need to request more data here.  The next StartCommandL will do that for us.
+			}
+			break;
+		case CImapCommand::EImapResponseNo:
+			{
+			DoPendingOperationForFail();			
+			CompleteAndDestroyCommand(KErrImapNo, EFalse);
+			SetSessionState(ESessionNormal);
+			}
+			break;
+		case CImapCommand::EImapResponseBad:
+			{
+			DoPendingOperationForFail();			
+			CompleteAndDestroyCommand(KErrImapBad, EFalse);
+			SetSessionState(ESessionNormal);
+			}
+			break;
+		default:
+			{
+			// No other result codes should be returned.
+			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInvalidCommandResponseCode));
+			}
+			break;
+		}
+	}
+
+/**
+Destroys the current command object and completes any outstanding asynchronous request with the supplied completion code
+The input and output streams are optionally closed too - this should be done when we are completing because an unexpected error has just occured.
+@param aCompletionCode the return error/status code for the completed operation
+@param aCloseStreams whether to close the input and output streams
+*/
+void CImapSession::CompleteAndDestroyCommand(TInt aCompletionCode, TBool aCloseStreams)
+	{
+	__LOG_FORMAT((iLogId, "CImapSession::CompleteAndDestroyCommand() - START - iSessionState: %d, iPendingOperation: %d, iCurrentCommand: 0x%08x, aCompletionCode: %d, aCloseStreams: %d", iSessionState, iPendingOperation, iCurrentCommand, aCompletionCode, aCloseStreams ));
+	// Complete must happen BEFORE closing the stream so that the correct error code is returned to the async caller.
+	// If we allowed iOutputStream->Close() to be called first, then KErrImapClosed would be returned - which is not what we want.
+	Complete(aCompletionCode);
+	
+	//	Pipelining commands can increment the tag-id internally, so we need to find the 
+	// latest value for the next command.And before closing the command object, fetch the current command tag Id.
+	if( iCurrentCommand != NULL )	
+		{
+		iNextTagId = iCurrentCommand->GetTagId();
+		}
+
+	// iOutputStream->Close() must be called BEFORE iCurrentCommand is destroyed.
+	// This is because iCurrentCommand might have initiated an async write operation on the stream 
+	// which may not have completed yet, in which case the stream will still be referencing output buffer
+	// the descriptor owned by the iCurrentCommand.
+	// Calling Close() will cancel the async write, making it safe to destroy the command.
+	// This helps avoid an ESOCK 14 panic (bad descriptor)
+	if (aCloseStreams && iOutputStream != NULL)
+		{
+		iOutputStream->Close();
+		}
+		
+	// Finished with this command object...
+	delete iCurrentCommand;
+	iCurrentCommand = NULL;
+	
+	SetPendingOperation(EOpNone);
+	__LOG_FORMAT((iLogId, "CImapSession::CompleteAndDestroyCommand() - END - iSessionState: %d, iPendingOperation: %d, iCurrentCommand: 0x%08x, aCompletionCode: %d, aCloseStreams: %d", iSessionState, iPendingOperation, iCurrentCommand, aCompletionCode, aCloseStreams ));
+	}
+
+/**
+Perform any extra operations upon reciept of the command's tagged OK response
+@return Whether it is OK to CompleteAndDestroyCommand() straight away.  
+		Some commands may continue to run asynchronusly after receiving the tagged OK response
+		CompleteAndDestroyCommand() after receving further notification that the command has completed.
+*/
+TBool CImapSession::DoPendingOperationForOk()
+	{
+	TBool completeNow = ETrue;
+	
+	switch (iPendingOperation)
+		{
+		case EOpServerGreeting:
+			{
+			if (iServerGreetingInfo->ResponseTag() == CImapServerGreetingInfo::ETagOk)
+				{
+				SetServerState(EServerStateNotAuthenticated);
+				}
+			else if (iServerGreetingInfo->ResponseTag() == CImapServerGreetingInfo::ETagPreAuth)
+				{
+				SetServerState(EServerStateAuthenticated);
+				}					
+			}
+			break;
+			
+		case EOpSelect:
+			{
+			SetServerState(EServerStateSelected);
+			}
+			break;
+		case EOpClose:
+			{
+			SetServerState(EServerStateAuthenticated);
+			
+			delete iSelectedFolderInfo;
+			iSelectedFolderInfo = NULL;
+			}
+			break;
+		case EOpLogin:
+			{
+			SetServerState(EServerStateAuthenticated);
+			
+			__ASSERT_DEBUG(iSelectedFolderInfo == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNullSelectedFolderInfo));
+			}
+			break;
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+		case EOpAuthenticate:
+			{
+			SetServerState(EServerStateAuthenticated);
+			
+			__ASSERT_DEBUG(iSelectedFolderInfo == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionNullSelectedFolderInfo));
+			}
+			break;
+#endif
+		case EOpLogout:
+			{
+			SetServerState(EServerStateNone);
+			
+			delete iSelectedFolderInfo;
+			iSelectedFolderInfo = NULL;
+			}
+			break;
+		case EOpWaitForBodyOperationComplete:
+			{
+			// Wait for FetchBodyOperationComplete() to complete.
+			CImapFetchBody* fetchBodyCommand = static_cast<CImapFetchBody*>(iCurrentCommand);
+			completeNow = fetchBodyCommand->IsStoreOperationComplete();
+			}			
+			break;
+		default:
+			break;
+		}
+		
+	return completeNow;
+	}
+	
+/**
+Called when the IMAP server returns with a tagged NO or tagged BAD response.
+*/
+void CImapSession::DoPendingOperationForFail()
+	{
+	switch (iPendingOperation)
+		{
+		case EOpSelect:
+			{
+			SetServerState(EServerStateAuthenticated);
+			
+			delete iSelectedFolderInfo;
+			iSelectedFolderInfo = NULL;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+/**
+Called when cancelling a command.
+If an operation can change the server state, assume that the server is in the lowest
+state for that operation.
+*/
+void CImapSession::DoPendingOperationForCancel()
+	{
+	switch (iPendingOperation)
+		{
+		case EOpSelect:
+		case EOpClose:
+			{
+			SetServerState(EServerStateAuthenticated);
+			
+			delete iSelectedFolderInfo;
+			iSelectedFolderInfo = NULL;
+			}
+			break;
+		case EOpLogout:
+			{
+			SetServerState(EServerStateNone);
+			
+			delete iSelectedFolderInfo;
+			iSelectedFolderInfo = NULL;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+/**
+This is called after any resopnse other than the final response has been parsed.
+@return whether to continue reading data from the output stream
+*/
+TBool CImapSession::DoPendingOperationForIntermediateResponse()
+	{
+	TBool continueParsing = ETrue;
+	
+	switch (iPendingOperation)
+		{
+		case EOpIdleEnter:
+		case EOpIdleWait:
+			{
+			CImapIdle* idleCommand = static_cast<CImapIdle*>(iCurrentCommand);
+			if (idleCommand->IdleState() == CImapIdle::EIdleWaitResponse)
+				{
+				// EOpIdleEnter: The continuation response has been received.
+				// EOpIdleWait:  The next response has been received.
+				//	
+				// Notify the caller and exit the parse loop.
+				// We expect the caller to call either WaitForIdleEvent() or DoneIdle()
+				
+				Complete(KErrNone);
+				continueParsing = EFalse;
+				}
+			}
+			break;
+		default:
+			break;
+		}
+	
+	return continueParsing;
+	}
+	
+/**
+Takes data from iInputBuffer in order to build a literal block.
+If not enough data is available, then the whole of iInputBuffer is added to the cache.
+iInputBuffer will be updated so that it only points at unconsumed data.
+@param aDataToDeliver Output parameter that receives the literal block.
+@return 			  If enough data is available, then this parameter is set to point to the whole literal block.
+					  Otherwise it points at KNullDesC8.
+@return Whether enough data was available to return a whole literal block in the output parameter.
+*/
+TBool CImapSession::TryFindLiteralBlockL(TPtrC8& aDataToDeliver)
+	{
+	TInt inputCacheLength = iInputCache->Size();
+	TBool foundLiteralBlock = EFalse;
+	aDataToDeliver.Set(KNullDesC8());
+	
+	// Is there enough data to send a complete block?
+	if (iInputBuffer.Length() + inputCacheLength >= iCommandLiteralRequestSize)
+		{
+		foundLiteralBlock = ETrue;
+		
+		// Either send it in directly or use the cache
+		TInt lengthToConsume = 0;
+		if (inputCacheLength > 0)
+			{			
+			// Some of the block is in the cache.  So append the rest of the block to the cache and return the cache.
+			lengthToConsume = iCommandLiteralRequestSize - inputCacheLength;
+			iInputCache->InsertL(iInputCache->Size(), iInputBuffer.Left(lengthToConsume));
+			
+			aDataToDeliver.Set(iInputCache->Ptr(0));
+			}
+		else
+			{
+			// The whole of the block is in iInput buffer.  So just return that.
+			lengthToConsume = iCommandLiteralRequestSize;
+			aDataToDeliver.Set(iInputBuffer.Left(iCommandLiteralRequestSize));
+			}		
+			
+		// Consume the input buffer
+		if (iInputBuffer.Length() > lengthToConsume)
+			{
+			iInputBuffer.Set(iInputBuffer.Mid(lengthToConsume));
+			}
+		else
+			{
+			iInputBuffer.Set(KNullDesC8());
+			}
+		}
+	else
+		{
+		// Not enough data is available yet.  So cache all of it and wait for more
+		iInputCache->InsertL(iInputCache->Size(), iInputBuffer);
+		iInputBuffer.Set(KNullDesC8());
+		}
+	
+	return foundLiteralBlock;
+	}
+	
+/**
+Takes data from iInputBuffer in order to build a line.
+If not enough data is available, then whole of iInputBuffer is added to the cache.
+iInputBuffer will be updated so that it only points at unconsumed data.
+@param aDataToDeliver Output parameter that receives the line of data.
+	   				  If enough data is available, then this parameter is set to point to the whole line (without the CRLF).
+					  Otherwise it points at KNullDesC8.
+@return Whether enough data was available to return a whole line in the output parameter.
+*/
+TBool CImapSession::TryFindLineL(TPtrC8& aDataToDeliver)
+	{
+	TInt inputCacheLength = iInputCache->Size();
+	TBool foundLine = EFalse;
+	aDataToDeliver.Set(KNullDesC8());
+	
+
+	// Need to check for a Special Case first: 
+	//   End of cache has the Cr, start of iInputBuffer has the Lf
+	//
+	// Check for Lf first
+	if (iInputBuffer[0] == '\n')
+		{
+		// Check for Cr
+		if (inputCacheLength > 0 && iInputCache->Ptr(inputCacheLength-1)[0] == '\r')
+			{
+			foundLine = ETrue;
+			
+			// Point at the line in the cache (but not the Cr)
+			aDataToDeliver.Set(iInputCache->Ptr(0).Left(inputCacheLength-1));
+			
+			// Consume the Lf
+			if (iInputBuffer.Length() > 1)
+				{
+				iInputBuffer.Set(iInputBuffer.Mid(1));
+				}
+			else
+				{
+				iInputBuffer.Set(KNullDesC8());
+				}
+			}
+		}
+			
+	// Usually, the Special Case will fail, so aDataToDeliver will still be empty at this point.
+	if (aDataToDeliver.Size() == 0)
+		{
+		// Try to find a whole CRLF
+		
+		// In some cases server sends the FETCH header data in the following manner(Eg: Lukku server)
+		// *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}
+		// Kopio_paperikirjeestä.pdf) NIL NIL "BASE64" 187346 NIL ("ATTACHMENT" ("FILENAME" {25} 
+		// 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}
+		
+		TInt posCrlf =0;
+		posCrlf = iInputBuffer.Find(KImapTxtCrlf());
+		TInt pos1= iInputBuffer.LocateReverse('\n');
+		TInt len=iInputBuffer.Length();
+		
+		if(pos1>0)  // => posCrlf>0 
+			{	
+			if((iInputBuffer[pos1-1] == '\r') )
+				{
+				 if(posCrlf != pos1-1)  // Checking if there are more than 1 CRLF(\r\n) in InputBuffer
+				 	{
+				 	 if((posCrlf> 0) && (iInputBuffer[posCrlf-1] == '}') )
+				 	 	{
+			 	 		_LIT8(KImapTxt1, "]");
+			 	 		TInt pos2= iInputBuffer.Find(KImapTxt1());
+						// Checking for out of boundary condition
+			 	 		if(((pos2+2) <= (len-1)) && (iInputBuffer[pos2+2] == '{'))
+				 	 		{
+				 	 		TInt pos3=pos2+2;
+				 	 		
+				 	 		// Checking for out of boundary condition
+				 	 		while(((pos3+1) <= (len-1)) && (iInputBuffer[pos3] != '}'))
+				 	 			{
+				 	 			pos3++;
+			 	 				}
+			 	 				
+							if((pos3+2) <= (len-1)) // Checking for out of boundary condition
+								{
+								if((iInputBuffer[pos3+1] == '\r') && (iInputBuffer[pos3+2] == '\n'))
+									{
+									if((pos2>0) && (iInputBuffer[pos2-1] == ')'))
+										{
+										posCrlf = pos3+1;
+										}
+									}
+								}
+			 		 		}
+				 	 	}
+				 	}
+				}
+			}
+
+		if (posCrlf == KErrNotFound)
+			{
+			// Append all the data to the cache
+			iInputCache->InsertL(iInputCache->Size(), iInputBuffer);
+			iInputBuffer.Set(KNullDesC8());
+			}
+		else if(posCrlf >= 0)
+			{
+			foundLine = ETrue;
+			
+			// Fetch the line and consume it from iInputBuffer
+			aDataToDeliver.Set(iInputBuffer.Left(posCrlf));
+			if (iInputBuffer.Length() > posCrlf + KImapTxtCrlf.iTypeLength)
+				{
+				iInputBuffer.Set(iInputBuffer.Mid(posCrlf + KImapTxtCrlf.iTypeLength));
+				}
+			else
+				{
+				iInputBuffer.Set(KNullDesC8());
+				}
+			
+			// Prepend any cached data
+			if (inputCacheLength > 0)
+				{
+				iInputCache->InsertL(iInputCache->Size(), aDataToDeliver);
+				aDataToDeliver.Set(iInputCache->Ptr(0));
+				}
+			}
+		else
+			{
+			// Unexpected error from Find()
+			User::Leave(posCrlf);
+			}
+		}
+		
+	return foundLine;	
+	}
+
+/**
+This method is called by the transport handler upon completion of a request to 
+upgrade to a secure connection, using MInputStream::SecureClientReq().
+But as CImapSession never makes such a request, the method should never be called here.
+*/
+void CImapSession::SecureServerCnf()
+// Implements MInputStreamObserver
+	{
+	// CImapSessionManager uses MInputStream::SecureClientReq() and should have its own version of this method called.
+	__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionReceivedSecureServerCnf));
+	}
+	
+void CImapSession::InputStreamCloseInd(TInt /*aError*/)
+// Implements MInputStreamObserver
+	{
+	__LOG_TEXT(iLogId, "CImapSession::InputStreamCloseInd() - START");
+	// Input stream has been closed. Transport handler is about to delete it
+	// and the output stream too.
+	iInputStream = NULL;
+	iOutputStream = NULL;
+
+	// DoCancel() must be called after CompleteAndDestroyCommand(), to prevent attempt 
+	// by DoCancel() to call Cancel on the command.
+	CompleteAndDestroyCommand(KErrImapClosed, EFalse);
+	DoCancel(); 
+	__LOG_TEXT(iLogId, "CImapSession::InputStreamCloseInd() - END");
+	}
+
+void CImapSession::SendDataCnf()
+// Implements MOutputStreamObserver
+	{
+	if (iCurrentCommand != NULL)
+		{
+		TRAPD(err, 
+			iCurrentCommand->SendDataCnfL();
+			);
+			
+		if (err != KErrNone)
+			{
+			CompleteAndDestroyCommand(err, ETrue);
+			DoCancel();
+			
+			SetSessionState(ESessionUnrecoverable);
+			}		
+		}
+	}
+void CImapSession::OutputStreamCloseInd(TInt /*aError*/)
+// Implements MOutputStreamObserver
+	{
+	__LOG_TEXT(iLogId, "CImapSession::OutputStreamCloseInd() - START");
+	// Output stream has been closed. Transport handler is about to delete it
+	// and the input stream too.
+	iInputStream = NULL;
+	iOutputStream = NULL;
+
+	// DoCancel() must be called after CompleteAndDestroyCommand(), to prevent attempt 
+	// by DoCancel() to call Cancel on the command.
+	CompleteAndDestroyCommand(KErrImapClosed, EFalse);
+	DoCancel();
+    __LOG_TEXT(iLogId, "CImapSession::OutputStreamCloseInd() - END");
+	}
+	
+/**
+Checks whether the input and output streams are closed.
+If they are closed, then aStatus is completed immediately with the completion code KErrImapClosed.
+@param aStatus The request status that will may be completed immediately if the streams are closed.
+@return ETrue if aStatus was completed immediately, otherwise EFalse.
+*/
+TBool CImapSession::CompleteIfStreamsClosed(TRequestStatus& aStatus)
+	{
+	TBool completed = EFalse;
+	
+	if (iInputStream == NULL || iOutputStream == NULL)
+		{
+		// If one stream is NULL then so should the other
+		__ASSERT_DEBUG(iInputStream==NULL && iOutputStream==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionStreamsNotBothNull));
+		
+		Queue(aStatus);
+		Complete(KErrImapClosed);
+		
+		completed = ETrue;
+		}
+		
+	return completed;
+	}
+
+/**
+Appends a formatted string to the supplied buffer.  If the buffer is too small to append 
+the string, then the buffer is expanded and another attempt is made to perform the append operation.
+@param aBuffer 			The buffer that will have formatted string appended to it.
+@param aFormat 			Specifies the format of the string to be appended.
+@param aOverflowHandler An object that helps detect when the buffer needs to be expanded.  
+						Despite this object being a pointer, it may not be NULL, and no 
+						transference of ownership occurs.
+*/	
+void CImapSession::AppendAndGrowFormatL(RBuf8& aBuffer, TRefByValue<const TDesC8> aFormat, TImapOverflowDetector* aOverflowHandler, ...)
+// static 
+	{
+	__ASSERT_DEBUG(aOverflowHandler != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInvalidOverFlowHandler));
+	
+	VA_LIST list;
+	VA_START(list, aOverflowHandler); // Note that VA_START() does not accept reference parameters.
+	
+	TInt bufferLength = aBuffer.Length();
+	
+	while (ETrue)
+		{
+		aBuffer.AppendFormatList(aFormat, list, aOverflowHandler);
+		
+		if (aOverflowHandler->OverflowDetected())
+			{
+			// Invalidate any data that was added in the previous attempt to append, by reseting its length.
+			aBuffer.SetLength(bufferLength);
+		
+			// Make some more space for the next attempt.	
+		 	TInt newSize = aBuffer.MaxLength() + KSequenceSetGranularity;
+			User::LeaveIfError(aBuffer.ReAlloc(newSize));
+			}
+		else
+			{
+			break;
+			}
+		}
+	}
+	
+/**
+Called by the CImapFetchBody command when a body part has been stored.
+@param aErrorCode the result of the store operation.
+*/
+void CImapSession::FetchBodyOperationComplete(TInt aErrorCode)
+	{
+	__ASSERT_DEBUG(iPendingOperation == EOpWaitForBodyOperationComplete, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInvalidPendingOp));
+	__ASSERT_DEBUG(iSessionState == ESessionNormal, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionInvalidSessionState));
+
+	// Check for unrecoverable errors
+	if (aErrorCode < 0 || aErrorCode == KErrImapCorrupt)
+		{
+		// Finished with this command object...	
+		CompleteAndDestroyCommand(aErrorCode, ETrue);
+		
+		// This unexpected error cannot be recovered from as we no longer know our state
+		SetSessionState(ESessionUnrecoverable);
+		}
+	else
+		{
+		// Finished with this command object...	
+		CompleteAndDestroyCommand(aErrorCode, EFalse);
+		}	
+	}
+
+/**
+Sets the session state, and logs the transision.
+All assignments to iSessionState should occur through this method.
+*/
+void CImapSession::SetSessionState(TSessionState aSessionState)
+	{
+#ifdef __IMAP_LOGGING
+
+	_LIT8(KTxtSessionNormal, "ESessionNormal");
+	_LIT8(KTxtSessionFlushing, "ESessionFlushing");
+	_LIT8(KTxtSessionUnrecoverable, "ESessionUnrecoverable");
+	_LIT8(KTxtSessionStateUnknown, "Unknown");
+	
+	TPtrC8 ptrOldState(KTxtSessionStateUnknown);
+	TPtrC8 ptrNewState(KTxtSessionStateUnknown);
+	
+	switch(iSessionState)
+		{
+		case ESessionNormal: 		ptrOldState.Set(KTxtSessionNormal); 		break;
+		case ESessionFlushing: 		ptrOldState.Set(KTxtSessionFlushing); 		break;
+		case ESessionUnrecoverable:	ptrOldState.Set(KTxtSessionUnrecoverable);	break;
+		}
+	switch(aSessionState)
+		{
+		case ESessionNormal: 		ptrNewState.Set(KTxtSessionNormal); 		break;
+		case ESessionFlushing: 		ptrNewState.Set(KTxtSessionFlushing); 		break;
+		case ESessionUnrecoverable:	ptrNewState.Set(KTxtSessionUnrecoverable);	break;
+		}
+	
+	_LIT8(KLogFormat, "CImapSession::iSessionState %S ==> %S");
+	__LOG_FORMAT((iLogId, KLogFormat, &ptrOldState, &ptrNewState));
+	
+#endif //__IMAP_LOGGING
+	
+	iSessionState = aSessionState;	
+	}
+
+/**
+Sets the current pending operation, and logs the transision.
+All assignments to iPendingOperation should occur through this method.
+*/
+void CImapSession::SetPendingOperation(TPendingOperation aPendingOperation)
+	{
+#ifdef __IMAP_LOGGING
+
+	_LIT8(KTxtOpNone, "EOpNone");
+	_LIT8(KTxtOpServerGreeting, "EOpServerGreeting");
+	_LIT8(KTxtOpSelect, "EOpSelect");
+	_LIT8(KTxtOpLogin, "EOpLogin");
+	_LIT8(KTxtOpLogout, "EOpLogout");
+	_LIT8(KTxtOpClose, "EOpClose");
+	_LIT8(KTxtOpWaitForBodyOperationComplete, "EOpWaitForBodyOperationComplete");
+	_LIT8(KTxtOpIdleEnter, "EOpIdleEnter");
+	_LIT8(KTxtOpIdleWait, "EOpIdleWait");
+	_LIT8(KTxtOpUnknown, "Unknown");
+	_LIT8(KTxtOpIdleDone, "EOpIdleDone");
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+	_LIT8(KTxtOpAuthenticate, "EOpAuthenticate");
+#endif
+	TPtrC8 ptrOldOperation(KTxtOpUnknown);
+	TPtrC8 ptrNewOperation(KTxtOpUnknown);
+	
+	switch(iPendingOperation)
+		{
+		case EOpNone:				ptrOldOperation.Set(KTxtOpNone);			break;
+		case EOpServerGreeting:		ptrOldOperation.Set(KTxtOpServerGreeting);	break;
+		case EOpSelect:				ptrOldOperation.Set(KTxtOpSelect);			break;
+		case EOpLogin:				ptrOldOperation.Set(KTxtOpLogin);			break;
+		case EOpLogout:				ptrOldOperation.Set(KTxtOpLogout);			break;
+		case EOpClose:				ptrOldOperation.Set(KTxtOpClose);			break;
+		case EOpWaitForBodyOperationComplete:
+									ptrOldOperation.Set(KTxtOpWaitForBodyOperationComplete);	break;
+		case EOpIdleEnter:			ptrOldOperation.Set(KTxtOpIdleEnter);		break;
+		case EOpIdleWait:			ptrOldOperation.Set(KTxtOpIdleWait);		break;
+		case EOpIdleDone:			ptrOldOperation.Set(KTxtOpIdleDone);		break;
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+		case EOpAuthenticate:		ptrOldOperation.Set(KTxtOpAuthenticate);	break;
+#endif
+		}
+		
+	switch(aPendingOperation)
+		{
+		case EOpNone:				ptrNewOperation.Set(KTxtOpNone);			break;
+		case EOpServerGreeting:		ptrNewOperation.Set(KTxtOpServerGreeting);	break;
+		case EOpSelect:				ptrNewOperation.Set(KTxtOpSelect);			break;
+		case EOpLogin:				ptrNewOperation.Set(KTxtOpLogin);			break;
+		case EOpLogout:				ptrNewOperation.Set(KTxtOpLogout);			break;
+		case EOpClose:				ptrNewOperation.Set(KTxtOpClose);			break;
+		case EOpWaitForBodyOperationComplete:
+									ptrNewOperation.Set(KTxtOpWaitForBodyOperationComplete);	break;
+		case EOpIdleEnter:			ptrNewOperation.Set(KTxtOpIdleEnter);		break;
+		case EOpIdleWait:			ptrNewOperation.Set(KTxtOpIdleWait);		break;
+		case EOpIdleDone:			ptrNewOperation.Set(KTxtOpIdleDone);		break;
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+		case EOpAuthenticate:		ptrNewOperation.Set(KTxtOpAuthenticate);	break;
+#endif
+		}
+	
+	_LIT8(KLogFormat, "CImapSession::iPendingOperation %S ==> %S");
+	__LOG_FORMAT((iLogId, KLogFormat, &ptrOldOperation, &ptrNewOperation));
+	
+#endif //__IMAP_LOGGING
+	
+	iPendingOperation = aPendingOperation;	
+	}
+
+/**
+Sets the state we believe the IMAP Server is in, and logs the transision.
+All assignments to iServerState should occur through this method.
+*/
+void CImapSession::SetServerState(TImapServerState aServerState)
+	{
+	
+#ifdef __IMAP_LOGGING
+
+	_LIT8(KTxtServerStateNone, "EServerStateNone");
+	_LIT8(KTxtServerStateNotAuthenticated, "EServerStateNotAuthenticated");
+	_LIT8(KTxtServerStateAuthenticated, "EServerStateAuthenticated");
+	_LIT8(KTxtServerStateSelected, "EServerStateSelected");
+	_LIT8(KTxtServerStateUnknown, "Unknown");
+
+	TPtrC8 ptrOldState(KTxtServerStateUnknown);
+	TPtrC8 ptrNewState(KTxtServerStateUnknown);
+	
+	switch(iServerState)
+		{
+		case EServerStateNone: 				ptrOldState.Set(KTxtServerStateNone); 				break;
+		case EServerStateNotAuthenticated: 	ptrOldState.Set(KTxtServerStateNotAuthenticated); 	break;
+		case EServerStateAuthenticated: 	ptrOldState.Set(KTxtServerStateAuthenticated); 		break;
+		case EServerStateSelected: 			ptrOldState.Set(KTxtServerStateSelected); 			break;
+		}
+		
+	switch(aServerState)
+		{
+		case EServerStateNone: 				ptrNewState.Set(KTxtServerStateNone); 				break;
+		case EServerStateNotAuthenticated: 	ptrNewState.Set(KTxtServerStateNotAuthenticated); 	break;
+		case EServerStateAuthenticated: 	ptrNewState.Set(KTxtServerStateAuthenticated); 		break;
+		case EServerStateSelected: 			ptrNewState.Set(KTxtServerStateSelected); 			break;
+		}
+	
+	_LIT8(KLogFormat, "CImapSession::iServerState %S ==> %S");
+	__LOG_FORMAT((iLogId, KLogFormat, &ptrOldState, &ptrNewState));
+	
+#endif //__IMAP_LOGGING
+	
+	iServerState = aServerState;	
+	}
+
+/**
+Returns the logger file identifier
+@return Logger file identifier
+*/
+EXPORT_C TInt CImapSession::LogId() const
+	{
+	return iLogId;
+	}
+
+TImapOverflowDetector::TImapOverflowDetector()
+	: iOverflowDetected(EFalse)
+	{}
+
+/**
+Returns whether Overflow() has been called.
+This method resets the object's overflow flag, so that subsequent calls to this method
+will return EFalse until the next time Overflow is called.
+@return whether Overflow() has been called.
+*/
+TBool TImapOverflowDetector::OverflowDetected()
+	{
+	if (iOverflowDetected)
+		{
+		iOverflowDetected = EFalse;
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+/**
+Implements TDes8Overflow by recording that an overflow has been detected.
+*/
+void TImapOverflowDetector::Overflow(TDes8& /*aDes*/)
+	{
+	iOverflowDetected = ETrue;
+	}
+
+
+/**
+Issue the IMAP AuthenticateL command.
+@param aStatus Signals completion of the request
+@param  iCurrentAuthProfile  indicates which authenticate(cram-md5,plain,login)method to use
+@param aStatus 		Signals completion of the request
+*/
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+EXPORT_C void CImapSession::AuthenticateL(TRequestStatus& aStatus,CImapAuthMechanismHelper::TImapAuthProfileFlag iCurrentAuthProfile)
+	{
+	
+	__LOG_TEXT(iLogId, "CImapSession::AuthenticateL()");
+	__ASSERT_DEBUG(iServerState == EServerStateNotAuthenticated, TImapServerPanic::ImapPanic(TImapServerPanic::ELoginWhenServerStateUnknown));
+	__ASSERT_DEBUG(iPendingOperation == EOpNone, TImapServerPanic::ImapPanic(TImapServerPanic::ELoginWhenInvalidOperationPending));
+	if (CompleteIfStreamsClosed(aStatus)) 
+		{
+		return;
+		}
+	
+	SetPendingOperation(EOpAuthenticate); // << Server state becomes Authenticated
+	switch(iCurrentAuthProfile)
+		{
+		case CImapAuthMechanismHelper::EPlain:
+			{
+			StartCommandL(aStatus, CImapAuthPlainMechanismHelper::NewL(iImapSettings, iSelectedFolderInfo, iLogId));
+			}
+			break;
+		case CImapAuthMechanismHelper::ELogin:
+			{
+			StartCommandL(aStatus, CImapAuthLoginMechanismHelper::NewL(iImapSettings,iSelectedFolderInfo,iLogId));
+			}
+			break;
+		case CImapAuthMechanismHelper::ECramMD5:
+			{
+			StartCommandL(aStatus, CImapAuthCramMd5MechanismHelper::NewL(iImapSettings,iSelectedFolderInfo,iLogId));
+			}
+			break;
+		default:
+			{
+			User::Leave(KErrNotSupported);
+			break;	
+			}	
+	    }
+	}
+#endif