email/imap4mtm/imapsession/src/cimapsessionmanager.cpp
changeset 0 72b543305e3a
child 26 ebe688cedc25
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/imap4mtm/imapsession/src/cimapsessionmanager.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,1440 @@
+// 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 "cimapsettings.h"
+#include <miut_err.h>		// imap server error codes
+#include <utf.h>
+#include <imutcon.h>
+#include "cimapsessionmanager.h"
+#include "cimapsession.h"
+#include "ctransportmanager.h"
+#include "msocketfactory.h"
+#include "cimapcapabilityinfo.h"
+#include "cimaplistfolderinfo.h"
+#include "cimapservergreetinginfo.h"
+#include "moutputstream.h"
+#include "minputstream.h"
+#include "cimaplogger.h"
+#include "msocketconnector.h"
+#include "cimapsessionconsts.h"
+
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+	#include "cimapauthhelpers.h"
+#endif
+/**
+Static factory constructor. Part of two phased construction.
+
+@param aImapSettings Settings for the Imap service
+@param aImapMailStore Imap mail store
+@return The constructed CImapSessionManager object
+*/
+EXPORT_C CImapSessionManager* CImapSessionManager::NewL(CImapSettings& aImapSettings, CImapMailStore& aImapMailStore)
+	{
+	CImapSessionManager* self = new (ELeave) CImapSessionManager(aImapSettings, aImapMailStore);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/**
+Class constructor
+
+@param aImapSettings Settings for IMAP service
+@param aImapMailStore Imap mail store
+*/
+CImapSessionManager::CImapSessionManager(CImapSettings& aImapSettings, CImapMailStore& aImapMailStore)
+	: CMsgActive(EPriorityStandard)
+	, iProgressState(TImap4GenericProgress::EIdle)
+	, iImapSettings(aImapSettings)
+	, iImapMailStore(aImapMailStore)
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+	, iCurrentAuthProfile(CImapAuthMechanismHelper::EUndefined)
+#endif
+	{
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::CImapSessionManager");
+	}
+
+/**
+Class destructor
+*/
+CImapSessionManager::~CImapSessionManager()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::~CImapSessionManager");
+
+	Cancel();
+
+	delete iTransportManager;
+	delete iImConnect;
+	iSocketServ.Close();
+	delete iHostAddr8;
+	iDisconnectList.Reset();
+	iSeparatorFolderList.ResetAndDestroy();
+	delete iImapSession;
+	}
+
+/**
+Non-trival constructor. Part of two phased construction.
+*/
+void CImapSessionManager::ConstructL()
+	{
+	// We're an active object...
+	CActiveScheduler::Add(this);
+	}
+
+/**
+Requests construction of a connected, logged in CImapSession object
+
+@param aStatus Signals completion of the request.
+@param aSession Place to store the session once it is fully created.
+*/
+EXPORT_C void CImapSessionManager::GetSessionL(TRequestStatus& aStatus, CImapSession*& aSession)
+	{
+	__ASSERT_DEBUG(iState == EStateIdle,
+	               TImapServerPanic::ImapPanic(TImapServerPanic::EUnexpectedStateForGetSession));
+
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::GetSessionL");
+
+	iStoreSession = &aSession;
+
+	if (!iCreatedNetworkConnection)
+		{
+		CreateNetworkConnectionL();
+		}
+	else
+		{
+		CreateSocketL();
+		}
+
+	Queue(aStatus);
+	}
+
+/**
+Requests logout and disconnect of a list of sessions.
+
+@param aStatus Signals completion of the request.
+@param aSessionList List of sessions to disconnect
+*/
+EXPORT_C void CImapSessionManager::Disconnect(TRequestStatus& aStatus, const RPointerArray<CImapSession>& aSessionList)
+	{
+	__ASSERT_DEBUG(iState == EStateIdle,
+	               TImapServerPanic::ImapPanic(TImapServerPanic::EUnexpectedStateForDisConnect));
+
+	__ASSERT_DEBUG(iDisconnectList.Count() == 0,
+	               TImapServerPanic::ImapPanic(TImapServerPanic::EDisconnectWhenDisconnectListNotEmpty1));
+
+	// Take a copy of the list of sessions to disconnect
+	iDisconnectList.Reset();
+	for (TInt session(0); session < aSessionList.Count(); ++session)
+		{
+		__LOG_TEXT(aSessionList[session]->LogId(), "CImapSessionManager::Disconnect (async, list)");
+
+		iDisconnectList.Append(aSessionList[session]);
+		}
+
+	iProgressState = TImap4GenericProgress::EDisconnecting;
+	DisconnectFirstSessionInList();
+
+	Queue(aStatus);
+	}
+
+/**
+Requests immediate disconnect of a list of sessions. The logout command is not
+sent.
+
+@param aSessionList List of sessions to disconnect
+*/
+EXPORT_C void CImapSessionManager::Disconnect(const RPointerArray<CImapSession>& aSessionList)
+	{
+	__ASSERT_DEBUG(iDisconnectList.Count() == 0,
+	               TImapServerPanic::ImapPanic(TImapServerPanic::EDisconnectWhenDisconnectListNotEmpty2));
+
+	// Take a copy of the list of sessions to disconnect
+	iDisconnectList.Reset();
+	for (TInt session(0); session < aSessionList.Count(); ++session)
+		{
+		__LOG_TEXT(aSessionList[session]->LogId(), "CImapSessionManager::Disconnect (sync, list)");
+
+		iDisconnectList.Append(aSessionList[session]);
+		}
+
+	ImmediateDisconnect();
+	}
+
+/**
+Requests immediate disconnect of a session. The logout command is not sent.
+
+@param aSession Session to disconnect
+*/
+EXPORT_C void CImapSessionManager::Disconnect(const CImapSession& aSession)
+	{
+	__ASSERT_DEBUG(iDisconnectList.Count() == 0,
+	               TImapServerPanic::ImapPanic(TImapServerPanic::EDisconnectWhenDisconnectListNotEmpty3));
+
+	__LOG_TEXT(aSession.LogId(), "CImapSessionManager::Disconnect() - START - (sync, session)");
+
+	// Create a disconnect list with just this one session
+	iDisconnectList.Reset();
+	iDisconnectList.Append(&aSession);
+	ImmediateDisconnect();
+
+    __LOG_TEXT(aSession.LogId(), "CImapSessionManager::Disconnect() - END - (sync, session)");
+	}
+
+/**
+Gets LastSocketActivityTimeout value for the connection
+
+@return LastSocketActivityTimeout value
+*/
+EXPORT_C TUint32 CImapSessionManager::LastSocketActivityTimeout()
+	{
+	return iLastSocketActivityTimeout;
+	}
+
+/**
+Gets progress for an asynchronous operation
+
+@param aProgress Holds progress information
+*/
+EXPORT_C void CImapSessionManager::Progress(TImap4GenericProgress& aProgress)
+	{
+	aProgress.iOperation = TImap4GenericProgress::EConnect;
+	switch(iState)
+		{
+	case EStateCreatingNetworkConnection:
+	case EStateConnectingSocket:
+	case EStateGettingLastSocketActivityTimeout:
+	case EStateUpgradingSSLWrappedSocket:
+	case EStateGreetingWait:
+	case EStateCapabilityWait:
+	case EStateStartTLSWait:
+	case EStateUpgradingTLSSocket:
+	case EStateSecureCapabilityWait:
+	case EStateLoginWait:
+	case EStateSeparatorWait:
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+	case EStateAuthenticateWait:
+#endif
+		{
+		// We are connecting a session
+		aProgress.iState = TImap4GenericProgress::EConnecting;
+		aProgress.iImap4SubStateProgress = TImap4GenericProgress::EIdle;
+
+		// iMsgsDone must contain the connection stage information
+		aProgress.iMsgsDone = GetConnectionStage();
+		TUint32 iap = 0;
+		TInt err(KErrNotFound);
+		// Get the IAP value to store in iMsgsToDo
+		if (iImConnect != NULL)
+			{
+			err = iImConnect->GetIAPValue(iap);
+			}
+		if (err == KErrNone)
+			{
+			aProgress.iMsgsToDo = iap;
+			}
+		else
+			{
+			aProgress.iMsgsToDo = err;
+			}
+
+		break;
+		}
+
+	case EStateIdle:
+	default:
+		{
+		aProgress.iState = iProgressState;
+		aProgress.iImap4SubStateProgress = TImap4GenericProgress::EIdle;
+		break;
+		}
+		}				
+	}
+
+/**
+From MSocketConnectObserver - called when a connection to the remote server
+has been successfully made.
+
+@param aInputStream Created socket's input stream API
+@param aOutputStream Created socket's output stream API
+*/
+void CImapSessionManager::ConnectionMadeL(MInputStream& aInputStream, MOutputStream& aOutputStream)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::ConnectionMadeL");
+
+	iInputStream = &aInputStream;
+	iOutputStream = &aOutputStream;
+
+	// Temporarily bind to the streams for close notifications.
+	// Once the session is created, it will take over the binding.
+	iInputStream->Bind(*this, KDefaultLog);
+	iOutputStream->Bind(*this, KDefaultLog);
+	
+	// Store the current bearer type in the imap settings class
+	TUint32 bearerType;
+	iImConnect->GetIAPBearer(bearerType);
+	iImapSettings.SetCurrentBearerType(bearerType);
+
+	CompleteSelf(KErrNone);
+	}
+
+/**
+From MSocketConnectObserver - Handle the Connection Error
+
+Notification that an error has occured in the connecting service. The error 
+code will have a value of KErrCancel if the observer has stopped the connect 
+service via the MSocketConnectObserver::StopConnect() API. If a problem
+does occur the socket connector will suicide after handling the error.
+
+@param aError The error code.
+@return A value of KErrNone if the error has been handled or any other 
+        value if its has not been handled.
+*/
+TInt CImapSessionManager::HandleConnectError(TInt aError)
+	{
+	__LOG_FORMAT((KDefaultLog, "CImapSessionManager::HandleConnectError - %d", aError));
+
+	// We should only get this while trying to connect the socket.
+	// At any other time we can ignore it.
+	// We can also get this after stopping a socket connect due to a
+	// cancel. However we will have already self completed and we don't
+	// want to complete again. If we have completed, then the iSocketConnector
+	// will have been set to NULL, so check this.
+	if ((iState == EStateConnectingSocket) && (iSocketConnector != NULL))
+		{
+		CompleteSelf(aError);
+		}
+
+	return KErrNone;
+	}
+
+/**
+From MOutputStreamObserver - Indicates data has been sent successfully.
+*/
+void CImapSessionManager::SendDataCnf()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::SendDataCnf");
+
+	// No action required
+	}
+
+/**
+From MOutputStreamObserver - Indicates that output stream has closed.
+
+@param aError Error code that caused stream close.
+*/
+void CImapSessionManager::OutputStreamCloseInd(TInt aError)
+	{
+	__LOG_FORMAT((KDefaultLog, "CImapSessionManager::OutputStreamCloseInd - Error %d, State %d", aError, iState));
+
+	// We can only get this when bound to the stream before the stream
+	// binding is taken over by the session.
+	__ASSERT_DEBUG(iState == EStateUpgradingSSLWrappedSocket,
+	               TImapServerPanic::ImapPanic(TImapServerPanic::EUnexpectedOutputStreamCloseInd));
+
+	if (iState == EStateUpgradingSSLWrappedSocket)
+		{
+		iOutputStream = NULL;
+
+		// The only reason the socket should have closed is if a genuine error
+		// has occured. However if for some reason we don't get an error
+		// returned we need to complete with KErrDisconnected because the
+		// socket has closed and we need to tell the caller that we are not
+		// going to be able to finish creating the requested session.
+		if (aError >= KErrNone)
+			{
+			aError = KErrDisconnected;
+			}
+
+		CompleteSelf(aError);
+		}
+	}
+
+/**
+From MOutputStreamSecureObserver - Indicates that a secure socket request
+has completed on the output stream.
+
+@param aError Error code from completed request.
+*/
+void CImapSessionManager::SecureClientCnf(TInt aError)
+	{
+	__LOG_FORMAT((LogId(), "CImapSessionManager::SecureClientCnf - Error %d, State %d", aError, iState));
+
+	// Should only get this when we are upgrading to a secure socket
+	__ASSERT_DEBUG((iState == EStateUpgradingSSLWrappedSocket ||
+	                iState == EStateUpgradingTLSSocket),
+	                TImapServerPanic::ImapPanic(TImapServerPanic::EUnexpectedSecureClientCnf));
+
+	if ((iState == EStateUpgradingSSLWrappedSocket) ||
+	    (iState == EStateUpgradingTLSSocket))
+		{
+		CompleteSelf(aError);
+		}
+	}
+
+/**
+From MInputStreamObserver - Indicates that the stream has received some data.
+*/
+void CImapSessionManager::ReceivedDataIndL(const TDesC8& /*aBuffer*/)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::ReceivedDataIndL");
+	// No action required
+	}
+
+/**
+From MInputStreamObserver - Indicates that a secure socket request
+has completed on the input stream.
+*/
+void CImapSessionManager::SecureServerCnf()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::SecureServerCnf");
+ 
+	// No action required
+	}
+
+/**
+From MInputStreamObserver - Indicates that the input stream has been closed.
+
+@param aError Error code that caused stream close.
+*/
+void CImapSessionManager::InputStreamCloseInd(TInt aError)
+	{
+	__LOG_FORMAT((KDefaultLog, "CImapSessionManager::InputStreamCloseInd - Error %d, State %d", aError, iState));
+
+	// We can only get this when bound to the stream before the stream
+	// binding is taken over by the session.
+	__ASSERT_DEBUG(iState == EStateUpgradingSSLWrappedSocket,
+	               TImapServerPanic::ImapPanic(TImapServerPanic::EUnexpectedInputStreamCloseInd));
+
+	if (iState == EStateUpgradingSSLWrappedSocket)
+		{
+		iInputStream = NULL;
+
+		// The only reason the socket should have closed is if a genuine error
+		// has occured. However if for some reason we don't get an error
+		// returned we need to complete with KErrDisconnected because the
+		// socket has closed and we need to tell the caller that we are not
+		// going to be able to finish creating the requested session.
+		if (aError >= KErrNone)
+			{
+			aError = KErrDisconnected;
+			}
+		
+		// Complete the request if there is no error while upgrading socket to SSL/TLS.
+		if(!iErrorUpgradingSSLSocket)
+		   {
+		   CompleteSelf(aError);	
+		   }
+		
+		}
+	}
+
+/**
+Called when asynchronous IMAP Session events occur.
+Manages the various steps necessary to log in to the IMAP Service, or to
+logout from it.
+*/
+void CImapSessionManager::DoRunL()
+	{
+	iCompletedSelf = EFalse;
+
+	// If we get a positive error code such as KErrImapNO, leave now
+	// which will mean that DoComplete is called with that error code.
+	// This is derived from CMsgActive, so we should not get any negative
+	// error codes. They will go straight to DoComplete.
+	if (iStatus.Int() > KErrNone)
+		{
+		User::Leave(iStatus.Int());
+		}
+
+	switch(iState)
+		{
+		case EStateCreatingNetworkConnection:
+			{
+			ProcessNetworkConnectionCreatedL();
+			break;
+			}
+
+		case EStateConnectingSocket:
+			{
+			ProcessSocketConnectedL();
+			break;
+			}
+
+		case EStateUpgradingSSLWrappedSocket:
+			{
+			ProcessUpgradedSSLWrappedSocketL();
+			break;
+			}
+
+		case EStateGreetingWait:
+			{
+			ProcessGreetingResponseL();
+			break;
+			}
+
+		case EStateCapabilityWait:
+			{
+			ProcessCapabilityResponseL();
+			break;
+			}
+	
+		case EStateStartTLSWait:
+			{
+			ProcessStartTLSResponse();
+			break;
+			}
+
+		case EStateUpgradingTLSSocket:
+			{
+			ProcessUpgradedTLSSocketL();
+			break;
+			}
+
+		case EStateSecureCapabilityWait:
+			{
+			ProcessSecureCapabilityResponseL();
+			break;
+			}
+
+		case EStateLoginWait:
+			{
+			ProcessLoginResponseL();
+			break;
+			}
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+		case EStateAuthenticateWait:
+			{
+			ProcessAuthenticateResponseL();
+			break;
+			}	
+#endif
+		case EStateSeparatorWait:
+			{
+			ProcessSeparatorResponseL();
+			break;
+			}
+
+		case EStateLogoutWait:
+			{
+			ProcessLogoutResponse();
+			break;
+			}
+
+		case EStateDisconnectingSocket:
+			{
+			ProcessSocketDisconnected();
+			break;
+			}
+
+		case EStateGettingLastSocketActivityTimeout:
+		default:
+			{
+			__ASSERT_DEBUG(ETrue, TImapServerPanic::ImapPanic(TImapServerPanic::EUnexpectedStateForDoRunL));
+			break;
+			}
+		}
+	}
+
+/**
+Cancels the current outstanding asynchronous operation.
+
+The operation will only be cancelled here. Any cleanup is deferred to the
+DoComplete method which will be called next as this is a CMsgActive.
+*/
+void CImapSessionManager::DoCancel()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::DoCancel");
+
+	switch(iState)
+		{
+		case EStateCreatingNetworkConnection:
+			{
+			iImConnect->Cancel();
+			break;
+			}
+
+		case EStateConnectingSocket:
+			{
+			// We will call StopConnect() on the socket connector.
+			// This is supposed to call back to HandleConnectError with
+			// KErrCancel once it completes. However it appears that this
+			// does not always happen, so it is safer to self complete here
+			// with KErrCancel.
+			// Just in case the HandleConnectError() routine does get called
+			// we will set the iSocketConnector to NULL to indicate that the
+			// request has been cancelled
+			MSocketConnector* socketConnector(iSocketConnector);
+			iSocketConnector = NULL;
+			socketConnector->StopConnect();
+			CompleteSelf(KErrCancel);
+			break;
+			}
+
+		case EStateGreetingWait:
+		case EStateCapabilityWait:
+		case EStateStartTLSWait:
+		case EStateSecureCapabilityWait:
+		case EStateLoginWait:
+		case EStateSeparatorWait:
+		case EStateLogoutWait:
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+		case EStateAuthenticateWait:
+#endif
+			{
+			iImapSession->Cancel();
+			break;
+			}
+
+		case EStateUpgradingSSLWrappedSocket:
+		case EStateUpgradingTLSSocket:
+		case EStateDisconnectingSocket:
+			{
+			// There is no specific cancel for any of these states.
+			// Just self complete with KErrCancel.
+			CompleteSelf(KErrCancel);
+			break;
+			}
+
+		case EStateGettingLastSocketActivityTimeout:
+		default:
+			{
+			__ASSERT_DEBUG(ETrue, TImapServerPanic::ImapPanic(TImapServerPanic::EUnexpectedStateForDoCancel));
+			CompleteSelf(KErrCancel);
+			break;
+			}
+		}
+
+	// Call base class version to notify parent class.
+	CMsgActive::DoCancel();
+	}
+
+/**
+The current outstanding asynchronous operation has completed.
+Perform any necessary cleanup.
+
+@param aStatus Status of completed operation.
+*/
+void CImapSessionManager::DoComplete(TInt& aStatus)
+	{
+	__LOG_FORMAT((LogId(), "CImapSessionManager::DoComplete - Error %d, State %d", aStatus, iState));
+
+	iCompletedSelf = EFalse;
+	
+	// If we have completed with an error code, we may want to change it to a
+	// more specific one based on the state we are in.
+	if (aStatus != KErrNone)
+		{
+		ConvertError(aStatus);
+		}
+
+	// If we have completed with an error, handle any required cleanup
+	if (aStatus != KErrNone)
+		{
+		switch (iState)
+			{
+			case EStateCreatingNetworkConnection:
+				{
+				delete iImConnect;
+				iImConnect = NULL;
+				break;
+				}
+
+			case EStateConnectingSocket:
+				{
+				// Nothing required.
+				break;
+				}
+
+			case EStateGettingLastSocketActivityTimeout:
+			case EStateUpgradingSSLWrappedSocket:
+				{
+				// Need to close the streams as we have not yet passed them
+				// on to the session.
+				// Closing the output stream will close the input stream too.
+				if (iOutputStream != NULL)
+					{
+					// Since there was a error while upgrading socket to SSL/TLS.
+					iErrorUpgradingSSLSocket = ETrue;
+					iOutputStream->Close();
+					}
+				break;
+				}
+
+			case EStateUpgradingTLSSocket:
+			case EStateGreetingWait:
+			case EStateCapabilityWait:
+			case EStateStartTLSWait:
+			case EStateSecureCapabilityWait:
+			case EStateLoginWait:
+			case EStateSeparatorWait:
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+			case EStateAuthenticateWait:
+#endif
+				{
+				// At this stage, the session has been created so we need
+				// to close the streams and then delete the session.
+				CloseSessionStreams(iImapSession);
+				delete iImapSession;
+				iImapSession = NULL;
+				break;
+				}
+
+			case EStateLogoutWait:
+			case EStateDisconnectingSocket:
+				{
+				// Immediately disconnect all the sessions remaining in the disconnect
+				// list. This is because the protocol controller has no way of knowing
+				// which ones are disconnected and which ones are not, so it is safer
+				// just to disconnect them all.
+				ImmediateDisconnect();
+				break;
+				}
+
+			default:
+				{
+				__ASSERT_DEBUG(ETrue, TImapServerPanic::ImapPanic(TImapServerPanic::EUnexpectedStateForDoCompleteWithError));
+				break;
+				}
+			}
+		iProgressState = TImap4GenericProgress::EDisconnected;
+		}
+	else
+		{
+		iProgressState = TImap4GenericProgress::EIdle;
+		}
+
+	// If we have completed successfully, and we are performing a connect,
+	// store the completed session information in the place requested by the
+	// caller.
+	// Note that iImapSession will only be non NULL for a successful connection
+	// attempt.
+	if (iImapSession != NULL)
+		{
+		*iStoreSession = iImapSession;
+		iImapSession = NULL;
+		}
+
+	delete iHostAddr8;
+	iHostAddr8 = NULL;
+
+	iInputStream = NULL;
+	iOutputStream = NULL;
+	iSocketConnector = NULL;
+
+	iState = EStateIdle;
+	}
+
+/**
+State handler called when network connection has been created.
+*/
+void CImapSessionManager::ProcessNetworkConnectionCreatedL()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::ProcessNetworkConnectionCreatedL");
+
+	iCreatedNetworkConnection = ETrue;
+	CreateSocketL();
+	}
+
+/**
+State handler called when socket has been connected.
+*/
+void CImapSessionManager::ProcessSocketConnectedL()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::ProcessSocketConnectedL");
+
+	// Change state just in case the call to GetLastSocketActivityTimeout
+	// returns an error and we leave. This allows the subsequent DoComplete
+	// to clean up the connection
+	iState = EStateGettingLastSocketActivityTimeout;
+	User::LeaveIfError(iImConnect->GetLastSocketActivityTimeout(iLastSocketActivityTimeout));
+
+	if (iImapSettings.SSLWrapper())
+		{
+		iState = EStateUpgradingSSLWrappedSocket;
+		iErrorUpgradingSSLSocket = EFalse;
+		CreateSecureSocket();
+		}
+	else
+		{
+		StartSessionL();
+		}
+	}
+
+/**
+State handler called when a socket has been upgraded to SSL.
+*/
+void CImapSessionManager::ProcessUpgradedSSLWrappedSocketL()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::ProcessUpgradedSSLWrappedSocketL");
+
+	StartSessionL();
+	}
+
+/**
+State handler called when a greeting response has been received.
+*/
+void CImapSessionManager::ProcessGreetingResponseL()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::ProcessGreetingResponseL");
+
+	const CImapServerGreetingInfo& greetingInfo = iImapSession->ServerGreetingInfo();
+
+	switch (greetingInfo.ResponseTag())
+		{
+		case CImapServerGreetingInfo::ETagOk:
+			{
+			__LOG_TEXT(LogId(), "CImapSessionManager::Greeting response OK");
+
+			// Request capability information from server
+			iImapSession->CapabilityL(iStatus);
+			iState=EStateCapabilityWait;
+			SetActive();
+			break;
+			}
+
+		case CImapServerGreetingInfo::ETagPreAuth:
+			{
+			__LOG_TEXT(LogId(), "CImapSessionManager::Greeting response PreAuth");
+
+			// Server has pre authenticated us. Just request hierarchy
+			// separator and complete.
+			GetHierarchySeparatorL();
+			break;
+			}
+
+		case CImapServerGreetingInfo::ETagBye:
+			{
+			__LOG_TEXT(LogId(), "CImapSessionManager::Greeting response Bye");
+
+			// Server is currently busy. Complete with an error
+			Complete(KErrImapServerBusy);
+			break;
+			}
+ 
+		default:
+			{
+			__ASSERT_DEBUG(ETrue, TImapServerPanic::ImapPanic(TImapServerPanic::EUnexpectedServerGreetingResponse));
+			Complete(KErrCorrupt);
+			break;
+			}
+		}
+	}
+
+/**
+State handler called when a capability response has been received.
+*/
+void CImapSessionManager::ProcessCapabilityResponseL()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::ProcessCapabilityResponseL");
+
+	const CImapCapabilityInfo& capabilityInfo = iImapSession->CapabilityInfo();
+
+	// Check that a valid IMAP revision number has been received
+	if (!capabilityInfo.QueryFlag(CImapCapabilityInfo::EVersion4Rev1))
+		{
+		__LOG_TEXT(LogId(), "CImapSessionManager - Capability not IMAP4Rev1");
+
+		Complete(KErrImapServerVersion);
+		return;
+		}
+
+	// Check if we must use a secure socket connection
+	if (iImapSettings.SecureSockets())
+		{
+		__LOG_TEXT(LogId(), "CImapSessionManager - Use secure socket");
+
+		// We need a secure socket connection, so check that the server
+		// supports TLS
+		if (capabilityInfo.QueryFlag(CImapCapabilityInfo::EStartTls))
+			{
+			iImapSession->StartTlsL(iStatus);
+			iState=EStateStartTLSWait;
+			SetActive();
+			}
+		else
+			{
+			__LOG_TEXT(LogId(), "CImapSessionManager - Server does not support TLS");
+
+			// Server does not support TLS, so complete with an error
+			Complete(KErrImapServerNoSecurity);
+			}
+		}
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+	else if (iImapSettings.IMAP4Auth())
+		{
+		SelectAuthExtensionProfileL(capabilityInfo);
+		}
+#endif	
+	else 
+		{
+		if (capabilityInfo.QueryFlag(CImapCapabilityInfo::ELoginDisabled))
+			{
+			__LOG_TEXT(LogId(), "CImapSessionManager - Login disabled");
+
+			// The server does not support insecure logins, so complete with an error
+			Complete(KErrImapServerLoginDisabled);
+			}	
+		else
+			{
+			// Just do plain login
+			SendLoginL();	
+			} 
+		}
+	}
+
+/**
+State handler called when the Start TLS response has been received.
+*/
+void CImapSessionManager::ProcessStartTLSResponse()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::ProcessCapabilityResponseL");
+
+	iState = EStateUpgradingTLSSocket;
+	CreateSecureSocket();
+	}
+
+/**
+State handler called when a socket has been upgraded to TLS.
+*/
+void CImapSessionManager::ProcessUpgradedTLSSocketL()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::ProcessUpgradedTLSSocketL");
+
+	iImapSession->CapabilityL(iStatus);
+	iState=EStateSecureCapabilityWait;
+	SetActive();
+	}
+
+/**
+State handler called when a capability response has been received after
+secure connection setup.
+*/
+void CImapSessionManager::ProcessSecureCapabilityResponseL()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::ProcessSecureCapabilityResponseL");
+
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+	const CImapCapabilityInfo& capabilityInfo = iImapSession->CapabilityInfo();
+	if (iImapSettings.IMAP4Auth())
+		{
+		SelectAuthExtensionProfileL(capabilityInfo);
+		}
+	else
+#endif 
+		{
+		SendLoginL();	
+		}
+	}
+
+/**
+State handler called when a login response has been received.
+*/
+void CImapSessionManager::ProcessLoginResponseL()
+	{
+	__LOG_ON(LogId(), ETrue);
+	__LOG_TEXT(LogId(), "CImapSessionManager::ProcessLoginResponseL");
+
+	GetHierarchySeparatorL();
+	}
+
+/**
+State handler called when a separator response has been received.
+*/
+void CImapSessionManager::ProcessSeparatorResponseL()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::ProcessSeparatorResponseL");
+
+	if ((iSeparatorFolderList.Count() > 0) &&
+	    (iSeparatorFolderList[0]->iHierarchySeperator != '\0'))
+		{
+		iImapSettings.SetPathSeparatorL(iSeparatorFolderList[0]->iHierarchySeperator);
+		}
+
+	iSeparatorFolderList.ResetAndDestroy();
+
+	Complete(KErrNone);
+	}
+
+/**
+State handler called when a logout response has been received.
+*/
+void CImapSessionManager::ProcessLogoutResponse()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::ProcessLogoutResponse");
+
+	DisconnectSocket();
+	}
+
+/**
+State handler called when a socket has been disconnected.
+*/
+void CImapSessionManager::ProcessSocketDisconnected()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::ProcessSocketDisconnected");
+
+	iDisconnectList.Remove(0);
+	DisconnectFirstSessionInList();
+	}
+
+/**
+Start creation of a network connection.
+*/
+void CImapSessionManager::CreateNetworkConnectionL()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::CreateNetworkConnectionL");
+	
+	if (iSocketServ.Handle() == 0)
+		{
+		User::LeaveIfError(iSocketServ.Connect());
+		}
+
+	if (iImConnect == NULL)
+		{
+		iImConnect = CImConnect::NewL(iImapSettings.IAPPreferences(), iSocketServ);
+		}
+
+	iImConnect->StartL(iStatus);
+	iState = EStateCreatingNetworkConnection;
+	SetActive();
+	}
+
+/**
+Gets the server address and password from the CImapSettings class and
+establishes a socket to the server.
+*/
+void CImapSessionManager::CreateSocketL()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::CreateSocket");
+
+	if (iTransportManager == NULL)
+		{
+		iTransportManager = CTransportManager::NewL(iSocketServ.Handle(), iImConnect->GetConnection());
+		}
+
+	TPtrC hostAddress = iImapSettings.ServerAddress();
+	TInt hostPort     = iImapSettings.Port();
+
+	iHostAddr8 = CnvUtfConverter::ConvertFromUnicodeToUtf8L(hostAddress);	
+	TPtr8 addr8ptr = iHostAddr8->Des();
+
+	iSocketConnector = &(iTransportManager->SocketFactory().ConnectL(*this, addr8ptr, hostPort));
+
+	iState = EStateConnectingSocket;
+	iStatus = KRequestPending;
+	SetActive();
+	}
+
+/**
+Start creation of a secure socket.
+*/
+void CImapSessionManager::CreateSecureSocket()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::CreateSecureSocket");
+
+	iOutputStream->BindSecure(*this);
+
+	TPtrC8 domainName;
+	if (iImapSettings.TlsSslDomain().Length() > 0)
+		{
+		domainName.Set(iImapSettings.TlsSslDomain());
+		}
+	else
+		{
+		domainName.Set(*iHostAddr8);
+		}
+
+	iOutputStream->SecureClientReq(domainName);
+
+	iStatus = KRequestPending;
+	SetActive();
+	}
+
+/**
+Create a new session, and wait for the greeting response from the IMAP server.
+*/
+void CImapSessionManager::StartSessionL()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapSessionManager::StartSessionL");
+
+	// Create the IMAP Session. 
+	iImapSession = CImapSession::NewL(iImapSettings, iImapMailStore, *iInputStream, *iOutputStream);
+
+	// Connected, request consumption of Server Greeting
+	iState = EStateGreetingWait;
+	iImapSession->ReadServerGreetingL(iStatus);
+	SetActive();
+	}
+
+/**
+Send a login command to the IMAP server.
+*/
+void CImapSessionManager::SendLoginL()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::SendLoginL");
+	
+	// Turn off logging so that user name and password are hidden
+	__LOG_ON(LogId(), EFalse);
+
+	iImapSession->LoginL(iStatus, iImapSettings.LoginName(), iImapSettings.Password());
+	iState = EStateLoginWait;
+	SetActive();
+	}
+
+/**
+Request the hierarchy separator from the IMAP server by sending a list command
+with no folders specified.
+*/
+void CImapSessionManager::GetHierarchySeparatorL()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::GetHierarchySeparatorL");
+
+	// Always update the path separator on connection.
+	iSeparatorFolderList.ResetAndDestroy();
+	iImapSession->ListL(iStatus, KNullDesC(), KNullDesC(), iSeparatorFolderList);
+	iState = EStateSeparatorWait;
+	SetActive();
+	}
+
+/**
+We are disconnecting a list of sessions. If there are any left in the list,
+send a logout command for the first one.
+*/
+void CImapSessionManager::DisconnectFirstSessionInList()
+	{
+	if (iDisconnectList.Count() == 0)
+		{
+		iDisconnectList.Reset();
+		Complete(KErrNone);
+		return;
+		}
+
+	__LOG_TEXT(LogId(), "CImapSessionManager::Logout session");
+
+	iState = EStateLogoutWait;
+	TRAPD(err, iDisconnectList[0]->LogoutL(iStatus));
+
+	if (err == KErrNone)
+		{
+		SetActive();
+		}
+	else
+		{
+		DisconnectSocket();
+		}
+	}
+
+/**
+Disconnect a socket.
+*/
+void CImapSessionManager::DisconnectSocket()
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::DisconnectSocket");
+
+	// Just close the streams as this will delete the socket to.
+	CloseSessionStreams(iDisconnectList[0]);
+
+	iState = EStateDisconnectingSocket;
+
+	// Complete ourselves to keep the state machine moving
+	iStatus = KRequestPending;
+	CompleteSelf(KErrNone);
+	SetActive();
+	}
+
+/**
+Immediately disconnect all the sessions in the disconnect list.
+No logout command is sent. We just disconnect the socket.
+*/
+void CImapSessionManager::ImmediateDisconnect()
+	{
+	while (iDisconnectList.Count() > 0)
+		{
+		__LOG_TEXT(LogId(), "CImapSessionManager::ImmediateDisconnect");
+
+		// Just close the streams as this will delete the socket to.
+		CloseSessionStreams(iDisconnectList[0]);
+		iDisconnectList.Remove(0);
+		}
+
+	iDisconnectList.Reset();
+	}
+
+/**
+Close the session streams. This will also delete the streams and their
+associated socket.
+*/
+void CImapSessionManager::CloseSessionStreams(CImapSession* aSession)
+	{
+	__LOG_TEXT(aSession->LogId(), "CImapSessionManager::CloseSessionStreams");
+
+	// Closing the output stream will close the input stream too.
+	MOutputStream* outputStream = aSession->OutputStream();
+
+	if (outputStream != NULL)
+		{
+		outputStream->Close();
+		}
+	}
+
+/**
+Gets the connection stage information from the connection.
+
+@return Connection stage or error code.
+*/
+TInt CImapSessionManager::GetConnectionStage()
+	{
+	if (iImConnect)
+		{
+		TNifProgress progress;
+		TInt err = iImConnect->Progress(progress);
+		return (err == KErrNone) ? (progress.iStage) : (err);
+		
+		}
+
+	return KErrNotFound;
+	}
+
+/**
+Converts an error code to one that is more meaningful for the state we are in.
+
+@param aError Error code to convert.
+*/
+void CImapSessionManager::ConvertError(TInt& aError)
+	{
+	if (aError <= KErrNone)
+		{
+		return;
+		}
+
+	switch (iState)
+		{
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)			
+
+		case EStateCapabilityWait:
+			{
+			__LOG_TEXT(LogId(), "CImapSessionManager - Convert error to bad  authenticate");
+			aError = KErrImapAuthenticationFailed;
+			break;
+			}
+#endif
+		case EStateLoginWait:
+			{
+			__LOG_TEXT(LogId(), "CImapSessionManager - Convert error to bad logon");
+			aError = KErrImapBadLogon;
+			break;
+			}
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)			
+		case EStateAuthenticateWait:
+			{
+			__LOG_TEXT(LogId(), "CImapSessionManager - Convert error to bad logon");
+			aError = KErrImapBadLogon;
+			break;
+			}
+#endif
+		case EStateStartTLSWait:
+			{
+			__LOG_TEXT(LogId(), "CImapSessionManager - Convert error to TLS negotiate failed");
+			aError = KErrImapTLSNegotiateFailed;
+			break;
+			}
+
+		case EStateLogoutWait:
+		case EStateDisconnectingSocket:
+			{
+			// Do nothing
+			break;
+			}
+
+		default:
+			{
+			__LOG_TEXT(LogId(), "CImapSessionManager - Convert error to Imap connect error");
+			// This is a postive error code (defined in cimapsession, thus allowing further
+			// handling in the protocol controller. Specifically, this is used in the case
+			// of initial connection failure where brearer mobility is configured.
+			aError = KErrImapConnectError;
+			break;
+			}
+		}
+	}
+
+/**
+Self complete with specified error code.
+
+@param aError Error code to complete with.
+*/
+void CImapSessionManager::CompleteSelf(TInt aError)
+	{
+	// We use callbacks from the transport handler to receive error
+	// notifications and then self complete with those error codes. As it is
+	// possible to receive more than one callback at a time, we have to make
+	// sure we don't try to self complete more than once.
+	if (!iCompletedSelf)
+		{
+		TRequestStatus* status = &iStatus;
+		User::RequestComplete(status, aError);
+		iCompletedSelf = ETrue;
+		}
+	}
+
+/**
+Gets the log ID to use for debug logging.
+
+@return Log ID
+*/
+TInt CImapSessionManager::LogId()
+	{
+#ifdef __IMAP_LOGGING
+	if (iImapSession)
+		{
+		return iImapSession->LogId();
+		}
+	else if (iDisconnectList.Count() > 0)
+		{
+		return iDisconnectList[0]->LogId();
+		}
+#endif //__IMAP_LOGGING
+	return KDefaultLog;
+	}
+
+/**
+Returns ETrue if the session manager has access to an RConnection
+
+@return TBool
+*/
+EXPORT_C TBool CImapSessionManager::HasConnection()
+	{
+	if (iImConnect)
+		{
+		return ETrue;
+		}
+
+	return EFalse;
+	}
+
+/**
+Gets the RConnection used for all the sessions
+
+@return RConnection reference
+*/
+EXPORT_C RConnection& CImapSessionManager::GetConnectionL()
+	{
+	__ASSERT_DEBUG(iImConnect,
+	               TImapServerPanic::ImapPanic(TImapServerPanic::ERConnectionNotDefined));
+
+	if (!iImConnect)
+		{
+		User::Leave(KErrNotFound);		
+		}
+
+	return iImConnect->GetConnection();
+	}
+
+/**
+Gets the access point ID in use for the connection to the server
+
+@param aAccessPointId On return stores the access point ID value
+
+@return KErrNone if successful, or a system wide error code
+*/
+EXPORT_C TInt CImapSessionManager::GetAccessPointIdForConnection(TUint32& aAccessPointId) const
+	{
+	if (iImConnect)
+		{
+		return iImConnect->GetIAPValue(aAccessPointId);
+		}
+
+	return KErrNotFound;
+	}
+	
+/**
+Close the current network connection since all the sessions will be closed before this
+*/
+EXPORT_C void CImapSessionManager::CloseNetworkConnection()
+ 	{
+ 	iCreatedNetworkConnection = EFalse;	
+	delete iTransportManager;
+	iTransportManager = NULL;
+ 	delete iImConnect;
+	iImConnect = NULL ;
+ 	iSocketServ.Close();
+ 	}
+
+/**
+Send a Authenticate  command to the IMAP server.
+@param aCurrentAuthProfile
+*/
+#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT)
+
+void CImapSessionManager::SendAuthenticateL(CImapAuthMechanismHelper::TImapAuthProfileFlag aCurrentAuthProfile) 
+	{
+	__LOG_TEXT(LogId(), "CImapSessionManager::SendAuthenticateL");
+	
+	iImapSession->AuthenticateL(iStatus,aCurrentAuthProfile);
+
+	iState =  EStateAuthenticateWait;
+	SetActive();
+	}
+
+/**
+State handler called when a authenticate response has been received.
+*/
+void CImapSessionManager::ProcessAuthenticateResponseL()
+	{
+	__LOG_ON(LogId(), ETrue);
+	__LOG_TEXT(LogId(), "CImapSessionManager::ProcessLoginResponseL");
+
+	GetHierarchySeparatorL();
+	}
+
+/**
+Selects  Authentication extension profile depending on authentication and fallback flags
+@param aCapabilityInfo
+*/	
+void CImapSessionManager::SelectAuthExtensionProfileL(const CImapCapabilityInfo& aCapabilityInfo)
+	{
+	if (aCapabilityInfo.QueryFlag(CImapCapabilityInfo::EAuthCramMd5))	
+		{
+		__LOG_TEXT(LogId(), "CImapSessionManager - ECramMD5");
+		iCurrentAuthProfile = CImapAuthMechanismHelper::ECramMD5;
+		SendAuthenticateL(iCurrentAuthProfile);
+		}
+	else if(iImapSettings.FallBack())
+    	{
+      	if (aCapabilityInfo.QueryFlag(CImapCapabilityInfo::EAuthPlain))
+      		{
+      		__LOG_TEXT(LogId(), "CImapSessionManager - EAuthPlain");
+      		iCurrentAuthProfile=CImapAuthMechanismHelper::EPlain;
+			SendAuthenticateL(iCurrentAuthProfile);
+      		}
+		else if (aCapabilityInfo.QueryFlag(CImapCapabilityInfo::EAuthLogin))
+			{
+			__LOG_TEXT(LogId(), "CImapSessionManager - ELogin");
+			iCurrentAuthProfile = CImapAuthMechanismHelper::ELogin;
+			SendAuthenticateL(iCurrentAuthProfile);
+			}
+    	else
+			{
+			__LOG_TEXT(LogId(), "CImapSessionManager - ELogin");
+			SendLoginL();	
+			}
+		}
+	else 
+		{
+		TInt status = 1;
+		Complete(status);
+		}
+	}
+#endif