email/imap4mtm/imapsyncmanager/src/cimapfolder.cpp
changeset 0 72b543305e3a
child 14 c6838af47512
equal deleted inserted replaced
-1:000000000000 0:72b543305e3a
       
     1 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "cimapfolder.h"
       
    17 #include "cimapsyncmanager.h"
       
    18 #include "cimapsession.h"
       
    19 #include "cimaprfc822headerfields.h"
       
    20 #include "cimapbodystructure.h"
       
    21 #include "cimapfolderinfo.h"
       
    22 #include "cimapfolderindex.h"
       
    23 #include "cimaplogger.h"
       
    24 #include <numberconversion.h>
       
    25 #include <imcvcodc.h>
       
    26 #include <miutconv.h>
       
    27 #include "cimapmimeheaderfields.h"
       
    28 #include "cimapcharconv.h"
       
    29 
       
    30 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS  
       
    31 #include "cimconvertheader.h"
       
    32 #include "cimconvertcharconv.h"
       
    33 #endif
       
    34 
       
    35 // For first stage download (to view in folder list)
       
    36 _LIT8(KImapFetchHeaderToEnd, "%d:*");
       
    37 _LIT8(KImapFetchHeaderRange,"%d:%d");
       
    38 _LIT8(KImapFetchHeaderUIDRange,"UID %d:%d");
       
    39 _LIT8(KImapFetchHeaderRangeSearch,"%d:%d %S");
       
    40 _LIT8(KImapSmallHeaderFields,"Received Date Subject From Priority X-MSMail-Priority X-Priority Importance");
       
    41 //_LIT8(KImapLargeHeaderFields,"Received Date Subject From Reply-to To Cc Bcc Message-ID Priority %S");
       
    42 
       
    43 _LIT(KQuoteChar, "\\");
       
    44 _LIT(KIMAP_INBOX, "INBOX");
       
    45 
       
    46 _LIT8(KImapTxtImage, "IMAGE");
       
    47 _LIT8(KImapTxtAudio, "AUDIO");
       
    48 _LIT8(KImapTxtVideo, "VIDEO");
       
    49 _LIT8(KImapTxtApplication, "APPLICATION");
       
    50 _LIT8(KImapTxtRelated, "RELATED");
       
    51 _LIT8(KImapTxtAlternative, "ALTERNATIVE");
       
    52 _LIT8(KImapTxtMixed, "MIXED");
       
    53 _LIT8(KImapTxtHtml, "HTML");
       
    54 _LIT8(KImapTxtAttachment, "ATTACHMENT");
       
    55 _LIT8(KImapTxtCalendar, "CALENDAR");
       
    56 _LIT8(KImapTxtXVCalendar, "X-VCALENDAR");
       
    57 _LIT8(KImapTxtDeliveryStatus, "DELIVERY-STATUS");
       
    58 _LIT8(KImapTxtBase64, "BASE64");
       
    59 
       
    60 // IMAP Priority fields
       
    61 //_LIT8(KImapPriorityFieldsSearch, "Priority Priority X-MSMail-Priority Precedence Importance");
       
    62 
       
    63 // Constants
       
    64 const TInt KImapUidSearchSize = 400;
       
    65 const TInt KImapMaxIntChars = 11; // the maximum number of characters needed to represent a 32-bit unsigned integer as a string.
       
    66 
       
    67 
       
    68 // Efficient and SAFE way of comparing TBools which might have different integers representing TRUE
       
    69 inline TBool BoolsAreEqual( TBool aA, TBool aB )
       
    70 	{
       
    71 	return ((aA && aB) || (!aA && !aB));
       
    72 	}
       
    73 	
       
    74 inline TBool BoolsAreNotEqual( TBool aA, TBool aB )
       
    75 	{
       
    76 	return ((!aA || !aB) && (aA || aB));
       
    77 	}
       
    78 
       
    79 /**
       
    80 Constructor
       
    81 */
       
    82 CImapFolder::CImapFolder(CImapSyncManager& aSyncMan, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings)
       
    83 : CMsgActive(EPriorityStandard), 
       
    84 iSyncManager(aSyncMan),
       
    85 iServerEntry(aServerEntry),
       
    86 iImapSettings(aImapSettings),
       
    87 iHeaderConverter(CImapUtils::GetRef().Charconv().HeaderConverter())
       
    88 	{
       
    89 	}
       
    90 
       
    91 /**
       
    92 Destructor
       
    93 */
       
    94 CImapFolder::~CImapFolder()
       
    95 	{
       
    96 	iMatchingMessageIds.Reset();
       
    97 	iDeletedMessageIds.Reset();
       
    98 	iMissingMessageIds.Reset();
       
    99 	delete iSelection;
       
   100 	iFolderIndex->Reset();
       
   101 	iMessageFlagInfoArray.Reset();
       
   102 	delete iFolderIndex;
       
   103 	delete iCachedEntryData;
       
   104 	delete iClearSeenList;
       
   105 	delete iSetSeenList;
       
   106 	iFullFolderPath.Close();
       
   107 	}
       
   108 
       
   109 /**
       
   110 Static factory constructor
       
   111 
       
   112 @param aSyncMan
       
   113 Reference to the owning Sync Manager (for access to offline control object)
       
   114 @param aEmailServerEntry
       
   115 The folder entry on the Message server that will be use to construct this folder object
       
   116 @return The constructed CImapFolder object pushed onto the cleanup stack
       
   117 */
       
   118 EXPORT_C CImapFolder* CImapFolder::NewLC(CImapSyncManager& aSyncMan, CMsvServerEntry& aServerEntry, TMsvEmailEntry& aEmailEntry, CImapSettings& aImapSettings, const TDesC& aFullFolderPath)
       
   119 	{
       
   120 	CImapFolder* self=new (ELeave) CImapFolder(aSyncMan, aServerEntry, aImapSettings);
       
   121 	CleanupStack::PushL(self);
       
   122 
       
   123 	// Non-trivial constructor
       
   124 	self->ConstructL(aServerEntry, aEmailEntry, aFullFolderPath);
       
   125 	return self;
       
   126 	}
       
   127 
       
   128 /**
       
   129 Static factory constructor with cleanup
       
   130 
       
   131 @param aSyncMan
       
   132 Reference to the owning Sync Manager (for access to offline control object)
       
   133 @param aEmailServerEntry
       
   134 The folder entry on the Message server that will be use to construct this folder object
       
   135 @return The constructed CImapFolder object
       
   136 */
       
   137 EXPORT_C CImapFolder* CImapFolder::NewL(CImapSyncManager& aSyncMan, CMsvServerEntry& aServerEntry, TMsvEmailEntry& aEmailEntry, CImapSettings& aImapSettings, const TDesC& aFullFolderPath)
       
   138 	{
       
   139 	CImapFolder* self=NewLC(aSyncMan, aServerEntry, aEmailEntry, aImapSettings, aFullFolderPath);
       
   140 	CleanupStack::Pop();
       
   141 	return self;
       
   142 	}
       
   143 
       
   144 /**
       
   145 The non-trival constructor
       
   146 
       
   147 @param aEmailServerEntry
       
   148 The folder entry on the Message server that will be use to construct this folder object
       
   149 */
       
   150 void CImapFolder::ConstructL(CMsvServerEntry& /*aServerEntry*/, TMsvEmailEntry& aEmailEntry, const TDesC& aFullFolderPath)
       
   151 	{
       
   152 	iSessionFolderInfo = NULL;
       
   153 	iMailboxId = aEmailEntry.Id();
       
   154 	iMailboxSize = aEmailEntry.RemoteFolderEntries();
       
   155 	iFolderIndex = new (ELeave) CImapFolderIndex();
       
   156 	iSelection   = new (ELeave) CMsvEntrySelection();
       
   157 	iSyncLimit = KImImapSynchroniseAll;
       
   158 
       
   159 	// Set the Path of folder object
       
   160 	SetFullFolderPathL(aFullFolderPath) ;
       
   161 	
       
   162 	// We're an active object...
       
   163 	CActiveScheduler::Add(this);
       
   164 	}
       
   165 
       
   166 /**
       
   167 Sets the full path name of the folder from the service entry downwards. Layout as on the remote IMAP Server.
       
   168 Should only be use by the CImapSyncManager.
       
   169 
       
   170 @param aFullFolderPath
       
   171 The new path to use as the full path of the folder.
       
   172 */
       
   173 void CImapFolder::SetFullFolderPathL(const TDesC& aFullFolderPath)
       
   174 	{
       
   175 	iFullFolderPath.Close(); // discard any previous data
       
   176 	iFullFolderPath.CreateL(aFullFolderPath);
       
   177 	}
       
   178 
       
   179 /**
       
   180 Processing message loop for the active object. Called by the active scheduler.
       
   181 */
       
   182 void CImapFolder::DoRunL()
       
   183 	{
       
   184 	// Finish if any server errors.
       
   185 	if(iStatus.Int()!=KErrNone)
       
   186 		{
       
   187 		Complete(iStatus.Int());
       
   188 		return;
       
   189 		}
       
   190 
       
   191 	switch(iNextAction)
       
   192 		{
       
   193 	case CImapSyncManager::ESyncNew:
       
   194 		{
       
   195 		__LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Starting SynchroniseNewL");				
       
   196 		SynchroniseNewL();
       
   197 		return;
       
   198 		}
       
   199 	case CImapSyncManager::ESyncFlags:
       
   200 		{
       
   201 		__LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Syncing flags for local messages");
       
   202 
       
   203 		// Whenever iSavedSession->FetchBodyStructureAndHeadersL() is called, this is the next step 
       
   204 		// (although this step can be reached without calling FetchBodyStructureAndHeadersL())
       
   205 		// This means that OnFetchLD() may have been called, in which case there are 
       
   206 		// outstanding BULK entry operations that need to be completed here.
       
   207 		iServerEntry.CompleteBulk();
       
   208 
       
   209 		// If there were message flags that has been changed locally and need upadting on the remote server
       
   210 		// then do those first.
       
   211 		if(iSetSeenList)
       
   212 			{
       
   213 			if(ProcessSeenFlagsL(ESetSeenFlag))
       
   214 				{
       
   215 				return;
       
   216 				}
       
   217 			}
       
   218 
       
   219 		if(iClearSeenList)
       
   220 			{
       
   221 			if(ProcessSeenFlagsL(EClearSeenFlag))
       
   222 				{
       
   223 				return;
       
   224 				}
       
   225 			}
       
   226 
       
   227 		__LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Syncing flags for remote messages");
       
   228 		// Creates the list of old messages then update the flags for them
       
   229 		GetMessageChildrenL(iMailboxId, iSelection);
       
   230 		MakeSortedFolderIndexL(ETrue);
       
   231 	
       
   232 		if(iSelection->Count() > 0)
       
   233 			{
       
   234 			iHighestUid = (*iFolderIndex)[iSelection->Count()-1].iUid;
       
   235 			}
       
   236 		else
       
   237 			{
       
   238 			iHighestUid = 0;
       
   239 			}
       
   240 
       
   241 		TInt localcount = iFolderIndex->Size();
       
   242 		iMatchingMessageIds.Reset();
       
   243 		for(TInt localloop = 0; localloop < localcount; ++localloop)
       
   244 			{
       
   245 			TMsvId localid = (*iFolderIndex)[localloop].iUid;
       
   246 			iMatchingMessageIds.Append(localid);
       
   247 			}
       
   248 			
       
   249 		iMessageFlagInfoArray.Reset();
       
   250 		
       
   251 		HBufC8* sequenceSetOnHeap = CImapSession::CreateSequenceSetLC(iMatchingMessageIds);
       
   252 		
       
   253 		// Call the FETCHFLAGS function in the session
       
   254 		iNextAction = CImapSyncManager::EEndSyncFlags;
       
   255 		iSavedSession->FetchFlagsL(iStatus, *sequenceSetOnHeap, iMessageFlagInfoArray);
       
   256 		SetActive();
       
   257 		CleanupStack::PopAndDestroy(sequenceSetOnHeap);
       
   258 		}
       
   259 		break;		
       
   260 	case CImapSyncManager::EEndSyncFlags:
       
   261 		{
       
   262 		__LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Call to get the remote flags completed");
       
   263 		// Loop through the messages array and update the flags for each UID returned
       
   264 		// Get a clean version of the folder children
       
   265 		TInt resultscount = iMessageFlagInfoArray.Count();
       
   266 		for(TInt resultsloop = 0; resultsloop < resultscount; ++resultsloop)
       
   267 			{
       
   268 			TMessageFlagInfo *messageinfo = &(iMessageFlagInfoArray[resultsloop]);
       
   269 
       
   270 			TInt localmsgcount = iFolderIndex->Size();
       
   271 			// For each returned Uid go through the list of local index to find the matching TMsvId.
       
   272 			for(TInt localloop = 0; localloop < localmsgcount; ++localloop)
       
   273 				{
       
   274 				if((*iFolderIndex)[localloop].iUid == messageinfo->MessageUid())
       
   275 					{
       
   276 					SetEntryL((*iFolderIndex)[localloop].iMsvId);
       
   277 					TMsvEmailEntry entry = iServerEntry.Entry();
       
   278 
       
   279 					TBool messageInfoSeen = messageinfo->QueryFlag(TMessageFlagInfo::ESeen);
       
   280 					TBool oldEntrySeen = entry.SeenIMAP4Flag();
       
   281 
       
   282 					// Are we configured to update the \seen flag on the server?
       
   283 					// If so then we need to translate "read\unread" flags into "\\seen" or not "\\seen"
       
   284 					if (iImapSettings.UpdatingSeenFlags())
       
   285 						{
       
   286 						// Make a note to update the servers \Seen flag if "read\unread" has CHANGED on the client
       
   287 						// and is different to the server's version
       
   288 						if (BoolsAreEqual(entry.Unread(), messageInfoSeen) && BoolsAreEqual(oldEntrySeen, messageInfoSeen))
       
   289 							{
       
   290 							if (entry.Unread())
       
   291 								{
       
   292 								AppendClearSeenL(entry.UID());
       
   293 								}
       
   294 							else
       
   295 								{
       
   296 								AppendSetSeenL(entry.UID());
       
   297 								}								
       
   298 							}
       
   299 						}
       
   300 
       
   301 					entry.SetSeenIMAP4Flag(messageInfoSeen);
       
   302 					entry.SetFlaggedIMAP4Flag(messageinfo->QueryFlag(TMessageFlagInfo::EFlagged));
       
   303 					entry.SetDeletedIMAP4Flag(messageinfo->QueryFlag(TMessageFlagInfo::EDeleted));
       
   304 					entry.SetDraftIMAP4Flag(messageinfo->QueryFlag(TMessageFlagInfo::EDraft));
       
   305 					entry.SetRecentIMAP4Flag(messageinfo->QueryFlag(TMessageFlagInfo::ERecent));
       
   306 					
       
   307 					// Are we configured to update the \seen flag on the server?
       
   308 					if (iImapSettings.UpdatingSeenFlags())
       
   309 						{
       
   310 						// Now copy the inverse of the \Seen flag down to the phone's Unread flag 
       
   311 						//  except when LastSyncSeen is set (ie when the client version is more up to date)
       
   312 						//  This means that the client Read status ALWAYS reflects the IMAP \Seen state
       
   313 						if (BoolsAreEqual(entry.Unread(), messageInfoSeen) 		&& BoolsAreNotEqual(oldEntrySeen, messageInfoSeen))
       
   314 							{
       
   315 							// Phone and server disagree on whether the message has been read.
       
   316 							// And the seen flag on the server is different to that on the phone.
       
   317 							// Which means that a change has happened on the server that the phone was not aware of.
       
   318 							// So server is more up to date, and we take its value.
       
   319 							entry.SetUnread(!messageInfoSeen);
       
   320 							}
       
   321 						}
       
   322 					
       
   323 					ChangeEntryBulkL(entry); // completed after the outer for loop
       
   324 					break;
       
   325 					}
       
   326 				} // end of inner for loop
       
   327 			}
       
   328 			
       
   329 		iServerEntry.CompleteBulk();
       
   330 		
       
   331 		iNextAction = CImapSyncManager::ESyncProcessSeenFlagsAndComplete;
       
   332 		SetActive();
       
   333 		CompleteSelf();
       
   334 		}
       
   335 		break;		
       
   336 	case CImapSyncManager::ESyncProcessSeenFlagsAndComplete:
       
   337 		{
       
   338 		// If there were message flags that has been changed locally and need upadting on the remote server
       
   339 		// then do those now.
       
   340 		if(iSetSeenList)
       
   341 			{
       
   342 			if(ProcessSeenFlagsL(ESetSeenFlag))
       
   343 				{
       
   344 				return;
       
   345 				}
       
   346 			}
       
   347 
       
   348 		if(iClearSeenList)
       
   349 			{
       
   350 			if(ProcessSeenFlagsL(EClearSeenFlag))
       
   351 				{
       
   352 				return;
       
   353 				}
       
   354 			}
       
   355 			
       
   356 		SyncCompleteL();
       
   357 		Complete(iStatus.Int());
       
   358 		}
       
   359 		break;
       
   360 	case CImapSyncManager::ESyncSearch:
       
   361 		{
       
   362 		if(iStatus == KErrNone) 
       
   363 			{
       
   364 			if(iSyncState == CImapSyncManager::ESyncSearch)
       
   365 				{
       
   366 				// Process the seach results to see if any messages ids are left.
       
   367 				if(CheckAndProgressMessageSyncL())
       
   368 					{
       
   369 					// More messages and the search command has been sent so just return.
       
   370 					return;
       
   371 					}
       
   372 				}
       
   373 				
       
   374 			// If the sync action is syncing old messages then go through the list of found ids
       
   375 			// match it with the folder index and build a list of messages that are no longer on the server.
       
   376 			if(iSyncState == CImapSyncManager::ESyncOld)
       
   377 				{
       
   378 				// At this point the remote command to search out folder UIDs has completed. This
       
   379 				// list will contain all UIDs for messages in the remote folder. This may be too
       
   380 				// many, and if so, the list is truncated to only include the N most recent 
       
   381 				// messages in accordance with the synchronisation limit.
       
   382 
       
   383 				TInt syncThresh = GetSyncThreshold();
       
   384 				__LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Syncing old. syncThresh = %d", syncThresh));
       
   385 
       
   386 				// Perform the check on the message ids to make sure that only messages that are within
       
   387 				// the sync limit will be left in iDeletedMessageIds.
       
   388 				CheckMessagesInRangeL(syncThresh);
       
   389 				
       
   390 				// Check the delete ids list and delete messages according to what's in the list	
       
   391 				TInt deletedcount = iDeletedMessageIds.Count();
       
   392 				// Remove message from the local server
       
   393 				for(TInt deleteloop = 0; deleteloop < deletedcount; ++deleteloop)
       
   394 					{
       
   395 					__LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Local message (%d) deleting.", (*iFolderIndex)[iDeletedMessageIds[deleteloop]].iMsvId));
       
   396 					DeleteMessageL((*iFolderIndex)[iDeletedMessageIds[deleteloop]].iMsvId);
       
   397 					}
       
   398 
       
   399 				// Trim the list down to the most recent UIDs consistant with the sync limit.
       
   400 				if(iMatchingMessageIds.Count() > syncThresh)
       
   401 					{
       
   402 					for(TInt i = 0; i < syncThresh; ++i)
       
   403 						{
       
   404 						iMatchingMessageIds.Remove(0);
       
   405 						}
       
   406 					}
       
   407 
       
   408 				// Get a clean version of the folder children
       
   409 				GetMessageChildrenL(iMailboxId, iSelection);
       
   410 				
       
   411 				//update the progress object
       
   412 				iMsgsToDo = iMatchingMessageIds.Count();
       
   413 				
       
   414 				MakeSortedFolderIndexL(ETrue);
       
   415 			
       
   416 				if(iSelection->Count() > 0)
       
   417 					{
       
   418 					iHighestUid = (*iFolderIndex)[iSelection->Count()-1].iUid;
       
   419 					}
       
   420 				else
       
   421 					{
       
   422 					iHighestUid = 0;
       
   423 					}
       
   424 
       
   425 				// At this point, the remote command to fetch all old messages has completed. 
       
   426 				// Now we can look at fetching all new messages. 'iHighestUid' will contain the
       
   427 				// highest UID of the old messages. The top entry in 'iSearchList' will contain
       
   428 				// the highest UID in the remote folder. This gives us the range of UID to fetch
       
   429 				// for new messages.
       
   430 
       
   431 				// First check are there any new messages to fetch? If 'iHighestUid' is the highest
       
   432 				// UID locally and remotely, then we finished sync'ing when we completed the old
       
   433 				// sync.
       
   434 				if(iMatchingMessageIds.Count() == 0)
       
   435 					{
       
   436 					__LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Search List is empty");				
       
   437 					}
       
   438 				else if(iHighestUid < iMatchingMessageIds[iMatchingMessageIds.Count()-1])
       
   439 					{
       
   440 					TUint32 uidLow = iHighestUid;
       
   441 					TUint32 uidHigh = iMatchingMessageIds[iMatchingMessageIds.Count()-1];
       
   442 
       
   443 					// Only want new messages.
       
   444 					uidLow++;
       
   445 
       
   446 					// Are there only new messages (and no old)?
       
   447 					if(iHighestUid == 0)
       
   448 						{
       
   449 						// Set this to ensure range is correct.
       
   450 						uidLow = iMatchingMessageIds[0];
       
   451 						}
       
   452 					
       
   453 					// Perform the new sync.
       
   454 					__LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Synchronising new messages with range %d:%d", uidLow, uidHigh));
       
   455 					SynchroniseRangeL(uidLow, uidHigh);
       
   456 					return;
       
   457 					}
       
   458 
       
   459 				iSyncState = CImapSyncManager::ESyncNew;	
       
   460 				}
       
   461 
       
   462 			if((iSyncState == CImapSyncManager::ESyncNew) && (iImapSettings.SearchString().Length() == 0))
       
   463 				{
       
   464 				// Check for missing UIDs, ie. holes in the messages that we should have.
       
   465 				CheckForMissingMessagesUidsL();
       
   466 
       
   467 				// If there were any "missing" messages found during the sync that we should have
       
   468 				// mirrored previously, get these now.
       
   469 				if(iMissingMessageIds.Count() != 0)
       
   470 					{
       
   471 					__LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Missing messages detected %d", iMissingMessageIds.Count()));
       
   472 					HBufC8* sequenceSetOnHeap = CImapSession::CreateSequenceSetLC(iMissingMessageIds);
       
   473 					// Call the FETCH function in the session
       
   474 					iSavedSession->FetchBodyStructureAndHeadersL(iStatus, sequenceSetOnHeap->Des(), KImapSmallHeaderFields(), *this);
       
   475 					CleanupStack::PopAndDestroy(sequenceSetOnHeap);
       
   476 					SetActive();
       
   477 
       
   478 				 	iSyncState = CImapSyncManager::EFolderSynchronise;
       
   479 				 	iNextAction = CImapSyncManager::ESyncFlags;
       
   480 					return;
       
   481 					}
       
   482 
       
   483 				if(iSyncLimit <= KImImapSynchroniseAll)
       
   484 					{
       
   485 					__LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Starting SynchroniseNewL");				
       
   486 					SynchroniseNewL();
       
   487 					return;
       
   488 					}
       
   489 				
       
   490 				// If we haven't returned yet, then no new messages to sync
       
   491 				// so just sync the flags of existing messages.
       
   492 				iSyncState = CImapSyncManager::EFolderSynchronise;
       
   493 			 	iNextAction = CImapSyncManager::ESyncFlags;
       
   494 			 	SetActive();
       
   495 			 	CompleteSelf();
       
   496 				return;
       
   497 				}
       
   498 			else
       
   499 				{
       
   500 				SyncCompleteL();
       
   501 				Complete(iStatus.Int());
       
   502 				}
       
   503 			}
       
   504 		}
       
   505 		break;
       
   506 	case CImapSyncManager::EFolderExpunge:
       
   507 		{
       
   508 		// Call expunge to delete messages marked for delete
       
   509 		__LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: calling EXPUNGE in folder %S", &iFullFolderPath));
       
   510 
       
   511 		iSyncState = CImapSyncManager::EFolderExpunge;
       
   512 		iNextAction = CImapSyncManager::EFolderLocalExpunge;
       
   513 		iSavedSession->ExpungeL(iStatus);
       
   514 		SetActive();
       
   515 		}
       
   516 		break;
       
   517 	case CImapSyncManager::EFolderClose:
       
   518 		{
       
   519 		//Permanently removes all messages that have the deleted flag set
       
   520 		//from the currently selected mailbox
       
   521 		iSyncState = CImapSyncManager::EFolderClose;
       
   522 		iNextAction = CImapSyncManager::EFolderReSelect;
       
   523 		iSavedSession->CloseL(iStatus);
       
   524 		SetActive();
       
   525 		}
       
   526 		break;
       
   527 	case CImapSyncManager::EFolderReSelect:
       
   528 		{
       
   529 		//Selecting a mailbox so that messages in the mailbox can be accessed. 
       
   530 		iSyncState = CImapSyncManager::EFolderReSelect;
       
   531 		iNextAction = CImapSyncManager::EFolderLocalExpunge;
       
   532 		SelectL(iStatus, *iSavedSession);
       
   533 		SetActive();
       
   534 		}
       
   535 		break;
       
   536 	case CImapSyncManager::EFolderLocalExpunge:
       
   537 		{
       
   538 		// delete local messages locally
       
   539 		__LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: local message delete in folder %S", &iFullFolderPath));
       
   540 		
       
   541 		// Check the delete ids list and delete messages according to what's in the list	
       
   542 		TInt deletedcount = iDeletedMessageIds.Count();
       
   543 		// Remove message from the local server
       
   544 		for(TInt deleteloop = 0; deleteloop < deletedcount; ++deleteloop)
       
   545 			{
       
   546 			__LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Local message (%d) deleting.", (*iFolderIndex)[iDeletedMessageIds[deleteloop]].iMsvId));
       
   547 			DeleteMessageL((*iFolderIndex)[iDeletedMessageIds[deleteloop]].iMsvId);
       
   548 			}
       
   549 			
       
   550 		// clear the count of expunged messages to prevent a sync triggering
       
   551 		CImapFolderInfo* folderInfo = iSavedSession->SelectedFolderInfo();
       
   552 		if (folderInfo->ExpungedMessages().Count()==deletedcount)
       
   553 			{
       
   554 			folderInfo->ResetExpungedMessages();
       
   555 
       
   556 			// store updated remote mailbox size.
       
   557 			iMailboxSize = folderInfo->Exists();
       
   558 			SetEntryL(iMailboxId);
       
   559 			TMsvEmailEntry entry = iServerEntry.Entry();
       
   560 			entry.SetRemoteFolderEntries(folderInfo->Exists());
       
   561 			ChangeEntryL(entry);
       
   562 			}
       
   563 
       
   564 		iSyncState = CImapSyncManager::EFolderLocalExpunge;
       
   565 		iNextAction = CImapSyncManager::ENotSyncing;
       
   566 		}
       
   567 		// fall through...
       
   568 	case CImapSyncManager::ENotSyncing:
       
   569 		{
       
   570 		iSyncState = CImapSyncManager::ENotSyncing;
       
   571 		Complete(iStatus.Int());
       
   572 		}
       
   573 		break;
       
   574 	default:
       
   575 		break;
       
   576 		}
       
   577 	}
       
   578 
       
   579 /**
       
   580 Processing of the returned message ids after the completion of the IMAP SEARCH command.
       
   581 If there are more messages ids to be look at then the SEARCH commands will be call again.
       
   582 This call could set this folder object active
       
   583 
       
   584 @return ETrue if the call has resulted in the folder object being made Active.
       
   585 */
       
   586 TBool CImapFolder::CheckAndProgressMessageSyncL()
       
   587 	{
       
   588 	iFolderPosition += KImapUidSearchSize;
       
   589 
       
   590 	__LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Session Search command completed: %d", iFolderPosition));
       
   591 
       
   592 	// Check to see if we have all the messages.
       
   593 	if(iFolderPosition >= iMailboxSize)
       
   594 		{
       
   595 		__LOG_TEXT(iSavedSession->LogId(), "ImapFolder: UID search complete");
       
   596 
       
   597 		//update the progress object
       
   598 		iMsgsToDo = iMatchingMessageIds.Count();
       
   599 
       
   600 		iSyncState = CImapSyncManager::ESyncOld;
       
   601 		// We have the full list of remote UIDs - fall through.
       
   602 		}
       
   603 	else
       
   604 		{
       
   605 		// Should be able to hit this code if KImapUidSearchSize is reduced to < the size
       
   606 		// of the remote mailbox (iMailboxSize)
       
   607 		// SearchString non 0 means a refined UID SEARCH is required
       
   608 		
       
   609 		if(iImapSettings.SearchString().Length() != 0)
       
   610 			{
       
   611 			// Refined search required
       
   612 			// Still uses the indexes but appends user specified search criteria
       
   613 			RBuf8 tempstr;
       
   614 			tempstr.CleanupClosePushL();
       
   615 					
       
   616 			TPtrC8 searchstr = iImapSettings.SearchString();
       
   617 			
       
   618 			TInt tempstrLen = KImapFetchHeaderRangeSearch().Length() + KImapMaxIntChars + KImapMaxIntChars + searchstr.Length();
       
   619  			tempstr.CreateL(tempstrLen);			
       
   620 			tempstr.Format(KImapFetchHeaderRangeSearch, iFolderPosition+1, Min(iMailboxSize,iFolderPosition+KImapUidSearchSize), &searchstr);
       
   621 			
       
   622 			__LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Refined UID search - get next manageable block of UIDs: %S", &tempstr));
       
   623 			iSavedSession->SearchL(iStatus, tempstr, iMatchingMessageIds);
       
   624 			
       
   625 			// Go active
       
   626 			SetActive();
       
   627 			
       
   628 			CleanupStack::PopAndDestroy(&tempstr);
       
   629 			return ETrue;
       
   630 			}
       
   631 		else
       
   632 			{
       
   633 			// Normal unrefined SEARCH. Will pull back all the UIDs between indexes
       
   634 			// Perform a UID search on this folder.
       
   635 			RBuf8 tempstr;
       
   636 			tempstr.CleanupClosePushL();
       
   637 				
       
   638 			TInt tempstrLen = KImapFetchHeaderRange().Length() + KImapMaxIntChars + KImapMaxIntChars;
       
   639 			tempstr.CreateL(tempstrLen);
       
   640 			tempstr.Format(KImapFetchHeaderRange, iFolderPosition+1, Min(iMailboxSize,iFolderPosition+KImapUidSearchSize));
       
   641 			
       
   642 			__LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Unrefined UID search - get next manageable block of UIDs: %S", &tempstr));
       
   643 			iSavedSession->SearchL(iStatus, tempstr, iMatchingMessageIds);
       
   644 			
       
   645 			// Go active
       
   646 			SetActive();
       
   647 			
       
   648 			CleanupStack::PopAndDestroy(&tempstr);
       
   649 			return ETrue;
       
   650 			}
       
   651 		}
       
   652 	return EFalse;
       
   653 	}
       
   654 
       
   655 /**
       
   656 Sets number of messages to be sync. If there is a search string or the sync limit is outside
       
   657 the mailbox size then everything will be sync. i.e. the sync limit will be set to 0.
       
   658 
       
   659 */
       
   660 TInt CImapFolder::GetSyncThreshold()
       
   661 	{
       
   662 	// If no UID Search string defined then the old logic is used
       
   663 	if((iImapSettings.SearchString().Length() == 0) && (iSyncLimit > KImImapSynchroniseAll))
       
   664 		{	
       
   665 		if(iSyncLimit < iMailboxSize)
       
   666 			{
       
   667 			return (iMailboxSize - iSyncLimit);
       
   668 			}
       
   669 		}
       
   670 
       
   671 	return 0;
       
   672 	}
       
   673 
       
   674 /**
       
   675 Go through the list of found message ids and work out if each message is within the sync limit
       
   676 as defined by the user. If a message isn't within the limit it will be removed if it hasn't been fetched.
       
   677 
       
   678 @param aSyncThreshold
       
   679 The number of messages that will define the sync limit
       
   680 */
       
   681 void CImapFolder::CheckMessagesInRangeL(TInt aSyncThreshold)
       
   682 	{
       
   683 	// At this stage (prior to truncation), we have a full list of all the message
       
   684 	// UIDs in the remote folder. We also have a list of all UIDs for messages in
       
   685 	// the local folder. With these lists, we can establish which local messages
       
   686 	// have been orphaned and which have not using the following rules.
       
   687 
       
   688 	// * If the remote message is no longer there:
       
   689 	//		(1) Then the local message is orphaned (always).
       
   690 	// * If the remote message is still there and it is not one of the N most recent:
       
   691 	//		(2) If the local message has body parts do nothing.
       
   692 	//		(3) If the local message does not have body parts, then it is orphaned
       
   693 	//		    unless is is a message we have selected for download.
       
   694 	// * If the remote message is still there and it is one of the N most recent:
       
   695 	//		(4) Do nothing.
       
   696 
       
   697 	// Search through the local folder list while checking the remote folder list.
       
   698 
       
   699 	TInt localcount = iFolderIndex->Size();
       
   700 	TInt resultscount = iMatchingMessageIds.Count();
       
   701 
       
   702 	iFolderPosition = 0;
       
   703 
       
   704 	iDeletedMessageIds.Reset();
       
   705 	
       
   706 	for(TInt localloop = 0; localloop < localcount; ++localloop)
       
   707 		{
       
   708 		// Go through the list of index, if can't find matching in found ids then mark for delete.
       
   709 		TInt resultsloop = 0;//listprogress;
       
   710 		TBool removeThis = EFalse;
       
   711 		TBool folderPositionFound = EFalse;
       
   712 
       
   713 		while(resultsloop < resultscount)
       
   714 			{
       
   715 			TUint remoteUid = iMatchingMessageIds[resultsloop];
       
   716 			TUint localUid = (*iFolderIndex)[localloop].iUid;
       
   717 
       
   718 			// Check the synclimit
       
   719 			TBool inSyncRange = (resultsloop >= aSyncThreshold);
       
   720 			if((remoteUid > localUid) || (remoteUid > iHighestUid))
       
   721 				{
       
   722 				break;
       
   723 				}
       
   724 			if(remoteUid == localUid)
       
   725 				{
       
   726 				// Found id, sets new lower limit then exit loop
       
   727 				folderPositionFound = ETrue;
       
   728 				
       
   729 				// Check for if message is in the syncrange
       
   730 				if(!inSyncRange)
       
   731 					{
       
   732 					// Here the remote uid matches the local uid, but the message falls outside
       
   733 					// of the N most recent messages. See cases (2) & (3).
       
   734 					SetEntryL((*iFolderIndex)[localloop].iMsvId);
       
   735 					TMsvEmailEntry message(iServerEntry.Entry());
       
   736 					// Does message have any downloaded parts?
       
   737 					if(!message.Complete() && 
       
   738 						!message.BodyTextComplete())
       
   739 						{
       
   740 						// The local message does not have any body parts and
       
   741 						// is not selected for download, so it is orphaned.
       
   742 						// See case (3) above.
       
   743 						__LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Local message (%d) is only header and not selected for download, deleting", (*iFolderIndex)[localloop].iMsvId));
       
   744 						removeThis = ETrue;
       
   745 						iOrphanedMessages++;
       
   746 						}
       
   747 					}
       
   748 				break;
       
   749 				}
       
   750 
       
   751 			++resultsloop;
       
   752 			}
       
   753 		if(!folderPositionFound || removeThis)
       
   754 			{
       
   755 			// Saved the index position of the message to be deleted from the local view
       
   756 			__LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Local message (%d) marked for deleting, loop = %d", (*iFolderIndex)[localloop].iMsvId, localloop));
       
   757 			iDeletedMessageIds.Append(localloop);
       
   758 			}
       
   759 		else
       
   760 			{
       
   761 				iMsgsDone++;
       
   762 			}
       
   763 			
       
   764 		}
       
   765 	
       
   766 	}
       
   767 	
       
   768 
       
   769 /**
       
   770 Go through the list of found remote message ids and work out if each message header has been fetched or not.
       
   771 Messages that are missing from the local index will be added to a list of messages to be fetch.
       
   772 */
       
   773 void CImapFolder::CheckForMissingMessagesUidsL()
       
   774 	{
       
   775 	iMissingMessageIds.Reset();
       
   776 
       
   777 	if(iFolderIndex->Size() == 0)
       
   778 		{
       
   779 		return;
       
   780 		}
       
   781 		
       
   782 	TInt remotecount = iMatchingMessageIds.Count();
       
   783 
       
   784 	for(TInt remoteloop = 0; remoteloop < remotecount; ++remoteloop)
       
   785 		{
       
   786 		TUint remoteUid = iMatchingMessageIds[remoteloop];
       
   787 
       
   788 		if(iFolderIndex->FindMsg(remoteUid) == 0)
       
   789 			{
       
   790 			iMissingMessageIds.Append(remoteUid);
       
   791 			}
       
   792 		}
       
   793 	}
       
   794 /**
       
   795 Cancels any outstanding asynchronous service requests.
       
   796 */
       
   797 void CImapFolder::DoCancel()
       
   798 	{
       
   799 	iSavedSession->Cancel();
       
   800 	CMsgActive::DoCancel();
       
   801 	}
       
   802 
       
   803 /**
       
   804 Called when the requested operation is completed.
       
   805 Cleans up data members used only during synchronisation
       
   806 */
       
   807 void CImapFolder::DoComplete(TInt& /*aStatus*/)
       
   808 	{
       
   809 	iCachedEntryData->Reset();
       
   810 	iSelection->Reset();
       
   811 	iFolderIndex->Reset();
       
   812 	iMatchingMessageIds.Reset();
       
   813 	iDeletedMessageIds.Reset();
       
   814 	iMessageFlagInfoArray.Reset();
       
   815 
       
   816 	// iSessionFolderInfo is not owned by this class,
       
   817 	// Set to NULL as it should never be assumed to exist.
       
   818 	iSessionFolderInfo = NULL;
       
   819 	}
       
   820 
       
   821 /**
       
   822 Returns the full mailbox path description from the Message server
       
   823 
       
   824 @return Pointer to the descriptor object
       
   825 */
       
   826 EXPORT_C TDesC& CImapFolder::FullFolderPathL()
       
   827 	{
       
   828 	if(iFullFolderPath.Length() == 0)
       
   829 		{
       
   830 		HBufC16* temp = MakePathL(ETrue);
       
   831 		iFullFolderPath.Close();
       
   832 		iFullFolderPath.Assign(temp);
       
   833 		}
       
   834 
       
   835 	return iFullFolderPath;
       
   836 	}
       
   837 
       
   838 /**
       
   839 Returns the mailbox uid from the Message server
       
   840 
       
   841 @return The mailbox UID;
       
   842 */
       
   843 EXPORT_C TMsvId CImapFolder::MailboxId()
       
   844 	{
       
   845 	return iMailboxId;
       
   846 	}
       
   847 
       
   848 /**
       
   849 Issue the IMAP select command to select this folder using the supplied session.
       
   850 The session must already have been opened and connected.
       
   851 
       
   852 NOTE:  The client TRequestStatus is passed through to the CImapSession.
       
   853        Therefore a CImapSession::Cancel() must be called to cancel the
       
   854        SELECT operation.
       
   855 NOTE:  UpdateSessionFolderInfoL() should be called following completion
       
   856 	   of the select operation. And definitely prior to synchronising
       
   857 	   the folder (see below)
       
   858 
       
   859 @param aStatus 
       
   860 The status request object to be use to send the completion signal. 
       
   861 This is passed through to the session object.
       
   862 @param aSession 
       
   863 The session to be use for the SELECT command
       
   864 */
       
   865 EXPORT_C void CImapFolder::SelectL(TRequestStatus& aStatus, CImapSession& aSession, TBool aSelectInbox)
       
   866 	{
       
   867 	// Construct the info object for the select command then just pass it on.
       
   868 	CImapFolderInfo* folderInfo = CImapFolderInfo::NewL();
       
   869 	CleanupStack::PushL(folderInfo);
       
   870 	folderInfo->SetMsvId(iMailboxId);
       
   871 	folderInfo->SetNameL(FullFolderPathL());
       
   872 	
       
   873 	// Issue the SELECT - the session takes immediate 
       
   874 	// ownership of the folder info object.
       
   875 	CleanupStack::Pop(folderInfo);
       
   876 	aSession.SelectL(aStatus, folderInfo, aSelectInbox);
       
   877 	}
       
   878 
       
   879 
       
   880 /**
       
   881 Issue the IMAP select command to select this folder using the supplied session.
       
   882 The session must already have been opened and connected.
       
   883 
       
   884 NOTE:  The client TRequestStatus is passed through to the CImapSession.
       
   885        Therefore a CImapSession::Cancel() must be called to cancel the 
       
   886        EXAMINE operation.
       
   887 NOTE:  UpdateSessionFolderInfoL() should be called following completion
       
   888 	   of the EXAMINE operation.
       
   889 
       
   890 @param aStatus 
       
   891 The status request object to be use to send the completion signal. 
       
   892 This is passed through to the session object.
       
   893 @param aSession 
       
   894 The session to be use for the EXAMINE command
       
   895 */
       
   896 EXPORT_C void CImapFolder::ExamineL(TRequestStatus& aStatus, CImapSession& aSession)
       
   897 	{
       
   898 	// Construct the info object for the select command then just pass it on.
       
   899 	CImapFolderInfo* folderInfo = CImapFolderInfo::NewL();
       
   900 	CleanupStack::PushL(folderInfo);
       
   901 	folderInfo->SetMsvId(iMailboxId);
       
   902 	folderInfo->SetNameL(FullFolderPathL());
       
   903 	
       
   904 	// Issue the EXAMINE - the session takes immediate 
       
   905 	// ownership of the folder info object.
       
   906 	CleanupStack::Pop(folderInfo);
       
   907 	aSession.ExamineL(aStatus, folderInfo);
       
   908 	}
       
   909 
       
   910 /**
       
   911 Updates the remote folder mirror with status information about the folder 
       
   912 on the remote server. Called at the start of the synchronisation process.
       
   913 
       
   914 Panics if the folder is not selected folder on the passed session.
       
   915 
       
   916 NOTE: MUST be called prior to synchronising (done internally)
       
   917 
       
   918 NOTE: updates the context of the CMsvServerEntry to the folder.
       
   919 
       
   920 @param aSession - the session that has SELECTed this folder
       
   921 */
       
   922 EXPORT_C void CImapFolder::UpdateSessionFolderInfoL(CImapSession& aSession)
       
   923 	{
       
   924 	iSessionFolderInfo = aSession.SelectedFolderInfo();
       
   925 	if(iSessionFolderInfo)
       
   926 		{
       
   927 		__ASSERT_ALWAYS(iSessionFolderInfo->MsvId()==iMailboxId, User::Invariant());
       
   928 		
       
   929 		iMailboxSize = iSessionFolderInfo->Exists();
       
   930 		// Need to be save away. NOT Done on Session.
       
   931 		SetEntryL(iMailboxId);
       
   932 		TMsvEmailEntry entry = iServerEntry.Entry();
       
   933 		entry.SetRemoteFolderEntries(iSessionFolderInfo->Exists());
       
   934 		if(entry.UID() != iSessionFolderInfo->UidValidity())
       
   935 			{
       
   936 			entry.SetValidUID(EFalse);
       
   937 			}
       
   938 		entry.SetRecentIMAP4Flag(iSessionFolderInfo->Recent());
       
   939 		entry.SetUnreadIMAP4Flag(iSessionFolderInfo->Unseen());
       
   940 		ChangeEntryL(entry);
       
   941 		}
       
   942 	}
       
   943 
       
   944 /**
       
   945 This can be called at any time with a given IMAP session to find out if the folder has 
       
   946 changed in any way since the last sync.
       
   947 
       
   948 Panics if the folder is not selected folder on the passed session.
       
   949 
       
   950 @return
       
   951 Returns true if any of Exists, Recent or Expunged counts are non-zero.
       
   952 */
       
   953 EXPORT_C TBool CImapFolder::Changed(CImapSession& aSession)
       
   954 	{
       
   955 	iSessionFolderInfo = aSession.SelectedFolderInfo();
       
   956 	if(iSessionFolderInfo)
       
   957 		{
       
   958 		__ASSERT_ALWAYS(iSessionFolderInfo->MsvId()==iMailboxId, User::Invariant());
       
   959 
       
   960 		// True if the exists count has changed
       
   961 		// or if recent or expunged counts are non-zero
       
   962 		TBool existChanged = (iMailboxSize != iSessionFolderInfo->Exists());
       
   963 		TBool flagChanged = iSessionFolderInfo->MessageFlagsChanged();
       
   964 		TBool otherChanged = (existChanged || 
       
   965 			     				iSessionFolderInfo->Recent() || 
       
   966 			     				iSessionFolderInfo->ExpungedMessages().Count());
       
   967 			     				
       
   968 		iFlagChangedOnly = (flagChanged && !(otherChanged));
       
   969 		
       
   970 		return ( otherChanged || flagChanged );
       
   971 		}
       
   972 	return EFalse;
       
   973 	}
       
   974 
       
   975 /**
       
   976 Clears the counts that indicate that an event has occurred on the remote server
       
   977 for the selected folder since the last sync operation.
       
   978 
       
   979 This method is called during synchronisation such that subsequent changes are
       
   980 identified.
       
   981 */
       
   982 void CImapFolder::ClearChangeCounts()
       
   983 	{
       
   984 	if(iSessionFolderInfo)
       
   985 		{
       
   986 		// Do not clear exists count 
       
   987 		iSessionFolderInfo->SetRecent(0);
       
   988 		iSessionFolderInfo->ResetExpungedMessages();
       
   989 		iSessionFolderInfo->SetMessageFlagsChanged(EFalse);
       
   990 		}
       
   991 	iFlagChangedOnly = EFalse;
       
   992 	}
       
   993 
       
   994 /** 
       
   995 Set the current entry context on the Message server
       
   996 
       
   997 @param aId
       
   998 The entry id to set in the Message server
       
   999 @leave KErrNotFound if the entry does not exist or KErrLocked if the entry is locked.
       
  1000 */
       
  1001 void CImapFolder::SetEntryL(const TMsvId aId)
       
  1002 	{
       
  1003 	User::LeaveIfError(iServerEntry.SetEntry(aId));
       
  1004 	}
       
  1005 
       
  1006 /**
       
  1007 Sets the context's index entry to the specified values on the Message server
       
  1008 
       
  1009 @param  aEntry
       
  1010 The new details for the entry.
       
  1011 @leave KErrAccessDenied - the entry is read only (deleted entry, standard folder, or locked); 
       
  1012 KErrNotSupported - aEntry is invalid or the ID specified in aEntry is not the same as the context ID
       
  1013 or no context has been set for the object; 
       
  1014 KErrNoMemory - a memory allocation failed. 
       
  1015 */
       
  1016 void CImapFolder::ChangeEntryL(const TMsvEntry& aEntry)
       
  1017 	{
       
  1018 	User::LeaveIfError(iServerEntry.ChangeEntry(aEntry));
       
  1019 	}
       
  1020 
       
  1021 /**
       
  1022 Change entry in bulk mode (i.e no index file commit. no notify)
       
  1023 
       
  1024 @param  aEntry
       
  1025 The new details for the entry.
       
  1026 @leave KErrAccessDenied - the entry is read only (deleted 
       
  1027 entry, standard folder, or locked); 
       
  1028 KErrNotSupported - aEntry is invalid or the 
       
  1029 ID specified in aEntry is not the same as the context ID or no context has been 
       
  1030 set for the object; 
       
  1031 KErrNoMemory - a memory allocation failed.
       
  1032 */
       
  1033 void CImapFolder::ChangeEntryBulkL(const TMsvEntry& aEntry)
       
  1034 	{
       
  1035 	User::LeaveIfError(iServerEntry.ChangeEntryBulk(aEntry));
       
  1036 	}
       
  1037 
       
  1038 /**
       
  1039 Get a list of ids of the children from the current context of the message server.
       
  1040 
       
  1041 @param aSelection
       
  1042 A reference to the email entries selection object which will be filled with the ids of the child entries.
       
  1043 @leave Error from CMsvServerEntry::GetChildren.
       
  1044 */
       
  1045 void CImapFolder::GetChildrenL(CMsvEntrySelection& aSelection)
       
  1046 	{
       
  1047 	User::LeaveIfError(iServerEntry.GetChildren(aSelection));
       
  1048 	}
       
  1049 
       
  1050 /**
       
  1051 Delete a local message.
       
  1052 
       
  1053 @param aMessage
       
  1054 The TMsvId of the message to be removed from the message server.
       
  1055 */
       
  1056 EXPORT_C void CImapFolder::DeleteMessageL(const TMsvId aMessage)
       
  1057 	{
       
  1058  	if(aMessage == KMsvNullIndexEntryId)
       
  1059 		{
       
  1060 		// Attempted delete of null entry
       
  1061  		return;
       
  1062 		}
       
  1063 	// Delete message and all subparts: first, move to parent
       
  1064 	SetEntryL(aMessage);
       
  1065 
       
  1066 	SetEntryL(iServerEntry.Entry().Parent());
       
  1067 
       
  1068 	// Do not leave when entry is in use 
       
  1069 	TInt err (iServerEntry.DeleteEntry(aMessage));
       
  1070 	if(err == KErrInUse)
       
  1071 		{
       
  1072 		// Dont leave if err = KErrInUse
       
  1073 		}
       
  1074 	else
       
  1075 		{
       
  1076 		User::LeaveIfError(err);
       
  1077 		}
       
  1078 	}
       
  1079 
       
  1080 /**
       
  1081 Add a message UID to the list of messages that should have the SEEN flag sets during the SYNC FLAGS stage.
       
  1082 
       
  1083 @param aMessage
       
  1084 The UID of the message to be added to the list of messages to have the SEEN flag set.
       
  1085 */
       
  1086 EXPORT_C void CImapFolder::AppendSetSeenL(const TUint32 aMessage)
       
  1087 	{
       
  1088 	if(iSetSeenList)
       
  1089 		{
       
  1090 		iSetSeenList->AppendL(aMessage);
       
  1091 		}
       
  1092 	else
       
  1093 		{
       
  1094 		iSetSeenList = new(ELeave) RArray<TUint>(4);
       
  1095 		iSetSeenList->AppendL(aMessage);
       
  1096 		}
       
  1097 	}
       
  1098 	
       
  1099 /**
       
  1100 Add a message UID to the list of messages that should have the SEEN flag cleared during the SYNC FLAGS stage.
       
  1101 
       
  1102 @param aMessage
       
  1103 The UID of the message to be added to the list of messages to have the SEEN flag cleared.
       
  1104 */
       
  1105 EXPORT_C void CImapFolder::AppendClearSeenL(const TUint32 aMessage)
       
  1106 	{
       
  1107 	if(iClearSeenList)
       
  1108 		{
       
  1109 		iClearSeenList->AppendL(aMessage);
       
  1110 		}
       
  1111 	else
       
  1112 		{
       
  1113 		iClearSeenList = new(ELeave) RArray<TUint>(4);
       
  1114 		iClearSeenList->AppendL(aMessage);
       
  1115 		}
       
  1116 	}
       
  1117 
       
  1118 /**
       
  1119 Get MESSAGE ONLY children of a folder. Ignore shadows as they are not going to be synced against the server
       
  1120 */
       
  1121 void CImapFolder::GetMessageChildrenL(const TMsvId aFolder, CMsvEntrySelection* aChildren)
       
  1122 	{	
       
  1123 	aChildren->Reset();
       
  1124 	// Get *all* the children
       
  1125 	SetEntryL(aFolder);
       
  1126 	GetChildrenL(*aChildren);
       
  1127 
       
  1128 	delete iCachedEntryData;
       
  1129 	iCachedEntryData = NULL;
       
  1130 
       
  1131 	iCachedEntryData = new(ELeave) RArray<TMsvCacheData>(5);
       
  1132 
       
  1133 	// Go through them, checking to see if they're messages and removing ones that aren't
       
  1134 	TInt pos = 0;
       
  1135 	while(pos < aChildren->Count())
       
  1136 		{
       
  1137 		TMsvEntry* entryPtr;
       
  1138 		TMsvId id = (*aChildren)[pos];
       
  1139 		User::LeaveIfError(iServerEntry.GetEntryFromId(id, entryPtr));
       
  1140 
       
  1141 		// Is it a message? And is it real (not shadow)
       
  1142 		if(entryPtr->iType != KUidMsvMessageEntry ||
       
  1143 			entryPtr->iRelatedId != KMsvNullIndexEntryId )
       
  1144 			{
       
  1145 			// No, remove it
       
  1146 			aChildren->Delete(pos,1);
       
  1147 			}
       
  1148 		else
       
  1149 			{
       
  1150 			//cache two parts of the TMsvEntry data to avoid having to refind it later
       
  1151 			TMsvCacheData data;
       
  1152 			data.iOrphan = ((TMsvEmailEntry)(*entryPtr)).Orphan();
       
  1153 			data.iUid = ((TMsvEmailEntry)(*entryPtr)).UID();
       
  1154 			iCachedEntryData->AppendL(data);
       
  1155 			// Next entry
       
  1156 			pos++;
       
  1157 			}
       
  1158 		}
       
  1159 	}
       
  1160 
       
  1161 /**
       
  1162 Populates the entry selection with messages that are eligible for auto-fetch.
       
  1163 Auto fetch is performed as a second synchronisation stage, following the header
       
  1164 synchronisation. Eligible messages are synchronised according to defined 
       
  1165 download rules. Note that this method does not filter the returned entry
       
  1166 selection according to these rules.
       
  1167 
       
  1168 @return TInt - number of meesages in the updated selection
       
  1169 */
       
  1170 EXPORT_C TInt CImapFolder::GetFetchMessageChildrenL(CMsvEntrySelection& aSelection)
       
  1171 	{	
       
  1172 	aSelection.Reset();
       
  1173 	// Get *all* the children
       
  1174 	SetEntryL(iMailboxId);
       
  1175 	GetChildrenL(aSelection);
       
  1176 
       
  1177 	// Go through them, checking to see if they're messages and removing ones that aren't
       
  1178 	TInt pos = 0;
       
  1179 	TMsvEntry* entryPtr;
       
  1180 	while(pos < aSelection.Count())
       
  1181 		{
       
  1182 		TMsvId id = (aSelection)[pos];
       
  1183 		User::LeaveIfError(iServerEntry.GetEntryFromId(id, entryPtr));
       
  1184 
       
  1185 		// Remove entry from the selection if:
       
  1186 		//   It is not a message
       
  1187 		//   It is not real (ie it is a shadow entry)
       
  1188 		//   If has been previously fetched.
       
  1189 		TBool previouslyFetched = ((TMsvEmailEntry*)entryPtr)->ValidUID();
       
  1190 		if(entryPtr->iType != KUidMsvMessageEntry ||
       
  1191 			entryPtr->iRelatedId != KMsvNullIndexEntryId ||
       
  1192 			previouslyFetched)
       
  1193 			{
       
  1194 			aSelection.Delete(pos,1);
       
  1195 			}
       
  1196 		else
       
  1197 			{
       
  1198 			++pos;
       
  1199 			}
       
  1200 		}
       
  1201 	
       
  1202 	return aSelection.Count();
       
  1203 	}
       
  1204 
       
  1205 /**
       
  1206 Transfers the current selection into the iFolderIndex, and sorts it by UID.
       
  1207 */
       
  1208 void CImapFolder::MakeSortedFolderIndexL(TBool aUseCachedEntryData)
       
  1209 	{
       
  1210 		
       
  1211 	TInt noofchildren = iSelection->Count();
       
  1212 	
       
  1213 	// Reset folder index
       
  1214 	iFolderIndex->SetSizeL(noofchildren);
       
  1215 	TInt acounter = 0;
       
  1216 
       
  1217 	if(!aUseCachedEntryData)
       
  1218 		{ //can't rely on iCachedEntryData
       
  1219 		TMsvEntry* entryPtr;
       
  1220 		TMsvId id;
       
  1221 		for(acounter = 0; acounter < noofchildren; acounter++)
       
  1222 			{
       
  1223 			// Save UID/TMsvId of this entry
       
  1224 			id = (*iSelection)[acounter];
       
  1225 			User::LeaveIfError(iServerEntry.GetEntryFromId(id, entryPtr));
       
  1226 			(*iFolderIndex)[acounter].iUid = ((TMsvEmailEntry)(*entryPtr)).UID();
       
  1227 			(*iFolderIndex)[acounter].iMsvId = id;
       
  1228 			}
       
  1229 		}
       
  1230 	else
       
  1231 		{
       
  1232 		for(acounter = 0; acounter < noofchildren; acounter++)
       
  1233 			{
       
  1234 			// Save UID/TMsvId of this entry
       
  1235 			(*iFolderIndex)[acounter].iUid = (*iCachedEntryData)[acounter].iUid;
       
  1236 			(*iFolderIndex)[acounter].iMsvId = (*iSelection)[acounter];
       
  1237 			}
       
  1238 		}
       
  1239 
       
  1240 	// Sort it by UID
       
  1241 	iFolderIndex->Sort();
       
  1242 
       
  1243 	// Check for any duplicate UIDs (ie, a dud netscape server)
       
  1244 	TMsvEntry* entryPtr;
       
  1245 	TMsvEntry* nextEntryPtr;
       
  1246 	for(acounter = 1; acounter < noofchildren; acounter++)
       
  1247 		{
       
  1248 		if((*iFolderIndex)[acounter].iUid != 0 && (*iFolderIndex)[acounter].iUid == (*iFolderIndex)[acounter-1].iUid)
       
  1249 			{
       
  1250 			if(!aUseCachedEntryData)
       
  1251 				{
       
  1252 				// Get the TMsvEntry for the message/folder
       
  1253 				User::LeaveIfError(iServerEntry.GetEntryFromId((*iFolderIndex)[acounter].iMsvId,entryPtr));
       
  1254 				User::LeaveIfError(iServerEntry.GetEntryFromId((*iFolderIndex)[acounter-1].iMsvId,nextEntryPtr));
       
  1255 				// Check if type of TMsvEntry and type of next TMsvEntry are both Messages
       
  1256 				if( entryPtr->iType.iUid == nextEntryPtr->iType.iUid && entryPtr->iType.iUid == KUidMsvMessageEntryValue)
       
  1257 					{
       
  1258 					User::Leave(KErrCorrupt);
       
  1259 					}
       
  1260 				}
       
  1261 			else
       
  1262 				{
       
  1263 				User::Leave(KErrCorrupt);
       
  1264 				}
       
  1265 			}
       
  1266 			
       
  1267 		}
       
  1268 	}
       
  1269 
       
  1270 /**
       
  1271 Synchronise the local view of the contents with that of the remote folder. 
       
  1272 If the folder is not the currently in SELECTED state on the CImapSesssion, 
       
  1273 the first step should be to issue a SELECT command via the IMAP Session
       
  1274 */
       
  1275 EXPORT_C void CImapFolder::SynchroniseL(TRequestStatus& aStatus, CImapSession& aSession, TBool aNewOnly, TInt aDeleteOption)
       
  1276 	{
       
  1277 	__LOG_TEXT(aSession.LogId(), "SyncMan: Starting full IMAP Sync");
       
  1278 	
       
  1279 	//initialise counters
       
  1280 	iMsgsDone=0;
       
  1281 	iMsgsToDo=0;
       
  1282 	
       
  1283 	iHeadersFetched=0;
       
  1284 	iOrphanedMessages=0;
       
  1285 	iRemoteMessagesDeleteTagged=0;
       
  1286 	
       
  1287 	// Saved the calling status request
       
  1288 	iSavedSession = &aSession;
       
  1289 	iDeleteOption = aDeleteOption;
       
  1290 	
       
  1291 	// Set the Synchronisation states
       
  1292 	iSyncState = CImapSyncManager::ESynchronise;
       
  1293 	iNextAction = CImapSyncManager::ESynchronise;
       
  1294 	iMatchingMessageIds.Reset();
       
  1295 	
       
  1296 	// Update the remote folder info and clear the change indication counts
       
  1297 	UpdateSessionFolderInfoL(*iSavedSession);
       
  1298 	ClearChangeCounts();
       
  1299 
       
  1300 	// Some pre-bits that need doing - get the children & count them
       
  1301 	// Get the folder info for the selected folder
       
  1302 	SetEntryL(iMailboxId);
       
  1303 	TMsvEmailEntry message = iServerEntry.Entry();
       
  1304 	GetMessageChildrenL(iMailboxId, iSelection);
       
  1305 	TInt noofchildren = iSelection->Count();
       
  1306 
       
  1307 	// Check if new flags for the current selection should be cleared.
       
  1308 	ClearNewFlagsIfRequiredL();
       
  1309 		
       
  1310 	/*
       
  1311 	First of all check the UIDVALIDITY of the mirror and the 
       
  1312 	remote folder match - this is indicated by message.ValidUID() returning true.
       
  1313 	If not, we have to delete everything in the local mirror and start again.
       
  1314 	We also do this if there are 0 messages in the remote mailbox (0 EXISTS)
       
  1315 	and there are messages locally
       
  1316 	*/
       
  1317 	if(!message.ValidUID() || iMailboxSize == 0)
       
  1318 		{
       
  1319 		/*
       
  1320 		They don't match: do we have local children?
       
  1321 		If we were doing a new-only sync, change this to a full sync as the 
       
  1322 		UIDVALIDITY shows major changes.
       
  1323 		*/
       
  1324 		aNewOnly = EFalse;
       
  1325 
       
  1326 		if(noofchildren)
       
  1327 			{
       
  1328 		    // We've got local children then delete them
       
  1329 			for(TInt a = 0; a < noofchildren; a++)
       
  1330 				{
       
  1331 				// We should be skipping locally generated messages. i.e. Offline operations
       
  1332 				DeleteMessageL((*iSelection)[a]);
       
  1333 				}
       
  1334 
       
  1335 			// Reset the number of children as this may have changed.
       
  1336 			GetMessageChildrenL(iMailboxId, iSelection);
       
  1337 			noofchildren = iSelection->Count();
       
  1338 			}
       
  1339 
       
  1340 		// Match the remote's UIDVALIDITY: 
       
  1341 		// reset the context as it may have been used by the deletion process.
       
  1342 		SetEntryL(iMailboxId);
       
  1343 		if(!message.ValidUID())
       
  1344 			{
       
  1345 			// Do the change if necessary
       
  1346 			message.SetUID(iSessionFolderInfo->UidValidity());
       
  1347 			message.SetValidUID(ETrue);
       
  1348 			ChangeEntryL(message);
       
  1349 			}
       
  1350 		}
       
  1351 
       
  1352 	// Any remote messages? If not, complete now as there's nothing else to do
       
  1353 	if(iMailboxSize == 0)
       
  1354 		{
       
  1355 		// This folder is now sync'ed
       
  1356 		// No need to set seen flags as no messages in remote mailbox
       
  1357 		SyncCompleteL(); 
       
  1358 		Queue(aStatus);
       
  1359 		Complete(KErrNone);
       
  1360 		return;
       
  1361 		}
       
  1362 
       
  1363 	// Start the synchronise with sync'ing old messages: are there any
       
  1364 	// messages in our mirror folder?
       
  1365  	iSyncState = CImapSyncManager::ESyncOld; //EImapStateSynchroniseWait;
       
  1366 	iNextAction = CImapSyncManager::ESyncOld;
       
  1367 
       
  1368 	iSomeUnread = EFalse;
       
  1369 	iHighestUid = 0;
       
  1370 
       
  1371 	// Any children?
       
  1372 	iFolderIndex->Reset();
       
  1373 	if(noofchildren > 0) 
       
  1374 		{
       
  1375 		// Children exist, we need to do an old-sync to check all the messages
       
  1376 		// are still there.
       
  1377 		
       
  1378 		// Build an index of UIDs/TMsvIds currently in the mirror folder, and
       
  1379 		// sort this by UID: this is the order in which we expect the fetch to
       
  1380 		// return UIDs - any missing have been deleted on the server. They may
       
  1381 		// well not be in UID order in the index because locally-appended
       
  1382 		// messages will not have been added to the index in UID order.
       
  1383 		TRAPD(err,MakeSortedFolderIndexL(ETrue));
       
  1384 		if(err != KErrNone)
       
  1385 			{
       
  1386 			// Children exist, need to do old sync
       
  1387 			Queue(aStatus);
       
  1388 			Complete(err);
       
  1389 			return;
       
  1390 			}
       
  1391 
       
  1392 		// Find the highest UID in the index
       
  1393 		iHighestUid = (*iFolderIndex)[noofchildren-1].iUid;
       
  1394 		}
       
  1395 
       
  1396 	// Retrieve folder synchronisation limit.
       
  1397 	if(iSyncManager.EntryIsInbox(iServerEntry.Entry()))
       
  1398 		{
       
  1399 		// Leave iSyncLimit at the maximum if a Search String is set
       
  1400 		// If no Search String is set and this is the inbox, then use the inbox sync limit.
       
  1401 		if(iImapSettings.SearchString().Length() == 0)
       
  1402 			{
       
  1403 			iSyncLimit = iImapSettings.InboxSynchronisationLimit();	
       
  1404 			}
       
  1405 		}
       
  1406 	else
       
  1407 		{
       
  1408 		// Otherwise use the folder sync limit.
       
  1409 		// Leave iSyncLimit at the maximum if a Search String is set
       
  1410 		if(iImapSettings.SearchString().Length() == 0)
       
  1411 			{
       
  1412 			iSyncLimit = iImapSettings.MailboxSynchronisationLimit();
       
  1413 			}
       
  1414 		}
       
  1415 	
       
  1416 	// Call function to create and send the search command for the remote server message ids
       
  1417 	// if there wasn't a command sent then a full sync is needed.
       
  1418 	if(CreateAndSendUIDSearchStringL(aStatus))
       
  1419 		{
       
  1420 		return;
       
  1421 		}
       
  1422 		
       
  1423 	if(noofchildren > 0) 
       
  1424 		{
       
  1425 		// A complete list of the message ids on the remote server is needed.
       
  1426 		if(!aNewOnly && iHighestUid > 0)
       
  1427 			{
       
  1428 			// Do old sync
       
  1429 			iSyncState = CImapSyncManager::ESyncOld;
       
  1430 			iNextAction = CImapSyncManager::ESyncSearch;
       
  1431 			
       
  1432 			iFolderPosition = 0;
       
  1433 			// If a UID Search String is used it looks like this is FULL sync only 
       
  1434 			// so leave as is
       
  1435 			RBuf8 tempstr;
       
  1436 			tempstr.CleanupClosePushL();
       
  1437 			
       
  1438 			TInt tempstrLen = KImapFetchHeaderUIDRange().Length() + KImapMaxIntChars; // KImapFetchHeaderUIDRange provides enough room for "1"
       
  1439 			tempstr.CreateL(tempstrLen);
       
  1440 			tempstr.Format(KImapFetchHeaderUIDRange, 1, iHighestUid);
       
  1441 
       
  1442 			__LOG_FORMAT((iSavedSession->LogId(), "CImapFolder SynchroniseL : Sending Search for old messages with no synclimits: %S", &tempstr));
       
  1443 			
       
  1444 			iSavedSession->SearchL(iStatus, tempstr, iMatchingMessageIds);
       
  1445 			// Go active and note that a send has been queued
       
  1446 			Queue(aStatus);
       
  1447 			SetActive();
       
  1448 			
       
  1449 			CleanupStack::PopAndDestroy(&tempstr);
       
  1450 			return;
       
  1451 			}
       
  1452 		}
       
  1453 
       
  1454 	// There was no need to search for old ids hence do new sync
       
  1455 	SynchroniseNewL();
       
  1456 	Queue(aStatus);
       
  1457 	}
       
  1458 
       
  1459 /**
       
  1460 Find the messages available on the remote server by creating and send the string of messages
       
  1461 to be search for by the session SearchL command.
       
  1462 
       
  1463 @param aStatus
       
  1464 Reference to the request status object to be use for the Search command.
       
  1465 @return ETrue if the search command has been sent and the folder object has been made Active.
       
  1466 */
       
  1467 TBool CImapFolder::CreateAndSendUIDSearchStringL(TRequestStatus& aStatus)
       
  1468 	{
       
  1469 	// Get the user defined UID SEARCH string and if there is one
       
  1470 	// do a refined search.
       
  1471 	if(iImapSettings.SearchString().Length() != 0)
       
  1472 		{
       
  1473 		iSyncState = CImapSyncManager::ESyncSearch;
       
  1474 		iNextAction = CImapSyncManager::ESyncSearch;
       
  1475 		
       
  1476 		iFolderPosition = 0;
       
  1477 
       
  1478 		// Refined search
       
  1479 		RBuf8 tempstr;
       
  1480 		tempstr.CleanupClosePushL();
       
  1481 		
       
  1482 		TPtrC8 searchstr = iImapSettings.SearchString();
       
  1483 		
       
  1484 		TInt tempstrLen = KImapFetchHeaderRangeSearch().Length() + KImapMaxIntChars + searchstr.Length(); // KImapFetchHeaderRangeSearch provides enough room for "1"
       
  1485 		tempstr.CreateL(tempstrLen);		
       
  1486 		tempstr.Format(KImapFetchHeaderRangeSearch, 1, Min(iMailboxSize,KImapUidSearchSize), &searchstr);
       
  1487 		
       
  1488 		__LOG_FORMAT((iSavedSession->LogId(), "CImapFolder SynchroniseL : Sending Search for old messages with search string: %S", &searchstr));
       
  1489 		iSavedSession->SearchL(iStatus, tempstr, iMatchingMessageIds);
       
  1490 		
       
  1491 		// Go active and note that a send has been queued
       
  1492 		Queue(aStatus);
       
  1493 		SetActive();
       
  1494 		
       
  1495 		CleanupStack::PopAndDestroy(&tempstr);
       
  1496 		return ETrue;
       
  1497 		}
       
  1498 	else
       
  1499 		{
       
  1500 		// if no search string we use the old behaviour
       
  1501 		// Check the folder synchronisation limit.
       
  1502 		if(iSyncLimit > KImImapSynchroniseNone)
       
  1503 			{
       
  1504 			// Limited folder synchronisation, perform a UID search.
       
  1505 			iSyncState = CImapSyncManager::ESyncSearch;
       
  1506 			iNextAction = CImapSyncManager::ESyncSearch;
       
  1507 			
       
  1508 			iFolderPosition = 0;
       
  1509 
       
  1510 			// Perform a UID search on this folder.
       
  1511 			RBuf8 tempstr;
       
  1512 			tempstr.CleanupClosePushL();
       
  1513 						
       
  1514 			TInt tempstrLen = KImapFetchHeaderRange().Length() + KImapMaxIntChars; // KImapFetchHeaderRange provides enough room for "1"
       
  1515 			tempstr.CreateL(tempstrLen);			
       
  1516 			tempstr.Format(KImapFetchHeaderRange, 1, Min(iMailboxSize,KImapUidSearchSize));
       
  1517 			
       
  1518 			__LOG_FORMAT((iSavedSession->LogId(), "CImapFolder SynchroniseL : Sending Search for old messages with synclimits: %S", &tempstr));
       
  1519 			iSavedSession->SearchL(iStatus, tempstr, iMatchingMessageIds);
       
  1520 			
       
  1521 			// Go active and note that a send has been queued
       
  1522 			Queue(aStatus);
       
  1523 			SetActive();
       
  1524 			
       
  1525 			CleanupStack::PopAndDestroy(&tempstr);			
       
  1526 			return ETrue;
       
  1527 			}
       
  1528 		else if(iSyncLimit == KImImapSynchroniseNone)
       
  1529 			{
       
  1530 			// No synchronisation required.
       
  1531 			// This folder is now sync'ed
       
  1532 			SyncCompleteL();
       
  1533 			Queue(aStatus);
       
  1534 			iSyncState = CImapSyncManager::ENotSyncing;
       
  1535 			// iSyncLimit=KImImapSynchroniseNone, no sync required
       
  1536 			Complete(KErrNone);
       
  1537 			return ETrue;
       
  1538 			}
       
  1539 		// iSyncLimit <= KImImapSynchroniseAll so Full synchronisation required - fall through.
       
  1540 		}
       
  1541 	return EFalse;
       
  1542 	}
       
  1543 	
       
  1544 /**
       
  1545 Synchronise new messages for a given range.
       
  1546 */
       
  1547 void CImapFolder::SynchroniseRangeL(const TUint32 aLowUid,const TUint32 aHighUid)
       
  1548 	{
       
  1549   
       
  1550  	iSyncState = CImapSyncManager::EFolderSynchronise;
       
  1551  	iNextAction = CImapSyncManager::ESyncFlags;
       
  1552 	iFolderPosition = 0;
       
  1553 
       
  1554 	// First, resize folder index to hold all messages in the folder,
       
  1555 	// as opposed to the old sync list. This will preserve the old
       
  1556 	// contents of the index, which is what we want as it's up-to-date
       
  1557 	// and correct.
       
  1558 	iFolderIndex->SetSizeL(iMailboxSize);
       
  1559 	
       
  1560 	// Create list of priority fields to request
       
  1561 	// If a UID search string has been specified, the we should create the UID FETCH
       
  1562 	// string from the UID integer list.
       
  1563 	if(iImapSettings.SearchString().Length() != 0)
       
  1564 		{
       
  1565 		HBufC8* sequenceSetOnHeap = CImapSession::CreateSequenceSetLC(iMatchingMessageIds);
       
  1566 		iSavedSession->FetchBodyStructureAndHeadersL(iStatus, *sequenceSetOnHeap, KImapSmallHeaderFields(), *this);
       
  1567 		CleanupStack::PopAndDestroy(sequenceSetOnHeap);
       
  1568 		}
       
  1569 	else
       
  1570 		{
       
  1571 		RBuf8 tempstr;
       
  1572 		tempstr.CleanupClosePushL();
       
  1573 		
       
  1574 		TInt tempstrLen = KImapFetchHeaderRange().Length() + KImapMaxIntChars + KImapMaxIntChars;
       
  1575 		tempstr.CreateL(tempstrLen);		
       
  1576 		tempstr.Format(KImapFetchHeaderRange, aLowUid, aHighUid);
       
  1577 		
       
  1578 		iSavedSession->FetchBodyStructureAndHeadersL(iStatus, tempstr, KImapSmallHeaderFields(), *this);
       
  1579 		
       
  1580 		CleanupStack::PopAndDestroy(&tempstr);
       
  1581 		}
       
  1582 	SetActive();
       
  1583 	}
       
  1584 
       
  1585 /**
       
  1586 Synchronise new mesasges from current highest UID to end.
       
  1587 */
       
  1588 void CImapFolder::SynchroniseNewL()
       
  1589 	{
       
  1590 	iSyncState = CImapSyncManager::ESyncNew;
       
  1591 	iNextAction = CImapSyncManager::ESyncFlags;
       
  1592 	iFolderPosition = 0;
       
  1593 
       
  1594 	// First, resize folder index to hold all messages in the folder,
       
  1595 	// as opposed to the old sync list. This will preserve the old
       
  1596 	// contents of the index, which is what we want as it's up-to-date
       
  1597 	// and correct.
       
  1598 	iFolderIndex->SetSizeL(iMailboxSize);
       
  1599 
       
  1600 	// Fetch just the header of the new mails
       
  1601 	RBuf8 tempstr;
       
  1602 	tempstr.CleanupClosePushL();
       
  1603 	
       
  1604 	TInt tempstrLen = KImapFetchHeaderToEnd().Length() + KImapMaxIntChars;
       
  1605 	tempstr.CreateL(tempstrLen);		
       
  1606 	tempstr.Format(KImapFetchHeaderToEnd, iHighestUid + 1);
       
  1607 	
       
  1608 	iSavedSession->FetchBodyStructureAndHeadersL(iStatus, tempstr, KImapSmallHeaderFields(), *this);
       
  1609 	SetActive();
       
  1610 	
       
  1611 	CleanupStack::PopAndDestroy(&tempstr);
       
  1612 	}
       
  1613 
       
  1614 /**
       
  1615 Update iDate in iMailboxId to show the time now (last sync time)
       
  1616 */
       
  1617 void CImapFolder::SyncCompleteL()
       
  1618 	{
       
  1619 	__LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Starting SyncCompleteL");
       
  1620 	// Find entry
       
  1621 	SetEntryL(iMailboxId);
       
  1622 	TMsvEmailEntry message = iServerEntry.Entry();
       
  1623 
       
  1624 	// Find 'now'
       
  1625 	message.iDate.UniversalTime();
       
  1626 
       
  1627 	// Check to see if there has been a change in the number of messages in the remote folder.
       
  1628 	TBool folderSizeChanged = (message.RemoteFolderEntries()!=iMailboxSize);
       
  1629 
       
  1630 	// Set 'unread' flag on folder if there are any unread messages within it
       
  1631 	if(message.Unread() != iSomeUnread || !message.Visible() || folderSizeChanged)
       
  1632 		{
       
  1633 		// Update flags
       
  1634 		message.SetUnread(iSomeUnread);
       
  1635 		message.SetVisible(ETrue);
       
  1636 		message.SetRemoteFolderEntries(iMailboxSize);
       
  1637  		ChangeEntryBulkL(message); // completed at the end of this method
       
  1638 		}
       
  1639 
       
  1640 	// we need to ensure the hierarchy of folders containing this one
       
  1641 	// is now visible. Note previously this incorrectly only did this
       
  1642 	// when we were not in DisconncetedUserMode
       
  1643 	do
       
  1644 		{
       
  1645 		// Move up one
       
  1646 		SetEntryL(message.Parent());
       
  1647 		message=iServerEntry.Entry();
       
  1648 
       
  1649 		// Ensure visibility
       
  1650 		if(!message.Visible())
       
  1651 			{
       
  1652 			message.SetVisible(ETrue);
       
  1653 			ChangeEntryBulkL(message); // completed at the end of this method
       
  1654 			}
       
  1655 		}
       
  1656 	while(message.iType!=KUidMsvServiceEntry);
       
  1657 	
       
  1658 
       
  1659 	// commit any outstanding entries to the index file to complete the bulk
       
  1660 	// synchronization operation
       
  1661 	iServerEntry.CompleteBulk();
       
  1662 	
       
  1663 	// Set the current id to null so that we aren't locking any folders
       
  1664 	SetEntryL(KMsvNullIndexEntryId);
       
  1665 	}
       
  1666 
       
  1667 /**
       
  1668 Reset subscription flags for all children, and recurse into folders
       
  1669 */
       
  1670 void CImapFolder::ResetSubscriptionFlagsL(const TMsvId aFolder)
       
  1671 	{
       
  1672 	// Do this one
       
  1673 	SetEntryL(aFolder);
       
  1674 	TMsvEmailEntry entry = iServerEntry.Entry();
       
  1675 
       
  1676 	// A folder or service? If not, return
       
  1677 	if(entry.iType != KUidMsvServiceEntry &&
       
  1678 		entry.iType != KUidMsvFolderEntry)
       
  1679 		{
       
  1680 		return;
       
  1681 		}
       
  1682 
       
  1683 	// Reset flag if needed
       
  1684 	if(entry.Subscribed())
       
  1685 		{
       
  1686 		// Reset flag and save
       
  1687 		entry.SetSubscribed(EFalse);
       
  1688 		ChangeEntryL(entry);
       
  1689 		}
       
  1690 
       
  1691 	// Any children?
       
  1692 	CMsvEntrySelection *children = new (ELeave) CMsvEntrySelection;
       
  1693 	CleanupStack::PushL(children);
       
  1694 	GetChildrenL(*children);
       
  1695 	if(children->Count())
       
  1696 		{
       
  1697 		// Do each in turn
       
  1698 		for(TInt child = 0; child < children->Count(); child++)
       
  1699 			ResetSubscriptionFlagsL((*children)[child]);
       
  1700 		}
       
  1701 	CleanupStack::PopAndDestroy();
       
  1702 	}
       
  1703 
       
  1704 /**
       
  1705 Performs any outstanding offline delete operations
       
  1706 
       
  1707 deletes from the remote server any messages marked /deleted locally.
       
  1708 */
       
  1709 EXPORT_C void CImapFolder::SyncDeletesL(TRequestStatus& aStatus, CImapSession& aSession)
       
  1710 	{
       
  1711 	iSavedSession = &aSession;
       
  1712 
       
  1713 	SetEntryL(iMailboxId);
       
  1714 	GetMessageChildrenL(iMailboxId, iSelection);
       
  1715 	TRAPD(err,MakeSortedFolderIndexL(ETrue));
       
  1716 	if(err!=KErrNone)
       
  1717 		{
       
  1718 		Queue(aStatus);
       
  1719 		Complete(err);
       
  1720 		return;
       
  1721 		}
       
  1722 	TInt pos = 0;
       
  1723 	TInt deleted = 0;
       
  1724 
       
  1725 	// Build command
       
  1726 	HBufC8* command=HBufC8::NewLC(256);
       
  1727 	RArray<TUint>	deletingMessageIds;
       
  1728 	
       
  1729 	// Start command
       
  1730 	//command->Des().Append(_L8("UID STORE "));
       
  1731 	
       
  1732 	iDeletedMessageIds.Reset();
       
  1733 	iMessageFlagInfoArray.Reset();
       
  1734 
       
  1735 	while(pos < iFolderIndex->Size())
       
  1736 		{
       
  1737 		// Look for messages with deleted flag set
       
  1738 		SetEntryL((*iFolderIndex)[pos].iMsvId);
       
  1739 		if(((TMsvEmailEntry)iServerEntry.Entry()).DeletedIMAP4Flag())
       
  1740 			{
       
  1741 			__LOG_FORMAT((aSession.LogId(), "Message id %x marked as deleted",iServerEntry.Entry().Id()));
       
  1742 			++iRemoteMessagesDeleteTagged;
       
  1743 			
       
  1744 			// Append to the delete list
       
  1745 			TInt64 uid=(TUint)((TMsvEmailEntry)iServerEntry.Entry()).UID();
       
  1746 			deletingMessageIds.Append(uid);
       
  1747 			// index of local message in iFolderIndex to be deleted
       
  1748 			iDeletedMessageIds.Append(pos);
       
  1749 			++deleted;
       
  1750 			}
       
  1751 
       
  1752 		// Next message
       
  1753 		pos++;
       
  1754 		}
       
  1755 
       
  1756 	// Anything deleted?
       
  1757 	if(deleted)
       
  1758 		{
       
  1759 		// Append flags & send command
       
  1760 		_LIT8(KDeleteFlag,"+FLAGS");
       
  1761 		_LIT8(KDeleteItem,"(\\Deleted)");
       
  1762 		command->Des().Append(KDeleteFlag);
       
  1763 		
       
  1764 		HBufC8* sequenceSetOnHeap = CImapSession::CreateSequenceSetLC(deletingMessageIds);
       
  1765 		// Call the STORE function in the session
       
  1766 		aSession.StoreL(iStatus, *sequenceSetOnHeap, KDeleteFlag(), KDeleteItem(), ETrue, iMessageFlagInfoArray);
       
  1767 		CleanupStack::PopAndDestroy(sequenceSetOnHeap);
       
  1768 		
       
  1769 		Queue(aStatus);
       
  1770 		SetActive();
       
  1771 		iSyncState = CImapSyncManager::EFolderEarlyExpunge;
       
  1772 				
       
  1773 		if (iImapSettings.UseExpunge())
       
  1774 			{
       
  1775 			iNextAction = CImapSyncManager::EFolderExpunge;
       
  1776 			}
       
  1777 		else
       
  1778 			{
       
  1779 			iNextAction = CImapSyncManager::EFolderClose;
       
  1780 			}
       
  1781 		}
       
  1782 	else
       
  1783 		{
       
  1784 		// Nothing to do just complete
       
  1785 		Queue(aStatus);
       
  1786 		Complete(KErrNone);
       
  1787 		}
       
  1788 
       
  1789 	// Get rid of command buffer
       
  1790 	CleanupStack::PopAndDestroy(command);
       
  1791 	}
       
  1792 
       
  1793 /**
       
  1794 Enquote a string (being sent as a string literal) if required
       
  1795 */
       
  1796 EXPORT_C void CImapFolder::DoQuoteL(HBufC16*& aBuffer)
       
  1797 	{
       
  1798 	// Null string? Nothing to do
       
  1799 	if(!aBuffer->Length() || !aBuffer->Des().Length())
       
  1800 		{
       
  1801 		return;
       
  1802 		}
       
  1803 
       
  1804 	// Anything needing quoting in there?
       
  1805 	if(aBuffer->Des().Locate('\\') == KErrNotFound &&
       
  1806 		aBuffer->Des().Locate('\"') == KErrNotFound)
       
  1807 		{
       
  1808 		return;
       
  1809 		}
       
  1810 
       
  1811 	// Run through string, inserting quote characters as needed
       
  1812 	for(TInt a = 0; a<aBuffer->Des().Length(); a++)
       
  1813 		{
       
  1814 		if(aBuffer->Des()[a] == '\\' || aBuffer->Des()[a] == '\"')
       
  1815 			{
       
  1816 			HBufC16 *newbuf = aBuffer->ReAllocL(aBuffer->Des().Length()+1);
       
  1817 
       
  1818 			// Been moved due to realloc?
       
  1819 			if(newbuf != aBuffer)
       
  1820 				{
       
  1821 				// In all cases when DoQuoteL() is called, the buffer is on the top of
       
  1822 				// the cleanup stack: change this to indicate the correct entry
       
  1823 				CleanupStack::Pop();
       
  1824 				CleanupStack::PushL(aBuffer = newbuf);
       
  1825 				}
       
  1826 
       
  1827 			aBuffer->Des().Insert(a, KQuoteChar);
       
  1828 			a++;
       
  1829 			}
       
  1830 		}
       
  1831 	}
       
  1832 
       
  1833 
       
  1834 /**
       
  1835 Implementation of the observer function for the session fetch command. For each call
       
  1836 creates the required entry tree.
       
  1837 */
       
  1838 void CImapFolder::OnFetchLD(CImapFetchResponse* aImapFetchResponse)
       
  1839 	{
       
  1840 	// Take ownership of parameter
       
  1841 	CleanupStack::PushL(aImapFetchResponse);
       
  1842 	
       
  1843 	CImapRfc822HeaderFields* headerinfo = aImapFetchResponse->HeaderFields();
       
  1844 	CImapBodyStructure* bodystructure = aImapFetchResponse->BodyStructure();
       
  1845 	
       
  1846 	//update the progress object
       
  1847 	++iMsgsDone;
       
  1848 	++iHeadersFetched;
       
  1849 
       
  1850 	if(headerinfo)
       
  1851 		{
       
  1852 		TUint remoteUid = aImapFetchResponse->MessageUid();
       
  1853 		if(iFolderIndex->Size() > 0)
       
  1854 			{
       
  1855 			if(iFolderIndex->FindMsg(remoteUid) != 0)
       
  1856 				{
       
  1857 				CleanupStack::PopAndDestroy(aImapFetchResponse);
       
  1858 				return;
       
  1859 				}
       
  1860 			}
       
  1861 		// Create an email entry in this folder.
       
  1862 		SetEntryL(iMailboxId);
       
  1863 
       
  1864 		// Skeleton for new entry
       
  1865 		TMsvEmailEntry entry;
       
  1866 		TFileName attachmentFilename;	//	Attachment filename
       
  1867 
       
  1868 		entry.iSize = 0;
       
  1869 		entry.iType = KUidMsvMessageEntry;
       
  1870 		entry.iMtm = KUidMsgTypeIMAP4;
       
  1871 		entry.iServiceId = iImapSettings.ServiceId();
       
  1872 		entry.SetValidUID(EFalse);		// reuse ValidUID Flag record if the message has ever been fetched
       
  1873 		entry.SetComplete(EFalse);
       
  1874 		entry.SetUnread(ETrue);
       
  1875 		entry.SetNew(ETrue);
       
  1876 		entry.SetUID(aImapFetchResponse->MessageUid());
       
  1877 
       
  1878 		// Set from line in TMsvEntry
       
  1879 		const TDesC8& temp2 = headerinfo->FieldValue(CImapRfc822HeaderFields::EImapFrom);
       
  1880 		HBufC* decodedFromBuffer = HBufC::NewLC(temp2.Length());
       
  1881 		TPtr decodedFromPtr(decodedFromBuffer->Des());
       
  1882 
       
  1883 		iHeaderConverter.DecodeHeaderFieldL(temp2, decodedFromPtr);
       
  1884 		entry.iDetails.Set(decodedFromPtr);
       
  1885 		
       
  1886 		// Set subject in TMsvEntry
       
  1887 		const TDesC8& temp3 = headerinfo->FieldValue(CImapRfc822HeaderFields::EImapSubject); 
       
  1888 		HBufC* decodedSubjectBuffer = HBufC::NewLC(temp3.Length());
       
  1889 		TPtr decodedSubjectPtr(decodedSubjectBuffer->Des());
       
  1890 
       
  1891 		iHeaderConverter.DecodeHeaderFieldL(temp3, decodedSubjectPtr);
       
  1892 		entry.iDescription.Set(decodedSubjectPtr);
       
  1893 
       
  1894 		// Set the Date
       
  1895 		entry.iDate = headerinfo->Date();
       
  1896 
       
  1897 		// Set the priority field
       
  1898 		entry.SetPriority(headerinfo->PriorityL());
       
  1899 
       
  1900 		if (bodystructure)
       
  1901 			{
       
  1902 			SetMessageFlagsL(entry, bodystructure);
       
  1903 			}
       
  1904 
       
  1905 		// Set the flags
       
  1906 		TBool messageInfoSeen = aImapFetchResponse->MessageFlagInfo().QueryFlag(TMessageFlagInfo::ESeen);
       
  1907 
       
  1908 		entry.SetSeenIMAP4Flag(messageInfoSeen);
       
  1909 		entry.SetAnsweredIMAP4Flag(aImapFetchResponse->MessageFlagInfo().QueryFlag(TMessageFlagInfo::EAnswered));
       
  1910 		entry.SetFlaggedIMAP4Flag(aImapFetchResponse->MessageFlagInfo().QueryFlag(TMessageFlagInfo::EFlagged));
       
  1911 		entry.SetDeletedIMAP4Flag(aImapFetchResponse->MessageFlagInfo().QueryFlag(TMessageFlagInfo::EDeleted));
       
  1912 		entry.SetDraftIMAP4Flag(aImapFetchResponse->MessageFlagInfo().QueryFlag(TMessageFlagInfo::EDraft));
       
  1913 		entry.SetRecentIMAP4Flag(aImapFetchResponse->MessageFlagInfo().QueryFlag(TMessageFlagInfo::ERecent));
       
  1914 		
       
  1915 		// Are we configured to update the \seen flag on the server?
       
  1916 		if (iImapSettings.UpdatingSeenFlags())
       
  1917 			{
       
  1918 			// Now copy the inverse of the \Seen flag down to the clients Unread flag
       
  1919 			if (messageInfoSeen)
       
  1920 				{
       
  1921 				entry.SetUnread(EFalse);
       
  1922 				}
       
  1923 			}
       
  1924 		
       
  1925 		// note that an unread message has been spotted.
       
  1926 		if(!entry.SeenIMAP4Flag() || entry.RecentIMAP4Flag())
       
  1927 			{
       
  1928 			iSomeUnread	= ETrue;
       
  1929 			}
       
  1930 		
       
  1931 		// If sync'ing to download rules is disabled, mark that message as
       
  1932 		// "fetched"  using the re-used ValidUID flag. This prevents the
       
  1933 		// message from being sync'd according to download rules if rules
       
  1934 		// are subsequently enabled.
       
  1935 		if(!iImapSettings.UseSyncDownloadRules())
       
  1936 			{
       
  1937 			entry.SetValidUID(ETrue);
       
  1938 			}
       
  1939 
       
  1940 		// Create message
       
  1941 		User::LeaveIfError(iServerEntry.CreateEntryBulk(entry));
       
  1942 		// The matching CompleteBulk() is called in OnFetchCommit() and the ESyncFlags section of the DoRunL()
       
  1943 		// that is called after FetchBodyStructureAndHeadersL() has completed.
       
  1944 		__ASSERT_DEBUG(iNextAction == CImapSyncManager::ESyncFlags, User::Invariant());
       
  1945 		CleanupStack::PopAndDestroy(decodedSubjectBuffer);
       
  1946 		CleanupStack::PopAndDestroy(decodedFromBuffer);
       
  1947 		}
       
  1948 		
       
  1949 	CleanupStack::PopAndDestroy(aImapFetchResponse);
       
  1950 	}
       
  1951 	
       
  1952 void CImapFolder::OnFetchCommit()
       
  1953 	{
       
  1954 	iServerEntry.CompleteBulk();
       
  1955 	}
       
  1956 
       
  1957 /**
       
  1958 Set or clear the \\Seen flags on the server
       
  1959 
       
  1960 @param aUpdateMode
       
  1961 ETrue -> Sets the flag
       
  1962 @return False if no messages need to be processed
       
  1963 */
       
  1964 TBool CImapFolder::ProcessSeenFlagsL(TSeenFlagUpdateMode aUpdateMode)
       
  1965 	{
       
  1966 	RArray<TUint>* pendingList;
       
  1967 	TBool settingFlag = (aUpdateMode == ESetSeenFlag);
       
  1968 
       
  1969 	// Point pendingList to the correct list
       
  1970 	pendingList = (settingFlag ? iSetSeenList: iClearSeenList);
       
  1971 	
       
  1972 	// Exit if nothing to process
       
  1973 	if(!pendingList->Count())
       
  1974 		{
       
  1975 		return EFalse;
       
  1976 		}
       
  1977 	
       
  1978 	__LOG_FORMAT((iSavedSession->LogId(), "CImapFolder : ProcessSeenFlags(%d)", aUpdateMode));
       
  1979 
       
  1980 	_LIT8(KStoreFlagsSetCommand, "+FLAGS");
       
  1981 	_LIT8(KStoreFlagsClearCommand, "-FLAGS");
       
  1982 	_LIT8(KStoreFlagsSeenCommand,"(\\Seen)");
       
  1983 	
       
  1984 	iMessageFlagInfoArray.Reset();
       
  1985 
       
  1986 	HBufC8* sequenceSetOnHeap = CImapSession::CreateSequenceSetLC(*pendingList);
       
  1987 
       
  1988 	// Call the STORE function in the session
       
  1989 	if(settingFlag)
       
  1990 		{
       
  1991 		iSavedSession->StoreL(iStatus, *sequenceSetOnHeap, KStoreFlagsSetCommand(), KStoreFlagsSeenCommand(), ETrue, iMessageFlagInfoArray);
       
  1992 		}
       
  1993 	else
       
  1994 		{
       
  1995 		iSavedSession->StoreL(iStatus, *sequenceSetOnHeap, KStoreFlagsClearCommand(), KStoreFlagsSeenCommand(), ETrue, iMessageFlagInfoArray);
       
  1996 		}
       
  1997 	
       
  1998 	CleanupStack::PopAndDestroy(sequenceSetOnHeap);
       
  1999 	
       
  2000 	SetActive();
       
  2001 
       
  2002 	// Reset the list
       
  2003 	pendingList->Reset();
       
  2004 
       
  2005 	return ETrue;
       
  2006 	}
       
  2007 
       
  2008 /**
       
  2009 Construct a full mailbox path for the folder object
       
  2010 This is expensive in memory movement terms, as it works UP the path,
       
  2011 inserting new data at the start. This is based on the principle that it's
       
  2012 more expensive to find an entry in the index with SetEntryL() than it is to
       
  2013 move some bytes about, otherwise we'd find the path upwards then create the
       
  2014 string downwards.
       
  2015 */
       
  2016 EXPORT_C HBufC16* CImapFolder::MakePathL(const TBool aIncludeLeaf)
       
  2017 	{
       
  2018 	// Making a path: we start with nothing
       
  2019 	HBufC16 *path = HBufC16::NewLC(256);
       
  2020 	TBool skipfirst = ETrue;
       
  2021 	TMsvId traverse = iMailboxId;
       
  2022 
       
  2023 	// Move to the entry
       
  2024 	User::LeaveIfError(iServerEntry.SetEntry(traverse));
       
  2025 
       
  2026 	// Skipping the leaf?
       
  2027 	if(!aIncludeLeaf && iServerEntry.Entry().iType != KUidMsvServiceEntry)
       
  2028 		{
       
  2029 		// Up a level before we generate the path
       
  2030 		traverse = iServerEntry.Entry().Parent();
       
  2031 		User::LeaveIfError(iServerEntry.SetEntry(traverse));
       
  2032 		}
       
  2033 
       
  2034 	// Check and see if we are dealing with the INBOX, in which case return immediately
       
  2035 	if (iSyncManager.EntryIsInbox(iServerEntry.Entry()))
       
  2036 		{
       
  2037 		path->Des().Insert(0,KIMAP_INBOX);
       
  2038 		CleanupStack::Pop(path);
       
  2039 		return path;
       
  2040 		}
       
  2041 
       
  2042 	// While we can still go up within this service...
       
  2043 	while(iServerEntry.Entry().iType != KUidMsvServiceEntry)
       
  2044 		{
       
  2045 		// Make sure the path maxlength is still ok with the added folder name and an extra separator character
       
  2046 		if((path->Length() + iServerEntry.Entry().iDetails.Length() + 1) > path->Des().MaxLength())
       
  2047 			{
       
  2048 			HBufC16* newpath = path->ReAllocL(path->Length() + iServerEntry.Entry().iDetails.Length() + 1);
       
  2049 			if(path != newpath)
       
  2050 				{
       
  2051 				CleanupStack::Pop(path);
       
  2052 				path = newpath;
       
  2053 				CleanupStack::PushL(path);
       
  2054 				}
       
  2055 			}
       
  2056 
       
  2057 		// Add the name of this component to the path
       
  2058 		if(!skipfirst)
       
  2059 			{
       
  2060 			path->Des().Insert(0,iImapSettings.PathSeparator());
       
  2061 			}
       
  2062 		else
       
  2063 			{
       
  2064 			skipfirst = EFalse;
       
  2065 			}
       
  2066 
       
  2067 		// Ensure uppercase 'INBOX' is used in folder name. This allows case
       
  2068 		// sensitive searches to be used later.
       
  2069 		if (iSyncManager.EntryIsInbox(iServerEntry.Entry()))
       
  2070 			{
       
  2071 			path->Des().Insert(0,KIMAP_INBOX);
       
  2072 			}
       
  2073 		else
       
  2074 			{
       
  2075 			path->Des().Insert(0,iServerEntry.Entry().iDetails);
       
  2076 			}
       
  2077 
       
  2078 		// Go up a level
       
  2079 		SetEntryL(traverse = iServerEntry.Entry().Parent());
       
  2080 		}
       
  2081 	
       
  2082 	// Add the path at the very start, if it exists
       
  2083 	if(iImapSettings.FolderPath().Length())
       
  2084 		{
       
  2085 		// Make sure the path maxlength is still ok with the added folder path and an extra separator character
       
  2086 		if((path->Length() + iImapSettings.FolderPath().Length() + 1) > path->Des().MaxLength())
       
  2087 			{
       
  2088 			HBufC16* newpath = path->ReAllocL(path->Length() + iImapSettings.FolderPath().Length() + 1);
       
  2089 			if(path != newpath)
       
  2090 				{
       
  2091 				CleanupStack::Pop(path);
       
  2092 				path = newpath;
       
  2093 				CleanupStack::PushL(path);
       
  2094 				}
       
  2095 			}
       
  2096 
       
  2097 		// Anything there already? If not, don't bother with the separator
       
  2098 		if(path->Des().Length()) 
       
  2099 			{
       
  2100 			path->Des().Insert(0,iImapSettings.PathSeparator());	
       
  2101 			}
       
  2102 		
       
  2103 		RBuf tempstr;
       
  2104 		tempstr.CleanupClosePushL();
       
  2105 		tempstr.CreateL(iImapSettings.FolderPath().Length());
       
  2106 		
       
  2107 		tempstr.Copy(iImapSettings.FolderPath());
       
  2108 		path->Des().Insert(0, tempstr);
       
  2109 		
       
  2110 		CleanupStack::PopAndDestroy(&tempstr);
       
  2111 		}
       
  2112 
       
  2113 	// Pop it off cleanup stack
       
  2114 	CleanupStack::Pop(path);
       
  2115 
       
  2116 	// Return the path
       
  2117 	return(path);
       
  2118 	}
       
  2119 
       
  2120 void CImapFolder::CompleteSelf()
       
  2121 	{
       
  2122 // Complete self.
       
  2123 	TRequestStatus* status = &iStatus;
       
  2124 	iStatus = KRequestPending;
       
  2125 	User::RequestComplete(status, KErrNone);
       
  2126 	}
       
  2127 
       
  2128 /**
       
  2129 Returns updated progress information on outstanding synchronisation operations.
       
  2130 */
       
  2131 EXPORT_C void CImapFolder::Progress(TImap4SyncProgress& aProgress)
       
  2132 	{
       
  2133 	//copy values from member progress ob into aProgress
       
  2134 	aProgress.iMsgsDone=iMsgsDone;
       
  2135 	aProgress.iMsgsToDo=iMsgsToDo;
       
  2136 	
       
  2137 	aProgress.iHeadersFetched = iHeadersFetched;	
       
  2138 	aProgress.iOrphanedMessages = iOrphanedMessages;
       
  2139 	aProgress.iRemoteMessagesDeleteTagged = iRemoteMessagesDeleteTagged;
       
  2140 	
       
  2141 	if(iImapSettings.SearchString().Length() != 0)
       
  2142 		{
       
  2143 		aProgress.iMsgsToDo=iMailboxSize;	
       
  2144 		}
       
  2145 	else
       
  2146 		{
       
  2147 		aProgress.iMsgsToDo=(iSyncLimit<=0)?iMailboxSize:Min(iMailboxSize,iSyncLimit);	
       
  2148 		}
       
  2149 	aProgress.iMsgsDone = Min(iMsgsDone,aProgress.iMsgsToDo);	
       
  2150 		
       
  2151 	}
       
  2152 
       
  2153 /**
       
  2154 Sets various flags in the message entry by doing a quick scan of the
       
  2155 bodystructure. This routine is intended to be relatively straightforward
       
  2156 so as not to impact performance during the sync phase too much.
       
  2157 
       
  2158 @param aEntry Entry to set flags for
       
  2159 @param aBodyStructure Message body structure 
       
  2160 */
       
  2161 void CImapFolder::SetMessageFlagsL(TMsvEmailEntry& aEntry, CImapBodyStructure* aBodyStructure)
       
  2162 	{
       
  2163 	TBool hasAttachments(EFalse);
       
  2164 	TBool hasHtml(EFalse);
       
  2165 	TBool afterRelated(EFalse);
       
  2166 	TBool afterAlternative(EFalse);
       
  2167 	TBool htmlAfterAltRel(EFalse);
       
  2168 	TBool hasICalendar(EFalse);
       
  2169 	TBool hasVCalendar(EFalse);
       
  2170 	TInt size(0);
       
  2171 
       
  2172 	// Check if top level of message contains attachment type
       
  2173 	if ((aBodyStructure->BodyStructureType() == CImapBodyStructure::ETypeBasic) &&
       
  2174 	    ((aBodyStructure->Type().CompareF(KImapTxtImage) == 0) ||
       
  2175 	     (aBodyStructure->Type().CompareF(KImapTxtAudio) == 0) ||
       
  2176 	     (aBodyStructure->Type().CompareF(KImapTxtVideo) == 0) ||
       
  2177 	     (aBodyStructure->Type().CompareF(KImapTxtApplication) == 0)))
       
  2178 		{
       
  2179 		hasAttachments = ETrue;
       
  2180 		}
       
  2181 
       
  2182 	RPointerArray<CImapBodyStructure> bodyStructureStack;
       
  2183 	bodyStructureStack.AppendL(aBodyStructure);
       
  2184 
       
  2185 	TInt count;
       
  2186 
       
  2187 	// A body structure stack is maintained. This avoids the use of recursion
       
  2188 	// when processing the embedded body structure list at each level, and also
       
  2189 	// maintains the order of processing that the old IMAP implementation used.
       
  2190 	// As nobody could explain exactly why the old code used this order, it was
       
  2191 	// felt that it was safer that the new implementation matched the old.
       
  2192 	while (bodyStructureStack.Count() > 0)
       
  2193 		{
       
  2194 		GetFlagsForBodyStructurePart(bodyStructureStack[0], hasAttachments, hasHtml, afterRelated, afterAlternative, htmlAfterAltRel, hasICalendar, hasVCalendar, size);
       
  2195 
       
  2196 		for (count = 0; count < bodyStructureStack[0]->EmbeddedBodyStructureList().Count(); ++count)
       
  2197 			{
       
  2198 			bodyStructureStack.AppendL(bodyStructureStack[0]->EmbeddedBodyStructureList()[count]);
       
  2199 			}
       
  2200 
       
  2201 		bodyStructureStack.Remove(0);
       
  2202 		}
       
  2203 
       
  2204 	aEntry.SetAttachment(hasAttachments);
       
  2205 	aEntry.SetMHTMLEmail(hasHtml || htmlAfterAltRel);
       
  2206 	aEntry.SetICalendar(hasICalendar);
       
  2207 	aEntry.SetVCalendar(hasVCalendar);
       
  2208 	aEntry.iSize = size;
       
  2209 	bodyStructureStack.Reset();
       
  2210 	}
       
  2211 
       
  2212 /**
       
  2213 Gets a set of flags for a body structure part
       
  2214 
       
  2215 @param aBodyStructure Body structure part
       
  2216 @param aHasAttachments Flag to indicate if message contains attachments
       
  2217 @param aHasHtml Flag to indicate if message has HTML part
       
  2218 @param aAfterRelated Flag to indicate a multipart/related part has been found
       
  2219 @param aAfterAlternative Flag to indicate a multipart/alternative part has been found
       
  2220 @param aHtmlAfterAltRel Flag to indicate a HTML part has been found after a multipart/related or multipart/alternative part
       
  2221 @param aHasICalendar Flag to indicate message contains ICalendar
       
  2222 @param aHasVCalendar Flag to indicate message contains VCalendar
       
  2223 @param aSize Running total of size of message
       
  2224 */
       
  2225 void CImapFolder::GetFlagsForBodyStructurePart(CImapBodyStructure* aBodyStructure,
       
  2226                                                TBool& aHasAttachments, TBool& aHasHtml,
       
  2227                                                TBool& aAfterRelated, TBool& aAfterAlternative,
       
  2228                                                TBool& aHtmlAfterAltRel, TBool& aHasICalendar,
       
  2229                                                TBool& aHasVCalendar, TInt& aSize)
       
  2230 	{
       
  2231 	switch (aBodyStructure->BodyStructureType())
       
  2232 		{
       
  2233 		case CImapBodyStructure::ETypeMultipart:
       
  2234 			{
       
  2235 			if (aBodyStructure->SubType().CompareF(KImapTxtRelated) == 0)
       
  2236 				{
       
  2237 				aAfterRelated = ETrue;
       
  2238 				}
       
  2239 			else if (aBodyStructure->SubType().CompareF(KImapTxtAlternative) == 0)
       
  2240 				{
       
  2241 				aAfterAlternative = ETrue;
       
  2242 				}
       
  2243 			else if (aBodyStructure->SubType().CompareF(KImapTxtMixed) == 0)
       
  2244 				{
       
  2245 				aHasAttachments = ETrue;
       
  2246 				}
       
  2247 			
       
  2248 			break;
       
  2249 			}
       
  2250 
       
  2251 		case CImapBodyStructure::ETypeText:
       
  2252 			{
       
  2253 			if (aBodyStructure->SubType().CompareF(KImapTxtHtml) == 0)
       
  2254 				{
       
  2255 				if (aBodyStructure->ExtDispositionName().CompareF(KImapTxtAttachment) != 0)
       
  2256 					{
       
  2257 					aHasHtml = ETrue;
       
  2258 					}
       
  2259 
       
  2260 				if (aAfterRelated || aAfterAlternative)
       
  2261 					{
       
  2262 					aHtmlAfterAltRel = ETrue;
       
  2263 					}
       
  2264 				}
       
  2265 			else if (aBodyStructure->SubType().CompareF(KImapTxtCalendar) == 0)
       
  2266 				{
       
  2267 				aHasICalendar = ETrue;
       
  2268 				}
       
  2269 			else if (aBodyStructure->SubType().CompareF(KImapTxtXVCalendar) == 0)
       
  2270 				{
       
  2271 				aHasVCalendar = ETrue;
       
  2272 				}
       
  2273 
       
  2274 			break;
       
  2275 			}
       
  2276 
       
  2277 		case CImapBodyStructure::ETypeMessageRfc822:
       
  2278 			{
       
  2279 			return;
       
  2280 			}
       
  2281 
       
  2282 		default:
       
  2283 			{
       
  2284 			if (aBodyStructure->SubType().CompareF(KImapTxtDeliveryStatus) == 0)
       
  2285 				{
       
  2286 				aHasAttachments = ETrue;
       
  2287 				}
       
  2288 
       
  2289 			break;
       
  2290 			}
       
  2291 		}
       
  2292 
       
  2293 	// Add size of this body part to the running total
       
  2294 	TInt size(0);
       
  2295 	TLex8 lex(aBodyStructure->BodySizeOctets());
       
  2296 	lex.Val(size);
       
  2297 
       
  2298 	// For Base64, use the pre encoding data size
       
  2299 	if (aBodyStructure->BodyEncoding().CompareF(KImapTxtBase64) == 0)
       
  2300 		{
       
  2301 		size = (size * 3) / 4;
       
  2302 		}
       
  2303 
       
  2304 	aSize += size;
       
  2305 	}
       
  2306 
       
  2307 /**
       
  2308 Checks if we need to clear the new flags on the messages in the folder,
       
  2309 and if so clears them.
       
  2310 @pre Current selection (iSelection) contains all the messages in the folder.
       
  2311 */
       
  2312 void CImapFolder::ClearNewFlagsIfRequiredL()
       
  2313 	{
       
  2314 	if (iSyncManager.EntryIsInbox(iServerEntry.Entry()))
       
  2315 		{
       
  2316 		if (iSyncManager.InboxClearNewFlags())
       
  2317 			{
       
  2318 			__LOG_TEXT(iSavedSession->LogId(), "CImapFolder: Clearing new flags (inbox)");
       
  2319 			
       
  2320 			// Change attributes on the current selection
       
  2321 			User::LeaveIfError(iServerEntry.ChangeAttributes(*iSelection, 0, KMsvNewAttribute));
       
  2322 
       
  2323 			// Set the flag to False to indicate that we have cleared the flags
       
  2324 			// on the inbox, and so any subsequent synchronise of the inbox
       
  2325 			// will not clear them again.
       
  2326 			iSyncManager.ResetInboxClearNewFlags();
       
  2327 			}
       
  2328 		}
       
  2329 	else
       
  2330 		{
       
  2331 		if (iSyncManager.NonInboxClearNewFlags())
       
  2332 			{
       
  2333 			__LOG_TEXT(iSavedSession->LogId(), "CImapFolder: Clearing new flags (non inbox)");
       
  2334 
       
  2335 			// Change attributes on the current selection
       
  2336 			User::LeaveIfError(iServerEntry.ChangeAttributes(*iSelection, 0, KMsvNewAttribute));
       
  2337 			
       
  2338 			// Note that we do not clear the flag here as it will be required
       
  2339 			// for any subsequent non inbox folders that are yet to be synced.
       
  2340 			}
       
  2341 		}
       
  2342 	}
       
  2343 
       
  2344 /**
       
  2345 Sets the folder matched flag
       
  2346 
       
  2347 @param aFolderMatched Value to set flag to
       
  2348 */
       
  2349 void CImapFolder::SetFolderMatched(TBool aFolderMatched)
       
  2350 	{
       
  2351 	iFolderMatched = aFolderMatched;
       
  2352 	}
       
  2353 
       
  2354 /**
       
  2355 Gets the folder matched flag
       
  2356 
       
  2357 @return Folder matched flag value
       
  2358 */
       
  2359 TBool CImapFolder::FolderMatched()
       
  2360 	{
       
  2361 	return iFolderMatched;
       
  2362 	}
       
  2363 
       
  2364 /**
       
  2365 Performs comparison between two folders by comparing their folder names.
       
  2366 
       
  2367 @param aFirst The first folder to compare
       
  2368 @param aSecond The second folder to compare
       
  2369 
       
  2370 @return The result of calling Compare on the folder names
       
  2371 */
       
  2372 TInt CImapFolder::CompareByFolderName(const CImapFolder& aFirst, const CImapFolder& aSecond)
       
  2373 // static method
       
  2374 	{
       
  2375 	return aFirst.iFullFolderPath.Compare(aSecond.iFullFolderPath);
       
  2376 	}