--- /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