diff -r 000000000000 -r 72b543305e3a email/imap4mtm/imapprotocolcontroller/src/cimapcompounddelete.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/imap4mtm/imapprotocolcontroller/src/cimapcompounddelete.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,491 @@ +// 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 "cimapcompounddelete.h" +#include "cimapsession.h" +#include "cimapfolder.h" +#include "cimapsessionconsts.h" +#include "cimaplogger.h" +#include "imappaniccodes.h" + +#include "mobilitytestmtmapi.h" + +_LIT8(KImapAddFlags, "+FLAGS"); +_LIT8(KImapFlagDeleted, "(\\Deleted)"); + +CImapCompoundDelete::CImapCompoundDelete(CImapSyncManager& aSyncManager, + CMsvServerEntry& aServerEntry, + CImapSettings& aImapSettings) : + CImapCompoundBase(aSyncManager, aServerEntry, aImapSettings) + { + + } + +CImapCompoundDelete::~CImapCompoundDelete() + { + delete iSourceSel; + iMessageUids.Reset(); + iFlagInfoResponseArray.Reset(); + } + +CImapCompoundDelete* CImapCompoundDelete::NewL(CImapSyncManager& aSyncManager, + CMsvServerEntry& aServerEntry, + CImapSettings& aImapSettings, + const CMsvEntrySelection& aSourceSel) + { + CImapCompoundDelete* self = new (ELeave) CImapCompoundDelete(aSyncManager, + aServerEntry, + aImapSettings ); + CleanupStack::PushL(self); + self->ConstructL(aSourceSel); + CleanupStack::Pop(self); + return self; + } + +void CImapCompoundDelete::ConstructL(const CMsvEntrySelection& aSourceSel) + { + iSourceSel=new (ELeave) CMsvEntrySelection; + + + // Check the source selection contains only messages + // Messages True + // Handle Parts False + // Handle Folders False + // Check source True + // Makes a local copy of the source selection in iSourceSel + CheckSelectionL(aSourceSel, iSourceSel, ETrue, EFalse, EFalse, ETrue); + + // Initialise the progress counters + iProgressMsgsToDo=iSourceSel->Count(); + iProgressMsgsDone=0; + + // Add to the active scheduler + CActiveScheduler::Add(this); + } + +void CImapCompoundDelete::StartOperation(TRequestStatus& aStatus, CImapSession& aSession) + { + iSession = &aSession; + __LOG_TEXT(iSession->LogId(), "CImapCompoundDelete::StartOperation()"); + iNextStep = ESelectSourceMailboxRW; + Queue(aStatus); + CompleteSelf(); + } + +/** +Handles the compound operation state machine + +@return ETrue if compound operation is completed, + EFalse otherwise (will be called again, unless active) +*/ +TBool CImapCompoundDelete::DoRunLoopL() + { + SetCurrentStep(); + switch (iCurrentStep) + { + case ESelectSourceMailboxRW: // asynchronous + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapDelete1); // select source folder + // Anything to do? + if (iSourceSel->Count()==0) + { + iNextStep = EFinished; + return EFalse; + } + + // suspend operation if stop for migrate has been requested + if (iStopForMigrate) + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundDelete::Stopped for migrate"); + iCurrentStep = ESuspendedForMigrate; + iNextStep = ESelectSourceMailboxRW; + Complete(KErrNone); + return ETrue; + } + + // Find the parent folder for the first entry in the array + iParentFolderId = FindFolderL((*iSourceSel)[0]); + iParentFolder = iSyncManager.GetFolderL(iParentFolderId); + if (iParentFolder==NULL) + { + // Try again with the next message + // Delete the first entry in the source selection + iSourceSel->Delete(0, 1); + // increment progress counter + ++iProgressMsgsDone; + return EFalse; + } + + // reset the message UID list + iMessageUids.Reset(); + + __LOG_FORMAT((iSession->LogId(), "Deleting message %x in folder %x", (*iSourceSel)[0], iParentFolderId)); + + iParentFolder->SelectL(iStatus, *iSession); + iProgressState = TImap4GenericProgress::ESelecting; + iNextStep = EDeleteMessage; + SetActive(); + break; + } + + case EDeleteMessage: // asynchronous + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapDelete2); // issue the STORE /deleted command + // build an array of the UIDs to delete, + // mark local IMAP \deleted flag. + TInt i = 0; + TInt count = iSourceSel->Count(); + TBool sameParent = ETrue; + while (iStoreL(iStatus, uidSeq->Des(), KImapAddFlags, KImapFlagDeleted, ETrue, iFlagInfoResponseArray); + iProgressState = TImap4GenericProgress::EDeleting; + CleanupStack::PopAndDestroy(uidSeq); + if (iImapSettings.UseExpunge()) + { + iNextStep=EExpunge; + } + else + { + iNextStep=ECloseFolder; + } + SetActive(); + break; + } + + case EExpunge: // asynchronous + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapDelete3); // issue the EXPUNGE command + // Messages have been marked for delete on the remote server + // issue the expunge command to remove them completely + iSession->ExpungeL(iStatus); + // no change in progress state + iNextStep = EDeleteLocalMessage; + SetActive(); + break; + } + + case ECloseFolder: + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapDelete4); // 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: + { + MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapDelete5); // SELECT FOLDER + //Selecting a mailbox so that messages in the mailbox can be accessed. + iParentFolder->SelectL(iStatus, *iSession); + iNextStep=EDeleteLocalMessage; + SetActive(); + break; + } + + case EDeleteLocalMessage: // synchronous + { + DeleteLocalMessagesL(); + // test for remaining messages to delete performed in state ESelectSourceMailboxRW; + iNextStep=ESelectSourceMailboxRW; + // no async request made. + return EFalse; + } + + case EFinished: // finished + { + __LOG_TEXT(iSession->LogId(), "CImapCompoundDelete::Completing OK"); + iProgressState = TImap4GenericProgress::EIdle; + Complete(KErrNone); + return ETrue; + } + + default: + { + __ASSERT_DEBUG(EFalse, + TImapServerPanic::ImapPanic(TImapServerPanic::EDeleteCompoundUnexpectedState)); + // unexpected state - complete the request + iProgressState = TImap4GenericProgress::EIdle; + return ETrue; + } + } + 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 CImapCompoundDelete::DoCancel() + { + switch (iCurrentStep) + { + case ESelectSourceMailboxRW: + { + // There is an outstanding select command issued. Cancel it. + // Need to re-start with next step = ESelectSourceMailboxRW + iSession->Cancel(); + iNextStep = ESelectSourceMailboxRW; + break; + } + case EDeleteMessage: + { + // in DoRunL an array of messages in this folder has been created + // so, it is possible that that requested delete has not actually + // been received by the remote server. When the operation re-starts + // it will need to re-select the parent folder and then re-issue the + // STORE \deleted command. + + // the entries have already been marked locally. + // we still have the iUID array - iMessageUids. + + // Cancel the session command + iSession->Cancel(); + iSelectionToDelete = 0; // reset the count of messages to delete locally + iNextStep = EDeleteMessage; // restart function + break; + } + case EExpunge: + { + // Messages have been marked for delete on the remote server + // and the expunge command issued - cancel it + + // Cancel the session command + iSession->Cancel(); + + // need to re-issue the expunge command after migrate + // (and re-select of correct folder) + iNextStep = EExpunge; + break; + } + case ECloseFolder: + { + iSession->Cancel(); + iNextStep = ECloseFolder; + break; + } + case ESelectFolderAfterClose: + { + iSession->Cancel(); + iNextStep = ESelectFolderAfterClose; + break; + } + case EDeleteLocalMessage: + case EFinished: + { + // self-completed or no outstanding request. + break; + } + case ESuspendedForMigrate: + default: + { + __ASSERT_DEBUG(EFalse, + TImapServerPanic::ImapPanic(TImapServerPanic::EDeleteCompoundCancelUnexpectedState)); + break; + } + } // end switch (iCurrentStep) + + if (!iCancelForMigrate) + { + // genuine cancel - update progress + iProgressErrorCode = KErrCancel; + } + CMsgActive::DoCancel(); + } + + +void CImapCompoundDelete::Progress(TImap4CompoundProgress& aCompoundProgress) + { + aCompoundProgress.iGenericProgress.iOperation = TImap4GenericProgress::EDelete; + aCompoundProgress.iGenericProgress.iState = iProgressState; + aCompoundProgress.iGenericProgress.iMsgsToDo = iProgressMsgsToDo; + aCompoundProgress.iGenericProgress.iMsgsDone = iProgressMsgsDone; + + // Put error into progress buffer + if( aCompoundProgress.iGenericProgress.iErrorCode == KErrNone ) + { + aCompoundProgress.iGenericProgress.iErrorCode = iProgressErrorCode; + } + + } + + +/** +Deletes messages locally +Deletes messages in iSourceSel contains only messages +belonging to a single parent folder. +*/ +void CImapCompoundDelete::DeleteLocalMessagesL() + { + // Set the server entry context to the parent. + SetEntryL((*iSourceSel)[0]); + SetEntryL(iServerEntry.Entry().Parent()); + + // delete local entries that have been expunged remotely + TInt count = 0; + while(countDelete(0,count); + iSelectionToDelete=0; + + // increment progress counter + iProgressMsgsDone+=count; + } + + +/** +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 CImapCompoundDelete::ProcessNegativeServerResponse() + { + TInt err = iStatus.Int(); + switch (iCurrentStep) + { + case ESelectSourceMailboxRW: + case ESelectFolderAfterClose: + { + // only knowingly operating on a single message at this point. + if (err == KErrImapNo) + { + err = KErrNotFound; + } + // save the error with the associated message + TRAP_IGNORE(MessageErrorL((*iSourceSel)[0], err)); + ++iProgressMsgsDone; + // Skip to the next message, or finish + iNextStep = (iProgressMsgsDoneLogId(), "CImapCompoundDelete::Resuming"); + __ASSERT_DEBUG(iCurrentStep==ESuspendedForMigrate, TImapServerPanic::ImapPanic(TImapServerPanic::EDeleteCompoundUnexpectedState)); + iStopForMigrate = EFalse; + + // Switch on next step - some "next steps" require a SELEECT first... + switch (iNextStep) + { + case ESelectSourceMailboxRW: + case ESelectFolderAfterClose: + { + // just return to the main state machine + CompleteSelf(); + break; + } + case EDeleteMessage: + case EExpunge: + case ECloseFolder: + { + // re-issue the SELECT before kicking off the DELETE or EXPUNGE command: + iParentFolder->SelectL(iStatus, *iSession); + iCurrentStep = ESelectSourceMailboxRW; + SetActive(); + break; + } + case EDeleteLocalMessage: + case EFinished: + // not expected + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EDeleteCompoundUnexpectedState)); + // abandon the compound operation + iNextStep=EFinished; + CompleteSelf(); + break; + } + } // end switch (iNextStep) + Queue(aStatus); + }