diff -r 000000000000 -r 72b543305e3a email/imap4mtm/imapsession/src/cimapsession.cpp --- /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(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& 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
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()] 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()] 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& 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 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(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(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(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 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