email/pop3andsmtpmtm/imapservermtm/src/IMAPSESS.CPP
changeset 25 84d9eb65b26f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/pop3andsmtpmtm/imapservermtm/src/IMAPSESS.CPP	Mon May 03 12:29:07 2010 +0300
@@ -0,0 +1,10210 @@
+// Copyright (c) 1998-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:
+// Deal with a connection to an IMAP4rev1 server
+// This involves logging in, issuing commands and parsing responses into
+// suitable return formats as necessary, and updating the message server's
+// database.
+// 
+//
+
+#include <msvstd.h>
+#include <miuthdr.h>
+#include <miut_err.h>
+#include <msventry.h>
+#include <imcvutil.h>
+
+#include <txtetext.h>
+
+#include <cmsvbodytext.h>
+#include <txtrich.h>
+#include <imcvtext.h>
+#include <imcvsend.h>
+#include <imcm.rsg>		// resource definition for IMCV
+#include <barsread.h>	// TResourceReader
+#include <msvapi.h>
+
+#include "impsmtm.h"
+
+#include "imapsess.h"
+#include "impspan.h"
+#include "fldindex.h"
+#include <imapset.h>
+#include <miutlog.h>
+#include <msvstore.h>
+#include <commdb.h>
+#include <commdbconnpref.h>
+#include <mmsvattachmentmanager.h>
+#include <mmsvattachmentmanagersync.h>
+#include <cemailaccounts.h>
+#ifdef __WINS__
+#include <e32wins.h>    // for maxfilename lengths
+#include <msvapi.h>
+#endif
+
+#ifdef _DEBUG
+#define LOG_COMMANDS(a) a
+#define DBG(a) a
+#define PRINTING
+#else
+#define LOG_COMMANDS(a)
+#define DBG(a)
+#undef PRINTING
+#endif
+
+#include "cimapcanceltimer.h"
+#include "mimapsessionobserver.h"
+
+// For the "UID SEARCH" command, the reply is of the form:
+// "* SEARCH 1234547890 1234567891 ......."
+//      :         :
+//  |---9---|	  :
+//           |---11----|
+//
+// Here the untagged headers is 9 characters and each UID part may be up to 11 characters.
+// So if we wanted to limit the number of replies recieved in a single search so that it is
+// no larger is size than a single body fetch part....
+
+// Maximum number of UIDs acceptable in a single reply.
+const TInt KImapUidSearchSize=(5120-9)/11;
+
+// Initial size of buffer in which the "Real name <user@host>" strings
+// are built before they're put into the CImHeader and the size by
+// which to increment the buffer when necessary
+const TInt KImapAddressSizeInc=256;
+
+// Initial size of the buffer containing the UID Search list
+const TInt KUidListStringSize=256;
+
+// Illegal UID we use for marker
+const TUint KIllegalUID	= 0xffffffff;
+
+// Idle time when waiting for a cancelled fetch to complete (microseconds)
+const TInt KImapFetchCancelIdleTime	= 10000000;	// 10 seconds
+
+// Idle time (seconds) when waiting for a DONE command to complete
+// when coming out of IMAP IDLE state.
+const TInt KImapDoneInactivityTimeSeconds = 5;
+
+// IMAP text
+_LIT8(KIMAP_UNTAGGED, "*");
+_LIT8(KIMAP_CONTINUATION, "+");
+_LIT8(KIMAP_BODY, "BODY");
+_LIT8(KIMAP_BODYPEEK, "BODY.PEEK");
+_LIT8(KIMAP_BODYSTRUCTURE, "BODYSTRUCTURE");
+_LIT8(KIMAP_BYE, "BYE");
+_LIT8(KIMAP_CAPABILITY, "CAPABILITY");
+_LIT8(KIMAP_EXISTS, "EXISTS");
+_LIT8(KIMAP_EXPUNGE, "EXPUNGE");
+_LIT8(KIMAP_FETCH, "FETCH");
+_LIT8(KIMAP_FLAGS, "FLAGS"); 
+_LIT8(KIMAP_HEADERFIELDS, "HEADER.FIELDS");
+
+_LIT8(KIMAP_ALERT, "ALERT");
+_LIT8(KIMAP_LIST, "LIST");
+_LIT8(KIMAP_LSUB, "LSUB");
+_LIT8(KIMAP_NIL, "NIL");
+_LIT8(KIMAP_NO, "NO");
+_LIT8(KIMAP_PREAUTH, "PREAUTH");
+_LIT8(KIMAP_READWRITE, "READ-WRITE");
+_LIT8(KIMAP_READONLY, "READ-ONLY");
+_LIT8(KIMAP_RECENT, "RECENT");
+_LIT8(KIMAP_RFC822SIZE, "RFC822.SIZE");
+_LIT8(KIMAP_UID, "UID");
+_LIT8(KIMAP_UIDVALIDITY, "UIDVALIDITY");
+_LIT8(KIMAP_UIDNEXT, "UIDNEXT");
+_LIT8(KIMAP_MIME, "MIME");
+_LIT8(KIMAP_SEARCH, "SEARCH");
+
+// IMAP capabilities
+_LIT8(KIMAP_VERSION, "IMAP4rev1");
+_LIT8(KIMAP_STARTTLS, "STARTTLS");
+_LIT8(KIMAP_LOGINDISABLED, "LOGINDISABLED");
+_LIT8(KIMAP_IDLE, "IDLE");
+
+// IMAP commands (trailing spaces, except for those which take no params)
+_LIT8(KIMAPC_CLOSE, "CLOSE");
+_LIT8(KIMAPC_LOGOUT, "LOGOUT");
+_LIT8(KIMAPC_SUBSCRIBE, "SUBSCRIBE ");
+_LIT8(KIMAPC_UNSUBSCRIBE, "UNSUBSCRIBE ");
+_LIT8(KIMAPC_IDLE, "IDLE");
+_LIT8(KIMAPC_DONE, "DONE");
+
+// IMAP flags
+_LIT8(KIMAPFLAG_NOSELECT, "\\Noselect");
+_LIT8(KIMAPFLAG_NOINFERIORS, "\\Noinferiors");
+_LIT8(KIMAPFLAG_ANSWERED, "\\Answered");
+_LIT8(KIMAPFLAG_DELETED, "\\Deleted");
+_LIT8(KIMAPFLAG_DRAFT, "\\Draft");
+_LIT8(KIMAPFLAG_FLAGGED, "\\Flagged");
+_LIT8(KIMAPFLAG_RECENT, "\\Recent");
+_LIT8(KIMAPFLAG_SEEN, "\\Seen");
+_LIT8(KIMAPFLAG_UNREAD, "\\Unread");
+
+// MIME message types
+_LIT8(KMIME_MESSAGE, "MESSAGE");
+_LIT8(KMIME_RFC822, "RFC822");
+_LIT8(KMIME_TEXT, "TEXT");
+_LIT8(KMIME_HTML, "HTML");
+_LIT8(KMIME_XVCARD, "X-VCARD");
+_LIT8(KMIME_VCALENDAR, "X-VCALENDAR");
+_LIT8(KMIME_ICALENDAR, "CALENDAR");
+_LIT8(KMIME_NAME, "NAME");
+_LIT8(KMIME_NAME_RFC2231, "NAME*");
+_LIT8(KMIME_FILENAME, "FILENAME");
+_LIT8(KMIME_FILENAME_RFC2231, "FILENAME*");
+_LIT8(KMIME_ATTACHMENT, "ATTACHMENT");
+_LIT8(KMIME_DELIVERY_STATUS, "DELIVERY-STATUS");
+_LIT8(KMIME_ALTERNATIVE, "ALTERNATIVE");
+_LIT8(KMIME_RELATED, "RELATED");
+_LIT8(KMIME_IMAGE, "IMAGE");
+_LIT8(KMIME_AUDIO, "AUDIO");
+_LIT8(KMIME_VIDEO, "VIDEO");
+_LIT8(KMIME_APPLICATION, "APPLICATION");
+
+// Encoding types
+_LIT8(KMIME_BASE64, "BASE64");
+
+// Multipart types
+_LIT8(KMIME_MIXED, "MIXED");
+
+// for first stage download (to view in folder list)
+_LIT8(KImapFetchSmallHeaderToEnd, "%d UID FETCH %d:* (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Received Date Subject From %S)])\r\n");
+_LIT8(KImapFetchSmallHeaderRange,"%d UID FETCH %d:%d (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Received Date Subject From %S)])\r\n");
+_LIT8(KImapFetchSmallHeaderRangeRefined,"%d UID FETCH %S (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Received Date Subject From %S)])\r\n");
+
+// for second stage download (to view when downloading whole email)
+_LIT8(KImapFetchLargeHeader, "%d UID FETCH %d (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Received Date Subject From Reply-to To Cc Bcc Message-ID %S)])\r\n");
+_LIT8(KImapFetchLargeHeaderRange, "%d UID FETCH %d:* (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Received Date Subject From Reply-to To Cc Bcc Message-ID %S)])\r\n");
+
+// Use %S so we can specify a refined FETCH based on a previous refined SEARCH
+_LIT8(KImapFetchLargeHeaderRangeRefined, "%d UID FETCH %S (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Received Date Subject From Reply-to To Cc Bcc Message-ID %S)])\r\n");
+// Constants for fetching body
+_LIT8(KImapFetchBodyPeek, "%d UID FETCH %d (BODY.PEEK[%S]<%d.%d>)\r\n");
+_LIT8(KImapFetchBody,     "%d UID FETCH %d (BODY[%S]<%d.%d>)\r\n");
+_LIT8(KImapFetchMimeBodyPeek, "%d UID FETCH %u (BODY.PEEK[%S]<0.%d> BODY.PEEK[%S.MIME])\r\n");
+_LIT8(KImapFetchMimeBody,     "%d UID FETCH %u (BODY[%S]<0.%d> BODY[%S.MIME])\r\n");
+
+// Constants for sending commands 
+_LIT8(KImapCommand, "%S\r\n");
+
+// The maximum number of octets to be uuencoded on each line is 45
+const TInt KUuDecodedLineLength = 45;	
+
+const TInt KBodyTextChunkSizeBytes = 512;
+const TInt KKiloByteSize = 1024;
+// UIDs are  32bit integers, which takes maximum 10 decimal digits.
+// KMaxUint32Chars is used to check whether the string containg the UID 
+// list has allocated enough memory when creating it.
+const TInt KMaxUint32Chars = 10;
+
+// Processing to remove illegal characters from a filename
+LOCAL_C void StripIllegalCharactersFromFileName(TDes16& aName)
+    {
+    TInt length=aName.Length();
+    for(TInt index=0; index < length; index++)
+        {
+		//parse extracted filename and replace any illegal chars  with a default
+        TUint charr=(TUint)aName[index];
+        if(	charr == '*' || charr == '\\' || charr == '<' || charr == '>' ||
+            charr == ':'  || charr == '"' || charr == '/' || charr == '|' ||
+			charr == '?' || charr < ' ')
+            {
+            aName[index] = KImcvDefaultChar;
+            }
+        }
+   }
+
+
+// We don't do the async notifications yet: they make everything a bit more wobbly
+// SJM actually I guess we do do them now!
+#define ASYNC_NOTIFICATIONS
+
+// do we automatically set the iRelatedId of each object to itself? No
+// for now as it confuses the offline handling code which assumes it
+// is a shadow entry if iRelatedId is set.
+#define SET_RELATED_ID	0
+
+// This is very nasty but necessary as the flag returning functions
+// return 0 or not-zero not 0 or 1
+
+#define FIXBOOL(a)		(a?ETrue:EFalse)
+
+// Directory structure
+CImImap4DirStruct::CImImap4DirStruct()
+	{
+	}
+
+CImImap4DirStruct::~CImImap4DirStruct()
+	{
+	// Get rid of leaf
+	delete iLeafname;
+	}
+
+void CImImap4DirStruct::SetLeafnameL(const TDesC& aName)
+	{
+	// Make buffer, set it
+	iLeafname=HBufC::NewL(aName.Length());
+	*iLeafname=aName;
+	}
+
+TPtrC CImImap4DirStruct::Leafname()
+	{
+	// Return it
+	return(*iLeafname);
+	}
+
+CImImap4Session::CImImap4Session(MImapSessionObserver& aObserver) // construct high-priority active object
+	: CMsgActive(1), iState(EImapStateDisconnected), iCancelledTag(-1),
+	  iSecurityState(EUnknown), iCharset(KCharacterSetIdentifierUtf8),
+	  iObserver(aObserver),iPrimarySession(NULL)
+	{
+	__DECLARE_NAME(_S("CImImap4Session"));
+	}
+
+CImImap4Session* CImImap4Session::NewLC(TInt aId, MImapSessionObserver& aObserver)
+
+	{
+	CImImap4Session* self=new (ELeave) CImImap4Session(aObserver);
+
+	CleanupStack::PushL(self);
+	self->ConstructL(aId);
+	return self;
+	}
+
+CImImap4Session* CImImap4Session::NewL(TInt aId, MImapSessionObserver& aObserver)
+	{
+	CImImap4Session* self=NewLC(aId, aObserver);
+	CleanupStack::Pop();
+	return self;
+	}
+
+void CImImap4Session::ConstructL(TInt aId)
+	{
+	// Add to active scheduler
+	CActiveScheduler::Add(this);
+
+	// Get FS
+	User::LeaveIfError(iFs.Connect());
+
+	// Get somewhere to store service settings
+	iServiceSettings=new (ELeave) CImImap4Settings;
+
+	// Get an IO processor
+	iImapIO=CImapIO::NewL(aId);
+
+	// Message selection
+	iSelection=new (ELeave) CMsvEntrySelection;
+
+	// List of messages to delete on folder close
+	iDeletedUids=new (ELeave) CArrayFixFlat<TMsvId>(8);
+
+	// List of messages to fetch in single fetch operation
+	iFetchList=new (ELeave) CArrayFixFlat<TMsvId>(8);
+
+	// List of messages who's Seen status has changed
+	iSetSeenList=new (ELeave) CArrayFixFlat<TMsvId>(8);
+	iClearSeenList=new (ELeave) CArrayFixFlat<TMsvId>(8);
+
+	RResourceFile resFile;
+	OpenResourceFileL(resFile,iFs);	// NB leaves if file not found
+
+	// make sure the resource file will be closed if anything goes wrong
+	// CloseResourceFile is declared in IMCVDLL.H and defined in IMCVDLL.CPP
+	TCleanupItem close(CloseResourceFile,&resFile);
+	CleanupStack::PushL(close);
+		
+	// Read iStore8BitData flag.
+	HBufC8* buf = resFile.AllocReadLC( STORE_8BIT_BODY_TEXT );
+	TResourceReader reader;
+	reader.SetBuffer(buf);
+	iStore8BitData = reader.ReadInt8();
+	CleanupStack::PopAndDestroy(buf);
+
+	buf=resFile.AllocReadLC(DEFAULT_ATTACHMENT_NAME);
+	reader.SetBuffer(buf);
+	iDefaultAttachmentName=reader.ReadTPtrC().AllocL();
+	CleanupStack::PopAndDestroy(2, &resFile); // buf, resFile (Close resfile)
+
+	if (!iStore8BitData)
+		{
+		// CRichText bits
+		iParaLayer=CParaFormatLayer::NewL();
+		iCharLayer=CCharFormatLayer::NewL();
+		}
+
+	// Create converter objects
+	iCharacterConverter=CCnvCharacterSetConverter::NewL();
+	iCharConv=CImConvertCharconv::NewL(*iCharacterConverter, iFs);
+	iHeaderConverter=CImConvertHeader::NewL(*iCharConv); 
+
+	// we assume that this message is MIME as we have no way of
+	// detecting otherwise (without sending a new FETCH to the
+	// server to get the MIME-Version).
+	iHeaderConverter->SetMessageType(ETrue);
+
+	// Get timer to defeat IMSK timeout
+	iDummyRead = CImImap4SessionDummyRead::NewL(*this, *iImapIO, Priority());
+	
+	iIdleRead =  CImImap4SessionIdleRead::NewL(*this, Priority());
+	
+	// List of message UIDs in a folder
+	iSearchList=new (ELeave) CArrayFixFlat<TUint32>(8);		// For granularity of 8, 32 bytes are allocated each time.
+	iSyncLimit = KImImapSynchroniseAll;
+	// set up progress types
+	iProgress.iType=EImap4GenericProgressType;
+	iCurrentDrive = MessageServer::CurrentDriveL(iFs);
+
+	iIdleTimerExpired = EFalse;
+
+	iIdleTimer = CIdleTimeoutTimer::NewL(*this);
+	iReissueIdle = EFalse;
+	iDisconnectAfterIdleStopped = EFalse;
+	iFetchPartialMail=EFalse;
+	iCaf = new (ELeave) CImCaf(iFs);
+
+	iIsICalendar = EFalse;
+	iIsVCalendar = EFalse;
+	
+	iUidString = HBufC8::NewL(KUidListStringSize);
+	
+	iCancelTimer = CImapCancelTimer::NewL(*this);
+	}
+
+CImImap4Session::~CImImap4Session()
+	{
+	Cancel(); // make sure we're cancelled
+
+	delete iDummyRead;
+	delete iIdleRead;
+
+	// No settings
+	delete iServiceSettings;
+	delete iPrefs;
+
+	// Get rid of connection
+	delete iImapIO;
+
+	// Get rid of message body bits
+	delete iBodyBuf;
+	delete iMessageBody;
+	delete iBodyText;
+	delete iParaLayer;
+	delete iCharLayer;
+	
+	// Message selection
+	delete iSelection;
+
+	// List of messages to delete
+	delete iDeletedUids;
+
+	// List of messages to fetch
+	delete iFetchList;
+
+	// List of seen flags to set/clear
+	delete iSetSeenList;
+	delete iClearSeenList;
+
+	// Partial line: used when doing Q-P decoding, as it works on a line at a time.
+	delete iPartialLine;
+
+	// Any attachment info (ie: there was a fetch in progress)
+	delete iAttachmentFile;
+	delete iAttachmentFullPath;
+	delete iAttachmentMimeInfo;
+	delete iDefaultAttachmentName;
+	delete iFooterString;
+	
+	// Any message sizer left over (ie: we've been deleted in the middle of
+	// an append operation)
+	delete iMessageSizer;
+	delete iMessageSender;
+	delete iLineBuffer;
+
+	// Characterset conversion
+	delete iHeaderConverter;
+	delete iCharConv;
+	delete iCharacterConverter;
+
+	// CMsvServerEntry used for moves
+	delete iMoveEntry;
+
+	//cached TMsvEntry data
+	delete iCachedEntryData;
+
+	// List of message UIDs in a folder
+	delete iSearchList;
+
+	// Selection passed during synchronisation
+	delete iSynchronisationSelection;
+
+	delete iIdleTimer;
+	delete iCaf;
+	delete iUidString;
+	iFs.Close();
+	
+	delete iCancelTimer;
+	
+	//Delete Username and Password
+	delete iUsername;
+	delete iPassword;
+	
+	// Note: iList is owned by a caller, not us - we don't delete it
+	}
+
+// Logging calls: passed through to ImapIO
+void CImImap4Session::LogText(const TDesC8& aString)
+	{
+	// Log the text
+	iImapIO->LogText(aString);
+	}
+
+void CImImap4Session::LogText(TRefByValue<const TDesC8> aFmt,...)
+	{
+	VA_LIST list;
+	VA_START(list,aFmt);
+	TBuf8<1024> aBuf;
+	//handles the data over flow panics. returns immediately without performing any action.
+	TDes8OverflowHandler overFlowHandler;
+	
+	aBuf.AppendFormatList(aFmt,list, &overFlowHandler);
+	LogText(aBuf);
+	}
+
+// Do setentry, leave if there is an error
+void CImImap4Session::SetEntryL(const TMsvId aId)
+	{
+#ifdef PRINTING
+	TInt error=iEntry->SetEntry(aId);
+	if (error)
+		LogText(_L8("SetEntryL(%x) returned %d"),aId,error);
+	User::LeaveIfError(error);
+#else
+	User::LeaveIfError(iEntry->SetEntry(aId));
+#endif
+	}
+
+// Change entry, leave if error
+void CImImap4Session::ChangeEntryL(const TMsvEntry& aEntry)
+	{
+#ifdef PRINTING
+	TInt error=iEntry->ChangeEntry(aEntry);
+	if (error)
+		LogText(_L8("ChangeEntryL(%x) returned %d"),aEntry.Id(),error);
+	User::LeaveIfError(error);
+#else
+	User::LeaveIfError(iEntry->ChangeEntry(aEntry));
+#endif
+	}
+
+// Change entry in bulk mode (i.e. no index file commit), leave if error
+void CImImap4Session::ChangeEntryBulkL(const TMsvEntry& aEntry)
+	{
+#ifdef PRINTING
+	TInt error=iEntry->ChangeEntryBulk(aEntry);
+	if (error)
+		LogText(_L8("ChangeEntryL(%x) returned %d"),aEntry.Id(),error);
+	User::LeaveIfError(error);
+#else
+	User::LeaveIfError(iEntry->ChangeEntryBulk(aEntry));
+#endif
+	}
+// Get children, leave if error
+void CImImap4Session::GetChildrenL(CMsvEntrySelection& aSelection)
+	{
+#ifdef PRINTING
+	TInt error=iEntry->GetChildren(aSelection);
+	if (error)
+		LogText(_L8("GetChildrenL() returned %d"),error);
+	User::LeaveIfError(error);
+#else
+	User::LeaveIfError(iEntry->GetChildren(aSelection));
+#endif
+	}
+
+// This can be called after Select() has completed to find out if the
+// folder has changed in any way since the last sync
+TBool CImImap4Session::FolderChanged() const
+	{
+	return iMailboxReceivedExists ||
+		iMailboxReceivedExpunge ||
+		iMailboxReceivedFlags;
+	}
+
+TBool CImImap4Session::ImapIdleSupported() const
+	{
+	return iUseIdleCommand && iCapabilityIdleSupport;
+	}
+
+TBool CImImap4Session::IsIdling() const
+	{
+	return(iState==EImapStateIdling);
+	}
+
+// Transfers the current selection into the iFolderIndex, and sorts it by
+// UID.
+void CImImap4Session::MakeSortedFolderIndexL(TBool aUseCachedEntryData)
+	{
+		
+	TInt noofchildren=iSelection->Count();
+	
+	// Reset folder index
+	iFolderIndex.SetSizeL(noofchildren);
+	TInt a=0;
+
+	if(!aUseCachedEntryData)
+		{ //can't rely on iCachedEntryData
+		TMsvEntry* entryPtr;
+		TMsvId id;
+		for(a=0;a<noofchildren;a++)
+			{
+			// Save UID/TMsvId of this entry
+			id=(*iSelection)[a];
+			User::LeaveIfError(iEntry->GetEntryFromId(id,entryPtr));
+			iFolderIndex[a].iUid=((TMsvEmailEntry)(*entryPtr)).UID();
+			iFolderIndex[a].iMsvId=id;
+			}
+		}
+	else
+		{
+		for(a=0;a<noofchildren;a++)
+			{
+			// Save UID/TMsvId of this entry
+			iFolderIndex[a].iUid=(*iCachedEntryData)[a].iUid;
+			iFolderIndex[a].iMsvId=(*iSelection)[a];
+			}
+		}
+
+	// Sort it by UID
+	iFolderIndex.Sort();
+
+	// Check for any duplicate UIDs (ie, a dud netscape server)
+	TMsvEntry* entryPtr;
+	TMsvEntry* nextEntryPtr;
+	for(a=1;a<noofchildren;a++)
+		{
+		if(iFolderIndex[a].iUid!=0 && iFolderIndex[a].iUid==iFolderIndex[a-1].iUid)
+			{
+			if(!aUseCachedEntryData)
+				{
+				// get the TMsvEntry for the message/folder
+				User::LeaveIfError(iEntry->GetEntryFromId(iFolderIndex[a].iMsvId,entryPtr));
+				User::LeaveIfError(iEntry->GetEntryFromId(iFolderIndex[a-1].iMsvId,nextEntryPtr));
+				// check if type of TMsvEntry and type of next TMsvEntry are both Messages
+				if( entryPtr->iType.iUid == nextEntryPtr->iType.iUid && entryPtr->iType.iUid == KUidMsvMessageEntryValue)
+					{
+					User::Leave(KErrCorrupt);
+					}
+				}
+			else
+				{
+				User::Leave(KErrCorrupt);
+				}
+			}
+			
+		}
+
+#ifdef PRINTING
+	LogText(_L8("MakeSortedFolderIndex done: index list follows. children=%d"),noofchildren);
+	for(a=0;a<noofchildren;a++)
+		LogText(_L8("  MsvId=%8x   UID=%d"),iFolderIndex[a].iMsvId,iFolderIndex[a].iUid);
+#endif
+	}
+
+HBufC* CImImap4Session::DoUnModUTF7LC(TDesC8& aBuffer)
+	{
+	iCharConv->PrepareToConvertToFromOurCharsetL(KCharacterSetIdentifierImapUtf7);
+
+	// unicode version won't be longer than the original (in chars)
+	HBufC* text=HBufC::NewL(aBuffer.Length());
+	CleanupStack::PushL(text);
+
+	//LogText(_L8("DoUnModUTF7LC: from %S"),&aBuffer);
+
+	TInt numUC, indexUC;
+	TPtr des = text->Des();
+	iCharConv->ConvertToOurCharsetL(aBuffer, des, numUC, indexUC);
+
+	//LogText(_L8("DoUnModUTF7LC: to   %S len %d numUC %d"),&des, des.Length(), numUC);
+	
+	return text;
+	}
+
+// Enquote a string (being sent as a string literal) if required
+void CImImap4Session::DoQuoteL(HBufC8*& aBuffer)
+	{
+	// Null string? Nothing to do
+	if (!aBuffer->Length() || !aBuffer->Des().Length()) return;
+
+	// Anything needing quoting in there?
+	if (aBuffer->Des().Locate('\\')==KErrNotFound &&
+		aBuffer->Des().Locate('\"')==KErrNotFound) return;
+
+	// Run through string, inserting quote characters as needed
+	for(TInt a=0;a<aBuffer->Des().Length();a++)
+		{
+		if (aBuffer->Des()[a]=='\\' || aBuffer->Des()[a]=='\"')
+			{
+			HBufC8 *newbuf=aBuffer->ReAllocL(aBuffer->Des().Length()+1);
+
+			// Been moved due to realloc?
+			if (newbuf!=aBuffer)
+				{
+				// In all cases when DoQuoteL() is called, the buffer is on the top of
+				// the cleanup stack: change this to indicate the correct entry
+				CleanupStack::Pop();
+				CleanupStack::PushL(aBuffer=newbuf);
+				}
+
+			aBuffer->Des().Insert(a,_L8("\\"));
+			a++;
+			}
+		}
+	}
+
+TInt CImImap4Session::FindFilename(const CImMimeHeader& aMimeInfo, TPtrC8& aFilename)
+	{
+	// Look in content-type list
+	const CDesC8Array& ctype=aMimeInfo.ContentTypeParams();
+
+	DBG((LogText(_L8("FindFilename: Checking %d entries in content-type list"),
+				ctype.Count())));
+
+	TInt tuple=0;
+	while(tuple<ctype.Count())
+		{
+#ifdef PRINTING
+		TPtrC8 t1=ctype[tuple],t2=ctype[tuple+1];
+		LogText(_L8("  %S %S"),&t1,&t2);
+#endif
+		// Look for "name xxx"
+		if (ctype[tuple].CompareF(KMIME_NAME)==0)
+			{
+			// Got it: report that we found it
+			aFilename.Set(ctype[tuple+1]);
+			TBuf8<KMaxFileName>buf(aFilename);
+			buf.Trim();
+			if(buf.Length()==0)
+				{
+				return(KErrNotFound);
+				}
+
+			return(KErrNone);
+			}
+		else if (ctype[tuple].CompareF(KMIME_NAME_RFC2231)==0)
+			{
+			// Got it: report that we found it
+			aFilename.Set(ctype[tuple+1]);
+			return(KErrRFC2231Encoded);
+			}
+		tuple+=2;
+		}
+
+	// Not found in the content type, try content disposition
+	tuple=0;
+	const CDesC8Array& cdisp=aMimeInfo.ContentDispositionParams();
+	while(tuple<cdisp.Count())
+		{
+#ifdef PRINTING
+		TPtrC8 t1=cdisp[tuple],t2=cdisp[tuple+1];
+		LogText(_L8("  %S %S"),&t1,&t2);
+#endif
+		// Look for "filename xxx"
+		if (cdisp[tuple].CompareF(KMIME_FILENAME)==0)
+			{
+			// Got it: report that we found it
+			aFilename.Set(cdisp[tuple+1]);
+			return(KErrNone);
+			}
+		else if (cdisp[tuple].CompareF(KMIME_FILENAME_RFC2231)==0)
+			{
+			// Got it: report that we found it
+			aFilename.Set(cdisp[tuple+1]);
+			return(KErrRFC2231Encoded);
+			}
+
+		tuple+=2;
+		}
+
+	// Didn't find it
+	return(KErrNotFound);
+	}
+	
+void CImImap4Session::FindFilenameDecodeL(const CImMimeHeader& aMimeInfo, TFileName& aFileName)
+	{
+	// Make an attachment name
+	aFileName.Zero();
+
+	TPtrC8 origFileName;
+
+	// Look for filename in Content-Type list
+	TInt err = FindFilename(aMimeInfo, origFileName);
+	if (KErrNotFound == err)
+		{
+		// Fall back to simple "attachment" (language specific)
+		aFileName=iDefaultAttachmentName->Des();
+		}
+	else if (KErrRFC2231Encoded == err)
+		{
+		// A file name has been found but it is encoded (RFC2231)
+		// Use the default file name but append the file extension so that its type can be recognised
+		aFileName=iDefaultAttachmentName->Des();
+		TInt dotPos = origFileName.Length() - 1;
+		TBool dotFound = EFalse;
+		
+		// Find the extension
+		while ((dotPos != 0) && (!dotFound))
+			{
+			if (origFileName[dotPos] == '.')
+				{
+				dotFound = ETrue;
+				// Extension found: append it to the filename
+				TInt extensionLength = origFileName.Length() - dotPos;
+				if ((aFileName.Length() + extensionLength) <= aFileName.MaxLength())
+					{
+					HBufC* extension = HBufC::NewLC(extensionLength);
+					extension->Des().Copy(origFileName.Right(extensionLength));
+					aFileName.Append(*extension);
+					CleanupStack::PopAndDestroy(extension);
+					}
+				}
+
+			--dotPos;
+			}
+		}
+	else
+		{
+		// Run it through the QP decoder
+		HBufC *decoded=HBufC::NewLC(origFileName.Length());
+		TPtr decoded_ptr(decoded->Des());
+
+		// Decode filename from the header
+		iHeaderConverter->DecodeHeaderFieldL(origFileName, decoded_ptr);
+		
+		DBG((LogText(_L8("FindFilenameDecode: '%S' to '%S' "),&origFileName,&decoded_ptr)));
+
+		// Need to do a check on the filename length here.
+		// If it is too long, set to the max possible, keeping extension.
+		TFileName path;
+			
+		TInt fileNameLength = path.Length() + decoded_ptr.Length();
+
+		if( fileNameLength > KMaxFileName)
+			{
+#ifdef __WINS__
+			TFileName winsFileName;
+			TFileName mailStoreDrive;
+			TDriveUnit drive(MessageServer::CurrentDriveL(iFs));
+			mailStoreDrive.Append(drive.Name());
+			mailStoreDrive.Append(KPathDelimiter);
+			MapEmulatedFileName(winsFileName, mailStoreDrive);
+			TInt prefixLen = winsFileName.Length();
+#else
+			TInt prefixLen = 0;
+#endif
+			// Crop the Old File Name
+			TInt lengthToCrop = (fileNameLength - KMaxFileName) + prefixLen;
+			// Use LocateReverse rather than TParsePtr as decoded_ptr may be > 256 chars
+			TInt dot = decoded_ptr.LocateReverse( '.' );
+			TPtrC extension = decoded_ptr.Mid(dot != KErrNotFound ? dot : decoded_ptr.Length());
+			TInt newFileNameLength = decoded_ptr.Length() - extension.Length() - lengthToCrop;
+			TPtrC newFileName=decoded_ptr.Left(newFileNameLength);
+
+			// Create the New File Name (ie File Name & Extension)
+			aFileName.Zero();
+			aFileName.Append(newFileName);
+			aFileName.Append(extension);
+			}
+		else
+			{
+			aFileName.Copy(decoded_ptr);
+			}
+		CleanupStack::PopAndDestroy(); // decoded
+		}
+	}
+
+// Update iDate in iMailboxId to show the time now (last sync time)
+void CImImap4Session::SyncCompleteL()
+	{
+        DBG((LogText(_L8("CImImap4Session::SyncCompleteL()"))));
+	// Find entry
+	SetEntryL(iMailboxId);
+	TMsvEmailEntry message=iEntry->Entry();
+
+	// Find 'now'
+	TTime now;
+	now.UniversalTime();
+	message.iDate=now;
+
+	// Check to see if there has been a change in the number of messages in the remote folder.
+	TBool folderSizeChanged=(message.RemoteFolderEntries()!=iMailboxSize);
+
+	// Set 'unread' flag on folder if there are any unread messages within it
+	if (FIXBOOL(message.Unread())!=iSomeUnread || !message.Visible() || folderSizeChanged)
+		{
+		// Update flags
+		message.SetUnread(iSomeUnread);
+		message.SetVisible(ETrue);
+		message.SetRemoteFolderEntries(iMailboxSize);
+ 		ChangeEntryBulkL(message);
+		}
+
+	// we need to ensure the hierarchy of folders containing this one
+	// is now visible. Note previously this incorrectly only did this
+	// when we were not in DisconncetedUserMode
+	do
+		{
+		// Move up one
+		SetEntryL(message.Parent());
+		message=iEntry->Entry();
+
+		// Ensure visibility
+		if (!message.Visible())
+			{
+			message.SetVisible(ETrue);
+			ChangeEntryL(message);
+			}
+		}
+	while(message.iType!=KUidMsvServiceEntry);
+	
+	// Before we got back to the idle state, we need to commit any
+	// outstanding entries to the index file to complete the bulk
+	// synchronization operation
+	iEntry->CompleteBulk();
+
+	if (iReissueIdle)
+		{
+		iState=EImapStateSelected;
+		DoStartIdleL();
+		}
+	}
+
+// Reset subscription flags for all children, and recurse into folders
+void CImImap4Session::ResetSubscriptionFlagsL(const TMsvId aFolder)
+	{
+	// Do this one
+	SetEntryL(aFolder);
+	TMsvEmailEntry entry=iEntry->Entry();
+
+	// A folder or service? If not, return
+	if (entry.iType!=KUidMsvServiceEntry &&
+		entry.iType!=KUidMsvFolderEntry)
+		return;
+
+	// Reset flag if needed
+	if (entry.Subscribed())
+		{
+		// Reset flag and save
+		entry.SetSubscribed(EFalse);
+		ChangeEntryL(entry);
+		}
+
+	// Any children?
+	CMsvEntrySelection *children=new (ELeave) CMsvEntrySelection;
+	CleanupStack::PushL(children);
+	GetChildrenL(*children);
+	if (children->Count())
+		{
+		// Do each in turn
+		for(TInt child=0;child<children->Count();child++)
+			ResetSubscriptionFlagsL((*children)[child]);
+		}
+	CleanupStack::PopAndDestroy();
+	}
+	
+TBool CImImap4Session::IsCancelling() const
+	{
+	return iCancelAndIdle;
+	}
+	
+void CImImap4Session::CancelAndIdleL(TBool aReissueIdle)
+	{
+	// Flag that a cancel and idle command has been requested.
+	iCancelAndIdle = ETrue;
+
+	switch( iState )
+		{
+	case EImapStateFetchWait:
+		{
+		// Stop requesting fetches and wait for the current fetches to be completed.
+		iState = EImapStateFetchCancelWait;
+		iReissueIdle = aReissueIdle;
+		
+		// Start an idle timer - this is ensure that if the GPRS session is currently
+		// suspended we don't hang forever waiting for the remaining fetch data to be
+		// received.
+		iCancelTimer->After(KImapFetchCancelIdleTime);
+		
+		// Delete any partially downloaded attachments
+		if ( iAttachmentFileState == EFileIsOpen )
+			{
+			iAttachmentFileState=EFileIsIncomplete;
+			if(iCaf->Processing())
+				{
+				iCaf->EndProcessingL();
+				}
+			else
+				{
+				iAttachmentFile->CloseFile();
+				}
+			CMsvStore* store = iEntry->EditStoreL(); 
+			CleanupStack::PushL(store);
+			// Could be multiple attachments in the folder.
+			TInt i;
+			TInt attachmentCount = store->AttachmentManagerL().AttachmentCount();
+			for(i=0;i<attachmentCount;i++)
+				{
+				// Remove [0] as array is shuffled. Once index [n] is removed n+1 becomes n
+				store->AttachmentManagerExtensionsL().RemoveAttachmentL(0);
+				}
+			if(attachmentCount)			
+				store->CommitL();
+			CleanupStack::PopAndDestroy(store);
+			TMsvEmailEntry message=iEntry->Entry();
+
+			CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry());
+			ChangeEntryBulkL(message);
+			}
+	
+		DBG((LogText(_L8("CImImap4Session::CancelAndIdleL() - fetch cancel; continue to process requested fetches - no more parts to be requested"))));
+		} break;
+		
+	case EImapStateIdleWait:
+		{
+		// Not much to do - already issued IDLE command, just wait for the IDLE 
+		// command to 'start'.
+		// NOTE - set the iReissueIdle flag to ensure that IssueIdleRead is 
+		// called when we get the server response.
+		iReissueIdle = aReissueIdle;
+		
+		// Start an idle timer - this is ensure that if the GPRS session is currently
+		// suspended we don't hang forever waiting for the remaining data to be
+		// received.
+		iCancelTimer->After(KImapFetchCancelIdleTime);		
+		
+		DBG((LogText(_L8("CImImap4Session::CancelAndIdleL() - waiting for IDLE - continue"))));
+		} break;
+		
+	case EImapStateIdling:
+		{
+		// Nothing much to do here - already IDLE-ing! Unset the cancel-and-idle flag
+		iCancelAndIdle = EFalse;
+		DBG((LogText(_L8("CImImap4Session::CancelAndIdleL() - already IDLE-ing - do nothing"))));
+		} break;
+	
+	case EImapStateStopIdleWait:
+		{
+		// Need to wait for the current IDLE command to complete, then re-issue
+		// another one!!
+		iReissueIdle = aReissueIdle;
+
+		// Start an idle timer - this is ensure that if the GPRS session is currently
+		// suspended we don't hang forever waiting for the remaining data to be
+		// received.
+		iCancelTimer->After(KImapFetchCancelIdleTime);
+		
+		DBG((LogText(_L8("CImImap4Session::CancelAndIdleL() - IDLE stop requested - wait for response and then re-issue IDLE command"))));
+		} break;
+		
+	case EImapStateSelectWait:
+		{
+		// Waiting for select to complete - issue IDLE once select completes.
+		// A check will be made to ensure that the selected mailbox is the inbox.
+
+		iReissueIdle = aReissueIdle;
+
+		// Start an idle timer - this is ensure that if the GPRS session is currently
+		// suspended we don't hang forever waiting for the remaining data to be
+		// received.
+		iCancelTimer->After(KImapFetchCancelIdleTime);
+		
+		DBG((LogText(_L8("CImImap4Session::CancelAndIdleL() - cancel select - wait for response and then issue IDLE command"))));		
+		} break;
+		
+	case EImapStateSelected:
+		{
+		// A mailbox has been selected - does IDLE need to be re-issued?
+		iReissueIdle = aReissueIdle;
+		
+		if( iReissueIdle )
+			{
+			// Yep, need to re-issue the IDLE (as was IDLE-ing before).
+			// For this need to be in the INBOX as a writable-select.
+			
+			// First reset counts to safe values here to avoid reporting left
+			// over values from previous fetch. Correct values will be set up
+			// once headers have been fetched and parts counted (taken from SelectL).
+			iProgress.iPartsToDo=iProgress.iBytesToDo=1;
+			iProgress.iPartsDone=iProgress.iBytesDone=0;
+
+			// Do the select (if we really need to) or skip if possible.
+			if( iMailboxId==GetInbox() && iMailboxWritable )
+				{
+				DBG((LogText(_L8("Need to re-issue IDLE command"))));
+
+				// No need to do the select - so re-issue the IDLE
+				DoStartIdleL();				
+				}
+			else
+				{
+				DBG((LogText(_L8("Need to issue IDLE - first select Inbox (writable)"))));
+			
+				// Looks like we need to do the select...
+				DoSelectL(GetInbox(), ETrue);
+				}
+			}		
+		else
+			{
+			DBG((LogText(_L8("Do not re-issue IDLE - cancel completed"))));
+			
+			// Cancelling completed - stay in this mailbox and do not issue idle.
+			iCancelAndIdle = EFalse;
+			}			
+		} break;
+		
+	case EImapStateMoveEntryWait:
+		{
+		// We're cancelling a move entry: we need to stop it specifically
+		iMoveEntry->Cancel();
+		iState=iSavedState;	
+		
+		// Recurse (yuk!) into method with the new 'saved' state.	
+		CancelAndIdleL(aReissueIdle);
+		return;
+		}
+		// No break statement required due to return statement
+
+	default:
+		// For all other states - just do a 'normal' cancel - this will probably
+		// disconnect the session.
+		iCancelAndIdle = EFalse;
+		Cancel();
+		return;
+		}
+
+	// Need to complete the parent/observer.
+	CMsgActive::DoCancel();
+	}
+	
+// Called when parent wants to cancel current operation
+void CImImap4Session::DoCancel()
+	{
+	DBG((LogText(_L8("CImImap4Session::DoCancel() called whilst in state %d"),iState)));
+
+	if(IsIdling())
+		{
+		iIdleRead->Cancel();
+		}
+	else 
+		{
+		if(iAttachmentFile && iAttachmentFileState==EFileIsOpen)
+			{
+			DBG((LogText(_L8("CImImap4Session::DoCancel() closing attachment file"))));
+			if(iCaf->Processing())
+				{
+				TRAP_IGNORE(iCaf->EndProcessingL());
+				}
+			else
+				{
+				iAttachmentFile->CloseFile();
+				}
+			iAttachmentFileState=EFileNotOpen;
+			}
+	
+		// What were we about to do?
+		switch(iState)
+			{
+		case EImapStateConnectWait:
+		case EImapStateGreetingWait:
+		case EImapStateLoginSendUser:
+		case EImapStateLoginSendPassword:
+		case EImapStateLoginWait:
+			DoDisconnect();
+			break;
+			
+		case EImapStateSelectWait:
+			// Selecting: fail back to noselect
+			iState=EImapStateNoSelect;
+			break;
+
+		case EImapStateMoveEntryWait:
+			// We're cancelling a move entry: we need to stop it specifically
+			iMoveEntry->Cancel();
+			iState=iSavedState;
+			break;
+
+
+		default:
+			// Something else: disconnect for safety
+			DoDisconnect();
+			break;
+			}
+
+		// Note tag which we've cancelled: anything outstanding, basically,
+		// which means anything up to (and including) the last command issued,
+		// which is iTag
+		iCancelledTag=iTag;
+
+		iImapIO->Cancel();
+		}
+ 
+	iIdleTimer->Cancel();
+
+	DBG((LogText(_L8("CImImap4Session::DoCancel() finished 1"))));
+
+	// ...ask parent to finish up
+	CMsgActive::DoCancel();
+
+	DBG((LogText(_L8("CImImap4Session::DoCancel() finished 2"))));
+	}
+
+// Disconnect and complete with an error code
+void CImImap4Session::Fail(const TInt aError)
+	{
+	DBG((LogText(_L8("CImImap4Session::Fail(%d)"),aError)));
+	DoDisconnect();
+        DBG((LogText(_L8("-----------------------------------------------------------"))));
+	DBG((LogText(_L8("CImap4Session::Fail(): calling Complete()"))));
+	DBG((LogText(_L8("-----------------------------------------------------------"))));
+	Complete(aError);
+	}
+	
+void CImImap4Session::DoDisconnect()
+	{
+	DBG(LogText (_L8("CImImap4Session::DoDisconnect()")));
+	iImapIO->Disconnect();
+	iState = EImapStateDisconnected;
+	iSecurityState = EUnknown;
+	iCommandsOutstanding = 0;
+	iSendQueued = EFalse;
+	iReceiveQueued = EFalse;
+	iReissueIdle = EFalse;
+	iCancelAndIdle = EFalse;
+	iIdleTimer->Cancel(); 
+	iIdleTimerExpired = EFalse;
+	}
+
+void CImImap4Session::DummyComplete(TInt aError)
+	{
+	DBG(LogText(_L8("+ CImImap4Session::DummyComplete(err=%d)"), aError));
+	if(aError >= KErrNone)
+		{ // Got a response, let's have a look at it
+		CImapAtom* p=iImapIO->RootAtom()->Child();
+		if (!p)
+			{
+			aError = KErrNotFound;
+			return;
+			}
+
+		if (p->Compare(KIMAP_UNTAGGED))
+			{
+			// Process it, it could be something useful
+			TRAP( aError, ProcessUntaggedL(p->ToNextL(),EFalse) );
+			DBG( LogText(_L8("Dummy read untagged msg processed with %d"), aError) );
+			}
+		else
+			{
+			// Got a valid response that wasn't untagged... uhoh!
+			DBG( LogText(_L8("Dummy read received a non-untagged response!")) );
+			aError = KErrCorrupt;
+			}
+		}
+
+	if(aError < KErrNone)
+		{
+		// If the dummy read returned an error then the line was probably dropped,
+		// or, if the read returned something that was rubbish then the server is 
+		// not playing by the rules.
+		// Either way, let's disconnect.
+		LostConnection(aError);
+
+		// Can't report the error, no one to report to
+ 		}
+	DBG( LogText(_L8("- CImImap4Session::DummyComplete()")) );
+	}
+
+void CImImap4Session::LostConnection(TInt /*aError*/)
+	{
+	// the line must have been dropped so call DoDisconnect to ensure state is up
+	// to date
+	DoDisconnect();
+
+	// mark service as offline immediately
+	// the returned error code ignored
+	if (iEntry->SetEntry(iServiceId))
+		{
+		TMsvEntry entry=iEntry->Entry();
+		entry.SetConnected(EFalse);
+		iEntry->ChangeEntry(entry);
+		}
+	}
+
+
+void CImImap4Session::IdleReadError(TInt aError)
+	{	
+	 // Read completed with an error, probably lost the connection
+	DBG(LogText(_L8("IMAP Idle outstanding read completed with %d"), aError));
+
+	LostConnection(aError);
+
+	// Stop the idle timer so that it does not try to restart the idle when it expires
+	iIdleTimer->Cancel();
+	}
+ 
+
+void CImImap4Session::IssueIdleRead()
+	{
+	__ASSERT_DEBUG(IsIdling() && !IsActive(), gPanic(EBadUseOfImap4Op));
+	DBG((LogText(_L8("Idle read issued"))));
+	iIdleRead->Start(iStatus);
+	DBG((LogText(_L8("******************************************************************"))));
+	DBG((LogText(_L8("CImImap4Session::IssueIdleRead(): waiting for iIdleRead to wake me"))));
+	DBG((LogText(_L8("******************************************************************"))));
+ 	SetActive();
+ 	}
+	
+
+void CImImap4Session::IssueDummy()
+	{
+#ifdef ASYNC_NOTIFICATIONS
+	// Issue a dummy read from the CImapIO class so we can check the connection
+	// status. ONLY IF WE'RE CONNECTED!
+	if (iState>=EImapStateNoSelect)
+		{
+		iDummyRead->Start();
+		}
+#endif
+	}
+
+void CImImap4Session::CancelDummy()
+	{
+#ifdef ASYNC_NOTIFICATIONS
+	// Cancel it!
+	iDummyRead->Cancel();
+#endif
+	}
+
+void CImImap4Session::ReissueIdleL()
+	{
+	DBG((LogText(_L8("CImImap4Session::Re-issueIdle(): State: %d"), iState)));
+ 	Cancel();
+ 
+        DBG((LogText(_L8("CImImap4Session::ReIssueIdle(): setting iReissueIdle to true"))));
+ 	iReissueIdle=ETrue;
+	DoStopIdleL();
+	}
+
+void CImImap4Session::ReissueDummy()
+	{
+	IssueDummy();
+	}
+
+
+void CImImap4Session::DoComplete(TInt& aStatus)
+	{
+	DBG((LogText(_L8("CImImap4Session::DoComplete(iState=%d, aStatus=%d)"),iState,aStatus)));
+
+	if (iState == EImapStateSelected)
+		{
+		iCompoundStopIdle = EFalse;
+		iStoppingIdleForSync = EFalse;
+		}
+
+	if( iState != EImapStateFetchCancelWait &&
+		iAttachmentFile && iAttachmentFileState==EFileIsOpen )
+		{
+		// Do not close the attachment file if we're cancelling the fetch - we 
+		// will still be receiving data and if this completes the attachment
+		// then we want the attachment file available for that.
+		
+		DBG((LogText(_L8("CImImap4Session::DoComplete closing attachment file"))));
+		if(iCaf->Processing())
+			{
+			TRAP_IGNORE(iCaf->EndProcessingL());						
+			}
+		else
+			{
+			iAttachmentFile->CloseFile();
+			}
+		iAttachmentFileState=EFileNotOpen;
+		}
+
+	// All ok?
+	if (aStatus==KErrNone)
+		{
+		// Everything is fine. However, we need to queue a dummy read from the
+		// CImapIO layer to ensure that we get notified if the connection dies
+		// unexpectedly		
+
+		// Update the progress error code first.
+		iProgress.iErrorCode=aStatus;
+
+		if (ImapIdleSupported()==EFalse)
+			{
+			IssueDummy();
+			}
+
+		if (IsIdling())
+			{
+			IssueIdleRead();
+			}
+
+		return;
+		}
+		
+	if( iCancelAndIdle )
+		{
+		// Record the error code and exit the method - ensure that we don't
+		// disconnect.
+		iProgress.iErrorCode=aStatus;
+		return;
+		}
+
+	// Some error has ocurred. Deal with it.
+	switch(iState)
+		{
+	case EImapStateCreateWait:
+	case EImapStateRenameWait:
+	case EImapStateDeleteWait:
+	case EImapStateSubscribeWait:
+		// A 'KErrIMAPNO' error isn't fatal to the connection
+		if (aStatus==KErrCancel)
+			{
+			// Back to previous state: these commands won't have
+			// disturbed it.
+			iState=iSavedState;
+			return;
+			}
+		else if (aStatus==KErrIMAPNO)
+			{
+			// Report error
+			if (iState == EImapStateDeleteWait)
+				iProgress.iErrorCode=KErrImapCantDeleteFolder;
+			else
+				iProgress.iErrorCode=KErrNotSupported;
+			aStatus=iProgress.iErrorCode;
+
+			// Back to previous state
+			iState=iSavedState;
+			return;
+			}
+
+		// Otherwise, process as per normal
+		break;
+
+	case EImapStateSelectWait:
+	case EImapStateSynchroniseWait:
+		// KErrIMAPNO isn't fatal, we just go back to the selected state
+		if (aStatus==KErrIMAPNO)
+			{
+			iState=EImapStateSelected;
+			return;
+			}
+		break;
+
+	case EImapStateMoveEntryWait:
+		// We're done with the moveentry
+
+		// Park the move entry again
+		iMoveEntry->SetEntry(NULL);
+		break;
+		
+	case EImapStateIdleWait:
+	case EImapStateStopIdleWait:
+		if (iIdleTimerExpired)
+			{
+			// error has occurred following re-issue of an IDLE command
+			// Notify the server MTM that the error has occurred as there
+			// is no outstanding asynchonous request on this session.
+			// (IDLE is issued autonomously by the IMAP Session).
+			iObserver.NonCompletedFailure();
+			}
+		break;	
+
+	case EImapStateFetchCancelWait:
+		// record the error (i.e. cancel) for progress and do not disconnect.
+		iProgress.iErrorCode=aStatus;
+		// drop through to next case... (as that is returning and so not disconnecting).
+	case EImapStateNoSelect:
+	case EImapStateSelected:
+	case EImapStateIdling:
+		return;
+
+	default:
+		break;
+		}
+
+	// If we get here with an error, then we need to disconnect.
+	// Earlier on, if there was a time when disconnection wasn't
+	// required, we would have returned.
+	DoDisconnect();
+	
+	// Save error code in progress
+	iProgress.iErrorCode=aStatus;
+	}
+
+// Copy a message: in fact, we move the entire message to the destination, but
+// then recreate the empty shell (no parts fetched) of the source
+void CImImap4Session::CopyMessage(TRequestStatus& aRequestStatus, const TMsvId aSourceFolder, const TMsvId aSource, const TMsvId aDestinationFolder, TMsvId* aNewSource, const TBool aRemoveOriginal)
+	{
+	TInt err=KErrNone;
+	if (!Connected())
+		{
+		Queue(aRequestStatus);
+		err=KErrDisconnected;
+		}
+	else
+		TRAP(err,CopyMessageL(aRequestStatus, aSourceFolder, aSource, aDestinationFolder, aNewSource, aRemoveOriginal));
+	if (err!=KErrNone)
+		{
+		// park moveentry if it fails to get going
+		if (iMoveEntry)
+			iMoveEntry->SetEntry(NULL);
+                DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::CopyMessage(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+		Complete(err);
+		}
+	}
+
+void CImImap4Session::CopyMessageL(TRequestStatus& aRequestStatus, const TMsvId aSourceFolder, const TMsvId aSource, const TMsvId aDestinationFolder, TMsvId* aNewSource, const TBool aRemoveOriginal)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND CopyMessage(%x (in %x) to %x)"),aSource,aSourceFolder,aDestinationFolder)));
+
+	Queue(aRequestStatus);
+
+	// Get a moveentry if we don't already have one
+	if (!iMoveEntry)
+		{
+		// Get a MoveEntry: we need to ask for one as a child of this entry, so
+		// move it to the root and ask for a child in the local service, which should
+		// always be there.
+		SetEntryL(KMsvRootIndexEntryId);
+
+		// Get child
+		iMoveEntry=iEntry->NewEntryL(KMsvLocalServiceIndexEntryId);
+		}
+
+	// Do the move, using the iMoveEntry CMsvServerEntry object, after parking our original
+	// one
+	iEntry->SetEntry(NULL);
+	User::LeaveIfError(iMoveEntry->SetEntry(aSourceFolder));	
+
+	DBG((LogText(_L8("About to call iEntry->MoveEntry(%x,%x) when in folder %x"),aSource,aDestinationFolder,aSourceFolder)));
+
+	// We're moving - note bits for bottom half handling
+	iMoveSource=aSource;
+	iMoveSourceFolder=aSourceFolder;
+	iNewSource=aNewSource;
+
+	// Cancel any dummy operation that might be outstanding
+	if (ImapIdleSupported()==EFalse)
+		{
+		CancelDummy();
+		}
+
+	// Trap around this so we can park moveentry if it fails
+	//DS - now selectively either Copy or Move.
+	aRemoveOriginal?
+		iMoveEntry->MoveEntryL(aSource,aDestinationFolder,iStatus):
+		iMoveEntry->CopyEntryL(aSource,aDestinationFolder,iStatus);
+
+	// Move into the new state and go active
+	iSavedState=iState;
+	iState=EImapStateMoveEntryWait;
+	if (!IsActive()) SetActive();
+	}
+
+#if 0
+// Debug only: print out a representation of the parse tree
+void CImImap4Session::showtree(CImapAtom *root,int indent)
+	{
+	TInt b=0;
+
+	// Run through children
+	do
+		{
+		TPtrC8 atom=root->Atom();
+		LogText(_L8("%03d Sibling %d: '%S'"),indent,b++,&atom);
+
+		if (root->Child())
+			showtree(root->Child(),indent+2);
+
+		root=root->Next();
+		}
+	while(root);
+	}
+#endif
+
+// Parse greeting message
+TInt CImImap4Session::ProcessGreetingL()
+	{
+	CImapAtom* p=iRootAtom->ToChildL();
+
+	// Should be a '*'
+	if (!p->Compare(KIMAP_UNTAGGED))
+		User::Leave(KErrGeneral);
+	
+	// Greeting line can be:
+	// * BYE ... (server busy)
+	// * PREAUTH ... (no login needed)
+	// * OK ... (normal)
+	p=p->ToNextL();
+	if (p->Compare(KIMAP_BYE))
+		{
+		// Server is busy
+		return(KErrImapServerBusy);
+		}
+	else if (p->Compare(KIMAP_PREAUTH))
+		{
+		// Already authorised, straight into Noselect
+		iSavedState=EImapStateNoSelect;
+		}
+	else if (p->Compare(KIMAP_OK))
+		{
+		// Need to login
+		iSavedState=EImapStateLoginWait;
+
+		// Is this a CC:Mail server? (paranoid mode)
+		if (p->Next())
+			{
+			if (p->Next()->Compare(_L8("CC:Mail")))
+				{
+				// We are, note it.
+				iTalkingToCCMail=ETrue;
+
+				DBG((LogText(_L8("We're talking to a CC:Mail server, modified fetch strategy enabled."))));
+				}
+			else if (p->Next()->Compare(_L8("OpenMail")) )
+				{
+				iTalkingToOpenMail=ETrue;
+				DBG((LogText(_L8("We're talking to an OpenMail server, modified fetch strategy enabled."))));
+				}
+			}
+		}
+
+	// Looks ok
+	return(KErrNone);
+	}
+
+// Parse select reply messages
+TInt CImImap4Session::ProcessCommandReplyL()
+	{
+	CImapAtom *p=iRootAtom->ToChildL();
+
+	// Which command does this reply belong to?
+	TInt thisis=iTag-(iCommandsOutstanding-1);
+
+	// A cancelled command?
+	TBool cancelled(EFalse);
+	if (thisis<=iCancelledTag)
+		{
+		cancelled=ETrue;
+		}
+
+	// '+' indicates ideling
+	if (p->Compare(KIMAP_CONTINUATION))
+		{
+		return(ProcessContinuationResponse(p->ToNextL()));
+		}
+
+	// '*' indicates an untagged message
+	if (p->Compare(KIMAP_UNTAGGED))
+		{
+		// Process it
+		return(ProcessUntaggedL(p->ToNextL(),cancelled));
+		}
+	
+	// If we got here, it's a tagged reply.
+	// Check it's the one we're expecting
+	TInt tag(0);
+	TInt error(p->Value(tag));
+	if (error!=KErrNone)
+		{
+		// Problem parsing
+		return error;
+		}
+	
+	// Some command sequencing debugging
+	DBG((LogText(_L8("Expecting tag %d, got tag %d"),thisis,tag)));
+
+	// One less outstanding command
+	iCommandsOutstanding--;
+
+	// If the tagged reply is for a command that had been cancelled,
+	// and there are still commands outstanding, then don't complete:
+	// instead, just keep reading replies.
+	if (tag < iCancelledTag + 1)
+		{
+		return KErrNotReady;
+		}
+
+	// Move on to result
+	p=p->ToNextL();
+
+#ifdef PRINTING
+	// Print success of failure
+	TPtrC8 n=p->Atom();
+	LogText(_L8("Result for command tag %d is '%S'"),iTag,&n);
+#endif
+	// If it's OK, pass it to untagged processor
+	if (p->Compare(KIMAP_OK))
+		{		
+		// It might have stuff like 'READ-WRITE' in it... but only if
+		// the next atom has a child (ie open bracket)
+		if (p->Next() && p->Next()->Child())
+			{
+			DBG((LogText(_L8("CImap4Session::ProcessCommandReply(): OK recieved with children"))));
+ 			// Ignore the return code: we've got our tagged reply!
+			ProcessUntaggedL(p,EFalse);
+			}
+		else
+			{
+			// received ok response
+			DBG((LogText(_L8("CImap4Session::ProcessCommandReply(): OK received - no children"))));
+			}
+		return(KErrNone);
+		}
+	else if (p->Compare(KIMAP_NO))
+		{
+		// The server didn't like this
+		return(KErrIMAPNO);
+		}
+
+	// It's not OK: there's been an error
+	return(KErrGeneral);
+	}
+
+// Parse Continuation Response
+TInt CImImap4Session::ProcessContinuationResponse(CImapAtom* /*aAtom*/)
+	{
+	if (iState==EImapStateIdleWait)
+		{
+		iState = EImapStateIdling;
+		if(!iIdleTimer->IsActive())
+			{
+			iIdleTimer->After(iIdleTimeout);
+			}
+		return KErrNone;
+		}
+	else
+		{
+		return KErrArgument;
+		}
+	}
+
+// Parse untagged messages
+TInt CImImap4Session::ProcessUntaggedL(CImapAtom *aAtom, const TBool aCancelled)
+	{
+	DBG((LogText(_L8("CImap4Session::ProcessUntaggedL(): running..."))));
+
+	CImapAtom *p=aAtom;
+
+	// Look at first atom
+	if (static_cast<TChar>(p->Atom()[0]).IsDigit())
+		{
+		// First atom is a number
+		TUint  msgnr(0);
+
+		// Got it ok?
+		if (p->Value(msgnr)!=KErrNone)
+			User::Leave(KErrArgument);
+
+		// Next atom will be one of:
+		// EXISTS, RECENT, FETCH, etc
+		p=p->ToNextL();
+		if (p->Compare(KIMAP_EXISTS))
+			{
+			// Mailbox size changed?
+			DBG((LogText(_L8("Mailbox size now %d was %d"),msgnr,iMailboxSize)));
+
+			// Note it
+			if (iMailboxSize != static_cast<TInt>(msgnr) )
+				{
+				// Set it to EXISTS
+				iMailboxSize=msgnr;
+
+				// if the EXISTS didn't report a change in size then
+				// pretend it wasn't received
+				iMailboxReceivedExists=ETrue;
+				}
+
+			// Resize index
+			iFolderIndex.SetSizeL(iMailboxSize);
+			}
+		else if (p->Compare(KIMAP_EXPUNGE))
+			{
+			// Note it
+			iMailboxReceivedExpunge=ETrue;
+			}
+		else if (p->Compare(KIMAP_RECENT))
+			{
+			// Note it
+			iMailboxRecent=msgnr;
+			}
+		else if (!aCancelled && p->Compare(KIMAP_FETCH))
+			{
+			// Process fetch: any fetch data following?
+			if (p->Next() && p->Next()->Child())
+				{
+				// Got an open bracket situation, looks good
+				return(ProcessFetchL(msgnr,p->Next()->Child()));
+				}
+			else
+				User::Leave(KErrGeneral);
+			}
+		else
+			{
+#ifdef PRINTING
+			// Unknown
+			TPtrC8 a=p->Atom();
+			LogText(_L8("Unknown reply '* %d %S'"),msgnr,&a);
+#endif
+			}
+		}
+	else
+		{
+		// First atom not a number. Is it OK?
+		if (p->Compare(KIMAP_OK))
+			{
+			// OK *can* be followed by bracketed attrib or attrib/value pair
+			// however, this is not always the case: for example, EXAMINE'ing
+			// a new folder with Netscape IMAP4rev1 Service 3.56 gives this
+			// response:
+			// 24/01/99 13:44:58 >> 53 EXAMINE "Thingy/trevor"
+			// 24/01/99 13:44:59 << * OK Reset UID sequence counter.
+			// This is totally legal in the spec, but not awfully useful to us as
+			// plain text messages are server-specific and are really for carbon-
+			// based lifeforms to read.
+			if ((p=p->Next())==NULL)
+				{
+				// No message. Just '* OK'. What a pointless waste of bandwidth.
+				return(KErrNotReady);
+				}
+
+			// Is this the start of a bracketed construct?
+			if (p->Compare(_L8("(")) ||
+				p->Compare(_L8("[")))
+				{
+				CImapAtom* child=p->ToChildL();
+
+				if (child->Compare(KIMAP_UIDVALIDITY))
+					{
+					// Save it
+					child=child->ToNextL();
+					if (child->Value(iUidValidity)!=KErrNone)
+						User::Leave(KErrArgument);
+					}
+				else if (child->Compare(KIMAP_UIDNEXT))
+					{
+					// Save it
+					child=child->ToNextL();
+					if (child->Value(iUidNext)!=KErrNone)
+						User::Leave(KErrArgument);
+					}
+				else if (child->Compare(KIMAP_READWRITE))
+					{
+					// Note read-write open
+					iMailboxWritable=ETrue;
+					}
+				else if (child->Compare(KIMAP_READONLY))
+					{
+					// Note read-only open
+					iMailboxWritable=EFalse;
+					}
+				else if (child->Compare(KIMAP_ALERT))
+					{
+ 					// alerts to be handled here, but return with no error until then
+					LogText(_L8("Alert received- SessionState:iState %d"),iState);	
+					if(iState==EImapStateIdling)
+						{
+						return KErrNone;
+						}
+					}
+				else
+					{
+#ifdef PRINTING
+					TPtrC8 unk=p->Atom();
+					LogText(_L8("* OK [%S ???]"),&unk);
+#endif
+					}
+				}
+			else
+				{
+#ifdef PRINTING
+				TPtrC8 unk=p->Atom();				
+				LogText(_L8("* OK %S ???"),&unk);
+#endif
+
+				if(iState==EImapStateIdling)
+					{
+					return(KErrNotReady);
+					}	
+				}
+			}
+		else if (!aCancelled && p->Compare(KIMAP_LIST))
+			{
+			// Ignore it unless we've got somewhere to save it
+			ProcessListL(p->ToNextL());
+			}
+		else if (!aCancelled && p->Compare(KIMAP_LSUB))
+			{
+			// Process subscription list reply
+			ProcessLsubL(p->ToNextL());
+			}
+		else if (!aCancelled && p->Compare(KIMAP_SEARCH))
+			{
+			// Process UID search reply
+			// Need to check that we actually received a list of UIDs in the response
+			// because under certain circumstances we can get a reply that lists no UIDs.
+			// This can happen if the search command specifies a set of UIDs but none
+			// of them can be found on the server because they have all been expunged.
+			// It can also happen if the search command includes a search string but
+			// no messages on the server match it.
+			if (p->Next())
+				{
+				ProcessSearchL(p->ToNextL());
+				}
+			}
+		else if (p->Compare(KIMAP_BYE))
+			{
+			// Are we already logging out?
+			if (iState!=EImapStateLogoutWait)
+				{
+				// Unexpected disconnection
+				// WRITE!
+				}
+			}
+		else if (p->Compare(KIMAP_NO))
+			{
+#ifdef PRINTING
+			// NO message from server. Display it.
+			LogText(_L8("Got NO:"));
+			while((p=p->Next())!=NULL)
+				{
+				TPtrC8 word(p->Atom());
+				LogText(_L8(" %S"),&word);
+				}
+#endif
+			if(iState==EImapStateIdling)
+					{
+					// ignore this and remain in IDLE.
+					return KErrNotReady;
+					}
+			}
+		else if (!aCancelled && p->Compare(KIMAP_FLAGS))
+			{
+			// FLAGS response during folder open
+			iMailboxReceivedFlags=ETrue;
+#ifdef PRINTING
+			LogText(_L8("Got FLAGS:"));
+			p=p->ToNextL();
+			p=p->ToChildL();
+			do
+				{
+				TPtrC8 word(p->Atom());
+				LogText(_L8(" %S"),&word);
+				p=p->Next();
+				}
+			while(p!=NULL);
+#endif
+			}
+		else if (p->Compare(KIMAP_CAPABILITY))
+			{
+			// clear here just for good measure
+			iSeenVersion=EFalse;
+			iCapabilityIdleSupport = EFalse;
+			iCapabilityStartTLS=EFalse;
+			iCapabilityLoginDisabled=EFalse;
+			
+			// CAPABILITY reply
+			while((p=p->Next())!=NULL)
+				{
+				if (p->Compare(KIMAP_VERSION))
+					iSeenVersion=ETrue;
+				else if (p->Compare(KIMAP_IDLE))
+					iCapabilityIdleSupport = ETrue;
+				else if (p->Compare(KIMAP_STARTTLS))
+					iCapabilityStartTLS=ETrue;
+				else if (p->Compare(KIMAP_LOGINDISABLED))
+					iCapabilityLoginDisabled=ETrue;
+				}
+			}
+		else
+			{
+#ifdef PRINTING
+			// Unknown
+			TPtrC8 a=p->Atom();
+			LogText(_L8("Unknown reply '* %S'"),&a);
+#endif
+			}
+		}
+
+	return(KErrNotReady);
+	}
+
+// Fill in a CImHeader from an envelope atom
+void CImImap4Session::ProcessEnvelopeL(CImHeader* aHeader, TMsvEntry& aEntry, CImapAtom *aAtom)
+	{
+	CImapAtom *q=aAtom->ToChildL();
+	TPtrC8 tptr;
+	// ensure that nothing is placed on the cleanup stack between here
+	// and calls to ProcessAddress/ProcessAddressList
+	HBufC8 *address=HBufC8::NewLC(KImapAddressSizeInc);
+
+	DBG((LogText(_L8("Processing envelope data"))));
+
+	// Parse date information
+	tptr.Set(q->Atom());
+	TImRfc822DateField date;
+	date.ParseDateField(tptr,aEntry.iDate);
+	q=q->ToNextL();
+
+	// Subject in CImHeader (TMsvEntry is later on after post-processing)
+	if (!q->Compare(KIMAP_NIL))
+		aHeader->SetSubjectL(q->Atom());
+	q=q->ToNextL();
+
+	// From information: both in CImHeader and TMsvEntry
+	if (q->Child())
+		{
+		DBG((LogText(_L8("Processing 'From' information"))));
+
+		ProcessAddressL(&address,q->ToChildL());
+		aHeader->SetFromL(address->Des());
+		}
+	else
+		{
+		// No From information. Set blank
+		aHeader->SetFromL(_L(""));
+		}
+	q=q->ToNextL();
+
+	// Discard sender information
+	q=q->ToNextL();
+
+	// ReplyTo information
+	if (q->Child())
+		{
+		DBG((LogText(_L8("Processing 'ReplyTo' information"))));
+
+		// Replyto exists
+		ProcessAddressL(&address,q->ToChildL());
+		aHeader->SetReplyToL(address->Des());
+		}
+	else
+		{
+		// No replyto. Use From info
+		aHeader->SetReplyToL(aHeader->From());
+		}
+	q=q->ToNextL();
+
+	// To information
+	DBG((LogText(_L8("Processing 'To' information"))));
+
+	ProcessAddressListL(&address,aHeader->ToRecipients(),q->Child());
+	q=q->ToNextL();
+			
+	// CC list
+	DBG((LogText(_L8("Processing 'CC' information"))));
+
+	ProcessAddressListL(&address,aHeader->CcRecipients(),q->Child());
+	q=q->ToNextL();
+
+	// BCC list
+	DBG((LogText(_L8("Processing 'BCC' information"))));
+
+	ProcessAddressListL(&address,aHeader->BccRecipients(),q->Child());
+	q=q->ToNextL();
+
+	// In-Reply-To
+	q=q->ToNextL();
+
+	// Message-Id
+	aHeader->SetImMsgIdL(q->AtomNoAngleBrackets());
+
+	// Decode any QP encoding in header fields
+	iHeaderConverter->DecodeAllHeaderFieldsL(*aHeader);
+
+	// Set from line in TMsvEntry
+	aEntry.iDetails.Set(aHeader->From());
+
+	// Set subject in TMsvEntry
+	aEntry.iDescription.Set(aHeader->Subject());
+
+	// Get rid of buffer
+	CleanupStack::PopAndDestroy();
+
+	DBG((LogText(_L8("Finished processing envelope information"))));
+	}
+
+void CImImap4Session::StripSpace(HBufC8* aBuf)
+	{
+	TInt len = aBuf->Length();
+	TInt in = 0;
+	TInt out = 0;
+	TPtr8 p = aBuf->Des();
+	while (in < len)
+		{
+		TUint8 c = p[in++];
+		if (c > ' ')
+			p[out++] = c;
+		}
+	// we could shrink the buffer here but we won't bother because it
+	// is going to get copied and freed anyway
+	}
+
+// Fill in a CImHeader from the extra header fields atoms, currently
+// Priority and Receipt info. aText is an extract direct from the
+// message header, ie lines of name: value\r\n terminated with an
+// empty line. Not known whether the lines can be folded so assume
+// they may.
+void CImImap4Session::ProcessHeaderExtraL(CImHeader* aHeader, CImMimeHeader* aMimeHeader, TMsvEmailEntry* aEntry, TPtrC8 aText)
+	{
+#ifdef _DEBUG
+	TPtrC8 dump = aText.Left(256);
+	DBG((LogText(_L8("Processing HeaderExtra data '%S'"), &dump)));
+#endif
+
+	// utils class
+	CImcvUtils* utils=CImcvUtils::NewLC();
+
+	TPtrC8 line = aText;
+	HBufC8* valueBuf = NULL;
+
+	TPtrC8 name;
+
+	TBool foundReplyToPrompt = EFalse;
+	// Check for content-type Application/xxx
+	// There may be a CAF agent ready to consume the content if it's DRM
+	// If aMimeHeader is set then this is the mime header prior to the actual mime section download
+	if(aMimeHeader && aMimeHeader->ContentType().MatchF(KImcvApplication) == 0)
+		{		
+		// CAF registration requires concatenated content-type and subtype
+		// The type and subtype have been received and stored.
+		// Create buffer for concatenating. + 1 creates space for '/' 
+		HBufC8* buf = HBufC8::NewLC(aMimeHeader->ContentSubType().Length() + aMimeHeader->ContentType().Length() + 1);
+		TPtr8 ptr(buf->Des());
+		ptr.Copy(aMimeHeader->ContentType());
+		ptr.Append(KImcvForwardSlash);
+		ptr.Append(aMimeHeader->ContentSubType());
+		// Registration does not necessarily succeed but we don't care at this point.
+		iCaf->RegisterL(ptr);
+		CleanupStack::PopAndDestroy(buf);
+		}
+
+	while (line.Length())
+		{
+		TBool processPrevious = valueBuf != NULL;
+		TPtrC8 current;
+
+		TInt len = line.Find(KImcvCRLF);
+		if (len > 0)
+			{
+			// split line into this one and the rest
+			current.Set(line.Left(len));
+			line.Set(line.Mid(len+2));
+
+			// handle folded headers
+			if (current[0] <= ' ' && valueBuf)
+				{
+				HBufC8* buf=valueBuf->ReAllocL( valueBuf->Length() + current.Length() );
+				if (buf!=valueBuf)
+					{
+					CleanupStack::Pop();
+					CleanupStack::PushL(valueBuf=buf);
+					}
+				valueBuf->Des().Append( current );
+
+				processPrevious = EFalse;
+				}
+			}
+		else
+			{
+			// set line to null
+			line.Set(line.Left(0));
+			}
+		
+		// find matching headers, can only be set if valueBuf was
+		// non-null
+		if (processPrevious)
+			{
+			// Dont put the following line back in as will cause a panic if the subject
+			// field is too long. Defect EXT-53KD67.
+			//DBG((LogText(_L8("header: name %S value %S"), &name, valueBuf)));
+
+			if (aEntry)
+				{
+				CDesC8ArrayFlat* array = new(ELeave) CDesC8ArrayFlat(4);
+			  	CleanupStack::PushL(array);
+			  	CImcvUtils::PriorityFieldsL(*array);
+			  	for (TInt i(0); i<array->Count(); i++)
+				  	{
+					if(name.CompareF((*array)[i])==0)
+				  		{
+						aEntry->SetPriority(utils->EvaluatePriorityText(*valueBuf));
+				  		}
+				  	}
+			  	CleanupStack::PopAndDestroy(array); 
+				}
+
+			if (aHeader)
+				{
+				CImcvUtils* imcvUtils = CImcvUtils::NewLC();
+				if (imcvUtils->EvaluateReturnReceiptFields(name))
+					{
+					aHeader->SetReceiptAddressL(*valueBuf);
+
+					// Only set Receipt flag if this email has not
+					// been 'seen' by somebody - to prevent multiple
+					// notifications
+					if (!aEntry->SeenIMAP4Flag())
+						aEntry->SetReceipt(ETrue);
+					}
+					
+				if((name.CompareF(KImcvFromPrompt))==0)
+					{
+					// Set from line in TMsvEntry
+					aHeader->SetFromL(*valueBuf);
+					}
+				else if((name.CompareF(KImcvSubjectPrompt))==0)
+					{
+					// Set subject in TMsvEntry
+					aHeader->SetSubjectL(*valueBuf);
+					}
+				else if((name.CompareF(KImcvDatePrompt))==0)
+					{
+					if(!iParsedTime)
+						{
+						// Set date in TMsvEntry
+						TImRfc822DateField date;
+						date.ParseDateField(*valueBuf,aEntry->iDate);	
+						}
+					}
+				else if((name.CompareF(KImcvReceivedPrompt))==0)
+					{
+					if(!iParsedTime)
+						{
+						// Set date in TMsvEntry
+						TImRfc822DateField date;
+						
+						//remove the data before the comma, to just leave the date				
+						TPtr8 ptr(valueBuf->Des());
+						TInt lPos=ptr.Locate(';');	
+						ptr = ptr.Right(ptr.Length()-lPos-2);				
+						date.ParseDateField(ptr,aEntry->iDate);
+						iParsedTime=ETrue;	
+						}
+					}
+				else if((name.CompareF(KImcvReplyToPrompt))==0)
+					{
+					aHeader->SetReplyToL(*valueBuf);
+					foundReplyToPrompt = ETrue;
+					}
+				else if((name.CompareF(KImcvMessageIdPrompt))==0)
+					aHeader->SetImMsgIdL(*valueBuf);
+				else if((name.CompareF(KImcvToPrompt))==0)
+					ProcessAddressListL(aHeader->ToRecipients(), &valueBuf);
+				else if((name.CompareF(KImcvCcPrompt))==0)
+					ProcessAddressListL(aHeader->CcRecipients(), &valueBuf);
+				else if((name.CompareF(KImcvBccPrompt))==0)
+					ProcessAddressListL(aHeader->BccRecipients(), &valueBuf);
+
+				CleanupStack::PopAndDestroy(); // imcvUtils
+
+				// we are currently ignoring DispositionOptions as
+				// there is nowhere to store it
+				}
+
+			if (aMimeHeader)
+				{
+				// Check to see if this extra header data should be passed to the CAF agent				
+				if(iCaf->Registered())
+					{
+					iCaf->AddToMetaDataL(name,valueBuf->Des());
+					}
+				if (name.CompareF(KImcvContentBase) == 0)
+					{
+					StripSpace(valueBuf);
+					aMimeHeader->SetContentBaseL(*valueBuf);
+					}
+				else if (name.CompareF(KImcvContentLocation) == 0)
+					{
+					StripSpace(valueBuf);
+
+					HBufC *decoded=HBufC::NewLC(valueBuf->Length());
+					TPtr decoded_ptr(decoded->Des());
+
+					iHeaderConverter->DecodeHeaderFieldL(*valueBuf,decoded_ptr);
+					aMimeHeader->SetContentLocationL(*decoded);
+					CleanupStack::PopAndDestroy(); // decoded
+					}
+				}
+
+			CleanupStack::PopAndDestroy(); // valueBuf
+			valueBuf = NULL;
+			}
+
+		if (current.Length() && current[0] > ' ')
+			{
+			// split this line into name and value
+			TInt colon = current.Locate(':');
+
+			name.Set(current.Left(colon+1)); // include the colon
+			TPtrC8 value = current.Mid(colon+1);
+			
+			// skip any initial WS in the value
+			while (value.Length() != 0 && value[0] <= ' ')
+				value.Set(value.Mid(1));
+
+			valueBuf = value.AllocLC();
+			}
+		}	
+
+	if (aHeader)
+		{
+		// If no reply to information, use the From value
+		if (!foundReplyToPrompt)
+			{
+			aHeader->SetReplyToL(aHeader->From());
+			}
+
+		// Decode any QP encoding in header fields
+		iHeaderConverter->DecodeAllHeaderFieldsL(*aHeader);
+
+		// Set from line in TMsvEntry
+		aEntry->iDetails.Set(aHeader->From());
+
+		// Set subject in TMsvEntry
+		aEntry->iDescription.Set(aHeader->Subject());
+		}	
+
+	// just in case 
+	if (valueBuf)
+		CleanupStack::PopAndDestroy(); // valueBuf
+
+	// pop off the items allocated
+	CleanupStack::PopAndDestroy(); // utils
+	
+	DBG((LogText(_L8("Finished processing HeaderExtra"))));
+	}
+
+// adapted this function from the one in CImRecvConvert
+void CImImap4Session::GetDefaultFilename(TDes& aName, const TMsvEmailEntry& aMessage, const CImMimeHeader* mime)
+	{
+	aName = *iDefaultAttachmentName;
+
+	// Add on appropriate extension
+	if (aMessage.iType == KUidMsvEmailTextEntry)
+		{
+		aName.Append(KTextExtension);
+		}
+	else if (aMessage.MHTMLEmail())
+		{
+		aName.Append(KHtmlExtension);
+		}
+	else if (aMessage.VCard() || aMessage.VCalendar())
+		{
+		aName.Append(KVCardExtension);
+		}
+	else if (aMessage.ICalendar())
+		{
+		aName.Append(KICalExtension);
+		}
+	else if ( aMessage.iType == KUidMsvAttachmentEntry )
+		{
+ 		if ( (mime->ContentSubType()==KImcvBmp) ||
+			 (mime->ContentSubType()==KImcvGif) ||
+			 (mime->ContentSubType()==KImcvJpeg) ||
+			 (mime->ContentSubType()==KImcvTiff) ||
+			 (mime->ContentSubType()==KImcvWav) )
+			{
+			TBuf<KMaxExtensionLength> buf;
+			buf.Copy(mime->ContentSubType());
+			aName.Append(KImcvFullStop);
+			aName.Append(buf);
+			}
+		}
+	}
+
+TBool CImImap4Session::DoesAtomContainAttachment(CImapAtom *aAtom)
+// Check through all of this Atom's Siblings to see if they contain an attachment
+	{
+	TBool hasAttachment = EFalse;
+	CImapAtom* currentAtom = aAtom;
+
+	// Search through all of the Sibling Atoms
+	while (currentAtom != NULL)
+		{
+		// Check if there is a Child Atom with an Attachment
+		if (currentAtom->Child() != NULL)
+			{
+			if (currentAtom->Child()->Compare(KMIME_ATTACHMENT))
+				{
+				// This Sibling contains an Attachment.
+				hasAttachment = ETrue;
+				break;
+				}
+			}
+
+		// Move onto the next sibling
+		currentAtom = currentAtom->Next();
+		}
+
+	return hasAttachment;
+	}
+
+// Build a single entry
+void CImImap4Session::BuildTreeOneL(const TMsvId aParent, CImapAtom *aAtom, const TDesC8& aPath,
+									const TMsvId aThisMessage, TInt& aAttachments, TBool& aIsMHTML, TInt& aRelatedAttachments)
+	{
+	DBG((LogText(_L8("BuildTreeOneL(message=%x, parent=%x)"),aThisMessage,aParent)));
+
+	// First, is this actually an entry, or another level of nesting?
+	if (aAtom->Child())
+		{
+		// Another level of nesting? Call BuildTreeL()
+		BuildTreeL(aParent,aAtom,aPath,aThisMessage,aAttachments,aIsMHTML, aRelatedAttachments);
+		return;
+		}
+
+	// Skeleton for new entry
+	SetEntryL(aParent);
+
+	TFileName attachmentFilename;	//	DS somewhere to store an attachment filename
+	TMsvEmailEntry message;
+	message.iSize=0;
+	message.iMtm=KUidMsgTypeIMAP4;
+	message.iServiceId=iServiceId;
+	message.SetUID(iMessageUid);
+	message.SetValidUID(ETrue);
+	message.SetComplete(EFalse);
+
+	// Reply from server is in this form:
+	// TYPE SUBTYPE (PARAM1 VALUE1 ...) ID DESCRIPTION ENCODING OCTETS
+	//
+	// Text parts:
+	// TYPE SUBTYPE (PARAM1 VALUE1 ...) ID DESCRIPTION ENCODING OCTETS NLINES
+
+	// Save mime TYPE/SUBTYPE
+	CImMimeHeader *mime=CImMimeHeader::NewLC();
+	CImapAtom *type=aAtom;
+	CImapAtom *subtype=aAtom->Next();
+	mime->SetContentTypeL(type->Atom());
+	mime->SetContentSubTypeL(subtype->Atom());
+
+#ifdef PRINTING
+	TPtrC8 mt=type->Atom(),ms=subtype->Atom();
+	LogText(_L8("  MIME type %S/%S"),&mt,&ms);
+#endif
+
+	// We start by assuming the data will be stored as a binary
+	// attachment
+	message.iType=KUidMsvAttachmentEntry;
+	if (type->Compare(KMIME_TEXT))
+		{
+		// text/html?
+		if (subtype->Compare(KMIME_HTML))
+			{
+			//  If this Atom doesn't contain an Attachment, then this is a MHTML Message.
+			if (!DoesAtomContainAttachment(subtype))
+				{
+				message.iType=KUidMsvEmailHtmlEntry;
+				aIsMHTML=ETrue;	
+				}	
+			}
+		// text/x-vcard?
+		else if (subtype->Compare(KMIME_XVCARD))
+			{
+			// Set vCard flag in message
+			message.SetVCard(ETrue);
+
+			// Defaults to binary
+			}
+		// text/x-vcalendar
+		else if (subtype->Compare(KMIME_VCALENDAR))
+			{
+			// Set vCalendar flag in message
+			message.SetVCalendar(ETrue);
+			iIsVCalendar = ETrue;
+			
+			// Defaults to binary
+			}
+		// text/calendar
+		else if (subtype->Compare(KMIME_ICALENDAR))
+			{
+			// Set iCalendar flag in message
+			message.SetICalendar(ETrue);
+			iIsICalendar = ETrue;
+			
+			// Defaults to binary
+			}
+		else
+			message.iType=KUidMsvEmailTextEntry;
+		}
+
+	// ...and mime path
+	mime->SetRelativePathL(aPath);
+
+	DBG((LogText(_L8("  MIME path %S"),&aPath)));
+
+	// Parameter list
+	CImapAtom *parameter=subtype->ToNextL();
+
+	TUint charset = KUidMsvCharsetNone;
+
+	// Store parameter stuff
+	if (!parameter->Compare(KIMAP_NIL))
+		{
+		DBG((LogText(_L8("  Parameter list:"))));
+
+		// Process list
+		CImapAtom *type_param=parameter->ToChildL();
+		while(type_param && type_param->Next())
+			{
+			CImapAtom *type_value;
+			type_value=type_param->ToNextL();
+
+			// All items are 2-tuples (parameter value (...)): get both, and store
+			TPtrC8 param=type_param->Atom();
+			TPtrC8 value=type_value->Atom();
+
+			DBG((LogText(_L8("    %S %S"),&param,&value)));
+		
+			mime->ContentTypeParams().AppendL(param);
+			mime->ContentTypeParams().AppendL(value);
+		
+			// Have we come across a 'NAME' tuple? If so, force the MIME type of this
+			// entry to be an attachment.
+			if ((param.CompareF(KMIME_NAME)==0)
+				|| (param.CompareF(KMIME_NAME_RFC2231) == 0))
+				{
+				DBG((LogText(_L8("It has an attachment filename, therefore this is an attachment"))));
+
+				FindFilenameDecodeL(*mime,attachmentFilename);
+				StripIllegalCharactersFromFileName(attachmentFilename);
+				message.iDetails.Set(attachmentFilename);
+
+				// If embedded message do not save as an attachment
+				if (message.iType!=KUidMsvMessageEntry)
+					message.iType=KUidMsvAttachmentEntry;
+				}
+			else if (param.CompareF(KImcvCharset)==0)
+				{
+				// Set the Mime charset from the parameter value
+				if (value.Length() != 0)
+					{
+					charset = iCharConv->GetMimeCharsetUidL(value);
+					}
+				}
+
+
+			// Next item
+			type_param=type_value->Next();
+			}
+		}
+
+	mime->SetMimeCharset(charset);
+
+	// ID: save it
+	CImapAtom *id=parameter->ToNextL();
+	if (!id->Compare(_L8("NIL")))
+		mime->SetContentIDL(id->AtomNoAngleBrackets());
+
+	// Description: save it
+	CImapAtom *description=id->ToNextL();
+	if (!description->Compare(_L8("NIL")))
+		mime->SetContentDescriptionL(description->Atom());
+
+	// Encoding
+	CImapAtom *encoding=description->ToNextL();
+	mime->SetContentTransferEncodingL(encoding->Atom());
+
+#ifdef PRINTING
+	TPtrC8 enc=encoding->Atom();
+	LogText(_L8("  Encoding %S"),&enc);
+#endif
+
+	// Octets (encoded form)
+	CImapAtom *octets=encoding->ToNextL();
+	TInt actualsize;
+	if (octets->Value(actualsize)!=KErrNone)
+		User::Leave(KErrGeneral);
+
+	// Twiddle this to show *decoded* size: this is basically the size of
+	// this part, multiplied by 6/8 if it's BASE64 encoded. For all other
+	// encodings, we leave the size as-is as there's no hard & fast rule
+	// which can be applied.
+	if (encoding->Compare(KMIME_BASE64))
+		message.iSize=(actualsize*6)/8;
+	else
+		message.iSize=actualsize;
+
+	// Add into total message size
+	iDecodedSizeOfAllParts+=message.iSize;
+
+	// Store *remote* size in a dodgy place
+	message.iBioType=actualsize;
+
+	//If any part of email (text/plain mime, text/html mime, attachment....) 
+	// is empty then should not fetch it.
+	if(actualsize == 0)
+		{
+		message.SetComplete(ETrue);
+		}
+
+#ifdef PRINTING
+	LogText(_L8("  Octets %d"),message.iBioType);
+
+	TPtrC8 type_p=type->Atom(),subtype_p=subtype->Atom();
+	LogText(_L8("Building mime stuff: %S/%S"),&type_p,&subtype_p);
+#endif
+
+	// MD5 block will start after any optional parts
+	CImapAtom *md5;
+
+	if (type->Compare(KMIME_MESSAGE) && subtype->Compare(KMIME_RFC822))
+		{
+		// Skip RFC822 header, which should *all* be present
+		// Like this for clarity
+		CImapAtom *envelope=octets->ToNextL();
+		CImapAtom *structure=envelope->ToNextL();
+		CImapAtom *nooflines=structure->ToNextL();
+
+		// embedded message - marked as a message
+		message.iType=KUidMsvMessageEntry;
+
+		iDecodedSizeOfAllParts-=message.iSize;
+
+		// Next atom is MD5 - IF PRESENT
+		md5=nooflines->Next();
+		}
+	else
+		{
+		// Find MD5 block: if this part is TEXT/* we have number of lines next
+		if (type->Compare(KMIME_TEXT))
+			{
+			// Number of lines is next atom, followed by MD5 - IF PRESENT
+			CImapAtom *nooflines=octets->ToNextL();
+			md5=nooflines->Next();
+			}
+		else		
+			md5=octets->Next();
+		}
+
+	// Do we have any extended fields? If so, deal with them
+	if (md5)
+		{
+		// Next (if present) is Content-Disposition, closely followed by language
+		CImapAtom *disposition=md5->Next();
+		CImapAtom *language=(disposition==NULL)?NULL:disposition->Next();
+		language=language; // Stop .aer warnings: we know it's not used (yet)
+
+		DBG((LogText(_L8("Processing content-disposition"))));
+
+		// Store disposition stuff
+		if (disposition && !disposition->Compare(KIMAP_NIL))
+			{
+			// Process list
+			CImapAtom *pos=disposition->Child();
+			while(pos)
+				{
+				// Single item (eg "INLINE") or 2-tuple (eg ("FILENAME" "blah.gif"))?
+				if (pos->Child())
+					{
+					// Tuple
+					CImapAtom* tuple = pos->ToChildL();
+					while(tuple)
+						{
+						mime->ContentDispositionParams().AppendL(tuple->Atom());
+						mime->ContentDispositionParams().AppendL(tuple->ToNextL()->Atom());
+
+						// Filename? If so, force this as an attachment
+						if ((tuple->Atom().CompareF(KMIME_FILENAME)==0)
+							|| (tuple->Atom().CompareF(KMIME_FILENAME_RFC2231)==0))
+							{
+							DBG((LogText(_L8("It has an attachment filename, therefore this is an attachment"))));
+							FindFilenameDecodeL(*mime,attachmentFilename);
+							StripIllegalCharactersFromFileName(attachmentFilename);
+							message.iDetails.Set(attachmentFilename);
+
+							// If embedded message do not save as an attachment
+							if (message.iType!=KUidMsvMessageEntry)
+								message.iType=KUidMsvAttachmentEntry;
+							}
+
+						// Skip to next tuple
+						tuple = tuple->ToNextL()->Next();
+						}
+					}
+				else
+					{
+					// Single item
+					mime->ContentDispositionParams().AppendL(pos->Atom());
+					mime->ContentDispositionParams().AppendL(_L8(""));
+					}
+			
+				// Skip to next entry
+				pos=pos->Next();
+				}
+			}
+		}
+
+	// Now we're working on the type
+	if (message.iType==KUidMsvMessageEntry)
+		{
+		// MESSAGE/RFC822
+		// This means that the next atom will be the envelope info, and the
+		// one following that will be the body structure of the embedded
+		// message.
+		//
+		// This is an entire message-within-a-message and so gets treated like
+		// an actual mail (has it's own multipartdata thing)
+	
+		// Make CImHeader bits
+		CImHeader *messageheader=CImHeader::NewLC();
+		CImapAtom *envelope=octets->ToNextL();
+		ProcessEnvelopeL(messageheader,message,envelope);
+
+		// Create message
+		User::LeaveIfError(iEntry->CreateEntryBulk(message));
+		SetEntryL(message.Id());
+
+		// Store CImHeader bits
+		CMsvStore* entryStore=iEntry->EditStoreL();
+		CleanupStack::PushL(entryStore);
+		messageheader->StoreL(*entryStore);
+		mime->StoreL(*entryStore);
+		entryStore->CommitL();
+		CleanupStack::PopAndDestroy(3);
+
+#if SET_RELATED_ID
+		//	DS - Set message's iRelatedId to messageId to allow later UI kludges
+		TMsvEntry changeEntry(iEntry->Entry());
+		changeEntry.iRelatedId=changeEntry.Id();
+		ChangeEntryBulkL(changeEntry);
+#endif
+		// Descend into attachments of this embedded message
+		CImapAtom *structure=envelope->ToNextL();
+		TInt attachments=0;
+		TBool isMHTML=EFalse;
+
+		BuildTreeL(message.Id(),structure->ToChildL(),aPath,message.Id(),attachments,isMHTML,aRelatedAttachments);
+		DBG((LogText(_L8("Build embedded message id %x attachments %d MHTML %d"),message.Id(),attachments,isMHTML)));
+
+		// Save attachment and MHTML flags
+		if (attachments>0 || isMHTML)
+			{
+			SetEntryL(message.Id());
+			TMsvEmailEntry thisMessage=iEntry->Entry();
+
+			if (attachments>0)
+				{
+				thisMessage.SetAttachment(ETrue);
+				}
+
+			if (isMHTML)
+				{
+				thisMessage.SetMHTMLEmail(ETrue);
+				}
+
+			ChangeEntryBulkL(thisMessage);
+			}
+
+		// we are now counting embedded messages as attachments
+		aAttachments++;
+		}
+	else
+		{
+		// Something else - create an attachment entry
+		SetEntryL(aParent);
+
+		// save parent folder type
+		TImEmailFolderType parentFolderType = ((TMsvEmailEntry)iEntry->Entry()).MessageFolderType();
+	
+		// set attachment and HTML flags on item
+		if ( message.iType==KUidMsvAttachmentEntry)
+			message.SetAttachment(ETrue);
+
+		if ( message.iType==KUidMsvEmailHtmlEntry)
+			message.SetMHTMLEmail(ETrue);
+
+		// ensure there is a filename if it is a non-text item (which
+		// also controls iFetchIsText, the flag used in DecodeAndStore
+		// to say whether to stream to a file or RichText store.
+		if (message.iType!=KUidMsvEmailTextEntry && message.iDetails.Length() == 0)
+			{
+			// use iAttachmentName for temporary buffer
+			GetDefaultFilename(iAttachmentName, message, mime);
+			message.iDetails.Set(iAttachmentName);
+			}
+		
+		User::LeaveIfError(iEntry->CreateEntryBulk(message));
+		SetEntryL(message.Id());
+		
+		DBG((LogText(_L8("Created attachment id %x as child of %x - type %d"),message.Id(),aParent, parentFolderType)));
+
+#if SET_RELATED_ID
+		//	DS - Set message's iRelatedId to messageId to allow later UI kludges
+		TMsvEntry changeEntry(iEntry->Entry());
+		changeEntry.iRelatedId=changeEntry.Id();
+		ChangeEntryBulkL(changeEntry);
+#endif
+
+		DBG((LogText(_L8("Streaming MIME info into id %x"),iEntry->Entry().Id())));
+
+		// Stream the MIME info out into the message
+		// This will either stream it to the actual message (if the above if
+		// evaluated to True, the entry is still set to the message), or to
+		// the newly created child
+		CMsvStore* entryStore=iEntry->EditStoreL();
+		CleanupStack::PushL(entryStore);
+		mime->StoreL(*entryStore);
+		entryStore->CommitL();	
+		CleanupStack::PopAndDestroy(2, mime);
+
+    	// This entry is NOT an attachment in the following cases - 
+		// 1)	This is an attachment whose parent is a MULTIPART/RELATED folder.
+		//		In this case, this entry could be a image entity for an MHTML
+		//		entry with the same parent.
+		// 2)	This is an MHTML entry whose parent is a MULTIPART/ALTERNATIVE 
+		//		folder. In this case, this entry is the MHTML alternative to a
+		//		text entry with the same parent.
+		// 3)	This is an MHTML entry whose parent is MESSAGE folder. In this
+		//		case, the message is a simple MHTML message with no text 
+		//		alternative or embedded image.
+		// 4)	This is an MHTML entry whose parent is a MULTIPART/RELATED folder.
+		//		In this case, this entry is the MHTML for the message.
+		// 5)	This is an MHTML entry whose parent is a MULTIPART/MIXED folder.
+		//		In this case, this entry is the MHTML for the message. It cannot
+		//		be the attachment it self as then it would be of type attachment.
+		// Therefore, an entry is only an attachment if is of type attachment and
+		// its parent is not a MULTIPART/RELATED folder.
+    	if( message.iType==KUidMsvAttachmentEntry && parentFolderType != EFolderTypeRelated )
+			{
+    		++aAttachments;
+			}
+		// if it is related we might want to include it if the message
+		// turns out not to be MHTML
+		else if ( message.iType==KUidMsvAttachmentEntry &&
+			parentFolderType == EFolderTypeRelated )
+			{
+			++aRelatedAttachments;
+			}
+		}
+
+	DBG((LogText(_L8("BuildTreeOneL done: created id %x, attachments so far %d"), message.Id(), aAttachments)));
+	}
+
+// Build attachment tree below a message
+void CImImap4Session::BuildTreeL(TMsvId aParent, CImapAtom *aAtom, const TDesC8& aPath,
+								 const TMsvId aThisMessage, TInt& aAttachments, TBool& aIsMHTML, TInt& aRelatedAttachments)
+	{
+	DBG((LogText(_L8("BuildTreeL(message=%x, parent=%x"),aThisMessage,aParent)));
+
+	// One attachment only?
+	if (aAtom->Child()==NULL)
+		{
+		// Deal with the single entry (doesn't use AllocL)
+		HBufC8* newpath=HBufC8::NewLC(aPath.Length()+4);
+		*newpath=aPath;
+		if (aPath.Length())
+			newpath->Des().Append(_L8("."));
+		newpath->Des().AppendNum(1);
+		BuildTreeOneL(aParent,aAtom,newpath->Des(),aThisMessage,aAttachments,aIsMHTML, aRelatedAttachments);
+		CleanupStack::PopAndDestroy();
+		}
+	else
+		{
+		// Nest down a level: create a folder
+		SetEntryL(aParent);
+		TMsvEmailEntry message;
+		message.iMtm=KUidMsgTypeIMAP4;
+		message.iServiceId=iServiceId;
+		message.iType=KUidMsvFolderEntry;
+		message.iSize=0;
+		message.SetComplete(EFalse);
+		User::LeaveIfError(iEntry->CreateEntryBulk(message));
+
+		DBG((LogText(_L8("Created attachment folder id %x as child of %x"),message.Id(),aParent)));
+
+		aParent=message.Id();
+
+		// CC:Mail server doesn't respond to BODYSTRUCTURE correctly:
+		// it gives the same response as FETCH BODY, ie it doesn't have
+		// all the extended MIME stuff.
+		// Skip to the last 4 atoms: this is the multipart type & stuff
+		CImapAtom *multipart=aAtom;
+		while(multipart && multipart->Child()!=NULL)
+			multipart=multipart->Next();
+
+		// Got anything?
+		if (multipart)
+			{
+			// Parse multipart type string, do this first so
+			// information is available when parsing children
+			TImEmailFolderType ft=EFolderTypeUnknown;
+			if (multipart->Compare(KImcvRelated))
+				ft=EFolderTypeRelated;
+			if (multipart->Compare(KImcvMixed))
+				ft=EFolderTypeMixed;
+			if (multipart->Compare(KImcvParallel))
+				ft=EFolderTypeParallel;
+			if (multipart->Compare(KImcvAlternative))
+				ft=EFolderTypeAlternative;
+			if (multipart->Compare(KImcvDigest))
+				ft=EFolderTypeDigest;
+			
+			SetEntryL(aParent);
+			
+			// ...and save it
+			TMsvEmailEntry folder=iEntry->Entry();
+			folder.SetMessageFolderType(ft);
+#if SET_RELATED_ID
+			//	DS - Set message's iRelatedId to messageId to allow later UI kludges
+			folder.iRelatedId=folder.Id();
+#endif
+			ChangeEntryBulkL(folder);
+
+			// Process the multipart object
+			TInt subnr=1;
+			while(aAtom && aAtom!=multipart)
+				{
+				// Tag or child?
+				if (aAtom->Child())
+					{
+					// Process item (doesn't use AllocL)
+					HBufC8* newpath=HBufC8::NewLC(aPath.Length()+4);
+					*newpath=aPath;
+					if (aPath.Length())
+						newpath->Des().Append(_L8("."));
+					newpath->Des().AppendNum(subnr++);
+					BuildTreeOneL(aParent,aAtom->ToChildL(),newpath->Des(),
+								  aThisMessage,aAttachments,aIsMHTML, aRelatedAttachments);
+					CleanupStack::PopAndDestroy();
+					}
+		
+				// Next item
+				aAtom=aAtom->Next();
+				}
+			}
+		}
+	}
+
+
+// convert text from its charset and write to richtext store. aText
+// can span multiple and partial lines
+void CImImap4Session::WriteToBodyL(const TDesC8& aText)
+	{
+	TInt pos = iMessageBody->DocumentLength();
+
+	// Add bits of body text, converting along the way, till no characters left
+	// .. to convert.
+
+	// Convert text before writing to body.
+	TInt rem = 0;
+
+	// there will be a max of one output char per input byte
+	HBufC16* text16=HBufC16::NewLC(aText.Length());
+	TPtr16 ptr16=text16->Des();
+
+	if (!iPreparedToConvert)
+		{
+		ptr16.Copy(aText);
+		iMessageBody->InsertL(pos, ptr16);
+		}
+	else
+		{
+		TInt unconvertedChars, firstPos; // not used 
+		rem = iCharConv->ConvertToOurCharsetL(aText, ptr16, 
+											  unconvertedChars, firstPos);
+		if (rem < 0) // error
+			{
+			// Copy unconverted characters.
+			ptr16.Copy(aText);
+			iMessageBody->InsertL(pos, ptr16);
+			}
+		else if (rem && rem < iLeftOver.MaxLength())
+			iLeftOver.Copy(aText.Right(rem));	
+
+		// convert CRLF to ELineBreak
+		TInt start = 0;
+		TInt length = ptr16.Length();
+		TInt i;
+		for (i=1; i<length; i++)
+			{
+			if (ptr16[i-1] == KImcvCR && ptr16[i] == KImcvLF)
+				{
+				ptr16[i-1] = CEditableText::ELineBreak;
+
+				// write this line to body
+				TPtrC ptr = ptr16.Mid(start, i-start);
+				iMessageBody->InsertL(pos, ptr);
+				pos += ptr.Length();
+				start = i+1;
+				}
+			}
+
+		if (start != i)
+			{
+			TPtrC ptr = ptr16.Mid(start, i-start);
+			iMessageBody->InsertL(pos, ptr);
+			}
+		}
+
+	CleanupStack::PopAndDestroy(); // text16
+	}
+	
+// convert text from its charset and write to file, return error code
+// from write
+TInt CImImap4Session::WriteToAttachmentL(const TDesC8& aText)
+	{
+	TInt error;
+	
+	// Convert text before writing to attachment.
+	TInt rem = 0;
+
+	// there will be a max of one output char per input byte
+	HBufC16* text16=HBufC16::NewLC(aText.Length());
+	TPtr16 ptr16=text16->Des();
+
+	if (!iPreparedToConvert)
+		{
+		if(iCaf->Processing())
+			{
+			error = iCaf->WriteData(aText);
+			}
+		else
+			{
+			error = iAttachmentFile->WriteFile(aText);
+			}
+		}
+	else
+		{
+		TInt unconvertedChars, firstPos; // not used 
+		rem = iCharConv->ConvertToOurCharsetL(aText, ptr16, 
+											  unconvertedChars, firstPos);
+		if (rem < 0) // error
+			{
+			ptr16.Copy(aText);  // Copy unconverted characters.
+			}
+		else if (rem && rem < iLeftOver.MaxLength())
+			{
+			// any remainder is due to partial code sequence not lack of space
+			iLeftOver.Copy(aText.Right(rem));
+			}
+
+		TPtrC8 text8((TUint8*) text16->Des().Ptr(), text16->Des().Size());
+		if(iCaf->Processing())
+			{
+			error = iCaf->WriteData(text8);
+			}
+		else
+			{
+			error = iAttachmentFile->WriteFile(text8);
+			}
+		}
+
+	CleanupStack::PopAndDestroy(); // text16
+
+	return error;
+	}
+
+// Copied and adapted this function from the one in CImRecvConvert
+TBool CImImap4Session::CheckUUEStartL(const TDesC8& aSourceLine)
+	{
+	// Checks if the descriptor contains the UUE begin header
+	// Extracts the file name if it is
+	
+	TInt sourceLength = aSourceLine.Length();
+	if(sourceLength < KImcvUueStart().Length()+3) // can't be "begin ###", it's not long enough; 3=length of ###
+		return EFalse;
+
+	if(!aSourceLine.Left(KImcvUueStart().Length()).CompareF(KImcvUueStart)) // start of line might be UUE boundary
+		{
+		// we also need to check that the next three chars are numbers - Unix file access code
+		const TUint8* sourceLinePtr = aSourceLine.Ptr();
+		TInt length=KImcvUueStart().Length();// this defines length as 6 ie. "b e g i n  " 
+		if( TChar(sourceLinePtr[length]).IsDigit() && 
+			TChar(sourceLinePtr[length+1]).IsDigit() && 
+			TChar(sourceLinePtr[length+2]).IsDigit() )
+			{
+			// Found 'begin ###' at the start of a line - assume this is a UUencode header
+			// The attachment name in this header is ignored. We use the value from the MIME header
+			return ETrue;
+			}
+		}
+		
+	return EFalse;
+	}
+
+
+// Decode and store received data
+void CImImap4Session::DecodeAndStoreL(const TPtrC8& aBodyData, const TBool aEndOfStream)
+	{
+	DBG((LogText(_L8("DecodeAndStore(%d bytes, endofstream=%d, encoding=%d, iLeftOver=%d)"),aBodyData.Length(),aEndOfStream,iEncodingType,iLeftOver.Length())));
+
+	// Somewhere to store decoded data, at least as long as source (plus anything we have left
+	// in the partial line buffer which may now get consumed)
+	TInt outputbuffersize=aBodyData.Length()+4;
+	if (iPartialLine)
+		outputbuffersize+=iPartialLine->Des().Length();
+
+	HBufC8* decoded=HBufC8::NewLC(outputbuffersize);
+	TPtr8 decoded_ptr=decoded->Des();
+
+	// Bump progress: bytesdone is *encoded* length, so we just use the encoded length
+	iProgress.iBytesDone+=aBodyData.Length();
+	// Which decoder are we using?
+	switch(iEncodingType)
+		{
+		case EEncodingTypeNone:
+		case EEncodingType7Bit:
+		case EEncodingType8Bit:
+		case EEncodingTypeBinary:
+		case EEncodingTypeUnknown:
+			// Nothing to do, just copy data
+			decoded->Des().Append(aBodyData);
+			break;
+
+		case EEncodingTypeBASE64:
+			// Decode Base64 data: just filter it through decoder, it
+			// ignores line breaks anyway.
+			iB64Decoder.Decode(aBodyData,decoded_ptr);
+			break;
+
+		case EEncodingTypeUU:
+			{
+			TPtrC8 bodydata=aBodyData;
+
+			// Got a partial buffer?
+			if (!iPartialLine)
+				{
+				// Allocate buffer
+				iPartialLine=HBufC8::NewL(KUuDecodedLineLength);
+				iUUDecoding = EFalse;
+				}
+			
+			// Decode UUEncoded data: line by line
+			TBool decodeEnded = EFalse;
+			TInt position=0;
+			while ( bodydata.Length() && !decodeEnded )
+				{
+				// Find() returns the start of "\r\n". The decoding algorithm
+				// requires that the encoded line contains the "\r\n".
+				TInt lineEnd = bodydata.Find( _L8("\r\n") );
+				if (lineEnd != KErrNotFound)
+					{
+					lineEnd = lineEnd + 2;
+					AppendExtendL( &iPartialLine, bodydata.Left( lineEnd ), EFalse);
+				
+					bodydata.Set( bodydata.Mid( lineEnd ) );
+				
+					// Check for a well-formated  begin-tag
+					if ( CheckUUEStartL( iPartialLine->Des() ) )
+						{
+						iUUDecoding = ETrue;
+						}
+					else if ( iPartialLine->Compare( KImcvUueEnd ) != 0 && iUUDecoding )
+						{
+						// Every malformatted string is decoded as an empty string 
+						// with length 0. Appending such a string is harmless.
+						TPtr8 destination((unsigned char*)decoded_ptr.Ptr()+position,0,outputbuffersize-position);
+						iUUDecoder.Decode(*iPartialLine,destination);
+						position+=destination.Length();			
+						}
+					else if ( iUUDecoding )
+						{
+						decodeEnded = ETrue;
+						iUUDecoding = EFalse;	
+						}
+					
+					iPartialLine->Des().Zero();
+					}
+				else
+					{
+					AppendExtendL( &iPartialLine, bodydata, EFalse);
+					
+					// advance to end of bodydata
+  					bodydata.Set(bodydata.Ptr()+bodydata.Length(), 0);
+					}
+				}
+			decoded->Des().SetLength(position);	
+			break;
+			}
+
+		case EEncodingTypeQP:
+			{
+			TPtrC8 bodydata=aBodyData;
+
+			// Got a partial buffer?
+			if (!iPartialLine)
+				{
+				// Allocate buffer
+				iPartialLine=HBufC8::NewL(256);
+				}
+
+			// Build buffer to decode: basically, QP decoder wants CRLF terminated
+			// lines, so we build them in the iPartialLine buffer. There may be
+			// stuff already there from previous data packet - so we just append.
+			TInt position=0;
+			while(bodydata.Length())
+				{
+				// Find a line break
+				TInt lineend=bodydata.Find(_L8("\r\n"));
+
+				// No break?
+				if (lineend==KErrNotFound && !aEndOfStream)
+					{
+					// Stick it all in the partialline buffer, we should get a CRLF
+					// soon...
+					AppendExtendL( &iPartialLine,bodydata, EFalse);
+					break;
+					}
+				else
+					{
+					if (lineend==KErrNotFound)
+						{
+						// Append whole thing left to buffer
+						AppendExtendL( &iPartialLine,bodydata, EFalse);
+
+						// advance to end of bodydata
+						bodydata.Set(bodydata.Ptr()+bodydata.Length(), 0);
+						}
+					else
+						{
+						// Append to buffer up to that point (including the \r\n)
+						AppendExtendL( &iPartialLine,bodydata.Left(lineend+2), EFalse);
+
+						// Remove from the buffer we're working on (including the \r\n)
+						bodydata.Set(bodydata.Ptr()+lineend+2,bodydata.Length()-lineend-2);
+						}
+
+					// Decode & skip on in buffer
+					TPtr8 destination((unsigned char*)decoded_ptr.Ptr()+position,0,outputbuffersize-position);
+					iQPDecoder.Decode(*iPartialLine,destination);
+					position+=destination.Length();
+					iPartialLine->Des().Zero();
+					}
+				}
+
+			// Update decoded
+			decoded->Des().SetLength(position);
+			break;
+			}
+		}
+
+	// put back any partially converted data
+	if (iLeftOver.Length())
+		{
+		decoded->Des().Insert(0, iLeftOver);
+		iLeftOver.SetLength(0);
+		}
+
+	// What format is it? TEXT/* we put into a richtext thingy, otherwise just
+	// stream it to store
+	if (iFetchIsText)
+		{
+		if(aEndOfStream && (iMessageBody || iBodyBuf) && iBodyPartRemainingSize)
+			{
+			CleanupStack::Pop();// decoded 
+			TInt newSize = decoded->Size() + iFooterString->Size();
+			decoded = decoded->ReAlloc(newSize);
+			CleanupStack::PushL(decoded);
+			decoded->Des().Append(*iFooterString);
+			delete iFooterString;
+			iFooterString = NULL;
+			}
+		// Got somewhere to put it? Store it!
+		if (iStore8BitData)
+			{
+			if (decoded->Length() && iBodyBuf)
+				iBodyBuf->InsertL(iBodyBuf->Size(), *decoded);
+			}
+		else
+			{
+			if (decoded->Length() && iMessageBody)
+				WriteToBodyL(decoded->Des());
+			}
+		
+		
+		// Got the whole thing buffered?
+		if (aEndOfStream && (iMessageBody || iBodyBuf))
+			{
+			DBG((LogText(_L8("Doing StoreBodyTextL()"))));
+
+			// The whole message is built in iMessageBody or iBodyBuf.  Store it.
+			SetEntryL(iMessageId);
+			CMsvStore *entryStore=iEntry->EditStoreL();
+			CleanupStack::PushL(entryStore);
+			if (iStore8BitData)
+				iBodyText->StoreL(*entryStore, *iBodyBuf);
+			else
+				entryStore->StoreBodyTextL(*iMessageBody);
+			entryStore->CommitL();
+			CleanupStack::PopAndDestroy();
+
+			// Get rid of body copy, etc
+			delete iBodyBuf;
+			iBodyBuf = NULL;
+			delete iMessageBody;
+			iMessageBody=NULL;
+			}
+		}
+	else
+		{
+		// Select the entry
+		SetEntryL(iMessageId);
+
+		// Save it direct to store
+		if (iAttachmentFileState==EFileNotOpen)
+			{
+			// Get and set Attachment File path
+			TFileName filepath;
+	
+			// Retrieving the attachment name from an earlier saved one
+			// If it's a CAF interested file then this will get overidden
+			CMsvStore* store = iEntry->ReadStoreL();
+			CleanupStack::PushL(store);
+			MMsvAttachmentManager& attachmentMgr = store->AttachmentManagerL();
+			if(attachmentMgr.AttachmentCount())
+				{
+				// get the file path 	
+				CMsvAttachment* attachment = attachmentMgr.GetAttachmentInfoL(0);
+				CleanupStack::PushL(attachment);
+				filepath = attachment->FilePath();
+				CleanupStack::PopAndDestroy(attachment);
+				}
+
+			if (iAttachmentFullPath)
+				{
+				delete iAttachmentFullPath;
+				iAttachmentFullPath=NULL;
+				}
+			if(attachmentMgr.AttachmentCount())
+				{
+				TParse fileParser;
+				User::LeaveIfError(fileParser.Set(filepath, NULL, NULL));
+				iAttachmentFullPath=fileParser.DriveAndPath().AllocL();
+				}
+			// We've already extracted the attachment file name in
+			// BuildTree so just copy it out of details
+			iAttachmentName=iEntry->Entry().iDetails;			
+			if(attachmentMgr.AttachmentCount())
+				{
+			DBG((LogText(_L8("name '%S', '%S'"),iAttachmentFullPath,&iAttachmentName)));
+				}
+			if (!iAttachmentFile)
+					iAttachmentFile=new (ELeave) TImAttachmentFile(iFs);
+			CleanupStack::PopAndDestroy(store); // store opened above
+			store = iEntry->EditStoreL(); 
+			CleanupStack::PushL(store);
+			// Could be multiple attachments in the folder.
+			TInt attachmentCount = store->AttachmentManagerL().AttachmentCount();
+			for(TInt i=0;i<attachmentCount;i++)
+				{
+				// Remove [0] as array is shuffled. Once index [n] is removed n+1 becomes n
+				store->AttachmentManagerExtensionsL().RemoveAttachmentL(0);
+				}
+			if(attachmentCount)			
+				store->CommitL();
+
+			// Now create the attachment entry
+			CMsvAttachment* attachment = CMsvAttachment::NewL(CMsvAttachment::EMsvFile);
+			CleanupStack::PushL(attachment);
+			attachment->SetAttachmentNameL(iAttachmentName);
+			
+			// Need to create the MIME-type information - first get the MIME headers
+			CImMimeHeader* mimeHeaders = CImMimeHeader::NewLC();
+			mimeHeaders->RestoreL(*store);
+
+			HBufC8* buf = HBufC8::NewLC(mimeHeaders->ContentSubType().Length() + mimeHeaders->ContentType().Length() + 1);
+			TPtr8 ptr(buf->Des());
+			ptr.Copy(mimeHeaders->ContentType());
+			ptr.Append(KImcvForwardSlash);
+			ptr.Append(mimeHeaders->ContentSubType());
+		
+			attachment->SetMimeTypeL(ptr);
+			
+			CleanupStack::PopAndDestroy(2, mimeHeaders);
+
+			
+			RFile file;	
+			if(iCaf->Registered())
+				{
+				iCaf->PrepareProcessingL(); // Init the CAF import file session
+				RFile startFile;
+				TFileName suggestedFileName;
+				if(iCaf->GetSuggestedAttachmentFileName(suggestedFileName) == KErrNone) // CAF agent may provide a filename
+					{
+					store->CreateShareProtectedAttachmentL(suggestedFileName,startFile,attachment);
+					}
+				else
+					{
+					store->CreateShareProtectedAttachmentL(iAttachmentName,startFile,attachment);
+					}
+				iCaf->StartProcessing(iDefaultAttachmentName->Des(),attachment->FilePath(),*iEntry,startFile); // Init the CAF session
+				startFile.Close();
+				}
+			else
+				{
+				// Normal behaviour
+				store->AttachmentManagerExtensionsL().CreateAttachmentL(iAttachmentName,file,attachment);
+				iAttachmentFile->SetFileHandle(file,TImAttachmentFile::EImFileWrite);
+				}
+
+			// CreateAttachmentL takes ownership of CMsvAttachment so if call was successful we can pop it here
+			CleanupStack::Pop(attachment);
+			
+			iAttachmentFileState = EFileIsOpen;	
+			store->CommitL();
+			CleanupStack::PopAndDestroy(store);	
+
+			if (iAttachmentFileState!=EFileIsOpen)
+				{
+				DBG((LogText(_L8("Couldn't open file!"))));
+				}
+			}	
+
+		if (iAttachmentFileState==EFileIsOpen && decoded->Length())
+			{
+			// write decoded data into a file if there is any data there to write
+			TInt error=WriteToAttachmentL(decoded->Des());
+
+			if (error!=KErrNone)
+				{
+				// the file write failed, (eg.there is no space left set new file state 
+				// and skip any remaining encoded data in message
+				iAttachmentFileState=EFileIsIncomplete;
+
+				DBG((LogText(_L8("Failed to write %d bytes to attachment file (error=%d): deleting it"),decoded->Length(),error)));
+				if(iCaf->Processing())
+					{
+					iCaf->EndProcessingL();
+					}
+				else
+					{
+					iAttachmentFile->CloseFile();
+					}
+
+				CMsvStore* store = iEntry->EditStoreL(); 
+				CleanupStack::PushL(store);
+				// Could be multiple attachments in the folder.
+				TInt i;
+				TInt attachmentCount = store->AttachmentManagerL().AttachmentCount();
+				for(i=0;i<attachmentCount;i++)
+					{
+					// Remove [0] as array is shuffled. Once index [n] is removed n+1 becomes n
+					store->AttachmentManagerExtensionsL().RemoveAttachmentL(0);
+					}
+				if(attachmentCount)			
+					store->CommitL();
+				CleanupStack::PopAndDestroy(store);
+				TMsvEmailEntry message=iEntry->Entry();
+				message.SetAttachment(EFalse);
+				ChangeEntryBulkL(message);
+
+				// Leave with the error
+				User::Leave(error);
+				}
+			else
+				{
+				DBG((LogText(_L8("Written %d bytes to attachment file"),decoded->Length())));
+				}
+			}
+
+		// Finished?
+		if ((aEndOfStream) && (iAttachmentFileState == EFileIsOpen))
+			{
+			DBG((LogText(_L8("Closing attachment file"))));
+			if(iCaf->Processing())
+				{
+				iCaf->EndProcessingL();
+				}
+			else
+				{
+				iAttachmentFile->CloseFile();
+				}
+			iAttachmentFileState=EFileNotOpen;
+			}
+		}
+
+	// Free memory
+	CleanupStack::PopAndDestroy();
+	}
+
+// Given that aId has become complete see if we can propagate the
+// Complete state and partial fetch state flag up
+void CImImap4Session::PropagateCompleteFlagL(TMsvId aId, TBool aDoBodyText,TBool aPartialFetched)
+	{
+	CMsvEntrySelection* selection=new (ELeave) CMsvEntrySelection;
+	CleanupStack::PushL(selection);
+	
+	// get the siblings of this id
+	SetEntryL(aId);
+	TMsvId parent = iEntry->Entry().Parent();
+
+	// finish if we've managed to reach the top
+	if (parent == KMsvRootIndexEntryId)
+		return;
+
+	SetEntryL(parent);
+
+	// finish if we've reached a service
+	if (iEntry->Entry().iType == KUidMsvServiceEntry)
+		return;
+
+	GetChildrenL(*selection);
+
+	TBool complete=ETrue;
+	TBool bodyTextComplete=ETrue;
+	TBool partiallyFetched=EFalse;
+
+	TBool related=((TMsvEmailEntry) iEntry->Entry()).MessageFolderType()==EFolderTypeRelated ? 
+																					ETrue:EFalse;
+	for (TInt i=0; i < selection->Count(); i++)
+		{
+		SetEntryL((*selection)[i]);
+		if (!iEntry->Entry().Complete())
+			{
+			complete=EFalse;
+			if((iEntry->Entry().iType==KUidMsvFolderEntry) && aPartialFetched)
+				complete=ETrue;
+			// The current part is not complete so...
+			// if it is either a text part or a HTML part then the body
+			// text is marked as being incomplete.
+			//
+			// This code means that, if present, then both the text/plain
+			// and text/html alternatives need to be downloaded before
+			// the body text is marked as being complete.
+			if ((iEntry->Entry().iType == KUidMsvEmailTextEntry)
+				|| (iEntry->Entry().iType == KUidMsvEmailHtmlEntry ) || related )
+				{
+				if(aPartialFetched)
+					{
+					complete = ETrue;
+					bodyTextComplete=ETrue;
+					}
+				else
+					bodyTextComplete=EFalse;
+				}
+
+			break;
+			}
+		}
+	
+	CleanupStack::PopAndDestroy(); // selection
+
+	// if all the siblings were complete then make the parent
+	// complete and continue up.
+	if (complete || ((aDoBodyText || related) && bodyTextComplete))
+		{
+		SetEntryL(parent);
+		TMsvEmailEntry entry = iEntry->Entry();
+
+		// check whether parent is complete, this wil prevent us
+		// checking all the messages in a real folder as they will all
+		// be initialised to Complete
+		if (!entry.Complete())
+			{
+			if (complete || ((iEntry->Entry().iType==KUidMsvFolderEntry) && aPartialFetched))
+				entry.SetComplete(ETrue);
+			if(aPartialFetched)
+				{	
+				if((iEntry->Entry().iType != KUidMsvAttachmentEntry) &&
+						(iEntry->Entry().iType != KUidMsvEmailExternalBodyEntry))
+					{
+					entry.SetPartialDownloaded(ETrue);
+					}
+				partiallyFetched = ETrue;
+				}
+			else
+				{
+				entry.SetPartialDownloaded(EFalse);
+				partiallyFetched = EFalse;
+				}
+			entry.SetBodyTextComplete(ETrue);
+			ChangeEntryL(entry);
+		
+			PropagateCompleteFlagL(parent, related|aDoBodyText,partiallyFetched);
+			}
+		else if (entry.PartialDownloaded())
+			{
+			entry.SetPartialDownloaded(EFalse);
+			ChangeEntryL(entry);
+			PropagateCompleteFlagL(parent, related|aDoBodyText,partiallyFetched);
+			}
+		}	
+	}
+
+void CImImap4Session::CreateAttachmentInfoL(TMsvEmailEntry& aMsvEmailEntry)
+	{
+	// create an empty attachment to store the attachment infomation, for the case
+	// where the attachment is not downloaded due to download limits.
+	CMsvStore* store = iEntry->EditStoreL();
+	CleanupStack::PushL(store);
+
+	MMsvAttachmentManager& attachmentMgr = store->AttachmentManagerL();
+	
+	// Check to see if this entry already has an attachment - if so, then don't
+	// add it again!
+	if( attachmentMgr.AttachmentCount() == 0 )
+		{
+		MMsvAttachmentManagerSync& attachmentMgrSync = store->AttachmentManagerExtensionsL();
+		
+		// Now create the attachment entry
+		CMsvAttachment* attachment = CMsvAttachment::NewL(CMsvAttachment::EMsvFile);
+		CleanupStack::PushL(attachment);
+						
+		// Need to create the MIME-type information - first get the MIME headers
+		CImMimeHeader* mimeHeaders = CImMimeHeader::NewLC();
+		mimeHeaders->RestoreL(*store);
+
+		HBufC8* buf = HBufC8::NewLC(mimeHeaders->ContentSubType().Length() + mimeHeaders->ContentType().Length() + 1);
+		TPtr8 ptr(buf->Des());
+		ptr.Copy(mimeHeaders->ContentType());
+		ptr.Append(KImcvForwardSlash);
+		ptr.Append(mimeHeaders->ContentSubType());
+	
+		attachment->SetMimeTypeL(ptr);
+		
+		CleanupStack::PopAndDestroy(2, mimeHeaders);
+
+		attachment->SetComplete(EFalse);
+		attachment->SetSize(aMsvEmailEntry.iSize);
+		attachment->SetAttachmentNameL(aMsvEmailEntry.iDetails);
+		RFile file;
+		attachmentMgrSync.CreateAttachmentL(aMsvEmailEntry.iDetails,file,attachment);
+		CleanupStack::Pop(attachment); // ownership passed to attachment manager
+		file.Close();
+		store->CommitL();
+		}
+	CleanupStack::PopAndDestroy(store);
+	}
+
+
+// Parse fetch messages
+TInt CImImap4Session::ProcessFetchL(const TUint aMsgnr, CImapAtom *aAtom)
+	{
+	CImapAtom *p=aAtom;
+	CImapAtom *attribute;
+	CImapAtom *structure=NULL;
+	CImapAtom *flags=NULL;
+	CImapAtom *bodydata=NULL;
+	CImapAtom *header=NULL;
+	TInt error=KErrNotReady;
+	TInt rfc822size=0;
+	TBool foundUnwantedMimeHeader=EFalse;
+	TBool wholeMessage=EFalse;
+	TInt fetchSizeBytes = static_cast<TInt>(iServiceSettings->FetchSize());
+
+	// Fetch data consists of attribute/value pairs
+	while(p!=NULL)
+		{
+		// Get attribute & value
+		attribute=p;
+		// broken servers can give us just an attribute rather than an
+		// attribute pair, if so then just finish the scan here and
+		// process what we've got
+		p=p->Next();
+		if (p==NULL)
+			break;
+
+		// Work on attributes
+		if (attribute->Compare(KIMAP_UID))
+			{
+			iFoundUid = ETrue;
+
+			// Lex it ok?
+			if (p->Value(iMessageUid)!=KErrNone)
+				User::Leave(KErrArgument);
+
+			// Skip to next attribute
+			p=p->Next();
+			}
+	
+		else if (attribute->Compare(KIMAP_BODY))
+			{
+			// some example responses
+
+			// expected without extra header fields
+			// * 1 FETCH (UID 1 BODY[2]<0> {1024}
+
+			// expected with extra (empty) header fields
+			// * 2 FETCH (UID 35 BODY[1.HEADER.FIELDS ("CONTENT-BASE" "CONTENT-LOCATION")] "" BODY[1]<0> {60}
+
+			// unwanted BODYSTRUCTURE info
+			// * 3 FETCH (UID 2 BODY ("text" "plain" ("CHARSET" "us-ascii") NIL NIL "7bit" 4337 67) BODY[1]<0> {1024}
+
+			// unwanted nested BODYSTRUCTURE info
+			// * 4 FETCH (UID 1 BODY (("text" "plain" ("CHARSET" "us-ascii") NIL NIL "7bit" 1346 53)
+			// ("text" "plain" ("NAME" "install.ins") NIL NIL "7bit" 5156 215) "MIXED")
+			// BODY[2]<0> {1024}
+
+			// unwanted BODYSTRUCTURE info and complete message
+			// * 5 FETCH (UID 2 BODY ("text" "plain" ("CHARSET" "us-ascii") NIL NIL "7bit" 4337 67) BODY[1] {1550}
+
+			// Body part is across & down one (it's in [])
+			CImapAtom* bodypart=p->ToChildL();
+
+			if (bodypart->CompareTail(KIMAP_HEADERFIELDS))
+				{
+				// Got BODY[HEADER.FIELDS ("header1" "header2" ...)] {LEN} data ...
+				// or  BODY[<part>.HEADER.FIELDS ("header1" "header2" ...)] {LEN} data ...
+				header=p->ToNextL();
+#ifdef PRINTING
+				TPtrC8 bpt=bodypart->Next()->Child()->Atom();
+				LogText(_L8("Found header fields (%S...)"),&bpt);
+#endif
+				// Skip to next attribute
+				p=header->Next();
+				}
+			else if (bodypart->CompareTail(KIMAP_MIME))
+				{
+				// Got BODY[MIME] {LEN} data ...
+				// or  BODY[<part>.MIME] {LEN} data ...
+				header=p->ToNextL();
+
+        		DBG((LogText(_L8("Found MIME header fields"))));
+        		
+       				// Skip to next attribute
+				p=header->Next();
+				}
+			else if (bodypart->Child() != NULL || bodypart->Next() != NULL)
+				{
+				// we've been unexpectedly returned a BODY () response
+				// which we don't want, so just skip it
+				DBG((LogText(_L8("Unexpected BODY response, ignoring"))));
+				p=p->Next();
+				}
+			else
+				{
+				// is body data, ie BODY[part]<offset> or BODY[part]
+
+				// Offset is next atom
+				CImapAtom* offset=p->ToNextL();
+
+				// there may not be an offset in which case offset is
+				// actually bodydata
+				
+				// Get the offset
+				TUint offsetn=0;
+				// see if this is an offset
+				if( offset->Child() != NULL )
+					{
+					TLex8 lex(offset->Child()->Atom());				
+					if( lex.Val(offsetn)!=KErrNone ) 
+						{
+						error=KErrGeneral;
+						break;
+						}
+
+					// Body data is next atom
+					bodydata=offset->Next();
+					}
+				else
+					{
+					// if not an offset then this is not a partial
+					// message
+					bodydata=offset;
+					wholeMessage=ETrue;
+					}
+								
+				// Additional code added to address additional unrequested non-RFC data sent by the 
+				// imap server caused problems in downloading the email and any attachments.
+				// Upto this point the BODY tag has correctly been interpreted.
+				// 
+				// We may however have an unwanted MIME header data which we will need to process or get rid of.					
+				// If we have  an unwanted mime header then we currently have the bodydata atom storing:
+				//
+				// BODY[x.MIME]<offset> 
+				// MIME_HEADER_INFO
+				// BODY_DATA
+				//
+				// or
+				//
+				// BODY.PEEK[x.MIME]<offset>
+				// BODY_DATA
+				// 
+				// The problem here is that the BODY or BODY.PEEK tag will be read as the actual body data.
+				// We need to move the bodydata pointer to the beginning of the MIME_HEADER_INFO.
+				// When decoding the information we will send the data after the header info to be decoded.
+				
+				CImapAtom* nextbodypart = NULL;
+				CImapAtom* mimetest =NULL;
+				
+				// Check if the unwanted body tag is "BODY" or "BODY.PEEK"
+				if((bodydata->Compare(KIMAP_BODY)) || (bodydata->Compare(KIMAP_BODYPEEK))) //we have an extra body tag
+					{
+					//find out if it is MIME
+					nextbodypart=bodydata->Next();
+					if(nextbodypart)
+						{
+						mimetest = nextbodypart->Child();
+						if(mimetest)
+							{
+							if (mimetest->CompareTail(KIMAP_MIME)) 
+								{
+								
+								// Unrequested tag is "BODY.PEEK"														
+								if((bodydata->Compare(KIMAP_BODYPEEK)))
+									{
+									LogText(_L8("UNEXPECTED RESPONSE: TAG \"BODY.PEEK\"- Found unwanted additional \"BODY.PEEK\" tag in the FETCH response"));
+									LogText(_L8("PROCESSING UNEXPECTED RESPONSE: TAG \"BODY.PEEK\" - Additional MIME header data NOT expected to PREFIX the bodypart - unlike additional \"BODY\" tag"));
+									// Not expecting additional / unwanted mime header info at the beginning of the body of the message part
+									// Hence, no additional processing required on body data
+									foundUnwantedMimeHeader=EFalse;
+									}
+								else // Unrequested tag is "BODY"
+									{
+									LogText(_L8("UNEXPECTED RESPONSE: TAG \"BODY\" Found unwanted additional \"BODY\" tag in the FETCH response"));
+									LogText(_L8("PROCESSING UNEXPECTED RESPONSE: TAG \"BODY\"- Expecting UNWANTED MIME HEADER DATA prefixing the BODY DATA of the message part"));
+									// Expecting additional / unwanted mime header info at the beginning of the body data of the message part.
+									// Hence, the atom pointed to by bodydata pointer will be parsed / truncated appropriately to extract just 
+									// the bodypart later.
+									foundUnwantedMimeHeader=ETrue;						
+									}
+								//we may have an offset that we need to ignore
+								CImapAtom* possOffset = nextbodypart->Next();
+								TLex8 lex(possOffset->Atom());
+								if (lex.Get()=='<') //has an offset
+									{
+									// Body data is next atom
+									bodydata=possOffset->Next();
+									}
+								else 
+									{						
+									bodydata=possOffset;
+									}
+								}
+							}							
+						}
+					}//end of code addressing
+				
+
+#ifdef PRINTING
+				TPtrC8 bpt=bodypart->Atom();
+				LogText(_L8("Found body part [%S] iSizeWait %d"),&bpt,iSizeWait);
+#endif
+				if (iSizeWait && bodydata!=NULL)
+					{
+					// No longer waiting for the size
+					iSizeWait=EFalse;
+
+					// Size of this part
+					TUint sizen=bodydata->Atom().Length();
+
+					DBG((LogText(_L8("  offset=%d, length=%d"),offsetn,sizen)));
+					TInt fetchSize = fetchSizeBytes;
+					
+					// In CC:Mail workaround mode?
+					if (iTalkingToCCMail || iTalkingToOpenMail)
+						{
+						// How much message is there left to fetch?
+						TInt sizeleft=iSizeOfThisPart-(offsetn+sizen);
+							
+						if (sizeleft>0)
+							{
+							if( iState != EImapStateFetchCancelWait )
+								{
+								// Limit chunk size
+								if(iFetchPartialMail)
+									{
+									fetchSize = GetFetchSizeL(sizeleft,offsetn+sizen);
+									if(fetchSize > fetchSizeBytes )
+										{
+										fetchSize = fetchSizeBytes;
+										}
+									}
+								else
+									{
+									if (sizeleft>fetchSizeBytes)
+										{
+										fetchSize=fetchSizeBytes;
+										}
+									}	
+								// Issue new fetch command
+								NewTag();
+								TPtrC8 bp(bodypart->Atom());
+								if (iServiceSettings->UpdatingSeenFlags())
+									{
+									iImapIO->SendL(iStatus,KImapFetchBodyPeek,
+												iTag,iMessageFetching,&bp,(offsetn+sizen),sizeleft);
+									}
+								else
+									{
+									iImapIO->SendL(iStatus,KImapFetchBody,
+												iTag,iMessageFetching,&bp,(offsetn+sizen),sizeleft);
+									}
+
+								NewTagSent();
+
+								// Get the rest of this line uninterrupted
+								error=KErrWrite;
+								}
+							}
+						else
+							{
+							// Got the whole message
+							TInt sizeleft=iSizeOfThisPart-(offsetn+sizen);
+							if(iFetchPartialMail && (sizeleft || !iHtmlEntryPart))
+								{
+								ProcessFooterMessageL(sizeleft);
+								}
+							}
+						}
+					else
+						{
+						// Anything more to get? We decide this on wether we got near to
+						// our requested packet size on the last fetch: if we were within
+						// 100 bytes of the requested size, we ask for another load just
+						// in case the server is serving us line by line. Otherwise, we
+						// assume that was the end of the data and flush it out.
+						
+						// Check whether we have downloaded the message completely or not,
+						// before sending the FETCH command again.
+						iSizeLeftToFetch = iSizeOfThisPart-(offsetn+sizen);
+						if ((fetchSizeBytes-sizen)<100 && iSizeLeftToFetch>0)
+							{
+							if( iState != EImapStateFetchCancelWait )
+								{
+								TInt sizeleft=iSizeOfThisPart-(offsetn+sizen);
+								fetchSize=sizeleft;
+								if(iFetchPartialMail)
+									{
+									fetchSize = GetFetchSizeL(sizeleft,offsetn+sizen);
+									}
+								if(fetchSize > fetchSizeBytes)
+									{
+									fetchSize = fetchSizeBytes;
+									}
+								// Yes, issue a new fetch command
+								NewTag();
+								TPtrC8 bp(bodypart->Atom());
+								if (iServiceSettings->UpdatingSeenFlags())
+									{
+									iImapIO->SendL(iStatus,KImapFetchBodyPeek,
+											   iTag,iMessageFetching,&bp,(offsetn+sizen),fetchSize);
+									
+									}
+								else
+									{
+									iImapIO->SendL(iStatus,KImapFetchBody,
+											   iTag,iMessageFetching,&bp,(offsetn+sizen),fetchSize);
+									}
+								NewTagSent();
+
+								// Get the rest of this line uninterrupted
+								error=KErrWrite;
+								}
+							}
+						else
+							{
+							// Got the whole message
+							TInt sizeleft = iSizeOfThisPart-(offsetn+sizen);
+							
+							if(iFetchPartialMail && (sizeleft || !iHtmlEntryPart))
+								{
+								ProcessFooterMessageL(sizeleft);
+								}
+							}
+						}
+					}
+
+				// Finish processing here if we didn't get everything
+				if (!iGotWholeLine)
+					break;	
+
+				// Skip to next attribute
+				p=bodydata->Next();
+				}
+			}
+		else if (attribute->Compare(KIMAP_BODYSTRUCTURE))
+			{
+			// Body structure: Save it until later when we have created
+			// the message - then we can create the attachment tree
+			// underneath it
+			structure=p->ToChildL();
+
+			// Skip to next attribute
+			p=p->Next();
+			}
+		else if (attribute->Compare(KIMAP_FLAGS))
+			{
+			// Process flag list later
+			flags=p;
+
+			// Skip to next attribute
+			p=p->Next();
+			}
+		else if (attribute->Compare(KIMAP_RFC822SIZE))
+			{
+			// Save total message size
+			if (p->Value(rfc822size)!=KErrNone)
+				User::Leave(KErrGeneral);
+
+			// Skip to next attribute
+			p=p->Next();
+			}
+
+		else
+			{
+#ifdef PRINTING
+			TPtrC8 att=attribute->Atom();
+			LogText(_L8("Unknown attribute '%S'"),&att);
+			//showtree(attribute,0);
+#endif
+			}
+		}
+
+	DBG((LogText(_L8("About to process, error=%d, iGotWholeLine=%d, iSyncState=%d, bodydata=%x, wholeMessage=%d"),
+			error,iGotWholeLine,iSyncState,(int)bodydata,wholeMessage)));
+
+		// No error?
+	if ((error==KErrNotReady || error==KErrWrite) && iGotWholeLine)
+		{
+		// check to see if uid is present in server message. If not present, we are getting a
+		// message flag update
+		if(!iFoundUid)
+			{
+#ifdef PRINTING
+			DBG((LogText(_L8("UID not present in Fetch, so process flags"))));
+#endif
+
+			// this is just a message flag update	
+
+			TInt msgnr = aMsgnr;
+
+			// if the aMsgnr index into the local message array points to a valid message entry
+			if((msgnr < iFolderIndex.Size()) && (iFolderIndex[aMsgnr-1].iMsvId != 0))
+				{
+
+#ifdef PRINTING			
+				for(TInt i=0; i<iFolderIndex.Size(); i++)
+					{
+					DBG((LogText(_L8("iFolderIndex[%d].iMsvId=%d"), i, iFolderIndex[i].iMsvId)));	
+					}
+#endif
+				// Mirror flags and ensure that the message is visible
+				// as it might have been made invisible by
+				// unsubscribing.
+
+				// set the current entry to be the message pointed to by aMsgnr
+				SetEntryL(iFolderIndex[aMsgnr - 1].iMsvId);
+
+#ifdef PRINTING	
+				DBG((LogText(_L8("SetEntry for aMsgnr: %d with msvid %d"), aMsgnr-1, iFolderIndex[aMsgnr-1].iMsvId)));
+#endif
+
+				TMsvEmailEntry message=iEntry->Entry();
+
+				// since there is no uid associated with this server response, we just need to update flags
+				if (ProcessFlagsL(flags,message)|| !message.Visible())
+					{
+					message.SetVisible(ETrue);
+					ChangeEntryL(message);
+					}
+
+#ifdef PRINTING			
+				DBG((LogText(_L8("check for deleted imap 4 flags"))));
+#endif
+
+				if (message.DeletedIMAP4Flag())
+					{
+					iRemoteMessagesDeleteTagged++;
+					}
+				}
+
+			return(KErrNotReady);
+			}
+
+		// What synchronisation state are we in?
+		switch(iSyncState)
+			{
+		case ENotSyncing:
+			// Ignore it
+			break;
+
+		case EFetching:
+			{
+			if (header)
+				{
+				ProcessHeaderExtraL(NULL,iAttachmentMimeInfo,NULL,header->Atom());
+
+				// Store CImMimeHeader info
+				SetEntryL(iMessageId);
+				CMsvStore* entryStore=iEntry->EditStoreL();
+				CleanupStack::PushL(entryStore);
+				iAttachmentMimeInfo->StoreL(*entryStore);
+				entryStore->CommitL();
+				CleanupStack::PopAndDestroy(entryStore);
+				}
+
+			// Now, decode the body data and store it: completion can't be
+			// indicated by 'complete' as we may have been called with a partial line,
+			// so we just rely on the same 'early a full buffer' indicator as
+			// we do when issuing the pipelined fetches above
+
+			TBool endOfStream = EFalse;
+			if (iTalkingToCCMail || iTalkingToOpenMail)
+				{
+				// As we'll never get the 0 byte terminating read with CC:mail, we have to use
+				// our own definition of "end of file", which is simply a packet smaller than
+				// the maximum fetch size
+				if (bodydata)
+					{
+					endOfStream=bodydata->Atom().Length()!=fetchSizeBytes;
+					DecodeAndStoreL(bodydata->Atom(),endOfStream);
+					}
+				}
+			else
+				{
+				// Here, we'll treat anything 100 or more bytes shy of the maximum packet size as
+				// end of file. If we're closer than that, a new read will have been issued which will
+				// return 0 bytes, which *will* cause the EOF to be signalled.
+				if (bodydata)
+					{
+					endOfStream=wholeMessage || (!((fetchSizeBytes-bodydata->Atom().Length())<100) || (iSizeLeftToFetch == 0));
+					
+					// depending on whether or not we found an unwanted mime header we need to get rid of it.
+					// This is done in response to the imap server issues where the server was sending additional information.
+					// At this point we know how big the extra header is and we can calculate the correct size of the bodydata by subtracting the 
+					// size of the header. 
+					if(foundUnwantedMimeHeader)
+						{
+						foundUnwantedMimeHeader=EFalse;
+						//calculate size of actual bodydata without the additional header
+						TInt length = bodydata->Atom().Length() - header->Atom().Length();
+						//call decode and store, but only pass in the data we are interested in i.e.: skip the additional header.
+						DecodeAndStoreL(bodydata->Atom().Right(length),endOfStream);
+						}
+					else //bodydata only points to the data
+						{
+						DecodeAndStoreL(bodydata->Atom(),endOfStream);
+						}
+					}
+				}
+
+			// Update flags on message: it should have been marked as read if it wasn't
+			// already
+			if (flags || endOfStream && ( iState != EImapStateFetchCancelWait ))
+				{
+				// SJM 19990922: Previously this only set the Unread
+				// flag in a rather inefficient way. Change to use new
+				// return value of ProcessFlags
+				SetEntryL(iMessageId);
+				TMsvEmailEntry message=iEntry->Entry();
+
+				TBool hasBodyText = message.iType == KUidMsvEmailTextEntry || message.iType == KUidMsvEmailHtmlEntry;
+				TBool partiallyDownloaded = EFalse;
+				if (endOfStream)
+					{
+					message.SetComplete(ETrue);
+					if(iFetchPartialMail && iBodyPartRemainingSize && message.iType == KUidMsvEmailTextEntry) 
+						{
+						message.SetPartialDownloaded(ETrue);
+						partiallyDownloaded = ETrue;
+						}
+					else
+						{
+						message.SetPartialDownloaded(EFalse);
+						}
+
+					if (hasBodyText)
+						message.SetBodyTextComplete(ETrue);
+					}
+				
+				// Process flags in this fetch response
+				TBool changed = EFalse;
+				if (flags)
+					changed = ProcessFlagsL(flags,message);
+
+				if (changed || endOfStream)
+					ChangeEntryBulkL(message);
+
+				if (endOfStream)
+					{
+					//iMessagePartsFetchOK++;
+					PropagateCompleteFlagL(iMessageId, hasBodyText, partiallyDownloaded);
+					}
+				}
+			break;
+			}
+
+		case ESyncListNew:
+		case ESyncOld:
+			// Got a message's details
+			
+			// check folder position is not out of bounds and that we have not run out of the local index.
+			if (iFolderPosition >= iFolderIndex.Size())
+				{
+				// All done/ array was out of bounds
+				DBG((LogText(_L8("ERROR - Position %d was out of bounds for the FolderIndex arrays size (%d)"),iFolderPosition,iFolderIndex.Size())));
+				iSyncState = ENotSyncing;
+				break;
+				}
+
+			// Collecting UIDs of messages in the folder
+			DBG((LogText(_L8("At pos %d, expecting UID %u, got UID %u"),
+							 iFolderPosition,iFolderIndex[iFolderPosition].iUid,iMessageUid)));
+
+			// Messages deleted from remote mailbox?
+			while(iFolderPosition<iFolderIndex.Size() &&
+				  iMessageUid>iFolderIndex[iFolderPosition].iUid)
+				{
+				// Orphan this message
+				DBG((LogText(_L8("Orphaning UID %u"),iFolderIndex[iFolderPosition].iUid)));
+
+				if (iFolderIndex[iFolderPosition].iUid != KIllegalUID)
+					{
+					// Do it
+					OrphanMessageL(iFolderIndex[iFolderPosition].iMsvId);
+					}  
+				else
+					{
+					DBG((LogText(_L8("Illegal UID, do not delete."))));
+					}
+
+				// Remove it from the index
+				iFolderIndex.Expunge(iFolderPosition+1);
+
+				// Increment stats
+				iOrphanedMessages++;
+				}
+
+			// Run out of local index?
+			if (iFolderPosition>=iFolderIndex.Size())
+				{
+				// All done
+				iSyncState=ENotSyncing;
+				}
+			// In sync again?
+			else if (iMessageUid==iFolderIndex[iFolderPosition].iUid)
+				{
+				// Fine, mirror flag information onto local message
+				DBG((LogText(_L8("Match UID %u, mirroring flags"),iMessageUid)));
+
+				// Mirror flags and ensure that the message is visible
+				// as it might have been made invisible by
+				// unsubscribing.
+				SetEntryL(iFolderIndex[iFolderPosition].iMsvId);
+				TMsvEmailEntry message=iEntry->Entry();
+				if (ProcessFlagsL(flags,message)|| !message.Visible())
+                    {
+                    message.SetVisible(ETrue);
+					ChangeEntryBulkL(message);
+       				}
+
+				if (message.DeletedIMAP4Flag())
+					iRemoteMessagesDeleteTagged++;
+				
+				// Next message
+				iFolderPosition++;
+
+				// Update counters.
+				iMsgsDone++;
+				iHeadersFetched++;
+				}
+			else if (iMessageUid<iHighestUid)
+				{
+				// If we are seeing uids below the oldest mirrored message, then it is likely
+				// that the sync limit has changed. The following code captures the range of 
+				// "missing" messages.
+				if (iMessageUid<iMissingUidLow || iMissingUidLow==0)
+					iMissingUidLow=iMessageUid;
+				if (iMessageUid>iMissingUidHigh || iMissingUidHigh==0)
+					iMissingUidHigh=iMessageUid;
+				}
+			break;
+
+		case EGettingStructure:
+			{
+			error = KErrNone;
+			User::LeaveIfError(iEntry->SetEntry(iGetPart));
+
+			if (!(iEntry->Entry().Owner()))
+				{
+				TMsvEmailEntry entry = iEntry->Entry();
+				
+				//reset flag for new message
+				iParsedTime=EFalse;
+				//initialise the time-stamp to the current UTC time
+				entry.iDate.UniversalTime();
+
+				// Make a CImHeader to populate with the data
+				CImHeader *messageheader=CImHeader::NewLC();
+				if (header)
+					ProcessHeaderExtraL(messageheader,NULL,&entry,header->Atom());
+
+				// Set correct 'remote size' in CImHeader
+				messageheader->SetRemoteSize(rfc822size);
+
+				// Create a message store.
+				CMsvStore* entryStore=iEntry->EditStoreL();
+				CleanupStack::PushL(entryStore);
+
+				// Store the RFC822 header information.
+				messageheader->StoreL(*entryStore);
+				entryStore->CommitL();
+				CleanupStack::PopAndDestroy(entryStore);
+
+				TInt attachments=0;
+				TInt relatedAttachments=0;
+				TBool isMHTML=EFalse;
+				iDecodedSizeOfAllParts = 0;
+
+				// Create the message entry structure under the root message
+				BuildTreeL(entry.Id(),structure,_L8(""),entry.Id(),attachments,isMHTML,relatedAttachments);
+				if(isMHTML==EFalse)
+					attachments+=relatedAttachments;
+
+				// Now that the structure has been created we can set the real message attributes.
+				// The MHTML, attachment flags and size were estimated (hopefully correctly) when the envelope was downloaded.
+				entry.iSize = iDecodedSizeOfAllParts;
+				entry.SetMHTMLEmail(isMHTML);
+				entry.SetAttachment(attachments);
+				entry.SetICalendar(iIsICalendar);
+				entry.SetVCalendar(iIsVCalendar);
+
+				/* If IDLE is enabled for the account , a new session is not created,
+				   so iIsICalendar,iIsVCalendar are not initialised to EFalse 
+				   when we fetch a new message, so do it HERE */
+
+				iIsICalendar = EFalse;
+				iIsVCalendar = EFalse;
+
+				User::LeaveIfError(iEntry->SetEntry(entry.Id()));
+				User::LeaveIfError(iEntry->ChangeEntryBulk(entry));
+				CleanupStack::PopAndDestroy(messageheader);
+				}
+
+			if (ImapIdleSupported()==EFalse)
+				{
+				CancelDummy();
+				}
+
+			DoFetchL();
+			iJustSentFetch = ETrue;
+			}
+			break;
+
+		case ESyncNew:
+			{
+			// Got a message's details
+
+			// First, let's check we asked for it: for example, the UW server
+			// gives us messages not in the correct range!
+			if (iMessageUid<=iHighestUid) 
+				{
+				DBG((LogText(_L8("Searching local messages for %d"),iMessageUid)));
+				while (iFolderPosition<iFolderIndex.Size())
+					{
+					if (iMessageUid==iFolderIndex[iFolderPosition].iUid)
+						{
+						DBG((LogText(_L8("Got duplicate UID %d - ignoring"),iMessageUid)));
+						return(error);
+						}
+					else if (iFolderIndex[iFolderPosition].iUid>iMessageUid || iFolderIndex[iFolderPosition].iMsvId==-1)
+						break;
+					iFolderPosition++;
+					}
+				}
+
+			// Update counters.
+			iMsgsDone++;
+			iHeadersFetched++;
+
+			// Creating messages in current folder: create this one
+			SetEntryL(iMailboxId);
+
+			// Check to see we have at least the minimum free disk space available
+			if (--iCheckDiskSpaceCounter <= 0)
+				{
+				// If we are running low on disk space then leave
+				ImCheckDiskSpace::LeaveIfLowDiskL(iFs, iCurrentDrive);
+				iCheckDiskSpaceCounter = KCheckDiskSpaceEveryNMessages;
+				}
+	
+			TMsvEmailEntry message;
+			message.iType=KUidMsvMessageEntry;
+			message.iMtm=KUidMsgTypeIMAP4;
+			message.iServiceId=iServiceId;
+			message.SetUID(iMessageUid);
+			message.SetValidUID(ETrue);
+			message.SetComplete(EFalse);
+			message.SetUnread(ETrue);
+			
+			//reset flag for new message
+			iParsedTime=EFalse;
+			//initialise the time-stamp to the current UTC time
+			message.iDate.UniversalTime();
+
+			// Process message flags
+			ProcessFlagsL(flags,message);
+
+			if (message.DeletedIMAP4Flag())
+				iRemoteMessagesDeleteTagged++;
+				
+			// Size of root message entry gets twiddled later
+			message.iSize=0;
+
+			// Set new flag
+			message.SetNew(ETrue);
+
+			// initialise the send state since the constructor sets it
+			// to StateUnknown
+			message.SetSendingState(KMsvSendStateNotApplicable);
+			
+			// Make a CImHeader to populate with the data
+			CImHeader *messageheader=CImHeader::NewLC();
+
+			if (header)
+				{
+				ProcessHeaderExtraL(messageheader,NULL,&message,header->Atom());
+				}
+
+			// Set correct 'remote size' in CImHeader
+			messageheader->SetRemoteSize(rfc822size);
+
+			// Save message size & attachment flag
+  			SetMessageFlagsL(message, structure);
+
+			// Create message
+			User::LeaveIfError(iEntry->CreateEntryBulk(message));
+			SetEntryL(iMessageId=message.Id());
+
+
+#if SET_RELATED_ID
+			// DS - Set message's iRelatedId to messageId to allow later UI kludges
+			message.iRelatedId=iMessageId;
+#endif
+			ChangeEntryBulkL(message);
+			CleanupStack::PopAndDestroy(messageheader);
+			break;
+			}
+		default:
+			// ESyncListNew and ESyncSearch should not result in a FETCH reply.
+			__ASSERT_DEBUG(EFalse,gPanic(EUnknownState));
+			break;
+			}
+		}
+
+	return(error);
+	}
+
+// code originally from void CImRecvConvert::ParseRecipientListL(...)
+void CImImap4Session::ProcessAddressListL(CDesCArray& aWhere, HBufC8** aAddresses)
+ 	{
+	TInt length((*aAddresses)->Length());
+ 	HBufC8* pBuf=HBufC8::NewLC(length);
+ 	TPtrC8 source((*aAddresses)->Ptr(), length);
+ 	const TUint8* ptr(source.Ptr());
+ 	const TUint8* lastCharPtr(ptr + source.Length() - 1);
+ 	TUint8 lookFor(0);
+ 	TInt count(0);
+ 	TBool finishedEntry(EFalse);
+ 
+ 	// get past white space
+ 	while(*ptr&&((*ptr==KImcvSP)||(*ptr==KImcvSemiColon))) ptr++;
+ 
+ 	// Entries are separated by commas or semicolons.
+ 	// Separators do not count if they appear within
+ 	// "", <>, () or embedded series of these, eg "(one, two)"
+ 	// so we need to keep track of these, including nesting.
+ 	while(*ptr && ptr <= lastCharPtr)
+ 		{
+ 		if(pBuf->Length()==0)
+ 			{
+ 			finishedEntry = EFalse;
+ 			}
+ 
+ 		switch(*ptr)
+ 			{
+ 			case KImcvLeftBracket:
+ 				if(lookFor==KImcvRightBracket)
+ 					{ // We've already had a "(", so now we need another one
+ 					count++;
+ 					}
+ 				else if(lookFor==0)
+ 					{ //We weren't looking for anything else, now we need to
+ 					lookFor = KImcvRightBracket;
+ 					count = 1;
+ 					}
+ 				// else we were already looking for something else, ignore this
+ 				break;
+ 			case KImcvLeftChevron:
+ 				if(lookFor==KImcvRightChevron)
+ 					{ //We've already had a "<", so now we need another one
+ 					count++;
+ 					}
+ 				else if(lookFor==0)
+ 					{ //We weren't looking for anything else
+ 					lookFor = KImcvRightChevron;
+ 					count = 1;
+ 					}
+ 				// else we were already looking for something else, ignore this
+ 				break;
+ 			case KImcvDoubleQuote:
+ 				if(lookFor==KImcvDoubleQuote)
+ 					{ // We already had a quote, so this matches it
+ 					lookFor = 0;
+ 					}
+ 				else if(lookFor==0)
+ 					{ //We weren't looking for anything else
+ 					lookFor = KImcvDoubleQuote;
+ 					}
+ 				// else we were already looking for something else, ignore this
+ 				break;
+ 			case KImcvRightBracket:
+ 			case KImcvRightChevron:
+ 				if(*ptr == lookFor)
+ 					{ //If we have found what we were looking for, decrease the count
+ 					count--;
+ 					if(count==0)
+ 						{ // Got everything, now we're not looking for anything
+ 						lookFor = 0;
+ 						}
+ 					// else keep looking for the same thing	again
+ 					}
+ 				// else we're looking for something else, ignore it
+ 				break;
+ 			case KImcvComma:
+ 			case KImcvSemiColon:
+ 				// If we're not looking for anything, we're finished
+ 				if (lookFor == 0)
+ 					finishedEntry = ETrue;
+ 				// else this comma or semicolon is part of a different token, ignore it
+ 				break;
+ 			}
+ 
+ 		if(!finishedEntry)
+ 			{
+ 			pBuf->Des().Append((TChar)*ptr);
+ 			// move to the next character
+ 			ptr++;
+ 			}
+ 		else
+ 			{
+ 			// that's it! store the address away
+#ifdef UNICODE
+ 			HBufC16* pBuf16 = HBufC16::NewLC(pBuf->Des().Length());
+ 			pBuf16->Des().Copy(pBuf->Des());
+ 			aWhere.AppendL( (HBufC16&) *pBuf16 );
+ 			CleanupStack::PopAndDestroy(pBuf16); // pBuf16
+#else
+			aWhere.AppendL( *pBuf );
+#endif			
+ 			pBuf->Des().SetLength(0);
+ 			finishedEntry = EFalse; //Ready for next entry
+ 
+ 			// get past the separator
+ 			ptr++;
+ 
+ 			// get past white space (& any other separators)
+ 			while(*ptr && (*ptr==KImcvSP || *ptr==KImcvTab || *ptr==KImcvComma || *ptr==KImcvSemiColon)) ptr++;
+ 			}
+ 		}
+ 		// catch the last name in the list
+ 		if (pBuf)
+ 			{
+ 			TInt recipientLength(pBuf->Length());
+ 			if (recipientLength > 0)
+ 				{
+#ifdef UNICODE
+ 				HBufC16* pBuf16 = HBufC16::NewLC(recipientLength);
+ 				pBuf16->Des().Copy(*pBuf);
+ 				aWhere.AppendL(*pBuf16);
+ 				CleanupStack::PopAndDestroy(pBuf16); // pBuf16
+#else
+ 				aWhere.AppendL( *pBuf );
+#endif
+ 				}
+ 			}
+ 		CleanupStack::PopAndDestroy(pBuf); // pBuf
+ 	}
+
+void CImImap4Session::ProcessFooterMessageL(TInt aSizeLeft)
+	{
+	TUid type = iEntry->Entry().iType;	
+	if (type == KUidMsvEmailTextEntry)
+		{
+		if(iHtmlEntrySize)
+			{
+			iBodyPartRemainingSize = aSizeLeft + iHtmlEntrySize;
+			}
+		else
+			{
+			iBodyPartRemainingSize = aSizeLeft;
+			}
+		// Message has both text and html and if sizeleft = 0 ,
+		// then there could be only html part left on server
+		// Message has only plain text, then footer message not required if sizeleft = 0
+		if(iBodyPartRemainingSize)
+			AttachFooterInfoL();
+		}
+	}
+
+void CImImap4Session::AttachFooterInfoL()
+	{
+	DBG((LogText(_L8("AttachFooterInfoL(): Footer Sting for this partially downloaded message"))));
+	RResourceFile resFile;
+	OpenResourceFileL(resFile,iFs);	// NB leaves if file not found
+	const TInt KNoOfDigitsInMailSize = 10; // maximum length for no of digits for remaining body parts size
+	_LIT(KIntegerDirective,"%d");
+	// make sure the resource file will be closed if anything goes wrong
+	// CloseResourceFile is declared in IMCMMAIN.H and defined in IMCMMAIN.CPP
+	TCleanupItem close(CloseResourceFile,&resFile);
+	CleanupStack::PushL(close);
+		
+	// Read the string for remaining mail size for footer
+	HBufC8* buf = NULL;
+	buf = resFile.AllocReadLC( PARTIAL_DOWNLOAD_FOOTER_MESSAGE );
+	TResourceReader reader;
+	reader.SetBuffer(buf);
+	// Check if %d is not found in the resource string (To avoid problems due to localisation)
+	if(buf->Find((TDesC8&)KIntegerDirective) == KErrNotFound)
+		{
+		iFooterString = (reader.ReadTPtrC()).AllocL();
+		}
+	else
+		{
+		HBufC* resourceBuf = (reader.ReadTPtrC()).AllocL();
+		iFooterString = HBufC::NewL(resourceBuf->Length()+KNoOfDigitsInMailSize);
+		iFooterString->Des().Format(*resourceBuf,(iBodyPartRemainingSize / KKiloByteSize));
+		delete resourceBuf;
+		}
+	CleanupStack::PopAndDestroy(2); // buf, resFile (Close resfile)
+	}
+
+// Set the following attributes of the given message entry for the IMAP information contained in atom tree:
+// iSize
+// 
+void CImImap4Session::SetMessageFlagsL(TMsvEmailEntry& aMessageEntry, CImapAtom* aRootAtom)
+	{
+	CArrayFixFlat<CImapAtom*>* atomStack = new (ELeave) CArrayFixFlat<CImapAtom*>(10);
+	CleanupStack::PushL(atomStack);
+	atomStack->AppendL(aRootAtom);
+
+	TBool hasAttachments = EFalse;
+	TBool hasHtml = EFalse;
+	TBool possibleHtml = EFalse;
+	TBool afterRelated = EFalse;
+	TBool afterAlternative = EFalse;
+	TBool htmlAfterAltRel = EFalse;
+	TBool hasICalendar = EFalse;
+	TBool hasVCalendar = EFalse;
+	TInt size = 0;
+
+	CImapAtom* currentAtom;
+
+	TBool doneAllSiblings;
+	TBool base64;
+	TInt index;
+	TBool numeric;
+	TInt atomValue;
+	TBool foundSize;
+	
+	// the root atom is of these types, the message body is an attachment
+	if(aRootAtom->Compare(KMIME_IMAGE) || 
+		aRootAtom->Compare(KMIME_AUDIO) || 
+		aRootAtom->Compare(KMIME_APPLICATION)||
+		aRootAtom->Compare(KMIME_VIDEO))
+		{
+		hasAttachments = ETrue;
+		}
+	
+	while (atomStack->Count() != 0)
+   		{
+		// Pop the top atom off of the stack
+		currentAtom = (*atomStack)[atomStack->Count() - 1];
+ 		atomStack->ResizeL(atomStack->Count() - 1);
+		base64 = EFalse;
+ 
+		// Run through all the sibling atoms unless this atom is a message/rfc822
+		if( currentAtom->Compare(KMIME_MESSAGE) && currentAtom->Next()->Compare(KMIME_RFC822) )
+			doneAllSiblings = ETrue;
+		else
+			doneAllSiblings = EFalse;
+		foundSize = EFalse;
+		possibleHtml = EFalse;
+		TInt siblingIndex = 0;
+		while (!doneAllSiblings)
+			{
+			// If a previous Sibling Atom has a HTML tag, then this could possibly 
+			// be an HTML message.  So we need to check all Sibling Atoms (ie the 
+			// current atom) to see if they contain Attachments (ie check their 
+			// child atoms for the Tag "ATTACHMENT"). If an attachment is found 
+			// then ignore the HTML flag. If none of the siblings contain an 
+			// attachment, then set the HTML flag, as this is a MHTML message.
+			if (possibleHtml)
+				{
+				// Check if the Child has an Attachment
+				if (currentAtom->Child() != NULL)
+					{
+					if (currentAtom->Child()->Compare(KMIME_ATTACHMENT))
+						{
+						// This Sibling contains an Attachment, so ignore the HTML flag.
+						possibleHtml = EFalse;
+						}
+					}
+
+				// Check if we have searched all Sibling Atoms
+				if (possibleHtml && currentAtom->Next() == NULL)
+					{
+					// None of the Siblings have attachments, so set the HTML flag.
+					hasHtml = ETrue;
+					possibleHtml = EFalse;
+					}
+					
+				}
+
+			// If there is a child atom then add it to the stack, we will check it later
+			if (currentAtom->Child() != NULL)
+				atomStack->AppendL(currentAtom->Child());
+
+			// If we pass a related atom then we may have an html message
+			// remember for later
+			if (currentAtom->Compare(KMIME_RELATED))
+				afterRelated = ETrue;
+
+			// If we pass an alternative atom then we may have an html message
+			// remember for later
+			if (currentAtom->Compare(KMIME_ALTERNATIVE))
+				afterAlternative = ETrue;
+
+			// If we find an html under a related or alternative then we
+			// can ignore the mixed section and assume that we have an html mail
+			if (afterAlternative || afterRelated)
+				{
+				if (currentAtom->Compare(KMIME_HTML))
+					htmlAfterAltRel = ETrue;
+				}
+
+			// MIXED ? If so then this email probably contains attachments
+			// or if there is a message/delivery-status then that is an
+			// attachment. 
+			if (currentAtom->Compare(KMIME_MIXED) || currentAtom->Compare(KMIME_DELIVERY_STATUS))
+				{
+				hasAttachments = ETrue;
+				}
+		
+			// HTML ?  If so this email could be an HTML message.  To make sure 
+			// we need to check this Atom's siblings
+			if (currentAtom->Compare(KMIME_HTML))
+				possibleHtml = ETrue;		
+
+			// Does this sibling atom say that the data is base64 encoded ?
+			// If so then we need to remember it to calculate the size.
+			if (currentAtom->Compare(KMIME_BASE64))
+				base64 = ETrue;
+			
+			if (currentAtom->Compare(KMIME_ICALENDAR))
+				hasICalendar = ETrue;
+			
+			if (currentAtom->Compare(KMIME_VCALENDAR))
+				hasVCalendar = ETrue;
+
+			// If this is the first numeric value of the current siblings then it is a size
+			// and must be added to the message size total
+			// Note that this size must be multiplied by 3/4 if it is base64
+			if (!foundSize && siblingIndex == 6)
+				{
+				index = currentAtom->Atom().Length();
+				
+				if (index != 0)
+					numeric = ETrue;
+				else
+					// If the atom is of 0 length then it can't possibly be numeric.
+					numeric = EFalse;
+
+				while ((index--) && (numeric))
+					{
+					if ((currentAtom->Atom()[index] < '0')
+						|| (currentAtom->Atom()[index] > '9'))
+						numeric = EFalse;
+					}
+
+				if (numeric)
+					{
+					TLex8 lex(currentAtom->Atom());
+					User::LeaveIfError(lex.Val(atomValue));
+					if (base64)
+						atomValue = (atomValue * 3) / 4;
+					size += atomValue;
+					foundSize = ETrue;
+					}
+				}
+
+			siblingIndex++;
+			currentAtom = currentAtom->Next();
+			if (currentAtom == NULL)
+				doneAllSiblings = ETrue;
+			}
+   		}
+
+	// Set the size
+	aMessageEntry.iSize = size;
+
+	// Set the Attachment, MHTML, ICalendar and VCalendar flags, if required.
+	if (hasAttachments)
+		{
+		aMessageEntry.SetAttachment(ETrue);
+		}
+
+	if( hasHtml || htmlAfterAltRel )
+		{
+		aMessageEntry.SetMHTMLEmail(ETrue);
+		}
+
+	if(hasICalendar)
+		{
+		aMessageEntry.SetICalendar(ETrue);
+		}
+		
+	if(hasVCalendar)
+		{
+		aMessageEntry.SetVCalendar(ETrue);
+		}
+		
+	CleanupStack::PopAndDestroy(atomStack);
+   	}
+
+TInt32 CImImap4Session::GetFetchSizeL(TInt32 aSizeLeft, TInt32 aSizeDownLoaded)
+{
+	TInt fetchSizeBytes = static_cast<TInt>(iServiceSettings->FetchSize());	
+	TInt32 minimumLimit = fetchSizeBytes;
+	TInt32 fetchSize = fetchSizeBytes;
+	TUid type = iEntry->Entry().iType;
+
+	if(iGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits)
+		{
+		return KMaxTInt;
+		}
+	
+	if (type == KUidMsvEmailTextEntry  || type == KUidMsvEmailHtmlEntry)
+		{
+		if(type == KUidMsvEmailHtmlEntry)
+			{
+//			iHtmlEntrySize = iSizeOfThisPart;
+			minimumLimit = Minimum(iGetPartialMailInfo.iBodyTextSizeLimit,iGetPartialMailInfo.iTotalSizeLimit-iBodyTextSize);
+			}
+		else
+			{
+			// store body text size so that we can check whether html part for 
+			// this message can be downloaded 
+			// text size + html size < iBodyTextSizeLimit then download html part
+			iBodyTextSize = iSizeOfThisPart;
+			minimumLimit = Minimum(iGetPartialMailInfo.iBodyTextSizeLimit,iGetPartialMailInfo.iTotalSizeLimit);
+			}
+		// check disk space
+		if(!iIsDiskSpaceChecked)
+			{
+			CheckForDiskSpaceL(minimumLimit);
+			iIsDiskSpaceChecked = ETrue;
+			}
+
+		fetchSize = FetchSize(minimumLimit,aSizeDownLoaded,aSizeLeft);
+		}
+	else if (type == KUidMsvAttachmentEntry || type == KUidMsvEmailExternalBodyEntry)
+		{
+		minimumLimit = Minimum(iGetPartialMailInfo.iAttachmentSizeLimit,
+						iGetPartialMailInfo.iTotalSizeLimit-(iBodyTextSize+iHtmlEntrySize));
+
+		if(!iIsDiskSpaceChecked)
+			{
+			CheckForDiskSpaceL(minimumLimit);
+			iIsDiskSpaceChecked = ETrue;
+			}
+
+		fetchSize = FetchSize(minimumLimit,aSizeDownLoaded,aSizeLeft);
+		}
+	return fetchSize;
+	}
+
+TInt32 CImImap4Session::FetchSize(TInt32 aMinimumLimit,TInt32 aSizeDownLoaded, TInt32 aSizeLeft)
+	{
+	TInt fetchSizeBytes = static_cast<TInt>(iServiceSettings->FetchSize());	
+	TInt32 fetchSize = fetchSizeBytes;
+	if(aSizeLeft > (aMinimumLimit-aSizeDownLoaded))
+		{
+		if((aMinimumLimit-aSizeDownLoaded) < fetchSizeBytes)
+			{
+			fetchSize = aMinimumLimit-aSizeDownLoaded;
+			}
+		}
+	else
+		{
+		if(aSizeLeft < fetchSizeBytes)
+			{
+			fetchSize = aSizeLeft;
+			}
+		}
+	return fetchSize;
+	}
+
+// Process an address list structure into a CImHeader
+void CImImap4Session::ProcessAddressListL(HBufC8 **aBufferPtr, CDesCArray& aWhere, CImapAtom *aAtom)
+	{
+	while(aAtom)
+		{
+		// Process this address and add it to ToRecipients()
+		ProcessAddressL(aBufferPtr,aAtom);
+
+#ifdef UNICODE
+		HBufC *newaddress=HBufC::NewL((*aBufferPtr)->Length());
+		CleanupStack::PushL(newaddress);
+		newaddress->Des().Copy((*aBufferPtr)->Des());
+		aWhere.AppendL(newaddress->Des());
+		CleanupStack::PopAndDestroy();
+#else
+		aWhere.AppendL((*aBufferPtr)->Des());
+#endif
+
+		// Next address
+		aAtom=aAtom->Next();
+		}
+	}
+
+void CImImap4Session::AppendExtendL(HBufC8** aBufferPtr, const TDesC8& aText, TBool aOnStack)
+	{
+	HBufC8 *buffer = *aBufferPtr;
+	TInt32 space = buffer->Des().MaxLength() - (buffer->Des().Length() + aText.Length());
+	if (space < 0)
+		{
+		TInt32 inc = (-space) + KImapAddressSizeInc - ((-space) % KImapAddressSizeInc);
+		TInt32 newSize = buffer->Des().MaxLength() + inc;
+		HBufC8 *newBuf = buffer->ReAllocL(newSize);
+
+		if (aOnStack && newBuf!=buffer)
+			{
+			CleanupStack::Pop();
+			CleanupStack::PushL(newBuf);
+			}
+		
+		*aBufferPtr = buffer = newBuf;
+		}
+	buffer->Des().Append(aText);
+	}
+
+// Process an address structure into a buffer
+void CImImap4Session::ProcessAddressL(HBufC8** aBufferPtr, CImapAtom *aAtom)
+	{
+	// Erase buffer
+	(*aBufferPtr)->Des().Zero();
+
+	// Descend into address
+	aAtom=aAtom->ToChildL();
+
+	// Save name
+	CImapAtom* name=aAtom;
+	aAtom=aAtom->ToNextL();
+
+	// Skip route
+	aAtom=aAtom->ToNextL();
+
+	// Save user
+	CImapAtom* user=aAtom;
+	aAtom=aAtom->ToNextL();
+
+	// Save host
+	CImapAtom* host=aAtom;
+
+	// Build address string: is there a name?
+	if (name->Compare(_L8("")) || name->Compare(_L8("NIL")))
+		{
+		// No, just save user@host
+		AppendExtendL(aBufferPtr, user->Atom());
+		AppendExtendL(aBufferPtr, _L8("@"));
+		AppendExtendL(aBufferPtr, host->Atom());
+		}
+	else
+		{
+		// Yes, in the form 'Name <user@host>'
+		AppendExtendL(aBufferPtr, _L8("\""));
+		AppendExtendL(aBufferPtr, name->Atom());
+		AppendExtendL(aBufferPtr, _L8("\""));
+		AppendExtendL(aBufferPtr, _L8(" <"));
+		AppendExtendL(aBufferPtr, user->Atom());
+		AppendExtendL(aBufferPtr, _L8("@"));
+		AppendExtendL(aBufferPtr, host->Atom());
+		AppendExtendL(aBufferPtr, _L8(">"));
+		}
+
+#ifdef PRINTING
+	TPtrC8 addr=(*aBufferPtr)->Des();
+	if (addr.Length() > 256)
+		addr.Set(addr.Ptr(), 256);
+	LogText(_L8("  address '%S'"),&addr);
+#endif
+	}
+
+// SJM 19990922. Previous version of this function used SetUnread
+// rather than SetIMAP4Unread or SetIMAP4Flags. This meant that the
+// IMAP4Unread flag itself was never set. This seems like a bug.
+
+TBool CImImap4Session::ProcessFlagsL(CImapAtom* aAtom, TMsvEmailEntry& aMessage)
+	{
+	TBool unread = EFalse;
+    TBool seen = EFalse;
+	TBool answered = EFalse;
+	TBool flagged = EFalse;
+	TBool deleted = EFalse;
+	TBool draft = EFalse;
+	TBool recent = EFalse;
+	TBool flagsUpdated = EFalse;
+
+	// Descend
+	aAtom=aAtom->Child();
+
+	// Process flags
+	while(aAtom!=NULL)
+		{
+		// Check for standard IMAP flags
+		if (aAtom->Compare(KIMAPFLAG_ANSWERED))
+			answered = ETrue;
+		else if (aAtom->Compare(KIMAPFLAG_DELETED))
+			deleted = ETrue;
+		else if (aAtom->Compare(KIMAPFLAG_DRAFT))
+			draft = ETrue;
+		else if (aAtom->Compare(KIMAPFLAG_FLAGGED))
+			flagged = ETrue;
+		else if (aAtom->Compare(KIMAPFLAG_RECENT))
+			recent = ETrue;
+		else if (aAtom->Compare(KIMAPFLAG_SEEN))
+			seen = ETrue;
+		else if (aAtom->Compare(KIMAPFLAG_UNREAD))
+			{
+			unread = ETrue;
+
+			// There is at least one unread message in this folder
+			iSomeUnread=ETrue;
+			}
+
+		// Next atom
+		aAtom=aAtom->Next();
+		}
+
+
+	TBool oUnread, oSeen, oAnswered, oFlagged, oDeleted, oDraft, oRecent;
+	aMessage.GetIMAP4Flags(oUnread, oSeen, oAnswered, oFlagged, oDeleted, oDraft, oRecent);
+
+	// Are we configured to update the \seen flag on the server?
+	if (iServiceSettings->UpdatingSeenFlags())
+		{
+		// Make a note to update the servers \Seen flag if CHANGED on the client
+		//  and different to the servers version
+		if ( FIXBOOL(aMessage.Unread()) == seen && FIXBOOL(oSeen) == seen)
+			{
+			if (aMessage.Unread())
+				iClearSeenList->AppendL(aMessage.Id());
+			else
+				iSetSeenList->AppendL(aMessage.Id());
+			}
+		}
+
+	if ( FIXBOOL(oUnread) != unread || FIXBOOL(oSeen) != seen || FIXBOOL(oAnswered) != answered
+		 || FIXBOOL(oFlagged) != flagged || FIXBOOL(oDeleted) != deleted
+		 || FIXBOOL(oDraft) != draft || FIXBOOL(oRecent) != recent )
+		{
+		aMessage.SetIMAP4Flags(unread, seen, answered, flagged, deleted, draft, recent);
+		flagsUpdated = ETrue;
+		}
+
+	// Are we configured to update the \seen flag on the server?
+	if (iServiceSettings->UpdatingSeenFlags())
+		{
+		// Now copy the inverse of the \Seen flag down to the clients Unread flag 
+		//  except when LastSyncSeen is set (ie when the client version is more up to date)
+		//  This means that the client Read status ALWAYS reflects the IMAP \Seen state
+		if ( FIXBOOL(aMessage.Unread()) == seen && FIXBOOL(oSeen) != seen)
+			{
+			aMessage.SetUnread(!seen);
+			flagsUpdated = ETrue;
+			}
+		}
+
+	return flagsUpdated;
+	}
+
+// Process output from list command
+void CImImap4Session::ProcessListL(CImapAtom *aAtom)
+	{
+	// Just getting hierarchy separator?
+	if (iState==EImapStateSeparatorWait)
+		{
+		// Save it away
+		aAtom=aAtom->ToNextL();
+
+		// Is it one character long? (all should be) - if not, ignore it
+		if (aAtom->Atom().Length()!=1)
+			return;
+
+		// Save it for local use
+		iHierarchySeparator=aAtom->Atom();
+
+		// Update service entry
+  		CEmailAccounts* account = CEmailAccounts::NewLC();
+		TImapAccount id;
+		id.iImapAccountId = iEntry->Entry().MtmData2();  // iMtmData2 of the service entry contains TImapAccountId
+		id.iImapAccountName = iEntry->Entry().iDetails;
+		id.iImapService = iEntry->Entry().iServiceId;
+		id.iSmtpService = iEntry->Entry().iRelatedId;
+
+  		account->LoadImapSettingsL(id, *iServiceSettings);
+  		
+  		// Set new path separator
+  		iServiceSettings->SetPathSeparator(iHierarchySeparator[0]);
+  		id.iImapAccountName = KNullDesC;    // So that account name is not updated
+  		account->SaveImapSettingsL(id, *iServiceSettings);
+  		CleanupStack::PopAndDestroy(account);    
+		return;
+		}
+
+	// Anywhere to save it?
+	if (!iList) return;
+
+	// Getting hierarchy from server - process the lot
+	CImapAtom *fl;
+	CImImap4DirStruct *entry=new (ELeave) CImImap4DirStruct;
+	CleanupStack::PushL(entry);
+
+	// List reply is of the form: * LIST (flags) "pathsep" "path"
+
+	// At the start, it's a mailbox
+	entry->iIsMailbox=ETrue;
+	entry->iIsFolder=ETrue;
+
+	// Process flags
+	if ((fl=aAtom->Child())!=NULL)
+		{
+		while(fl)
+			{
+			// Check flags
+			if (fl->Compare(KIMAPFLAG_NOSELECT))
+				{
+				// \Noselect means it isn't a mailbox
+				entry->iIsMailbox=EFalse;
+				}
+			else if (fl->Compare(KIMAPFLAG_NOINFERIORS))
+				{
+				// \Noinferiors means it can't be a folder
+				entry->iIsFolder=EFalse;
+				}
+
+			// Next flag
+			fl=fl->Next();
+			}
+		}
+
+	// Path separator: only save this if it is a valid length
+	aAtom=aAtom->ToNextL();
+	if (aAtom->Atom().Length()==1)
+		iHierarchySeparator=aAtom->Atom();
+
+	// Path
+	aAtom=aAtom->ToNextL();
+	TPtrC8 path(aAtom->Atom());
+
+	// In case server has returned us the parent when we asked for children,
+	// ignore items which are too small to be path(separator)child names.
+	if (path.Length()>iCommandBuf.Length())
+		{
+		// CC:Mail has an annoying habit of not listening to us when we ask for
+		// children, and returning siblings instead: this takes some extra CPU
+		// time, so we only do it if we're talking to a CC:Mail server, but here
+		// we check that the returned path is actually what we asked for before
+		// proceding.
+		if (iTalkingToCCMail)
+			{
+			// Check path starts with iCommandBuf's contents
+#ifdef UNICODE
+			HBufC8 *narrow=HBufC8::NewL(iCommandBuf.Length());
+			narrow->Des().Copy(iCommandBuf);
+			if (path.Find(*narrow)!=0)
+				{
+				// Nope. Ignore this. Silly server.
+				delete narrow;
+				CleanupStack::PopAndDestroy();
+				return;
+				}
+			delete narrow;
+#else
+			if (path.Find(iCommandBuf)!=0)
+				{
+				// Nope. Ignore this. Silly server.
+				CleanupStack::PopAndDestroy();
+				return;
+				}
+#endif
+			}
+			
+		// Add leaf, ignoring the path that all servers return before
+		// the leaf.
+
+		// Removed the UNICODE special case since we might need to
+		// modify the buffer now to do the UnModUTF7.
+#if 1
+		TPtrC8 leaf = path.Mid(iCommandBuf.Length());
+		HBufC* text = DoUnModUTF7LC( leaf );
+		entry->SetLeafnameL(text->Des());
+
+		CleanupStack::PopAndDestroy();
+#else
+		HBufC *newleaf=HBufC::NewL(path.Length()-iCommandBuf.Length());
+		CleanupStack::PushL(newleaf);
+		newleaf->Des().Copy(path.Mid(iCommandBuf.Length()));
+		entry->SetLeafnameL(newleaf->Des());
+		CleanupStack::PopAndDestroy();
+#endif
+
+		// If there's anything there (might just be the path), then add it to array
+		if (entry->Leafname().Length()>0)
+			{
+			// Add to array
+			iList->AppendL(entry);
+
+			// It's in the array now
+			CleanupStack::Pop();
+			}
+		else
+			{
+			// Get rid of it - it's not needed
+			CleanupStack::PopAndDestroy();
+			}
+		}
+	else
+		{
+		// Get rid of it - it's not needed
+		CleanupStack::PopAndDestroy();
+		}
+	}
+
+// Process output from list command
+void CImImap4Session::ProcessLsubL(CImapAtom *aAtom)
+	{
+	// Process the lot: it'll be a full path which we need to cut up
+	// Reply is of the form: * LSUB (flags) "pathsep" "path"
+
+	// Skip flags
+	aAtom=aAtom->ToNextL();
+
+	// We already know the path separator, so skip it
+	aAtom=aAtom->ToNextL();
+
+	// Strip folder path: to do this we just remove the start of the path from
+	// the returned string, so we need the length of it.
+	TInt pathlength=iFolderPath.Length();
+
+	// If the length is non-zero, this means that there is a folderpath, and
+	// we will need to strip both it, and the following path separator character.
+	// If there is no path, we don't want to strip anything, so we don't
+	// increment it at all.
+	if (pathlength)
+		pathlength++;
+
+	// Get a (possibly wide) copy of the path, skipping the prefix
+	TPtrC8 skippedPrefix(aAtom->Atom().Mid(pathlength));
+	HBufC* basePath = DoUnModUTF7LC( skippedPrefix );
+	TPtrC path(basePath->Des());
+	
+	// Go down through path
+	TMsvId where=iServiceId;
+	while(where)
+		{
+		// Truncate this bit of path: look for a hierarchy separator
+		TInt separator=path.Locate(iHierarchySeparator[0]);
+		TPtrC element(path);
+
+		// Anything?
+		if (separator!=KErrNotFound)
+			{
+			// Truncate this element there
+			element.Set(element.Ptr(),separator);
+			}
+
+		// Try to find this element at the search level
+		SetEntryL(where);
+		GetChildrenL(*iSelection);
+
+		// Nothing? Give up.
+		if (!iSelection->Count())
+			break;				// out of while
+
+		// Check all the children
+		TInt a;
+		for(a=0;a<iSelection->Count();a++)
+			{
+			// This one?
+			SetEntryL((*iSelection)[a]);
+
+			if (iEntry->Entry().iDetails.Compare(element)==0)
+				{
+				// Found it! Are we at the end, ie no more elements?
+				if (separator==KErrNotFound)
+					{
+					// Set subscribed flag
+					TMsvEmailEntry message=iEntry->Entry();
+					if (!message.Subscribed())
+						{
+						// It needs changing, do it
+						message.SetSubscribed(ETrue);
+						ChangeEntryL(message);
+						}
+
+					// All done: this will exit the loop
+					where=0;
+					break;
+					}
+				else
+					{
+					// Update path
+					where=(*iSelection)[a];
+					path.Set(path.Ptr()+separator+1,path.Length()-separator-1);
+
+					// Exit loop
+					break;
+					}
+				}
+			}
+
+		if (a==iSelection->Count())
+			{
+			// Didn't find it. Humm
+			DBG((LogText(_L8("Didn't find entry '%S': tree out of date?"),&element)));
+
+			break;				// out of while
+			}
+		}
+
+	CleanupStack::PopAndDestroy();
+	}
+
+void CImImap4Session::SendLoginL()
+	{
+	// We need to login in as few steps as possible, ie avoid using
+	// literals if possible as this incurrs a RTT delay as we have to
+	// wait for server's OK before sending the literal.
+
+	// Set up to send a command
+	NewTag();
+
+	// No need to quote? Do it in one line if we can,
+	if (!iLiteralUsername && !iLiteralPassword)
+		{
+		// Send login line. Turn off logging before sending
+		iImapIO->PerformLogging(EFalse);
+		iImapIO->SendL(iStatus,_L8("%d LOGIN %S %S\r\n"),iTag,iUsername,iPassword);
+		iImapIO->PerformLogging(ETrue);
+		}
+	else
+		{
+		if (iLiteralUsername)
+			{
+			// Send literal username
+			iImapIO->SendL(iStatus,_L8("%d LOGIN {%d}\r\n"),iTag,iUsername->Length());
+			iState=EImapStateLoginSendUser;
+			}
+		else
+			{
+			// Send username and literal password. Turn off logging before sending
+			iImapIO->PerformLogging(EFalse);
+			iImapIO->SendL(iStatus,_L8("%d LOGIN %S {%d}\r\n"),iTag,iUsername,iPassword->Length());
+			iImapIO->PerformLogging(ETrue);
+			
+			iState=EImapStateLoginSendPassword;
+			}
+		}
+
+	// Don't complete yet!
+	NewTagSent();
+	}
+
+void CImImap4Session::SendCapabilityL()
+	{
+	// Check server capabilities
+	iState=EImapStateCapabilityWait;
+	iSeenVersion=EFalse;
+
+	// Send the command
+	NewTag();
+	iImapIO->SendL(iStatus,_L8("%d CAPABILITY\r\n"),iTag);
+	NewTagSent();
+	}
+
+void CImImap4Session::StartIdle(TRequestStatus& aRequestStatus)
+	{
+        DBG((LogText(_L8("CImImap4Session::StartIdle()"))));
+	TInt err=KErrNone;
+	if (!Connected() || iState==EImapStateIdleWait)
+		{
+		Queue(aRequestStatus);
+		err=KErrDisconnected;
+		}
+	else
+		TRAP(err,StartIdleL(aRequestStatus));
+	if (err!=KErrNone)
+                {
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::StartIdle(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		
+
+		Complete(err);
+                }
+	}
+
+void CImImap4Session::StartIdleL(TRequestStatus& aRequestStatus)
+	{
+        DBG((LogText(_L8("CImImap4Session::StartIdleL()"))));
+	Queue(aRequestStatus);
+	DoStartIdleL();
+	}
+
+void CImImap4Session::DoStartIdleL()
+	{
+        DBG((LogText(_L8("CImImap4Session::DoStartIdleL()"))));
+ 	__ASSERT_DEBUG(ImapIdleSupported(), gPanic(EBadUseOfImap4Op));
+ 	if(!ImapIdleSupported())
+	 	{
+	 	User::LeaveIfError(KErrGeneral);//Bad use of Imap4Op
+	 	}
+ 	__ASSERT_DEBUG(iState==EImapStateSelected, gPanic(ESyncWhenNotSelected));
+	if(!(iState==EImapStateSelected))
+		{
+		User::LeaveIfError(KErrArgument);// Sync when not selected .
+		}
+	// Reset flags 	
+	iMailboxReceivedExists=EFalse;
+	iMailboxReceivedExpunge=EFalse;
+	iMailboxReceivedFlags=EFalse;
+	
+	iState=EImapStateIdleWait;
+	SendMessageL(KIMAPC_IDLE);
+	}
+
+void CImImap4Session::StopIdle(TRequestStatus& aRequestStatus)
+	{
+        DBG((LogText(_L8("CImImap4Session::StopIdle()"))));
+	TInt err(KErrNone);
+	if (!Connected())
+		{
+		Queue(aRequestStatus);
+		err=KErrDisconnected;
+		}
+	else
+		{
+		// stop idle is only called by compound command
+		// so flag this 
+		iCompoundStopIdle = ETrue;
+		TRAP(err,StopIdleL(aRequestStatus));
+		}
+	if (err!=KErrNone)
+		{
+                DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::StopIdle(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		Complete(err);
+		}
+	}
+
+void CImImap4Session::SyncStopIdleL(TRequestStatus& aRequestStatus)
+	{
+	DBG(LogText(_L8("CImImap4Session::SyncStopIdleL (state=%d)"), iState));
+	iStoppingIdleForSync = ETrue;
+	StopIdleL(aRequestStatus);
+	}
+
+void CImImap4Session::StopIdleL(TRequestStatus& aRequestStatus)
+	{
+        DBG((LogText(_L8("CImImap4Session::StopIdleL()"))));
+	Cancel();
+	Queue(aRequestStatus);
+	DoStopIdleL();
+	}
+
+void CImImap4Session::DoStopIdleL()
+	{
+	DBG(LogText(_L8("CImImap4Session::DoStopIdleL(state=%d)"),iState)) ;
+	__ASSERT_DEBUG(ImapIdleSupported(), gPanic(EBadUseOfImap4Op));
+	if(!ImapIdleSupported())
+		{
+		User::LeaveIfError(KErrGeneral);//Bad Use of Imap4Op
+		}
+	__ASSERT_DEBUG(IsIdling(), gPanic(EInvalidStatus));
+	if(!IsIdling())
+		{
+		User::LeaveIfError(KErrGeneral); // Invalid Status
+		}
+
+	iIdleTimer->Cancel();
+	iState=EImapStateStopIdleWait;
+
+	SendUntaggedMessageWithTimeoutL(KIMAPC_DONE, KImapDoneInactivityTimeSeconds);
+	}
+
+// Process output from search command
+// V2 version could alter significantly and become #ifdef-unreadable
+void CImImap4Session::ProcessSearchL(CImapAtom *aAtom)
+	{
+	// Process the reply from a uid search command into an fixed array of uids.
+	// The data is recieved as a series of atoms containing numerical only data.
+	// If non-numerical data is recieved, then this function will leave.
+	// Use the key to do inserts in sequence. Takes care of duplicates
+	TKeyArrayFix key(0,ECmpTInt32);
+	TUint atomUid;
+	while (aAtom)
+		{
+		if (aAtom->Atom().Length()>0)
+			{
+			// Check for numerical value.
+			TChar atomChar(aAtom->Atom()[0]);
+			if (atomChar.IsDigit() && aAtom->Value(atomUid)==KErrNone)
+				{
+				// Append it to the end search UID list
+				// Put in sequence no duplicates
+				TRAPD(err,iSearchList->InsertIsqL(static_cast<TUint32>(atomUid),key));
+				if(err != KErrNone && err != KErrAlreadyExists)
+					{
+					User::Leave(err);
+					}
+				}
+			else
+				// Not a number or cant get its value.
+				User::Leave(KErrArgument);
+			}
+		else
+			{
+			// Null atom.
+			User::Leave(KErrArgument);
+			}
+		aAtom=aAtom->Next();
+		}
+
+	DBG((LogText(_L8("UID search found %d UIDs in remote folder"),iSearchList->Count())));
+	}
+
+// A tag has completed OK
+void CImImap4Session::CommandCompleteL(TInt aResult)
+	{
+	DBG((LogText(_L8("CImImap4Session::CommandComplete(state=%d, result=%d)"),iState,aResult)));
+	iCommandFailure=aResult;
+	
+	// Data has been received from the remote server - can cancel the cancel-timer,
+	// cancel wasn't cos of hanging due to GPRS suspend.
+	iCancelTimer->Cancel();
+
+	switch(iState)
+		{
+	case EImapStateCapabilityWait:
+		// Did we see the correct version ID?
+		if (!iSeenVersion)
+			{
+			// No - not in any of the lines between issuing the capability
+			// command and the completion of the command
+			Fail(KErrImapServerVersion);
+			return;
+			}
+
+		// Move into whatever logon state is required
+		iState=iSavedState;
+
+		// Need to login?
+		if (iState==EImapStateLoginWait)
+			{
+			switch (iSecurityState)
+				{
+			case ESecure:
+			case EUnsecure:
+				// shouldn't ever get here...
+				SendLoginL();
+				break;
+
+			case ENegotiating:
+				SendLoginL();
+				iSecurityState=ESecure;
+				break;
+
+			case EUnknown:
+				if (iServiceSettings->SecureSockets())
+					{
+					if (iCapabilityStartTLS)
+						{
+						// send StartTLS
+						NewTag();
+						iImapIO->SetTLSResponseL();
+						iImapIO->SendL(iStatus,_L8("%d STARTTLS\r\n"),iTag);
+						NewTagSent();
+						iState=EImapStateStartTLSWait;
+						iSendQueued=EFalse; // no need to queue 'send' cause session will take care of the response.
+						}
+					else
+						Fail(KErrImapServerNoSecurity);
+					}
+				else
+					{
+					if (iCapabilityLoginDisabled)
+						Fail(KErrImapServerLoginDisabled);
+					else
+						{
+						SendLoginL();
+						iSecurityState=EUnsecure;
+						}
+					}
+				break;
+				}
+
+			return;
+			}
+		break;
+
+	case EImapStateStartTLSWait:
+		SendCapabilityL();
+		iSecurityState = ENegotiating;
+		return;
+	
+	case EImapStateSelectWait:
+		switch(aResult)
+			{
+		case KErrNone:
+			// Select OK
+			DBG((LogText(_L8("CImImap4Session::CommandCompleteL(): setting iState to EImapStateSelected"))));
+			iState=EImapStateSelected;
+			if( iCancelAndIdle )
+				{
+				if( iReissueIdle )
+					{
+					// Need to re-issue the IDLE command - probably due to a cancel
+					// during a populate commmand and session was IDLE before the
+					// command.
+					// As the populate could have been for a message not in the Inbox,
+					// (and this is the completion of SELECT command for appropriate
+					// mailbox) need to ensure IDLE started in inbox.
+
+					// Do the select (if we really need to) or skip if possible.
+					if( iMailboxId==GetInbox() && iMailboxWritable )
+						{
+						DBG((LogText(_L8("Need to re-issue IDLE command"))));
+
+						// No need to do the select - so re-issue the IDLE
+						DoStartIdleL();				
+						}
+					else
+						{
+						DBG((LogText(_L8("Need to issue IDLE - first select Inbox (writable)"))));
+
+						// Looks like we need to do the select...
+						DoSelectL(GetInbox(), ETrue);
+						}
+					}
+				else
+					{
+					DBG((LogText(_L8("Do not re-issue IDLE - cancel completed"))));
+					
+					// Cancelling completed - stay in this mailbox and do not issue idle.
+					iCancelAndIdle = EFalse;
+					}
+				}
+			break;
+
+		case KErrIMAPNO:
+			DBG((LogText(_L8("CImap4Session::CommandComplete(): iState=EImapStateSelectWait"))));
+			DBG((LogText(_L8("-----------------------------------------------------------"))));
+			DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()"))));
+			DBG((LogText(_L8("-----------------------------------------------------------"))));
+			// Select failed
+			Complete(KErrIMAPNO);
+			return;
+			}
+		break;
+
+	case EImapStateSeparatorWait:
+	case EImapStateCloseWait:
+		// Back to unselected
+		iState=EImapStateNoSelect;
+		break;
+
+	case EImapStateCommandWait:
+	case EImapStateListWait:
+	case EImapStateLsubWait:
+	case EImapStateAppendResultWait:
+		// Restore old state: could have been selected/unselected
+		iState=iSavedState;
+		break;
+		
+	case EImapStateFetchCancelWait:
+		// No more parts to fetch - reset the fetch list
+		iFetchList->Reset();
+
+		// Dispose of any buffers we've got left
+		delete iPartialLine;
+		iPartialLine=NULL;
+		delete iAttachmentFile;
+		iAttachmentFile=NULL;
+		delete iAttachmentFullPath;
+		iAttachmentFullPath=NULL;
+		delete iAttachmentMimeInfo;
+		iAttachmentMimeInfo=NULL;
+		
+		// Ensure attachment file state correct so that any re-fetch will be ok
+		iAttachmentFileState=EFileNotOpen;
+
+		// All done - ensure any changes are committed to disk
+		iEntry->CompleteBulk();
+
+		// Back in selected state - do we need to move back to inbox?
+		iState=EImapStateSelected;
+		iSyncState=ENotSyncing;
+		if( iCancelAndIdle )
+			{
+			if( iReissueIdle )
+				{
+				// Need to re-issue the IDLE command - probably due to a cancel
+				// during a populate commmand and session was IDLE before the
+				// command.
+				// As the populate could have been for a message not in the Inbox,
+				// (and this is the completion of SELECT command for appropriate
+				// mailbox) need to ensure IDLE started in inbox.
+
+				// Do the select (if we really need to) or skip if possible.
+				if( iMailboxId==GetInbox() && iMailboxWritable )
+					{
+					DBG((LogText(_L8("Need to re-issue IDLE command"))));
+
+					// No need to do the select - so re-issue the IDLE
+					DoStartIdleL();				
+					}
+				else
+					{
+					DBG((LogText(_L8("Need to issue IDLE - first select Inbox (writable)"))));
+
+					// Looks like we need to do the select...
+					DoSelectL(GetInbox(), ETrue);
+					}
+				}
+			else
+				{
+				DBG((LogText(_L8("Do not re-issue IDLE - cancel completed"))));
+				
+				// Cancelling completed - stay in this mailbox and do not issue idle.
+				iCancelAndIdle = EFalse;
+				}
+			}
+		break;
+
+	case EImapStateFetchWait:
+		// Fetch has completed: any more parts to go?
+		iProgress.iPartsDone++;
+
+		if (iFetchList->Count())
+			{
+			// Yes, fetch next one
+			FetchAnItemL((*iFetchList)[0]);
+			iFetchList->Delete(0,1);
+			return;
+			}
+
+		// Dispose of any buffers we've got left
+		delete iPartialLine;
+		iPartialLine=NULL;
+		delete iAttachmentFile;
+		iAttachmentFile=NULL;
+		delete iAttachmentFullPath;
+		iAttachmentFullPath=NULL;
+		delete iAttachmentMimeInfo;
+		iAttachmentMimeInfo=NULL;
+		// All done - ensure any changes are committed to disk
+		iEntry->CompleteBulk();
+		iState=EImapStateSelected;
+		iSyncState=ENotSyncing;
+		break;
+
+	case EImapStateLoginWait:
+		switch(aResult)
+			{
+		case KErrNone:
+			// Login OK: do we know the separator character?
+			if (!iHierarchySeparator.Length())
+				{
+				// No, ask server for it
+				NewTag();
+				iImapIO->SendL(iStatus,_L8("%d LIST \"\" \"\"\r\n"),iTag);
+				NewTagSent();
+				iState=EImapStateSeparatorWait;
+				return;
+				}
+				
+			iState=EImapStateNoSelect;
+			break;
+
+		case KErrIMAPNO:
+		case KErrImapBadLogon:
+			// Bad username/password
+			Fail(KErrImapBadLogon);
+			return;
+			}
+		break;
+	case EImapStateSelected:
+		{
+		// reset flag after stop idle completes
+		iCompoundStopIdle = EFalse;
+		iStoppingIdleForSync = EFalse;
+		}
+	case EImapStateCreateWait:
+	case EImapStateRenameWait:
+	case EImapStateDeleteWait:
+	case EImapStateSubscribeWait:
+		// Modification operation has happened: did it go OK?
+		if (aResult==KErrNone)
+			{
+			TMsvEmailEntry message;
+			SetEntryL(iCommandIds[0]);
+
+			switch(iState)
+				{
+			case EImapStateCreateWait:
+				{
+				// We need to create the actual folder, as it now exists on the server
+				message.iType=KUidMsvFolderEntry;
+				message.iMtm=KUidMsgTypeIMAP4;
+				message.iServiceId=iServiceId;
+				message.SetMtmData1(0);
+				message.SetMtmData2(0);
+				message.SetMtmData3(0);
+				message.SetValidUID(EFalse);
+				message.SetMailbox(ETrue); // Default to creating a mailbox
+				message.SetComplete(ETrue);
+				message.iSize=0;
+				message.iDetails.Set(iCommandBuf);
+				iEntry->CreateEntry(message);
+				// Save the created id in the progress buffer
+				iProgress.iReturnedMsvId=message.Id();
+				break;
+				}
+
+			case EImapStateRenameWait:
+				{
+				// Modify the entry
+				message=iEntry->Entry();
+				message.iDetails.Set(iCommandBuf);
+				ChangeEntryL(message);
+				break;
+				}
+
+			case EImapStateDeleteWait:
+				// Delete the entry: we are set to the parent currently
+				iEntry->DeleteEntry(iCommandIds[1]);
+				break;
+
+			case EImapStateSubscribeWait:
+				// Set/reset the subscription flag
+				message=iEntry->Entry();
+				message.SetSubscribed(iCommandFlags[0]);
+				ChangeEntryL(message);
+				break;
+
+			default: // To stop AER warnings
+				break;
+				}
+
+			// Back to previous state
+			iState=iSavedState;
+			}
+		DBG((LogText(_L8("CImap4Session::CommandComplete(): iState=EImapStateSubscribeWait"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		Complete(aResult);
+		return;
+
+	case EImapStateDeleteAllWait:
+		// Expunge the folder by closing
+		NewTag();
+		iImapIO->SendL(iStatus,_L8("%d CLOSE\r\n"),iTag);
+		NewTagSent();
+		iState=EImapStateDeleteFolderWait;
+		return;
+
+	case EImapStateDeleteFolderWait:
+		{
+		// Delete all children in folder locally as they have all been expunged
+		SetEntryL(iMailboxId);
+		GetChildrenL(*iSelection);
+		TInt a=0;
+		while(a<iSelection->Count())
+			iEntry->DeleteEntry((*iSelection)[a++]);
+		iSelection->Reset();
+
+		// Back to unselected as we've closed the folder
+		iState=EImapStateNoSelect;
+		break;
+		}
+
+	case EImapStateSynchroniseWait:
+		{
+		if (aResult==KErrNone && iSyncState==ESyncSearch)
+			{
+			iFolderPosition+=KImapUidSearchSize;
+			// Check to see if we have all the messages.
+			if (iFolderPosition>=iMailboxSize)
+				{
+				DBG((LogText(_L8("UID search complete"))));
+
+				// Set the mailbox size to the number of UIDs returned by the search
+				iMailboxSize = iSearchList->Count();
+
+				iSyncState=ESyncListOld;
+				// We have the full list of remote UIDs - fall through.
+				}
+			else
+				{
+				// Should be able to hit this code if KImapUidSearchSize is reduced to < the size
+				// of the remote mailbox (iMailboxSize)
+				// SearchString non 0 means a refined UID SEARCH is required
+				DBG((LogText(_L8("UID search - get next manageable block of UIDs"))));
+				NewTag();
+				if(iServiceSettings->SearchString().Length() != 0)
+					{
+					// Refined search required
+					// Still uses the indexes but appends user specified search criteria
+					_LIT8(KSearchString,"%d UID SEARCH %d:%d %S\r\n");
+					TPtrC8 ptr = iServiceSettings->SearchString();
+					iImapIO->SendL(iStatus,KSearchString,iTag,iFolderPosition+1,Min(iMailboxSize,iFolderPosition+KImapUidSearchSize),&ptr);
+					}
+				else
+					// Normal unrefined SEARCH. Will pull back all the UIDs between indexes
+					{
+					_LIT8(KSearchString,"%d UID SEARCH %d:%d\r\n");
+					iImapIO->SendL(iStatus,KSearchString,iTag,iFolderPosition+1,Min(iMailboxSize,iFolderPosition+KImapUidSearchSize));
+					}
+				NewTagSent();
+				return;
+				}
+			}
+
+		if (aResult==KErrNone && iSyncState==ESyncListOld)
+			{
+			// At this point the remote command to search out folder UIDs has completed. This
+			// list will contain all UIDs for messages in the remote folder. This may be too
+			// many, and if so, the list is truncated to only include the N most recent 
+			// messages in accordance with the synchronisation limit.
+
+			TInt local=0;
+			TInt remote=0;
+			// if no UID Search string defined then the old logic is used
+			TInt syncThresh = 0;
+			if(iServiceSettings->SearchString().Length() != 0)
+				{
+				syncThresh = 0;
+				}
+			else	// If no search string is set we will use the old behaviour
+				{
+				syncThresh=(iSyncLimit<iMailboxSize)?iMailboxSize-iSyncLimit:0;	
+				}
+			TBool orphanThis=EFalse;
+			TBool folderPositionFound=EFalse;
+			iFolderPosition=0;
+
+			// At this stage (prior to truncation), we have a full list of all the message
+			// UIDs in the remote folder. We also have a list of all UIDs for messages in
+			// the local folder. With these lists, we can establish which local messages
+			// have been orphaned and which have not using the following rules.
+
+			// * If the remote message is no longer there:
+			//		(1) Then the local message is orphaned (always).
+			// * If the remote message is still there and it is not one of the N most recent:
+			//		(2) If the local message has body parts do nothing.
+			//		(3) If the local message does not have body parts, then it is orphaned
+			//		    unless is is a message we have selected for download.
+			// * If the remote message is still there and it is one of the N most recent:
+			//		(4) Do nothing.
+
+			// Search through the local folder list while checking the remote folder list.
+			while (local<iFolderIndex.Size() && iFolderIndex[local].iMsvId!=-1)
+				{
+				orphanThis=EFalse;	// Default orphaning of this message to false.
+
+				// Find next uid in remote folder uid list that is >= local entry uid.
+				while (remote<iMailboxSize)
+					{
+					if (remote < iSearchList->Count() &&
+					    (*iSearchList)[remote]>=iFolderIndex[local].iUid)
+						break;
+					remote++;
+					}
+
+				// Within scope of search list?
+				if (remote<iMailboxSize)
+					{
+					TBool inSyncRange=(remote>=syncThresh);
+					TBool uidMatch=((*iSearchList)[remote]==iFolderIndex[local].iUid);
+					TBool uidNewer=((*iSearchList)[remote]>iFolderIndex[local].iUid);
+
+					// Folder position must point to 1st old local message that matches a
+					// remote message that will be sync'ed.
+					if (uidMatch && inSyncRange && !folderPositionFound)
+						{
+						iFolderPosition=local;
+						folderPositionFound=ETrue;
+						}
+
+					if (uidNewer)
+						{
+						// Here the next remote uid is greater than the local uid indicating that
+						// a message has been removed on the remote folder and that this local
+						// message should be orphaned. See case (1) above.
+						
+						DBG((LogText(_L8("Message no longer available on remote server, orphaning"))));
+						orphanThis=ETrue;
+						}
+					else if (uidMatch && !inSyncRange)
+						{
+						// Here the remote uid matches the local uid, but the message falls outside
+						// of the N most recent messages. See cases (2) & (3).
+
+						DBG((LogText(_L8("Local message old (%u)"),iFolderIndex[local].iUid)));
+
+						SetEntryL(iFolderIndex[local].iMsvId);
+						TMsvEmailEntry message(iEntry->Entry());
+						TBool inSyncSelection = EFalse;
+
+						// Is the message part of the synchronisation selection?
+						// If so, we will want to view the message after the sync so don't delete.
+						if (iSynchronisationSelection && iSynchronisationSelection->Count() > 1)
+							{
+							if (iSynchronisationSelection->Find(iFolderIndex[local].iMsvId) != KErrNotFound)
+								{
+								inSyncSelection = ETrue;
+								}
+							}
+
+						// Does message have any downloaded parts?
+						if (!message.Complete() && 
+							!message.BodyTextComplete() && 
+							!inSyncSelection)
+							{
+							// The local message does not have any body parts and
+							// is not selected for download, so it is orphaned.
+							// See case (3) above.
+
+							DBG((LogText(_L8("Local message (%u) is only header and not selected for download, deleting"),iFolderIndex[local].iUid)));
+							orphanThis=ETrue;
+							}
+						}
+					}
+				else
+					{
+					// Outside of scope of search list, so none of the remaining local
+					// messages are present in the remote folder and therefore are orphaned.
+					// See case (1) above.
+
+					DBG((LogText(_L8("Message no longer available on remote server, orphaning"))));
+					orphanThis=ETrue;
+					}
+
+				// Orphan this one?
+				if (orphanThis)
+					{
+					OrphanMessageL(iFolderIndex[local].iMsvId);
+					iFolderIndex.Expunge(local+1);
+					iOrphanedMessages++;
+					}
+				else
+					{
+					// If we have arrive here, then the local message is one of the N most 
+					// recent and still exists remotely OR it exists remotely, is not one of
+					// the N most recent and the local message had body parts.
+					// See cases (2) & (4) above.
+
+					local++;
+					}
+				} // End of the big while()
+
+			// Trim the list down to the most recent UIDs consistant with the sync limit.
+			if (syncThresh && iSearchList->Count() > syncThresh)
+				iSearchList->Delete(0,syncThresh);
+
+			// So now "iFolderIndex" will only have messages that are in the remote folder.
+			// And therefore, the highest UID stored in "iFolderIndex" will be the most 
+			// recent old message. Also, the lowest UID in "iSearchList" will be the oldest
+			// sync'able remote message.
+
+			// Are there any old messages left?
+			if (iFolderIndex.Size() && folderPositionFound)
+				{
+				DBG((LogText(_L8("Updating flags for %d old messages (UIDs %u to %u)"),
+						iFolderIndex.Size(),(*iSearchList)[0],iHighestUid)));
+
+				// Re-assign highest UID, the previous highest may no longer exist.
+				iHighestUid=iFolderIndex[iFolderIndex.Size()-1].iUid;
+				iSyncState=ESyncListNew;
+
+				// Fetch old messages.
+				NewTag();
+				// If a UID search string has been specified, the we should create the UID FETCH
+				// string from the UID integer list otherwise they'll all come down.
+				if(iServiceSettings->SearchString().Length() != 0)
+					{
+					CreateUidStringL(); // Construct the UID string from the UID list
+					TPtrC8 ptr(iUidString->Des());
+					_LIT8(KFetchString,"%d UID FETCH %S (UID FLAGS)\r\n");
+					iImapIO->SendL(iStatus,KFetchString,iTag,&ptr);
+					}
+				else
+					{
+					_LIT8(KFetchString,"%d UID FETCH %d:%d (UID FLAGS)\r\n");
+					iImapIO->SendL(iStatus,KFetchString,iTag,(*iSearchList)[0],iHighestUid);
+					}
+				NewTagSent();
+				return;
+				}
+			else
+				{
+				DBG((LogText(_L8("No old message headers to update"))));
+
+				iSyncState=ESyncListNew;
+				iHighestUid=0;
+				// All remote messages are new - fall through.
+				}
+			}
+
+		if (aResult==KErrNone && iSyncState==ESyncListNew)
+			{
+			// At this point, the remote command to fetch all old messages has completed. 
+			// Now we can look at fetching all new messages. 'iHighestUid' will contain the
+			// highest UID of the old messages. The top entry in 'iSearchList' will contain
+			// the highest UID in the remote folder. This gives us the range of UID to fetch
+			// for new messages.
+
+			// First check are there any new messages to fetch? If 'iHighestUid' is the highest
+			// UID locally and remotely, then we finished sync'ing when we completed the old
+			// sync.
+			if (iSearchList->Count() == 0)
+				{
+				DBG((LogText(_L8("Search List is empty"))));				
+				}
+			else if (iHighestUid<(*iSearchList)[iSearchList->Count()-1])
+				{
+				TUint32 uidLow=iHighestUid;
+				TUint32 uidHigh=(*iSearchList)[iSearchList->Count()-1];
+
+				// Only want new messages.
+				uidLow++;
+
+				// Are there only new messages (and no old)?
+				if (iHighestUid==0)
+					{
+					// Set this to ensure range is correct.
+					uidLow=(*iSearchList)[0];
+					}
+				
+				// Perform the new sync.
+				SynchroniseNewL(uidLow,uidHigh);
+				return;
+				}
+			else
+				{
+				DBG((LogText(_L8("No new message headers to sync"))));
+
+				// Synchronisation complete - fall through.
+				}
+			iSyncState=ESyncNew;
+			}
+
+		// Synchronising: moving on to the next state?
+		if (aResult==KErrNone && iSyncState==ESyncOld)
+			{
+			// At this point, the remote command to list old messages has
+			// completed: however, if there was a 'gap' at the end (ie messages at
+			// the end of our copy of the mailbox had been deleted) we won't have
+			// noticed it until this point as we woulnd't have got back in sync which
+			// happens when we get a UID which isn't sequential.
+			// So, as we now know there are no more messages in the range we know about
+			// locally, anything else must have been deleted remotely.
+			while(iFolderPosition<iFolderIndex.Size())
+				{
+				// Orphan this message
+				DBG((LogText(_L8("Orphaning UID %u"),iFolderIndex[iFolderPosition].iUid)));
+
+				// Do it
+				OrphanMessageL(iFolderIndex[iFolderPosition].iMsvId);
+
+				// Remove it from the index
+				iFolderIndex.Expunge(iFolderPosition+1);
+
+				// Increment stats
+				iOrphanedMessages++;
+				}
+
+			// Anything to new sync? If we've processed all the messages up to
+			// iMailboxSize, then there's nothing left that might possibly be
+			// new
+			if (iMsgsDone<iMailboxSize)
+				{
+				// Do new sync
+				SynchroniseNewL();
+				return;
+				}
+
+			iSyncState=ESyncNew;
+			}
+
+		if (aResult==KErrNone && iSyncState==ESyncNew)
+			{
+			// If there were any "missing" messages found during the sync that we should have
+			// mirrored previously, get these now.
+			if (iMissingUidLow!=0 && iMissingUidHigh!=0)
+				{
+				DBG((LogText(_L8("Missing messages detected %d - %d"),iMissingUidLow,iMissingUidHigh)));
+
+				SynchroniseNewL(iMissingUidLow,iMissingUidHigh);
+				iMissingUidLow=iMissingUidHigh=0;
+				return;
+				}
+			}
+
+		// If we got a NO response in the course of the sync, we've not fully sync'ed the folder,
+		// so we shouldn't update the last sync date, etc. BUT as NO isn't a fatal response, we should
+		// not disconnect.
+		if (aResult!=KErrIMAPNO)
+			{
+			// Are we configured to update the \seen flag on the server?
+			if (iServiceSettings->UpdatingSeenFlags())
+				{
+				DBG((LogText(_L8("Sync completed: updating servers flags"))));
+
+				// Now drop through to EImapStateSetSeenWait case
+				iState = EImapStateSetSeenWait;
+				iSyncState=ENotSyncing;
+				}
+			else
+				{
+				// Not updating servers \seen flags - use old code to continue after sync
+				// Back to selected state
+				iState=EImapStateSelected;
+				iSyncState=ENotSyncing;
+
+				// We've synchronised the folder. Update iDate field to
+				// indicate last sync date of this folder.
+				// This also updates the 'folders done' count
+				SyncCompleteL();
+                                DBG((LogText(_L8("CImap4Session::CommandComplete(): iState=EImapStateSynchroniseWait"))));
+				DBG((LogText(_L8("-----------------------------------------------------------"))));
+				DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()"))));
+				DBG((LogText(_L8("-----------------------------------------------------------"))));
+				Complete(aResult);		
+				return;
+				}
+			}
+		else
+			{
+			// NO isn't a fatal response.Ignore it,continue...
+			iState=EImapStateSelected;
+			iSyncState=ENotSyncing;
+            DBG((LogText(_L8("CImap4Session::CommandComplete(): iState=EImapStateSynchroniseWait, aResult == KErrIMAPNO"))));
+			DBG((LogText(_L8("-----------------------------------------------------------"))));
+			DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()"))));
+			DBG((LogText(_L8("-----------------------------------------------------------"))));
+			Complete(aResult);
+			return;
+			}
+		}
+
+	case EImapStateSetSeenWait:    // Only a valid state when iServiceSettings->UpdatingSeenFlags() is true
+		// We may not be able to set all the seen flags due to a large list, 
+		//  so call ProcessSeenFlagsL until False is returned
+		if (ProcessSeenFlagsL(ESetSeenFlag))
+			{
+			// More have been processed - wait for response in same state.
+			return;
+			}
+		// Now drop through to EImapStateClearSeenWait state
+		iState=EImapStateClearSeenWait;
+		iSyncState=ENotSyncing;
+
+	case EImapStateClearSeenWait:  // Only a valid state when iServiceSettings->UpdatingSeenFlags() is true
+		// We may not be able to clear all the seen flags due to a large list, 
+		//  so call ProcessSeenFlagsL until False is returned
+		if (ProcessSeenFlagsL(EClearSeenFlag))
+			{
+			// More have been processed - wait for response in same state.
+			return;
+			}
+
+		// All seen flags processed - return to selected state
+		// Will only arrive here if iServiceSettings->UpdatingSeenFlags() is true
+		iState=EImapStateSelected;
+		iSyncState=ENotSyncing;
+
+		// We've synchronised the folder. Update iDate field to
+		// indicate last sync date of this folder.
+		// This also updates the 'folders done' count
+		SyncCompleteL();
+		DBG((LogText(_L8("CImap4Session::CommandComplete(): iState=EImapStateClearSeenWait"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		Complete(aResult);
+		return;
+
+	case EImapStateLogoutWait:
+		DoDisconnect();
+		break;
+
+	case EImapStateDeleteMarkWait:
+		// Messages marked for deletion OK, send close command
+		SendMessageL(KIMAPC_CLOSE);
+		iState=EImapStateExpungeWait;
+		return;
+
+	case EImapStateExpungeWait:
+		{
+		DBG((LogText(_L8("Close completed OK: expunging messages locally"))));
+
+		// Expunge the messages locally, the close was successful
+		for(TInt a=0;a<iFolderIndex.Size();a++)
+			{
+			// If it's got a valid MsvId, delete it!
+			if (iFolderIndex[a].iMsvId)
+				{
+				DBG((LogText(_L8("Expunging message %x"),iFolderIndex[a].iMsvId)));
+
+				DeleteMessageL(iFolderIndex[a].iMsvId);
+				}
+			}
+
+		// Back to unselected state (close sucessful)
+		iState=EImapStateNoSelect;
+		break;
+		}
+
+	case EImapStateIdleWait:
+		{
+		DBG((LogText(_L8("CImImap4Session::CommandCompleteL(): iState = EImapStateIdleWait"))));
+ 		if (iCompoundStopIdle) 
+			{
+			// the reply from server is missed if
+			// compound command
+			GetReply(EFalse);
+			}
+		break;		
+		}
+
+	case EImapStateIdling:
+		{
+		DBG((LogText(_L8("CImImap4Session::CommandCompleteL(): iState = EImapStateIdling"))));
+ 		if (iReissueIdle)
+			{
+			// IDLE command has been re-issued after synchronise OR a re-issue
+			// after a cancel-and-idle (if idling before).
+			// NOTE - also unset cancel-and-idle flag here too.
+			iReissueIdle = EFalse;
+			iCancelAndIdle	= EFalse;
+			IssueIdleRead();
+			}
+		break;		
+		}
+
+	case EImapStateStopIdleWait:
+		{
+		DBG((LogText(_L8("Idling completed OK"))));
+		
+		iState=EImapStateSelected;
+
+		if (FolderChanged())
+			{
+			DBG((LogText(_L8("CImImap4Session::CommandCompleteL(): FolderChanged"))));
+
+			// If the folder has changed then we are going to do a synchronise to
+			// ensure we have up to date message information. At the end of the
+			// synchronise we would normally reissue the idle command, however
+			// we don't want to do this if we are stopping idle due to a
+			// compound command or a sync command. The compound / sync active
+			// objects will reissue the idle themselves.
+			if(!iCompoundStopIdle && !iStoppingIdleForSync)
+				{
+				DBG((LogText(_L8("CImImap4Session::CommandComplete(): ReissueIdle = true"))));
+				iReissueIdle=ETrue;
+				}
+			DoSynchroniseL(EFalse);
+			return;
+			}
+		else if (iReissueIdle)
+			{
+			if( !iCancelAndIdle && !iIdleTimerExpired )
+				{
+				// Don't unset the iReissueIdle flag - need it to be true to ensure
+				// that IssueIdleRead is issued once 'Done' is received.
+				// FYI - IssueIdleRead is normally called in the DoComplete method 
+				// when the StartIdle request completes. If we've cancel-and-idle-ed,
+				// then DoComplete won't be called when we do actually go idle.
+				DBG((LogText(_L8("CImImap4Session::CommandComplete(): ReissueIdle = false"))));
+				iReissueIdle = EFalse;
+				}
+			iIdleTimerExpired = EFalse;
+			DoStartIdleL();
+			return;
+			}
+		else if (iDisconnectAfterIdleStopped)
+			{
+			iDisconnectAfterIdleStopped=EFalse; 
+			DoDisconnectL();
+			return;
+			}
+
+		break;		
+		}
+		
+	default:
+		gPanic(ECommandCompleteInUnknownState);
+		return;
+		}
+
+	// Completing an operation upwards: ensure iEntry is not pointing to anything
+	SetEntryL(NULL);
+        
+        DBG((LogText(_L8("CImap4Session::CommandComplete(): End of method"))));
+	DBG((LogText(_L8("-----------------------------------------------------------"))));
+	DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()"))));
+	DBG((LogText(_L8("-----------------------------------------------------------"))));
+	
+	Complete(KErrNone);
+	}
+	
+	
+/* 
+Overrides CMsgActive::RunL() to allow check for errored completion of
+autonomously initiated IO requests, specifically the reissue of IDLE
+commands on idle timeout.
+*/
+void CImImap4Session::RunL()
+	{
+	TInt status=iStatus.Int();
+	if (status < KErrNone &&
+		iIdleTimerExpired && 
+		(iState==EImapStateIdleWait || iState==EImapStateStopIdleWait))
+		{
+		DoComplete(status);
+		}
+	else
+		{
+		// Behave exactly as CMsgActive::RunL
+		if (status>=KErrNone)
+			{
+			TRAPD(error,DoRunL());      // continue operations, may re-queue
+			__ASSERT_DEBUG(error==KErrNone || !IsActive(),User::Invariant());   // must not requeue in error situations
+			if (IsActive())             // requeud
+				return;
+			status=error;
+			}
+		Complete(status);
+		}
+	}
+
+	
+
+// A child async process has completed
+void CImImap4Session::DoRunL()
+	{
+	DBG((LogText(_L8("CImImap4Session::DoRunL(status=%d, state=%d)"),iStatus.Int(),iState)));
+
+	// Did we have a send queued? If so, this is just the send completion: we
+	// now queue a receive and clear the send flag
+	if (iSendQueued)
+		{
+                DBG((LogText(_L8("CImImap4Session::DoRunL(): iSendQueued"))));
+
+		// No send queued anymore...
+		iSendQueued=EFalse;
+		// Any problems sending?
+		if(iStatus.Int()!=KErrNone)
+			{
+			// Yes, humm...
+			DBG((LogText(_L8("Error during send %d"),iStatus.Int())));
+
+			// We have to Fail() here, as the connection might be screwed:
+			// we just don't know. Fail() will disconnect us and reset our
+			// internal state.
+			Fail(KErrImapSendFail);
+			}
+		else
+			{
+			// Have we just issued a partial fetch, ie waiting for body
+			// size?
+			if (iSizeWait)
+				{
+				// Queue partial line fetch
+				GetReply(ETrue);
+				}
+			else
+				{
+				// Queue full line read
+				GetReply(EFalse);
+				}
+			}	
+		return;
+		}
+
+	// Was a receive queued? If so, we need to get the new root pointer
+	if (iReceiveQueued)
+		{
+                DBG((LogText(_L8("CImImap4Session::DoRunL(): iReceiveQueued"))));
+ 		// Not anymore
+		iReceiveQueued=EFalse;
+
+		// Get root
+		iRootAtom=iImapIO->RootAtom();
+                 
+                DBG((LogText(_L8("CImImap4Session::DoRunL(): Got root"))));
+
+		// Did we get the whole line?
+		if (iStatus.Int()==KErrFoundEOL)
+			{
+                        DBG((LogText(_L8("CImImap4Session::DoRunL(): got the whole line"))));
+			iGotWholeLine=ETrue;
+			iStatus=KErrNone;
+			}
+		else
+                        {
+			DBG((LogText(_L8("CImImap4Session::DoRunL(): didn't got the whole line"))));			
+			iGotWholeLine=EFalse;
+                        }
+		}
+
+	// Problems with connection?
+	if (iStatus.Int()!=KErrNone)
+		{
+		DBG((LogText(_L8("CImImap4Session::DoRunL(): Problems with connection"))));
+                if (iState==EImapStateConnectWait)
+			Fail(KErrImapConnectFail);
+		else
+			Fail(KErrImapServerFail);
+
+		return;
+		}
+        
+        DBG((LogText(_L8("CImImap4Session::DoRunL(): checking iState"))));
+
+	switch(iState)
+		{
+	// Waiting for connect response
+	case EImapStateConnectWait:
+		{
+		// Get the bearer idle timeout
+		TUint32 timeout;
+		User::LeaveIfError(iImapIO->GetLastSocketActivityTimeout(timeout));
+
+		// Sets timeout to iMtmData1. This is used by Imcm.
+		SetEntryL(iServiceId);
+		TMsvEntry entry = iEntry->Entry();
+		entry.SetMtmData1(timeout);
+		iEntry->ChangeEntry(entry);
+		DBG((LogText(_L8("IdleTimeout %d"),timeout)));
+
+		// Connected, queue receive for greeting line
+		iState=EImapStateGreetingWait;
+		GetReply(ETrue);
+		break;
+		}
+
+	// Waiting for greeting line
+	case EImapStateGreetingWait:
+		{
+		TInt result=KErrNone; // To stop .AER warnings...
+
+		// Process line
+		TRAPD(err,result=ProcessGreetingL());
+		if (err!=KErrNone)
+			Fail(KErrImapServerFail);
+		else if (result!=KErrNone)
+			{
+			// Greeting process returned an error
+			DBG((LogText(_L8("CImap4Session::CommandComplete(): iState=EImapStateGreetingWait"))));
+			DBG((LogText(_L8("-----------------------------------------------------------"))));
+			DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()"))));
+			DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+                        Complete(result);
+			}
+		else
+			{
+			SendCapabilityL();
+			}
+		break;
+		}
+
+	// Waiting for continuation response to send username
+	case EImapStateLoginSendUser:
+		{
+		// Looking for a '+'
+		CImapAtom *p=iRootAtom->ToChildL();
+
+		// '*' indicates an untagged message
+		if (p->Compare(KIMAP_UNTAGGED))
+			{
+			// Process it
+			TRAPD(err,ProcessUntaggedL(p->ToNextL(),EFalse));
+
+			// A problem here is a server fail, nothing else
+			if (err!=KErrNone)
+				Fail(KErrImapServerFail);
+			}
+		else if (p->Compare(KIMAP_CONTINUATION))
+			{
+			// Send username literal
+
+			// Literal password?
+			if (iLiteralPassword)
+				{
+				// Turn off logging before sending
+				iImapIO->PerformLogging(EFalse);
+				iImapIO->SendL(iStatus,_L8("%S {%d}\r\n"),iUsername,iPassword->Length());
+				iImapIO->PerformLogging(ETrue);
+	
+				iState=EImapStateLoginSendPassword;
+				}
+			else
+				{
+				// Turn off logging before sending
+				iImapIO->PerformLogging(EFalse);
+				iImapIO->SendL(iStatus,_L8("%S %S\r\n"),iUsername,iPassword);
+				iImapIO->PerformLogging(ETrue);
+				
+				iState=EImapStateLoginWait;
+				}
+
+			iSendQueued=ETrue;
+                        DBG((LogText(_L8("*******************************************************"))));
+			DBG((LogText(_L8("CImap4Session::DoRunL(): waiting for iImapIO to wake me"))));
+			DBG((LogText(_L8("*******************************************************"))));
+
+			SetActive();
+			}
+		break;
+		}
+
+	// Waiting for continuation response to send password
+	case EImapStateLoginSendPassword:
+		{
+		// Looking for a '+'
+		CImapAtom *p=iRootAtom->ToChildL();
+
+		// '*' indicates an untagged message
+		if (p->Compare(KIMAP_UNTAGGED))
+			{
+			// Process it
+			TRAPD(err,ProcessUntaggedL(p->ToNextL(),EFalse));
+
+			// A problem here is a server fail, nothing else
+			if (err!=KErrNone)
+				Fail(KErrImapServerFail);
+			}
+		else if (p->Compare(KIMAP_CONTINUATION))
+			{
+			// Send password literal
+			iSendQueued=ETrue;
+                        DBG((LogText(_L8("*******************************************************"))));
+			DBG((LogText(_L8("CImap4Session::DoRunL(): waiting for iImapIO to wake me"))));
+			DBG((LogText(_L8("*******************************************************"))));
+
+			SetActive();
+			// Turn off logging before sending
+			iImapIO->PerformLogging(EFalse);
+			iImapIO->SendL(iStatus,_L8("%S\r\n"),iPassword);
+			iImapIO->PerformLogging(ETrue);
+			
+			iState=EImapStateLoginWait;
+			}
+		break;
+		}
+
+	case EImapStateSetSeenWait:		// Wait for setting the send flags response
+	case EImapStateClearSeenWait:   // Wait for clearing the send flags response
+	case EImapStateCapabilityWait:	// Wait for reply to capability command
+	case EImapStateLoginWait:		// Wait for reply to login command
+	case EImapStateCommandWait:		// Generic OK/BAD command issued, wait for response
+	case EImapStateCreateWait:		// Create command issued, wait for response
+	case EImapStateRenameWait:		// Rename command issued, wait for response
+	case EImapStateDeleteWait:		// Delete command issued, wait for response
+	case EImapStateDeleteAllWait:	// DeleteAll command issued, wait for response
+	case EImapStateDeleteFolderWait:// DeleteAll has issued a folder delete, wait for response
+	case EImapStateSeparatorWait:	// Wait for reply to null list command
+	case EImapStateSubscribeWait:	// Wait for subscription command reply
+	case EImapStateSelectWait:		// Wait for all select information
+	case EImapStateSynchroniseWait:	// Wait for synchronise
+	case EImapStateListWait:		// Wait for all list information
+	case EImapStateLsubWait:		// Wait for all lsub information
+	case EImapStateAppendResultWait:// Wait for append result
+	case EImapStateDeleteMarkWait:	// Marking messages for deletion before CLOSE
+	case EImapStateExpungeWait:		// Messages expunged: remove them locally
+	case EImapStateExpungeAllWait:	// Messages expunged from remote, remove them
+	case EImapStateCloseWait:		// Wait for close to finish
+	case EImapStateLogoutWait:		// Wait for logout
+	case EImapStateStartTLSWait:	// Wait for starttls to return
+	case EImapStateIdleWait:
+	case EImapStateIdling:
+	case EImapStateStopIdleWait:
+		{
+		TInt result=KErrNone; // To stop .AER warnings...
+
+		// Process it
+		TRAPD(err,result=ProcessCommandReplyL());
+		if (err!=KErrNone)
+			{
+			// check for TLS errors
+			if (iState == EImapStateCapabilityWait && iSecurityState == ENegotiating)
+				err = KErrImapTLSNegotiateFailed;
+			
+			// We've had a fail during reply processing: pass it on
+			Fail(err);
+			}
+		else
+			{
+			switch(result)
+				{
+			case KErrNotReady:
+				{
+				if (IsIdling() && FolderChanged())
+					{
+                    DBG((LogText(_L8("CImImap4Session::DoRunL(): Calling DoStopIdleL"))));
+					DoStopIdleL();
+					}
+				else if (IsIdling())
+					{
+					IssueIdleRead();
+					return;
+					}
+				else
+					{
+					// Need the next line/part of line
+					GetReply(EFalse);
+					}
+				break;
+				}
+
+			default:
+				// Complete it if there's no error or if it's just a 'NO'
+				// reply (ie, KErrNotSupported)
+				if (result>=KErrNone || result==KErrNotSupported)
+					{
+					DBG((LogText(_L8("CImImap4Session::DoRunL(): No error - calling CommandComplete()"))));
+					CommandCompleteL(result);
+					}
+				else
+					Fail(result);
+				break;
+				}
+			}
+		break;
+		}
+
+	case EImapStateAppendSizeWait:	// Work out the size of the append
+		{
+		// Sizing operation completed, get the size
+		iCommandSize=iMessageSizer->MessageSize();
+		delete iMessageSizer;
+		iMessageSizer=NULL;
+
+		DBG((LogText(_L8("Sized message to %d bytes"),iCommandSize)));
+
+		iProgress.iBytesToDo=iCommandSize;
+		iProgress.iBytesDone=0;
+		
+		// Make a sender, to actually send the text
+		delete iMessageSender;
+		iMessageSender=NULL;
+		iMessageSender=CImSendMessage::NewL(/*iFs,*/*iEntry);
+
+		// Change in API, should be retrieving iSettings.SendCopyToSelf(), 
+		// instead using default ESendNoCopy.
+		iMessageSender->InitialiseL(iCommandIds[0], ESendAsMimeEmail, 
+									iMessageDate, iHost, iCharset, ESendNoCopy);
+
+		// Make path to the message's destination, and send command
+		HBufC8* path=MakePathL(iCommandIds[1],ETrue);
+
+		// Reset the entry to first param(message id), as it will be changed to 
+		// second param(directory id) in the MakePathL function. This entry 
+		// will be sent to destination as message(line by line) by CImSendMessage class.
+		SetEntryL(iCommandIds[0]);
+		
+		CleanupStack::PushL(path);
+		DoQuoteL(path);
+		TPtrC8 pathptr=path->Des();
+		NewTag();
+		iImapIO->SendL(iStatus,_L8("%d APPEND \"%S\" {%d}\r\n"),iTag,&pathptr,iCommandSize);
+		NewTagSent();
+		CleanupStack::PopAndDestroy();
+
+		// Append the actual message
+		iState=EImapStateAppendPromptWait;
+		break;
+		}
+
+	case EImapStateAppendPromptWait: // Wait for prompt before sending message body
+		{
+		// Looking for a '+'
+		CImapAtom *p=iRootAtom->ToChildL();
+
+		// '*' indicates an untagged message
+		if (p->Compare(KIMAP_UNTAGGED))
+			{
+			// Process it
+			TRAPD(err,ProcessUntaggedL(p->ToNextL(),EFalse));
+			if (err!=KErrNone)
+				Fail(err);
+			}
+		else if (p->Compare(KIMAP_CONTINUATION))
+			{
+			// Start sending message body: move into sending body state
+			iState=EImapStateAppendWait;
+
+			// Make line buffer
+			delete iLineBuffer;
+			iLineBuffer=NULL;
+			iLineBuffer=HBufC8::NewL(KImMailMaxBufferSize);
+
+			// Send first line: Must be more than one line, so we don't bother
+			// with checking the return code.
+			TInt padcount=0;
+			TPtr8 line=iLineBuffer->Des();
+			iMessageSender->NextLineL(line,padcount);
+			iImapIO->SendL(iStatus,_L8("%S"),&line);
+
+			DBG((LogText(_L8("CImap4Session::DoRunL(): iState = EImapStateAppendWait"))));
+			DBG((LogText(_L8("*******************************************************"))));
+			DBG((LogText(_L8("CImap4Session::DoRunL(): waiting for iImapIO to wake me"))));
+			DBG((LogText(_L8("*******************************************************"))));
+
+                        SetActive();
+			iSendQueued=EFalse; // We want to come back to here - we've not sent a command
+			iProgress.iBytesDone+=line.Length();
+
+			DBG((LogText(_L8("Sent: %d '%S'"),line.Length(),&line)));
+			}
+		break;
+		}
+
+	case EImapStateAppendWait:
+		{
+		// Send line of message
+		TInt padcount=0;
+		TPtr8 line=iLineBuffer->Des();
+		if (iMessageSender->NextLineL(line,padcount)==KImCvFinished)
+			{
+			// Send last line, plus CRLF to terminate command
+			iImapIO->SendL(iStatus,_L8("%S\r\n"),&line);
+			iProgress.iBytesDone+=line.Length();
+
+			DBG((LogText(_L8("Sent: %d '%S'"),line.Length(),&line)));
+			DBG((LogText(_L8("Total bytes sent %d plus the CRLF"),iProgress.iBytesDone)));
+
+			delete iLineBuffer;
+			iLineBuffer=NULL;
+			delete iMessageSender;
+			iMessageSender=NULL;
+			iSendQueued=ETrue;
+			iState=EImapStateAppendResultWait;
+			}
+		else
+			{
+			// Send a line
+			iImapIO->SendL(iStatus,_L8("%S"),&line);
+			iSendQueued=EFalse;
+			iProgress.iBytesDone+=line.Length();
+
+			DBG((LogText(_L8("Sent: %d '%S'"),line.Length(),&line)));
+			}
+
+                DBG((LogText(_L8("CImap4Session::DoRunL(): iState = EImapStateAppendWait"))));
+		DBG((LogText(_L8("*******************************************************"))));
+		DBG((LogText(_L8("CImap4Session::DoRunL(): waiting for iImapIO to wake me"))));
+		DBG((LogText(_L8("*******************************************************"))));
+
+		SetActive();
+		break;
+		}
+
+	case EImapStateFetchCancelWait:
+	case EImapStateFetchWait:// Wait for body length/data
+		{
+		TInt result=KErrNone; // To stop .AER warnings...
+
+		// Process it
+		TRAPD(err,result=ProcessCommandReplyL());
+	
+		if (err!=KErrNone)
+			Fail(err);
+		else
+			{
+			//LogText(_L8("ProcessCommandReplyL() returned %d\n"),result);
+			switch(result)
+				{
+			case KErrNotReady:
+				// Still waiting for body size, another partial fetch
+				if (iSizeWait)
+					GetReply(ETrue);
+				else
+					GetReply(EFalse);
+				break;
+			case KErrImapInvalidServerResponse:
+				// Nothing to do , return back
+				break;
+
+			case KErrWrite:
+				// Process has issued a command of its own, nothing for us to
+				// do here
+				break;
+
+			default:
+				// Complete it
+				if (!iCommandsOutstanding)
+					{
+					DBG((LogText(_L8("CImap4Session::DoRunL(): No commands outstanding- calling CommandComplete()"))));
+ 					CommandCompleteL(result);
+					}
+				else
+					{
+					if (iJustSentFetch)
+ 						{
+						DBG((LogText(_L8("CImap4Session::DoRunL(): Just sent fetch"))));
+   						iJustSentFetch = EFalse;
+  						}
+  					else
+  						{
+  						// Get next fetch result
+						DBG((LogText(_L8("CImap4Session::DoRunL(): NOT just sent fetch"))));
+   						iSizeWait=ETrue;
+  						GetReply(ETrue);
+  						}
+					}
+				break;
+				}
+			}
+		break;
+		}
+
+	case EImapStateMoveEntryWait:
+		{
+		// We're done with the moveentry
+
+		// Park the move entry again
+		iMoveEntry->SetEntry(NULL);
+
+		// Copy the structure: the MsvId is still the same, it's just not in the same
+		// place.
+
+		//DS - Selectively using a copy or a move depending on user intention, so no need for this
+		//"copy structure back to mirror" malarky.
+		//		TMsvId newid=CopyLevelL(iMoveSource,iMoveSourceFolder);
+	
+		// Note this ID in the iRelatedId, so that higher levels know where we are again...
+
+		//DS iMoveSource could be either the same message moved to a different place
+		//or still the original message - could employ some logic for differentiating if there
+		//is some need to twiddle flags etc, but I'll leave that for someone else to do...
+		SetEntryL(iMoveSource);
+		TMsvEntry entry=iEntry->Entry();
+
+		entry=iEntry->Entry();
+		entry.SetNew(EFalse);
+		ChangeEntryL(entry);
+
+		// Park iEntry
+		SetEntryL(NULL);
+
+		// Inform caller of new ID
+		//DS as far as I can tell, this isn't used!
+		//*iNewSource=newid;
+
+		// Back to previous state
+		iState=iSavedState;
+		break;
+		}
+
+	default:
+		gPanic(ERunLInUnknownState);
+		return;
+		}
+        DBG((LogText(_L8("CImImap4Session::DoRunL(): exiting..."))));
+	}
+
+
+// The IMAP Idle case for doing a "GetReply". 
+// Called by CImImap4SessionIdleRead::Start() to get the whole thing going.
+void CImImap4Session::DoIdleRead(TRequestStatus& aIdleReadStatus)
+	{
+	// Unlike a normal read, give the reponse to the CImImap4SessionIdleRead when done
+	iImapIO->GetReply(aIdleReadStatus);
+	iReceiveQueued=ETrue;
+	}
+
+void CImImap4Session::CancelIdleRead()
+	{
+	// Undo what DoIdleRead() did
+	iImapIO->Cancel();
+	iReceiveQueued=EFalse;
+	}
+
+// The standard case for a normal transaction "GetReply"
+// Queue request from IO layer to get the next atom
+void CImImap4Session::GetReply(const TBool aPartialReturn)
+	{
+	// Cancel any dummy operation
+	if (ImapIdleSupported()==EFalse)
+		{
+		CancelDummy();
+		}
+
+	// Queue the receive
+	if (aPartialReturn)
+		{
+		// Get a partial line, as we need to see how much data is on its way
+		// for flood control/issuing next fetch command to keep it streaming.
+		iImapIO->GetReply(iStatus,80,ETrue);
+		}
+	else
+		{
+		// Get a *whole* line, we don't want a partial return
+		iImapIO->GetReply(iStatus);
+		}
+
+	// Note that we have ???
+	iReceiveQueued=ETrue;
+        DBG((LogText(_L8("*******************************************************"))));
+	DBG((LogText(_L8("CImap4Session::GetReply(): waiting for iImapIO to wake me"))));
+	DBG((LogText(_L8("*******************************************************"))));
+
+	SetActive();
+	}
+
+// Make a new tag, and queue send
+void CImImap4Session::NewTag()
+	{
+	if (ImapIdleSupported()==EFalse)
+		{
+		// Cancel any dummy operation that might be outstanding
+		CancelDummy();
+		}
+
+	// Make a new tag
+	iTag++;
+
+	// One more outstanding command
+	iCommandsOutstanding++;
+	}
+
+void CImImap4Session::NewTagSent()
+	{
+	// Go active and note that a send has been queued
+	SetActive();
+	iSendQueued=ETrue;
+	}
+
+// Queue sending of a command line
+void CImImap4Session::SendMessageL(const TDesC8& aMessage)
+	{
+	__ASSERT_DEBUG(iSendQueued==EFalse,gPanic(EAlreadySending));
+	if(!(iSendQueued==EFalse))
+		{	
+		User::LeaveIfError(KErrInUse);//Already Sending
+		}
+	// Make a new tag
+	NewTag();
+	iImapIO->SendL(iStatus,_L8("%d %S\r\n"),iTag,&aMessage);
+	NewTagSent();
+	}
+
+void CImImap4Session::SendUntaggedMessageL(const TDesC8 &aMessage)
+	{
+	__ASSERT_DEBUG(iSendQueued==EFalse,gPanic(EAlreadySending));
+	if(!(iSendQueued==EFalse))
+		{
+		User::LeaveIfError(KErrInUse); // Already Sending
+		}
+	if (ImapIdleSupported()==EFalse)
+		{
+		// Cancel any dummy operation that might be outstanding
+		CancelDummy();
+		}
+
+	iImapIO->SendL(iStatus,KImapCommand,&aMessage);
+	DBG((LogText(_L8("*******************************************************"))));
+	DBG((LogText(_L8("CImap4Session::SendUntaggedMessageL(): waiting for iImapIO to wake me"))));
+	DBG((LogText(_L8("*******************************************************"))));
+
+	SetActive();
+	iSendQueued=ETrue;
+	}
+
+/**
+Allows a untagged message to be sent to the server with a short idle timeout applied.
+This is used when a fast response is expected
+*/
+void CImImap4Session::SendUntaggedMessageWithTimeoutL(const TDesC8 &aMessage, TInt aTimeout)
+	{
+	__ASSERT_ALWAYS(iSendQueued==EFalse,gPanic(EAlreadySending));
+	if(!(iSendQueued==EFalse))
+		{
+		User::LeaveIfError(KErrInUse); // Already Sending
+		}
+	if (ImapIdleSupported()==EFalse)
+		{
+		// Cancel any dummy operation that might be outstanding
+		CancelDummy();
+		}
+
+	iImapIO->SendWithTimeoutL(iStatus, aTimeout, KImapCommand, &aMessage);
+	DBG((LogText(_L8("*******************************************************"))));
+	DBG((LogText(_L8("CImap4Session::SendUntaggedMessageWithTimeoutL(): waiting for iImapIO to wake me"))));
+	DBG((LogText(_L8("*******************************************************"))));
+
+	SetActive();
+	iSendQueued=ETrue;
+	}
+
+// Construct a full mailbox path, given a TMsvId
+// This is expensive in memory movement terms, as it works UP the path,
+// inserting new data at the start. This is based on the principle that it's
+// more expensive to find an entry in the index with SetEntryL() than it is to
+// move some bytes about, otherwise we'd find the path upwards then create the
+// string downwards.
+HBufC8* CImImap4Session::MakePathL(const TMsvId aTarget, const TBool aIncludeLeaf)
+	{
+	__ASSERT_DEBUG(iState>=EImapStateNoSelect,gPanic(ENotLoggedOn));
+	if(!(iState>=EImapStateNoSelect))
+		{
+		User::LeaveIfError(KErrGeneral);
+		}
+	// Making a path: we start with nothing
+	HBufC8 *path=HBufC8::NewLC(256);
+	TBool skipfirst=ETrue;
+	TMsvId traverse=aTarget;
+
+	// Move to the entry
+	SetEntryL(traverse);
+
+	// Skipping the leaf?
+	if (!aIncludeLeaf && iEntry->Entry().iType!=KUidMsvServiceEntry)
+		{
+		// Up a level before we generate the path
+		SetEntryL(traverse=iEntry->Entry().Parent());
+		}
+
+	// check and see if we are dealing with the INBOX, in which case
+	// return immediately
+	if (iEntry->Entry().Parent()==iServiceId &&
+		iEntry->Entry().iDetails.CompareF(KIMAP_INBOX)==0)
+		{
+		path->Des().Insert(0,_L8("INBOX"));
+		CleanupStack::Pop();
+		return path;
+		}
+
+	iCharConv->PrepareToConvertToFromOurCharsetL(KCharacterSetIdentifierImapUtf7);
+	
+	// While we can still go up within this service...
+	while(iEntry->Entry().iType!=KUidMsvServiceEntry)
+		{
+		// Add the name of this component to the path
+   		if (!skipfirst)
+			path->Des().Insert(0,iHierarchySeparator);
+		else
+			skipfirst=EFalse;
+
+		// this should be a better sized allocation but the path is
+		// fixed to 256 anyway so this will do
+		HBufC8* utf7=HBufC8::NewL(256);
+		CleanupStack::PushL(utf7);
+
+		TInt numUC, indexUC;
+		TPtr8 des = utf7->Des();
+		iCharConv->ConvertFromOurCharsetL(iEntry->Entry().iDetails, des, numUC, indexUC);
+		path->Des().Insert(0,utf7->Des());
+
+		CleanupStack::PopAndDestroy();
+
+		// Go up a level
+		SetEntryL(traverse=iEntry->Entry().Parent());
+		}
+	
+	// Add the path at the very start, if it exists
+	if (iFolderPath.Length())
+		{
+		// Anything there already? If not, don't bother with the separator
+		if (path->Des().Length()) path->Des().Insert(0,iHierarchySeparator);
+		path->Des().Insert(0,iFolderPath);
+		}
+
+	// Pop it off cleanup stack
+	CleanupStack::Pop();
+
+	// Return the path
+	return(path);
+	}
+
+// Queue a connection
+void CImImap4Session::ConnectL(TRequestStatus& aRequestStatus, const TMsvId aService)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Connect(%x)"),aService)));
+	__ASSERT_DEBUG(iState==EImapStateDisconnected,gPanic(EOpenWhenNotClosed));
+	if(!(iState==EImapStateDisconnected))
+		{
+		User::LeaveIfError(KErrInUse); // Open when not closed.
+		}
+	// Any progress we give now should be related to this connect, so we need to 
+	// clear any previous progress first.
+	ResetStats();
+
+	Queue(aRequestStatus);
+
+	// Get host details from server
+	iServiceId=aService;
+	SetEntryL(iServiceId);
+
+  	// get the iap preferences
+  	CEmailAccounts* account = CEmailAccounts::NewLC();
+  	if (iPrefs == NULL)
+  		{
+  		iPrefs = CImIAPPreferences::NewLC();
+  		CleanupStack::Pop(iPrefs);
+  		}	
+
+	TImapAccount id;
+	id.iImapAccountId = iEntry->Entry().MtmData2();  // iMtmData2 of the service entry contains TImapAccountId
+	id.iImapAccountName = iEntry->Entry().iDetails;
+	id.iImapService = iEntry->Entry().iServiceId;
+	id.iSmtpService = iEntry->Entry().iRelatedId;
+
+  	account->LoadImapSettingsL(id, *iServiceSettings);
+    account->LoadImapIapSettingsL(id, *iPrefs);	
+  	CleanupStack::PopAndDestroy(account);
+
+	// Copy details
+	delete iUsername;	//need to delete iUsername first, just in case already it has a value.
+	iUsername = NULL;
+	iUsername=iServiceSettings->LoginName().AllocL();
+	delete iPassword;	//need to delete iPassword first, just in case already it has a value.
+	iPassword = NULL;
+	iPassword=iServiceSettings->Password().AllocL();
+	iFolderPath=iServiceSettings->FolderPath();
+	iHost=iServiceSettings->ServerAddress();
+	iPort=iServiceSettings->Port();
+	iUseIdleCommand = iServiceSettings->ImapIdle();
+
+	iIdleTimeout = iServiceSettings->ImapIdleTimeout();	
+	DBG((LogText(_L8("ImapIdleTimeout %d"),iIdleTimeout)));
+	// convert from seconds to microseconds
+	iIdleTimeout *= 1000000;
+
+	// Path separator: we store it as a string locally
+	iHierarchySeparator.Zero();
+	if (iServiceSettings->PathSeparator())
+		{
+		// Append to string
+		iHierarchySeparator.Append(iServiceSettings->PathSeparator());
+		}
+
+	// Any characters that will need quoting in them?
+	iLiteralUsername=EFalse;
+	int a;
+	TPtr8 userName = iUsername->Des();
+	for(a=0;a<iUsername->Length();a++)
+		{
+		if (userName[a]<=32  || userName[a]>=127 || userName[a]=='\"' || userName[a]=='%'  ||
+		    userName[a]=='(' || userName[a]==')' || userName[a]=='*'  || userName[a]=='\\' ||
+		    userName[a]=='{' || userName[a]=='}' )
+			{
+			iLiteralUsername=ETrue;
+			break;
+			}
+		}
+
+	iLiteralPassword=EFalse;
+	TPtr8 passWord = iPassword->Des();
+	for(a=0;a<iPassword->Length();a++)
+		{
+		if (passWord[a]<=32  || passWord[a]>=127 || passWord[a]=='\"' || passWord[a]=='%'  ||
+		    passWord[a]=='(' || passWord[a]==')' || passWord[a]=='*'  || passWord[a]=='\\' ||
+		    passWord[a]=='{' || passWord[a]=='}' )
+			{
+			iLiteralPassword=ETrue;
+			break;
+			}
+		}
+	
+	// Until we know we're seeing CC:Mail...
+	iTalkingToCCMail=EFalse;
+	iTalkingToOpenMail=EFalse;
+
+	// Start the connect
+	iState=EImapStateConnectWait;
+	iSendQueued=EFalse;
+	TBool sslWrappedSocket=iServiceSettings->SSLWrapper();
+	
+	// if local primarysession is active then set the local textserversion of ImapIO object.
+	if(iPrimarySession)
+		{
+		// Setting of PrimaryTextServerSession, Going to be set on the secondary session.
+		iImapIO->SetPrimaryTextServerSession(iPrimarySession->GetImap4Session()->GetTextServerSession());		
+		}
+	iImapIO->ConnectL(iStatus,iHost,iPort,*iPrefs, sslWrappedSocket);
+	
+#ifdef PRINTING
+	// Log version number now logfile is open
+	LogText(_L8("IMPS release 022.8"));
+
+	// Log connection destination
+	LogText(_L8("Connection queued to %S, port %d"),&iHost,iPort);
+
+	// Note any literal usage
+	if (iLiteralUsername)
+		LogText(_L8("Username contains unusual characters: using literal for username"));
+	if (iLiteralPassword)
+		LogText(_L8("Password contains unusual characters: using literal for password"));
+#endif
+	SetActive();
+	}
+
+// Queue a disconnection
+void CImImap4Session::DisconnectL(TRequestStatus& aRequestStatus)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Disconnect"))));
+	Cancel();
+
+	Queue(aRequestStatus);
+
+	// What are we doing at the moment?
+	if (iState<EImapStateNoSelect)
+		{
+		DoDisconnect();
+                DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::DisconnectL(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+		Complete(KErrNone);
+		}
+	else if (IsIdling())
+		{
+		iDisconnectAfterIdleStopped = ETrue;
+		DoStopIdleL();
+		}
+	else
+		{
+		DoDisconnectL();
+		}
+	}
+
+void CImImap4Session::DoDisconnectL()
+	{
+	// Send logout command
+	iState=EImapStateLogoutWait;
+	SendMessageL(KIMAPC_LOGOUT);
+	}
+
+// Are we connected?
+TBool CImImap4Session::Connected()
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Connected?"))));
+
+	if (iState>=EImapStateNoSelect)
+		return(ETrue);
+	return(EFalse);
+	}
+
+// Are we busy?
+TBool CImImap4Session::Busy()
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Busy?"))));
+
+	if (iState==EImapStateDisconnected ||
+		iState==EImapStateNoSelect ||
+		iState==EImapStateSelected ||
+		iState==EImapStateIdling)
+		return(EFalse);
+	return(ETrue);
+	}
+
+// Setting of PrimarySession, Going to be set on the secondary session.
+void CImImap4Session::SetPrimarySession(CActiveWrapper* aPrimarySession)
+	{
+	iPrimarySession=aPrimarySession;
+	}
+
+// Return of current textserversession
+CImTextServerSession* CImImap4Session::GetTextServerSession()
+	{
+	return iImapIO->GetTextServerSession();
+	}
+
+// Return the service settings
+CImImap4Settings* CImImap4Session::ServiceSettings()
+	{
+	// Return them
+	return(iServiceSettings);
+	}
+
+// List folder structure
+void CImImap4Session::ListL(TRequestStatus& aRequestStatus, const TMsvId aFolder, CArrayPtr<CImImap4DirStruct>* aList)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND List(%x)"),aFolder)));
+	__ASSERT_DEBUG(iState==EImapStateNoSelect || iState==EImapStateSelected,gPanic(ESelectWhenNotReady));
+	if(!(iState==EImapStateNoSelect || iState==EImapStateSelected))
+		{
+		User::LeaveIfError(KErrNotReady);// Select when not ready
+		}
+	Queue(aRequestStatus);
+
+	// Form the path
+	HBufC8* path=NULL;
+	TRAPD(err,path=MakePathL(aFolder,ETrue));
+	if (err!=KErrNone)
+		{
+                DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::ListL(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+		Complete(err);
+		return;
+		}
+	CleanupStack::PushL(path);
+
+	// Empty path? If not, append hierarchy separator
+	if (path->Length())
+		{
+		// Get more space
+		HBufC8 *rpath=path->ReAllocL(path->Length()+1);
+
+		// Moved?
+		if (path!=rpath)
+			{
+			// Get rid of old one and push new one
+			CleanupStack::Pop();
+			CleanupStack::PushL(path=rpath);
+			}
+
+		path->Des().Append(iHierarchySeparator);
+		}
+
+	// Save path, as we take this off replies to get the leaf: we need to
+	// do this *before* we quote it as replies will be unquoted by the
+	// time we process them.
+	iCommandBuf.Copy(*path);
+
+	// Quote it
+	DoQuoteL(path);
+
+	// Send the command
+	NewTag();	
+	iImapIO->SendL(iStatus,_L8("%d LIST \"\" \"%S%%\"\r\n"),iTag,path);
+	NewTagSent();
+
+	// Save list pointer to add to, and reset it
+	iList=aList;
+	iList->ResetAndDestroy();
+	
+	// Dispose of path
+	CleanupStack::PopAndDestroy();
+
+	// Save last state (selected/unselected) for restoring afterwards
+	iSavedState=iState;
+	iState=EImapStateListWait;
+	}
+	
+// Update subscribed bits on local folder structure
+// Local structure must have been refreshed before this is used:
+// if folders listed in the LSUB reply don't exist, they're ignored
+// silently.
+void CImImap4Session::LsubL(TRequestStatus& aRequestStatus)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Lsub"))));
+	__ASSERT_DEBUG(iState==EImapStateNoSelect || iState==EImapStateSelected,gPanic(ESelectWhenNotReady));
+	if(!(iState==EImapStateNoSelect || iState==EImapStateSelected))
+		{
+		User::LeaveIfError(KErrNotReady);
+		}
+	
+	Queue(aRequestStatus);
+
+	// First, we need to go through the entire service, resetting all
+	// the 'remote subscribed' flags.
+	ResetSubscriptionFlagsL(iServiceId);
+
+	// Build a buffer to quote the folder path (it may be necessary)
+	HBufC8* path=HBufC8::NewL(iFolderPath.Length()+1);
+	CleanupStack::PushL(path);
+	path->Des().Append(iFolderPath);
+	if (iFolderPath.Length())
+		path->Des().Append(iHierarchySeparator);
+
+	// Quote it
+	DoQuoteL(path);
+
+	// Send the command to list all of the folders in the tree: we
+	// can't do it hierarchically, as the servers aren't clever enough
+	// to tell us at parent levels about folders that contain subscribed
+	// children. Pah.
+	NewTag();
+
+	// Make a Des & send it
+	TPtrC8 pathdes(path->Des());
+	iImapIO->SendL(iStatus,_L8("%d LSUB \"\" \"%S*\"\r\n"),
+				  iTag,&pathdes);
+	NewTagSent();
+
+	// Clear up
+	CleanupStack::PopAndDestroy();
+
+	// Wait for reply
+	iSavedState=iState;
+	iState=EImapStateLsubWait;
+	}
+
+
+// Create a mailbox or folder
+void CImImap4Session::Create(TRequestStatus& aRequestStatus, const TMsvId aParent, const TDesC& aLeafName, const TBool aFolder)
+	{
+	TInt err=KErrNone;
+	if (!Connected())
+		{
+		Queue(aRequestStatus);
+		err=KErrDisconnected;
+		}
+	else
+		TRAP(err,CreateL(aRequestStatus, aParent, aLeafName, aFolder));
+	if (err!=KErrNone)
+                {
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::Create(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+		Complete(err);
+                }
+	}
+
+// Create a mailbox or folder
+void CImImap4Session::CreateL(TRequestStatus& aRequestStatus, const TMsvId aParent, const TDesC& aLeafName, const TBool aFolder)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Create(%x,%S,%d)"),aParent,&aLeafName,aFolder)));
+	__ASSERT_DEBUG(iState==EImapStateNoSelect || iState==EImapStateSelected,gPanic(ECreateWhenNotReady));
+	if(!(iState==EImapStateNoSelect || iState==EImapStateSelected))
+		{
+		User::LeaveIfError(KErrNotReady);
+		}
+	Queue(aRequestStatus);
+
+	// Make the path
+	iSavedState=iState;
+	iState=EImapStateCreateWait;
+	HBufC8 *path=NULL; // To stop .AER warnings...
+	TRAPD(err,path=MakePathL(aParent,ETrue));
+	if (err!=KErrNone)
+		{
+                DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::CreateL(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+		Complete(err);
+		return;
+		}
+	CleanupStack::PushL(path);
+
+	// Increase length of buffer for full name
+	TInt encodedLeafMaxSize = 2+aLeafName.Size()*4/3 + 1;
+	HBufC8* rpath=path->ReAllocL(path->Length()+2+encodedLeafMaxSize);
+	
+	// Moved?
+	if (rpath!=path)
+		{
+		// Destroy old one and push new one
+		CleanupStack::Pop();
+		CleanupStack::PushL(path=rpath);
+		}
+
+	// Path not blank? Put a separator in there
+	if (path->Des().Length())
+		path->Des().Append(iHierarchySeparator);
+
+	iCharConv->PrepareToConvertToFromOurCharsetL(KCharacterSetIdentifierImapUtf7);
+
+	// Add leafname, via the utf7 encoder
+	HBufC8* utf7=HBufC8::NewL(encodedLeafMaxSize);
+	CleanupStack::PushL(utf7);
+	
+	TInt numUC, indexUC;
+	TPtr8 des = utf7->Des();
+	iCharConv->ConvertFromOurCharsetL(aLeafName, des, numUC, indexUC);
+	path->Des().Append(des);
+
+	CleanupStack::PopAndDestroy(); // utf7
+
+	// Put a trailing hierarchy separator on there too if necessary
+	if (aFolder)
+		path->Des().Append(iHierarchySeparator);
+
+	// Quote it if necessary
+	DoQuoteL(path);
+	TPtrC8 pathdes(path->Des());
+
+	// Creating a folder
+	NewTag();
+	iImapIO->SendL(iStatus,_L8("%d CREATE \"%S\"\r\n"),iTag,&pathdes);
+	NewTagSent();
+
+	// Save what type of create we were doing
+	iCommandFlags[0]=aFolder;
+	iCommandIds[0]=aParent;
+	iCommandBuf=aLeafName;
+
+	// Free memory
+	CleanupStack::PopAndDestroy();
+	}
+
+// Rename a mailbox
+void CImImap4Session::Rename(TRequestStatus& aRequestStatus, const TMsvId aTarget, const TDesC& aNewName)
+	{
+	TInt err=KErrNone;
+	if (!Connected())
+		{
+		Queue(aRequestStatus);
+		err=KErrDisconnected;
+		}
+	else
+		TRAP(err,RenameL(aRequestStatus, aTarget, aNewName));
+	if (err!=KErrNone)
+		Complete(err);
+	}
+
+// Rename a mailbox
+void CImImap4Session::RenameL(TRequestStatus& aRequestStatus, const TMsvId aTarget, const TDesC& aNewName)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Rename(%x,%S)"),aTarget,&aNewName)));
+	__ASSERT_DEBUG(iState==EImapStateNoSelect || iState==EImapStateSelected,gPanic(ERenameWhenNotReady));
+	if(!(iState==EImapStateNoSelect || iState==EImapStateSelected))
+	{
+		User::LeaveIfError(KErrNotReady);
+	}
+	
+	Queue(aRequestStatus);
+
+	// Make the paths
+	HBufC8* path=NULL; // To stop .AER warnings...
+	TRAPD(err,path=MakePathL(aTarget,ETrue));
+	if (err!=KErrNone)
+		{
+		Complete(err);
+		return;
+		}
+	CleanupStack::PushL(path);
+
+	DoQuoteL(path);
+
+	HBufC8* newpath=NULL; // To stop .AER warnings...
+	TRAP(err,newpath=MakePathL(aTarget,EFalse));
+	if (err!=KErrNone)
+		{
+		CleanupStack::PopAndDestroy();
+		Complete(err);
+		return;
+		}
+	CleanupStack::PushL(newpath);
+
+	// Extend buffer for new name
+	TInt encodedLeafMaxSize = 2+aNewName.Size()*4/3 + 1;
+	HBufC8* rnewpath=newpath->ReAllocL(newpath->Length()+2+encodedLeafMaxSize);
+
+	// Moved?
+	if (rnewpath!=newpath)
+		{
+		// Destroy old one and push new one
+		CleanupStack::Pop();
+		CleanupStack::PushL(newpath=rnewpath);
+		}
+
+	if (newpath->Length())
+		newpath->Des().Append(iHierarchySeparator);
+	
+	iCharConv->PrepareToConvertToFromOurCharsetL(KCharacterSetIdentifierImapUtf7);
+
+	// Add leafname, via the utf7 encoder
+	HBufC8* utf7=HBufC8::NewL(encodedLeafMaxSize);
+	CleanupStack::PushL(utf7);
+	
+	TInt numUC, indexUC;
+	TPtr8 des = utf7->Des();
+	iCharConv->ConvertFromOurCharsetL(aNewName, des, numUC, indexUC);
+	newpath->Des().Append(des);
+
+	CleanupStack::PopAndDestroy(); // utf7
+
+	DoQuoteL(newpath);
+
+	// Save rename parameters
+	iCommandIds[0]=aTarget;
+	iCommandBuf=aNewName;			// this is still the original, unencoded name
+
+	// Set states	
+	iSavedState=iState;
+	iState=EImapStateRenameWait;
+
+	// Send the command
+	NewTag();
+	TPtrC8 pathdes(path->Des());
+	TPtrC8 newpathdes(newpath->Des());
+	iImapIO->SendL(iStatus,_L8("%d RENAME \"%S\" \"%S\"\r\n"),iTag,&pathdes,&newpathdes);
+	NewTagSent();
+
+	// Free memory
+	CleanupStack::PopAndDestroy(2);
+	}
+
+// Delete a message/mailbox
+void CImImap4Session::Delete(TRequestStatus& aRequestStatus, const CMsvEntrySelection& aTargetSel)
+	{
+	TInt err=KErrNone;
+	if (!Connected())
+		err=KErrDisconnected;
+	else
+		TRAP(err,DeleteL(aRequestStatus,aTargetSel));
+	if (err!=KErrNone)
+		{
+		Queue(aRequestStatus);
+		Complete(err);
+		}
+	}
+
+// Delete a message/mailbox
+void CImImap4Session::Delete(TRequestStatus& aRequestStatus, const TMsvId aTarget)
+	{
+	TInt err=KErrNone;
+	if (!Connected())
+		err=KErrDisconnected;
+	else
+		TRAP(err,DeleteL(aRequestStatus,aTarget));
+	if (err!=KErrNone)
+		{
+		Queue(aRequestStatus);
+		Complete(err);
+		}
+	}
+void CImImap4Session::DeleteEntryL( const TMsvId aTarget)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Delete(%x)"),aTarget)));
+
+	// Move to the entry in question
+	SetEntryL(aTarget);
+
+	LOG_COMMANDS((	LogText(_L8("COMMAND Delete(message)"))));
+	__ASSERT_DEBUG(iState==EImapStateSelected,gPanic(EDeleteWhenNotReady));	
+	if(!(iState==EImapStateSelected))
+	{
+		User::LeaveIfError(KErrNotReady);
+	}
+	
+	// SJM, remove check for right mailbox as we may be trying to
+	// delete a moved entry which is in fact no longer in the
+	// right mailbox.
+#if 0
+	// Check we're in the right mailbox
+	if (iEntry->Entry().Parent()!=iMailboxId)
+		{
+		// Nope.
+		Queue(aRequestStatus);
+		Complete(KErrImapWrongFolder);
+		return;
+		}
+#endif
+	
+	// Set deleted flag on this entry
+	TMsvEmailEntry entry=iEntry->Entry();
+	entry.SetDeletedIMAP4Flag(ETrue);
+	ChangeEntryL(entry);
+	}
+
+void CImImap4Session::DeleteL(TRequestStatus& aRequestStatus, const CMsvEntrySelection& aTargetSel)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Delete (%x)"),aTargetSel[0])));
+
+	// Move to the entry in question
+	SetEntryL(aTargetSel[0]);
+
+    CMsvEntrySelection* sel=aTargetSel.CopyL();
+	delete iSelection;
+	iSelection=sel;
+
+	// Only deleting message seletion currently
+	if (iEntry->Entry().iType==KUidMsvMessageEntry)
+		{
+		// Set delete flag on all selected entries.
+		TInt count=iSelection->Count();
+		while (count--)
+			DeleteEntryL((*iSelection)[count]);
+
+		// Force a folder close with expunge
+		CloseL(aRequestStatus,ETrue);
+		}
+	else
+		{
+		LOG_COMMANDS((	LogText(_L8("COMMAND Delete - Can only delete selection of Messages"))));
+
+		// Deleting selection of entries whicxh are not messages
+		Queue(aRequestStatus);
+		Complete(KErrNotSupported);
+		}
+	}
+
+void CImImap4Session::DeleteL(TRequestStatus& aRequestStatus, const TMsvId aTarget)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Delete(%x)"),aTarget)));
+
+	// Move to the entry in question
+	SetEntryL(aTarget);
+
+	// A message?
+	if (iEntry->Entry().iType==KUidMsvMessageEntry)
+		{
+		DeleteEntryL(aTarget);
+
+		// Temporary: force a folder close with expunge
+		CloseL(aRequestStatus,ETrue);
+		}
+	// A folder?
+	else if (iEntry->Entry().iType==KUidMsvFolderEntry)
+		{
+		LOG_COMMANDS((	LogText(_L8("COMMAND Delete(folder)"))));
+                __ASSERT_DEBUG(iState==EImapStateNoSelect,gPanic(EDeleteWhenNotReady));
+		if(!(iState==EImapStateNoSelect))
+			{
+			User::LeaveIfError(KErrNotReady);
+			}
+		
+		Queue(aRequestStatus);
+
+		// Save IDs of parent and target for actually doing the local delete when
+		// the remote one completes successfully.
+		iCommandIds[0]=iEntry->Entry().Parent();
+		iCommandIds[1]=aTarget;
+
+		// Get path to delete
+		HBufC8* path=NULL; // To stop .AER warnings...
+		path=MakePathL(aTarget,ETrue);
+		CleanupStack::PushL(path);
+		DoQuoteL(path);
+
+		// Set state
+		iSavedState=iState;
+		iState=EImapStateDeleteWait;
+
+		// Send command
+		NewTag();
+		TPtrC8 pathdes(path->Des());
+		iImapIO->SendL(iStatus,_L8("%d DELETE \"%S\"\r\n"),iTag,&pathdes);
+		NewTagSent();
+
+		// Destroy buffer
+		CleanupStack::PopAndDestroy();
+		}
+	// Something else?
+	else
+		{
+		LOG_COMMANDS((	LogText(_L8("COMMAND Delete(unknown)"))));
+
+		// Delete of something that isn't a folder or a message. Erk!
+		Queue(aRequestStatus);
+		Complete(KErrNotSupported);
+		}
+	}
+
+// Delete everything in this folder
+void CImImap4Session::DeleteAllMessagesL(TRequestStatus& aRequestStatus)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND DeleteAllMessages(%x)"),iMailboxId)));
+
+	// We have to be selected
+	__ASSERT_DEBUG(iState==EImapStateSelected,gPanic(EDeleteWhenNotReady));
+	if(!(iState==EImapStateSelected))
+		{
+		User::LeaveIfError(KErrNotReady);
+		}
+	
+	Queue(aRequestStatus);
+
+	// We're going to send a command of one style or another
+	NewTag();
+
+	// Are there any messages remotely to delete?
+	if (iMailboxSize==0)
+		{
+		// No: just do a close
+		iState=EImapStateCommandWait;
+		iSavedState=EImapStateNoSelect;
+		iImapIO->SendL(iStatus,_L8("%d CLOSE\r\n"),iTag);
+		}
+	else
+		{
+		// DeleteAllMessages is a special case: we want to delete everything,
+		// regardless of wether it's in the mirror or not. So, we set deleted
+		// flags on everything then expunge the folder
+		iState=EImapStateDeleteAllWait;
+
+		// Send command: we go into deleteall wait as the next
+		iImapIO->SendL(iStatus,_L8("%d STORE 1:* +FLAGS (\\Deleted)\r\n"),iTag);
+		}
+
+	// Sent it
+	NewTagSent();
+	}
+
+// Select a folder
+void CImImap4Session::Select(TRequestStatus& aRequestStatus, const TMsvId aFolder, const TBool aReadWrite)
+	{
+	TInt err=KErrNone;
+	if (!Connected())
+		{
+		Queue(aRequestStatus);
+		err=KErrDisconnected;
+		}
+	else
+		TRAP(err,SelectL(aRequestStatus, aFolder, aReadWrite));
+	if (err!=KErrNone)
+                {
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::Select(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+		Complete(err);
+                }
+	}
+
+void CImImap4Session::SelectL(TRequestStatus& aRequestStatus, const TMsvId aFolder, const TBool aReadWrite)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Select(%x (rw=%d), in state %d. Current mailbox=%x)"),aFolder,aReadWrite?1:0,iState,iMailboxId)));
+
+	if (!(iState==EImapStateNoSelect || iState==EImapStateSelected))
+		{
+		User::LeaveIfError(KErrNotReady);
+		}
+	Queue(aRequestStatus);
+	
+	// reset counts to safe values here to avoid reporting left over
+	// values from previous fetch. Correct values will be set up once
+	// headers have been fetched and parts counted.
+	iProgress.iPartsToDo=iProgress.iBytesToDo=1;
+	iProgress.iPartsDone=iProgress.iBytesDone=0;
+	
+	// Do the select
+
+	// Is it already selected and the read/write state is compatible?
+	// Skip the command if possible!
+	if (iMailboxId==aFolder && iState==EImapStateSelected &&
+		((aReadWrite && iMailboxWritable) || !aReadWrite))
+		{
+		if (ImapIdleSupported())
+			{
+                        DBG((LogText(_L8("-----------------------------------------------------------"))));
+			DBG((LogText(_L8("CImap4Session::SelectL(): calling Complete()"))));
+			DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+			Complete(KErrNone);
+			return;
+			}
+		else
+			{
+			// Just NOOP it so that we know about any mailbox size changes
+			NewTag();
+			iImapIO->SendL(iStatus,_L8("%d NOOP\r\n"),iTag);
+			iState=EImapStateSelectWait;
+			NewTagSent();
+			return;
+			}
+		}
+	
+	// Ok looks as if the SELECT actually needs to be done.
+	DoSelectL(aFolder, aReadWrite);
+	}
+
+void CImImap4Session::DoSelectL(const TMsvId aFolder, const TBool aReadWrite)
+	{
+	NewTag();	
+
+	// We should always get an EXISTS after a SELECT but just in case
+	// we will force it true here. This ensures that a NewOnlySync
+	// will always do the sync when it involves selecting a new folder
+	iMailboxReceivedExists=ETrue;
+
+	// Store name of new mailbox
+	iMailboxId=aFolder;
+
+	// Get rid of old index and reset everything
+	iFolderIndex.Reset();
+	iMailboxSize=0;
+	iMsgsDone=0;
+	iMailboxRecent=0;
+	iUidValidity=0;
+	iUidNext=0;
+
+	// Is it special-case inbox?
+	SetEntryL(iMailboxId);
+#if 0
+	if (iEntry->Entry().Parent()==iServiceId && iEntry->Entry().iDetails.CompareF(KIMAP_INBOX)==0)
+		{
+		// Inbox: no path prepended
+		iImapIO->SendL(iStatus,aReadWrite?_L8("%d SELECT INBOX\r\n"):_L8("%d EXAMINE INBOX\r\n"),iTag);
+		iMailboxIsInbox=ETrue;
+		}
+	else
+		{
+#endif
+		// Create path and send select command
+		HBufC8* path=MakePathL(iMailboxId,ETrue);
+		CleanupStack::PushL(path);
+		DoQuoteL(path);
+		TPtrC8 pathptr=path->Des();
+		iImapIO->SendL(iStatus,aReadWrite?_L8("%d SELECT \"%S\"\r\n"):_L8("%d EXAMINE \"%S\"\r\n"),iTag,&pathptr);
+		CleanupStack::PopAndDestroy();
+#if 0
+		iMailboxIsInbox=EFalse;
+		}
+#endif
+	// Sent command
+	iState=EImapStateSelectWait;
+	NewTagSent();
+	}
+
+// Copy a message to a new folder
+void CImImap4Session::Copy(TRequestStatus& aRequestStatus, const TMsvId aSource, const TMsvId aDestination, TBool aUnSelectIfSameFolder)
+	{
+	TInt err=KErrNone;
+	if (!Connected())
+		{
+		Queue(aRequestStatus);
+		err=KErrDisconnected;
+		}
+	else
+		TRAP(err,CopyL(aRequestStatus, aSource, aDestination, aUnSelectIfSameFolder));
+	if (err!=KErrNone)
+                {
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::Copy(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+		Complete(err);
+                }
+	}
+
+void CImImap4Session::CopyL(TRequestStatus& aRequestStatus, const TMsvId aSource, const TMsvId aDestination, TBool aUnSelectIfSameFolder)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Copy(%x,%x)"),aSource,aDestination)));
+	__ASSERT_DEBUG(iState==EImapStateSelected,gPanic(ECopyWhenNotSelected));
+	if(!(iState==EImapStateSelected))
+		{
+		User::LeaveIfError(KErrArgument);//Copy when not selected
+		}
+	
+	Queue(aRequestStatus);
+
+	// Make destination folder path
+	HBufC8 *command=NULL; // To stop .AER warnings...
+	TRAPD(err,command=MakePathL(aDestination,ETrue));
+	if (err!=KErrNone)
+		{
+                DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::CopyL(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+		Complete(err);
+		return;
+		}
+	CleanupStack::PushL(command);
+	DoQuoteL(command);
+
+	// Check that source is in the folder we have got selected: this also
+	// ensures it's actually a message, as the parent of attachments wouldn't
+	// be the folder we have selected...
+	SetEntryL(aSource);
+	__ASSERT_DEBUG(iEntry->Entry().Parent()==iMailboxId,gPanic(ECopyNotFromSelectedFolder));
+	if(!(iEntry->Entry().Parent()==iMailboxId))
+			{
+			User::LeaveIfError(KErrGeneral);
+			}
+	// Make command
+	TMsvEmailEntry entry=iEntry->Entry();
+	NewTag();
+	TPtrC8 commanddes(command->Des());
+	iImapIO->SendL(iStatus,_L8("%d UID COPY %u \"%S\"\r\n"),iTag,entry.UID(),&commanddes);
+	NewTagSent();
+
+	// If we're copying to the currently selected folder, pretend we've unselected it.
+	// This ensures we'll pick up on any changes in the next new sync
+	if (aUnSelectIfSameFolder && iMailboxId==aDestination && iState==EImapStateSelected)
+		{
+		// Return to 'no select' state after this
+		iSavedState=EImapStateNoSelect;
+		}
+	else
+		{
+		// Save existing selected/unselected state
+		iSavedState=iState;
+		}
+
+	// Set up state
+	iState=EImapStateCommandWait;
+
+	// Delete buffer
+	CleanupStack::PopAndDestroy();
+	}
+
+// Copy a message to a new folder
+void CImImap4Session::Append(TRequestStatus& aRequestStatus, const TMsvId aSource, const TMsvId aDestination)
+	{
+	TInt err=KErrNone;
+	if (!Connected())
+		{
+		Queue(aRequestStatus);
+		err=KErrDisconnected;
+		}
+	else
+		TRAP(err,AppendL(aRequestStatus, aSource, aDestination));
+	if (err!=KErrNone)
+		Complete(err);
+	}
+	
+void CImImap4Session::AppendL(TRequestStatus& aRequestStatus, const TMsvId aSource, const TMsvId aDestination)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Append(%x,%x)"),aSource,aDestination)));
+
+	Queue(aRequestStatus);
+
+	// Check that source is a complete message
+	SetEntryL(aSource);
+
+	// Check it's a message
+	if (iEntry->Entry().iType!=KUidMsvMessageEntry)
+		{
+		// Can't do it!
+		Complete(KErrGeneral);
+		return;
+		}
+
+	// Cancel any dummy operation that might be outstanding
+	if (ImapIdleSupported()==EFalse)
+		{
+		CancelDummy();
+		}
+
+	// Size message for sending using a CImCalculateMessageSize
+	delete iMessageSizer;
+	iMessageSizer=NULL;
+	iMessageSizer=CImCalculateMsgSize::NewL(iFs,*iEntry);
+
+	// Start sizing operation, using MIME: save source & destination for next step
+	// Use iHost (hostname of remote server) as the domain name seed for the MsgId
+	iCommandIds[0]=aSource;
+	iCommandIds[1]=aDestination;
+	iMessageDate=iEntry->Entry().iDate;
+	iMessageSizer->StartL(iStatus,iCommandIds[0], ESendAsMimeEmail, 
+												  iMessageDate, iHost, iCharset);
+
+	// If we're appending to the currently selected folder, pretend we've unselected it.
+	// This ensures we'll pick up on any changes in the next new sync
+	if (iMailboxId==aDestination && iState==EImapStateSelected)
+		{
+		// Return to 'no select' state after this
+		iSavedState=EImapStateNoSelect;
+		}
+	else
+		{
+		// Save existing selected/unselected state
+		iSavedState=iState;
+		}
+
+	iState=EImapStateAppendSizeWait;
+	if (!IsActive()) SetActive();
+	}
+
+// Close a selected folder
+void CImImap4Session::Close(TRequestStatus& aRequestStatus, const TBool aExpunge)
+	{
+	TInt err=KErrNone;
+	if (!Connected())
+		{
+		Queue(aRequestStatus);
+		err=KErrDisconnected;
+		}
+	else
+		TRAP(err,CloseL(aRequestStatus, aExpunge));
+	if (err!=KErrNone)
+		Complete(err);
+	}
+
+void CImImap4Session::CloseL(TRequestStatus& aRequestStatus, const TBool aExpunge)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Close(%d)"),aExpunge)));
+
+	Queue(aRequestStatus);
+
+	// Folder currently open? Just complete if we're not selected
+	if (iState==EImapStateNoSelect)
+		{
+		Complete(KErrNone);
+		return;
+		}
+
+	// Expunging? If not, just send close command
+	if (!aExpunge)
+		{
+		// Send close
+		SendMessageL(KIMAPC_CLOSE);
+		iSavedState=EImapStateNoSelect;
+		iState=EImapStateCommandWait;
+		}
+	else
+		{
+		// The deletion strategy will build up runs of UIDs in order to make the
+		// most efficient use of bandwidth. However, as UIDs are not necessarily
+		// contiguous, so we use their sorted position in the mirror to decide on
+		// runs. UIDs will be allocated on an increasing basis,as the server cannot
+		// have magically invented new UIDs between 2 existing ones.
+		// UIds can miss from run by using email sync limits.
+		//
+		// We use TInt64's because AppendNum() cannot take a TUint32, which is
+		// what a UID is.
+		SetEntryL(iMailboxId);
+		GetChildrenL(*iSelection);
+		TRAPD(err,MakeSortedFolderIndexL());
+		if (err!=KErrNone)
+			{
+			Complete(err);
+			return;
+			}
+		TInt pos=0;
+		TInt run=0;
+		TInt64 last=0;
+		TInt deleted=0;
+
+		// Build command
+		HBufC8* command=HBufC8::NewLC(256);
+
+		// Start command
+		command->Des().Append(_L8("UID STORE "));
+
+		while(pos<iFolderIndex.Size())
+			{
+			// Look for messages with deleted flag set
+			SetEntryL(iFolderIndex[pos].iMsvId);
+			if (((TMsvEmailEntry)iEntry->Entry()).DeletedIMAP4Flag())
+				{
+				LOG_COMMANDS((LogText(_L8("Message #%d marked as deleted"),pos+1)));
+
+				deleted++;
+				// If uids are missing from run
+				if ((pos > 0 )&& (((TUint)iFolderIndex[pos].iUid - (TUint)iFolderIndex[pos-1].iUid) > 1))
+				{
+				// Breaking a run
+				if(run > 1)
+					{
+					// A run of at least 2 is a range. Append 'last' UID,
+					// after removing comma and append a comma 
+					command->Des().Delete(command->Des().Length()-1,1);
+					command->Des().Append(_L8(":"));
+					command->Des().AppendNum(last);
+					command->Des().Append(_L8(","));
+					}
+				// run broken
+				run = 0;
+				}
+
+				// This one is deleted. Are we in a run?
+				if (!run)
+					{
+					// No, start of a run/single item. Add to command
+					// Enough room for this?
+					if ((command->Length()+32)>command->Size())
+						{
+						// Extend buffer
+						HBufC8* rcommand=command->ReAllocL(command->Size()+64);
+						
+						// Moved?
+						if (rcommand!=command)
+							{
+							// Destroy old one and push new one
+							CleanupStack::Pop();
+							CleanupStack::PushL(command=rcommand);
+							}
+						}
+
+					// Single number, plus a comma to terminate
+					TInt64 uid=(TUint)((TMsvEmailEntry)iEntry->Entry()).UID();
+					command->Des().AppendNum(uid);
+					command->Des().Append(_L8(","));
+
+					// A run of 1 :-)
+					run++;
+					}
+				else
+					{
+					// We're in a run already. Extend it: it will be terminated
+					// when the run is broken or we exit.
+					last=(TUint)iFolderIndex[pos].iUid;
+					run++;
+					}
+				}
+			else
+				{
+				// Mark this MsvId as 0 (so it will be kept in local expunge)
+				iFolderIndex[pos].iMsvId=0;
+
+				// Breaking a run?
+				if (run>1)
+					{
+					// A run of at least 2 is a range. Append 'last' UID,
+					// after removing comma
+					command->Des().Delete(command->Des().Length()-1,1);
+					command->Des().Append(_L8(":"));
+					command->Des().AppendNum(last);
+					command->Des().Append(_L8(","));
+					}
+
+				// Run broken
+				run=0;
+				}
+
+			// Next message
+			pos++;
+			}
+
+		// Anything deleted?
+		if (deleted)
+			{
+			// Remove the last character in the command string
+			command->Des().Delete(command->Des().Length()-1,1);	
+
+			// A run to complete?
+			if (run>1)
+				{
+				// A run of at least 2 is a range. Append 'last' UID.
+				command->Des().Append(_L8(":"));
+				command->Des().AppendNum(last);
+				}
+
+			// Append flags & send command
+			command->Des().Append(_L8(" +FLAGS (\\Deleted)"));
+			SendMessageL(command->Des());
+			iState=EImapStateDeleteMarkWait;
+			}
+		else
+			{
+			// Nothing to do: Just close the folder
+			iState=EImapStateCloseWait;
+			SendMessageL(KIMAPC_CLOSE);
+			}
+
+		// Get rid of command buffer
+		CleanupStack::PopAndDestroy();
+		}
+	}
+
+// Orphan a local message
+void CImImap4Session::OrphanMessageL(const TMsvId aMessage)
+	{
+	DBG((LogText(_L8("OrphanMessageL(%x)"),aMessage)));
+
+	// We no longer orphan the messages based on whether they have been downloaded.
+ 	// That was just delaying the inevitable and causing other problems
+ 	DeleteMessageL(aMessage);
+	DBG((LogText(_L8("  Deleting message"))));
+	}
+
+// Delete a local message
+void CImImap4Session::DeleteMessageL(const TMsvId aMessage)
+	{
+	DBG((LogText(_L8("CImImap4Session::DeleteMessageL(%x)"),aMessage)));
+
+ 	if(aMessage == KMsvNullIndexEntryId)
+		{
+		DBG((LogText(_L8("Attempted delete of null entry(%d)"),aMessage)));
+ 		return;
+		}
+	// Delete message and all subparts: first, move to parent
+	DBG((LogText(_L8("  SetEntry(%x)"),aMessage)));
+	SetEntryL(aMessage);
+
+	DBG((LogText(_L8("  SetEntry(%x)"),iEntry->Entry().Parent())));
+	SetEntryL(iEntry->Entry().Parent());
+
+	// Do it
+	DBG((LogText(_L8("  About to DeleteEntry(%x)"),aMessage)));
+	// Do not leave when entry is in use 
+	TInt err (iEntry->DeleteEntry(aMessage));
+	if(err==KErrInUse)
+		{
+		DBG((LogText(_L8("CImImap4Session::DeleteMessageL() dont leave if err = KErrInUse"))));
+		}
+	else
+		{
+		User::LeaveIfError(err);
+		}
+	
+	DBG((LogText(_L8("  Done!"))));
+	}
+
+// Get MESSAGE ONLY children of a folder. Ignore shadows as they are
+// not going to be synced against the server
+void CImImap4Session::GetMessageChildrenL(const TMsvId aFolder, CMsvEntrySelection* aChildren)
+	{	
+	// Get *all* the children
+	SetEntryL(aFolder);
+	GetChildrenL(*aChildren);
+
+	if(iCachedEntryData)
+		{
+		delete iCachedEntryData;
+		iCachedEntryData = 0;
+		}
+	iCachedEntryData = new(ELeave) CArrayFixFlat<TMsvCacheData>(5);
+
+	// Go through them, checking to see if they're messages and removing ones that aren't
+	TInt pos=0;
+	while(pos<aChildren->Count())
+		{
+		TMsvEntry* entryPtr;
+		TMsvId id = (*aChildren)[pos];
+		User::LeaveIfError(iEntry->GetEntryFromId(id,entryPtr));
+
+		// Is it a message? And is it real (not shadow)
+		if (entryPtr->iType!=KUidMsvMessageEntry ||
+			entryPtr->iRelatedId != KMsvNullIndexEntryId )
+			{
+			// No, remove it
+			aChildren->Delete(pos,1);
+			}
+		else
+			{
+			//cache two parts of the TMsvEntry data to avoid having to refind it later
+			TMsvCacheData data;
+			data.iOrphan = ((TMsvEmailEntry)(*entryPtr)).Orphan();
+			data.iUid = ((TMsvEmailEntry)(*entryPtr)).UID();
+			iCachedEntryData->AppendL(data);
+			// Next entry
+			pos++;
+			}
+		}
+	}
+
+// Synchronise a folder
+void CImImap4Session::Synchronise(TRequestStatus& aRequestStatus, TBool aNewOnly)
+	{
+        DBG((LogText(_L8("CImImap4Session::Synchronise()"))));
+	TInt err=KErrNone;
+	if (!Connected())
+		{
+		Queue(aRequestStatus);
+		err=KErrDisconnected;
+		}
+	else
+		TRAP(err,SynchroniseL(aRequestStatus, aNewOnly));
+	if (err!=KErrNone)
+                {
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::Synchronise(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+
+		Complete(err);
+	        }
+       }
+
+void CImImap4Session::SynchroniseL(TRequestStatus& aRequestStatus, TBool aNewOnly)
+	{
+	__ASSERT_DEBUG(iState==EImapStateSelected,gPanic(ESyncWhenNotSelected));
+	if(!(iState==EImapStateSelected))
+		{
+		User::LeaveIfError(KErrArgument);// Sync when not selected .
+		}
+	
+	Queue(aRequestStatus);
+
+	DoSynchroniseL(aNewOnly);
+	}
+
+void CImImap4Session::DoSynchroniseL(TBool aNewOnly)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND Synchronise"))));
+	__ASSERT_DEBUG(iState==EImapStateSelected,gPanic(ESyncWhenNotSelected));
+	if(!(iState==EImapStateSelected))
+			{
+			User::LeaveIfError(KErrArgument);// Sync when not selected .
+			}
+	// clear flags that may have been be set by SELECT or NOOP to say
+	// that a sync is required
+	iMailboxReceivedExists=EFalse;
+	iMailboxReceivedExpunge=EFalse;
+	iMailboxReceivedFlags=EFalse;
+
+	// Some pre-bits that need doing - get the children & count them
+	SetEntryL(iMailboxId);
+
+	TMsvEmailEntry message=iEntry->Entry();
+	GetMessageChildrenL(iMailboxId,iSelection);
+	TInt noofchildren=iSelection->Count();
+
+	if (!aNewOnly && noofchildren)
+		{
+		// Delete any orphaned messages completely at this point
+		DBG((LogText(_L8("Looking for orphans in %d local messages"),noofchildren)));
+
+		TInt pos=0;
+		while(pos<noofchildren)
+			{
+			if((*iCachedEntryData)[pos].iOrphan)
+				{
+				DBG((LogText(_L8("  Deleting orphan %x"),(*iSelection)[pos])));
+
+				// Delete it
+				SetEntryL(iMailboxId);
+				iEntry->DeleteEntry((*iSelection)[pos]);				
+				// Remove it from selection
+				iSelection->Delete(pos,1);
+				noofchildren--;
+				}
+			else
+				{
+				// Move on to next entry
+				pos++;
+				}
+			}
+		}
+
+	// First thing we have to do: check the UIDVALIDITY of the mirror and the
+	// remote folder match. If not, we have to orphan everything in the mirror
+	// and start again.
+	// We also do this if there are 0 messages in the remote mailbox (0 EXISTS)
+	// and there are messages locally
+	if (!message.ValidUID() || iUidValidity!=message.UID() || iMailboxSize==0)
+		{
+		// They don't match: do we have local children?
+#ifdef PRINTING
+		if (!iMailboxSize)
+			LogText(_L8("No remote messages"));
+		else
+			LogText(_L8("UIDVALIDITY changed: local %u, remote %u"),
+			    message.UID(),iUidValidity);
+#endif
+
+		// If we were doing a new-only sync, change this to a full sync as the
+		// UIDVALIDITY shows major changes
+		aNewOnly=EFalse;
+
+		if (noofchildren)
+			{
+		    // We've got local children: orphan them
+			DBG((LogText(_L8("Orphaning %d local messages"),noofchildren)));
+
+			for(TInt a=0;a<noofchildren;a++)
+				{
+				// ...we should be skipping locally generated messages
+				OrphanMessageL((*iSelection)[a]);
+				}
+
+			// Reget the number of children as this may have changed due to
+			// the orphaning process
+			GetMessageChildrenL(iMailboxId,iSelection);
+			noofchildren=iSelection->Count();
+			}
+
+		// Now, we match the remote's UIDVALIDITY: reset the pointer as it may
+		// well have been used by the orphaning process above.
+		SetEntryL(iMailboxId);
+		if (message.UID()!=iUidValidity || !message.ValidUID())
+			{
+			// Do the change if necessary
+			message.SetUID(iUidValidity);
+			message.SetValidUID(ETrue);
+			ChangeEntryBulkL(message);
+			}
+		}
+
+	// We've processed none of the remote messages yet
+	iMsgsDone=0;
+
+	// Any remote messages? If not, complete now as there's nothing else to do
+	if (iMailboxSize==0)
+		{
+		// This folder is now sync'ed
+		// No need to set seen flags as no messages in remote mailbox
+		SyncCompleteL(); 
+		DBG((LogText(_L8("CImap4Session::DoSynchroniseL(): iMailboxSize=0, folder now synched"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::DoSynchroniseL(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+		Complete(KErrNone);
+		return;
+		}
+
+	// Start the synchronise with sync'ing old messages: are there any
+	// messages in our mirror folder?
+	DBG((LogText(_L8("CImImap4Session::DoSynchroniseL(): Setting iState to EImapStateSynchroniseWait"))));
+ 	iState=EImapStateSynchroniseWait;
+	iSomeUnread=EFalse;
+	iHighestUid=0;
+
+	// Zero the "missing" message range limits.
+	iMissingUidLow=0;
+	iMissingUidHigh=0;
+
+	// Clear RX byte counter
+	iImapIO->RXbytes(ETrue);
+
+	// Any children?
+	iFolderIndex.Reset();
+	if (noofchildren>0) 
+		{
+		// Children exist, we need to do an old-sync to check all the messages
+		// are still there.
+		
+		// Build an index of UIDs/TMsvIds currently in the mirror folder, and
+		// sort this by UID: this is the order in which we expect the fetch to
+		// return UIDs - any missing have been deleted on the server. They may
+		// well not be in UID order in the index because locally-appended
+		// messages will not have been added to the index in UID order.
+		TRAPD(err,MakeSortedFolderIndexL(ETrue));
+		if (err!=KErrNone)
+			{
+			DBG((LogText(_L8("CImap4Session::DoSynchroniseL(): children exist, need to do old sync"))));
+			DBG((LogText(_L8("-----------------------------------------------------------"))));
+			DBG((LogText(_L8("CImap4Session::DoSynchroniseL(): calling Complete()"))));
+			DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+			Complete(err);
+			return;
+			}
+
+		// Find the highest UID in the index
+		iHighestUid=iFolderIndex[noofchildren-1].iUid;
+		}
+
+	// Retrieve folder synchronisation limit.
+	if (iEntry->Entry().Parent()==iServiceId && iEntry->Entry().iDetails.CompareF(KIMAP_INBOX)==0)
+		{
+		DBG((LogText(_L8("Folder sync of inbox folder"))));
+
+		// Leave iSyncLimit at the maximum if a Search String is set
+		// If no Search String is set and this is the inbox, then use the inbox sync limit.
+		if(iServiceSettings->SearchString().Length() == 0)
+			{
+			iSyncLimit=iServiceSettings->InboxSynchronisationLimit();	
+			}
+		}
+	else
+		{
+		// Otherwise use the folder sync limit.
+		// Leave iSyncLimit at the maximum if a Search String is set
+		DBG((LogText(_L8("Folder sync of non-inbox folder"))));
+		
+		if(iServiceSettings->SearchString().Length() == 0)
+			{
+			iSyncLimit=iServiceSettings->MailboxSynchronisationLimit();
+			}
+		}
+		
+	// Get the user defined UID SEARCH string if there is one
+	// Do a refined search if there's a string
+	if(iServiceSettings->SearchString().Length() != 0)
+		{
+		iSyncState=ESyncSearch;
+		iFolderPosition=0;
+		iSearchList->Reset();
+		NewTag();
+		// Refined search
+		_LIT8(KSearchString,"%d UID SEARCH 1:%d %S\r\n");
+		TPtrC8 ptr = iServiceSettings->SearchString();
+		iImapIO->SendL(iStatus,KSearchString,iTag,Min(iMailboxSize,KImapUidSearchSize),&ptr);
+		NewTagSent();
+		return;
+		}
+	else // if no search string we use the old behaviour
+	// Check the folder synchronisation limit.
+	if (iSyncLimit>KImImapSynchroniseNone)
+		{
+		DBG((LogText(_L8("Folder sync limited to %d messages"),iSyncLimit)));
+
+		// Limited folder synchronisation, perform a UID search.
+		iSyncState=ESyncSearch;
+		iFolderPosition=0;
+
+		// Reset the search list.
+		iSearchList->Reset();
+
+		// Perform a UID search on this folder.
+		NewTag();
+		iImapIO->SendL(iStatus,_L8("%d UID SEARCH 1:%d\r\n"),iTag,Min(iMailboxSize,KImapUidSearchSize));
+		NewTagSent();
+		return;
+		}
+	else if (iSyncLimit==KImImapSynchroniseNone)
+		{
+		DBG((LogText(_L8("No folder sync required"))));
+
+		// No synchronisation required.
+		// This folder is now sync'ed
+		SyncCompleteL();
+		// Back to selected state
+		iState=EImapStateSelected;
+		iSyncState=ENotSyncing;
+		DBG((LogText(_L8("CImap4Session::DoSynchroniseL(): iSyncLimit=KImImapSynchroniseNone, no sync required"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::DoSynchroniseL(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+ 
+		Complete(KErrNone);
+		return;
+		}
+	else if (iSyncLimit<=KImImapSynchroniseAll)
+		{
+		DBG((LogText(_L8("Full folder sync required"))));
+   
+		// Full synchronisation required - fall through.
+		}
+
+	if (noofchildren>0) 
+		{
+		if (!aNewOnly && iHighestUid>0)
+			{
+			// Do old sync
+			iSyncState=ESyncOld;
+			iFolderPosition=0;
+			NewTag();
+			// If a UID Search String is used it looks like this is FULL sync only 
+			// so leave as is
+			iImapIO->SendL(iStatus,_L8("%d UID FETCH 1:%d (UID FLAGS)\r\n"),
+						  iTag,iHighestUid);
+			NewTagSent();
+			return;
+			}
+		}
+
+	// Do new sync
+	SynchroniseNewL();
+	}
+
+void CImImap4Session::ColonSeparatorToSpace(TDes8& buf)
+	{
+	TInt colon;
+	while ((colon = buf.Locate(':')) != KErrNotFound)
+		buf.Replace(colon,1,_L8(" "));
+	buf.TrimRight();
+	}
+
+// Do second stage of sync with uid list, ie new synchronise.
+void CImImap4Session::SynchroniseNewL(const TUint32 aLowUid,const TUint32 aHighUid)
+	{
+        DBG((LogText(_L8("CImImap4Session::SynchroniseNewL()"))));
+ 	iSyncState=ESyncNew;
+	iCheckDiskSpaceCounter = 0;
+	iFolderPosition = 0;
+
+	// First, resize folder index to hold all messages in the folder,
+	// as opposed to the old sync list. This will preserve the old
+	// contents of the index, which is what we want as it's up-to-date
+	// and correct.
+	iFolderIndex.SetSizeL(iMailboxSize);
+	
+	DBG((LogText(_L8("Synchronising new messages (UIDs %u to %u)"),aLowUid,aHighUid)));
+
+	// Create list of priority fields to request
+	TBuf8<256> priorityFields;
+	CDesC8ArrayFlat* array = new(ELeave) CDesC8ArrayFlat(4);
+	CleanupStack::PushL(array);
+	CImcvUtils::PriorityFieldsL(*array);
+	for (TInt i=0; i<array->Count(); i++)
+		{
+		priorityFields.Append((*array)[i]);
+		}
+	CleanupStack::PopAndDestroy(array);
+	ColonSeparatorToSpace(priorityFields);
+	
+	// Send command
+	NewTag();
+
+	// If a UID search string has been specified, the we should create the UID FETCH
+	// string from the UID integer list.
+	if(iServiceSettings->SearchString().Length() != 0)
+		{
+		CreateUidStringL();
+		TPtrC8 ptr(iUidString->Des());
+		iImapIO->SendL(iStatus,KImapFetchSmallHeaderRangeRefined,iTag,&ptr, &priorityFields);
+		}
+	else
+		{
+		iImapIO->SendL(iStatus,KImapFetchSmallHeaderRange,iTag,aLowUid,aHighUid, &priorityFields);
+		}
+	NewTagSent();
+	}
+
+// Do second stage of sync, ie new synchronise.
+void CImImap4Session::SynchroniseNewL()
+	{
+	iSyncState=ESyncNew;
+	iCheckDiskSpaceCounter = 0;
+	iFolderPosition = 0;
+
+	// First, resize folder index to hold all messages in the folder,
+	// as opposed to the old sync list. This will preserve the old
+	// contents of the index, which is what we want as it's up-to-date
+	// and correct.
+	iFolderIndex.SetSizeL(iMailboxSize);
+
+	// fetch just the header of the new mails
+	FetchHeaderL(iHighestUid+1);
+	}
+
+// Build the fetch list
+void CImImap4Session::AddFetchItemL(TMsvId aPart, TImap4GetMailOptions aPartTypes, TBool& aHasTextParts)
+	{
+	DBG((LogText(_L8("AddFetchItemL(id %x, parts=%d)"),aPart,aPartTypes)));
+
+	// Is this part fetchable?
+	SetEntryL(aPart);
+
+	// if the part is complete, then this means everything below it is
+	// complete and therefore we don't need to fetch anything.
+	if ( iEntry->Entry().iType != KUidMsvFolderEntry && iEntry->Entry().Complete()
+		&& !(((TMsvEmailEntry)iEntry->Entry()).PartialDownloaded()))
+		{
+		DBG((LogText(_L8("Skipping, already complete"))));
+
+		// If this is an attachment which has been marked complete because it has
+  		// zero size, we still need to add it to the attachment manager.
+ 		if ((iEntry->Entry().iType == KUidMsvAttachmentEntry ||
+ 		      iEntry->Entry().iType == KUidMsvEmailExternalBodyEntry) &&
+ 		     (iEntry->Entry().iSize == 0))
+ 			{
+ 				DBG((LogText(_L8("Creating zero length attachment"))));
+   				CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry());
+  			}
+
+		return;
+		}
+
+	TBool addChildren = EFalse;
+	TBool addPart = EFalse;
+	
+	TUid type = iEntry->Entry().iType;
+	if (type == KUidMsvFolderEntry || type == KUidMsvMessageEntry)
+		{
+		// don't fetch anything - just let it recurse
+		addChildren = ETrue;
+		}
+	else if (type == KUidMsvEmailTextEntry || type == KUidMsvEmailHtmlEntry)
+		{
+		aHasTextParts = ETrue;
+		
+		if (aPartTypes == EGetImap4EmailBodyText ||
+			aPartTypes == EGetImap4EmailBodyTextAndAttachments ||
+			aPartTypes == EGetImap4EmailBodyAlternativeText)
+			{
+			addPart = ETrue;
+			}
+		}
+	else if (type == KUidMsvAttachmentEntry || type == KUidMsvEmailExternalBodyEntry)
+		{
+		if (aPartTypes == EGetImap4EmailBodyTextAndAttachments ||
+			aPartTypes == EGetImap4EmailAttachments)
+			{
+			addPart = ETrue;
+			}
+		else
+			{
+			SetEntryL(iEntry->Entry().Parent());
+			TImEmailFolderType folderType = static_cast<TMsvEmailEntry>((iEntry->Entry())).MessageFolderType();
+			SetEntryL(aPart);
+			
+			if( folderType==EFolderTypeRelated )
+				{
+			// if asked for bodytext and it is an attachment then
+			// fetch it if attachment is in a folder of
+			// Multipart/Related as it is most likely part of an MHTML
+			// document
+				addPart = ETrue;
+				}
+			else if( ( folderType == EFolderTypeAlternative || folderType == EFolderTypeUnknown ) && aPartTypes == EGetImap4EmailBodyAlternativeText)
+				{
+				// if non-HTML text alternative parts are requested, the alternative
+				// folder is checked and get the mime content type for the part
+				CMsvStore* store = iEntry->ReadStoreL();
+				CleanupStack::PushL(store);
+				CImMimeHeader* mimeHeaders = CImMimeHeader::NewLC();
+				mimeHeaders->RestoreL(*store);
+				
+				if( mimeHeaders->ContentType().CompareF(KMIME_TEXT)==0 )
+					{
+					// This is a alternative text part, and should be treated
+					// as a text part
+					addPart = ETrue;
+					}
+					
+				CleanupStack::PopAndDestroy(2, store); // mimeHeaders, store
+				}
+
+			// Store needs to be closed before calling CreateAttachmentInfoL
+			if(!addPart)
+				{
+				CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry());
+				}		
+			}
+		}
+	else
+		{
+		__ASSERT_DEBUG(0, gPanic(EUnknownMsvType));
+
+		// for anything else, if not debug mode then fetch anyway
+		addPart = ETrue;
+		}
+
+	if (addPart)
+		{
+		iFetchList->AppendL(aPart);
+		
+		// Add this part's size to the size total
+		iProgress.iBytesToDo+=iEntry->Entry().iBioType;
+		}
+	
+	if (addChildren)
+		{
+		// Check the children
+		CMsvEntrySelection *selection=new (ELeave) CMsvEntrySelection;
+		CleanupStack::PushL(selection);
+		GetChildrenL(*selection);
+		
+		DBG((LogText(_L8("...ID %x has %d children"),iEntry->Entry().Id(),selection->Count())));
+		
+		for(TInt a=0;a<selection->Count();a++)
+			{
+			// Process child
+			AddFetchItemL((*selection)[a],aPartTypes,aHasTextParts);
+			}
+		CleanupStack::PopAndDestroy();
+		}
+	}
+
+void CImImap4Session::AddFetchItemL(TMsvId aPart, TImImap4GetPartialMailInfo aGetPartialMailInfo, TBool& aHasTextParts)
+	{
+	DBG((LogText(_L8("AddFetchItemL(id %x, parts=%d)"),aPart,aGetPartialMailInfo.iPartialMailOptions)));
+
+	// Is this part fetchable?
+	SetEntryL(aPart);
+
+	//if the part is complete, then this means everything below it is
+	// complete and therefore we don't need to fetch anything.
+	if (iEntry->Entry().iType != KUidMsvFolderEntry && iEntry->Entry().Complete())
+		{
+		DBG((LogText(_L8("Skipping, already complete"))));
+		// If this is an attachment which has been marked complete because it has
+  		// zero size, we still need to add it to the attachment manager.
+ 		if ((iEntry->Entry().iType == KUidMsvAttachmentEntry ||
+ 		      iEntry->Entry().iType == KUidMsvEmailExternalBodyEntry) &&
+ 		     (iEntry->Entry().iSize == 0))
+ 			{
+ 				DBG((LogText(_L8("Creating zero length attachment"))));
+   				CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry());
+	  		}
+
+		return;
+		}
+
+	TBool addChildren = EFalse;
+	TBool addPart = EFalse;
+	
+	TUid type = iEntry->Entry().iType;
+	if (type == KUidMsvFolderEntry || type == KUidMsvMessageEntry)
+		{
+		// don't fetch anything - just let it recurse
+		addChildren = ETrue;
+		}
+	else if (type == KUidMsvEmailTextEntry) 
+		{
+		aHasTextParts = ETrue;
+		
+		if(aGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits)
+			addPart = ETrue;
+		else if (aGetPartialMailInfo.iPartialMailOptions == ECumulative)
+			{
+			if(iGetPartialMailInfo.iTotalSizeLimit > 0)
+				{
+				addPart = ETrue;
+				iBodyTextSize = iEntry->Entry().iBioType;
+				}
+			}
+		else if (aGetPartialMailInfo.iPartialMailOptions == EBodyTextOnly ||
+				aGetPartialMailInfo.iPartialMailOptions == EBodyTextAndAttachments ||
+				aGetPartialMailInfo.iPartialMailOptions == EBodyAlternativeText )
+			{
+			addPart = ETrue;
+			iBodyTextSize = iEntry->Entry().iBioType;
+			}
+		}
+	else if (type == KUidMsvEmailHtmlEntry)
+		{
+		aHasTextParts = ETrue;
+
+		iHtmlEntrySize = iEntry->Entry().iBioType;
+		if(aGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits)
+			addPart = ETrue;
+		else if (aGetPartialMailInfo.iPartialMailOptions == ECumulative)
+			{
+			if((iGetPartialMailInfo.iTotalSizeLimit > 0 ) && 
+				((iBodyTextSize + iEntry->Entry().iBioType) <= iGetPartialMailInfo.iTotalSizeLimit))
+				{
+				addPart = ETrue;
+				}
+			}
+		else if (aGetPartialMailInfo.iPartialMailOptions == EBodyTextOnly ||
+				aGetPartialMailInfo.iPartialMailOptions == EBodyTextAndAttachments ||
+				aGetPartialMailInfo.iPartialMailOptions == EBodyAlternativeText )
+				
+			{	
+			if(iBodyTextSize + iEntry->Entry().iBioType <=
+				Minimum(iGetPartialMailInfo.iBodyTextSizeLimit,iGetPartialMailInfo.iTotalSizeLimit))
+				{
+				addPart = ETrue;
+				}
+			}
+		// In case of html entry, store html entry id to check later,(when attaching partial footer 
+		// message)if whole body text is downloaded and the html size is not to be downloaded 
+		if(addPart)
+			iHtmlEntryPart = aPart;
+		}
+	else if (type == KUidMsvAttachmentEntry || type == KUidMsvEmailExternalBodyEntry)
+		{
+		
+		if(aGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits)
+			addPart = ETrue;
+		else if (aGetPartialMailInfo.iPartialMailOptions == ECumulative)
+			{
+			if(iGetPartialMailInfo.iTotalSizeLimit > 0 && 
+				((iBodyTextSize + iSizeOfToBeFetchedAttachments + iEntry->Entry().iBioType) <= iGetPartialMailInfo.iTotalSizeLimit))
+				{
+				addPart = ETrue;
+				if((iBodyTextSize + iSizeOfToBeFetchedAttachments + iEntry->Entry().iBioType + iHtmlEntrySize) 
+						>= iGetPartialMailInfo.iTotalSizeLimit)
+					{
+					RemoveHtmlPart(iHtmlEntryPart);
+					}
+				iSizeOfToBeFetchedAttachments+=iEntry->Entry().iBioType;
+				}	
+			else
+				{
+				CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry());
+				// for Ecumulative option ,after the body part downloading, check if there is any 
+				// attachment which can be downloaded , then check if the html part can be included.
+				if((iBodyTextSize + iSizeOfToBeFetchedAttachments + iHtmlEntrySize) >= iGetPartialMailInfo.iTotalSizeLimit)
+					{
+					RemoveHtmlPart(iHtmlEntryPart);
+					}
+				}
+			}
+		else if (aGetPartialMailInfo.iPartialMailOptions == EAttachmentsOnly ||
+				aGetPartialMailInfo.iPartialMailOptions == EBodyTextAndAttachments)
+				
+			{
+			if(iEntry->Entry().iBioType <= 
+				Minimum(iGetPartialMailInfo.iAttachmentSizeLimit,iGetPartialMailInfo.iTotalSizeLimit))
+				{
+				addPart = ETrue;
+				}
+			else
+				{
+				CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry());
+				}
+			}
+		else
+			{
+			SetEntryL(iEntry->Entry().Parent());
+			TImEmailFolderType folderType = static_cast<TMsvEmailEntry>((iEntry->Entry())).MessageFolderType();
+			SetEntryL(aPart);
+			
+			if( folderType==EFolderTypeRelated )
+				{
+				// if asked for bodytext and it is an attachment then
+				// fetch it if attachment is in a folder of
+				// Multipart/Related as it is most likely part of an MHTML
+				// document
+				addPart = ETrue;
+				}
+			else if( folderType==EFolderTypeAlternative && 
+					 aGetPartialMailInfo.iPartialMailOptions==EBodyAlternativeText &&
+					 iEntry->Entry().iBioType <= Minimum(iGetPartialMailInfo.iAttachmentSizeLimit,
+					 									 iGetPartialMailInfo.iTotalSizeLimit) )
+				{
+				// if non-HTML text alternative parts are requested, the alternative
+				// folder is checked and get the mime content type for the part
+				CMsvStore* store = iEntry->ReadStoreL();
+				CleanupStack::PushL(store);
+				CImMimeHeader* mimeHeaders = CImMimeHeader::NewLC();
+				mimeHeaders->RestoreL(*store);
+				
+				if( mimeHeaders->ContentType().CompareF(KMIME_TEXT)==0 )
+					{
+					// This is a alternative text part, and should be treated
+					// as a text part
+					addPart = ETrue;
+					}
+					
+				CleanupStack::PopAndDestroy(2, store); // mimeHeaders, store
+				}
+				
+			if(!addPart)
+				{
+				CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry());
+				}
+			}
+		}
+	else
+		{
+		__ASSERT_DEBUG(0, gPanic(EUnknownMsvType));
+
+		// for anything else, if not debug mode then fetch anyway
+		addPart = ETrue;
+		}
+
+	if (addPart)
+		{
+		iFetchList->AppendL(aPart);
+		// Add this part's size to the size total
+		iProgress.iBytesToDo+=iEntry->Entry().iBioType;
+		}
+
+	if (addChildren)
+		{
+		// Check the children
+		CMsvEntrySelection *selection=new (ELeave) CMsvEntrySelection;
+		CleanupStack::PushL(selection);
+		GetChildrenL(*selection);
+			
+		DBG((LogText(_L8("...ID %x has %d children"),iEntry->Entry().Id(),selection->Count())));
+		
+		for(TInt a=0;a<selection->Count();a++)
+			{
+			// Process child
+			AddFetchItemL((*selection)[a],aGetPartialMailInfo,aHasTextParts);
+			}
+		CleanupStack::PopAndDestroy();
+		}
+	}
+
+void CImImap4Session::RemoveHtmlPart(TMsvId aPart)
+	{
+	// removes the html part from the download list only if it exists in the list
+	if(aPart)
+		{
+		TInt aIndex = 0;
+		TKeyArrayFix sortKey(0, ECmpTInt32);
+		iFetchList->Find(aPart,sortKey,aIndex);
+		iFetchList->Delete(aIndex,1);
+		iHtmlEntryPart=0;
+		}
+	}
+
+// Checks for the minimum size limit between message type size limit 
+// (attachment size limit/body text sizelimit)
+TInt32 CImImap4Session::Minimum(TInt32 aThisPartTypeSizeLimit,TInt32 aTotalMailSizeLimit)
+	{
+	if(aTotalMailSizeLimit > 0)
+		{
+		if(aThisPartTypeSizeLimit > aTotalMailSizeLimit)
+			return aTotalMailSizeLimit;
+		else
+			return aThisPartTypeSizeLimit;
+		}
+	else
+		return aThisPartTypeSizeLimit;
+	}
+
+// Issue command to fetch an item in the fetch list
+void CImImap4Session::FetchAnItemL(const TMsvId aPart)
+	{
+	DBG((LogText(_L8("FetchAnItemL(%x)"),aPart)));
+
+	// set the iFoundUid member variable to false to show that a UID has not been
+	// found in the fetch response yet
+	iFoundUid = EFalse;
+
+	// Get part ID and read the MIME header so we can work out encoding style
+	iMessageId=aPart;
+	SetEntryL(iMessageId);
+	CMsvStore *store=iEntry->ReadStoreL();
+	CleanupStack::PushL(store);
+	
+	iIsDiskSpaceChecked = EFalse;
+	// Get MIME header
+	if (!iAttachmentMimeInfo)
+		iAttachmentMimeInfo=CImMimeHeader::NewL();
+	iAttachmentMimeInfo->RestoreL(*store);
+		
+
+	// We don't reset the stats here, as they're stats for a single
+	// fetch operation, which may include multiple parts of the same
+	// message. Stats initialisation is done in the FetchBody()
+	// function.
+
+	// Find the UID we need to fetch
+	TMsvEmailEntry entry = iEntry->Entry();
+	iMessageFetching=entry.UID();
+
+	// check there is enough disk space for this part (plus slop)
+	// iBioType contains remote size which will never be less than the
+	// local size so is a safe value to use
+	if(!iFetchPartialMail)
+		{
+		CheckForDiskSpaceL(entry.iBioType);
+		}
+
+	// Save encoding type
+	iEncodingType=iAttachmentMimeInfo->ContentTransferEncoding();
+	iB64Decoder.Initialise();
+
+	// Clear QP buffer
+	if (iPartialLine && iPartialLine->Length())
+		iPartialLine->Des().Zero();
+
+	// Is this a text fetch? (if so, save as a richtext in the database, as
+	// opposed to a separate file). Default to being an attachment.
+
+	// SJM: This code used to check for an attachment filename and
+	// force FetchIsTest False in that case. This is unnecessary as
+	// this check is already done in BuildTreeOne and reflected in the
+	// entry.iType.
+
+	iFetchIsText = EFalse;
+	if (entry.iType == KUidMsvEmailTextEntry)
+		{
+		iFetchIsText = ETrue;
+		// New message body
+		if (iStore8BitData)
+			{
+			delete iBodyBuf;
+			iBodyBuf = NULL;
+			iBodyBuf = CBufSeg::NewL(KBodyTextChunkSizeBytes);
+			delete iBodyText;
+			iBodyText = NULL;
+			iBodyText = CMsvBodyText::NewL();
+			}
+		else
+			{
+			delete iMessageBody;
+			iMessageBody = NULL;
+			iMessageBody=CRichText::NewL(iParaLayer, iCharLayer);
+			}
+		}
+
+	// Waiting for size
+	iSizeWait=ETrue;
+
+	// Size of item (mainly for CC:Mail bug workaround) */
+	iSizeOfThisPart=entry.iBioType;
+
+	// if going into richtext only then setup charset conversion
+	iPreparedToConvert=EFalse;
+	TInt fetchSizeBytes = static_cast<TInt>(iServiceSettings->FetchSize()); 
+	// If this was a partially fetched message then there is some already fetched 
+	// message content in the message store
+	TInt32 fetchSize = fetchSizeBytes;
+
+	if (iFetchIsText)
+		{
+
+		TUint charsetId = iAttachmentMimeInfo->MimeCharset();
+		
+		if (iStore8BitData)
+			{
+			iBodyText->SetDefaultCharacterSet(iCharConv->SystemDefaultCharset());
+			if (charsetId == KUidMsvCharsetNone)
+				iBodyText->SetCharacterSet(0);
+			else
+				iBodyText->SetCharacterSet(charsetId);
+			}
+		else
+			{
+			if (charsetId == KUidMsvCharsetNone)
+				charsetId = iCharConv->SystemDefaultCharset();
+			}
+		
+		iAttachmentMimeInfo->SetMimeCharset(charsetId);
+		if (!iStore8BitData)
+			{
+			if (charsetId != KUidMsvCharsetNone)
+				iPreparedToConvert = iCharConv->PrepareToConvertToFromOurCharsetL(charsetId);
+			}
+		}
+
+	// ensure nothing left over
+	iLeftOver.SetLength(0);
+
+	DBG((LogText(_L8("Starting fetch for body part (MsvId=%x, iSizeOfThisPart=%d, iFetchIsText=%d, convert=%d)"),
+				 iMessageId,iSizeOfThisPart,iFetchIsText, iPreparedToConvert)));
+
+	// Issue fetch command
+	NewTag();
+	TPtrC8 path=iAttachmentMimeInfo->RelativePath();
+
+	if(iFetchPartialMail)
+		fetchSize = GetFetchSizeL(iSizeOfThisPart,0); // As this is the first time 0 size downloaded already
+
+	if(fetchSize > fetchSizeBytes)
+		fetchSize = fetchSizeBytes;
+	
+	//Some servers dont support MIME.And it contains single body type.
+	//Using KImapFetchBodyPeek/KImapFetchBody to fetch the body of mails.
+	if(iProgress.iPartsToDo == 1)
+		{
+		if (iServiceSettings->UpdatingSeenFlags())
+			{
+			iImapIO->SendL(iStatus,KImapFetchBodyPeek, iTag,iMessageFetching,&path,0,fetchSize);
+			}
+		else
+			{
+			iImapIO->SendL(iStatus,KImapFetchBody, iTag,iMessageFetching,&path,0,fetchSize);
+			}
+		}
+	else
+		{
+		if (iServiceSettings->UpdatingSeenFlags())
+			{
+			iImapIO->SendL(iStatus,KImapFetchMimeBodyPeek, iTag,iMessageFetching,&path,fetchSize,&path);	
+			}		
+		else
+			{
+			iImapIO->SendL(iStatus,KImapFetchMimeBody, iTag,iMessageFetching,&path,fetchSize,&path);
+			}
+		}
+	NewTagSent();
+	CleanupStack::PopAndDestroy();
+	}
+
+void CImImap4Session::CheckForDiskSpaceL(TInt aSizeToBeDownloaded)
+	{
+	TInt needSpace = 0;
+	TVolumeInfo volumeInfo;
+	User::LeaveIfError(iFs.Volume(volumeInfo, iCurrentDrive));
+
+	needSpace = KMinimumDiskSpaceForSync + aSizeToBeDownloaded;
+	if (volumeInfo.iFree < needSpace)
+		User::Leave(KErrDiskFull);
+	}
+
+// Fetch body for partial download
+void CImImap4Session::FetchBody(TRequestStatus& aRequestStatus, const TMsvId aPart,
+								TImImap4GetPartialMailInfo aGetPartialMailInfo)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND FetchBody(%x)"),aPart)));
+	TInt err=KErrNone;
+
+	CheckForPartialPopulate(aGetPartialMailInfo);
+	if (!Connected())
+		{
+		Queue(aRequestStatus);
+		err=KErrDisconnected;
+		}
+	else
+		{
+		TRAP(err,FetchBodyL(aRequestStatus, aPart));
+		}
+	if (err!=KErrNone)
+                {
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+		DBG((LogText(_L8("CImap4Session::FetchBody(): calling Complete()"))));
+		DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+
+		Complete(err);
+                }
+	}
+
+// Checks if the partial mail download parameters are set to default 
+// and the full download mail option is set, then this is a request for full download. 
+void CImImap4Session::CheckForPartialPopulate(TImImap4GetPartialMailInfo aGetPartialMailInfo)
+	{
+	if(aGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits &&
+		aGetPartialMailInfo.iTotalSizeLimit == KMaxTInt &&
+		aGetPartialMailInfo.iBodyTextSizeLimit == KMaxTInt && 
+		aGetPartialMailInfo.iAttachmentSizeLimit == KMaxTInt && 
+		(aGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailHeaders || 
+		aGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailBodyText ||
+		aGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailBodyTextAndAttachments ||
+		aGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailAttachments ||
+		aGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailBodyAlternativeText))
+		{
+		DBG((LogText(_L8("Populate option = %d)"),aGetPartialMailInfo.iGetMailBodyParts)));
+		iFetchPartialMail = EFalse;
+		iGetOptions = aGetPartialMailInfo.iGetMailBodyParts;
+		}
+	else
+		{
+		DBG((LogText(_L8("Populate option = %d)"),aGetPartialMailInfo.iPartialMailOptions)));
+		iFetchPartialMail = ETrue;
+		iGetPartialMailInfo = aGetPartialMailInfo;
+		}
+	}
+
+void CImImap4Session::FetchBodyL(TRequestStatus& aRequestStatus, const TMsvId aPart)
+	{
+	__ASSERT_DEBUG(iState==EImapStateSelected,gPanic(EFetchWhenNotSelected));
+	if(!(iState==EImapStateSelected))
+		{
+		User::LeaveIfError(KErrArgument);// Fetch when not selected .
+		}
+	iGetPart = aPart;
+	
+	Queue(aRequestStatus);
+
+	// if we only want headers then there is nothing to do as they
+	// have already been fetched on synchronisation. We complete here
+	// and then the Compound object will copy the structure across
+	if(!iFetchPartialMail)
+		{
+		if (iGetOptions == EGetImap4EmailHeaders)
+			{
+                        DBG((LogText(_L8("-----------------------------------------------------------"))));
+			DBG((LogText(_L8("CImap4Session::FetchBodyL(): calling Complete()"))));
+			DBG((LogText(_L8("-----------------------------------------------------------"))));
+
+			Complete(KErrNone);
+			return;
+			}
+		}
+	// Get some basic info on what we're fetching
+	SetEntryL(aPart);
+
+	// First, check that we're in the right folder: this involves working up from the
+	// base part looking for a folder which matches the current iMailboxId. If we get
+	// to the service without finding the current folder, then we're not in the
+	// right place...
+  	TBool messageFound = EFalse;
+  	while((iEntry->Entry().Id()!=iMailboxId) && (!messageFound))
+		{
+		// Reached the service?
+		if (iEntry->Entry().iType==KUidMsvServiceEntry)
+			{
+			// Didn't find the folder - we must have the wrong one selected
+			Complete(KErrImapWrongFolder);
+
+			DBG((LogText(_L8("In the wrong folder!"))));
+
+			return;
+			}
+
+		// Reached the message ?
+		if (iEntry->Entry().iType==KUidMsvMessageEntry)
+			{
+			iMessageUid = ((TMsvEmailEntry&)iEntry->Entry()).UID();
+ 			iSyncState = EGettingStructure;
+  			iState = EImapStateFetchWait;
+  			if (!(iEntry->Entry().Owner()))
+  				{
+  				// If there are no child entries then we need to fetch the envelope and some headers (again.)
+  				// We can generate the message structure and header stores from the envelope and headers after they have been fetched.
+  				if (ImapIdleSupported()==EFalse)
+					{
+					CancelDummy();
+					}
+  				// We don't know the size of the body part yet as there is no structure.
+  				// The envelope will be fetched shortly so clear the progress information for now.
+  				// Set the iBytesToDo to 1 rather than 0 to avoid any possible division by 0 errors
+  				iProgress.iBytesToDo=1;
+  				iProgress.iBytesDone=0;
+  				FetchLargeHeaderL(iMessageUid, EFalse);
+  				}
+  			else
+  				{
+  				// If the structure is already present then do the fetch
+  				if (ImapIdleSupported()==EFalse)
+					{
+					CancelDummy();
+					}
+  				DoFetchL();
+  				messageFound = ETrue;
+  				}
+   			}
+
+		// Up a level
+		SetEntryL(iEntry->Entry().Parent());
+		}
+	}
+
+// Set/reset local subscribed flag
+void CImImap4Session::LocalSubscribeL(const TMsvId aTarget, const TBool aSubscribe)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND LocalSubscribe(%d,%d)"),aTarget,aSubscribe)));
+
+	// Set/reset subscribed flag
+	SetEntryL(aTarget);
+	TMsvEmailEntry message=iEntry->Entry();
+
+	// Change only if necessary
+	if (message.Subscribed()!=aSubscribe)
+		{
+		message.SetSubscribed(aSubscribe);
+		ChangeEntryL(message);
+		}
+	}
+
+// Set/reset remote subscribed flag
+void CImImap4Session::RemoteSubscribeL(TRequestStatus& aRequestStatus, const TMsvId aTarget, const TBool aSubscribe)
+	{
+	LOG_COMMANDS((LogText(_L8("COMMAND RemoteSubscribe(%d,%d)"),aTarget,aSubscribe)));
+	__ASSERT_DEBUG(iState==EImapStateNoSelect || iState==EImapStateSelected,gPanic(ESubscribeWhenNotReady));
+	if(!(iState==EImapStateNoSelect || iState==EImapStateSelected))
+		{
+		User::LeaveIfError(KErrInUse);// Subscribe when not ready.
+		}
+	Queue(aRequestStatus);
+	
+	// Save ID for updating subscription flag when the remote one completes
+	// successfully.
+	iCommandIds[0]=aTarget;
+	iCommandFlags[0]=aSubscribe;
+
+	// Get path to delete
+	HBufC8* path=NULL; // To stop .AER warnings...
+	TRAPD(err,path=MakePathL(aTarget,ETrue));
+	if (err!=KErrNone)
+		{
+		Complete(err);
+		return;
+		}
+	CleanupStack::PushL(path);
+	DoQuoteL(path);
+
+	// Set state
+	iSavedState=iState;
+	iState=EImapStateSubscribeWait;
+
+	// Send command
+	NewTag();
+	TPtrC8 pathdes(path->Des());
+	if(aSubscribe)
+		iImapIO->SendL(iStatus,_L8("%d %S\"%S\"\r\n"),iTag,&KIMAPC_SUBSCRIBE,&pathdes);
+	else
+		iImapIO->SendL(iStatus,_L8("%d %S\"%S\"\r\n"),iTag,&KIMAPC_UNSUBSCRIBE,&pathdes);
+	NewTagSent();
+
+	// Destroy buffer
+	CleanupStack::PopAndDestroy();
+	}
+
+// Reset sync/fetch stats
+void CImImap4Session::ResetStats()
+	{
+	// Reset all the stats
+	iHeadersFetched=0;
+	iOrphanedMessages=0;
+	iRemoteMessagesDeleteTagged=0;
+	iMsgsDone=0;
+
+	iProgress.iState = TImap4GenericProgress::EIdle;
+	iProgress.iImap4SubStateProgress = TImap4GenericProgress::EIdle;
+	iProgress.iMsgsToDo = iProgress.iMsgsDone = 0;
+	iProgress.iPartsToDo = iProgress.iPartsDone = 0;
+	iProgress.iBytesToDo = iProgress.iBytesDone = 0;
+	iProgress.iErrorCode = KErrNone;
+	iProgress.iReturnedMsvId = 0;
+	iProgress.iTotalSize = 0;
+	}
+
+// Return progress
+
+// Msgs ToDo/Done are only setup when doing a Synchronise command, ie
+// in state EImapStateSynchroniseWait.
+
+// Parts/Bytes ToDo/Done are set up and used in a FetchBody command,
+// ie in state EImapStateFetchWait.
+
+// BytesToDo/Done are used in Append command.
+
+void CImImap4Session::IncSyncStats(TImap4SyncProgress& aSync)
+	{
+	// do the synchronising stats
+	aSync.iHeadersFetched += iHeadersFetched;
+	aSync.iOrphanedMessages += iOrphanedMessages;
+	aSync.iRemoteMessagesDeleteTagged += iRemoteMessagesDeleteTagged;
+
+	if(iServiceSettings->SearchString().Length() != 0)
+		{
+		aSync.iMsgsToDo=iMailboxSize;	
+		}
+	else
+		{
+		aSync.iMsgsToDo=(iSyncLimit<=0)?iMailboxSize:Min(iMailboxSize,iSyncLimit);	
+		}
+	aSync.iMsgsDone = Min(iMsgsDone,aSync.iMsgsToDo);
+	}
+
+TImap4GenericProgress CImImap4Session::Progress()
+	{
+	// update the state with what we're doing
+	if (iState==EImapStateDisconnected)
+		{
+		iProgress.iState=TImap4GenericProgress::EDisconnected;
+		}
+	else if (iState<EImapStateNoSelect)
+		{
+		iProgress.iState = TImap4GenericProgress::EConnecting;
+		// If we're in the connecting state, get the stage and IAP values
+		//  and store them in iMsgsDone and iMsgsToDo to be accessed via
+		//  TImap4GenericProgress::ConnectionState() and TImap4GenericProgress::ConnectionIAP()
+		iProgress.iMsgsDone = iImapIO->GetConnectionStage();
+		TUint32 iap;
+		TInt err = iImapIO->GetIAPValue(iap);
+		if (err == KErrNone)
+			{
+			iProgress.iMsgsToDo = iap;
+			}
+		else
+			{
+			iProgress.iMsgsToDo = err;
+			}
+		}
+	else if (iState==EImapStateNoSelect || iState==EImapStateSelected || iState==EImapStateIdling)
+		{
+		iProgress.iState=TImap4GenericProgress::EIdle;
+		}
+	else
+		{
+		switch(iState)
+			{
+		case EImapStateFetchWait:
+			iProgress.iState=TImap4GenericProgress::EFetching;
+			break;
+
+		case EImapStateAppendSizeWait:
+		case EImapStateAppendPromptWait:
+		case EImapStateAppendWait:
+		case EImapStateAppendResultWait:
+			iProgress.iState=TImap4GenericProgress::EAppending;
+			break;
+
+		case EImapStateSynchroniseWait:
+			iProgress.iState=TImap4GenericProgress::ESyncing;
+			break;
+
+		case EImapStateLogoutWait:
+			iProgress.iState=TImap4GenericProgress::EDisconnecting;
+			break;
+
+		case EImapStateDeleteWait:
+		case EImapStateDeleteAllWait:
+		case EImapStateDeleteFolderWait:
+		case EImapStateDeleteMarkWait:
+		case EImapStateExpungeWait:
+		case EImapStateExpungeAllWait:
+			iProgress.iState = TImap4GenericProgress::EDeleting;
+			break;
+
+		case EImapStateSelectWait:
+			iProgress.iState = TImap4GenericProgress::ESelecting;
+			break;
+
+		default:
+			// Just 'busy' otherwise
+			iProgress.iState=TImap4GenericProgress::EBusy;
+			break;
+			}
+		}
+
+	return iProgress;
+	}
+
+// Set entry pointer
+void CImImap4Session::SetEntry(CMsvServerEntry *aEntry)
+	{
+	// Take note of this CMsvServerEntry
+	iEntry=aEntry;
+
+	// Park entry
+	iEntry->SetEntry(NULL);
+	}
+
+// Park entries
+void CImImap4Session::Park()
+	{
+	// Park normal entry
+	iEntry->SetEntry(NULL);
+
+	// Park moveentry if it exists
+	if (iMoveEntry) iMoveEntry->SetEntry(NULL);
+	}
+
+TInt CImImap4Session::CommandFailure() const
+	{
+	return iCommandFailure;
+	}
+
+void CImImap4Session::FetchHeaderL(TUint aUid)
+	{
+	iFolderIndex.SetSizeL(iMailboxSize);
+
+	// Create list of priority fields to request
+	TBuf8<256> priorityFields;
+	CDesC8ArrayFlat* array = new(ELeave) CDesC8ArrayFlat(4);
+	CleanupStack::PushL(array);
+	CImcvUtils::PriorityFieldsL(*array);
+	for (TInt i=0; i<array->Count(); i++)
+		{
+		priorityFields.Append((*array)[i]);
+		}
+	CleanupStack::PopAndDestroy(array);
+	ColonSeparatorToSpace(priorityFields);
+
+	NewTag();
+	
+	iImapIO->SendL(iStatus, KImapFetchSmallHeaderToEnd,iTag,aUid, &priorityFields);	  	
+  	NewTagSent();
+	}
+
+void CImImap4Session::FetchLargeHeaderL(TUint aUid, TBool aRange)
+ 	{
+  	// First, resize folder index to hold all messages in the folder,
+  	// as opposed to the old sync list. This will preserve the old
+  	// contents of the index, which is what we want as it's up-to-date
+  	// and correct.
+	iFolderIndex.SetSizeL(iMailboxSize);
+
+  	// build list of header fields we want note that
+  	// ReceiptFieldStrings returns strings colon terminated which we
+  	// convert to spaces for the fetch
+  	TBuf8<256> buf;
+  
+  	CDesC8ArrayFlat* array = new(ELeave) CDesC8ArrayFlat(4);
+  	CleanupStack::PushL(array);
+  
+  	CImcvUtils::ReceiptFieldsL(*array);
+  	TInt i;
+  	for (i=0; i<array->Count(); i++)
+  		buf.Append((*array)[i]);
+  
+  	CImcvUtils::PriorityFieldsL(*array);
+  	for (i=0; i<array->Count(); i++)
+  		buf.Append((*array)[i]);
+  
+  	CleanupStack::PopAndDestroy(); // array
+  
+  	ColonSeparatorToSpace(buf);
+  	
+  	// Send command
+  	NewTag();
+  	if (!aRange)
+  		{
+  		// We only want one envelope
+  		iImapIO->SendL(iStatus, KImapFetchLargeHeader, iTag, aUid, &buf);
+  		}
+  	else
+  		{
+		// If a UID search string has been specified, then we should create the UID FETCH
+		// string from the UID integer list.
+		if(iServiceSettings->SearchString().Length() !=0)
+			{
+			CreateUidStringL();
+			TPtrC8 ptr(iUidString->Des());
+ 			iImapIO->SendL(iStatus, KImapFetchLargeHeaderRangeRefined,iTag,&ptr,&buf);
+ 			}
+ 		else
+ 			{
+	  		iImapIO->SendL(iStatus, KImapFetchLargeHeaderRange,iTag,aUid, &buf);
+ 			}		
+  		}
+  	NewTagSent();
+   	}
+ 
+void CImImap4Session::DoFetchL()
+ 	{
+	DBG((LogText(_L8("CImap4Session::DoFetchL(): running..."))));
+
+ 	User::LeaveIfError(iEntry->SetEntry(iGetPart));
+ 	TUid type=iEntry->Entry().iType;
+ 
+ 	// if we are not asking for a Message type then override the get
+	// options to ensure that this is fetched
+ 	if(!iFetchPartialMail)
+		{
+		if (type != KUidMsvMessageEntry)
+ 			iGetOptions = EGetImap4EmailBodyTextAndAttachments;
+		}
+ 
+ 	User::LeaveIfError(iEntry->SetEntry(iServiceId));
+ 				
+ 	// What have we been asked to fetch? Build a list of parts to fetch: if the
+ 	// part requested has any children, that is.
+ 	// Reset stats
+ 	iProgress.iBytesToDo=0;
+ 	iProgress.iBytesDone=0;
+ 	iFetchList->Reset();
+	iHtmlEntryPart = 0;
+	iBodyTextSize = 0;
+	iHtmlEntrySize = 0;
+	iBodyPartRemainingSize = 0;
+	iFooterString = NULL;
+	iSizeOfToBeFetchedAttachments=0;
+	TBool hasTextParts = EFalse;
+	if(iFetchPartialMail)
+		{
+		DBG((LogText(_L8("Using partial mail options"))));
+		AddFetchItemL(iGetPart,iGetPartialMailInfo,hasTextParts);
+		DBG((LogText(_L8("Found %d parts to fetch (options=%d)"),iFetchList->Count(),iGetPartialMailInfo.iPartialMailOptions)));
+		}
+	else
+		{
+ 		AddFetchItemL(iGetPart,iGetOptions,hasTextParts);
+		DBG((LogText(_L8("Found %d parts to fetch (options=%d)"),iFetchList->Count(),iGetOptions)));
+		}
+		
+	if( !hasTextParts && type == KUidMsvMessageEntry )
+		{
+		// There are no text parts to this message - need to set body text 
+		// complete flag to true otherwise UI may allow such a message to 
+		// repeatedly be 'fetched' even though there is no text to fetch!
+		//
+		// So, set body text complete and message complete flags on the entry
+		// specified by iGetPart.
+		DBG((LogText(_L8("Message %d has no text parts - setting complete flag and body text complete flag to ETrue"),iGetPart)));
+
+	 	User::LeaveIfError(iEntry->SetEntry(iGetPart));
+		TMsvEmailEntry message = iEntry->Entry();
+
+		message.SetBodyTextComplete(ETrue);
+		
+		ChangeEntryL(message);
+
+		// NOTE - not sure if necessary, but changing back to service entry to 
+		// ensure consistent behaviour.
+	 	User::LeaveIfError(iEntry->SetEntry(iServiceId));
+		}
+		
+	// Any parts at all?
+ 	if (iFetchList->Count() == 0 || iState == EImapStateFetchCancelWait)
+ 		{
+ 		// No, complete the fetch.
+ 		if( iState != EImapStateFetchCancelWait )
+ 			{
+			iState=EImapStateSelectWait;
+			iSyncState=ENotSyncing;
+ 			}
+		
+ 		if( iCommandsOutstanding )
+ 			{
+ 			// Waiting for tagged response for fetch command that got the
+ 			// email structure
+	 		GetReply(EFalse);
+ 			}
+ 		else
+ 			{
+ 			// Message structure already known - therefore not waiting for the
+ 			// tagged response, complete immediately
+ 			CommandCompleteL(KErrNone);
+ 			}
+ 		return;
+ 		}
+ 
+ 	// Part count for stats
+ 	if (iFetchList->Count() > 0)
+ 		{
+ 		iProgress.iPartsToDo=iFetchList->Count();
+ 		iProgress.iPartsDone=0;
+ 
+ 		// Do the fetch
+ 		iState=EImapStateFetchWait;
+ 		iSyncState=EFetching;
+ 
+ 		DBG((LogText(_L8("Starting body fetch of %d parts (%d bytes)"),
+ 					 iProgress.iPartsToDo,iProgress.iBytesToDo)));
+ 
+ 		// Make the command to send to the server
+ 		FetchAnItemL((*iFetchList)[0]);
+ 		iFetchList->Delete(0,1);
+ 		}
+
+	}
+
+TInt CImImap4Session::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(!iEntry->Entry().Complete())
+			{
+			totalSize += iEntry->Entry().iSize;
+			}
+		}
+	return totalSize;
+	}
+
+void CImImap4Session::SetSynchronisationSelectionL(CMsvEntrySelection &aSelection)
+	{
+	// Used by the server mtm to prevent any messages selected for retrieval
+	// from being deleted during a synchronisation with a synchronisation limit set.
+	delete iSynchronisationSelection;
+	iSynchronisationSelection = 0;
+	iSynchronisationSelection = aSelection.CopyL();
+	}
+
+void CImImap4Session::SetInbox(TMsvId aInbox)
+	{
+	iInbox=aInbox;
+	}
+
+TMsvId CImImap4Session::GetInbox()
+	{
+	return iInbox;
+	}
+
+// Set or clear the \Seen flags on the server (aSettingsFlag = ETrue -> Sets the flag)
+// Returns False if no messages need to be processed
+TBool CImImap4Session::ProcessSeenFlagsL(TSeenFlagUpdateMode aUpdateMode)
+	{
+	CArrayFixFlat<TMsvId>* pendingList;
+	TBool settingFlag = (aUpdateMode == ESetSeenFlag);
+
+	// Point pendingList to the correct list
+	pendingList = (settingFlag ? iSetSeenList: iClearSeenList);
+	
+	// Exit if nothing to process
+	if (!pendingList->Count())
+		{
+		return EFalse;
+		}
+	
+	#ifdef PRINTING
+		_LIT8(KCommandProcessFlags, "COMMAND ProcessSeenFlags(%d)");
+		LOG_COMMANDS((LogText(KCommandProcessFlags, aUpdateMode)));
+	#endif
+
+	_LIT8(KStoreFlagsSetCommand, "%d UID STORE %S +FLAGS (\\Seen)\r\n");
+	_LIT8(KStoreFlagsClearCommand, "%d UID STORE %S -FLAGS (\\Seen)\r\n");
+	const TInt KMaxUIDsToProcess = 50;
+	const TInt KMaxCharsPerUID = 12;
+
+	// Ensure that the buffer passed to CImapIO does not exceed 1024
+	__ASSERT_DEBUG(KMaxUIDsToProcess * KMaxCharsPerUID < 1024 - (KStoreFlagsSetCommand().Length() + 10), gPanic(KMaxBufferLengthExceeded));
+
+	TInt pos = 0;
+	TInt stored = 0;
+	TInt run = 0;
+	TInt UID;
+	TInt lastUID = -1;
+	TInt listCount = pendingList->Count();
+	TBool haveSentCommand = EFalse;
+	TMsvEmailEntry message;
+	HBufC8* command=HBufC8::NewLC(KMaxUIDsToProcess * KMaxCharsPerUID);
+	TPtr8 commandDes = command->Des();
+
+	// Build up a list of UID's who's \Seen flag needs changing.
+	// To save bandwidth, group contiguous blocks of UID's together,
+	// e.g. 1:6,8:12,14 instead of 1,2,3,4,5,6,8,9,10,12,14
+	while(pos < listCount && stored < KMaxUIDsToProcess)
+		{
+		SetEntryL(pendingList->At(pos));
+		message = iEntry->Entry();
+		UID = static_cast<TInt>(message.UID());
+
+		// Should never have the state when the list contains flags that match the servers flag
+		__ASSERT_DEBUG(FIXBOOL(message.Unread()) != settingFlag, gPanic(KSettingSeenFlagToExistingState));
+
+		// Set the Seen flag for the message
+		message.SetSeenIMAP4Flag(settingFlag);
+		ChangeEntryL(message);
+		// If not first time through and the UID follows the previous one
+		if (pos !=0 && UID == lastUID+1)
+			{
+			// We are in a run
+			run++;
+			}
+		else
+			{
+			// If we were in a run
+			if (run)
+				{
+				// Append a ':' followed by the UID at the end of the run
+				commandDes.Append(TChar(':'));
+				commandDes.AppendNum(lastUID);
+				stored++;
+				// Reset the run count
+				run = 0;
+				}
+			// Append ',' only if not the first time through
+			if (pos!=0)
+				{
+				commandDes.Append(TChar(','));
+				}
+			// Append the current UID
+			commandDes.AppendNum(UID);
+			stored++;
+			}
+		// Next message
+		pos++;
+		lastUID = UID;
+		} // end while
+
+	// If we are at the end of the list but still in a run, add the last uid to the command buffer
+	if (run)
+		{
+		commandDes.Append(TChar(':'));
+		commandDes.AppendNum(lastUID);
+		}
+
+	// Anything stored?
+	if (stored)
+		{
+		// Get a new tag
+		NewTag();
+
+		if (settingFlag)
+			iImapIO->SendL(iStatus,KStoreFlagsSetCommand, iTag, command);
+		else
+			iImapIO->SendL(iStatus,KStoreFlagsClearCommand, iTag, command);
+
+		// Send the command
+		NewTagSent();
+		// Remove the processed flags from the list (pos = num_to_remove)
+		pendingList->Delete(0, pos);
+		haveSentCommand = ETrue;
+		}
+	else
+		{
+		// Should never have the state when the list contained flags but we never sent the 'STORE FLAGS' command to the server
+		__ASSERT_DEBUG(FIXBOOL(message.Unread()) != settingFlag, gPanic(KErrorBuildingStoreFlagsCommand));
+		}
+
+	// Get rid of command buffer
+	CleanupStack::PopAndDestroy(command);
+	return haveSentCommand;
+	}
+// Creates an IMAP4 defined optimised string of UIDs from the integer list of UIDs
+// retrieved by the UID SEARCH command.
+// Example: If the input array is 123 125 126 127 300 302 303 304 308
+// The output string will be "123,125:127,300,302,303:304,308
+// For the time being at least, keep the string as a class instance variable for possible re-use
+void CImImap4Session::CreateUidStringL()
+	{
+	iUidString->Des().Zero();
+	TPtr8 ptr(iUidString->Des());
+	TUint32* current = &iSearchList->At(0); // Loop iterator
+	TUint32* start(NULL); // Keep track of the start of a UID sequence
+	TInt count = iSearchList->Count(); // Quick access
+	while(current < &iSearchList->At(0) + count) // Loop till the end of the integer list
+		{
+		// Check for room to add 2 integers plus 2 separators.
+		// If not enough room then reallocate 
+		if((ptr.MaxLength() - ptr.Length()) < (KMaxUint32Chars)+2)
+			{
+			iUidString = iUidString->ReAllocL(ptr.MaxLength()*2); // Make sure this time
+			ptr.Set(iUidString->Des());
+			}
+		start = current; // Mark the start of a possible sequence
+		if(current != &iSearchList->At(0)) // If it's the first time through the loop don't append separator
+			{
+			ptr.Append(',');
+			}
+		ptr.AppendNum(*start); // First integer always written
+		// Loop until end of sequence or end of list.
+		// will loop through 125 126 127
+		while(current < &iSearchList->At(count-1) && *current == (*(current+1))-1)
+			{
+			++current;
+			}
+		// If there was a run then current won't equal start.
+		// That being the case, append the sequence separator followed by last in sequence
+		// 125:127
+		// If there's no sequence, the next int is written by ptr.AppendNum(*start)
+		if(current != start)
+			{
+			ptr.Append(':');
+			ptr.AppendNum(*current);
+			}
+		++current;
+		}	
+	}
+
+void CImImap4Session::IdleTimerExpiredL()
+	{
+	iIdleTimerExpired = ETrue;
+	ReissueIdleL();
+	}
+
+void CImImap4Session::CancelTimerExpired()
+	{
+	Cancel();
+	if (iState != EImapStateDisconnected)
+		{
+		DoDisconnect();
+		}
+	iObserver.NonCompletedFailure();
+	}
+
+	
+CIdleTimeoutTimer::CIdleTimeoutTimer(CImImap4Session& aOperation)
+: CTimer(EPriorityHigh), iOperation(aOperation)
+	{}
+
+void CIdleTimeoutTimer::RunL()
+	{
+	iOperation.IdleTimerExpiredL();
+	}
+
+CIdleTimeoutTimer* CIdleTimeoutTimer::NewL(CImImap4Session& aOperation)
+	{
+	CIdleTimeoutTimer* self = new(ELeave) CIdleTimeoutTimer(aOperation);
+	CleanupStack::PushL(self);
+	self->ConstructL(); // CTimer
+	CActiveScheduler::Add(self);
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+CImImap4SessionDummyRead* CImImap4SessionDummyRead::NewL(CImImap4Session& aSession, CImapIO& aImapIO, TInt aPriority)
+	{
+	CImImap4SessionDummyRead* self = new (ELeave) CImImap4SessionDummyRead(aSession, aImapIO, aPriority);
+	return self;
+	}
+
+CImImap4SessionDummyRead::CImImap4SessionDummyRead(CImImap4Session& aSession, CImapIO& aImapIO, TInt aPriority)
+: CActive(aPriority), iSession(aSession), iImapIO(aImapIO)
+	{
+	DBG(iSession.LogText(_L8("+ CImImap4SessionDummyRead()") ) );
+	CActiveScheduler::Add(this);
+	DBG(iSession.LogText(_L8("- CImImap4SessionDummyRead()") ) );
+	}
+
+CImImap4SessionDummyRead::~CImImap4SessionDummyRead()
+	{
+	DBG(iSession.LogText(_L8("+ ~CImImap4SessionDummyRead") ) );
+	Cancel();
+	DBG(iSession.LogText(_L8("- ~CImImap4SessionDummyRead") ) );
+	}
+
+void CImImap4SessionDummyRead::Start()
+	{
+	DBG( iSession.LogText( _L8("+ CImImap4SessionDummyRead::Start()") ) );
+	if(IsActive())
+		{ 
+		// already have an outstanding dummy read, ignore this
+		DBG( iSession.LogText( _L8("CImImap4SessionDummyRead::Start():Already active") ) );
+		return;
+		}
+
+	DBG( iSession.LogText( _L8("+ CImImap4SessionDummyRead::Start()") ) );
+	// As the spec is ambiguous about when we can get untagged responses,
+	// we can get them any time, so ask for a proper reply
+	iImapIO.GetReply(iStatus);
+        
+        DBG((iSession.LogText(_L8("*******************************************************"))));
+	DBG((iSession.LogText(_L8("CImap4SessionDummyRead::Start(): waiting for iImapIO to wake me"))));
+	DBG((iSession.LogText(_L8("*******************************************************"))));
+
+ 	SetActive();
+	DBG( iSession.LogText( _L8("- CImImap4SessionDummyRead::Start()") ) );
+	}
+
+void CImImap4SessionDummyRead::DoCancel()
+	{
+	DBG( iSession.LogText( _L8("+ CImImap4SessionDummyRead::DoCancel()") ) );
+	iImapIO.Cancel();
+	DBG( iSession.LogText( _L8("- CImImap4SessionDummyRead::DoCancel()") ) );
+	}
+
+void CImImap4SessionDummyRead::RunL()
+	{
+	//Inform iOperation that the dummy read has completed.
+
+	//Under normal conditions the dummy read should never complete.
+	//However, if it does complete it is likely that iStatus != KErrNone.
+	DBG( iSession.LogText( _L8("+ CImImap4SessionDummyRead::RunL()") ) );
+	iSession.DummyComplete(iStatus.Int());
+	if (iStatus.Int() >= KErrNone) 
+		{ 
+		DBG( iSession.LogText(_L8(" CImImap4SessionDummyRead::RunL() There might be more data coming, issue dummy reading again.")) ); 
+		iSession.ReissueDummy(); 
+		}
+	DBG( iSession.LogText( _L8("- CImImap4SessionDummyRead::RunL()") ) );
+	}
+	
+
+CImImap4SessionIdleRead* CImImap4SessionIdleRead::NewL(CImImap4Session& aOperation, TInt aPriority)
+	{
+	CImImap4SessionIdleRead* self = new (ELeave) CImImap4SessionIdleRead(aOperation, aPriority);
+	return self;
+	}
+
+CImImap4SessionIdleRead::CImImap4SessionIdleRead(CImImap4Session& aOperation, TInt aPriority)
+: CActive(aPriority), iOperation(aOperation)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+CImImap4SessionIdleRead::~CImImap4SessionIdleRead()
+	{
+	Cancel();
+	}
+
+void CImImap4SessionIdleRead::Start(TRequestStatus& aStatus)
+	{
+	// Store the status for completion later
+	iOperationStatus = &aStatus;
+	*iOperationStatus = KRequestPending;
+
+	// Issue the read with *our* status
+	iOperation.DoIdleRead(iStatus);
+        DBG((iOperation.LogText(_L8("*******************************************************"))));
+	DBG((iOperation.LogText(_L8("CImap4SessionIdleRead::Start(): waiting for iOperation to wake me"))));
+	DBG((iOperation.LogText(_L8("*******************************************************"))));
+
+	SetActive();
+	}
+
+void CImImap4SessionIdleRead::DoCancel()
+	{
+	DBG(iOperation.LogText(_L8("CImImap4SessionIdleRead::DoCancel()")));
+
+	// Cancel the idle read we started
+	iOperation.CancelIdleRead();
+        DBG((iOperation.LogText(_L8("-----------------------------------------------------------"))));
+	DBG((iOperation.LogText(_L8("CImap4SessionIdleRead::DoCancel(): calling request complete"))));
+	DBG((iOperation.LogText(_L8("-----------------------------------------------------------"))));
+
+	User::RequestComplete(iOperationStatus, KErrCancel);
+	}
+
+void CImImap4SessionIdleRead::RunL()
+	{
+	TInt err = iStatus.Int();
+	DBG(iOperation.LogText(_L8("CImImap4SessionIdleRead::RunL(iStatus=%d)"),err));
+
+	if(err < KErrNone)
+		{ // Special case for error
+		// Need to do this because if read completed with an error,
+		// the RequestComplete would not handle this by itself as 
+		// the RunL would not be called and neither would the 
+		// DoComplete (because the session's iReport is NULL...)
+		iOperation.IdleReadError(err);
+		}
+        DBG((iOperation.LogText(_L8("-----------------------------------------------------------"))));
+	DBG((iOperation.LogText(_L8("CImap4SessionIdleRead::RunL(): calling request complete"))));
+	DBG((iOperation.LogText(_L8("-----------------------------------------------------------"))));
+
+	User::RequestComplete(iOperationStatus, err);
+	}