email/imap4mtm/imapservermtm/src/cimap4servermtm.cpp
changeset 0 72b543305e3a
child 25 fa1df4b99609
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/imap4mtm/imapservermtm/src/cimap4servermtm.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,1265 @@
+// 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 "cimap4servermtm.h"
+#include "cimapofflinecontrol.h"
+#include "cimapprotocolcontroller.h"
+#include "cimaplogger.h"
+
+#include <e32std.h>
+#include <imapset.h>
+#include <imapcmds.h>
+#include <mentact.h>
+#include <msventry.h>	// CMsvServerEntry
+#include <tmsvsystemprogress.h>
+
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)
+#include "cimapupsresponsewaiter.h"
+#endif 
+
+#ifdef __IMAP_LOGGING
+_LIT(KDefaultLogFileName, "imaplog");
+#endif //__IMAP_LOGGING
+
+/**
+Static constructor for the server MTM. The server MTMs default constructor is 
+private hence this method must be called to instantiate the class.
+
+@param aRegisteredMtmDll
+@param aEntry
+@return A pointer to the newly created CImap4ServerMtm object. The caller is 
+responsible for cleanup
+*/
+EXPORT_C CImap4ServerMtm* CImap4ServerMtm::NewL( CRegisteredMtmDll& aRegisteredMtmDll,
+										   		 CMsvServerEntry* aEntry )
+	{
+	CImap4ServerMtm* self = new ( ELeave ) CImap4ServerMtm( aRegisteredMtmDll, 
+															aEntry );		
+	CleanupStack::PushL( self );
+	self->ConstructL();
+	CleanupStack::Pop( self );
+	return self;
+	
+	}
+
+/**
+CImap4ServerMtm constructor
+
+@param aRegisteredMtmDll
+@param aEntry
+*/
+CImap4ServerMtm::CImap4ServerMtm( CRegisteredMtmDll& aRegisteredMtmDll, 
+								  CMsvServerEntry* aServerEntry) :
+ CBaseServerMtm( aRegisteredMtmDll, aServerEntry ), iServerEntry (aServerEntry)
+	{
+	
+	}
+
+/**
+CImap4SeverMtm destructor
+
+*/	
+CImap4ServerMtm::~CImap4ServerMtm()
+	{
+	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::~CImap4ServerMtm()");
+	Cancel();
+
+	delete iImapProtocolController;
+	delete iImapOfflineControl;
+	__LOG_CLOSE(KDefaultLog);
+	CImapUtils::Delete();
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)
+	delete iWaiter;
+#endif		
+	}
+
+/**
+CImap4ServerMtm second phase constructor
+*/	
+void CImap4ServerMtm::ConstructL()
+	{
+	CImapUtils::CreateL();
+	__LOG_CREATE_DEFAULT(KDefaultLogFileName);
+
+	iImapOfflineControl = CImapOfflineControl::NewL( *iServerEntry );
+	iImapProtocolController = CImapProtocolController::NewL( *iServerEntry , *iImapOfflineControl);
+	
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)
+	iWaiter = CImapUpsResponseWaiter::NewL(*iServerEntry, *iImapProtocolController);
+	iHasCapability = ETrue;
+#endif	
+	// We need to see invisible entries
+	TMsvSelectionOrdering invisible;
+	invisible=iServerEntry->Sort();
+	invisible.SetShowInvisibleEntries(ETrue);
+	iServerEntry->SetSort(invisible);
+	// Add us to the scheduler
+	CActiveScheduler::Add( this );
+	}
+
+/**
+This command is used to copy a message to a local folder. This may be 
+the local mirror of the IMAP folder, i.e. the local representation of 
+the online IMAP folder as it exists on the remote server, or a local 
+service folder (eg the global inbox) in which case the message is fetched 
+completely to the mirror folder then copied to the destination folder. 
+This command results in an entire message being fetched, as it fetches 
+according to the default parameters, which are:
+
+iTotalSizeLimit = KMaxTInt
+iBodyTextSizeLimit = KMaxTInt
+iAttachmentSizeLimit = KMaxTInt
+iGetMailBodyParts = EGetImap4EmailBodyTextAndAttachments	 
+iPartialMailOptions = ENoSizeLimits
+iDestinationFolder = <target destination>
+
+This command can result in additional IMAP sessions being instantiated. 
+As it is a "read only" operation (ie, the contents of the remote server 
+are not updated as a result of performing this operation) it is safe to 
+perform such fetches while a background sync is in progress.
+
+@param aSelection The selection of message TMsvIds that are copied.
+@param aDestination The TMsvId of the destination folder.
+@param aStatus The request status to be completed when the operation has finished.
+*/
+void CImap4ServerMtm::CopyToLocalL( const CMsvEntrySelection& aSelection, 
+				  					TMsvId aDestination, 
+				  					TRequestStatus& aStatus )
+	{
+	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::CopyToLocalL()");
+
+	if (!iImapProtocolController->Connected())
+		{
+		iState = EMtmStateOffLineCopyToLocal;
+		CImapOfflineControl::TImap4OpType opType=CImapOfflineControl::EImap4OpCopyToLocal;
+		iImapOfflineControl->StoreOfflineCommandL(opType, aSelection, aDestination, iStatus);
+		}
+	else
+		{
+		iState = EMtmStateCopyToLocal;
+		iImapProtocolController->CopyToLocalL(iStatus, aSelection, aDestination);
+		}	
+	Queue(aStatus);
+	SetActive();										   
+	}
+
+/**
+CopyFromLocalL appends entire messages in the mirror to the server.
+
+@param aSelection The selection of message TMsvIds that are copied.
+@param aDestination The TMsvId of the destination folder.
+@param aStatus The request status to be completed when the operation has finished.
+*/
+void CImap4ServerMtm::CopyFromLocalL( const CMsvEntrySelection& aSelection,
+								      TMsvId aDestination, 
+				   					  TRequestStatus& aStatus )
+	{
+	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::CopyFromLocalL()");
+	
+	// Check the destination folder is a mailbox
+	User::LeaveIfError(iServerEntry->SetEntry(aDestination));
+	TMsvEmailEntry destination = iServerEntry->Entry();
+	if (!destination.Mailbox())
+		{
+		Queue(aStatus);
+		User::RequestComplete(iRequest, KErrNotSupported);
+		return;
+		}
+
+	if (!iImapProtocolController->Connected())
+		{
+		iState = EMtmStateOffLineCopyFromLocal;
+		CImapOfflineControl::TImap4OpType opType=CImapOfflineControl::EImap4OpCopyFromLocal;
+		iImapOfflineControl->StoreOfflineCommandL(opType, aSelection, aDestination, iStatus);
+		}
+	else
+		{
+		iState = EMtmStateCopyFromLocal;
+		iImapProtocolController->CopyFromLocalL(iStatus, aSelection, aDestination);
+		}	
+	Queue(aStatus);
+	SetActive();
+	}
+
+/**
+CopyWithinService copies entire messages to other folders on the server, 
+using the IMAP COPY command.
+
+@param aSelection The selection of message TMsvIds that are copied.
+@param aDestination The TMsvId of the destination folder.
+@param aStatus The request status to be completed when the operation has finished.
+*/
+void CImap4ServerMtm::CopyWithinServiceL( const CMsvEntrySelection& aSelection, 
+							 			  TMsvId aDestination, 
+							 			  TRequestStatus& aStatus )
+	{
+	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::CopyWithinServiceL()");
+	
+	// Check the destination folder is a mailbox
+	User::LeaveIfError(iServerEntry->SetEntry(aDestination));
+	TMsvEmailEntry destination = iServerEntry->Entry();
+	if (!destination.Mailbox())
+		{
+		Queue(aStatus);
+		User::RequestComplete(iRequest, KErrNotSupported);
+		return;
+		}
+
+	if (!iImapProtocolController->Connected())
+		{
+		iState = EMtmStateOffLineCopyWithinService;
+		CImapOfflineControl::TImap4OpType opType=CImapOfflineControl::EImap4OpCopyWithinService;
+		iImapOfflineControl->StoreOfflineCommandL(opType, aSelection, aDestination, iStatus);
+		}
+	else
+		{
+		iState = EMtmStateCopyWithinService;
+		iImapProtocolController->CopyWithinServiceL(iStatus, aSelection, aDestination);
+		}	
+	Queue(aStatus);
+	SetActive();
+	}
+
+/**
+MoveToLocalL moves a message from the remote server to a local folder: this 
+is performed as a fetch, then a delete on the remote server.
+
+@param aSelection The selection of message TMsvIds that are moved.
+@param aDestination The TMsvId of the destination folder.
+@param aStatus The request status to be completed when the operation has finished.
+*/
+void CImap4ServerMtm::MoveToLocalL( const CMsvEntrySelection& aSelection, 
+				  	   			    TMsvId aDestination, 
+				  	   				TRequestStatus& aStatus )
+	{
+	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::MoveToLocalL()");
+
+	if (!iImapProtocolController->Connected())
+		{
+		iState = EMtmStateOffLineMoveToLocal;
+		CImapOfflineControl::TImap4OpType opType=CImapOfflineControl::EImap4OpMoveToLocal;
+		iImapOfflineControl->StoreOfflineCommandL(opType, aSelection, aDestination, iStatus);
+		}
+	else
+		{
+		iState = EMtmStateMoveToLocal;
+		iImapProtocolController->MoveToLocalL(iStatus, aSelection, aDestination);
+		}	
+	Queue(aStatus);
+	SetActive();
+	}
+
+/**
+MoveFromLocalL does a CopyFromLocal (ie, IMAP APPEND of the message), then 
+deletes the local message if it was sucessful.
+
+@param aSelection The selection of message TMsvIds that are moved.
+@param aDestination The TMsvId of the destination folder.
+@param aStatus The request status to be completed when the operation has finished.
+*/
+void CImap4ServerMtm::MoveFromLocalL( const CMsvEntrySelection& aSelection,
+						 			  TMsvId aDestination, 
+						 			  TRequestStatus& aStatus )
+	{
+	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::MoveFromLocalL()");
+	
+	// Check the destination folder is a mailbox
+	User::LeaveIfError(iServerEntry->SetEntry(aDestination));
+	TMsvEmailEntry destination = iServerEntry->Entry();
+	if (!destination.Mailbox())
+		{
+		Queue(aStatus);
+		User::RequestComplete(iRequest, KErrNotSupported);
+		return;
+		}
+
+	if (!iImapProtocolController->Connected())
+		{
+		iState = EMtmStateOffLineMoveFromLocal;
+		CImapOfflineControl::TImap4OpType opType=CImapOfflineControl::EImap4OpMoveFromLocal;
+		iImapOfflineControl->StoreOfflineCommandL(opType, aSelection, aDestination, iStatus);
+		}
+	else
+		{
+		iState = EMtmStateMoveFromLocal;
+		iImapProtocolController->MoveFromLocalL(iStatus, aSelection, aDestination);
+		}	
+	Queue(aStatus);
+	SetActive();
+	}
+
+/**
+MoveWithinServiceL copies entire messages to other folders on the server, 
+using the IMAP COPY command. If the copy was successful, the original is deleted, 
+resulting in a move operation having been performed.
+
+@param aSelection The selection of message TMsvIds that are moved.
+@param aDestination The TMsvId of the destination folder.
+@param aStatus The request status to be completed when the operation has finished.
+*/
+void CImap4ServerMtm::MoveWithinServiceL( const CMsvEntrySelection& aSelection, 
+										  TMsvId aDestination, 
+										  TRequestStatus& aStatus )
+	{
+	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::MoveWithinServiceL()");
+	
+	// Check the destination folder is a mailbox
+	User::LeaveIfError(iServerEntry->SetEntry(aDestination));
+	TMsvEmailEntry destination = iServerEntry->Entry();
+	if (!destination.Mailbox())
+		{
+		Queue(aStatus);
+		User::RequestComplete(iRequest, KErrNotSupported);
+		return;
+		}
+
+	if (!iImapProtocolController->Connected())
+		{
+		iState = EMtmStateOffLineMoveWithinService;
+		CImapOfflineControl::TImap4OpType opType=CImapOfflineControl::EImap4OpMoveWithinService;
+		iImapOfflineControl->StoreOfflineCommandL(opType, aSelection, aDestination, iStatus);
+		}
+	else
+		{
+		iState = EMtmStateMoveWithinService;
+		iImapProtocolController->MoveWithinServiceL(iStatus, aSelection, aDestination);
+		}	
+	Queue(aStatus);
+	SetActive();
+	}
+
+/**
+Can be used to delete a selection of messages, a selection of folders, or 
+local parts of a message. The intention of the user is inferred from the
+entry type at location 0 of the passed CMsvEntrySelection.
+
+@param aSelection
+@param aStatus
+*/
+void CImap4ServerMtm::DeleteAllL( const CMsvEntrySelection& aSelection, 
+								  TRequestStatus& aStatus )
+	{
+	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::DeleteAllL()");
+	
+	if (PruneMessages(aSelection, aStatus))
+		{
+		// Was this call to DeleteAllL an instruction to delete local parts of a message ?
+		// If so then we don't need to continue. PruneMessages will complete the outstanding request.
+		return;
+		}
+	
+	// Not a prune of local messages. 
+	// Is the first entry a message or a folder?			
+	User::LeaveIfError(iServerEntry->SetEntry(aSelection[0]));
+
+	if (iServerEntry->Entry().iType==KUidMsvMessageEntry)
+		{
+		if (!iImapProtocolController->Connected())
+			{
+			iState = EMtmStateOffLineDelete;
+			CImapOfflineControl::TImap4OpType opType=CImapOfflineControl::EImap4OpDelete;
+			iImapOfflineControl->StoreOfflineCommandL(opType, aSelection,  KMsvNullIndexEntryId, iStatus);
+			}
+		else
+			{
+			iState = EMtmStateDelete;
+			iImapProtocolController->DeleteL(iStatus, aSelection);
+			}
+		}
+	else if (iServerEntry->Entry().iType==KUidMsvFolderEntry)
+		{
+		// no offline support for delete folder
+		if (!iImapProtocolController->Connected())
+			{
+			Queue(aStatus);
+			User::RequestComplete(iRequest, KErrDisconnected);
+			return;
+			}
+		else
+			{
+			iState = EMtmStateDeleteFolder;
+			iImapProtocolController->DeleteFolderL(iStatus, aSelection);
+			}
+		}
+	else
+		{
+		TImapServerPanic::ImapPanic(TImapServerPanic::EDeleteOfUnknownType);
+		}
+	Queue(aStatus);
+	SetActive();
+	}
+
+/**
+Create: make a folder or mailbox.
+Only supported when online.
+
+@param aNewEntry The Details of the folder or mailbox to be created.
+@param aStatus The request status to be completed when the operation has finished.
+*/
+void CImap4ServerMtm::CreateL( TMsvEntry aNewEntry,
+							   TRequestStatus& aStatus )
+	{
+	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::CreateL()");
+	
+	// Creating a folder?
+	if (aNewEntry.iType!=KUidMsvFolderEntry)
+		{
+		// No - illegal op
+		Queue(aStatus);
+		User::RequestComplete(iRequest, KErrNotSupported);
+		return;
+		}
+
+	// Not supported as an offline operation
+	if (!iImapProtocolController->Connected())
+		{
+		Queue(aStatus);
+		User::RequestComplete(iRequest, KErrDisconnected);
+		return;
+		}
+
+	TBool isfolder = ETrue;
+	if ( ((TMsvEmailEntry)aNewEntry).Mailbox() )
+		{
+		isfolder = EFalse;
+		}
+		
+	iImapProtocolController->CreateL(iStatus, aNewEntry.Parent(), aNewEntry.iDetails, isfolder);
+	iState = EMtmStateCreateFolder;
+	Queue(aStatus);
+	SetActive();
+	}
+	
+/**
+This command has been recinded, and is replaced by IMAP Client MTM command 
+KIMAP4MTMRenameFolder
+
+This command has been recinded,
+and is replaced by IMAP Client MTM command KIMAP4MTMRenameFolder
+		 
+@param aNewEntry The Details of the folder or mailbox to be renamed.
+@param aStatus The request status to be completed when the operation has finished.
+*/
+void CImap4ServerMtm::ChangeL( TMsvEntry aNewEntry, TRequestStatus& aStatus ) 
+	{
+	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::ChangeL()");
+	
+	TMsvEmailEntry origEntry = iServerEntry->Entry();
+	TMsvEntry *newEntry1 = new (ELeave) TMsvEntry;
+	CleanupStack::PushL(newEntry1);
+	TMsvEmailEntry newEntry = (TMsvEmailEntry) aNewEntry;
+	TMsvEmailEntry *newEntry2= new (ELeave) TMsvEmailEntry();
+	CleanupStack::PushL(newEntry2);
+	
+	if(origEntry.Id() != aNewEntry.Id())
+		{
+		iServerEntry->GetEntryFromId(aNewEntry.Id(), newEntry1);
+		newEntry2 = (TMsvEmailEntry*) newEntry1;
+		origEntry = newEntry;
+		}
+	else
+		{
+		newEntry2 = &newEntry;
+		}
+	TBool flagChanged = EFalse;
+	if((origEntry.SeenIMAP4Flag()!= newEntry2->SeenIMAP4Flag())
+	 || (origEntry.UnreadIMAP4Flag()!=newEntry2->UnreadIMAP4Flag())
+	 || (origEntry.Unread()!=newEntry2->Unread())
+	 || (origEntry.New()!=newEntry2->New()))
+		{	
+		flagChanged = ETrue;
+		}
+		
+	User::LeaveIfError(iServerEntry->SetEntry(aNewEntry.Id()));
+	User::LeaveIfError(iServerEntry->ChangeEntry(aNewEntry));
+
+	//if not offline
+	if (iImapProtocolController->Connected() && flagChanged)
+		{
+		//to update read/unread flags at remote server
+		iImapProtocolController->UpdateFlagL(iStatus);	
+  	 	iState= EMtmStateUpdateFlag;
+  	 	Queue(aStatus);
+		SetActive();	
+		}
+	else
+		{
+		Queue(aStatus);
+		User::RequestComplete(iRequest, KErrNone);
+		}
+	CleanupStack::PopAndDestroy(2);
+	}
+
+
+/**
+Performs the IMAP specific MTM commands.
+
+@param aSelection A selection of messages. The use is dependant upon 
+	   the command specified by aFunctionID. 
+@param aCommand Specifies which operation to perform e.g. connect, copy new mail 
+				etc. The specific operations are defined by the TImap4Cmds enumeration. 
+@param aParameter Use is dependant upon the command specified by aFunctionID 
+@param aStatus The request status to be completed when the operation has finished.
+*/
+void CImap4ServerMtm::StartCommandL( CMsvEntrySelection& aSelection, 
+					    			 TInt aCommand, 
+									 const TDesC8& aParameter, 
+									 TRequestStatus& aStatus )
+	{
+    const TBool connected = iImapProtocolController->Connected();
+    __LOG_FORMAT((KDefaultLog, "CImap4ServerMtm::StartCommandL() - iStatus: %d, iState: %d, aCommand: %d, connected: %d", iStatus.Int(), iState, aCommand, connected));
+	
+	// Complete now if offline and no offline support for requested command
+	if (!connected)
+		{
+		if ( aCommand == KIMAP4MTMSynchronise  || aCommand == KIMAP4MTMFullSync ||
+		 	 aCommand == KIMAP4MTMInboxNewSync || aCommand == KIMAP4MTMFolderFullSync ||
+		 	 aCommand == KIMAP4MTMRenameFolder || aCommand == KIMAP4MTMDisconnect ||
+			 aCommand == KIMAP4MTMSyncTree     || aCommand == KIMAP4MTMSelect ||
+			 aCommand == KIMAP4MTMIsConnected )
+			{
+			__LOG_TEXT(KDefaultLog, "     - Online only command requested.");
+			Queue(aStatus);
+			User::RequestComplete(iRequest, KErrDisconnected);
+			return;
+			}
+		}
+		
+	// change to false if an asynchronous request is issued.
+    TBool complete = ETrue;
+    iState = EMtmStateMiscCommand;
+    
+    switch (aCommand)
+		{
+	case KIMAP4MTMIsConnected:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMIsConnected");
+		// Completes the requesting client immediately with KErrNone (when a
+		// session is connected) or KErrDisconnected otherwise.
+		if (iImapProtocolController->Connected())
+			{
+			Queue(aStatus);
+			User::RequestComplete(iRequest, KErrNone);
+			}
+		else
+			{
+			Queue(aStatus);
+			User::RequestComplete(iRequest, KErrDisconnected);
+			}
+		
+		break;
+		}
+		
+	case KIMAP4MTMBusy:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMBusy");
+		// Completes immediately with KErrServerBusy if a background sync is
+		// in progress or second session is busy (this implies that all connected
+		// sessions are busy).
+		if (iImapProtocolController->Busy())
+			{
+			Queue(aStatus);
+			User::RequestComplete(iRequest, KErrServerBusy);
+			}
+		else
+			{
+			Queue(aStatus);
+			User::RequestComplete(iRequest, KErrNone);
+			}
+		break;	
+		}
+
+	case KIMAP4MTMConnect:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMConnect");
+		// Completes with KErrServerBusy if already connected, otherwise 
+		// establishes a connected IMAP session.
+		if (iImapProtocolController->Connected())
+			{
+			Queue(aStatus);
+			User::RequestComplete(iRequest, KErrServerBusy);
+			}
+		else
+			{
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)			
+			iState = EMtmStateAuthoriseConnection;
+			iWaiter->AuthoriseAndConnectL(aSelection, aCommand, iClientThreadId, iHasCapability, iStatus);
+#else			
+			iImapProtocolController->ConnectL(iStatus, aSelection);
+			iState = EMtmStatePrimaryConnect;
+#endif			
+			complete = EFalse;
+			}
+		break;
+		}
+		
+	case KIMAP4MTMConnectAndSynchronise:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMConnectAndSynchronise");
+		// Completes with KErrServerBusy if already connected, otherwise
+		// establishes a connected IMAP session.
+
+		// The Protocol Controller will start the synchronise operation as
+		// a background operation on completion of the connect operation.
+		
+		if (iImapProtocolController->Connected())
+			{
+			Queue(aStatus);
+			User::RequestComplete(iRequest, KErrServerBusy);
+			}
+		else
+			{
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)			
+			iState = EMtmStateAuthoriseConnection;
+			iWaiter->AuthoriseAndConnectL(aSelection, aCommand, iClientThreadId, iHasCapability, iStatus);
+#else			
+			iImapProtocolController->ConnectAndSynchroniseL(iStatus, aSelection);
+#endif			
+			complete = EFalse;
+			}
+		break;
+		}
+
+	case KIMAP4MTMStartBatch:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMStartBatch");
+		// Batch mode enables the client to declare that it is about to send a
+		// sequence of commands. While in batch mode the IMAP Server MTM ensures
+		// that it remains loaded by returning ETrue when the CommandExpected()
+		// function is called by the messaging server.
+		iBatchInProgress = ETrue;
+		Queue(aStatus);
+		User::RequestComplete(iRequest, KErrNone);
+		break;
+		}
+		
+	case KIMAP4MTMEndBatch:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMEndBatch");
+		iBatchInProgress = EFalse;
+		Queue(aStatus);
+		User::RequestComplete(iRequest, KErrNone);
+		break;
+		}
+		
+	case KIMAP4MTMCancelBackgroundSynchronise :
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMCancelBackgroundSynchronise");
+		// Enables the client to cancel an ongoing background synchronisation
+		// process.
+		iImapProtocolController->CancelBackgroundSync(iStatus);
+		complete = EFalse;
+		break;
+		}
+		
+	case KIMAP4MTMSelect:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMSelect");
+		// This command allows the client to issue a SELECT command for the
+		// specified mailbox.
+		iImapProtocolController->SelectL(iStatus, aSelection[0]);
+		complete = EFalse;
+		break;
+		}
+		
+	case KIMAP4MTMSynchronise:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMSynchronise");
+		// Synchronises the currently selected folder. 
+		iImapProtocolController->FullSyncSelectedFolderL(iStatus);
+		complete = EFalse;
+		break;
+		}
+		
+	case KIMAP4MTMSyncTree:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMSyncTree");
+		// Synchronises the remote and mirror folder tree. This operation is
+		// handled by the Sync Manager, via method
+		// CImapProtocolController::SynchroniseTreeL()
+		iImapProtocolController->SynchroniseTreeL(iStatus);
+		complete = EFalse;
+		break;
+		}
+		
+	case KIMAP4MTMLocalSubscribe:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMLocalSubscribe");
+		// Completes synchronously, having set the subscribed flag on the TMsvEntry 
+		// identified.
+		TInt err = iImapProtocolController->SetLocalSubscription(aSelection[0], ETrue);
+		Queue(aStatus);
+		User::RequestComplete(iRequest, err);
+		break;
+		}
+		
+	case KIMAP4MTMLocalUnsubscribe:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMLocalUnsubscribe");
+		// Completes synchronously, having cleared the subscribed flag for the
+		// specified folder. Also sets the invisible flag for the folder if it
+		// contains no subscribed subfolders, and the parent folder with the
+		// same test.
+		TInt err = iImapProtocolController->SetLocalSubscription(aSelection[0], EFalse);
+		Queue(aStatus);
+		User::RequestComplete(iRequest, err);
+		break;
+		}
+		
+	case KIMAP4MTMFullSync:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMFullSync");
+		// This command initiates a full foreground sync. Cannot be performed if a 
+		// background sync is already in progress. The steps performed to perform
+		// the complete synchronisation process is managed by the IMAP Sync Manager, 
+		// CImapSyncManager::SynchroniseL(), via a call to 
+		// CProtocolController::SynchroniseAllL()
+		iState = EMtmStateForegroundSync;
+		iImapProtocolController->SynchroniseAllL( iStatus );
+		complete = EFalse;
+		break;
+		}
+		
+	case KIMAP4MTMPopulate:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMPopulate");
+		// This command is used to fetch specified parts of the identified
+		// message to the local mirror folder. The parts to fetch and body size
+		// limits are specified by a passed TImImap4GetPartialMailInfo object
+		TImImap4GetPartialMailInfo imap4GetPartialMailInfo;	
+ 		if (aParameter.Length() > 0)
+			{
+			TPckgC<TImImap4GetPartialMailInfo> paramPartialPack(imap4GetPartialMailInfo);
+			paramPartialPack.Set(aParameter);
+			imap4GetPartialMailInfo = paramPartialPack();
+			}
+			
+		if (!iImapProtocolController->Connected())
+			{
+			iState = EMtmStateOffLinePopulate;
+			CImapOfflineControl::TImap4OpType opType=CImapOfflineControl::EImap4OpPopulate;
+			TPckgBuf<TImImap4GetPartialMailInfo> package(imap4GetPartialMailInfo);
+            iImapOfflineControl->StoreOfflineCommandL(opType, aSelection, KMsvNullIndexEntryId, package, iStatus);
+            complete = EFalse;
+			}
+		else
+			{
+			iState = EMtmStatePopulate;
+	        __LOG_FORMAT((KDefaultLog, "     -   pre-iStatus: %d", iStatus.Int()));
+			iImapProtocolController->PopulateL(iStatus, aSelection, imap4GetPartialMailInfo);
+	        __LOG_FORMAT((KDefaultLog, "     -  post-iStatus: %d", iStatus.Int()));
+			complete = EFalse;
+			}	
+		break;
+		}
+		
+	case KIMAP4MTMInboxNewSync:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMInboxNewSync");
+		// Performs synchronisation of messages received by the remote server
+		// Inbox since the last synchronisation operation.
+		iState = EMtmStateForegroundSync;
+		iImapProtocolController->NewOnlySyncFolderL( iStatus );
+		complete = EFalse;			
+		break;
+		}
+		
+	case KIMAP4MTMFolderFullSync:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMFolderFullSync");
+		// Performs a full synchronisation of the folder identified in aSelection[0].
+		iState = EMtmStateForegroundSync;
+		iImapProtocolController->FullSyncFolderL(iStatus, aSelection[0]);
+		complete = EFalse;
+		break;
+		}
+		
+	case KIMAP4MTMWaitForBackground:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMWaitForBackground");
+		// This effectively makes a background sync a foreground operation, by 
+		// requesting completion when an outstanding background synchronisation 
+		// operation completes. 
+		// If no background sync is in progress, this completes immediately with 
+		// KErrNone.
+		iState = EMtmStateWaitingForBackgroundToFinish;
+		iImapProtocolController->WaitForBackground(iStatus);
+		complete = EFalse;
+		break;
+		}
+		
+	case KIMAP4MTMDisconnect:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMDisconnect");
+		// Issues a disconnect to all connected IMAP sessions.
+		// If service settings specify synchronise deletes on disconnect, 
+		// CImapSyncManager::SynchroniseDeletes() is called to perform outstanding 
+		// offline delete operations.
+		iImapProtocolController->DisconnectL(iStatus);
+		complete = EFalse;
+		break;
+		}
+		
+	case KIMAP4MTMRenameFolder:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMRenameFolder");
+		// Renames the remote IMAP folder.
+
+		// extract the new folder name
+		TImap4RenameFolder cmd;
+		TPckgC<TImap4RenameFolder> package(cmd);
+		package.Set(aParameter);
+		TImap4RenameFolder rename = package();
+
+		// call the rename command
+		iImapProtocolController->RenameL(iStatus, aSelection[0], rename.iNewName);
+		iState = EMtmStateRenameFolder;
+		complete = EFalse;
+		break;
+		}
+		
+	case KIMAP4MTMUndeleteAll:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMUndeleteAll");
+		// This command allows the client to undo offline delete operations, and
+		// is handled by the offline command manager
+		// CImap4OffLineControl::StoreOfflineCommandL(). This command removes 
+		// the cause of the delete, ie it removes offline delete operations and 
+		// changes move to copy operations.
+		iState = EMtmStateOffLineUndelete;
+		CImapOfflineControl::TImap4OpType opType=CImapOfflineControl::EImap4OpUndelete;
+		iImapOfflineControl->StoreOfflineCommandL(opType, aSelection,  KMsvNullIndexEntryId, iStatus);
+		complete = EFalse;
+		break;
+		}
+		
+	case KIMAP4MTMCancelOffLineOperations:
+		{
+		__LOG_TEXT(KDefaultLog, "     -  KIMAP4MTMCancelOffLineOperations");
+		// This command allows the client to cancel queued offline operations in
+		// folders specified in aSelection. Note that offline operations that were
+		// performed while offline cannot be undone by this method. The operation
+		// array associated with the folder is emptied, and the offline command
+		// flags in the associated TMsvEmailEntries are cleared.
+		// CImap4OffLineControl::CancelOffLineOperationsL()
+		iImapOfflineControl->CancelOffLineOperationsL(aSelection);
+		Queue(aStatus);
+		User::RequestComplete(iRequest,KErrNone);
+		break;
+		}
+	default :
+		{
+		__LOG_TEXT(KDefaultLog, "     -  Unknown command");
+		Queue(aStatus);
+		User::RequestComplete(iRequest, KErrNotSupported);
+		break;
+		}
+		
+		} // end switch (aCommand)
+
+	if (!complete)
+		{
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+/**
+This is used by the Messaging Server to determine if the MTM can be unloaded.
+
+@return true if further client commands are expected.
+*/
+TBool CImap4ServerMtm::CommandExpected()
+	{
+	// ...basically, when we're disconnected we can be deleted
+	TBool connected = iImapProtocolController->Connected();
+
+	__LOG_FORMAT((KDefaultLog, "CommandExpected - Conn %d, Batch %d", connected, iBatchInProgress));
+
+	return (connected || iBatchInProgress);
+	}
+
+/**
+Report progress information back to client. Note that if we do a populate whilst 
+background syncing (for example) then this will give details of the populate. 
+If we are only syncing then this will give details about the sync operation.
+
+@return
+*/
+const TDesC8& CImap4ServerMtm::Progress()
+	{
+	TImap4CompoundProgress progress;
+	GetProgress(progress);
+	iProgressBuffer = TImap4ProgressBuf(progress);
+
+	return iProgressBuffer;
+	}
+
+/** 
+Obtain the progress information
+@return TImap4CompoundProgress progress structure.
+*/
+void CImap4ServerMtm::GetProgress(TImap4CompoundProgress& imap4Progress)
+	{
+		// Work out which object to get the progress from.
+	TBool useOffline = EFalse;
+	switch (iState)
+		{
+		case EMtmStateOffLineDelete:
+		case EMtmStateOffLineUndelete:
+		case EMtmStateOffLineCopyToLocal:
+		case EMtmStateOffLineMoveToLocal:
+		case EMtmStateOffLineCopyFromLocal:
+		case EMtmStateOffLineMoveFromLocal:
+		case EMtmStateOffLineCopyWithinService:
+		case EMtmStateOffLineMoveWithinService:
+		case EMtmStateOffLinePopulate:
+			{
+			useOffline = ETrue;
+			break;
+			}
+		case EMtmStateIdle:
+			{
+			useOffline = iLastOpWasOffline;
+			break;
+			}	
+		case EMtmStateCopyToLocal:
+		case EMtmStateCopyFromLocal:
+		case EMtmStateCopyWithinService:
+		case EMtmStateMoveToLocal:
+		case EMtmStateMoveFromLocal:
+		case EMtmStateMoveWithinService:
+		case EMtmStateDelete:
+		case EMtmStateDeleteFolder:
+		case EMtmStateCreateFolder:
+		case EMtmStateRenameFolder:
+		case EMtmStatePrimaryConnect:
+		case EMtmStateForegroundSync:
+		case EMtmStateMiscCommand:
+		case EMtmStateWaitingForBackgroundToFinish:
+		case EMtmStatePopulate:
+		case EMtmStateAuthoriseConnection:
+		case EMtmStateUpdateFlag:
+		default:
+			{
+			// useOffline remains EFalse
+			break;
+			}
+		} // end of switch (iState)
+	 
+	// Get the progress from the appropriate object
+	imap4Progress = useOffline ? iImapOfflineControl->Progress() 
+												 : iImapProtocolController->Progress();
+
+	__LOG_FORMAT((KDefaultLog, "Progress: GT %d GO %d GS %d GE %d SS %d SE %d",
+	              imap4Progress.iGenericProgress.iType,
+	              imap4Progress.iGenericProgress.iOperation,
+	              imap4Progress.iGenericProgress.iState,
+	              imap4Progress.iGenericProgress.iErrorCode,
+	              imap4Progress.iSyncProgress.iState,
+	              imap4Progress.iSyncProgress.iErrorCode));
+	}
+
+/** 
+This call leads to calling GetProgress() to populate the
+TImap4CompoundProgress structure.
+@param aOutSysProg The TMsvSystemProgress structure to be populated
+*/
+void CImap4ServerMtm::GetSystemProgress(TMsvSystemProgress& aOutSysProg)
+	{
+	TImap4CompoundProgress imap4Progress;
+	GetProgress(imap4Progress);
+	aOutSysProg.iErrorCode = imap4Progress.iGenericProgress.iErrorCode;
+	}
+
+/** 
+The extension method provides a polymorphic behaviour to call the correct
+MTM.
+@param aExtensionId The Uid passed in as KUIDMsgMsvSystemProgress to obtain the
+					System Progress.
+@return KErrNone if GetSystemProgress is called successfully.
+*/
+EXPORT_C TInt CImap4ServerMtm::Extension_(TUint aExtensionId, TAny *&a0, TAny *a1)	
+	{
+	switch(aExtensionId)
+		{
+		case KUIDMsgMsvSystemProgress:
+			{
+			TMsvSystemProgress* systemProgress = reinterpret_cast<TMsvSystemProgress*>(a1);
+			GetSystemProgress(*systemProgress);
+			return KErrNone;
+			}
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)			
+		case KUIDMsgClientThreadInfo:
+			{
+			iClientThreadId = *(TThreadId*) (a1);
+	    	iHasCapability = (TBool)*(TInt*)(a0);
+	    	return KErrNone;
+			}
+#endif
+
+		case KUidMsgNonOperationMtmData:
+			{
+			TNonOperationMtmDataType* mtmDataType = reinterpret_cast<TNonOperationMtmDataType*>(a0);
+			TPtrC8* mtmDataBuffer = reinterpret_cast<TPtrC8*>(a1);
+			return GetNonOperationMtmData(*mtmDataType, *mtmDataBuffer);
+			}
+
+		default:
+			{
+			break;
+			}
+		}
+
+	//Chain to base class
+	return CBaseServerMtm::Extension_(aExtensionId, a0, a1);
+	}
+
+void CImap4ServerMtm::DoCancel()
+	{
+    __LOG_FORMAT( ( KDefaultLog, "CImap4ServerMtm::DoCancel() - START - iState: %d, iRequest: 0x%08x, iStatus: %d, RThread().RequestCount: %d", iState, iRequest, iStatus.Int(), RThread().RequestCount() ) );
+
+	switch (iState)
+		{
+	case EMtmStateCopyToLocal:
+	case EMtmStateCopyFromLocal:
+	case EMtmStateCopyWithinService:
+	case EMtmStateMoveToLocal:
+	case EMtmStateMoveFromLocal:
+	case EMtmStateMoveWithinService:
+	case EMtmStateDelete:
+	case EMtmStateDeleteFolder:
+	case EMtmStateCreateFolder:
+	case EMtmStateRenameFolder:
+	case EMtmStatePrimaryConnect:
+	case EMtmStateForegroundSync:
+	case EMtmStateMiscCommand:
+	case EMtmStateWaitingForBackgroundToFinish:
+	case EMtmStatePopulate:
+	case EMtmStateUpdateFlag:
+		{
+		// online requests done by the protocol controller
+        __LOG_FORMAT( ( KDefaultLog, "CImap4ServerMtm::DoCancel() - about to CancelAndCleanup() - iStatus: %d, RThread().RequestCount: %d", iStatus.Int(), RThread().RequestCount() ) );
+		iImapProtocolController->CancelAndCleanup();
+        __LOG_FORMAT( ( KDefaultLog, "CImap4ServerMtm::DoCancel() - done CancelAndCleanup() - iStatus: %d, RThread().RequestCount: %d", iStatus.Int(), RThread().RequestCount() ) );
+		break;
+		}
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)		
+	case EMtmStateAuthoriseConnection:
+		{
+		iWaiter->Cancel();
+		break;
+		}
+#endif		
+	case EMtmStateOffLineDelete:
+	case EMtmStateOffLineUndelete:
+	case EMtmStateOffLineCopyToLocal:
+	case EMtmStateOffLineMoveToLocal:
+	case EMtmStateOffLineCopyFromLocal:
+	case EMtmStateOffLineMoveFromLocal:
+	case EMtmStateOffLineCopyWithinService:
+	case EMtmStateOffLineMoveWithinService:
+	case EMtmStateOffLinePopulate:
+		{
+		// ofline requests done by the offline controller
+		iImapOfflineControl->Cancel();
+		break;
+		}
+	case EMtmStateIdle:
+	default:
+		{
+		// no outstanding requests to cancel.
+		break;
+		}
+		} // end of switch (iState)
+	
+	if (iRequest)
+		{
+	    __LOG_FORMAT( ( KDefaultLog, "CImap4ServerMtm::DoCancel() - about to complete iRequest: 0x%08x with KErrCancel", iRequest ) );
+		User::RequestComplete(iRequest, KErrCancel);
+		}
+    else
+        {
+    	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::DoCancel() - ** NO REQUEST TO COMPLETE **");
+        }
+
+    __LOG_FORMAT( ( KDefaultLog, "CImap4ServerMtm::DoCancel() - END - iState: %d, iStatus: %d, RThread().RequestCount: %d", iState, iStatus.Int(), RThread().RequestCount() ) );
+	}
+
+/** 
+Called on completion of an asynchronous service request
+*/
+void CImap4ServerMtm::DoRunL()
+	{
+	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::DoRunL()");
+	
+	// Was the last operation offline?
+	switch (iState)
+		{
+		case EMtmStateOffLineDelete:
+		case EMtmStateOffLineUndelete:
+		case EMtmStateOffLineCopyToLocal:
+		case EMtmStateOffLineMoveToLocal:
+		case EMtmStateOffLineCopyFromLocal:
+		case EMtmStateOffLineMoveFromLocal:
+		case EMtmStateOffLineCopyWithinService:
+		case EMtmStateOffLineMoveWithinService:
+		case EMtmStateOffLinePopulate:
+			{
+			iLastOpWasOffline = ETrue;
+			break;
+			}
+		case EMtmStateIdle:
+		case EMtmStateCopyToLocal:
+		case EMtmStateCopyFromLocal:
+		case EMtmStateCopyWithinService:
+		case EMtmStateMoveToLocal:
+		case EMtmStateMoveFromLocal:
+		case EMtmStateMoveWithinService:
+		case EMtmStateDelete:
+		case EMtmStateDeleteFolder:
+		case EMtmStateCreateFolder:
+		case EMtmStateRenameFolder:
+		case EMtmStatePrimaryConnect:
+		case EMtmStateForegroundSync:
+		case EMtmStateMiscCommand:
+		case EMtmStateWaitingForBackgroundToFinish:
+		case EMtmStatePopulate:
+		case EMtmStateAuthoriseConnection:
+		case EMtmStateUpdateFlag:
+		default:
+			{
+			iLastOpWasOffline = EFalse;
+			break;
+			}
+		} // end of switch (iState)
+	
+	iState = EMtmStateIdle;
+	User::RequestComplete(iRequest, iStatus.Int());
+	}
+
+/** 
+Called if a leave occurs in DoRunL
+*/
+void CImap4ServerMtm::DoComplete(TInt aError)
+	{
+	__LOG_TEXT(KDefaultLog, "CImap4ServerMtm::DoComplete()");
+
+	// Park entry
+	iServerEntry->SetEntry(KMsvNullIndexEntryId);
+	if (iRequest)
+		{
+		User::RequestComplete(iRequest, aError);
+		}
+	}
+
+/**
+Call this last when an asynch operation has been requested
+@param aStatus The request status to be completed when the operation has finished.
+*/
+void CImap4ServerMtm::Queue(TRequestStatus& aStatus)
+    {
+	 __ASSERT_DEBUG(iRequest==NULL, User::Invariant());
+
+	aStatus=KRequestPending;
+	iRequest=&aStatus;
+	}
+
+/**
+Removes all message body parts that are in the selection from the message store 
+without removing the headers.
+@param aSelection The selection that is pruned
+*/
+TBool CImap4ServerMtm::PruneMessages(const CMsvEntrySelection& aSelection, TRequestStatus& aStatus)
+	{
+	TInt index = aSelection.Count();
+	
+	// See if the parent of the first entry is a message.
+	// If it is then we need to prune the entries, ie. delete them locally.
+	if (index == 0)
+		{
+		return EFalse;
+		}
+
+	TInt err = iServerEntry->SetEntry(aSelection[0]);
+
+	if (err == KErrNone)
+		{
+		err = iServerEntry->SetEntry(iServerEntry->Entry().Parent());
+		if (KUidMsvMessageEntry != iServerEntry->Entry().iType)
+			{
+			// The parent of the given entry was not a message, so we don't prune it.
+			return EFalse;
+			}
+		}
+
+	while ((index--) && (err==KErrNone))
+		{
+		// Go to the required entry
+		err = iServerEntry->SetEntry(aSelection[index]);
+
+		if (KErrNone == err)
+			{
+			// Go to the parent entry to see if it is a message entry
+			iServerEntry->SetEntry(iServerEntry->Entry().Parent());
+			TMsvEmailEntry entry = iServerEntry->Entry();
+
+			// Clear the complete flag because we are about to delete the child parts.
+			entry.SetComplete(EFalse);
+			entry.SetBodyTextComplete(EFalse);
+			err = iServerEntry->ChangeEntry(entry);
+
+			if (KErrNone == err)
+				{
+				// Delete the body of the message.
+				iServerEntry->DeleteEntry(aSelection[index]);
+				}
+			}
+		}
+
+	Queue(aStatus);
+	User::RequestComplete(iRequest, err);
+
+	return ETrue;
+	}
+
+/**
+Gets MTM information that is not related to the current operation
+
+@param aMtmDataType Type of data to fetch
+@param aMtmDataBuffer On return this points to a descriptor holding the data
+
+@return KErrNone if successful, or a system wide error code
+*/
+TInt CImap4ServerMtm::GetNonOperationMtmData(TNonOperationMtmDataType aMtmDataType, TPtrC8& aMtmDataBuffer)
+	{
+	if (aMtmDataType == EMtmDataAccessPointId)
+		{
+		TNonOperationMtmDataAccessPointId mtmDataAccessPointId;
+		
+		TInt err = iImapProtocolController->GetAccessPointIdForConnection(mtmDataAccessPointId.iAccessPointId);
+
+		if (err == KErrNone)
+			{
+			iMtmDataAccessPointIdBuffer = TNonOperationMtmDataAccessPointIdBuffer(mtmDataAccessPointId);
+			aMtmDataBuffer.Set(iMtmDataAccessPointIdBuffer);
+			return KErrNone;
+			}
+		
+		return KErrNotFound;
+		}
+	
+	return KErrNotSupported;
+	}