--- /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 (i<count && sameParent)
+ {
+ SetEntryL((*iSourceSel)[i]);
+ TMsvEmailEntry entry = iServerEntry.Entry();
+ if (entry.Parent()==iParentFolderId)
+ {
+ TUint id = entry.UID();
+ iMessageUids.AppendL(id);
+
+ // Set deleted flag on local entry
+ entry.SetDeletedIMAP4Flag(ETrue);
+ User::LeaveIfError(iServerEntry.ChangeEntry(entry));
+ ++iSelectionToDelete;
+ }
+ else
+ {
+ sameParent=EFalse;
+ }
+ ++i;
+ }
+
+ HBufC8* uidSeq = CImapSession::CreateSequenceSetLC(iMessageUids);
+
+ // issue the store command
+ iSession->StoreL(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(count<iSelectionToDelete)
+ {
+ // Do not leave when entry is in use
+ TInt err = iServerEntry.DeleteEntry((*iSourceSel)[count]);
+ if(err!=KErrNone)
+ {
+ if(err!=KErrInUse)
+ {
+ User::LeaveIfError(err);
+ }
+ }
+ ++count;
+ } // end while()
+
+ // remove deleted items from the selection
+ iSourceSel->Delete(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 = (iProgressMsgsDone<iProgressMsgsToDo)?ESelectSourceMailboxRW:EFinished;
+ break;
+ }
+ case EDeleteMessage:
+ case EExpunge:
+ case ECloseFolder:
+ {
+ // failed to mark one or more messages for delete.
+ // ignore the error - no point in storing it in a
+ // message that is just about to be deleted.
+ break;
+ }
+ case EDeleteLocalMessage:
+ case EFinished:
+ case ESuspendedForMigrate:
+ default:
+ {
+ // positive error code not expected,
+ // self-completed states or no outstanding request.
+ TImapServerPanic::ImapPanic(TImapServerPanic::EDeleteCompoundUnexpectedState);
+ break;
+ }
+ } // end switch (iCurrentStep)
+ iProgressErrorCode = err;
+ return KErrNone;
+ }
+
+/**
+Resumes the operation following a migration.
+*/
+void CImapCompoundDelete::ResumeOperationL(TRequestStatus& aStatus, CImapSession& aSession)
+ {
+ iSession = &aSession;
+ __LOG_TEXT(iSession->LogId(), "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);
+ }