diff -r 000000000000 -r 72b543305e3a email/imap4mtm/imapprotocolcontroller/src/cimapprotocolcontroller.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/imap4mtm/imapprotocolcontroller/src/cimapprotocolcontroller.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,3526 @@ +// Copyright (c) 2008-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 +#include +#include + +#include "cimapprotocolcontroller.h" +#include "cimapsettings.h" +#include "cimapsessionmanager.h" +#include "cimapsession.h" +#include "cimapsessionconsts.h" +#include "cimapmailstore.h" +#include "cimapofflinecontrol.h" +#include "cimapsyncmanager.h" +#include "cimapfolder.h" + +#include "cimapcompoundcopyfromlocal.h" +#include "cimapcompoundcopytolocal.h" +#include "cimapcompoundcopywithinservice.h" +#include "cimapcompoundcreate.h" +#include "cimapcompounddelete.h" +#include "cimapcompounddeletefolder.h" +#include "cimapcompounddisconnect.h" +#include "cimapcompoundrename.h" +#include "cimapcompoundselect.h" +#include "cimapcompoundsyncfolder.h" +#include "cimapcompoundsynctree.h" +#include "cimapcompoundsyncservice.h" + +#include "cimapopbackgroundsync.h" +#include "cimapidlecontroller.h" +#include "cimapupdateflagoperation.h" + +#include "cimaplogger.h" + +#include "mobilitytestmtmapi.h" + +// at the moment we do not need more than 2 sessions +const TInt KImapSessionArrayGranularity = 2; + +const TUid KUidImapServerMtm = {0x10003C4E}; + +CImapProtocolController::CImapProtocolController(CMsvServerEntry& aEntry,CImapOfflineControl& aImapOfflineControl): + CMsgActive(EPriorityStandard), + iEntry(aEntry), + iImapSessionArray(KImapSessionArrayGranularity), + iImapOfflineControl(aImapOfflineControl) + { + } + +CImapProtocolController::~CImapProtocolController() + { + Cancel(); + + // The mail store needs to be deleted before DisconnectAll() as its destructor + // uses the session that DisconnectAll() destroys. + delete iImapMailStore; + + //serviceid indicates that we are online + if(iServiceId!=0) + { + DisconnectAll(); + } + + delete iBackgroundSyncOp; + delete iImapCompound; + delete iMigrateCompound; + delete iImapSyncManager; + delete iImapIdleController; + delete iImapSettings; + + delete iMobilityManager; + + // Need to destroy the sessions before the session manager is deleted. + // This is because session manager will cleanup transport handler code, + // and it is important to ensure that the sockets are not being used + // by the session when that happens. + iImapSessionArray.ResetAndDestroy(); + delete iImapSessionManager; + } + +EXPORT_C CImapProtocolController* CImapProtocolController::NewL(CMsvServerEntry& aEntry,CImapOfflineControl& aImapOfflineControl) + { + CImapProtocolController* self = new (ELeave) CImapProtocolController(aEntry,aImapOfflineControl); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +/** +Part of two phase construction +Note that syncmanager is not constructed until a connection is made. +*/ +void CImapProtocolController::ConstructL() + { + iImapSettings = CImapSettings::NewL(iEntry); + iImapMailStore = CImapMailStore::NewL(iEntry); + iImapSessionManager = CImapSessionManager::NewL(*iImapSettings, *iImapMailStore); + + iFlushPrimary = EFalse; + // We're an active object... + CActiveScheduler::Add(this); + } + +/** +Query connection status - return true if at least one IMAP Session exists and is +connected. Also return true if migrating. +This is not a compound operation. + +@return +*/ +EXPORT_C TBool CImapProtocolController::Connected() const + { + if (iImapSessionArray.Count() > 0 || iMigrateState != ENotMigrating) + { + // Sessions are deleted if a disconnect is observed, + // therefore the existance of any sessions indicates + // that the Protocol Controller is connected. + return ETrue; + } + return EFalse; + } + +/** +If a connected session already exists, this API shall complete immediately with +KErrServerBusy. Otherwise, this API uses the IMAP Session Manager to create and connect +an IMAP Session object that can be used for subsequent commands. +On successful connection, the passed request status is completed. This API can not +be used to instantiate further IMAP Session objects. + +Sequence For Connect + CreateSession + SelectInboxRW + Finished + +@param aStatus +@param aSelection +@leave KErrServerBusy if a connected session already exists +*/ +EXPORT_C void CImapProtocolController::ConnectL( TRequestStatus& aStatus, + CMsvEntrySelection& aSelection ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::ConnectL()"); + + ResetProgress(); + + if (Connected()) + { + // Do not call Complete() - this would result in the Protocol + // Controller disconnecting all sessions. + TRequestStatus* pStatus = &aStatus; + User::RequestComplete(pStatus, KErrServerBusy); + } + else + { + // Load the Service Settings + iServiceId = aSelection[0]; + iImapSettings->LoadSettingsL(iServiceId); + + // We're going online - create a session manager if none exists + if (iImapSessionManager==NULL) + { + // session manage may have been deleted due to a mobility error + iImapSessionManager = CImapSessionManager::NewL(*iImapSettings, *iImapMailStore); + } + + // create a sync manager + delete iImapSyncManager; + iImapSyncManager = NULL; + iImapSyncManager = CImapSyncManager::NewL( iEntry, *iImapSettings); + + // create the mobility manager if mobility supported + delete iMobilityManager; + iMobilityManager = NULL; + + if (iImapSettings->BearerMobility()) + { + iMobilityManager = CImMobilityManager::NewL(KUidImapServerMtm, iServiceId, *this); + } + + // Create the primary session pointer + CImapSession* imapSession = NULL; + iImapSessionArray.AppendL(imapSession); + + // Request the session manager to connect the session + iImapSessionManager->GetSessionL(iStatus, iImapSessionArray[0]); + + iForegroundSession = 0; + iRequestedOp = EConnect; + iCurrentOp = iRequestedOp; + + Queue(aStatus); + SetActive(); + } + } + +/** +This method allows the client to connect to an IMAP account and initialise a +background synchronisation of the account once connection has completed. If a +connected IMAP Session already exists, the API shall complete immdediately with +KErrServerBusy. +On successful connection, a background synchronisation is started and the passed +request status is completed. The background synchronisation is processed by a +CImapBackgroundSyncOp object, which will notify the Protocol Controller that +the process has completed via a callback function void +BackgroundSyncCompleted( TInt aError ) + +Sequence For ConnectAndSynchronise + CreateSession + StartBackgroundSyncOp + Finished + +@param aStatus +@param aSelection +@leave KErrServerBusy if a connected session already exists +*/ +EXPORT_C void CImapProtocolController::ConnectAndSynchroniseL( TRequestStatus& aStatus, + CMsvEntrySelection& aSelection ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::ConnectAndSynchroniseL()"); + + // Issue the connect request (this will cause ResetProgress() to be called) + ConnectL(aStatus, aSelection); + + // Create the Background Sync Operation object + if (IsActive()) + { + __ASSERT_DEBUG(iBackgroundSyncOp==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EConnectAndSyncBgSyncOpIsNotNull)); + iBackgroundSyncOp = CImapOpBackgroundSync::NewL(*this, iEntry, *iImapSyncManager, *iImapSettings, *iImapMailStore, iImapOfflineControl); + } + } + +/** +Logs out and disconnects all connected sessions with the remote server. + +Disconnect request is handled and acted upon during migration, unlike other +client-requested operations which are rejected (with KErrServerBusy). + +The Bearer Mobility Manager is deleted prior to acting on this request, +effectively de-registering the server MTM from the mobility framework + +@param aStatus +*/ +EXPORT_C void CImapProtocolController::DisconnectL(TRequestStatus& aStatus) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectL()"); + + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EDisconnectCompoundIsNotNull)); + + // de-register for mobility notifications + delete iMobilityManager; + iMobilityManager = NULL; + + // Disconnect allowed to happen even if we are migrating... + switch (iMigrateState) + { + case ENotMigrating: + case EHandlingConnectError: + { + // normal behaviour... + DoDisconnectL(aStatus); + break; + } + case EConnectingAfterMigrate: // Cancel connecting of new session + case EStartingReconnect: // already disconnected. Cancel dummy request + case EWaitingForNewCarrier: // already disconnected. Cancel dummy request + case EWaitingInitialCarrierRejected: // already disconnected. Cancel dummy request + { + Cancel(); + // mark offline and complete + DisconnectAll(); + Queue(aStatus); + Complete(KErrNone); + break; + } + case EDisconnectingForMigrate: + { + // already doing a graceful disconnect, switch to main state machine. + iMigrateState = ENotMigrating; + iRequestedOp = EDisconnect; + iCurrentOp = EDisconnect; + // however, using the iMigrateCompound pointer... + iImapCompound = iMigrateCompound; + iMigrateCompound = NULL; + Queue(aStatus); + break; + } + case EWaitingForOpToComplete: + case EWaitingForOpToStop: + default: + { + __ASSERT_DEBUG(iBackgroundSyncOp==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EDisconnectUnexpectedMigrateState)); + break; + } + } + } + +void CImapProtocolController::DoDisconnectL(TRequestStatus& aStatus) + { + // Unlike the other async methods, DisconnectL() will wait for any flush operation to finish + // and then initiate the disconnect. + // Consequently there is no need to call CompleteIfBackgroundOpInProgress() + ResetProgress(); + + iImapCompound = CImapCompoundDisconnect::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + *iImapSessionManager, + *iImapMailStore, + iImapSessionArray, + iImapOfflineControl, + ETrue ); + iRequestedOp = EDisconnect; + + if (iCurrentOp != ECancelRecoverPrimary) + { + // Cancel any outstanding background sync + if (iBackgroundSyncOp!=NULL) + { + iBackgroundSyncOp->Cancel(); + delete iBackgroundSyncOp; + iBackgroundSyncOp=NULL; + + // Flush the session, now that it has been cancelled + iCurrentOp = ECancelRecoverPrimary; + CImapSession* session = iImapSessionArray[0]; + session->FlushCancelledCommand(iStatus); + SetActive(); + } + else + { + StartPrimaryOperation(); + SetActive(); + } + } + + // NOTE: In the case of iCurrentOp == ECancelRecoverPrimary, DoRunL() will start the async + // iImapCompound operation after the flush has completed. + // The iImapCompound will expect aStatus to have been queued so that it can be Completed later. + + Queue(aStatus); + } + +/** +Checks the status of the CImapSessions + +@return ETrue if a backgroundsync is in progress and the second session is busy, + EFalse otherwise. +*/ +EXPORT_C TBool CImapProtocolController::Busy() const + { + // second session is busy if a compound operation exists. + if (BackgroundSyncInProgress() && iImapCompound!=NULL) + { + return ETrue; + } + return EFalse; + + } + +/** +Gets the access point ID in use for the connection to the server + +@param aIap On return stores the access point ID value + +@return KErrNone if successful, or a system wide error code +*/ +EXPORT_C TInt CImapProtocolController::GetAccessPointIdForConnection(TUint32& aAccessPointId) const + { + if (iImapSessionManager) + { + return iImapSessionManager->GetAccessPointIdForConnection(aAccessPointId); + } + + return KErrNotFound; + } + +/** +Returns whether a Background synchronisation is in progress + +@return ETrue if a Background synchronisation is in progress, EFalse otherwise +*/ +EXPORT_C TBool CImapProtocolController::BackgroundSyncInProgress() const + { + return (iBackgroundSyncOp != NULL); + } + +/** +Cancels an outstanding background synchronise operation. Calls +CImapOpBackgroundSync::Cancel() to propagate the cancel and kicks off +an asynchronous cleanup operation. +The cancel request is completed immediately with KErrNone if no background +sync was in operation, otherwise it is completed with KErrCancel when +the cleanup process is complete. +*/ +EXPORT_C void CImapProtocolController::CancelBackgroundSync(TRequestStatus& aStatus) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::CancelBackgroundSync()"); + + ResetProgress(); + + Queue(aStatus); + if (BackgroundSyncInProgress()) + { + if (iMigrateState != ENotMigrating && iBackgroundSyncOp->IsSuspendedForMigrate()) + { + delete iBackgroundSyncOp; + iBackgroundSyncOp = NULL; + Complete(KErrCancel); + } + else + { + iBackgroundSyncOp->CancelAndCleanup(); + iWaitForBackgroundSync = ETrue; + } + } + else + { + Complete(KErrNone); + } + } + +/** +Wait for background sync operation to complete. This effectively makes a background +synchronise a foreground operation blocking subsequent Server MTM requests until the +synchonise is completed. + +@param aStatus +*/ +EXPORT_C void CImapProtocolController::WaitForBackground(TRequestStatus& aStatus) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::WaitForBackground()"); + + ResetProgress(); + + Queue(aStatus); + // Wait for background operation to complete: is one running? + if (!BackgroundSyncInProgress()) + { + // No, complete immediately + Complete(KErrNone); + } + else + { + // Otherwise, wait for completion + iWaitForBackgroundSync = ETrue; + } + } + + +/** +Completes the passed request status if a background operation is in progress, +or if the primary session is being recovered following a cancel operation. +This should be called prior to starting a compound operation that requires the +primary session to be performed. + +This method must be called before assigning iRequestedOp, +for any operation that requires the primary session. + +@return ETrue if the user request has been completed + EFalse otherwise. +*/ +TBool CImapProtocolController::CompleteIfBackgroundOpInProgress(TRequestStatus& aStatus) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::CompleteIfBackgroundOpInProgress()"); + if (BackgroundSyncInProgress() || iCurrentOp==ECancelRecoverPrimary || iMigrateState!=ENotMigrating) + { + // Complete the user request + // Do not call Complete() - this would result in the Protocol + // Controller disconnecting all sessions. + TRequestStatus* status = &aStatus; + User::RequestComplete(status, KErrServerBusy); + return ETrue; + } + return EFalse; + } + +/** +Synchronise Folder Tree - calls CImapSyncManager::SynchroniseTreeL() +Sequence For SynchroniseTree + StopIdle + SynchroniseTree // CImapSyncManager Operation + +@param aStatus +@leave +*/ +EXPORT_C void CImapProtocolController::SynchroniseTreeL(TRequestStatus& aStatus) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::SynchroniseTreeL()"); + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESynchroniseTreeCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapCompoundSyncTree::NewL( *iImapSyncManager, + iEntry, + *iImapSettings ); + + iRequestedOp = ESync; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + + +/** +Perform a full account synchronisation - calls CImapSyncManager::SynchroniseL() +Sequence For SynchroniseAll + StopIdle + SynchroniseL // CImapSyncManager Operation + +@param aStatus +@leave +*/ +EXPORT_C void CImapProtocolController::SynchroniseAllL( TRequestStatus& aStatus ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::SynchroniseAllL()"); + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESynchroniseAllCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapCompoundSyncService::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + *iImapMailStore, + iImapOfflineControl, + EFalse); + + iRequestedOp = ESync; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +/** +Callback function to notify Protocol Controller that a background sync +operation has completed. +Deletes the CImapOpBackgroundComplete object that has just completed. +If WaitForBackground has been previously called, completes the user request. + +@param aError +*/ +EXPORT_C void CImapProtocolController::BackgroundSyncComplete(TInt aError) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::BackgroundSyncComplete()"); + __ASSERT_DEBUG(iBackgroundSyncOp!=NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ENoBackgroundSyncInProgress)); + + // get final sync progress... + // unless a foreground operation is being performed + if (iImapCompound == NULL) + { + iBackgroundSyncOp->Progress(iProgress); + } + + if (iMigrateState != ENotMigrating) + { + // If the protocol controller is not active, then there is no + // operation currently in progress. Therefore we are now ready to + // migrate. Complete the request here, and then Disconnect current sockets in DoMigrateRunL(). + // We cannot just test existance of a compound operation object, as there may be a "paused" operation. + if (!IsActive()) + { + // Complete the request so that, we migrate from DoMigrateRunL() + SetActive(); + TRequestStatus* pStatus = &iStatus; + User::RequestComplete(pStatus, KErrNone); + } + + // the background sync may have been completed early to allow + // a migration to occur. If that is the case, then we do not want + // to delete the background sync operation object - it will need + // to be restarted once the migrate has occurred. Otherwise, just + // process the completion of the background sync normally. + if (iBackgroundSyncOp->IsSuspendedForMigrate()) + { + return; + } + } + + // Deleting the background sync op. + // CImapOpBackgroundComplete must finish immediately after calling this API + delete iBackgroundSyncOp; + iBackgroundSyncOp = NULL; + + if (aError>0) + { + // translate positive error codes, weed out non-fatal errors + ThranslateSessionError(aError); + } + + TBool foregroundOpCancelled = EFalse; + if (aError==KErrNone || aError==KErrCancel) + { + if (iImapCompound==NULL && iMigrateState == ENotMigrating) + { + // only start IDLE if foreground operation not in progress + StartIdle(); + MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateImapIdle); + } + } + else + { + // Cancel any foreground operation - the primary session has + // failed, so all sessions are to be torn down. + if (iImapCompound != NULL) + { + iImapCompound->Cancel(); + foregroundOpCancelled = ETrue; + delete iImapCompound; + iImapCompound=NULL; + } + + // DisconnectAll will be process by DoComplete if a client request is outstanding. + if (!iWaitForBackgroundSync && !foregroundOpCancelled) + { + // Drop all session connections and mark the service offline. + DisconnectAll(); + } + } + + // Store the error in the progress object + iProgress.iGenericProgress.iErrorCode=aError; + iProgress.iGenericProgress.iState=TImap4GenericProgress::EIdle; + + // Complete the client request if it was waiting for notification of + // bg sync complete, or if we have cancelled an foreground operation + if (iWaitForBackgroundSync || foregroundOpCancelled) + { + iCancelInProgress = ETrue; + Complete(aError); + iCancelInProgress = EFalse; + } + + iWaitForBackgroundSync = EFalse; + } + +/** +CopyToLocalL() is used to fetch a selection of messages from the remote +server as a single operation, without return to idle state between handling each +message. The email is fetched to the local mirror of the containing folder. Optionally +this function can also copy the message to a local folder, specified in aDestination. +A fetch to mirror only is performed by calling with aDestination set to +KMsvNullIndexEntryId + +Sequence For CopyToLocal + StopIdle + SelectSourceMailboxRO + FetchMessage(s) + InboxDuplicateCopy + +@param aStatus +@param aSourceSel +@param aDestination +*/ +EXPORT_C void CImapProtocolController::CopyToLocalL(TRequestStatus& aStatus, + const CMsvEntrySelection& aSourceSel, + const TMsvId aDestination) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::CopyToLocalL()"); + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyToLocalCompoundIsNotNull)); + + ResetProgress(); + + // Instead of calling CompleteIfBackgroundOpInProgress(), just check whether a flush is occurring + // on the primary session (always index 0). + // If a background op is in progress, then the copy will be performed on a secondary session. + if (iCurrentOp == ECancelRecoverPrimary || iMigrateState != ENotMigrating) + { + // Do not call Complete() - this would result in the Protocol + // Controller disconnecting all sessions. + TRequestStatus* pStatus = &aStatus; + User::RequestComplete(pStatus, KErrServerBusy); + + return; + } + + // must be set before calling SelectSession + iRequestedOp = ECopyToLocal; + + // request the session to use. + TBool startNow = SelectSessionL(iForegroundSession); + + if (iBackgroundSyncOp && iImapSettings->UseSyncDownloadRules()) + { + // we need a non-const copy of the message selection to operate on. + CMsvEntrySelection* selection = aSourceSel.CopyLC(); + + // show the list to the background sync controller + iBackgroundSyncOp->RemoveFromSelectionL(*selection); + // Create the copy to local operation object using the (possibly) updated selection + iImapCompound = CImapCompoundCopyToLocal::NewL(*iImapSyncManager, + iEntry, + *iImapSettings, + *iImapMailStore, + EFalse, + *selection, + aDestination); + CleanupStack::PopAndDestroy(selection); + } + else + { + // Create the compound operation object + iImapCompound = CImapCompoundCopyToLocal::NewL(*iImapSyncManager, + iEntry, + *iImapSettings, + *iImapMailStore, + EFalse, + aSourceSel, + aDestination); + } + + if (startNow) + { + // A session is ready - start the command + // otherwise a request to connect a secondary session has been issued + if (iForegroundSession==0) + { + // Primary session + StartPrimaryOperation(); + } + else + { + // Secondary session + iCurrentOp = iRequestedOp; + iImapCompound->StartOperation(iStatus, *iImapSessionArray[iForegroundSession]); + } + } + + Queue(aStatus); + SetActive(); + } + + +/** +PopulateL() performs the same action as CopyToLocalL(), however uses the passed +TImImap4GetPartialMailInfo object instead of using the default parameters. This +allows the client application to specify the parts of the message that are to be +fetched, and to impose limits to on the amount of data that can be transferred. + +Sequence For Populate + StopIdle + SelectSourceMailboxRO + FetchMessage(s) + InboxDuplicateCopy + +@param aStatus +@param aSourceSel +@param aGetPartialMailInfo +*/ +EXPORT_C void CImapProtocolController::PopulateL(TRequestStatus& aStatus, + const CMsvEntrySelection& aSourceSel, + TImImap4GetPartialMailInfo aGetPartialMailInfo) + { + __LOG_FORMAT((KDefaultLog, "CImapProtocolController::PopulateL() - START - iCurrentOp: %d, iRequestedOp: %d", iCurrentOp, iRequestedOp )); + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EPopulateCompoundIsNotNull)); + + // Instead of calling CompleteIfBackgroundOpInProgress(), just check whether a flush is occurring + // on the primary session (always index 0). + // If a background op is in prgoress, then the copy will be performed on a secondary session. + ResetProgress(); + + // If a flush is occurring on the primary session (always index 0) then we are busy + if (iCurrentOp == ECancelRecoverPrimary || iMigrateState != ENotMigrating) + { + // Do not call Complete() - this would result in the Protocol + // Controller disconnecting all sessions. + TRequestStatus* pStatus = &aStatus; + User::RequestComplete(pStatus, KErrServerBusy); + __LOG_TEXT(KDefaultLog, "CImapProtocolController::PopulateL() - END - KErrServerBusy..."); + return; + } + + // must be set before calling SelectSessionL() + iRequestedOp = EPopulate; + + // request the session to use. + TBool startNow = SelectSessionL(iForegroundSession); + __LOG_FORMAT((KDefaultLog, "CImapProtocolController::PopulateL() - iCurrentOp: %d, iRequestedOp: %d, startNow: %d", iCurrentOp, iRequestedOp, startNow )); + + if (iBackgroundSyncOp && iImapSettings->UseSyncDownloadRules()) + { + // we need a non-const copy of the message selection to operate on. + CMsvEntrySelection* selection = aSourceSel.CopyLC(); + + // show the list to the background sync controller + iBackgroundSyncOp->RemoveFromSelectionL(*selection); + // Create the copy to local operation object using the (possibly) updated selection + iImapCompound = CImapCompoundCopyToLocal::NewL(*iImapSyncManager, + iEntry, + *iImapSettings, + *iImapMailStore, + EFalse, + *selection, + KMsvNullIndexEntryId, + aGetPartialMailInfo); + CleanupStack::PopAndDestroy(selection); + } + else + { + // Create the compound operation object + iImapCompound = CImapCompoundCopyToLocal::NewL(*iImapSyncManager, + iEntry, + *iImapSettings, + *iImapMailStore, + EFalse, + aSourceSel, + KMsvNullIndexEntryId, + aGetPartialMailInfo); + } + + __LOG_TEXT(KDefaultLog, "CImapProtocolController::PopulateL() - done creation of CompoundCopyToLocal..."); + if (startNow) + { + // A session is ready - start the command + // otherwise a request to connect a secondary session has been issued + if (iForegroundSession==0) + { + // Primary session + __LOG_TEXT(KDefaultLog, "CImapProtocolController::PopulateL() - starting primary op..."); + StartPrimaryOperation(); + } + else + { + // Secondary session + __LOG_TEXT(KDefaultLog, "CImapProtocolController::PopulateL() - starting compound op..."); + iCurrentOp = iRequestedOp; + iImapCompound->StartOperation(iStatus, *iImapSessionArray[iForegroundSession]); + } + } + + Queue(aStatus); + SetActive(); + __LOG_FORMAT((KDefaultLog, "CImapProtocolController::PopulateL() - END - iCurrentOp: %d, iRequestedOp: %d, startNow: %d", iCurrentOp, iRequestedOp, startNow )); + } + +/** +MoveToCopyL() shall enable a selection of messages to be moved as a single operation, +without return to idle state between handling each message. If a message has not been +previously fetched (ie is not complete on the local mirror of the IMAP folder) the +message is fetched to the local mirror and a copy is then made in the local service +folder. The message is then deleted from the IMAP folder on the remote server and the +local mirror. + +Sequence For MoveToLocal + StopIdle + SelectSourceMailbox + FetchMessage + InboxDuplicateCopy + DeleteMessage + +@param aStatus +@param aSourceSel +@param aDestination +*/ +EXPORT_C void CImapProtocolController::MoveToLocalL(TRequestStatus& aStatus, + const CMsvEntrySelection& aSourceSel, + const TMsvId aDestination) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::MoveToLocalL()"); + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EMoveToLocalCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + // We can't do a 'movetolocal' with the destination being the same as the + // source, as then the mirror will be out of sync. We must be moving to somewhere + // outside this service. Check this. + if (!IdIsLocalL(aDestination)) + { + // Complete the user request + // Do not call Complete() - this would result in the Protocol + // Controller disconnecting all sessions. + TRequestStatus* status = &aStatus; + User::RequestComplete(status, KErrNotSupported); + } + else + { + iImapCompound = CImapCompoundCopyToLocal::NewL(*iImapSyncManager, + iEntry, + *iImapSettings, + *iImapMailStore, + ETrue, + aSourceSel, + aDestination); + + iRequestedOp = EMoveToLocal; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + } + +/** +Copy within service is the operation of moving a message or selection of messages from +one mailbox to another on the remote IMAP server. This is done by issuing a COPY IMAP +command to the remote server, followed by a sync operation on the destination folder. + +Sequence For CopyWithinService + StopIdle + SelectSourceMailboxRO + CopyMessage + SelectDestinationMailboxRO + NewSyncFolder + +@param aStatus +@param aSourceSel +@param aDestination +*/ +EXPORT_C void CImapProtocolController::CopyWithinServiceL( TRequestStatus& aStatus, + const CMsvEntrySelection& aSourceSel, + const TMsvId aDestination ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::CopyWithinServiceL()"); + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyWithinServiceCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapCompoundCopyWithinService::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + EFalse, + aSourceSel, + aDestination); + + iRequestedOp = ECopyWithinService; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +/** +Move within service is the same as Copy Within Service, however the message is deleted +from the original location following the copy operation. + +Sequence For MoveWithinService + StopIdle + SelectSourceMailboxRW + CopyMessage + DeleteMessage + SelectDestinationMailboxRO + NewSyncFolder + +@param aStatus +@param aSourceSel +@param aDestination +*/ +EXPORT_C void CImapProtocolController::MoveWithinServiceL( TRequestStatus& aStatus, + const CMsvEntrySelection& aSourceSel, + const TMsvId aDestination ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::MoveWithinServiceL()"); + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EMoveWithinServiceCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapCompoundCopyWithinService::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + ETrue, + aSourceSel, + aDestination ); + + iRequestedOp = EMoveWithinService; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +/** +Copy from local is the action of copying an email from a local service folder (for +example the local inbox) to a folder on the remote IMAP service. This is done using +the IMAP APPEND command. + +Sequence For CopyFromLocal + AppendMessage + SelectDestinationMailboxRO + NewSyncFolder + +@param aStatus +@param aSourceSel +@param aDestination +*/ +EXPORT_C void CImapProtocolController::CopyFromLocalL( TRequestStatus& aStatus, + const CMsvEntrySelection& aSourceSel, + const TMsvId aDestination ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::CopyFromLocalL()"); + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyFromLocalCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapCompoundCopyFromLocal::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + EFalse, + aSourceSel, + aDestination ); + + iRequestedOp = ECopyFromLocal; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +/** +Move from local is the action of moving an email from a local service folder (for +example the local inbox) to a folder on the remote IMAP service. It is performed +by following the same steps as for a Copy From Local operation, using the IMAP APPEND +command, however the local copy of the message is deleted after the APPEND has +successfully completed. + +Sequence For MoveFromLocal + StopIdle + AppendMessage + DeleteLocalMessage + SelectDestinationMailboxRO + NewSyncFolder + +@param aStatus +@param aSourceSel +@param aDestination +*/ +EXPORT_C void CImapProtocolController::MoveFromLocalL( TRequestStatus& aStatus, + const CMsvEntrySelection& aSourceSel, + const TMsvId aDestination ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::MoveFromLocalL()"); + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyFromLocalCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapCompoundCopyFromLocal::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + ETrue, + aSourceSel, + aDestination ); + + iRequestedOp = EMoveFromLocal; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +/** +Deletes the specified messages from the remote server. + +Sequence For Delete + StopIdle + SelectSourceMailboxRW + Store /delete flags for each message + Expunge remote messages + Delete local messages + +@param aStatus +@param aSourceSel +*/ +EXPORT_C void CImapProtocolController::DeleteL(TRequestStatus& aStatus, + const CMsvEntrySelection& aSourceSel) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DeleteL()"); + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EDeleteCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + // Create the compound object for message delete + iImapCompound = CImapCompoundDelete::NewL(*iImapSyncManager, + iEntry, + *iImapSettings, + aSourceSel); + + iRequestedOp = EDelete; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +/** +Deletes the specified folder(s) on the remote IMAP Server. + +Sequence For DeleteFolder + StopIdle + SelectSourceMailboxRW + DeleteAllMessages + CloseFolder + DeleteLocalFolder + +@param aStatus +@param aSelection +*/ +EXPORT_C void CImapProtocolController::DeleteFolderL( TRequestStatus& aStatus, + const CMsvEntrySelection& aSelection ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DeleteFolderL()"); + __ASSERT_DEBUG(iImapCompound == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EDeleteFolderCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapCompoundDeleteFolder::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + aSelection ); + + iRequestedOp = EDelete; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +/** +NewOnlySyncL() is used to synchronise recent messages in the specified folder, +i.e. any messages that have arrived in the remote IMAP mailbox since the last +synchronisation. This is done by requesting (FETCHing) the header details for +messages with UID's that are greater than the highest UID of the messages present +in the local mirror of the folder. The message's header summary information, +flags etc are stored as new entries in the Message Server entry array under the +folder entry, and the header information is streamed to the Mailstore using the +Mailstore API. + +Sequence For NewSyncFolder + StopIdle + SelectSourceMailboxRW + NewSyncFolder + +@param aStatus +*/ +EXPORT_C void CImapProtocolController::NewOnlySyncFolderL( TRequestStatus& aStatus ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::NewOnlySyncFolderL()"); + __ASSERT_DEBUG(iImapCompound == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ENewOnlySyncFolderCompoundIsNotNull)); + + ResetProgress(); + + if (iMigrateState!=ENotMigrating) + { + // Complete the user request if migrating. + // Do not call Complete() - this would result in the Protocol + // Controller disconnecting all sessions. + TRequestStatus* status = &aStatus; + User::RequestComplete(status, KErrServerBusy); + return; + } + + // If we support idle, and the server we are talking to supports idle, + // then we don't need to do the new only sync as IMAP idle will take + // care of it for us. + // Just complete the user request with KErrNone. + if (iImapSettings->ImapIdle() && iImapSessionArray[0]->ImapIdleSupported()) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::NewOnlySyncFolderL() - Immediate complete as idle is in use"); + Queue(aStatus); + Complete(KErrNone); + return; + } + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + CImapFolder* folder = iImapSyncManager->Inbox(); + TMsvId inboxId = folder->MailboxId(); + + iImapCompound = CImapCompoundSyncFolder::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + *iImapMailStore, + ETrue, + inboxId); + + iRequestedOp = ESync; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +/** +The full synchronisation of a folder involves the synchronisation of messages newly +received at the IMAP server, as described for NewOnlySyncL, above, and the +synchronisation of "old messages", ie messages that have previously been synchronised +to the local mirror of the folder. +When synchronising old messages, messages that have been marked for delete locally +are marked such on the remote folder (the actual delete occurs either at the end of +the synchronisation process, or is deferred until the connection is cancelled), +messages that have been removed from the remote server (by another client) are +deleted locally and any outstanding offline operations are performed, for example +move operations may be outstanding, etc. + +Sequence For FullSyncFolder + StopIdle + SelectSourceMailboxRW + SyncFolder + +@param aStatus +@param aFolder +*/ +EXPORT_C void CImapProtocolController::FullSyncFolderL( TRequestStatus& aStatus, + const TMsvId aFolder ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::FullSyncFolderL()"); + __ASSERT_DEBUG(iImapCompound == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EFullSyncFolderCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapCompoundSyncFolder::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + *iImapMailStore, + EFalse, + aFolder ); + + iRequestedOp = ESync; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +/** +SELECT is made available to the client application directly via an IMAP MTM command +which is handled by this function. It allows the client to specifically SELECT a +mailbox and then, using the Synchronise MTM command, to specifically request the +selected mailbox is synchronised. + +Sequence For Select + StopIdle + SelectSourceMailboxRW + +@param aStatus +@param aFolder +*/ +EXPORT_C void CImapProtocolController::SelectL(TRequestStatus& aStatus, const TMsvId aFolder) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::SelectL()"); + __ASSERT_DEBUG(iImapCompound == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESelectCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapCompoundSelect::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + aFolder ); + + iRequestedOp = ESelect; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +/** +Performs a full sync on the currently selected mailbox. + +Sequence For FullSyncSelectedFolder + StopIdle + SyncFolder + +@param aStatus +*/ +EXPORT_C void CImapProtocolController::FullSyncSelectedFolderL( TRequestStatus& aStatus ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::FullSyncSelectedFolderL()"); + __ASSERT_DEBUG( iImapCompound == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EFullSyncSelectedFolderCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapCompoundSyncFolder::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + *iImapMailStore, + EFalse ); + + iRequestedOp = ESync; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +/** +Creates a new folder on the remote IMAP service with the given name. + +Sequence For Create + StopIdle + Create + +@param aStatus +@param aParent +@param aLeafName +@param aFolder +*/ +EXPORT_C void CImapProtocolController::CreateL( TRequestStatus& aStatus, + const TMsvId aParent, + const TDesC& aLeafName, + const TBool aFolder ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::CreateL()"); + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECreateCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapCompoundCreate::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + aParent, + aLeafName, + aFolder ); + + iRequestedOp = ECreate; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +/** +Renames the specified folder. Note that although this does cause a write to the +remote folder, it does not change the contents of the folder and hence does not +force a re-synchronisation of the folder. + +Sequence For Rename + StopIdle + RenameRemote + RenameLocal + +@param aStatus +@param aTarget +@param aNewName +*/ +EXPORT_C void CImapProtocolController::RenameL( TRequestStatus& aStatus, + const TMsvId aTarget, + const TDesC& aNewName ) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::RenameL()"); + __ASSERT_DEBUG(iImapCompound == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ERenameCompoundIsNotNull)); + + ResetProgress(); + + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapCompoundRename::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + aTarget, + aNewName ); + + iRequestedOp = ERename; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + + +EXPORT_C void CImapProtocolController::DoRunL() + { + // Handle all migration operations in a separate state machnine. + if (iMigrateState != ENotMigrating) + { + DoMigrateRunL(); + return; + } + + // ProcessError completes and returns ETrue if an error occured + if (ProcessError(iStatus.Int())) + { + return; + } + + switch (iCurrentOp) + { + case EConnect: + { + TRAP_IGNORE(MarkOnOrOfflineL(ETrue)); + + // Collect the final connect progress information + iImapSessionManager->Progress(iProgress.iGenericProgress); + + // Set last socket activity timeout to iMtmData1. This is used by Imcm. + User::LeaveIfError( iEntry.SetEntry( iServiceId ) ); + TMsvEntry entry=iEntry.Entry(); + entry.SetMtmData1(iImapSessionManager->LastSocketActivityTimeout()); + User::LeaveIfError( iEntry.ChangeEntry( entry ) ); + + // Create an IMAP IDLE controller + delete iImapIdleController; + iImapIdleController=NULL; + iImapIdleController = CImapIdleController::NewL(*this, iImapSessionArray[0], *iImapSyncManager, iEntry, *iImapSettings, *iImapMailStore); + + // Register the connection with the mobility manager, + // if we are a mobile service + if (iMobilityManager) + { + iMobilityManager->SetConnection(iImapSessionManager->GetConnectionL()); + } + + // kick off a background sync if it was requested. + if (iBackgroundSyncOp != NULL) + { + iBackgroundSyncOp->StartSync(*iImapSessionArray[0]); + iCurrentOp = EIdle; + } + else + { + StartIdle(); + } + + // complete the connect request + Complete(iStatus.Int()); + break; + } + + case EConnectSecondary: + { + // perform the user-requested operation using the + // newly created imap session + iCurrentOp = iRequestedOp; + if(iImapCompound->Suspended()) + { + // if the operation was previously suspended for migration, resume it... + iImapCompound->ResumeOperationL(iStatus, *iImapSessionArray[iForegroundSession]); + } + else + { + // otherwise, just start the operation. + iImapCompound->StartOperation(iStatus, *iImapSessionArray[iForegroundSession]); + } + SetActive(); + break; + } + + case EStopIdle: + { + // Idle has been cancelled, the primary session is available + // Kick off the requested operation + iCurrentOp = iRequestedOp; + iImapCompound->StartOperation(iStatus, *iImapSessionArray[0]); + SetActive(); + break; + } + + case EDisconnect: + { + TRAP_IGNORE( MarkOnOrOfflineL( EFalse ) ); + + iImapSessionArray.ResetAndDestroy(); + + // update last operation progress state + iImapCompound->Progress(iProgress); + + delete iImapCompound; + iImapCompound = NULL; + iServiceId=0; + + Complete(iStatus.Int()); + break; + } + + case ECancelRecoverPrimary: + { + // Primary session is successfully recovered + // Start the next requested operation + + if (iRequestedOp == EIdle) + { + // We should only go into Idle if no async operations have been requested. + // Consequently, CMsgActive::iReport should be NULL. + // We can't ASSERT this here as it is a private member of CMsgActive, but it is worth checking while debugging. + StartIdle(); + } + else if (iRequestedOp == EDisconnect) + { + // We should only start a primary operation if an async operation has been requested, and an aStatus Queue()ed. + // Consequently, CMsgActive::iReport should NOT be NULL. + // We can't ASSERT this here as it is a private member of CMsgActive, but it is worth checking while debugging. + StartPrimaryOperation(); + SetActive(); + } + else + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EProtocolControllerUnexpectedRequestedOp)); + } + + break; + } + + case ESync: + case ESelect: + case ECopyToLocal: + case ECopyWithinService: + case ECopyFromLocal: + case EMoveToLocal: + case EMoveWithinService: + case EMoveFromLocal: + case EPopulate: + case EDelete: + case EDeleteFolder: + case ECreate: + case ERename: + case EUpdateFlag: + { + // update last operation progress state + iImapCompound->Progress(iProgress); + + delete iImapCompound; + iImapCompound = NULL; + + if ((iRequestedOp==ESelect) && (iProgress.iGenericProgress.iErrorCode==KErrNone)) + { + // Do not start IDLE following a select request + Complete(iStatus.Int()); + } + else + { + // Start idle and complete the server mtm request. + StartIdle(); + Complete(iStatus.Int()); + } + break; + } + + case EIdle: // shouldn't happen + default: + { + Complete(iStatus.Int()); + break; + } + + } // end of switch (iCurrentOp) + } + +/** +This variation on DoRunL() presents a super-state machine, that replaces +the default state machine during a migration process. +*/ +void CImapProtocolController::DoMigrateRunL() + { + // waiting for existing operation to stop to allow migration to occur. + __LOG_FORMAT((KDefaultLog, "CImapProtocolController::DoMigrateRunL(iMigrateState = %d) CurrentOp = %d", iMigrateState, iCurrentOp)); + switch (iMigrateState) + { + case EWaitingForOpToStop: + case EWaitingForOpToComplete: + { + // operation has completed (is either finished, or has "paused" + // to allow migration to complete. Do any tidying up necessary. + ProcessOpCompleteForMigrate(); + + // is there also a background sync op in progress? if not (or if the + // background sync op is suspended, we are now ready to migrate, + // otherwise wait for the background op to complete. + // + if (!iBackgroundSyncOp || iBackgroundSyncOp->IsSuspendedForMigrate()) + { + // Asynch disconnect current sockets. + DisconnectForMigrateL(); + } + break; + } + case EDisconnectingForMigrate: + { + // The disconnect operation has completed. Delete IMAP Sessions. + iImapSessionArray.ResetAndDestroy(); + + // delete the migration compound object + delete iMigrateCompound; + iMigrateCompound = NULL; + + // Notify the mobility framework that we are ready to migrate. + iMigrateState = EWaitingForNewCarrier; + iMobilityManager->MigrateToNewCarrier(); + + // do not set waiting if NewCarrierActive has been called synchronously. + if (iMigrateState == EWaitingForNewCarrier) + { + // Now in a waiting state, set self active + iStatus = KRequestPending; + SetActive(); + } + break; + } + case EHandlingConnectError: + { + // register with the mobility framework + iMobilityManager->SetConnection(iImapSessionManager->GetConnectionL()); + + // empty the session array + TInt numSessions = iImapSessionArray.Count(); + for (TInt i=0; iDisconnect(*(iImapSessionArray[i])); + } + } + iImapSessionArray.ResetAndDestroy(); + + // reject the initial carrier + iMigrateState = EWaitingInitialCarrierRejected; + iMobilityManager->NewCarrierRejected(); + + if (iMigrateState == EWaitingInitialCarrierRejected) + { + iStatus = KRequestPending; + SetActive(); + } + break; + } + case EStartingReconnect: + { + NewPrimarySessionL(); + break; + } + case EConnectingAfterMigrate: + { + if (iStatus.Int()!=KErrNone) + { + // An error has occurred while attempting to re-connect + // - reject this new carrier, wait to see if a new one turns up. + iMigrateState = EWaitingForNewCarrier; + iMobilityManager->NewCarrierRejected(); + + // Now in a waiting state, set self active + if (iMigrateState == EWaitingForNewCarrier) + { + iStatus = KRequestPending; + SetActive(); + } + } + else + { + iMobilityManager->NewCarrierAccepted(); + RestartAfterMigrateL(); + } + break; + } + case ENotMigrating: + case EWaitingForNewCarrier: + case EWaitingInitialCarrierRejected: + // DoRunMigrateL() Should never be called in this state. + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EProtocolControllerUnexpectedMigrateState)); + break; + } + } + } + + +/** +Called when completing an outstanding client request. +Handles negative (system-wide) error codes returned +on completion of asynchronous service requests. +*/ +void CImapProtocolController::DoComplete(TInt& aErr) + { + __LOG_FORMAT((KDefaultLog, "CImapProtocolController::DoComplete() - START - aErr = %d, CurrentOp = %d, iCancelInProgress: %d", aErr, iCurrentOp, iCancelInProgress)); + + // Requested operation is completed. + iRequestedOp = EIdle; + + // return if everything is OK + if (aErr==KErrNone) + { + __LOG_TEXT( KDefaultLog, "CImapProtocolController::DoComplete() - END - KErrNone" ); + return; + } + + // First retrieve the progress information + this->Progress(); + + // log that a cancel has occurred and return + if (aErr==KErrCancel) + { + __LOG_TEXT( KDefaultLog, "CImapProtocolController::DoComplete() - dealing with cancel case..." ); + iProgress.iGenericProgress.iErrorCode=aErr; + if(!iCancelInProgress) + { + __LOG_TEXT( KDefaultLog, "CImapProtocolController::DoComplete() - no cancel in progress, so disconnecting all..." ); + DisconnectAll(); + } + + __LOG_TEXT( KDefaultLog, "CImapProtocolController::DoComplete() - END - KErrCancel" ); + return; + } + + // Non-fatal errors should have been handled prior to this. + __LOG_TEXT( KDefaultLog, "CImapProtocolController::DoComplete() - fatal error - disconnect all..." ); + // iServiceId = 0, indicates we are already disconnected. + if(iServiceId!=0) + { + DisconnectAll(); + } + + // Save error code in progress and flag the disconnect + iProgress.iGenericProgress.iState=TImap4GenericProgress::EDisconnected; + iProgress.iGenericProgress.iErrorCode=aErr; + + __LOG_TEXT( KDefaultLog, "CImapProtocolController::DoComplete() - END - fatal error" ); + } + + +/** +Cancels any outstanding client requested asynchronous operations and recovers +the primary session if required. + +This function is exclusively for the use of the CImapServerMtm, and only calls +Cancel() if the PC is currently active performing the requested operation. +If the PC is active for other reasons (for example, waiting for a bearer +migration to complete), the appropriate action is taken. +*/ +EXPORT_C void CImapProtocolController::CancelAndCleanup() + { + __LOG_FORMAT( (KDefaultLog, "CImapProtocolController::CancelAndCleanup() - START - iCurrentOp: %d, iRequestedOp: %d, iFlushPrimary: %d", iCurrentOp, iRequestedOp, iFlushPrimary ) ); + if ( iMigrateState == ENotMigrating || + iMigrateState == EWaitingForOpToComplete || + iMigrateState == EWaitingForOpToStop ) + { + // In these states the protocol controller is currently doing something + // at the clients request. + // iCancelInProgress + iCancelInProgress = ETrue; + Cancel(); + + __LOG_TEXT(KDefaultLog, "CImapProtocolController::CancelAndCleanup() - done DoCancel"); + + // DoCancel() will set iFlushPrimary to ETrue for operations that require + // a flush after being cancelled. This will cause DoRunL() to be called when the + // session has been flushed. In the case of bearer migration, the next step will + // be started in DoRunL. + if (iFlushPrimary) + { + iRequestedOp = EIdle; // We want to return to idle when the flush has completed. + iCurrentOp = ECancelRecoverPrimary; + CImapSession* session = iImapSessionArray[0]; + session->FlushCancelledCommand(iStatus); + SetActive(); + iFlushPrimary = EFalse; + } + // clear the cancelling flag + iCancelInProgress = EFalse; + } + else + { + // in other migration states, we don't want to cancel because we + // are active for migration purposes. However, we do need to clean + // compound operation objects and complete the user. + iRequestedOp = iCurrentOp = EIdle; + + // Update the progress object + iProgress.iGenericProgress.iState = TImap4GenericProgress::EIdle; + iProgress.iGenericProgress.iErrorCode = KErrCancel; + + delete iImapCompound; + iImapCompound = NULL; + CMsgActive::DoCancel(); // completes the user request. + } + __LOG_TEXT(KDefaultLog, "CImapProtocolController::CancelAndCleanup() - END"); + } + + +/** +DoCancel - called by CMsgActive::Cancel() to cancel asychronous services: + +Cancel() must not be called on this class to cancel a requested async +service - CancelAndCleanup() should be used instead. CancelAndCleanup() +provides support for cancelling requested operations while this class +has become active for migration purposes. + +However, Cancel() must always be called eventually as the iStatus needs +to be cleared. Hence this function must support all cancel operations. + +This class is allows for Cancel() to be called internally to cancel +async operations that have been launched internally for migration +purposes, without completing the CImapServerMtm. +*/ +void CImapProtocolController::DoCancel() + { + __LOG_FORMAT( (KDefaultLog, "CImapProtocolController::DoCancel() - START - iCurrentOp: %d, iRequestedOp: %d, iFlushPrimary: %d", iCurrentOp, iRequestedOp, iFlushPrimary ) ); + + if (iMigrateState==ENotMigrating) + { + DoCancelClientOp(); + } + else + { + switch (iMigrateState) + { + case ESuspendingForMigrate: + { + // Special handling for cancelling current operation to allow + // migration to occur. + DoCancelForMigrate(); + break; + } + case EWaitingForOpToComplete: + case EWaitingForOpToStop: + { + // we have an outstanding request on the compound operation + // call the default DoCancelClientOp(). This will also + // complete the client's iStatus with KErrCancel + DoCancelClientOp(); + break; + } + case EDisconnectingForMigrate: + { + // outstanding op is on the iMigrateCompound + iMigrateCompound->Cancel(); + delete iMigrateCompound; + iMigrateCompound = NULL; + break; + } + case EConnectingAfterMigrate: + { + // This state represents re-connection of the primary session, + // it is safe to simply clear the imap session array. + iImapSessionManager->Cancel(); + iImapSessionArray.ResetAndDestroy(); + break; + } + case EWaitingForNewCarrier: + case EWaitingInitialCarrierRejected: + { + // in these states, we are in a self-induced active state + // Cancel it: + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrCancel); + break; + } + + case EStartingReconnect: // nothing to cancel + case EHandlingConnectError: // nothing to cancel + case ENotMigrating: // somethings gone wrong + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EProtocolControllerCancelBadMigrateState)); + } + } // switch (iMigrateState) + } + + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancel() - END"); + } + +void CImapProtocolController::DoCancelClientOp() + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - START"); + + // Update the requested and current operations + // the implication is that a return to EIdle is required + // current op may be updated later if an async op is issued. + TImapProtocolOp cancelledOp = iCurrentOp; + iRequestedOp = iCurrentOp = EIdle; + + switch (cancelledOp) + { + case EConnect: + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - EConnect"); + iImapSessionManager->Cancel(); + + // As this state represents connection of the primary session, + // it is safe to simply clear the imap session array. + iImapSessionArray.ResetAndDestroy(); + + // delete the background sync operation if it exists. + if (iBackgroundSyncOp != NULL) + { + iBackgroundSyncOp->Cancel(); + delete iBackgroundSyncOp; + iBackgroundSyncOp=NULL; + } + + // delete the sync manager + delete iImapSyncManager; + iImapSyncManager = NULL; + + break; + } + + case EConnectSecondary: + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - EConnectSecondary"); + // Cancel the connect request + iImapSessionManager->Cancel(); + iForegroundSession=0; + + // Remove the session pointer from the array + // The session has already been deleted - see note above. + TInt numSessions = iImapSessionArray.Count(); + iImapSessionArray.Remove(numSessions-1); + + if (iImapCompound!=NULL) + { + // The requested op hasn't actually started yet + // so don't cancel on the compound operation.. + // update last operation progress state + iImapCompound->Progress(iProgress); + + delete iImapCompound; + iImapCompound = NULL; + } + + // The progress will not report cancel at this stage - force it here. + iProgress.iGenericProgress.iState = TImap4GenericProgress::EIdle; + iProgress.iGenericProgress.iErrorCode = KErrCancel; + + break; + } + + case EDisconnect: + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - EDisconnect"); + // Cancel the disconnect compound operation + iImapCompound->Cancel(); + + delete iImapCompound; + iImapCompound = NULL; + + // A cancelled disconnect will leave the sessions disconnected + // but not deleted. Reset and destroy the session array. + iImapSessionArray.ResetAndDestroy(); + + break; + } + + case EIdle: + { + // This is unexpected - in this state there should be no + // outstanding asynconous requests on the Protocol Controller. + // Protocol Controller has no outstanding requests in this state + // however a sync may be running in the background. This is + // cancelled when the background sync object's destructor is called. + // So.. Nothing to do. + break; + } + + case EStopIdle: + { + if (iImapCompound!=NULL) + { + // The requested op hasn't actually started yet + // so don't cancel on the compound operation.. + // update last operation progress state + iImapCompound->Progress(iProgress); + + delete iImapCompound; + iImapCompound = NULL; + } + + // The progress will not report cancel at this stage - force it here. + iProgress.iGenericProgress.iState = TImap4GenericProgress::EIdle; + iProgress.iGenericProgress.iErrorCode = KErrCancel; + + // Cancel the idle controller. + iImapIdleController->Cancel(); + + // Cancelling the idle controller is likely to leave the IMAP Session + // with data left on the input stream to be dealt with. We need to + // flush the session if we wish to re-use it. + iFlushPrimary = ETrue; + + break; + } + + case ESync: + case ESelect: + case ECopyToLocal: + case ECopyWithinService: + case ECopyFromLocal: + case EMoveToLocal: + case EMoveWithinService: + case EMoveFromLocal: + case EPopulate: + case EDelete: + case EDeleteFolder: + case ECreate: + case ERename: + case EUpdateFlag: + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - ESync/ESelect/ECopyToLocal/ECopyWithinService/ECopyFromLocal/EMoveToLocal/EMoveWithinService/EMoveFromLocal/EPopulate/EDelete/EDeleteFolder/ECreate/ERename/"); + // This is the normal case, a requested operation is + // being performed by a compound operation object. + // First of all, check one exists. + if (iImapCompound!=NULL) + { + // Cancel the compound operation + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - cancelling iImapCompound..."); + iImapCompound->Cancel(); + + // update last operation progress state + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - getting progres..."); + iImapCompound->Progress(iProgress); + + // Delete the compound object + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - deleting compound..."); + delete iImapCompound; + iImapCompound = NULL; + + // Cancelling the compound operation is likely to leave + // the IMAP Session in an incomplete state. We need to + // flush the session if we wish to re-use it. + if (iForegroundSession==0) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - flush primary = ETrue..."); + iFlushPrimary = ETrue; + } + else + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - drop session..."); + DropSession(iForegroundSession); + iForegroundSession=0; + } + + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - main iImapCompound handling complete..."); + } + break; + } + + case ECancelRecoverPrimary: + { + // The recover must be taking too long. + // cancel the recover request.. + (iImapSessionArray[0])->Cancel(); + // Delete all sessions, go offline etc. + DisconnectAll(); + break; + } + default: + { + // nothing to do.. + break; + } + } // end of switch (cancelledOp) + + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - about to call CMsgActive::DoCancel()..."); + CMsgActive::DoCancel(); + + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - END"); + } + + +/** +Performs a fast disconnect on all sessions + +Ensures that the compound, background sync and sync manager objects are +deleted following the disconnect. +@param aMarkOffline - indicates if the service is to be marked offline. +*/ +void CImapProtocolController::DisconnectAll() + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - START"); + + // Delete the background sync op + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - deleting iBackgroundSyncOp"); + delete iBackgroundSyncOp; + iBackgroundSyncOp=NULL; + + // Delete the imap compound operation + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - deleting iImapCompound"); + delete iImapCompound; + iImapCompound=NULL; + + // Delete the migrate compound operation + delete iMigrateCompound; + iMigrateCompound = NULL; + + // delete the sync manager + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - deleting iImapSyncManager"); + delete iImapSyncManager; + iImapSyncManager = NULL; + + // delete the mobility manager (de-register for mobility notifications) + delete iMobilityManager; + iMobilityManager = NULL; + + // delete the imap idle controller + delete iImapIdleController; + iImapIdleController = NULL; + + // Drop connection on each of the imap sessions + TInt numSessions = iImapSessionArray.Count(); + for (TInt i=0; iDisconnect(*(iImapSessionArray[i])); + } + } + + // The current network connection will be closed + iImapSessionManager->CloseNetworkConnection(); + + + // empty the session array + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - resetting and destroying sessions..."); + iImapSessionArray.ResetAndDestroy(); + + // force the state to IDLE + iRequestedOp = EIdle; + iCurrentOp = EIdle; + + // Clear serviceid and mark the service as offline + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - about to MarkOnOrOffline..."); + TRAP_IGNORE(MarkOnOrOfflineL(EFalse)); + iServiceId=0; + + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - END"); + } + +/** +Performs an immediate disconnect of specifed session and removes it from +the session array. + +@param sessionId - the index in the session array of the session to drop. +*/ +void CImapProtocolController::DropSession(TInt sessionId) + { + if (sessionId < iImapSessionArray.Count()) + { + CImapSession* tempSession = iImapSessionArray[sessionId]; + if (tempSession) + { + iImapSessionManager->Disconnect(*tempSession); + delete tempSession; + } + iImapSessionArray.Remove(sessionId); + } + } + +/** +Private method to select session from the session array for use for operations +that are supported while background operations are in progress. +Issues a request to connect a secondary session if the primary session is busy +and a secondary session is not already available. In this case the requested op +is updated to show a secondary connect request, and the original requested op +is saved. + +@param aIndexToUse updated to indicate the index of the session to be used +@return ETrue if the session is connected and ready to use immediately. + EFalse if a request to connect a secondary session has been issued. +*/ +TBool CImapProtocolController::SelectSessionL(TInt& aIndexToUse) + { + __ASSERT_DEBUG(iCurrentOp != ECancelRecoverPrimary, TImapServerPanic::ImapPanic(TImapServerPanic::EProtocolControllerUnexpectedCurrentOp)); + + aIndexToUse = 0; // default to primary session. + TBool sessionReady = ETrue; + if (iBackgroundSyncOp != NULL) + { + TInt sessionCount = iImapSessionArray.Count(); + if (sessionCount>=2) + { + // already a secondary session available - use this + aIndexToUse = sessionCount-1; + } + else + { + // Create a new session and get it connected. + iCurrentOp = EConnectSecondary; + + CImapSession* imapSession = NULL; + iImapSessionArray.AppendL(imapSession); + iImapSessionManager->GetSessionL(iStatus, iImapSessionArray[sessionCount]); + aIndexToUse = sessionCount; + sessionReady = EFalse; + } + } + return sessionReady; + } + +/** +Called to kick off a foreground operation, ie a user requested +operation that is being performed on the primary IMAP session. + +If the session is in IMAP IDLE, a request is issued to stop this +and the requested operation is started when the stop-idle operation +has completed. Otherwise the operation is started immediately. +*/ +void CImapProtocolController::StartPrimaryOperation() + { + // Make sure the foreground session id is correct + iForegroundSession = 0; + // imap idle controller is active in any state after + // idle has been requested. + if (iImapIdleController->IsActive()) + { + iCurrentOp = EStopIdle; + iImapIdleController->StopIdle(iStatus); + } + else + { + iCurrentOp = iRequestedOp; + iImapCompound->StartOperation(iStatus, *iImapSessionArray[iForegroundSession]); + } + } + +/** +Starts the background IDLE process. +If IDLE is not supported by the server, or if IDLE is disabled via the +account settings, a "dummy read" is issued, to manage any unsolicited +server messages. + +This is called when user-requested compound operations have completed +to return the primary session to IDLE state. Note that some operations +require that IDLE is not re-issued, eg "select". It is also called +when a background sync operation has completed, if there is no foreground +operation currently in progress + +Idle is only issued on the primary session. + +Idle is not started if there is a background operation in progress +*/ +void CImapProtocolController::StartIdle() + { + iCurrentOp = EIdle; + if (iBackgroundSyncOp==NULL) + { + iImapIdleController->StartIdle(); + } + } + +/** +Called by the CImapIdleController in the case of an error being observed +while in IDLE state. + +@param aError - the observed error +*/ +void CImapProtocolController::OnIdleError(TInt aError) + { + __LOG_FORMAT((KDefaultLog, "CImapProtocolController::OnIdleError(aError = %d)", aError)); + if (aError>0) + { + aError = KErrImapServerFail; + } + iProgress.iGenericProgress.iErrorCode = aError; + iProgress.iGenericProgress.iState=TImap4GenericProgress::EDisconnected; + DisconnectAll(); + // Nothing to complete. + } + +/** +Returns the progress information for the outstanding command. + +@return progress information +*/ +EXPORT_C TImap4CompoundProgress CImapProtocolController::Progress() + { + // Progress of foreground operation is returned over that of a + // background sync operation. + // Do not refresh the progress while migrating. + if (iCurrentOp!=EIdle && iMigrateState == ENotMigrating) + { + // do not update if IDLEing - return the stored progress object + // this should have been populated correctly on completion of + // requested operation, whether successful or otherwise. + switch (iCurrentOp) + { + case EConnect: + { + iImapSessionManager->Progress(iProgress.iGenericProgress); + break; + } + case EStopIdle: + case ESync: + case ESelect: + case ECopyToLocal: + case ECopyWithinService: + case ECopyFromLocal: + case EMoveToLocal: + case EMoveWithinService: + case EMoveFromLocal: + case EPopulate: + case EDelete: + case EDeleteFolder: + case ECreate: + case ERename: + case EDisconnect: + case EUpdateFlag: + { + // Obtain progress from compound object if it exists + // Otherwise the last progress obtained on completion + // of the compound object is returned. + if (iImapCompound!=NULL) + { + iImapCompound->Progress(iProgress); + } + break; + } + case EIdle: + default: + break; + } + } + else if (iBackgroundSyncOp!=NULL) + { + // update sync progress if background sync is being performed. + iBackgroundSyncOp->Progress(iProgress); + } + + return iProgress; + } + +/** +changes local subscription flag on a folder immediately. +If unsubscribing, the folder is marked as invisible, and the invisible +flag is propagated to any parent folders that are not themselves subscribed +or contain subscribed folders. + +@param aFolder +@param aSubscribed +@return Error code +*/ +EXPORT_C TInt CImapProtocolController::SetLocalSubscription(const TMsvId aFolder, + TBool aSubscribed) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::SetLocalSubscription()"); + + TInt err; + + // Move to the entry + if ((err=iEntry.SetEntry(aFolder))!=KErrNone) + return(err); + + // Check it's a folder + if (iEntry.Entry().iType!=KUidMsvFolderEntry) + return(KErrNotSupported); + + // update subscription flag + TMsvEmailEntry entry=iEntry.Entry(); + entry.SetLocalSubscription(aSubscribed); + entry.SetVisible(aSubscribed); + + err = iEntry.ChangeEntry(entry); + + // Return if error or if setting subscribed + if (err!=KErrNone || !aSubscribed) + { + TRAP(err, PropagateUnsubscribeL(aFolder)); + return err; + } + else + { + return KErrNone; + } + } + +/** +Marks aFolder as invisible and propagates the invisible flag to parent folder. + +@param aFolder the unsubscribed folder. +*/ +void CImapProtocolController::PropagateUnsubscribeL(const TMsvId aFolder) + { + // Settings may not yet be loaded. + if (iImapSettings->SettingsLoaded()==EFalse) + { + iImapSettings->LoadSettingsL(aFolder); + } + + // if synchronisation setting is not remote only then + // update the invisibility flags + if (iImapSettings->Synchronise() != EUseRemote) + { + PropagateInvisibleFlagL(aFolder); + ChangeVisibilityL(aFolder,ETrue,EFalse,KUidMsvMessageEntry); + } + } + + +// Mark service as on or offline +void CImapProtocolController::MarkOnOrOfflineL( const TBool aOnline ) + { + // Mark service entry as on/offline + User::LeaveIfError( iEntry.SetEntry( iServiceId ) ); + + TMsvEntry entry=iEntry.Entry(); + entry.SetConnected( aOnline ); + User::LeaveIfError( iEntry.ChangeEntry( entry ) ); + + // Release the service entry + User::LeaveIfError( iEntry.SetEntry( KMsvNullIndexEntryId ) ); + + // Going offline? + if ( !aOnline && iImapSettings->DisconnectedUserMode() ) + { + // We're an expert user going offline: don't touch anything + return; + } + + // Mark all immediate children of the service as invisible + if ( !aOnline ) + ChangeVisibilityL( iServiceId, !aOnline ); + } + +void CImapProtocolController::ChangeVisibilityL(TMsvId aParent, TBool aInvisible) + { + ChangeVisibilityL(aParent, aInvisible, ETrue, KUidMsvFolderEntry); + } + + +void CImapProtocolController::ChangeVisibilityL(TMsvId aParent, TBool aInvisible, TBool aRecurse, TUid aType) + { + // Get children at this level + CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection; + CleanupStack::PushL( selection ); + + CMsvEntrySelection* folders = new (ELeave) CMsvEntrySelection; + CleanupStack::PushL( folders ); + + User::LeaveIfError( iEntry.SetEntry( aParent ) ); + User::LeaveIfError( iEntry.GetChildren( *selection ) ); + + if ( selection->Count() ) + { + for( TInt child=0; child < selection->Count(); child++ ) + { + // Move to this child + User::LeaveIfError( iEntry.SetEntry( (*selection)[child] ) ); + + TMsvEntry message=iEntry.Entry(); + + // Is this the type we want to change? + if (message.iType==aType) + { + // Add to selection to do bulk change on, if necessary + if ((message.Visible() && aInvisible) || + (!message.Visible() && !aInvisible)) + { + folders->AppendL(message.Id()); + } + } + + // Recurse downwards + if (aRecurse && message.iType==KUidMsvFolderEntry) + ChangeVisibilityL(message.Id(),aInvisible,aRecurse,aType); + } + + // Change its visibility off all children if necessary + if (folders->Count()) + { + // Do the change to the invisible flag (actual constant for the + // flag we want is private:() + + User::LeaveIfError( iEntry.SetEntry( aParent ) ); + User::LeaveIfError( iEntry.ChangeAttributes(*folders, + aInvisible?0:KMsvVisibilityAttribute, + aInvisible?KMsvVisibilityAttribute:0)); + } + } + + // Release the service entry + User::LeaveIfError( iEntry.SetEntry( KMsvNullIndexEntryId ) ); + + // Get rid of selection + CleanupStack::PopAndDestroy(2); + } + +/** +Propagates invisible flag for unsubscribed folders. +aId has been unsubscribed. If it has no visible child folders then +it is made invisible and its parent checked with the same test + +@param aId the folder that has been unsubscribed +*/ +void CImapProtocolController::PropagateInvisibleFlagL(TMsvId aId) + { + __LOG_FORMAT((KDefaultLog, "PropagateInvisibleFlagL: 0x%x", aId)); + + // finish if we've reached the top + if (aId == KMsvRootIndexEntryId) + { + return; + } + + User::LeaveIfError(iEntry.SetEntry(aId)); + + // finish if we've reached a service + if (iEntry.Entry().iType == KUidMsvServiceEntry) + { + return; + } + + // return if we've found a subscribed folder since we can't make + // it invisible + if (((TMsvEmailEntry)iEntry.Entry()).LocalSubscription()) + { + return; + } + + // check the children of this unsubscribed folder + CMsvEntrySelection* selection=new (ELeave) CMsvEntrySelection; + CleanupStack::PushL(selection); + + User::LeaveIfError(iEntry.GetChildren(*selection)); + + TBool visible=EFalse; + TInt count = selection->Count(); + for (TInt i=0; i < count; ++i) + { + User::LeaveIfError(iEntry.SetEntry((*selection)[i])); + + // look for a visible folder + TMsvEmailEntry entry = (TMsvEmailEntry)iEntry.Entry(); + if (entry.iType == KUidMsvFolderEntry && entry.Visible()) + { + visible=ETrue; + break; + } + } + + CleanupStack::PopAndDestroy(selection); + + // if no child folders were visible then make this folder + // invisible and continue up + if (!visible) + { + User::LeaveIfError(iEntry.SetEntry(aId)); + + // make this invisible + TMsvEntry entry = iEntry.Entry(); + entry.SetVisible(EFalse); + User::LeaveIfError(iEntry.ChangeEntry(entry)); + + // go up + PropagateInvisibleFlagL(entry.Parent()); + } + } + + +/** +Is this id in the local service? +@param aId the id of the entry to check +@return true, if the message id belongs to the local service. +*/ +TBool CImapProtocolController::IdIsLocalL(TMsvId aId) + { + return ServiceOfL(aId) == KMsvLocalServiceIndexEntryIdValue; + } + +/** +Returns the id of the service containing this id +@param aId the id of the entry to check +@return the id of the containing service +*/ +TMsvId CImapProtocolController::ServiceOfL(TMsvId aId) + { + TMsvId current=aId; + while(current!=KMsvRootIndexEntryIdValue) + { + // Visit this entry + User::LeaveIfError(iEntry.SetEntry(current)); + + TMsvEmailEntry entry = iEntry.Entry(); + + // if service then searched far enough + if (entry.iType==KUidMsvServiceEntry) + break; + + // Go upwards + current=entry.Parent(); + } + + return current; + } + +/** +Processes positive error codes returned by asynchronous service requests. +Specifically, these will be error codes returned by the CImapSession, +indicating error conditions in communication with the remote server. + +See cimapsessionconsts.h for full definition of error codes, summarised here: + +KErrImapNo IMAP server returned a tagged NO response +KErrImapBad IMAP server returned a tagged BAD response +KErrImapClosed indicates one of the streams has closed +KErrImapFlushTimeout an attempt to flush a cancelled command has timed out +KErrImapCorrupt corrupt data was enountered during parsing of IMAP server data + +@param aError - the positive error code received on completion + of an asynchronous service + +@return ETrue - if this function has called Complete() due to a fatal error. + EFalse - otherwise +*/ +TBool CImapProtocolController::ProcessError(TInt aError) + { + __LOG_FORMAT((KDefaultLog, "CImapProtocolController::ProcessError(iCurrentOp = %d, aError = %d)", iCurrentOp, aError)); + + // Bearer mobility support for initially connect failure + if (iMobilityManager && aError == KErrImapConnectError && iImapSessionManager->HasConnection()) + { + // Change migration state, so the connect error can be dealt with, + // using the migration state machine. + iMigrateState = EHandlingConnectError; + + SetActive(); + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + + return ETrue; + } + + + // weed out non-fatal server errors + ThranslateSessionError(aError); + if (aError == KErrNone) + { + return EFalse; + } + + if (iForegroundSession>0) + { + // error has occurred on a secondary session. + // This is not fatal to the whole protocol controller's + // existance, but it is fatal to the requested operation + + // get final progress state... + if (iCurrentOp==EConnectSecondary) + { + iImapSessionManager->Progress(iProgress.iGenericProgress); + } + else + { + iImapCompound->Progress(iProgress); + } + + //... and update reported error code + iProgress.iGenericProgress.iErrorCode=aError; + + // delete the compound operation object + delete iImapCompound; + iImapCompound = NULL; + + // drop the secondary session + DropSession(iForegroundSession); + + // complete the user request with KErrNone.. + Complete(KErrNone); + } + else + { + // DoComplete normally handles the session tidyup, but + // is not called if the error has occurred during a background + // operation, for example ECancelRecoverPrimary + if (iCurrentOp==ECancelRecoverPrimary) + { + // Non-fatal errors should have been handled prior to this. + DisconnectAll(); + + // Save error code in progress and flag the disconnect + // this is defensive as the client should not be watching progress + iProgress.iGenericProgress.iState=TImap4GenericProgress::EDisconnected; + iProgress.iGenericProgress.iErrorCode=aError; + } + Complete(aError); + } + return ETrue; + } + +/** +Translates session error codes for reporting to the Server MTM. +KErrImapNo and KErrImapBad are non-fatal to the session, and +should have been fielded by the compound operation, sync manager +or session manager (during session connect), however they are +translated to KErrNone here. + +All other session errors are fatal to the connection. If an +error occurs on the primary session, all connected sessions +should be disconnected and the client completed. +*/ +void CImapProtocolController::ThranslateSessionError(TInt& errCode) + { + if ( errCode == KErrNone + || errCode == KErrImapNo + || errCode == KErrImapBad ) + { + // No and bad should have been handled by the compound + // operation and are non-fatal to the connection. + errCode = KErrNone; + } + else + { + errCode = KErrImapServerFail; + } + } + +/** +Resets the progress object +*/ +void CImapProtocolController::ResetProgress() + { + //initialise the TImap4SyncProgress part + iProgress.iSyncProgress.iFoldersToDo = 0; + iProgress.iSyncProgress.iFoldersDone = 0; + iProgress.iSyncProgress.iMsgsToDo = 0; + iProgress.iSyncProgress.iMsgsDone = 0; + iProgress.iSyncProgress.iHeadersFetched = 0; + iProgress.iSyncProgress.iOrphanedFolders = 0; + iProgress.iSyncProgress.iNewFolders = 0; + iProgress.iSyncProgress.iOrphanedMessages = 0; + iProgress.iSyncProgress.iRemoteMessagesDeleteTagged = 0; + iProgress.iSyncProgress.iMessagesFetchedOK = 0; + iProgress.iSyncProgress.iMessagePartsFetchedOK = 0; + iProgress.iSyncProgress.iMessagePartsNotFound = 0; + iProgress.iSyncProgress.iFoldersNotFound = 0; + iProgress.iSyncProgress.iErrorCode = KErrNone; + iProgress.iSyncProgress.iType = EImap4SyncProgressType; + + //initialise the TImap4GenericProgress part + iProgress.iGenericProgress.iMsgsToDo = 0; + iProgress.iGenericProgress.iMsgsDone = 0; + iProgress.iGenericProgress.iPartsToDo = 0; + iProgress.iGenericProgress.iPartsDone = 0; + iProgress.iGenericProgress.iBytesToDo = 0; + iProgress.iGenericProgress.iBytesDone = 0; + iProgress.iGenericProgress.iReturnedMsvId = 0; + iProgress.iGenericProgress.iTotalSize = 0; + iProgress.iGenericProgress.iErrorCode = KErrNone; + iProgress.iGenericProgress.iType = EImap4GenericProgressType; + + } + +/** +Notice that a preferred carrier has become available, and migration to that bearer has been accepted. +The protocol controller shall either pause or allow any current operation to complete +according to the action specified in parameter aAction. Once the current operation +is paused or complete, the protocol controller shall close any existing sockets +and finally notify the mobility framework that it is ready to migrate to the new +carrier. + +@param aAction - indicates the action that should be taken re: current operations +@param aIsSeamless - indicates if the +*/ +void CImapProtocolController::PrepareForNewCarrier(TImMobilityAction aAction, TBool /*aIsSeamless*/) + { + __LOG_FORMAT((KDefaultLog, "CImapProtocolController::PrepareForNewCarrier(iMigrateState=%d, aAction = %d)", iMigrateState, aAction)); + // Handle being called in an already-migrating state before handling usual case + if (iMigrateState!=ENotMigrating) + { + switch (iMigrateState) + { + case EDisconnectingForMigrate: + case EWaitingForOpToStop: + { + // Do not allow change from EWaitingForOpToStop to EWaitingForOpToComplete + // but do allow an operation to be stopped suddenly if necessary. + if (aAction==KAcceptImmediately) + { + CarrierLost(); + iMigrateState = EWaitingForNewCarrier; + iMobilityManager->MigrateToNewCarrier(); + + // do not set waiting if NewCarrierActive has been called synchronously. + if (iMigrateState == EWaitingForNewCarrier) + { + // Now in a waiting state, set self active + iStatus = KRequestPending; + SetActive(); + } + } + // else already preparing for new carrier, nothing to do. + return; + } + + case EWaitingForOpToComplete: // async wait on compound op/bground op/idle controller + { + // Allow change from EWaitingForOpToComplete to EWaitingForOpToStop + // also allow an operation to be stopped suddenly if necessary. + if (aAction==KAcceptImmediately) + { + CarrierLost(); + iMigrateState = EWaitingForNewCarrier; + iMobilityManager->MigrateToNewCarrier(); + + // do not set waiting if NewCarrierActive has been called synchronously. + if (iMigrateState == EWaitingForNewCarrier) + { + // Now in a waiting state, set self active + iStatus = KRequestPending; + SetActive(); + } + } + else if (aAction==KAcceptStopCurrent) + { + // change from EWaitingForOpToComplete to EWaitingForOpToStop + StopCurrentForMigrate(); + } + // else already preparing for new carrier, nothing to do. + return; + } + case EHandlingConnectError: + { + // We have a Connect Error, from previous connect attempt + // We are ready to (migrate) reconnect + iMigrateState = EWaitingForNewCarrier; + iMobilityManager->MigrateToNewCarrier(); + + // do not set waiting if NewCarrierActive has been called synchronously. + if (iMigrateState == EWaitingForNewCarrier) + { + // Now in a waiting state, set self active + iStatus = KRequestPending; + SetActive(); + } + break; + } + case EConnectingAfterMigrate: // async wait on session manager + { + // cancelling in this state tidies up the session array, so + // nothing extra to do here - now ready for migration. + Cancel(); + iMigrateState = EWaitingForNewCarrier; + iMobilityManager->MigrateToNewCarrier(); + + // do not set waiting if NewCarrierActive has been called synchronously. + if (iMigrateState == EWaitingForNewCarrier) + { + // Now in a waiting state, set self active + iStatus = KRequestPending; + SetActive(); + } + return; + } + + case EWaitingInitialCarrierRejected: // async wait state on mobility engine + case EWaitingForNewCarrier: // async wait state on mobility engine + case EStartingReconnect: // intermediate state + { + // already ready and waiting for a new carrier - Cancel dummy request + // and tell the Mobility framework we are ready + Cancel(); + iMigrateState = EWaitingForNewCarrier; + iMobilityManager->MigrateToNewCarrier(); + + // do not set waiting if NewCarrierActive has been called synchronously. + if (iMigrateState == EWaitingForNewCarrier) + { + // Now in a waiting state, set self active + iStatus = KRequestPending; + SetActive(); + } + return; + } + case ESuspendingForMigrate: // intermediate state + case ENotMigrating: // already checked this isn't true + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EProConPreForNewCarBadMigrateState)); + } + } + } + + // Do the requested action... + switch (aAction) + { + case KAcceptCompleteCurrent: + { + // Any current operation shall be allowed to complete prior + // to indicating that the server MTM is ready to migrate to + // the new bearer. + CompleteCurrentForMigrate(); + break; + } + case KAcceptStopCurrent: + { + // Any current operation shall be allowed to continue until + // such a point that it may be resumed without re-sending or + // re-receiveing a significant amount to data prior to + // indicating that the server MTM is ready to migrate to the + // new bearer. + StopCurrentForMigrate(); + break; + } + case KAcceptImmediately: + default: + { + // accept immediately is an instruction to immediately suspend any + // current operation (cancel any outstanding server communication) + // and close any open sockets. This is the same behaviour as required + // for a downgrade situation. + CarrierLost(); + iMigrateState = EWaitingForNewCarrier; + iMobilityManager->MigrateToNewCarrier(); + + // do not set waiting if NewCarrierActive has been called synchronously. + if (iMigrateState == EWaitingForNewCarrier) + { + // Now in a waiting state, set self active + iStatus = KRequestPending; + SetActive(); + } + break; + } + } // end switch (aAction) + } + +/** +Configures the protocol controller (PC) to allow any current operation in progress +to complete prior to indicating to the mobility framework that it is ready to +migrate to the new carrier. + +Note the protocol controller may be in any of the following states + - IDLE (only one session exists) + - background sync in progress only (only one session exists) + - foreground op in progress only (only one session exists) + - background sync and foreground operation in progress (2 sessions exist) + - background sync in progress and connecting second session for foreground op + +*/ +void CImapProtocolController::CompleteCurrentForMigrate() + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::CompleteCurrentForMigrate()"); + // set the migrating flag + iMigrateState = EWaitingForOpToComplete; + + if (BackgroundSyncInProgress()) + { + // 2-phase background sync is always delayed till after migration + iBackgroundSyncOp->StopForMigrate(); + } + + // If no operation in progress, stop the IDLE + if (iCurrentOp == EIdle && iImapIdleController->IsActive()) + { + iCurrentOp = EStopIdle; + iImapIdleController->StopIdle(iStatus); + SetActive(); + return; + } + + // cancel a secondary session connect + if (iCurrentOp == EConnectSecondary) + { + CancelForMigrate(); + iMigrateState = EWaitingForOpToComplete; + + // defensive: it is possible that the background sync has completed + // while the secondary connect is still in progress. In this case + // neither DoRunL() nor BackgroundSyncComplete() will not be called. + if (!BackgroundSyncInProgress()) + { + // fake a stop-idle op + iCurrentOp = EStopIdle; + SetActive(); + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + } + } + } + +/** +CancelForMigrate() - cancels any outstanding service without deleting the +compound operation or completing the user request. + +This does not cancel a background sync. + +iMigrateState must be set to a new state immediately after calling this. +*/ +void CImapProtocolController::CancelForMigrate() + { + iMigrateState = ESuspendingForMigrate; + Cancel(); + } + +/** +*/ +void CImapProtocolController::DoCancelForMigrate() + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelForMigrate()"); + switch (iCurrentOp) + { + case EConnectSecondary: + { + // Cancel the connect request + iImapSessionManager->Cancel(); + // Remove the session pointer from the array + // The session has already been deleted - see note above. + TInt numSessions = iImapSessionArray.Count(); + iImapSessionArray.Remove(numSessions-1); + break; + } + + case EIdle: + case EStopIdle: + { + // Cancel the IDLE controller. + iImapIdleController->Cancel(); + break; + } + + case ESync: + case ESelect: + case ECopyToLocal: + case ECopyWithinService: + case ECopyFromLocal: + case EMoveToLocal: + case EMoveWithinService: + case EMoveFromLocal: + case EPopulate: + case EDelete: + case EDeleteFolder: + case ECreate: + case ERename: + { + // Cancel the compound operation + iImapCompound->CancelForMigrate(); + break; + } + + case ECancelRecoverPrimary: + { + // Cancel the session recovery. + (iImapSessionArray[0])->Cancel(); + } + + case EConnect: +// case EConnectAndSync: + case EDisconnect: + default: + { + // nothing to do.. + break; + } + + } // end of switch (iCurrentOp) + + // Do NOT call CMsgActive::DoCancel() + // - the client requested operation is not being cancelled + } + + +/** +Configures the protocol controller (PC) to stop any current operation in +progress at the next convenient point - the meaning of this depends on the +specific compound operations. For example, for simple operations such as +creating a folder, this will be when the operation has completed but for +operations for which multiple messages are processed in sequence (eg +fetching several messages), it shall be once the current message has been +processed (eg fetched). At this point the operation shall complete the +protocol controller's iStatus + +Note the protocol controller may be in any of the following states + - IDLE (only one session exists) + - background sync in progress only (only one session exists) + - foreground op in progress only (only one session exists) + - background sync and foreground operation in progress (2 sessions exist) + - background sync in progress and connecting second session for foreground op + +*/ +void CImapProtocolController::StopCurrentForMigrate() + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::StopCurrentForMigrate()"); + // set the migrating flag + iMigrateState = EWaitingForOpToStop; + + if (BackgroundSyncInProgress()) + { + iBackgroundSyncOp->StopForMigrate(); + } + + if (iCurrentOp == EIdle && iImapIdleController->IsActive()) + { + // If no operation in progress, stop the IDLE + iCurrentOp = EStopIdle; + iImapIdleController->StopIdle(iStatus); + SetActive(); + return; + } + else if (iCurrentOp == EConnectSecondary) + { + // cancel a secondary session connect + Cancel(); + + // defensive: it is possible that the background sync has completed + // while the secondary connect is still in progress. In this case + // neither DoRunL() nor BackgroundSyncComplete() will not be called. + if (!BackgroundSyncInProgress()) + { + // fake a stop-idle op + iCurrentOp = EStopIdle; + SetActive(); + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + } + } + else if (iCurrentOp != ECancelRecoverPrimary) + { + // nothing to do for ECancelRecoverPrimary - just wait for the + // session to flush. + // all other operations are told to stop: + iImapCompound->StopForMigrate(); + } + } + +/** +This API is called by the Bearer Mobility Manager. It is typically called in the +case that a downgrade is occuring, and the original sockets are no longer valid +for use. It may also be called if an immediate migration to an preferred bearer is +required, without graceful closing of the original sockets. + +Any operations are cancelled immediately using the CancelForMigrate() API. This +cancels the operations in such a way that they may be restarted following migration +to a new carrier. +*/ +void CImapProtocolController::CarrierLost() + { + __LOG_FORMAT((KDefaultLog, "CImapProtocolController::CarrierLost(iMigrateState=%d)", iMigrateState)); + + // Handle being called in an already-migrating state before handling usual case + if (iMigrateState!=ENotMigrating) + { + switch (iMigrateState) + { + case EWaitingForOpToStop: + case EWaitingForOpToComplete: + { + // break to default behaviour + break; + } + + case EStartingReconnect: // intermediate state + case EDisconnectingForMigrate: + case EConnectingAfterMigrate: + { + // cancel the disconnect/connect, then break to default behaviour + // (calling CancelForMigrate() shouldn't do anything as any compound + // operations will not themselves be active) + Cancel(); + break; + } + + case EHandlingConnectError: + { + // empty the session array + TInt numSessions = iImapSessionArray.Count(); + for (TInt i=0; iDisconnect(*(iImapSessionArray[i])); + } + } + iImapSessionArray.ResetAndDestroy(); + + // Set the migration state: + iMigrateState = EWaitingForNewCarrier; + + // Now in an waiting state, set self active + iStatus = KRequestPending; + SetActive(); + return; + } + + case EWaitingInitialCarrierRejected: + case EWaitingForNewCarrier: + { + // already ready and waiting for a new carrier - nothing to do. + return; + } + + case ESuspendingForMigrate: // intermediate state + case ENotMigrating: // normal state + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EProConKillCurrentBadMigrateState)); + break; + } + } + } + + + // stop any background operation + if (iBackgroundSyncOp) + { + iBackgroundSyncOp->CancelForMigrate(); + } + + // Cancel foreground operations + CancelForMigrate(); + + // delete the imap idle controller - this will be re-created on reconnect. + delete iImapIdleController; + iImapIdleController=NULL; + + // Drop connection on each of the imap sessions + TInt numSessions = iImapSessionArray.Count(); + for (TInt i=0; iDisconnect(*(iImapSessionArray[i])); + } + } + + // empty the session array + iImapSessionArray.ResetAndDestroy(); + + // Set the migration state: + iMigrateState = EWaitingForNewCarrier; + + // Now in an waiting state, set self active + iStatus = KRequestPending; + SetActive(); + } + +/** +Called to indicate the migration has completed and the RConnection is ready to +provide new sockets via the new bearer. + +Initiates the creation of a new IMAP session, following which any suspended +operation shall be restarted. +*/ +void CImapProtocolController::NewCarrierActive(TAccessPointInfo /*aNewAp*/, TBool /*aIsSeamless*/) + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::NewCarrierActive()"); + __ASSERT_DEBUG((iMigrateState == EWaitingForNewCarrier || iMigrateState == EWaitingInitialCarrierRejected), + TImapServerPanic::ImapPanic(TImapServerPanic::ENewCarrierActiveUnexpectedMigrateState)); + + // Cancel the dummy active state + Cancel(); + + // set the new migration state + iMigrateState = EStartingReconnect; + + // complete self - this requires some memory allocation, do it within the RunL. + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + SetActive(); + } + + +void CImapProtocolController::NewPrimarySessionL() + { + // Create the primary session pointer + CImapSession* imapSession = NULL; + iImapSessionArray.AppendL(imapSession); + + // Request the session manager to connect the session + iImapSessionManager->GetSessionL(iStatus, iImapSessionArray[0]); + iMigrateState = EConnectingAfterMigrate; + + SetActive(); + } + +/** +Indicates that the RConnection is no longer valid. +Therefore disconnect and mark the service as offline. +*/ +#ifdef __IMAP_LOGGING +void CImapProtocolController::MobilityError(TUint aError) +#else //__IMAP_LOGGING +void CImapProtocolController::MobilityError(TUint /*aError*/) +#endif //__IMAP_LOGGING + { + __LOG_FORMAT((KDefaultLog, "CImapProtocolController::MobilityError(iMigrateState=%d, aError=%d)", iMigrateState, aError)); + + // Cancel any async request + Cancel(); + + if (iRequestedOp != EIdle) + { + // Complete the user request - calls DisconnectAll() + Complete(KErrDisconnected); + } + else + { + // Disconnect all sessions and mark the service as offline + DisconnectAll(); + } + + // the RConnection is no longer valid - delete the session manager + // (will be recreated on ConnectL()) + delete iImapSessionManager; + iImapSessionManager = NULL; + + // no longer migrating... + iMigrateState = ENotMigrating; + + // Save error code in progress and flag the disconnect + // this is defensive as the client should not be watching progress + iProgress.iGenericProgress.iState=TImap4GenericProgress::EDisconnected; + iProgress.iGenericProgress.iErrorCode=KErrDisconnected; + } + +void CImapProtocolController::ProcessOpCompleteForMigrate() + { + // Any tidying up to do for the current user-requested operation + switch (iCurrentOp) + { + case ECancelRecoverPrimary: + case EIdle: + case EStopIdle: + { + // engine was IDLE or recovering from a cancel when the + // migrate notice arrived... nothing to do before disconnecting. + break; + } + + case ESync: + case ESelect: + case ECopyToLocal: + case ECopyWithinService: + case ECopyFromLocal: + case EMoveToLocal: + case EMoveWithinService: + case EMoveFromLocal: + case EPopulate: + case EDelete: + case EDeleteFolder: + case ECreate: + case ERename: + { + // update last operation progress state + iImapCompound->Progress(iProgress); + + // if the user requested operation has finished, + // tidyup and inform the client + if (iProgress.iGenericProgress.iState == TImap4GenericProgress::EIdle) + { + delete iImapCompound; + iImapCompound = NULL; + + // Complete the server mtm request. + Complete(iStatus.Int()); + } + break; + } + + case EConnect: + case EDisconnect: + case EConnectSecondary: + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EProcessOpCompleteForMigrateUnexpectedState)); + } + } // end of switch (iCurrentOp) + } + +/** +Logs out and disconnects all connected sessions with the remote server. +*/ +void CImapProtocolController::DisconnectForMigrateL() + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectForMigrateL()"); + __ASSERT_DEBUG(iMigrateCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EMigrateCompoundIsNotNull)); + + // Background operations will have been taken care of already - just disconnect. + // Do not perform the late-delete offline operations. + delete iMigrateCompound; + iMigrateCompound = NULL; + + iMigrateCompound = CImapCompoundDisconnect::NewL( *iImapSyncManager, + iEntry, + *iImapSettings, + *iImapSessionManager, + *iImapMailStore, + iImapSessionArray, + iImapOfflineControl, + EFalse ); + + iMigrateState = EDisconnectingForMigrate; + iMigrateCompound->StartOperation(iStatus, *iImapSessionArray[0]); + SetActive(); + } + + +/** +Resumes operations that were stopped to allow migration to occur, +or operations that were requested while migration was in progress. +*/ +void CImapProtocolController::RestartAfterMigrateL() + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::RestartAfterMigrateL()"); + + // The migration has completed. Restart anything that was going on + // before the migration started, or has been requested in the meantime. + iMigrateState = ENotMigrating; + + // was the initial carrier rejected during the connect operation? + // if so, mark as online and get the final connection progress info. + if (iCurrentOp == EConnect) + { + TRAP_IGNORE(MarkOnOrOfflineL(ETrue)); + + // Collect the final connect progress information + iImapSessionManager->Progress(iProgress.iGenericProgress); + + // Set last socket activity timeout to iMtmData1. This is used by Imcm. + User::LeaveIfError( iEntry.SetEntry( iServiceId ) ); + TMsvEntry entry=iEntry.Entry(); + entry.SetMtmData1(iImapSessionManager->LastSocketActivityTimeout()); + User::LeaveIfError( iEntry.ChangeEntry( entry ) ); + + // complete the connect request + Complete(iStatus.Int()); + } + + // Create a new IDLE controller - the old one was deleted. + delete iImapIdleController; + iImapIdleController=NULL; + iImapIdleController = CImapIdleController::NewL(*this, iImapSessionArray[0], *iImapSyncManager, iEntry, *iImapSettings, *iImapMailStore); + + // was there a background sync in progress prior to migration? + if (iBackgroundSyncOp) + { + // restart any background operation, using the new primary session. + iBackgroundSyncOp->ResumeOperationL(*iImapSessionArray[0]); + + // was there a forground operation paused as well? + if (iImapCompound) + { + // Create a second connected session. + iCurrentOp = EConnectSecondary; + // iRequestedOp should not have been changed... + CImapSession* imapSession = NULL; + iImapSessionArray.AppendL(imapSession); + TInt sessionCount = iImapSessionArray.Count(); + iImapSessionManager->GetSessionL(iStatus, iImapSessionArray[sessionCount-1]); + SetActive(); + } + else + { + iCurrentOp = EIdle; + } + return; + } + + if (iImapCompound) + { + // Idle has not been restarted after the migration, so the requested + // operation can be re-started without calling StopIdle() + // resume the suspended operation + iCurrentOp = iRequestedOp; + iImapCompound->ResumeOperationL(iStatus, *iImapSessionArray[0]); + SetActive(); + } + else + { + // otherwise just start Idle + iCurrentOp = EIdle; + iRequestedOp = EIdle; + StartIdle(); + } + } + +/** +Returns a packaged copy of the current progress for passing to the +mobility policy plugin when a preferred carrier available notice +is received. +*/ +const TDesC8& CImapProtocolController::MobilityProgress() + { + Progress(); + iProgressBuffer = TImap4ProgressBuf(iProgress); + return iProgressBuffer; + } +/** +Updates the specified messages read/unread status from the remote server. +@param aStatus +*/ +EXPORT_C void CImapProtocolController::UpdateFlagL( TRequestStatus& aStatus) + + { + __LOG_TEXT(KDefaultLog, "CImapProtocolController::UpdateFlagL()"); + __ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECreateCompoundIsNotNull)); + ResetProgress(); + if (!CompleteIfBackgroundOpInProgress(aStatus)) + { + iImapCompound = CImapUpdateFlagOperation::NewL(*iImapSyncManager, + iEntry, + *iImapSettings + ); + iRequestedOp = EUpdateFlag; + StartPrimaryOperation(); + Queue(aStatus); + SetActive(); + } + } + +