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