diff -r 6a20128ce557 -r ebfee66fde93 email/imap4mtm/imapprotocolcontroller/src/cimapcompoundcopytolocal.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/imap4mtm/imapprotocolcontroller/src/cimapcompoundcopytolocal.cpp Fri Jun 04 10:25:39 2010 +0100 @@ -0,0 +1,804 @@ +// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include "cimapcompoundcopytolocal.h" +#include "cimapopfetchbody.h" +#include "cimapsessionconsts.h" +#include "cimapsession.h" +#include "cimapsyncmanager.h" +#include "cimapfolder.h" +#include "cimaplogger.h" +#include "imappaniccodes.h" +#include "cimapfolderinfo.h" +#include + +#include "mobilitytestmtmapi.h" + +_LIT8(KMessageDataItem, "+FLAGS"); +_LIT8(KDeleteValue, "\\Deleted"); + +CImapCompoundCopyToLocal::CImapCompoundCopyToLocal(CImapSyncManager& aSyncManager, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, TBool aIsMove, const TMsvId aDestination) + : CImapCompoundBase(aSyncManager, aServerEntry, aImapSettings), + iMailStore(aImapMailStore), iIsMove(aIsMove), iDestinationFolderId(aDestination) + { + iPopulateCommand = EFalse; + UpdatePartialMailInfoToDefaults(aDestination); + } + +CImapCompoundCopyToLocal::CImapCompoundCopyToLocal(CImapSyncManager& aSyncManager, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, TBool aIsMove, const TMsvId aDestination, const TImImap4GetPartialMailInfo& aGetPartialMailInfo) + : CImapCompoundBase(aSyncManager, aServerEntry, aImapSettings), + iMailStore(aImapMailStore), iIsMove(aIsMove), iDestinationFolderId(aDestination), iGetPartialMailInfo(aGetPartialMailInfo) + { + iPopulateCommand = ETrue; + } + +CImapCompoundCopyToLocal::~CImapCompoundCopyToLocal() + { + iOutMessageFlagInfo.Close(); + delete iSourceSel; + delete iBodyFetcher; + delete iMoveEntry; + } + +CImapCompoundCopyToLocal* CImapCompoundCopyToLocal::NewL(CImapSyncManager& aSyncManager, + CMsvServerEntry& aServerEntry, + CImapSettings& aImapSettings, + CImapMailStore& aImapMailStore, + TBool aIsMove, + const CMsvEntrySelection& aSourceSel, + const TMsvId aDestination) + { + CImapCompoundCopyToLocal* self = new (ELeave) CImapCompoundCopyToLocal(aSyncManager, aServerEntry, aImapSettings, aImapMailStore, aIsMove, aDestination); + CleanupStack::PushL(self); + self->ConstructL(aSourceSel); + CleanupStack::Pop(self); + return self; + } + +CImapCompoundCopyToLocal* CImapCompoundCopyToLocal::NewL(CImapSyncManager& aSyncManager, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, TBool aIsMove, const CMsvEntrySelection& aSourceSel, const TMsvId aDestination, const TImImap4GetPartialMailInfo& aGetPartialMailInfo) + { + CImapCompoundCopyToLocal* self = new (ELeave) CImapCompoundCopyToLocal(aSyncManager, aServerEntry, aImapSettings, aImapMailStore, aIsMove, aDestination, aGetPartialMailInfo); + CleanupStack::PushL(self); + self->ConstructL(aSourceSel); + CleanupStack::Pop(self); + return self; + } + +/** +Secondary construction +*/ +void CImapCompoundCopyToLocal::ConstructL(const CMsvEntrySelection& aSourceSel) + { + // Local copy of the selection of messages to copy + iSourceSel=new (ELeave) CMsvEntrySelection; + + // Check the source selection for acceptable message types/parts + // Messages True + // Handle Parts True + // Handle Folders False + // Check source True + // Makes a local copy of the source selection in iSourceSel + User::LeaveIfError(CheckSelectionL(aSourceSel, iSourceSel, ETrue, ETrue, EFalse, ETrue)); + + // Create an Imap Body Fetcher object + iBodyFetcher = CImapOpFetchBody::NewL(iSession, iSyncManager, iServerEntry, iImapSettings, iMailStore); + // initialise progress stats + iMessageSelection = iSelectionStillToCopy = iSourceSel->Count(); + iTotalSize = CalculateDownloadSizeL(*iSourceSel); + + // Add to the active scheduler + CActiveScheduler::Add(this); + } + +void CImapCompoundCopyToLocal::StartOperation(TRequestStatus& aStatus, CImapSession& aSession) + { + iSession = &aSession; + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::StartOperation()"); + iNextStep = ESetSourceMailbox; + Queue(aStatus); + CompleteSelf(); + } + +/** +Handles the compound operation state machine + +Sequence of steps: + For each message: + Select source mailbox + Fetch the message to the local mirror + IF copy/move to local service + Copy message to local service + IF move to local service + STORE /deleted flag on remote message + issue EXPUNGE command + delete local copy of the message + + +@return ETrue if compound operation is completed, + EFalse otherwise. +*/ +TBool CImapCompoundCopyToLocal::DoRunLoopL() + { + SetCurrentStep(); + switch (iCurrentStep) + { + case ESetSourceMailbox: // synchronous + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(ESetSourceMailbox)"); + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal1); // SELECT source folder + + if (iSelectionStillToCopy > 0) + { + if (iStopForMigrate) + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::Stopped for migrate"); + iCurrentStep = ESuspendedForMigrate; + iNextStep = ESetSourceMailbox; + Complete(KErrNone); + return ETrue; + } + + // decrement iSelectionStillToCopy - it becomes the index in the list + // of the current message to operate on. + --iSelectionStillToCopy; + + iCurrentMsgId = (*iSourceSel)[iSelectionStillToCopy]; + if (SetSourceFolderL(iCurrentMsgId)) + { + // no change in state if folder not acceptable, + // try again with the next message in the selection. + if (iSourceFolderId != NULL && iSourceFolder != NULL) + { + // When iIsMove is EFalse, we still need to have a writable mailbox + // so that the server can update the \Seen message flag duriung FETCH BODY + + iNextStep=ESelectSourceMailboxRW; + } + } + else + { + iNextStep=EFetchMessage; + } + } + else + { + // All messages copied.. + iNextStep=EFinished; + } + // immediately proceed to the next step + return EFalse; + } + + case ESelectSourceMailboxRW: // asynchronous + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(ESelectSourceMailboxRW)"); + iSourceFolder->SelectL(iStatus, *iSession); + iNextStep=EFetchMessage; + SetActive(); + break; + } + + case ESelectSourceMailboxRO: // asynchronous + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(ESelectSourceMailboxRO)"); + iSourceFolder->ExamineL(iStatus, *iSession); + iNextStep=EFetchMessage; + SetActive(); + break; + } + + case EFetchMessage: // asynchronous + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(EFetchMessage)"); + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal2); // FETCHing the message + SetEntryL(iCurrentMsgId); + TMsvEmailEntry entry = static_cast (iServerEntry.Entry()); + + TBool doFetch(ETrue); + + // Don't fetch the message if it is marked for delete + if (entry.DisconnectedOperation() == EDisconnectedDeleteOperation) + { + doFetch = EFalse; + iNextStep = ESetSourceMailbox; + return EFalse; + } + else + { + // Don't attempt to fetch the message if it has either been fully or + // partially downloaded, unless this is a populate command. + if (entry.Complete() && entry.PartialDownloaded() && !iPopulateCommand) + { + doFetch = EFalse; + } + } + + if (doFetch) + { + // Fetch the message... + iBodyFetcher->FetchBodyL(iStatus, iCurrentMsgId, iGetPartialMailInfo); + SetActive(); + } + + // Set next state + if (iDestinationFolderId == KMsvNullIndexEntryId) + { + // Populating/Copying to local mirror. When fetched, proceed to next + // message in the selection + iNextStep=ESetSourceMailbox; + } + else + { + iNextStep=(iIsMove)?EInboxDuplicateMove:EInboxDuplicateCopy; + } + break; + } + + case EInboxDuplicateCopy: // asynchronous + case EInboxDuplicateMove: // asynchronous + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(EInboxDuplicate)"); + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal3); // local async copy + SetEntryL(iCurrentMsgId); + iNextStep=ELocalCopyComplete; + + // CopyMessage() kicks off an asynchronous copy operation if it succeeds + TInt ret = CopyMessage(iCurrentMsgId, iDestinationFolderId, iIsMove); + + if (ret != KErrNone) // Asynchronous copy operation failed to start + { + // Store the error in the message entry + TRAPD(err, MessageErrorL(iCurrentMsgId, ret)); + if (err != KErrNone) + { + iProgressErrorCode = err; + } + + // Move straight on to next step + iNextStep = ESetSourceMailbox; + + return EFalse; + } + break; + } + + case ELocalCopyComplete: // synchronous + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(ELocalCopyComplete)"); + // The copy to local folder has completed. + CopyCompleteL(); + // Delete the source message from the IMAP Service if it is a move operation, + // otherwise proceed to next message in the selection. + iNextStep=(iIsMove)?EDeleteMessage:ESetSourceMailbox; + // straight on to next step... + return EFalse; + } + + case EDeleteMessage: // asynchronous + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(EDeleteMessage)"); + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal4); // STORE /deleted flag (move only) + // Deleting a message is a two-stage operation: + // 1. STORE the /deleted flag on the remote server. + // 2.a) EXPUNGE the messages + // or + // 2.b) CLOSE the folder and then SELECT the folder + // 2a) or 2b) used is dependent on the KImap4EmailExpungeFlag in CImImap4Settings. + // we haven't changed the TMsvId of moved messages, + // therefore can just use the original TmsvId to identify the UID. + MarkMessageForDeleteL(iCurrentMsgId); + + if (iImapSettings.UseExpunge()) + { + iNextStep=EExpunge; + } + else + { + iNextStep=ECloseFolder; + } + SetActive(); + break; + } + + case EExpunge: // asynchronous + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(EExpunge)"); + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal5); // EXPUNGE + // Issue expunge to delete the message now. + iSession->ExpungeL(iStatus); + // This is the end of a move operation - move to EFetchMessage state + // to check for more messages to move. + iNextStep=ESetSourceMailbox; + SetActive(); + break; + } + + case ECloseFolder: + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(ECloseFolder)"); + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal6); // CLOSE FOLDER + //Permanently removes all messages that have the deleted flag set + //from the currently selected mailbox + iSession->CloseL(iStatus); + iNextStep=ESelectFolderAfterClose; + SetActive(); + break; + } + + case ESelectFolderAfterClose: + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(ESelectFolderAfterClose)"); + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal7); // SELECT FOLDER + //Selecting a mailbox so that messages in the mailbox can be accessed. + iSourceFolder->SelectL(iStatus, *iSession); + iNextStep=ESetSourceMailbox; + SetActive(); + break; + } + + case EFinished: + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(EFinished OK)"); + iProgressState = TImap4GenericProgress::EIdle; + Complete(KErrNone); + return ETrue; + } + + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyToLocalCompoundUnexpectedState)); + // unexpected state - complete the request + iProgressState = TImap4GenericProgress::EIdle; + return ETrue; + } + } // end of switch (iCurrentStep) + return EFalse; + } + +/** +Special form of cancel, which enables message currently being fetched to be resumed. +*/ +void CImapCompoundCopyToLocal::CancelEnableResume() + { + iResume = ETrue; + Cancel(); + iResume = EFalse; + } + +/** +May be called in case of a genuine cancel or a cancel for migrate. +Following a genuine cancel, the compound operation will be deleted. +Following a cancel for migrate, the compound operation will be resumed, +so the iNextState is updated here to ensure the operation is +correctly restarted. + +In either case, CMsgActive::DoCancel() is called to complete the +user request with KErrCancel. + +Note that if the default CMsgActive::DoComplete() is overridden, +then that must also be modified to handle either case described above. +*/ +void CImapCompoundCopyToLocal::DoCancel() + { + __LOG_FORMAT((iSession->LogId(), "CImapCompoundCopyToLocal::DoCancel(iCurrentStep=%d)", iCurrentStep)); + switch (iCurrentStep) + { + case ESelectSourceMailboxRW: + { + iSession->Cancel(); + iNextStep=ESelectSourceMailboxRW; // (already set up - just need to resume here) + break; + } + case ESelectSourceMailboxRO: + { + iSession->Cancel(); + iNextStep=ESelectSourceMailboxRO; // (already set up - just need to resume here) + break; + } + case EFetchMessage: + { + if (iResume) + { + iBodyFetcher->CancelEnableResume(); + } + else + { + iBodyFetcher->Cancel(); + } + iNextStep=EFetchMessage; // need to re-SELECT folder before resuming fetch + break; + } + case EInboxDuplicateCopy: + case EInboxDuplicateMove: + { + // outstanding request is on CMsvServerEntry + iMoveEntry->Cancel(); + // cancel the copy, restart on re-connect + iNextStep=iCurrentStep; + break; + } + case EDeleteMessage: + { + iSession->Cancel(); + iNextStep=EDeleteMessage; // need to re-SELECT folder before deleting + break; + } + case EExpunge: + { + iSession->Cancel(); + iNextStep=EExpunge; // need to re-SELECT folder before deleting + break; + } + case ECloseFolder: + { + iSession->Cancel(); + iNextStep=ECloseFolder; + break; + } + case ESelectFolderAfterClose: + { + iSession->Cancel(); + iNextStep=ESelectFolderAfterClose; + break; + } + case EDeleteLocalMessage: + case ESetSourceMailbox: + case ELocalCopyComplete: + case EFinished: + { + // self-completed or no outstanding request. + break; + } + case ESuspendedForMigrate: + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyToLocalCompoundCancelUnexpectedState)); + // attempt to resume from the next message in the array. + iNextStep=ESetSourceMailbox; + break; + } + } // end of switch (iCurrentStep) + + if (!iCancelForMigrate) + { + // genuine cancel - update progress + iProgressErrorCode = KErrCancel; + } + CMsgActive::DoCancel(); + } + +void CImapCompoundCopyToLocal::Progress(TImap4CompoundProgress& aCompoundProgress) + { + if (iPopulateCommand) + { + aCompoundProgress.iGenericProgress.iOperation = TImap4GenericProgress::EPopulate; + } + else + { + + if (iIsMove) + { + aCompoundProgress.iGenericProgress.iOperation = TImap4GenericProgress::EMoveToLocal; + } + else + { + aCompoundProgress.iGenericProgress.iOperation = TImap4GenericProgress::ECopyToLocal; + } + } + // Progress through the received selection + aCompoundProgress.iGenericProgress.iTotalSize = iTotalSize; + aCompoundProgress.iGenericProgress.iMsgsToDo = iMessageSelection; + aCompoundProgress.iGenericProgress.iMsgsDone = iMessageSelection - iSelectionStillToCopy; + // current message progress + iBodyFetcher->Progress(aCompoundProgress.iGenericProgress); + + // Put error into progress buffer + if( aCompoundProgress.iGenericProgress.iErrorCode == KErrNone ) + { + aCompoundProgress.iGenericProgress.iErrorCode = iProgressErrorCode; + } + } + +/** +Find the parent folder +@return ETrue if the source folder has changed since the last message operated on +always true on the first call after creation. +@post iSourceFolderId - The TMsvId of the source folder for the message. May be NULL +@post iSourceFolder - CImapFolder pointer for the parent folder. May be NULL. +*/ +TBool CImapCompoundCopyToLocal::SetSourceFolderL(const TMsvId aMessage) + { + TMsvId folderId = KMsvNullIndexEntryId; + TMsvId parentFolder = FindFolderL(aMessage); + CImapFolderInfo* selectedFolder = iSession->SelectedFolderInfo(); + if(selectedFolder != NULL) + { + folderId = selectedFolder->MsvId() ; + } + if (parentFolder==iSourceFolderId) + { + // Same folder as last message copied. + return EFalse; + } + // otherwise, update to the new folder. + iSourceFolderId = parentFolder; + if (iSourceFolderId!=NULL) + { + iSourceFolder = iSyncManager.GetFolderL(iSourceFolderId); + } + //Check the same INBOX folder is selected before + if (parentFolder== folderId) + { + return EFalse; + } + return ETrue; + } + +/** +Updates iGetPartialMailInfo to default values - (fetch whole message and attachments +without limits). This is used for copy/move to local operations (populate operations +uses a user-specified partial mail info object. + +@param aDestination - destination folder. +@internalTechnology +*/ +void CImapCompoundCopyToLocal::UpdatePartialMailInfoToDefaults(TMsvId aDestination) + { + iGetPartialMailInfo.iTotalSizeLimit= KMaxTInt; + iGetPartialMailInfo.iBodyTextSizeLimit = KMaxTInt; + iGetPartialMailInfo.iAttachmentSizeLimit = KMaxTInt; + iGetPartialMailInfo.iGetMailBodyParts = EGetImap4EmailBodyTextAndAttachments; + iGetPartialMailInfo.iPartialMailOptions=ENoSizeLimits; + iGetPartialMailInfo.iDestinationFolder=aDestination; + } + +/** +Copy or move a message to the specified local service folder. +@param aSource The message to copy or move +@param aDestinationFolder The id of the destination folder +@param aRemoveOriginal ETrue if this is a move, EFalse for a copy. +*/ +TInt CImapCompoundCopyToLocal::CopyMessage(const TMsvId aSource, const TMsvId aDestinationFolder, const TBool aRemoveOriginal) + { + TRAPD(err,CopyMessageL(aSource, aDestinationFolder, aRemoveOriginal)); + if (err!=KErrNone) + { + // park moveentry if it fails to get going + if (iMoveEntry) + { + iMoveEntry->SetEntry(NULL); + } + + __LOG_FORMAT((iSession->LogId(), " CopyMessage() failed, err=", err)); + } + + return err; + } + +void CImapCompoundCopyToLocal::CopyMessageL(const TMsvId aSource, const TMsvId aDestinationFolder, const TBool aRemoveOriginal) + { + __LOG_FORMAT((iSession->LogId(), "CImapCompoundCopyToLocal::CopyMessageL(%x (in %x) to %x)", aSource, iSourceFolderId, aDestinationFolder)); + + // Get a moveentry if we don't already have one + if (!iMoveEntry) + { + // Get a MoveEntry: we need to ask for one as a child of this entry, so + // move it to the root and ask for a child in the local service, which should + // always be there. + SetEntryL(KMsvRootIndexEntryId); + + // Get child + iMoveEntry=iServerEntry.NewEntryL(KMsvLocalServiceIndexEntryId); + } + + // Do the move, using the iMoveEntry CMsvServerEntry object + SetEntryL(aSource); + User::LeaveIfError(iMoveEntry->SetEntry(iServerEntry.Entry().Parent())); + + // Set context to null because the move / copy tries to lock each entry, + // and it will fail if we have it locked already + SetEntryL(KMsvNullIndexEntryId); + + aRemoveOriginal? + iMoveEntry->MoveEntryL(aSource,aDestinationFolder,iStatus): + iMoveEntry->CopyEntryL(aSource,aDestinationFolder,iStatus); + if (!IsActive()) SetActive(); + } + +/** +Called when the copy operation is complete. +Clears the new flag on the message entry just copied/moved. +*/ +void CImapCompoundCopyToLocal::CopyCompleteL() + { + // Park the move entry again + User::LeaveIfError(iMoveEntry->SetEntry(NULL)); + + SetEntryL(iCurrentMsgId); + TMsvEntry entry=iServerEntry.Entry(); + entry.SetNew(EFalse); + iServerEntry.ChangeEntry(entry); + } + +/** +Marks message specified by aTarget for delete locally, +and issues STORE command to mark it for delete remotely. + +Issues an asynchronous request to the CImapSession. SetActive() +must be called after calling this function. + +@param aTarget The message to be marked for delete +*/ +void CImapCompoundCopyToLocal::MarkMessageForDeleteL(const TMsvId& aTarget) + { + // Move to the entry in question + SetEntryL(aTarget); + + // Set deleted flag on this entry + TMsvEmailEntry entry=iServerEntry.Entry(); + entry.SetDeletedIMAP4Flag(ETrue); + User::LeaveIfError(iServerEntry.ChangeEntry(entry)); + + RArray uidArray; + CleanupClosePushL(uidArray); + + uidArray.AppendL(entry.UID()); + HBufC8* uids = CImapSession::CreateSequenceSetLC(uidArray); + + iSession->StoreL(iStatus, *uids, KMessageDataItem, KDeleteValue, ETrue, iOutMessageFlagInfo); + CleanupStack::PopAndDestroy(2, &uidArray); // uids, uidArray + } + +/** +Handles NO/BAD responses according to current step. +Negative server responses are not fatal - the error is saved in +the message currently being operated on and the state machine pushed +on to process the next message in the requested selection. + +@return KErrNone if the error has been handled + Completion error code otherwise. +*/ +TInt CImapCompoundCopyToLocal::ProcessNegativeServerResponse() + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::ProcessNegativeServerResponse"); + TInt err = iStatus.Int(); + switch (iCurrentStep) + { + case ESelectSourceMailboxRW: + case ESelectSourceMailboxRO: + case ESelectFolderAfterClose: + { + if (err == KErrImapNo) + { + err = KErrNotFound; + } + } // fall through + + case EDeleteMessage: + case EExpunge: + case ECloseFolder: + case EFetchMessage: + { + // save the error with the associated message + TRAP_IGNORE(MessageErrorL(iCurrentMsgId, err)); + // Skip to the next message or finish + iNextStep = (iSelectionStillToCopy>0)?ESetSourceMailbox:EFinished; + break; + } + + case EInboxDuplicateCopy: + case EInboxDuplicateMove: + case EDeleteLocalMessage: + case ESetSourceMailbox: + case ELocalCopyComplete: + case EFinished: + case ESuspendedForMigrate: + default: + { + // positive error code not expected, + // self-completed states or no outstanding request. + TImapServerPanic::ImapPanic(TImapServerPanic::ECopyToLocalCompoundUnexpectedState); + break; + } + } // end of switch (iCurrentStep) + iProgressErrorCode = err; + return KErrNone; + } + + +/** +Called to resume the compound operation following a bearer migration. +*/ +void CImapCompoundCopyToLocal::ResumeOperationL(TRequestStatus& aStatus, CImapSession& aSession) + { + iSession = &aSession; + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::Resuming"); + __ASSERT_DEBUG(iCurrentStep==ESuspendedForMigrate, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyToLocalCompoundUnexpectedState)); + iStopForMigrate = EFalse; + + // Switch on next step - some "next steps" require a SELEECT first... + switch (iNextStep) + { + case ESetSourceMailbox: // clean start on the next message in the array + case ESelectSourceMailboxRW: // current message already selected... continue... + case ESelectSourceMailboxRO: + case ESelectFolderAfterClose: + { + // just return to the main state machine + CompleteSelf(); + break; + } + case EFetchMessage: + case EDeleteMessage: + case EExpunge: + case ECloseFolder: + case EInboxDuplicateCopy: + case EInboxDuplicateMove: // need to be selected to delete the message if this is a move + { + // select the source folder before returning to main state machine + iSourceFolder->SelectL(iStatus, *iSession); + iCurrentStep = ESelectSourceMailboxRW; + SetActive(); + break; + } + case ELocalCopyComplete: + case EDeleteLocalMessage: + case EFinished: + // not expected + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyToLocalCompoundCancelUnexpectedState)); + // abandon the compound operation + iNextStep=EFinished; + CompleteSelf(); + break; + } + } // end switch (iNextStep) + Queue(aStatus); + } + +/** +Removes matching messages from the array of messages to be fetched. +This is called when a populate or copy to local operation is requested while +a background sync operation is in progress. It is used to remove +messages from the array of messages to be auto-fetched that are going to +be fetched due to the populate request. + +Note that this does not impact messages that have already been auto-fetched, or +messages that are in the process of being fetched as this function is called. + +@param aSelection - the selection of messages to be removed from the list +*/ +void CImapCompoundCopyToLocal::RemoveFromSelection(CMsvEntrySelection& aDeleteSel) + { + // Check each entry in the delete selection... + TInt source; + for (TInt del=0;delFind(aDeleteSel[del]); + if (source!=KErrNotFound) + { + if (source < iSelectionStillToCopy) + { + // message has not yet been auto-fetched, remove it from the list. + // note that iSelection starts at iSourceSel.Count() and counts down... + iSourceSel->Delete(source); + // update the fetch index. + --iSelectionStillToCopy; + } + // otherwise message has already been auto-fetched. No action necessary. + } + } + }