diff -r 000000000000 -r 72b543305e3a email/imap4mtm/imapprotocolcontroller/src/cimapcompoundcopywithinservice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/imap4mtm/imapprotocolcontroller/src/cimapcompoundcopywithinservice.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,684 @@ +// 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 "cimapcompoundcopywithinservice.h" +#include "cimapsessionconsts.h" +#include "cimapsyncmanager.h" +#include "cimapfolder.h" +#include "cimaplogger.h" +#include "imappaniccodes.h" + +#include "mobilitytestmtmapi.h" + +_LIT8( KMessageDataItem, "+FLAGS" ); +_LIT8( KDeleteValue, "\\Deleted" ); + +CImapCompoundCopyWithinService::CImapCompoundCopyWithinService( CImapSyncManager& aSyncManager, + CMsvServerEntry& aServerEntry, + CImapSettings& aImapSettings, + TBool aIsMove, + const TMsvId aDestination ) + : CImapCompoundBase(aSyncManager, aServerEntry, aImapSettings), + iIsMove(aIsMove), iDestinationId(aDestination) + { + + } + +CImapCompoundCopyWithinService::~CImapCompoundCopyWithinService() + { + iOutMessageFlagInfo.Close(); + iMessageUids.Reset(); + delete iUidSeq; + delete iSourceSel; + } + +CImapCompoundCopyWithinService* CImapCompoundCopyWithinService::NewL( CImapSyncManager& aSyncManager, + CMsvServerEntry& aServerEntry, + CImapSettings& aImapSettings, + TBool aIsMove, + const CMsvEntrySelection& aSourceSel, + const TMsvId aDestination ) + { + CImapCompoundCopyWithinService* self = new (ELeave) CImapCompoundCopyWithinService( aSyncManager, + aServerEntry, + aImapSettings, + aIsMove, + aDestination ); + CleanupStack::PushL(self); + self->ConstructL(aSourceSel); + CleanupStack::Pop(self); + return self; + + } + +void CImapCompoundCopyWithinService::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 + TInt err = CheckSelectionL(aSourceSel, iSourceSel, ETrue, ETrue, EFalse, ETrue); + if (err==KErrNone) + { + // reset the stats + iMsgsCopied = 0; + iMsgsDone = 0; + iMsgsToDo = aSourceSel.Count(); + iTotalSize = CalculateDownloadSizeL(*iSourceSel); + } + + // Add to the active scheduler + CActiveScheduler::Add(this); + } + +void CImapCompoundCopyWithinService::StartOperation(TRequestStatus& aStatus, CImapSession& aSession) + { + iSession = &aSession; + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyWithinService::StartOperation()"); + iNextStep = ECheckDestinationMailbox; + Queue(aStatus); + CompleteSelf(); + } + +/** +Handles the compound operation state machine + +@return ETrue if compound operation is completed, + EFalse otherwise. +*/ +TBool CImapCompoundCopyWithinService::DoRunLoopL( ) + { + SetCurrentStep(); + switch (iCurrentStep) + { + case ECheckDestinationMailbox: // synchronous + { + // Obtain pointer to CImapFolder object for the destination folder. + iDestinationFolder = iSyncManager.GetFolderL(iDestinationId); + if (iDestinationFolder==NULL) + { + // note the error code for the progress report and complete + iProgressErrorCode = KErrNotFound; + iNextStep = EFinished; + } + else + { + iNextStep = ESetSourceMailbox; + } + // go to the next step + return EFalse; + } + + case ESetSourceMailbox: // synchronous + { + // State machine returns to this state after performing a copy/move + // on messages in the selection in a single mailbox. The progress is + // incremented here for messages that have been copied. If all messages + // have been moved/copied the state machine progresses to the + // synchronisation of the destination folder. + IncrementProgress(iMsgsCopied); + + // starting a new copy operation... + iMsgsCopied = 0; + + if (iSourceSel->Count() > 0) + { + if (iStopForMigrate) + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyWithinService::Stopped for migrate"); + iCurrentStep = ESuspendedForMigrate; + iNextStep = ESetSourceMailbox; + Complete(KErrNone); + return ETrue; + } + + iCurrentMsgId = (*iSourceSel)[0]; + SetSourceFolderL(iCurrentMsgId); + if (iSourceFolderId != NULL && iSourceFolder != NULL) + { + iNextStep = (iIsMove) ? ESelectSourceMailboxRW : ESelectSourceMailboxRO; + } + else + { + // no change in state if folder not acceptable, + // try again with the next message in the selection. + iNextStep = ESetSourceMailbox; + IncrementProgress(1); + } + } + else + { + // All messages copied or moved... + // Sync the destination folder + iNextStep = ESelectDestinationMailboxRO; + } + // go to the next step + return EFalse; + } + + case ESelectSourceMailboxRW: // asynchronous + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyWithinService1); // SELECT source mailbox + iSourceFolder->SelectL(iStatus, *iSession); + iNextStep = ECopyMessage; + SetActive(); + break; + } + + case ESelectSourceMailboxRO: + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyWithinService1); // EXAMINE source mailbox + iSourceFolder->ExamineL(iStatus, *iSession); + iNextStep = ECopyMessage; + SetActive(); + break; + } + + case ECopyMessage: // asynchronous + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyWithinService2); // COPY message(s) + // issue the COPY command - copies all messages in the selection + // that have the same parent folder as the first in the selection. + CopyMessagesL(); + if (iIsMove) + { + iProgressState = TImap4GenericProgress::EMoving; + iNextStep = EDeleteMessage; + } + else + { + // on completion, return to SetSourceMailbox to check for + // more messages to copy (from other source folders) + iProgressState = TImap4GenericProgress::ECopying; + iNextStep = ESetSourceMailbox; + } + SetActive(); + break; + } + + case EDeleteMessage: // asynchronous + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyWithinService3); // 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. + MarkMessageForDeletesL(); + + if (iImapSettings.UseExpunge()) + { + iNextStep=EExpunge; + } + else + { + iNextStep=ECloseFolder; + } + + SetActive(); + break; + } + + case EExpunge: // asynchronous + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyWithinService4); // EXPUNGE source folder (move only) + iSession->ExpungeL(iStatus); + iNextStep = EDeleteLocalMessage; + SetActive(); + break; + } + + case ECloseFolder: + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyWithinService7); // CLOSE FOLDER (move only) + //Permanently removes all messages that have the deleted flag set + //from the currently selected mailbox + iSession->CloseL(iStatus); + iNextStep=ESelectFolderAfterClose; + SetActive(); + break; + } + + case ESelectFolderAfterClose: + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyWithinService8); // SELECT FOLDER (move only) + //Selecting a mailbox so that messages in the mailbox can be accessed. + iSourceFolder->SelectL(iStatus, *iSession); + iNextStep=EDeleteLocalMessage; + SetActive(); + break; + } + + case EDeleteLocalMessage: // synchonous + { + // Delete the messages locally + DeleteMovedMessagesL(); + // immediately proceed to the next step + iNextStep = ESetSourceMailbox; + return EFalse; + } + + case ESelectDestinationMailboxRO: + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyWithinService5); // SELECT destination folder + iDestinationFolder->ExamineL(iStatus, *iSession); + iNextStep = ENewSyncFolder; + SetActive(); + break; + } + + case ENewSyncFolder: + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyWithinService6); // sync'ing destination folder + // if the current folder hasn't actually changed then don't + // bother with the Sync + iNextStep = EFinished; + if (iDestinationFolder->Changed(*iSession)) + { + iSyncProgressState=TImap4SyncProgress::ESyncOther; + iDestinationFolder->SynchroniseL(iStatus, *iSession, ETrue, 0); + SetActive(); + } + break; + } + + case EFinished: + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyWithinService::Completing OK"); + iProgressState = TImap4GenericProgress::EIdle; + iSyncProgressState = TImap4SyncProgress::EIdle; + + Complete(KErrNone); + return ETrue; + } + + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyWithinServiceCompoundUnexpectedState)); + // unexpected state - complete the request + iProgressState = TImap4GenericProgress::EIdle; + return ETrue; + } + } // end switch (iCurrentStep) + return 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 CImapCompoundCopyWithinService::DoCancel() + { + switch (iCurrentStep) + { + case ESelectSourceMailboxRW: + { + iSession->Cancel(); + iNextStep = ESelectSourceMailboxRW; // just resume + break; + } + case ESelectSourceMailboxRO: + { + iSession->Cancel(); + iNextStep = ESelectSourceMailboxRO; // just resume + break; + } + case ECopyMessage: + { + iSession->Cancel(); + iMsgsCopied = 0; // reset copied count + iNextStep = ECopyMessage; // reSELECT source + break; + } + case EDeleteMessage: + { + iSession->Cancel(); + iNextStep = EDeleteMessage; // reSELECT source + break; + } + case EExpunge: + { + iSession->Cancel(); + iNextStep = EExpunge; // reSELECT source + break; + } + case ECloseFolder: + { + iSession->Cancel(); + iNextStep = ECloseFolder; + break; + } + case ESelectFolderAfterClose: + { + iSession->Cancel(); + iNextStep = ESelectFolderAfterClose; + break; + } + case ESelectDestinationMailboxRO: + { + iSession->Cancel(); + iNextStep = ESelectDestinationMailboxRO; // just restart + break; + } + case ENewSyncFolder: + { + iDestinationFolder->Cancel(); + iNextStep = ESelectDestinationMailboxRO; // just restart + break; + } + case ECheckDestinationMailbox: + case ESetSourceMailbox: + case EDeleteLocalMessage: + case EFinished: + // self-completed or no outstanding request. + case ESuspendedForMigrate: + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyWithinServiceCompoundCancelUnexpectedState)); + iNextStep = ESetSourceMailbox; + // starting a new copy operation... + iMsgsCopied = 0; + break; + } + } // end switch (iCurrentStep) + + if (!iCancelForMigrate) + { + // genuine cancel - update progress + iProgressErrorCode = KErrCancel; + } + CMsgActive::DoCancel(); + } + +void CImapCompoundCopyWithinService::Progress(TImap4CompoundProgress& aCompoundProgress) + { + if (iIsMove) + { + aCompoundProgress.iGenericProgress.iOperation = TImap4GenericProgress::EMoveWithinService; + } + else + { + aCompoundProgress.iGenericProgress.iOperation = TImap4GenericProgress::ECopyWithinService; + } + + // Progress through the received selection + aCompoundProgress.iGenericProgress.iTotalSize = iTotalSize; + aCompoundProgress.iGenericProgress.iMsgsToDo = iMsgsToDo; + aCompoundProgress.iGenericProgress.iMsgsDone = iMsgsDone; + + aCompoundProgress.iSyncProgress.iState=iSyncProgressState; + + //if currently syncing then get the latest progress + if(iSyncProgressState!=TImap4SyncProgress::EIdle) + { + iDestinationFolder->Progress(aCompoundProgress.iSyncProgress); + } + + + // Put error into progress buffer + if( aCompoundProgress.iGenericProgress.iErrorCode == KErrNone ) + { + aCompoundProgress.iGenericProgress.iErrorCode = iProgressErrorCode; + } + } + +/** +Builds an array of messages within the same source folder that can be copied +with a single command to the remote server. +*/ +void CImapCompoundCopyWithinService::CopyMessagesL( ) + { + iMessageUids.Reset(); + // build an array of the UIDs to copy, + TInt i = 0; + TInt count = iSourceSel->Count(); + + TBool sameParent = ETrue; + while(iCopyL(iStatus, *iUidSeq, iDestinationFolder->FullFolderPathL()); + } + +/* +Marks the messages that have been copied as deleted locally. + +Takes the uids in iMessageUids and marks messages for delete locally, +and issues STORE command to mark it for delete remotely. +*/ +void CImapCompoundCopyWithinService::MarkMessageForDeletesL() + { + for (TInt i=0 ; iStoreL( iStatus, + *iUidSeq, + KMessageDataItem, + KDeleteValue, + ETrue, + iOutMessageFlagInfo ); + } + +/** +Deletes the local copy of messages that have been expunged on the remote server. +*/ +void CImapCompoundCopyWithinService::DeleteMovedMessagesL() + { + // move to parent + SetEntryL(iSourceFolderId); + + TInt messagesToDelete = iMsgsCopied - iMsgsDone ; + for (TInt i=0 ; i0) + { + iMsgsDone+=aNum; + iSourceSel->Delete(0,aNum); + } + } + + +/** +Called to resume the compound operation following a bearer migration. +*/ +void CImapCompoundCopyWithinService::ResumeOperationL(TRequestStatus& aStatus, CImapSession& aSession) + { + iSession = &aSession; + __LOG_TEXT(iSession->LogId(), "CImapCompoundCopyWithinService::Resuming"); + __ASSERT_DEBUG(iCurrentStep==ESuspendedForMigrate, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyWithinServiceCompoundUnexpectedState)); + iStopForMigrate = EFalse; + + switch (iNextStep) + { + case ESelectSourceMailboxRW: + case ESelectSourceMailboxRO: + case ESelectDestinationMailboxRO: + case ENewSyncFolder: + case ESelectFolderAfterClose: + { + CompleteSelf(); + break; + } + case ECopyMessage: + case EDeleteMessage: + case EExpunge: + case ECloseFolder: + { + iSourceFolder->SelectL(iStatus, *iSession); + iCurrentStep = ESelectSourceMailboxRW; + SetActive(); + break; + } + case ECheckDestinationMailbox: + case ESetSourceMailbox: + case EDeleteLocalMessage: + case EFinished: + // self-completed or no outstanding request. + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyWithinServiceCompoundCancelUnexpectedState)); + // abandon the compound operation + iNextStep=EFinished; + CompleteSelf(); + break; + } + } // end switch (iNextStep) + Queue(aStatus); + } +