--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/email/imap4mtm/imapprotocolcontroller/src/cimapcompoundrename.cpp Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,461 @@
+// 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 "cimapcompoundrename.h"
+#include "cimapsession.h"
+#include "cimapsyncmanager.h"
+#include "cimapfolder.h"
+#include "cimaplogger.h"
+#include "imappaniccodes.h"
+
+#include "mobilitytestmtmapi.h"
+
+_LIT16(KFullPathFormat, "%S%S%S");
+
+CImapCompoundRename::CImapCompoundRename(CImapSyncManager& aSyncManager,
+ CMsvServerEntry& aServerEntry,
+ CImapSettings& aImapSettings,
+ const TMsvId aTarget)
+ : CImapCompoundBase(aSyncManager, aServerEntry, aImapSettings),
+ iFolderId(aTarget)
+ {
+ }
+
+CImapCompoundRename::~CImapCompoundRename()
+ {
+ delete iNewLeafName;
+ delete iMsgselection;
+ }
+
+CImapCompoundRename* CImapCompoundRename::NewL(CImapSyncManager& aSyncManager,
+ CMsvServerEntry& aServerEntry,
+ CImapSettings& aImapSettings,
+ const TMsvId aTarget,
+ const TDesC& aNewName)
+ {
+ CImapCompoundRename* self = new (ELeave) CImapCompoundRename(aSyncManager,
+ aServerEntry,
+ aImapSettings,
+ aTarget);
+ CleanupStack::PushL(self);
+ self->ConstructL(aNewName);
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+/**
+Secondary construction
+*/
+void CImapCompoundRename::ConstructL(const TDesC& aNewName)
+ {
+ // Store the new folder name
+ iNewLeafName = HBufC16::NewL(aNewName.Length());
+ TPtr16 ptr(iNewLeafName->Des());
+ ptr.Copy(aNewName);
+
+ CActiveScheduler::Add(this);
+ }
+
+/**
+Starts the folder rename operation.
+
+@param aStatus client object's TRequestStatus, to be notified on completion
+@param aTarget the local id of the folder to be renamed
+@param aNewName the new name for the specified folder
+*/
+void CImapCompoundRename::StartOperation(TRequestStatus& aStatus, CImapSession& aSession)
+ {
+ iSession = &aSession;
+ __LOG_TEXT(iSession->LogId(), "CImapCompoundRename::StartOperation()");
+ iNextStep = ERename;
+ 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 CImapCompoundRename::DoRunLoopL()
+ {
+ SetCurrentStep();
+ switch (iCurrentStep)
+ {
+
+ case ERename: // asynchronous
+ {
+ MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapRename1); // RENAME issued
+ // Issue the rename command to the remote server
+ // IssueRenameL() calls SetActive()
+ IssueRenameL();
+ break;
+ }
+
+ case EProcessRenameResponse: // asynchronous if renaming inbox, else synchronous
+ {
+ SetEntryL(iFolderId);
+ TMsvEmailEntry entry = iServerEntry.Entry();
+ TBool remotelySubscribed = entry.Subscribed();
+ iNextStep = remotelySubscribed?ESyncSubscriptions:EFinished;
+
+ // Rename the folder locally.
+ if (iFolderId == iSyncManager.Inbox()->MailboxId())
+ {
+ RenameInboxL(); // calls SetActive()
+ }
+ else
+ {
+ // Sync Manager updates affected CImapFolder objects.
+ iSyncManager.RenameLocalL(iFolderId, iNewLeafName->Des());
+ }
+ break;
+ }
+
+ case ESyncSubscriptions: // asynchronous
+ {
+ MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapRename2); // SUBSCRIBE issued
+ // The folder was previously remotely subscribed.
+ // IMAP RFC does not specify that a folder must retain its subscribed
+ // status following a rename, therefore we issue an explicit subscribe
+ // command to ensure remote subscription status is maintained.
+ CImapFolder* folder = iSyncManager.GetFolderL(iFolderId);
+ iSession->SubscribeL(iStatus, folder->FullFolderPathL());
+ SetActive();
+ iNextStep = EFinished;
+ break;
+ }
+
+ case EFinished: // finished
+ {
+ __LOG_TEXT(iSession->LogId(), "CImapCompoundRename::Completing OK");
+ iProgressState = TImap4GenericProgress::EIdle;
+ Complete(KErrNone);
+ return ETrue;
+ }
+
+ default :
+ {
+ __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ERenameCompoundUnexpectedState));
+ // unexpected state - complete the request
+ iProgressState = TImap4GenericProgress::EIdle;
+ return ETrue;
+ }
+
+ } // end of 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 CImapCompoundRename::DoCancel()
+ {
+ switch (iCurrentStep)
+ {
+ case ERename:
+ {
+ // outstanding request on the session
+ iSession->Cancel();
+ // Re-issue the rename after migration. There is a race condition
+ // - it is possible that the rename occurred on the server but the
+ // response was not received. This is not handled. The next folder
+ // sync will sort it out.
+ iNextStep=ERename;
+ break;
+ }
+ case ESyncSubscriptions:
+ {
+ iSession->Cancel();
+ iNextStep=ESyncSubscriptions;
+ break;
+ }
+ case EProcessRenameResponse:
+ {
+ // moving contents of inbox
+ iServerEntry.Cancel();
+ iNextStep=EProcessRenameResponse;
+ break;
+ }
+ case EFinished:
+ case ESuspendedForMigrate:
+ default:
+ {
+ __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ERenameCompoundCancelUnexpectedState));
+ iNextStep = ERename;
+ break;
+ }
+ } // end switch (iCurrentStep)
+
+ if (!iCancelForMigrate)
+ {
+ // genuine cancel - update progress
+ iProgressErrorCode = KErrCancel;
+ }
+ CMsgActive::DoCancel();
+ }
+
+
+/**
+Builds up the full path name for the renamed folder and issues the
+RENAME IMAP command via the imap session.
+
+@leave system wide error codes
+*/
+void CImapCompoundRename::IssueRenameL()
+ {
+ CImapFolder* folder = iSyncManager.GetFolderL(iFolderId);
+ if(folder == NULL)
+ {
+ // Sync manager hasn't found the folder under the imap service.
+ // exit with progress error code Not Found
+ iProgressErrorCode = KErrNotFound;
+ iNextStep = EFinished;
+ CompleteSelf();
+ return;
+ }
+
+ // build the new full path name for the folder
+ HBufC16* newPath = folder->MakePathL(EFalse);
+ CleanupStack::PushL(newPath);
+
+ // build up the full path
+ HBufC16* newFullPath = NULL;
+ if (newPath->Length() > 0)
+ {
+ // folder is a subdirectory
+ TInt buffLength = newPath->Length() + iNewLeafName->Length() + 1; // +1 for heirarchy separator
+ newFullPath = HBufC16::NewL(buffLength);
+ newFullPath->Des().Format(
+ KFullPathFormat,
+ newPath,
+ &(iImapSettings.PathSeparator()),
+ iNewLeafName);
+ CleanupStack::PopAndDestroy(newPath);
+ CleanupStack::PushL(newFullPath);
+ }
+ else
+ {
+ // folder is at root level
+ CleanupStack::PopAndDestroy(newPath);
+ newFullPath = iNewLeafName->AllocLC();
+ }
+
+ // Get the full path name for the folder's current name.
+ TDesC& oldPath = folder->FullFolderPathL();
+
+ // issue the command to the remote server.
+ iSession->RenameL(iStatus, oldPath, newFullPath->Des());
+ SetActive();
+ CleanupStack::PopAndDestroy(newFullPath);
+ iProgressState = TImap4GenericProgress::EBusy;
+ iNextStep = EProcessRenameResponse;
+ }
+
+/*
+Special handling of rename is required for the INBOX as this has special
+behaviour as specified in RFC 3501:
+Renaming INBOX is permitted, and has special behavior. It moves all
+messages in INBOX to a new mailbox with the given name, leaving INBOX empty.
+
+*/
+void CImapCompoundRename::RenameInboxL()
+ {
+ // create a new folder with the new name, a copy of the inbox
+ SetEntryL(iFolderId);
+
+ // Selection of messages
+ delete iMsgselection;
+ iMsgselection = NULL;
+ CMsvEntrySelection* iMsgselection = new (ELeave) CMsvEntrySelection;
+
+ // Children of original inbox
+ CMsvEntrySelection* children=new (ELeave) CMsvEntrySelection;
+ CleanupStack::PushL(children);
+
+ User::LeaveIfError(iServerEntry.GetChildren(*children));
+
+ // create the new mailbox
+ TMsvEmailEntry entry = iServerEntry.Entry();
+ SetEntryL(entry.Parent());
+ entry.iDetails.Set(*iNewLeafName);
+ iServerEntry.CreateEntry(entry);
+ iNewFolderId = entry.Id();
+
+ // create a reduced children list - just the messages in Inbox
+ TInt count = children->Count();
+ for (TInt child = 0; child < count; ++child)
+ {
+ SetEntryL((*children)[child]);
+ TMsvEntry entry = iServerEntry.Entry();
+ if (entry.iType==KUidMsvMessageEntry)
+ {
+ iMsgselection->AppendL(entry.Id());
+ }
+ }
+ CleanupStack::PopAndDestroy(children);
+
+ // reset the server entry context
+ SetEntryL(iFolderId);
+ if (iMsgselection->Count()>0)
+ {
+ // move children messages to the newly created folder
+ iServerEntry.MoveEntriesL(*iMsgselection, iNewFolderId, iStatus);
+ SetActive();
+ }
+ else
+ {
+ // inbox was empty - nothing to move
+ CompleteSelf();
+ }
+ }
+
+
+/**
+Populates the compound progress object with progress information for the
+rename compound operation.
+
+@param aCompoundProgress the compound progress object to be populated.
+*/
+void CImapCompoundRename::Progress(TImap4CompoundProgress& aCompoundProgress)
+ {
+ // rename does not set iOperation, it just sets iState to EBusy
+ // when doing the renaming
+ aCompoundProgress.iGenericProgress.iState = iProgressState;
+
+ // Put error into progress buffer
+ if( aCompoundProgress.iGenericProgress.iErrorCode == KErrNone )
+ {
+ aCompoundProgress.iGenericProgress.iErrorCode = iProgressErrorCode;
+ }
+ }
+
+/**
+Handles server error responses according to current step
+
+@return TInt error code for completion (if error fatal)
+*/
+TInt CImapCompoundRename::ProcessNegativeServerResponse()
+ {
+ switch(iCurrentStep)
+ {
+ case ERename:
+ {
+ iProgressErrorCode = KErrNotSupported;
+ iNextStep = EFinished;
+ break;
+ }
+ case ESyncSubscriptions:
+ {
+ // subscribe failing is not serious - silently ignore it
+ iNextStep = EFinished;
+ break;
+ }
+ case EFinished:
+ case ESuspendedForMigrate:
+ default:
+ {
+ // positive error code not expected,
+ // self-completed states or no outstanding request.
+ TImapServerPanic::ImapPanic(TImapServerPanic::ERenameCompoundUnexpectedState);
+ break;
+ }
+ }
+ return KErrNone;
+ }
+
+
+/**
+Resumes the operation following a migration.
+*/
+void CImapCompoundRename::ResumeOperationL(TRequestStatus& aStatus, CImapSession& aSession)
+ {
+ iSession = &aSession;
+ __LOG_TEXT(iSession->LogId(), "CImapCompoundRename::Resuming");
+ __ASSERT_DEBUG(iCurrentStep==ESuspendedForMigrate, TImapServerPanic::ImapPanic(TImapServerPanic::ERenameCompoundUnexpectedState));
+ iStopForMigrate = EFalse;
+
+ switch (iNextStep)
+ {
+ case ERename:
+ case ESyncSubscriptions:
+ {
+ // restart the state machine.
+ CompleteSelf();
+ break;
+ }
+ case EProcessRenameResponse:
+ {
+ // resume move of messages to renamed inbox..
+ // 1st, renew selection of children of original inbox
+ SetEntryL(iFolderId);
+ CMsvEntrySelection* children=new (ELeave) CMsvEntrySelection;
+ CleanupStack::PushL(children);
+ User::LeaveIfError(iServerEntry.GetChildren(*children));
+
+ // 2nd, create a reduced list - just the messages entries
+ delete iMsgselection;
+ iMsgselection = NULL;
+ CMsvEntrySelection* iMsgselection = new (ELeave) CMsvEntrySelection;
+ TInt count = children->Count();
+ for (TInt child = 0; child < count; ++child)
+ {
+ SetEntryL((*children)[child]);
+ TMsvEntry entry = iServerEntry.Entry();
+ if (entry.iType==KUidMsvMessageEntry)
+ {
+ iMsgselection->AppendL(entry.Id());
+ }
+ }
+ CleanupStack::PopAndDestroy(children);
+
+ // reset the server entry context
+ SetEntryL(iFolderId);
+ if (iMsgselection->Count()>0)
+ {
+ // move children messages to the newly created folder
+ iServerEntry.MoveEntriesL(*iMsgselection, iNewFolderId, iStatus);
+ SetActive();
+ }
+ else
+ {
+ // inbox was empty - nothing left to move
+ CompleteSelf();
+ }
+ break;
+ }
+ case EFinished:
+ default:
+ {
+ __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ERenameCompoundUnexpectedState));
+ // abandon the compound operation
+ iNextStep=EFinished;
+ CompleteSelf();
+ break;
+ }
+ } // end switch (iNextStep)
+ Queue(aStatus);
+ }