email/imap4mtm/imapprotocolcontroller/src/cimapcompoundbase.cpp
changeset 31 ebfee66fde93
parent 0 72b543305e3a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/imap4mtm/imapprotocolcontroller/src/cimapcompoundbase.cpp	Fri Jun 04 10:25:39 2010 +0100
@@ -0,0 +1,381 @@
+// 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:
+// cimapcompoundbase.cpp
+//
+
+#include "cimapcompoundbase.h"
+#include "cimapsession.h"
+#include "cimapsettings.h"
+#include "cimapsessionconsts.h"
+#include "cimaplogger.h"
+#include "imappaniccodes.h"
+
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS  
+#include "timrfc822datefield.h"				
+#endif
+
+CImapCompoundBase::CImapCompoundBase( CImapSyncManager& aSyncManager,
+					   				  CMsvServerEntry& aServerEntry,
+					   				  CImapSettings& aImapSettings) :
+	CMsgActive(EPriorityStandard ),
+	iSyncManager(aSyncManager),
+	iServerEntry(aServerEntry),
+	iImapSettings(aImapSettings),
+	iStopForMigrate(EFalse),
+	iCancelForMigrate(EFalse),
+	iProgressState(TImap4GenericProgress::EIdle),
+	iSyncProgressState(TImap4SyncProgress::EIdle)
+	{
+
+	}
+
+CImapCompoundBase::~CImapCompoundBase( )
+	{
+	}
+
+#ifdef __IMAP_LOGGING
+void CImapCompoundBase::DoComplete(TInt& aErr)
+#else
+void CImapCompoundBase::DoComplete(TInt& /*aErr*/)
+#endif //_IMAP_LOGGING
+	{
+	__LOG_FORMAT((iSession->LogId(), "CImapCompoundBase::DoComplete(aErr = %d) CurrentStep = %d", aErr, iCurrentStep));
+	}
+
+/**
+Called when asynchronous service requests complete.
+Handles errors returned by aSynchronous services. 
+
+The state machine for derived compound commands is managed by 
+the pure-virtual DoRunLoopL() which is called by DoRunL(). 
+
+This form of DoRunL also allows synchronous steps to be performed 
+sequentially.
+*/
+void CImapCompoundBase::DoRunL()
+	{
+	// Handle any server errors
+	if (iStatus.Int()!=KErrNone)
+		{
+		if (TInt err=ProcessSessionError() != KErrNone)
+			{
+			Complete(err);
+			return;
+			}
+		}
+
+	// Operation state machine handled by DoRunLoopL()
+	while (!IsActive() && !DoRunLoopL())
+		{
+		// do nothing in the body of this
+		}
+	}
+
+
+/**
+Base class method for handling errors returned by the session.
+Called on completion of an asynchronous request on the IMAP session,
+ie at the start of the DoRunL() method. If this method returns
+a value other than KErrNone, the compound operation should complete
+with the return value as the error code.
+
+This will catch any positive error codes returned by the imap
+session These indicate either NO/BAD server responses or a more
+serious error has occurred, for example a disconnect, or corrupt
+data received from the remote server.
+
+NO/BAD server responses are handled by the derived compound
+operation classes (these are non-fatal errors, an attempt is
+made to continue the requested operation)
+
+Other errors are more serious - these are passed up to the
+Protocol Controller to deal with.
+
+@return KErrNone if the error has been handled,
+        otherwise the positive error code as returned by
+        the IMAP session.
+*/
+TInt CImapCompoundBase::ProcessSessionError()
+	{
+	TInt errCode = iStatus.Int();
+	if (errCode==KErrImapNo || errCode==KErrImapBad)
+		{
+		return ProcessNegativeServerResponse();
+		}
+	else
+		{
+		return errCode;
+		}
+	}
+
+/**
+Checks the input selection for entries of specified types.
+In this context, "Messages" means complete messages, not RFC888
+parts of another message. "parts" means attachment or text bodies.
+
+@param aSelection    - original list of message entry ids
+@param aLocalCopy    - filtered copy of message entry ids.
+@param aMessages     - accept entries of type KUidMsvMessageEntry
+@param aParts        - accept entries of type KUidMsvEmailTextEntry,
+											  KUidMsvAttachmentEntry, 
+											  KUidMsvMessageEntry
+											  KUidMsvEmailHtmlEntry
+  	  	                                      KUidMsvEmailExternalBodyEntry
+  	  	                                      KUidMsvEmailRtfEntry
+@param aFolders		 - accept entries of type KUidMsvFolderEntry
+@param aIsInService	 - only accept entries under the IMAP service entry
+
+@return KErrNotSupported if no valid entries,
+        KErrNone otherwise
+*/
+TInt CImapCompoundBase::CheckSelectionL(const CMsvEntrySelection& aSelection,
+									    CMsvEntrySelection* aLocalCopy,
+									    const TBool aMessages,
+									    const TBool aParts,
+									    const TBool aFolders,
+									    const TBool aIsInService)
+	{
+	// reset the local copy
+	aLocalCopy->Reset();
+	
+	// get the service ID
+	TMsvId serviceId = iImapSettings.ServiceId();
+
+	// Check all entries are messages
+	for(TInt a=0;a<aSelection.Count();a++)
+		{
+		// Does entry exist?
+		TBool addIt = EFalse;
+
+		if (iServerEntry.SetEntry(aSelection[a])==KErrNone)
+			{
+			TUid type = iServerEntry.Entry().iType;
+			if ((aMessages && type==KUidMsvMessageEntry) ||
+				(aParts && (type==KUidMsvEmailTextEntry || type==KUidMsvAttachmentEntry || type==KUidMsvMessageEntry || type==KUidMsvEmailHtmlEntry || type==KUidMsvEmailExternalBodyEntry || type==KUidMsvEmailRtfEntry)) ||
+				(aFolders && type==KUidMsvFolderEntry))
+				{
+				TBool inEnclosingMessage=EFalse;
+
+				// Do we need to check if it's in the local service or
+				// if it is a complete message
+				if (aIsInService || (!aParts && type==KUidMsvMessageEntry))
+					{
+					// Work up the tree until we get to the service or the root
+					do
+						{
+						SetEntryL(iServerEntry.Entry().Parent());
+						if (iServerEntry.Entry().iType==KUidMsvMessageEntry)
+							{
+							inEnclosingMessage=ETrue;
+							}
+						}
+					while(iServerEntry.Entry().iType!=KUidMsvServiceEntry &&
+						  iServerEntry.Entry().Id()!=KMsvRootIndexEntryId);		
+	
+					// Are we at the service that this MTM referrs to?
+					// if offline iServiceId==0 so allow all
+					if (!aIsInService || serviceId==0 || iServerEntry.Entry().Id()==serviceId)
+						{
+						// it's OK if it is not a message type (in
+						// which case it has already been checked and
+						// passed) or it is not within an enclosing message
+						if (type!=KUidMsvMessageEntry || !inEnclosingMessage || aParts)
+							{
+							addIt = ETrue;
+							}
+						}
+					}
+				else
+					{
+					// Add to local copy
+					addIt = ETrue;
+					}
+				}
+			}
+
+		if (addIt)
+			{
+			aLocalCopy->AppendL(aSelection[a]);
+			}
+#ifdef _DEBUG
+		// UI shouldn't really be giving us bogus items so panic
+		else
+			{
+			TImapServerPanic::ImapPanic(TImapServerPanic::EInvalidMsvTypeToCommand); 
+			}
+#endif
+		}
+
+	// Anything to do?
+	if (!aLocalCopy->Count())
+		{
+		// Nothing valid to work with
+		return(KErrNotSupported);
+		}
+	else
+		{
+		// All OK, the selection isn't empty
+		return(KErrNone);
+		}
+	}
+
+/**
+Sums the total size of messages to be copied or moved
+
+@param aSelection - selection of messages to be processed
+@return TInt      - the total size of the messages in the selection
+*/
+TInt CImapCompoundBase::CalculateDownloadSizeL(const CMsvEntrySelection& aSelection)
+	{
+	TInt totalSize = 0;
+
+	// Do a quick tally on the size of messages which are to be copied / moved.
+	TInt count=aSelection.Count();
+	while (count--)
+		{
+		SetEntryL(aSelection.At(count));
+		// Only add the size up if the message is not complete.
+		if(!iServerEntry.Entry().Complete())
+			{
+			totalSize += iServerEntry.Entry().iSize;
+			}
+		}
+	return totalSize;
+	}
+
+/**
+Find the folder that contains specified message
+
+@param aMessage - selection of messages to be processed
+@return TMsvId  - the parent folder of aMessage, or the service ID.
+*/
+TMsvId CImapCompoundBase::FindFolderL(const TMsvId aMessage)
+	{
+	__LOG_FORMAT((iSession->LogId(), "CImapCompoundBase::FindFolderL(%d)", aMessage));
+
+	// Find folder that encloses this message (has Mailbox flag set), or service,
+	// whichever we find first
+	SetEntryL(aMessage);
+	TMsvEmailEntry entry;
+	do
+		{
+		// Change context to parent of current entry
+		SetEntryL(iServerEntry.Entry().Parent());
+		entry=iServerEntry.Entry();
+
+		__LOG_FORMAT((iSession->LogId(), "  At %x, type=%x, mailbox=%d", entry.Id(),entry.iType,entry.Mailbox()));
+		
+		// A folder & a mailbox, or a service?
+		if (entry.iType==KUidMsvFolderEntry && entry.Mailbox())
+			{
+			// This'll do!
+			return(entry.Id());
+			}
+		}
+	while(iServerEntry.Entry().iType!=KUidMsvServiceEntry && entry.Id()!=KMsvRootIndexEntryId);
+
+	__LOG_TEXT(iSession->LogId(), "  FindFolderL() Failed");
+
+	return(NULL);
+	}
+
+// Do setentry, leave if there is an error
+void CImapCompoundBase::SetEntryL(const TMsvId aId)
+	{
+	User::LeaveIfError(iServerEntry.SetEntry(aId));
+	}
+
+/**
+Self completes to allow RunL to be called.
+*/	
+void CImapCompoundBase::CompleteSelf()
+	{
+	SetActive();
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	}
+
+
+void CImapCompoundBase::SetCurrentStep()
+	{
+	iCurrentStep = iNextStep;
+	}
+
+/**
+Save error code in a message
+
+@param aMessgeId
+@param aError
+*/
+void CImapCompoundBase::MessageErrorL(const TMsvId aMessageId, const TInt aError)
+	{
+	// Save error code: if we can't access this entry, then it's probably something to do
+	// with the error we're trying to report: ignore it silently
+	if (iServerEntry.SetEntry(aMessageId)==KErrNone)
+		{
+		TMsvEntry entry=iServerEntry.Entry();
+
+		// Save unnecessary writes...
+		if (entry.iError!=aError)
+			{
+			entry.iError=aError;
+			User::LeaveIfError(iServerEntry.ChangeEntry(entry));
+			}
+		}
+	}
+
+/**
+Cancels outstanding service request, and recovers the compound operation
+object to a state in which the operation may be restarted following migration
+to a new carrier.
+The operation will be restarted by a call to ResumeOperationL().
+
+Derived classes must implement a DoCancel that sets iNextStep to the step
+that should be performed following the migration, when ReumeOperationL is called;
+*/
+void CImapCompoundBase::CancelForMigrate()
+	{
+	__LOG_TEXT(iSession->LogId(), "CImapCompoundBase::CancelForMigrate()");
+	iCancelForMigrate = ETrue;
+	Cancel();
+	iCancelForMigrate = EFalse;
+	// Mark Operation as suspended.
+	iCurrentStep = ESuspendedForMigrate;
+	}
+
+/**
+Indicates that the compound operation should complete as soon as it is possible
+for the operation to be re-started without requiring a significant amount of
+repeated communication.
+The operation will be restarted by a call to ResumeOperationL()
+*/
+void CImapCompoundBase::StopForMigrate()
+	{
+	__LOG_TEXT(iSession->LogId(), "CImapCompoundBase::StopForMigrate()");
+	iStopForMigrate = ETrue;
+	}
+	
+/**
+Returns ETrue if the compound operation has been suspended (stopped/cancelled).
+This would occur in the case of a bearer migration.
+*/
+TBool CImapCompoundBase::Suspended()
+	{
+	if (iCurrentStep == ESuspendedForMigrate)
+		{
+		return ETrue;
+		}
+	return EFalse;
+	}
+