diff -r 000000000000 -r 72b543305e3a email/imap4mtm/imapsyncmanager/src/cimapfolder.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/imap4mtm/imapsyncmanager/src/cimapfolder.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,2376 @@ +// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include "cimapfolder.h" +#include "cimapsyncmanager.h" +#include "cimapsession.h" +#include "cimaprfc822headerfields.h" +#include "cimapbodystructure.h" +#include "cimapfolderinfo.h" +#include "cimapfolderindex.h" +#include "cimaplogger.h" +#include +#include +#include +#include "cimapmimeheaderfields.h" +#include "cimapcharconv.h" + +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include "cimconvertheader.h" +#include "cimconvertcharconv.h" +#endif + +// For first stage download (to view in folder list) +_LIT8(KImapFetchHeaderToEnd, "%d:*"); +_LIT8(KImapFetchHeaderRange,"%d:%d"); +_LIT8(KImapFetchHeaderUIDRange,"UID %d:%d"); +_LIT8(KImapFetchHeaderRangeSearch,"%d:%d %S"); +_LIT8(KImapSmallHeaderFields,"Received Date Subject From Priority X-MSMail-Priority X-Priority Importance"); +//_LIT8(KImapLargeHeaderFields,"Received Date Subject From Reply-to To Cc Bcc Message-ID Priority %S"); + +_LIT(KQuoteChar, "\\"); +_LIT(KIMAP_INBOX, "INBOX"); + +_LIT8(KImapTxtImage, "IMAGE"); +_LIT8(KImapTxtAudio, "AUDIO"); +_LIT8(KImapTxtVideo, "VIDEO"); +_LIT8(KImapTxtApplication, "APPLICATION"); +_LIT8(KImapTxtRelated, "RELATED"); +_LIT8(KImapTxtAlternative, "ALTERNATIVE"); +_LIT8(KImapTxtMixed, "MIXED"); +_LIT8(KImapTxtHtml, "HTML"); +_LIT8(KImapTxtAttachment, "ATTACHMENT"); +_LIT8(KImapTxtCalendar, "CALENDAR"); +_LIT8(KImapTxtXVCalendar, "X-VCALENDAR"); +_LIT8(KImapTxtDeliveryStatus, "DELIVERY-STATUS"); +_LIT8(KImapTxtBase64, "BASE64"); + +// IMAP Priority fields +//_LIT8(KImapPriorityFieldsSearch, "Priority Priority X-MSMail-Priority Precedence Importance"); + +// Constants +const TInt KImapUidSearchSize = 400; +const TInt KImapMaxIntChars = 11; // the maximum number of characters needed to represent a 32-bit unsigned integer as a string. + + +// Efficient and SAFE way of comparing TBools which might have different integers representing TRUE +inline TBool BoolsAreEqual( TBool aA, TBool aB ) + { + return ((aA && aB) || (!aA && !aB)); + } + +inline TBool BoolsAreNotEqual( TBool aA, TBool aB ) + { + return ((!aA || !aB) && (aA || aB)); + } + +/** +Constructor +*/ +CImapFolder::CImapFolder(CImapSyncManager& aSyncMan, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings) +: CMsgActive(EPriorityStandard), +iSyncManager(aSyncMan), +iServerEntry(aServerEntry), +iImapSettings(aImapSettings), +iHeaderConverter(CImapUtils::GetRef().Charconv().HeaderConverter()) + { + } + +/** +Destructor +*/ +CImapFolder::~CImapFolder() + { + iMatchingMessageIds.Reset(); + iDeletedMessageIds.Reset(); + iMissingMessageIds.Reset(); + delete iSelection; + iFolderIndex->Reset(); + iMessageFlagInfoArray.Reset(); + delete iFolderIndex; + delete iCachedEntryData; + delete iClearSeenList; + delete iSetSeenList; + iFullFolderPath.Close(); + } + +/** +Static factory constructor + +@param aSyncMan +Reference to the owning Sync Manager (for access to offline control object) +@param aEmailServerEntry +The folder entry on the Message server that will be use to construct this folder object +@return The constructed CImapFolder object pushed onto the cleanup stack +*/ +EXPORT_C CImapFolder* CImapFolder::NewLC(CImapSyncManager& aSyncMan, CMsvServerEntry& aServerEntry, TMsvEmailEntry& aEmailEntry, CImapSettings& aImapSettings, const TDesC& aFullFolderPath) + { + CImapFolder* self=new (ELeave) CImapFolder(aSyncMan, aServerEntry, aImapSettings); + CleanupStack::PushL(self); + + // Non-trivial constructor + self->ConstructL(aServerEntry, aEmailEntry, aFullFolderPath); + return self; + } + +/** +Static factory constructor with cleanup + +@param aSyncMan +Reference to the owning Sync Manager (for access to offline control object) +@param aEmailServerEntry +The folder entry on the Message server that will be use to construct this folder object +@return The constructed CImapFolder object +*/ +EXPORT_C CImapFolder* CImapFolder::NewL(CImapSyncManager& aSyncMan, CMsvServerEntry& aServerEntry, TMsvEmailEntry& aEmailEntry, CImapSettings& aImapSettings, const TDesC& aFullFolderPath) + { + CImapFolder* self=NewLC(aSyncMan, aServerEntry, aEmailEntry, aImapSettings, aFullFolderPath); + CleanupStack::Pop(); + return self; + } + +/** +The non-trival constructor + +@param aEmailServerEntry +The folder entry on the Message server that will be use to construct this folder object +*/ +void CImapFolder::ConstructL(CMsvServerEntry& /*aServerEntry*/, TMsvEmailEntry& aEmailEntry, const TDesC& aFullFolderPath) + { + iSessionFolderInfo = NULL; + iMailboxId = aEmailEntry.Id(); + iMailboxSize = aEmailEntry.RemoteFolderEntries(); + iFolderIndex = new (ELeave) CImapFolderIndex(); + iSelection = new (ELeave) CMsvEntrySelection(); + iSyncLimit = KImImapSynchroniseAll; + + // Set the Path of folder object + SetFullFolderPathL(aFullFolderPath) ; + + // We're an active object... + CActiveScheduler::Add(this); + } + +/** +Sets the full path name of the folder from the service entry downwards. Layout as on the remote IMAP Server. +Should only be use by the CImapSyncManager. + +@param aFullFolderPath +The new path to use as the full path of the folder. +*/ +void CImapFolder::SetFullFolderPathL(const TDesC& aFullFolderPath) + { + iFullFolderPath.Close(); // discard any previous data + iFullFolderPath.CreateL(aFullFolderPath); + } + +/** +Processing message loop for the active object. Called by the active scheduler. +*/ +void CImapFolder::DoRunL() + { + // Finish if any server errors. + if(iStatus.Int()!=KErrNone) + { + Complete(iStatus.Int()); + return; + } + + switch(iNextAction) + { + case CImapSyncManager::ESyncNew: + { + __LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Starting SynchroniseNewL"); + SynchroniseNewL(); + return; + } + case CImapSyncManager::ESyncFlags: + { + __LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Syncing flags for local messages"); + + // Whenever iSavedSession->FetchBodyStructureAndHeadersL() is called, this is the next step + // (although this step can be reached without calling FetchBodyStructureAndHeadersL()) + // This means that OnFetchLD() may have been called, in which case there are + // outstanding BULK entry operations that need to be completed here. + iServerEntry.CompleteBulk(); + + // If there were message flags that has been changed locally and need upadting on the remote server + // then do those first. + if(iSetSeenList) + { + if(ProcessSeenFlagsL(ESetSeenFlag)) + { + return; + } + } + + if(iClearSeenList) + { + if(ProcessSeenFlagsL(EClearSeenFlag)) + { + return; + } + } + + __LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Syncing flags for remote messages"); + // Creates the list of old messages then update the flags for them + GetMessageChildrenL(iMailboxId, iSelection); + MakeSortedFolderIndexL(ETrue); + + if(iSelection->Count() > 0) + { + iHighestUid = (*iFolderIndex)[iSelection->Count()-1].iUid; + } + else + { + iHighestUid = 0; + } + + TInt localcount = iFolderIndex->Size(); + iMatchingMessageIds.Reset(); + for(TInt localloop = 0; localloop < localcount; ++localloop) + { + TMsvId localid = (*iFolderIndex)[localloop].iUid; + iMatchingMessageIds.Append(localid); + } + + iMessageFlagInfoArray.Reset(); + + HBufC8* sequenceSetOnHeap = CImapSession::CreateSequenceSetLC(iMatchingMessageIds); + + // Call the FETCHFLAGS function in the session + iNextAction = CImapSyncManager::EEndSyncFlags; + iSavedSession->FetchFlagsL(iStatus, *sequenceSetOnHeap, iMessageFlagInfoArray); + SetActive(); + CleanupStack::PopAndDestroy(sequenceSetOnHeap); + } + break; + case CImapSyncManager::EEndSyncFlags: + { + __LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Call to get the remote flags completed"); + // Loop through the messages array and update the flags for each UID returned + // Get a clean version of the folder children + TInt resultscount = iMessageFlagInfoArray.Count(); + for(TInt resultsloop = 0; resultsloop < resultscount; ++resultsloop) + { + TMessageFlagInfo *messageinfo = &(iMessageFlagInfoArray[resultsloop]); + + TInt localmsgcount = iFolderIndex->Size(); + // For each returned Uid go through the list of local index to find the matching TMsvId. + for(TInt localloop = 0; localloop < localmsgcount; ++localloop) + { + if((*iFolderIndex)[localloop].iUid == messageinfo->MessageUid()) + { + SetEntryL((*iFolderIndex)[localloop].iMsvId); + TMsvEmailEntry entry = iServerEntry.Entry(); + + TBool messageInfoSeen = messageinfo->QueryFlag(TMessageFlagInfo::ESeen); + TBool oldEntrySeen = entry.SeenIMAP4Flag(); + + // Are we configured to update the \seen flag on the server? + // If so then we need to translate "read\unread" flags into "\\seen" or not "\\seen" + if (iImapSettings.UpdatingSeenFlags()) + { + // Make a note to update the servers \Seen flag if "read\unread" has CHANGED on the client + // and is different to the server's version + if (BoolsAreEqual(entry.Unread(), messageInfoSeen) && BoolsAreEqual(oldEntrySeen, messageInfoSeen)) + { + if (entry.Unread()) + { + AppendClearSeenL(entry.UID()); + } + else + { + AppendSetSeenL(entry.UID()); + } + } + } + + entry.SetSeenIMAP4Flag(messageInfoSeen); + entry.SetFlaggedIMAP4Flag(messageinfo->QueryFlag(TMessageFlagInfo::EFlagged)); + entry.SetDeletedIMAP4Flag(messageinfo->QueryFlag(TMessageFlagInfo::EDeleted)); + entry.SetDraftIMAP4Flag(messageinfo->QueryFlag(TMessageFlagInfo::EDraft)); + entry.SetRecentIMAP4Flag(messageinfo->QueryFlag(TMessageFlagInfo::ERecent)); + + // Are we configured to update the \seen flag on the server? + if (iImapSettings.UpdatingSeenFlags()) + { + // Now copy the inverse of the \Seen flag down to the phone's Unread flag + // except when LastSyncSeen is set (ie when the client version is more up to date) + // This means that the client Read status ALWAYS reflects the IMAP \Seen state + if (BoolsAreEqual(entry.Unread(), messageInfoSeen) && BoolsAreNotEqual(oldEntrySeen, messageInfoSeen)) + { + // Phone and server disagree on whether the message has been read. + // And the seen flag on the server is different to that on the phone. + // Which means that a change has happened on the server that the phone was not aware of. + // So server is more up to date, and we take its value. + entry.SetUnread(!messageInfoSeen); + } + } + + ChangeEntryBulkL(entry); // completed after the outer for loop + break; + } + } // end of inner for loop + } + + iServerEntry.CompleteBulk(); + + iNextAction = CImapSyncManager::ESyncProcessSeenFlagsAndComplete; + SetActive(); + CompleteSelf(); + } + break; + case CImapSyncManager::ESyncProcessSeenFlagsAndComplete: + { + // If there were message flags that has been changed locally and need upadting on the remote server + // then do those now. + if(iSetSeenList) + { + if(ProcessSeenFlagsL(ESetSeenFlag)) + { + return; + } + } + + if(iClearSeenList) + { + if(ProcessSeenFlagsL(EClearSeenFlag)) + { + return; + } + } + + SyncCompleteL(); + Complete(iStatus.Int()); + } + break; + case CImapSyncManager::ESyncSearch: + { + if(iStatus == KErrNone) + { + if(iSyncState == CImapSyncManager::ESyncSearch) + { + // Process the seach results to see if any messages ids are left. + if(CheckAndProgressMessageSyncL()) + { + // More messages and the search command has been sent so just return. + return; + } + } + + // If the sync action is syncing old messages then go through the list of found ids + // match it with the folder index and build a list of messages that are no longer on the server. + if(iSyncState == CImapSyncManager::ESyncOld) + { + // At this point the remote command to search out folder UIDs has completed. This + // list will contain all UIDs for messages in the remote folder. This may be too + // many, and if so, the list is truncated to only include the N most recent + // messages in accordance with the synchronisation limit. + + TInt syncThresh = GetSyncThreshold(); + __LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Syncing old. syncThresh = %d", syncThresh)); + + // Perform the check on the message ids to make sure that only messages that are within + // the sync limit will be left in iDeletedMessageIds. + CheckMessagesInRangeL(syncThresh); + + // Check the delete ids list and delete messages according to what's in the list + TInt deletedcount = iDeletedMessageIds.Count(); + // Remove message from the local server + for(TInt deleteloop = 0; deleteloop < deletedcount; ++deleteloop) + { + __LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Local message (%d) deleting.", (*iFolderIndex)[iDeletedMessageIds[deleteloop]].iMsvId)); + DeleteMessageL((*iFolderIndex)[iDeletedMessageIds[deleteloop]].iMsvId); + } + + // Trim the list down to the most recent UIDs consistant with the sync limit. + if(iMatchingMessageIds.Count() > syncThresh) + { + for(TInt i = 0; i < syncThresh; ++i) + { + iMatchingMessageIds.Remove(0); + } + } + + // Get a clean version of the folder children + GetMessageChildrenL(iMailboxId, iSelection); + + //update the progress object + iMsgsToDo = iMatchingMessageIds.Count(); + + MakeSortedFolderIndexL(ETrue); + + if(iSelection->Count() > 0) + { + iHighestUid = (*iFolderIndex)[iSelection->Count()-1].iUid; + } + else + { + iHighestUid = 0; + } + + // At this point, the remote command to fetch all old messages has completed. + // Now we can look at fetching all new messages. 'iHighestUid' will contain the + // highest UID of the old messages. The top entry in 'iSearchList' will contain + // the highest UID in the remote folder. This gives us the range of UID to fetch + // for new messages. + + // First check are there any new messages to fetch? If 'iHighestUid' is the highest + // UID locally and remotely, then we finished sync'ing when we completed the old + // sync. + if(iMatchingMessageIds.Count() == 0) + { + __LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Search List is empty"); + } + else if(iHighestUid < iMatchingMessageIds[iMatchingMessageIds.Count()-1]) + { + TUint32 uidLow = iHighestUid; + TUint32 uidHigh = iMatchingMessageIds[iMatchingMessageIds.Count()-1]; + + // Only want new messages. + uidLow++; + + // Are there only new messages (and no old)? + if(iHighestUid == 0) + { + // Set this to ensure range is correct. + uidLow = iMatchingMessageIds[0]; + } + + // Perform the new sync. + __LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Synchronising new messages with range %d:%d", uidLow, uidHigh)); + SynchroniseRangeL(uidLow, uidHigh); + return; + } + + iSyncState = CImapSyncManager::ESyncNew; + } + + if((iSyncState == CImapSyncManager::ESyncNew) && (iImapSettings.SearchString().Length() == 0)) + { + // Check for missing UIDs, ie. holes in the messages that we should have. + CheckForMissingMessagesUidsL(); + + // If there were any "missing" messages found during the sync that we should have + // mirrored previously, get these now. + if(iMissingMessageIds.Count() != 0) + { + __LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Missing messages detected %d", iMissingMessageIds.Count())); + HBufC8* sequenceSetOnHeap = CImapSession::CreateSequenceSetLC(iMissingMessageIds); + // Call the FETCH function in the session + iSavedSession->FetchBodyStructureAndHeadersL(iStatus, sequenceSetOnHeap->Des(), KImapSmallHeaderFields(), *this); + CleanupStack::PopAndDestroy(sequenceSetOnHeap); + SetActive(); + + iSyncState = CImapSyncManager::EFolderSynchronise; + iNextAction = CImapSyncManager::ESyncFlags; + return; + } + + if(iSyncLimit <= KImImapSynchroniseAll) + { + __LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Starting SynchroniseNewL"); + SynchroniseNewL(); + return; + } + + // If we haven't returned yet, then no new messages to sync + // so just sync the flags of existing messages. + iSyncState = CImapSyncManager::EFolderSynchronise; + iNextAction = CImapSyncManager::ESyncFlags; + SetActive(); + CompleteSelf(); + return; + } + else + { + SyncCompleteL(); + Complete(iStatus.Int()); + } + } + } + break; + case CImapSyncManager::EFolderExpunge: + { + // Call expunge to delete messages marked for delete + __LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: calling EXPUNGE in folder %S", &iFullFolderPath)); + + iSyncState = CImapSyncManager::EFolderExpunge; + iNextAction = CImapSyncManager::EFolderLocalExpunge; + iSavedSession->ExpungeL(iStatus); + SetActive(); + } + break; + case CImapSyncManager::EFolderClose: + { + //Permanently removes all messages that have the deleted flag set + //from the currently selected mailbox + iSyncState = CImapSyncManager::EFolderClose; + iNextAction = CImapSyncManager::EFolderReSelect; + iSavedSession->CloseL(iStatus); + SetActive(); + } + break; + case CImapSyncManager::EFolderReSelect: + { + //Selecting a mailbox so that messages in the mailbox can be accessed. + iSyncState = CImapSyncManager::EFolderReSelect; + iNextAction = CImapSyncManager::EFolderLocalExpunge; + SelectL(iStatus, *iSavedSession); + SetActive(); + } + break; + case CImapSyncManager::EFolderLocalExpunge: + { + // delete local messages locally + __LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: local message delete in folder %S", &iFullFolderPath)); + + // Check the delete ids list and delete messages according to what's in the list + TInt deletedcount = iDeletedMessageIds.Count(); + // Remove message from the local server + for(TInt deleteloop = 0; deleteloop < deletedcount; ++deleteloop) + { + __LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Local message (%d) deleting.", (*iFolderIndex)[iDeletedMessageIds[deleteloop]].iMsvId)); + DeleteMessageL((*iFolderIndex)[iDeletedMessageIds[deleteloop]].iMsvId); + } + + // clear the count of expunged messages to prevent a sync triggering + CImapFolderInfo* folderInfo = iSavedSession->SelectedFolderInfo(); + if (folderInfo->ExpungedMessages().Count()==deletedcount) + { + folderInfo->ResetExpungedMessages(); + + // store updated remote mailbox size. + iMailboxSize = folderInfo->Exists(); + SetEntryL(iMailboxId); + TMsvEmailEntry entry = iServerEntry.Entry(); + entry.SetRemoteFolderEntries(folderInfo->Exists()); + ChangeEntryL(entry); + } + + iSyncState = CImapSyncManager::EFolderLocalExpunge; + iNextAction = CImapSyncManager::ENotSyncing; + } + // fall through... + case CImapSyncManager::ENotSyncing: + { + iSyncState = CImapSyncManager::ENotSyncing; + Complete(iStatus.Int()); + } + break; + default: + break; + } + } + +/** +Processing of the returned message ids after the completion of the IMAP SEARCH command. +If there are more messages ids to be look at then the SEARCH commands will be call again. +This call could set this folder object active + +@return ETrue if the call has resulted in the folder object being made Active. +*/ +TBool CImapFolder::CheckAndProgressMessageSyncL() + { + iFolderPosition += KImapUidSearchSize; + + __LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Session Search command completed: %d", iFolderPosition)); + + // Check to see if we have all the messages. + if(iFolderPosition >= iMailboxSize) + { + __LOG_TEXT(iSavedSession->LogId(), "ImapFolder: UID search complete"); + + //update the progress object + iMsgsToDo = iMatchingMessageIds.Count(); + + iSyncState = CImapSyncManager::ESyncOld; + // We have the full list of remote UIDs - fall through. + } + else + { + // Should be able to hit this code if KImapUidSearchSize is reduced to < the size + // of the remote mailbox (iMailboxSize) + // SearchString non 0 means a refined UID SEARCH is required + + if(iImapSettings.SearchString().Length() != 0) + { + // Refined search required + // Still uses the indexes but appends user specified search criteria + RBuf8 tempstr; + tempstr.CleanupClosePushL(); + + TPtrC8 searchstr = iImapSettings.SearchString(); + + TInt tempstrLen = KImapFetchHeaderRangeSearch().Length() + KImapMaxIntChars + KImapMaxIntChars + searchstr.Length(); + tempstr.CreateL(tempstrLen); + tempstr.Format(KImapFetchHeaderRangeSearch, iFolderPosition+1, Min(iMailboxSize,iFolderPosition+KImapUidSearchSize), &searchstr); + + __LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Refined UID search - get next manageable block of UIDs: %S", &tempstr)); + iSavedSession->SearchL(iStatus, tempstr, iMatchingMessageIds); + + // Go active + SetActive(); + + CleanupStack::PopAndDestroy(&tempstr); + return ETrue; + } + else + { + // Normal unrefined SEARCH. Will pull back all the UIDs between indexes + // Perform a UID search on this folder. + RBuf8 tempstr; + tempstr.CleanupClosePushL(); + + TInt tempstrLen = KImapFetchHeaderRange().Length() + KImapMaxIntChars + KImapMaxIntChars; + tempstr.CreateL(tempstrLen); + tempstr.Format(KImapFetchHeaderRange, iFolderPosition+1, Min(iMailboxSize,iFolderPosition+KImapUidSearchSize)); + + __LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Unrefined UID search - get next manageable block of UIDs: %S", &tempstr)); + iSavedSession->SearchL(iStatus, tempstr, iMatchingMessageIds); + + // Go active + SetActive(); + + CleanupStack::PopAndDestroy(&tempstr); + return ETrue; + } + } + return EFalse; + } + +/** +Sets number of messages to be sync. If there is a search string or the sync limit is outside +the mailbox size then everything will be sync. i.e. the sync limit will be set to 0. + +*/ +TInt CImapFolder::GetSyncThreshold() + { + // If no UID Search string defined then the old logic is used + if((iImapSettings.SearchString().Length() == 0) && (iSyncLimit > KImImapSynchroniseAll)) + { + if(iSyncLimit < iMailboxSize) + { + return (iMailboxSize - iSyncLimit); + } + } + + return 0; + } + +/** +Go through the list of found message ids and work out if each message is within the sync limit +as defined by the user. If a message isn't within the limit it will be removed if it hasn't been fetched. + +@param aSyncThreshold +The number of messages that will define the sync limit +*/ +void CImapFolder::CheckMessagesInRangeL(TInt aSyncThreshold) + { + // At this stage (prior to truncation), we have a full list of all the message + // UIDs in the remote folder. We also have a list of all UIDs for messages in + // the local folder. With these lists, we can establish which local messages + // have been orphaned and which have not using the following rules. + + // * If the remote message is no longer there: + // (1) Then the local message is orphaned (always). + // * If the remote message is still there and it is not one of the N most recent: + // (2) If the local message has body parts do nothing. + // (3) If the local message does not have body parts, then it is orphaned + // unless is is a message we have selected for download. + // * If the remote message is still there and it is one of the N most recent: + // (4) Do nothing. + + // Search through the local folder list while checking the remote folder list. + + TInt localcount = iFolderIndex->Size(); + TInt resultscount = iMatchingMessageIds.Count(); + + iFolderPosition = 0; + + iDeletedMessageIds.Reset(); + + for(TInt localloop = 0; localloop < localcount; ++localloop) + { + // Go through the list of index, if can't find matching in found ids then mark for delete. + TInt resultsloop = 0;//listprogress; + TBool removeThis = EFalse; + TBool folderPositionFound = EFalse; + + while(resultsloop < resultscount) + { + TUint remoteUid = iMatchingMessageIds[resultsloop]; + TUint localUid = (*iFolderIndex)[localloop].iUid; + + // Check the synclimit + TBool inSyncRange = (resultsloop >= aSyncThreshold); + if((remoteUid > localUid) || (remoteUid > iHighestUid)) + { + break; + } + if(remoteUid == localUid) + { + // Found id, sets new lower limit then exit loop + folderPositionFound = ETrue; + + // Check for if message is in the syncrange + if(!inSyncRange) + { + // Here the remote uid matches the local uid, but the message falls outside + // of the N most recent messages. See cases (2) & (3). + SetEntryL((*iFolderIndex)[localloop].iMsvId); + TMsvEmailEntry message(iServerEntry.Entry()); + // Does message have any downloaded parts? + if(!message.Complete() && + !message.BodyTextComplete()) + { + // The local message does not have any body parts and + // is not selected for download, so it is orphaned. + // See case (3) above. + __LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Local message (%d) is only header and not selected for download, deleting", (*iFolderIndex)[localloop].iMsvId)); + removeThis = ETrue; + iOrphanedMessages++; + } + } + break; + } + + ++resultsloop; + } + if(!folderPositionFound || removeThis) + { + // Saved the index position of the message to be deleted from the local view + __LOG_FORMAT((iSavedSession->LogId(), "ImapFolder: Local message (%d) marked for deleting, loop = %d", (*iFolderIndex)[localloop].iMsvId, localloop)); + iDeletedMessageIds.Append(localloop); + } + else + { + iMsgsDone++; + } + + } + + } + + +/** +Go through the list of found remote message ids and work out if each message header has been fetched or not. +Messages that are missing from the local index will be added to a list of messages to be fetch. +*/ +void CImapFolder::CheckForMissingMessagesUidsL() + { + iMissingMessageIds.Reset(); + + if(iFolderIndex->Size() == 0) + { + return; + } + + TInt remotecount = iMatchingMessageIds.Count(); + + for(TInt remoteloop = 0; remoteloop < remotecount; ++remoteloop) + { + TUint remoteUid = iMatchingMessageIds[remoteloop]; + + if(iFolderIndex->FindMsg(remoteUid) == 0) + { + iMissingMessageIds.Append(remoteUid); + } + } + } +/** +Cancels any outstanding asynchronous service requests. +*/ +void CImapFolder::DoCancel() + { + iSavedSession->Cancel(); + CMsgActive::DoCancel(); + } + +/** +Called when the requested operation is completed. +Cleans up data members used only during synchronisation +*/ +void CImapFolder::DoComplete(TInt& /*aStatus*/) + { + iCachedEntryData->Reset(); + iSelection->Reset(); + iFolderIndex->Reset(); + iMatchingMessageIds.Reset(); + iDeletedMessageIds.Reset(); + iMessageFlagInfoArray.Reset(); + + // iSessionFolderInfo is not owned by this class, + // Set to NULL as it should never be assumed to exist. + iSessionFolderInfo = NULL; + } + +/** +Returns the full mailbox path description from the Message server + +@return Pointer to the descriptor object +*/ +EXPORT_C TDesC& CImapFolder::FullFolderPathL() + { + if(iFullFolderPath.Length() == 0) + { + HBufC16* temp = MakePathL(ETrue); + iFullFolderPath.Close(); + iFullFolderPath.Assign(temp); + } + + return iFullFolderPath; + } + +/** +Returns the mailbox uid from the Message server + +@return The mailbox UID; +*/ +EXPORT_C TMsvId CImapFolder::MailboxId() + { + return iMailboxId; + } + +/** +Issue the IMAP select command to select this folder using the supplied session. +The session must already have been opened and connected. + +NOTE: The client TRequestStatus is passed through to the CImapSession. + Therefore a CImapSession::Cancel() must be called to cancel the + SELECT operation. +NOTE: UpdateSessionFolderInfoL() should be called following completion + of the select operation. And definitely prior to synchronising + the folder (see below) + +@param aStatus +The status request object to be use to send the completion signal. +This is passed through to the session object. +@param aSession +The session to be use for the SELECT command +*/ +EXPORT_C void CImapFolder::SelectL(TRequestStatus& aStatus, CImapSession& aSession, TBool aSelectInbox) + { + // Construct the info object for the select command then just pass it on. + CImapFolderInfo* folderInfo = CImapFolderInfo::NewL(); + CleanupStack::PushL(folderInfo); + folderInfo->SetMsvId(iMailboxId); + folderInfo->SetNameL(FullFolderPathL()); + + // Issue the SELECT - the session takes immediate + // ownership of the folder info object. + CleanupStack::Pop(folderInfo); + aSession.SelectL(aStatus, folderInfo, aSelectInbox); + } + + +/** +Issue the IMAP select command to select this folder using the supplied session. +The session must already have been opened and connected. + +NOTE: The client TRequestStatus is passed through to the CImapSession. + Therefore a CImapSession::Cancel() must be called to cancel the + EXAMINE operation. +NOTE: UpdateSessionFolderInfoL() should be called following completion + of the EXAMINE operation. + +@param aStatus +The status request object to be use to send the completion signal. +This is passed through to the session object. +@param aSession +The session to be use for the EXAMINE command +*/ +EXPORT_C void CImapFolder::ExamineL(TRequestStatus& aStatus, CImapSession& aSession) + { + // Construct the info object for the select command then just pass it on. + CImapFolderInfo* folderInfo = CImapFolderInfo::NewL(); + CleanupStack::PushL(folderInfo); + folderInfo->SetMsvId(iMailboxId); + folderInfo->SetNameL(FullFolderPathL()); + + // Issue the EXAMINE - the session takes immediate + // ownership of the folder info object. + CleanupStack::Pop(folderInfo); + aSession.ExamineL(aStatus, folderInfo); + } + +/** +Updates the remote folder mirror with status information about the folder +on the remote server. Called at the start of the synchronisation process. + +Panics if the folder is not selected folder on the passed session. + +NOTE: MUST be called prior to synchronising (done internally) + +NOTE: updates the context of the CMsvServerEntry to the folder. + +@param aSession - the session that has SELECTed this folder +*/ +EXPORT_C void CImapFolder::UpdateSessionFolderInfoL(CImapSession& aSession) + { + iSessionFolderInfo = aSession.SelectedFolderInfo(); + if(iSessionFolderInfo) + { + __ASSERT_ALWAYS(iSessionFolderInfo->MsvId()==iMailboxId, User::Invariant()); + + iMailboxSize = iSessionFolderInfo->Exists(); + // Need to be save away. NOT Done on Session. + SetEntryL(iMailboxId); + TMsvEmailEntry entry = iServerEntry.Entry(); + entry.SetRemoteFolderEntries(iSessionFolderInfo->Exists()); + if(entry.UID() != iSessionFolderInfo->UidValidity()) + { + entry.SetValidUID(EFalse); + } + entry.SetRecentIMAP4Flag(iSessionFolderInfo->Recent()); + entry.SetUnreadIMAP4Flag(iSessionFolderInfo->Unseen()); + ChangeEntryL(entry); + } + } + +/** +This can be called at any time with a given IMAP session to find out if the folder has +changed in any way since the last sync. + +Panics if the folder is not selected folder on the passed session. + +@return +Returns true if any of Exists, Recent or Expunged counts are non-zero. +*/ +EXPORT_C TBool CImapFolder::Changed(CImapSession& aSession) + { + iSessionFolderInfo = aSession.SelectedFolderInfo(); + if(iSessionFolderInfo) + { + __ASSERT_ALWAYS(iSessionFolderInfo->MsvId()==iMailboxId, User::Invariant()); + + // True if the exists count has changed + // or if recent or expunged counts are non-zero + TBool existChanged = (iMailboxSize != iSessionFolderInfo->Exists()); + TBool flagChanged = iSessionFolderInfo->MessageFlagsChanged(); + TBool otherChanged = (existChanged || + iSessionFolderInfo->Recent() || + iSessionFolderInfo->ExpungedMessages().Count()); + + iFlagChangedOnly = (flagChanged && !(otherChanged)); + + return ( otherChanged || flagChanged ); + } + return EFalse; + } + +/** +Clears the counts that indicate that an event has occurred on the remote server +for the selected folder since the last sync operation. + +This method is called during synchronisation such that subsequent changes are +identified. +*/ +void CImapFolder::ClearChangeCounts() + { + if(iSessionFolderInfo) + { + // Do not clear exists count + iSessionFolderInfo->SetRecent(0); + iSessionFolderInfo->ResetExpungedMessages(); + iSessionFolderInfo->SetMessageFlagsChanged(EFalse); + } + iFlagChangedOnly = EFalse; + } + +/** +Set the current entry context on the Message server + +@param aId +The entry id to set in the Message server +@leave KErrNotFound if the entry does not exist or KErrLocked if the entry is locked. +*/ +void CImapFolder::SetEntryL(const TMsvId aId) + { + User::LeaveIfError(iServerEntry.SetEntry(aId)); + } + +/** +Sets the context's index entry to the specified values on the Message server + +@param aEntry +The new details for the entry. +@leave KErrAccessDenied - the entry is read only (deleted entry, standard folder, or locked); +KErrNotSupported - aEntry is invalid or the ID specified in aEntry is not the same as the context ID +or no context has been set for the object; +KErrNoMemory - a memory allocation failed. +*/ +void CImapFolder::ChangeEntryL(const TMsvEntry& aEntry) + { + User::LeaveIfError(iServerEntry.ChangeEntry(aEntry)); + } + +/** +Change entry in bulk mode (i.e no index file commit. no notify) + +@param aEntry +The new details for the entry. +@leave KErrAccessDenied - the entry is read only (deleted +entry, standard folder, or locked); +KErrNotSupported - aEntry is invalid or the +ID specified in aEntry is not the same as the context ID or no context has been +set for the object; +KErrNoMemory - a memory allocation failed. +*/ +void CImapFolder::ChangeEntryBulkL(const TMsvEntry& aEntry) + { + User::LeaveIfError(iServerEntry.ChangeEntryBulk(aEntry)); + } + +/** +Get a list of ids of the children from the current context of the message server. + +@param aSelection +A reference to the email entries selection object which will be filled with the ids of the child entries. +@leave Error from CMsvServerEntry::GetChildren. +*/ +void CImapFolder::GetChildrenL(CMsvEntrySelection& aSelection) + { + User::LeaveIfError(iServerEntry.GetChildren(aSelection)); + } + +/** +Delete a local message. + +@param aMessage +The TMsvId of the message to be removed from the message server. +*/ +EXPORT_C void CImapFolder::DeleteMessageL(const TMsvId aMessage) + { + if(aMessage == KMsvNullIndexEntryId) + { + // Attempted delete of null entry + return; + } + // Delete message and all subparts: first, move to parent + SetEntryL(aMessage); + + SetEntryL(iServerEntry.Entry().Parent()); + + // Do not leave when entry is in use + TInt err (iServerEntry.DeleteEntry(aMessage)); + if(err == KErrInUse) + { + // Dont leave if err = KErrInUse + } + else + { + User::LeaveIfError(err); + } + } + +/** +Add a message UID to the list of messages that should have the SEEN flag sets during the SYNC FLAGS stage. + +@param aMessage +The UID of the message to be added to the list of messages to have the SEEN flag set. +*/ +EXPORT_C void CImapFolder::AppendSetSeenL(const TUint32 aMessage) + { + if(iSetSeenList) + { + iSetSeenList->AppendL(aMessage); + } + else + { + iSetSeenList = new(ELeave) RArray(4); + iSetSeenList->AppendL(aMessage); + } + } + +/** +Add a message UID to the list of messages that should have the SEEN flag cleared during the SYNC FLAGS stage. + +@param aMessage +The UID of the message to be added to the list of messages to have the SEEN flag cleared. +*/ +EXPORT_C void CImapFolder::AppendClearSeenL(const TUint32 aMessage) + { + if(iClearSeenList) + { + iClearSeenList->AppendL(aMessage); + } + else + { + iClearSeenList = new(ELeave) RArray(4); + iClearSeenList->AppendL(aMessage); + } + } + +/** +Get MESSAGE ONLY children of a folder. Ignore shadows as they are not going to be synced against the server +*/ +void CImapFolder::GetMessageChildrenL(const TMsvId aFolder, CMsvEntrySelection* aChildren) + { + aChildren->Reset(); + // Get *all* the children + SetEntryL(aFolder); + GetChildrenL(*aChildren); + + delete iCachedEntryData; + iCachedEntryData = NULL; + + iCachedEntryData = new(ELeave) RArray(5); + + // Go through them, checking to see if they're messages and removing ones that aren't + TInt pos = 0; + while(pos < aChildren->Count()) + { + TMsvEntry* entryPtr; + TMsvId id = (*aChildren)[pos]; + User::LeaveIfError(iServerEntry.GetEntryFromId(id, entryPtr)); + + // Is it a message? And is it real (not shadow) + if(entryPtr->iType != KUidMsvMessageEntry || + entryPtr->iRelatedId != KMsvNullIndexEntryId ) + { + // No, remove it + aChildren->Delete(pos,1); + } + else + { + //cache two parts of the TMsvEntry data to avoid having to refind it later + TMsvCacheData data; + data.iOrphan = ((TMsvEmailEntry)(*entryPtr)).Orphan(); + data.iUid = ((TMsvEmailEntry)(*entryPtr)).UID(); + iCachedEntryData->AppendL(data); + // Next entry + pos++; + } + } + } + +/** +Populates the entry selection with messages that are eligible for auto-fetch. +Auto fetch is performed as a second synchronisation stage, following the header +synchronisation. Eligible messages are synchronised according to defined +download rules. Note that this method does not filter the returned entry +selection according to these rules. + +@return TInt - number of meesages in the updated selection +*/ +EXPORT_C TInt CImapFolder::GetFetchMessageChildrenL(CMsvEntrySelection& aSelection) + { + aSelection.Reset(); + // Get *all* the children + SetEntryL(iMailboxId); + GetChildrenL(aSelection); + + // Go through them, checking to see if they're messages and removing ones that aren't + TInt pos = 0; + TMsvEntry* entryPtr; + while(pos < aSelection.Count()) + { + TMsvId id = (aSelection)[pos]; + User::LeaveIfError(iServerEntry.GetEntryFromId(id, entryPtr)); + + // Remove entry from the selection if: + // It is not a message + // It is not real (ie it is a shadow entry) + // If has been previously fetched. + TBool previouslyFetched = ((TMsvEmailEntry*)entryPtr)->ValidUID(); + if(entryPtr->iType != KUidMsvMessageEntry || + entryPtr->iRelatedId != KMsvNullIndexEntryId || + previouslyFetched) + { + aSelection.Delete(pos,1); + } + else + { + ++pos; + } + } + + return aSelection.Count(); + } + +/** +Transfers the current selection into the iFolderIndex, and sorts it by UID. +*/ +void CImapFolder::MakeSortedFolderIndexL(TBool aUseCachedEntryData) + { + + TInt noofchildren = iSelection->Count(); + + // Reset folder index + iFolderIndex->SetSizeL(noofchildren); + TInt acounter = 0; + + if(!aUseCachedEntryData) + { //can't rely on iCachedEntryData + TMsvEntry* entryPtr; + TMsvId id; + for(acounter = 0; acounter < noofchildren; acounter++) + { + // Save UID/TMsvId of this entry + id = (*iSelection)[acounter]; + User::LeaveIfError(iServerEntry.GetEntryFromId(id, entryPtr)); + (*iFolderIndex)[acounter].iUid = ((TMsvEmailEntry)(*entryPtr)).UID(); + (*iFolderIndex)[acounter].iMsvId = id; + } + } + else + { + for(acounter = 0; acounter < noofchildren; acounter++) + { + // Save UID/TMsvId of this entry + (*iFolderIndex)[acounter].iUid = (*iCachedEntryData)[acounter].iUid; + (*iFolderIndex)[acounter].iMsvId = (*iSelection)[acounter]; + } + } + + // Sort it by UID + iFolderIndex->Sort(); + + // Check for any duplicate UIDs (ie, a dud netscape server) + TMsvEntry* entryPtr; + TMsvEntry* nextEntryPtr; + for(acounter = 1; acounter < noofchildren; acounter++) + { + if((*iFolderIndex)[acounter].iUid != 0 && (*iFolderIndex)[acounter].iUid == (*iFolderIndex)[acounter-1].iUid) + { + if(!aUseCachedEntryData) + { + // Get the TMsvEntry for the message/folder + User::LeaveIfError(iServerEntry.GetEntryFromId((*iFolderIndex)[acounter].iMsvId,entryPtr)); + User::LeaveIfError(iServerEntry.GetEntryFromId((*iFolderIndex)[acounter-1].iMsvId,nextEntryPtr)); + // Check if type of TMsvEntry and type of next TMsvEntry are both Messages + if( entryPtr->iType.iUid == nextEntryPtr->iType.iUid && entryPtr->iType.iUid == KUidMsvMessageEntryValue) + { + User::Leave(KErrCorrupt); + } + } + else + { + User::Leave(KErrCorrupt); + } + } + + } + } + +/** +Synchronise the local view of the contents with that of the remote folder. +If the folder is not the currently in SELECTED state on the CImapSesssion, +the first step should be to issue a SELECT command via the IMAP Session +*/ +EXPORT_C void CImapFolder::SynchroniseL(TRequestStatus& aStatus, CImapSession& aSession, TBool aNewOnly, TInt aDeleteOption) + { + __LOG_TEXT(aSession.LogId(), "SyncMan: Starting full IMAP Sync"); + + //initialise counters + iMsgsDone=0; + iMsgsToDo=0; + + iHeadersFetched=0; + iOrphanedMessages=0; + iRemoteMessagesDeleteTagged=0; + + // Saved the calling status request + iSavedSession = &aSession; + iDeleteOption = aDeleteOption; + + // Set the Synchronisation states + iSyncState = CImapSyncManager::ESynchronise; + iNextAction = CImapSyncManager::ESynchronise; + iMatchingMessageIds.Reset(); + + // Update the remote folder info and clear the change indication counts + UpdateSessionFolderInfoL(*iSavedSession); + ClearChangeCounts(); + + // Some pre-bits that need doing - get the children & count them + // Get the folder info for the selected folder + SetEntryL(iMailboxId); + TMsvEmailEntry message = iServerEntry.Entry(); + GetMessageChildrenL(iMailboxId, iSelection); + TInt noofchildren = iSelection->Count(); + + // Check if new flags for the current selection should be cleared. + ClearNewFlagsIfRequiredL(); + + /* + First of all check the UIDVALIDITY of the mirror and the + remote folder match - this is indicated by message.ValidUID() returning true. + If not, we have to delete everything in the local mirror and start again. + We also do this if there are 0 messages in the remote mailbox (0 EXISTS) + and there are messages locally + */ + if(!message.ValidUID() || iMailboxSize == 0) + { + /* + They don't match: do we have local children? + If we were doing a new-only sync, change this to a full sync as the + UIDVALIDITY shows major changes. + */ + aNewOnly = EFalse; + + if(noofchildren) + { + // We've got local children then delete them + for(TInt a = 0; a < noofchildren; a++) + { + // We should be skipping locally generated messages. i.e. Offline operations + DeleteMessageL((*iSelection)[a]); + } + + // Reset the number of children as this may have changed. + GetMessageChildrenL(iMailboxId, iSelection); + noofchildren = iSelection->Count(); + } + + // Match the remote's UIDVALIDITY: + // reset the context as it may have been used by the deletion process. + SetEntryL(iMailboxId); + if(!message.ValidUID()) + { + // Do the change if necessary + message.SetUID(iSessionFolderInfo->UidValidity()); + message.SetValidUID(ETrue); + ChangeEntryL(message); + } + } + + // Any remote messages? If not, complete now as there's nothing else to do + if(iMailboxSize == 0) + { + // This folder is now sync'ed + // No need to set seen flags as no messages in remote mailbox + SyncCompleteL(); + Queue(aStatus); + Complete(KErrNone); + return; + } + + // Start the synchronise with sync'ing old messages: are there any + // messages in our mirror folder? + iSyncState = CImapSyncManager::ESyncOld; //EImapStateSynchroniseWait; + iNextAction = CImapSyncManager::ESyncOld; + + iSomeUnread = EFalse; + iHighestUid = 0; + + // Any children? + iFolderIndex->Reset(); + if(noofchildren > 0) + { + // Children exist, we need to do an old-sync to check all the messages + // are still there. + + // Build an index of UIDs/TMsvIds currently in the mirror folder, and + // sort this by UID: this is the order in which we expect the fetch to + // return UIDs - any missing have been deleted on the server. They may + // well not be in UID order in the index because locally-appended + // messages will not have been added to the index in UID order. + TRAPD(err,MakeSortedFolderIndexL(ETrue)); + if(err != KErrNone) + { + // Children exist, need to do old sync + Queue(aStatus); + Complete(err); + return; + } + + // Find the highest UID in the index + iHighestUid = (*iFolderIndex)[noofchildren-1].iUid; + } + + // Retrieve folder synchronisation limit. + if(iSyncManager.EntryIsInbox(iServerEntry.Entry())) + { + // Leave iSyncLimit at the maximum if a Search String is set + // If no Search String is set and this is the inbox, then use the inbox sync limit. + if(iImapSettings.SearchString().Length() == 0) + { + iSyncLimit = iImapSettings.InboxSynchronisationLimit(); + } + } + else + { + // Otherwise use the folder sync limit. + // Leave iSyncLimit at the maximum if a Search String is set + if(iImapSettings.SearchString().Length() == 0) + { + iSyncLimit = iImapSettings.MailboxSynchronisationLimit(); + } + } + + // Call function to create and send the search command for the remote server message ids + // if there wasn't a command sent then a full sync is needed. + if(CreateAndSendUIDSearchStringL(aStatus)) + { + return; + } + + if(noofchildren > 0) + { + // A complete list of the message ids on the remote server is needed. + if(!aNewOnly && iHighestUid > 0) + { + // Do old sync + iSyncState = CImapSyncManager::ESyncOld; + iNextAction = CImapSyncManager::ESyncSearch; + + iFolderPosition = 0; + // If a UID Search String is used it looks like this is FULL sync only + // so leave as is + RBuf8 tempstr; + tempstr.CleanupClosePushL(); + + TInt tempstrLen = KImapFetchHeaderUIDRange().Length() + KImapMaxIntChars; // KImapFetchHeaderUIDRange provides enough room for "1" + tempstr.CreateL(tempstrLen); + tempstr.Format(KImapFetchHeaderUIDRange, 1, iHighestUid); + + __LOG_FORMAT((iSavedSession->LogId(), "CImapFolder SynchroniseL : Sending Search for old messages with no synclimits: %S", &tempstr)); + + iSavedSession->SearchL(iStatus, tempstr, iMatchingMessageIds); + // Go active and note that a send has been queued + Queue(aStatus); + SetActive(); + + CleanupStack::PopAndDestroy(&tempstr); + return; + } + } + + // There was no need to search for old ids hence do new sync + SynchroniseNewL(); + Queue(aStatus); + } + +/** +Find the messages available on the remote server by creating and send the string of messages +to be search for by the session SearchL command. + +@param aStatus +Reference to the request status object to be use for the Search command. +@return ETrue if the search command has been sent and the folder object has been made Active. +*/ +TBool CImapFolder::CreateAndSendUIDSearchStringL(TRequestStatus& aStatus) + { + // Get the user defined UID SEARCH string and if there is one + // do a refined search. + if(iImapSettings.SearchString().Length() != 0) + { + iSyncState = CImapSyncManager::ESyncSearch; + iNextAction = CImapSyncManager::ESyncSearch; + + iFolderPosition = 0; + + // Refined search + RBuf8 tempstr; + tempstr.CleanupClosePushL(); + + TPtrC8 searchstr = iImapSettings.SearchString(); + + TInt tempstrLen = KImapFetchHeaderRangeSearch().Length() + KImapMaxIntChars + searchstr.Length(); // KImapFetchHeaderRangeSearch provides enough room for "1" + tempstr.CreateL(tempstrLen); + tempstr.Format(KImapFetchHeaderRangeSearch, 1, Min(iMailboxSize,KImapUidSearchSize), &searchstr); + + __LOG_FORMAT((iSavedSession->LogId(), "CImapFolder SynchroniseL : Sending Search for old messages with search string: %S", &searchstr)); + iSavedSession->SearchL(iStatus, tempstr, iMatchingMessageIds); + + // Go active and note that a send has been queued + Queue(aStatus); + SetActive(); + + CleanupStack::PopAndDestroy(&tempstr); + return ETrue; + } + else + { + // if no search string we use the old behaviour + // Check the folder synchronisation limit. + if(iSyncLimit > KImImapSynchroniseNone) + { + // Limited folder synchronisation, perform a UID search. + iSyncState = CImapSyncManager::ESyncSearch; + iNextAction = CImapSyncManager::ESyncSearch; + + iFolderPosition = 0; + + // Perform a UID search on this folder. + RBuf8 tempstr; + tempstr.CleanupClosePushL(); + + TInt tempstrLen = KImapFetchHeaderRange().Length() + KImapMaxIntChars; // KImapFetchHeaderRange provides enough room for "1" + tempstr.CreateL(tempstrLen); + tempstr.Format(KImapFetchHeaderRange, 1, Min(iMailboxSize,KImapUidSearchSize)); + + __LOG_FORMAT((iSavedSession->LogId(), "CImapFolder SynchroniseL : Sending Search for old messages with synclimits: %S", &tempstr)); + iSavedSession->SearchL(iStatus, tempstr, iMatchingMessageIds); + + // Go active and note that a send has been queued + Queue(aStatus); + SetActive(); + + CleanupStack::PopAndDestroy(&tempstr); + return ETrue; + } + else if(iSyncLimit == KImImapSynchroniseNone) + { + // No synchronisation required. + // This folder is now sync'ed + SyncCompleteL(); + Queue(aStatus); + iSyncState = CImapSyncManager::ENotSyncing; + // iSyncLimit=KImImapSynchroniseNone, no sync required + Complete(KErrNone); + return ETrue; + } + // iSyncLimit <= KImImapSynchroniseAll so Full synchronisation required - fall through. + } + return EFalse; + } + +/** +Synchronise new messages for a given range. +*/ +void CImapFolder::SynchroniseRangeL(const TUint32 aLowUid,const TUint32 aHighUid) + { + + iSyncState = CImapSyncManager::EFolderSynchronise; + iNextAction = CImapSyncManager::ESyncFlags; + iFolderPosition = 0; + + // First, resize folder index to hold all messages in the folder, + // as opposed to the old sync list. This will preserve the old + // contents of the index, which is what we want as it's up-to-date + // and correct. + iFolderIndex->SetSizeL(iMailboxSize); + + // Create list of priority fields to request + // If a UID search string has been specified, the we should create the UID FETCH + // string from the UID integer list. + if(iImapSettings.SearchString().Length() != 0) + { + HBufC8* sequenceSetOnHeap = CImapSession::CreateSequenceSetLC(iMatchingMessageIds); + iSavedSession->FetchBodyStructureAndHeadersL(iStatus, *sequenceSetOnHeap, KImapSmallHeaderFields(), *this); + CleanupStack::PopAndDestroy(sequenceSetOnHeap); + } + else + { + RBuf8 tempstr; + tempstr.CleanupClosePushL(); + + TInt tempstrLen = KImapFetchHeaderRange().Length() + KImapMaxIntChars + KImapMaxIntChars; + tempstr.CreateL(tempstrLen); + tempstr.Format(KImapFetchHeaderRange, aLowUid, aHighUid); + + iSavedSession->FetchBodyStructureAndHeadersL(iStatus, tempstr, KImapSmallHeaderFields(), *this); + + CleanupStack::PopAndDestroy(&tempstr); + } + SetActive(); + } + +/** +Synchronise new mesasges from current highest UID to end. +*/ +void CImapFolder::SynchroniseNewL() + { + iSyncState = CImapSyncManager::ESyncNew; + iNextAction = CImapSyncManager::ESyncFlags; + iFolderPosition = 0; + + // First, resize folder index to hold all messages in the folder, + // as opposed to the old sync list. This will preserve the old + // contents of the index, which is what we want as it's up-to-date + // and correct. + iFolderIndex->SetSizeL(iMailboxSize); + + // Fetch just the header of the new mails + RBuf8 tempstr; + tempstr.CleanupClosePushL(); + + TInt tempstrLen = KImapFetchHeaderToEnd().Length() + KImapMaxIntChars; + tempstr.CreateL(tempstrLen); + tempstr.Format(KImapFetchHeaderToEnd, iHighestUid + 1); + + iSavedSession->FetchBodyStructureAndHeadersL(iStatus, tempstr, KImapSmallHeaderFields(), *this); + SetActive(); + + CleanupStack::PopAndDestroy(&tempstr); + } + +/** +Update iDate in iMailboxId to show the time now (last sync time) +*/ +void CImapFolder::SyncCompleteL() + { + __LOG_TEXT(iSavedSession->LogId(), "ImapFolder: Starting SyncCompleteL"); + // Find entry + SetEntryL(iMailboxId); + TMsvEmailEntry message = iServerEntry.Entry(); + + // Find 'now' + message.iDate.UniversalTime(); + + // Check to see if there has been a change in the number of messages in the remote folder. + TBool folderSizeChanged = (message.RemoteFolderEntries()!=iMailboxSize); + + // Set 'unread' flag on folder if there are any unread messages within it + if(message.Unread() != iSomeUnread || !message.Visible() || folderSizeChanged) + { + // Update flags + message.SetUnread(iSomeUnread); + message.SetVisible(ETrue); + message.SetRemoteFolderEntries(iMailboxSize); + ChangeEntryBulkL(message); // completed at the end of this method + } + + // we need to ensure the hierarchy of folders containing this one + // is now visible. Note previously this incorrectly only did this + // when we were not in DisconncetedUserMode + do + { + // Move up one + SetEntryL(message.Parent()); + message=iServerEntry.Entry(); + + // Ensure visibility + if(!message.Visible()) + { + message.SetVisible(ETrue); + ChangeEntryBulkL(message); // completed at the end of this method + } + } + while(message.iType!=KUidMsvServiceEntry); + + + // commit any outstanding entries to the index file to complete the bulk + // synchronization operation + iServerEntry.CompleteBulk(); + + // Set the current id to null so that we aren't locking any folders + SetEntryL(KMsvNullIndexEntryId); + } + +/** +Reset subscription flags for all children, and recurse into folders +*/ +void CImapFolder::ResetSubscriptionFlagsL(const TMsvId aFolder) + { + // Do this one + SetEntryL(aFolder); + TMsvEmailEntry entry = iServerEntry.Entry(); + + // A folder or service? If not, return + if(entry.iType != KUidMsvServiceEntry && + entry.iType != KUidMsvFolderEntry) + { + return; + } + + // Reset flag if needed + if(entry.Subscribed()) + { + // Reset flag and save + entry.SetSubscribed(EFalse); + ChangeEntryL(entry); + } + + // Any children? + CMsvEntrySelection *children = new (ELeave) CMsvEntrySelection; + CleanupStack::PushL(children); + GetChildrenL(*children); + if(children->Count()) + { + // Do each in turn + for(TInt child = 0; child < children->Count(); child++) + ResetSubscriptionFlagsL((*children)[child]); + } + CleanupStack::PopAndDestroy(); + } + +/** +Performs any outstanding offline delete operations + +deletes from the remote server any messages marked /deleted locally. +*/ +EXPORT_C void CImapFolder::SyncDeletesL(TRequestStatus& aStatus, CImapSession& aSession) + { + iSavedSession = &aSession; + + SetEntryL(iMailboxId); + GetMessageChildrenL(iMailboxId, iSelection); + TRAPD(err,MakeSortedFolderIndexL(ETrue)); + if(err!=KErrNone) + { + Queue(aStatus); + Complete(err); + return; + } + TInt pos = 0; + TInt deleted = 0; + + // Build command + HBufC8* command=HBufC8::NewLC(256); + RArray deletingMessageIds; + + // Start command + //command->Des().Append(_L8("UID STORE ")); + + iDeletedMessageIds.Reset(); + iMessageFlagInfoArray.Reset(); + + while(pos < iFolderIndex->Size()) + { + // Look for messages with deleted flag set + SetEntryL((*iFolderIndex)[pos].iMsvId); + if(((TMsvEmailEntry)iServerEntry.Entry()).DeletedIMAP4Flag()) + { + __LOG_FORMAT((aSession.LogId(), "Message id %x marked as deleted",iServerEntry.Entry().Id())); + ++iRemoteMessagesDeleteTagged; + + // Append to the delete list + TInt64 uid=(TUint)((TMsvEmailEntry)iServerEntry.Entry()).UID(); + deletingMessageIds.Append(uid); + // index of local message in iFolderIndex to be deleted + iDeletedMessageIds.Append(pos); + ++deleted; + } + + // Next message + pos++; + } + + // Anything deleted? + if(deleted) + { + // Append flags & send command + _LIT8(KDeleteFlag,"+FLAGS"); + _LIT8(KDeleteItem,"(\\Deleted)"); + command->Des().Append(KDeleteFlag); + + HBufC8* sequenceSetOnHeap = CImapSession::CreateSequenceSetLC(deletingMessageIds); + // Call the STORE function in the session + aSession.StoreL(iStatus, *sequenceSetOnHeap, KDeleteFlag(), KDeleteItem(), ETrue, iMessageFlagInfoArray); + CleanupStack::PopAndDestroy(sequenceSetOnHeap); + + Queue(aStatus); + SetActive(); + iSyncState = CImapSyncManager::EFolderEarlyExpunge; + + if (iImapSettings.UseExpunge()) + { + iNextAction = CImapSyncManager::EFolderExpunge; + } + else + { + iNextAction = CImapSyncManager::EFolderClose; + } + } + else + { + // Nothing to do just complete + Queue(aStatus); + Complete(KErrNone); + } + + // Get rid of command buffer + CleanupStack::PopAndDestroy(command); + } + +/** +Enquote a string (being sent as a string literal) if required +*/ +EXPORT_C void CImapFolder::DoQuoteL(HBufC16*& aBuffer) + { + // Null string? Nothing to do + if(!aBuffer->Length() || !aBuffer->Des().Length()) + { + return; + } + + // Anything needing quoting in there? + if(aBuffer->Des().Locate('\\') == KErrNotFound && + aBuffer->Des().Locate('\"') == KErrNotFound) + { + return; + } + + // Run through string, inserting quote characters as needed + for(TInt a = 0; aDes().Length(); a++) + { + if(aBuffer->Des()[a] == '\\' || aBuffer->Des()[a] == '\"') + { + HBufC16 *newbuf = aBuffer->ReAllocL(aBuffer->Des().Length()+1); + + // Been moved due to realloc? + if(newbuf != aBuffer) + { + // In all cases when DoQuoteL() is called, the buffer is on the top of + // the cleanup stack: change this to indicate the correct entry + CleanupStack::Pop(); + CleanupStack::PushL(aBuffer = newbuf); + } + + aBuffer->Des().Insert(a, KQuoteChar); + a++; + } + } + } + + +/** +Implementation of the observer function for the session fetch command. For each call +creates the required entry tree. +*/ +void CImapFolder::OnFetchLD(CImapFetchResponse* aImapFetchResponse) + { + // Take ownership of parameter + CleanupStack::PushL(aImapFetchResponse); + + CImapRfc822HeaderFields* headerinfo = aImapFetchResponse->HeaderFields(); + CImapBodyStructure* bodystructure = aImapFetchResponse->BodyStructure(); + + //update the progress object + ++iMsgsDone; + ++iHeadersFetched; + + if(headerinfo) + { + TUint remoteUid = aImapFetchResponse->MessageUid(); + if(iFolderIndex->Size() > 0) + { + if(iFolderIndex->FindMsg(remoteUid) != 0) + { + CleanupStack::PopAndDestroy(aImapFetchResponse); + return; + } + } + // Create an email entry in this folder. + SetEntryL(iMailboxId); + + // Skeleton for new entry + TMsvEmailEntry entry; + TFileName attachmentFilename; // Attachment filename + + entry.iSize = 0; + entry.iType = KUidMsvMessageEntry; + entry.iMtm = KUidMsgTypeIMAP4; + entry.iServiceId = iImapSettings.ServiceId(); + entry.SetValidUID(EFalse); // reuse ValidUID Flag record if the message has ever been fetched + entry.SetComplete(EFalse); + entry.SetUnread(ETrue); + entry.SetNew(ETrue); + entry.SetUID(aImapFetchResponse->MessageUid()); + + // Set from line in TMsvEntry + const TDesC8& temp2 = headerinfo->FieldValue(CImapRfc822HeaderFields::EImapFrom); + HBufC* decodedFromBuffer = HBufC::NewLC(temp2.Length()); + TPtr decodedFromPtr(decodedFromBuffer->Des()); + + iHeaderConverter.DecodeHeaderFieldL(temp2, decodedFromPtr); + entry.iDetails.Set(decodedFromPtr); + + // Set subject in TMsvEntry + const TDesC8& temp3 = headerinfo->FieldValue(CImapRfc822HeaderFields::EImapSubject); + HBufC* decodedSubjectBuffer = HBufC::NewLC(temp3.Length()); + TPtr decodedSubjectPtr(decodedSubjectBuffer->Des()); + + iHeaderConverter.DecodeHeaderFieldL(temp3, decodedSubjectPtr); + entry.iDescription.Set(decodedSubjectPtr); + + // Set the Date + entry.iDate = headerinfo->Date(); + + // Set the priority field + entry.SetPriority(headerinfo->PriorityL()); + + if (bodystructure) + { + SetMessageFlagsL(entry, bodystructure); + } + + // Set the flags + TBool messageInfoSeen = aImapFetchResponse->MessageFlagInfo().QueryFlag(TMessageFlagInfo::ESeen); + + entry.SetSeenIMAP4Flag(messageInfoSeen); + entry.SetAnsweredIMAP4Flag(aImapFetchResponse->MessageFlagInfo().QueryFlag(TMessageFlagInfo::EAnswered)); + entry.SetFlaggedIMAP4Flag(aImapFetchResponse->MessageFlagInfo().QueryFlag(TMessageFlagInfo::EFlagged)); + entry.SetDeletedIMAP4Flag(aImapFetchResponse->MessageFlagInfo().QueryFlag(TMessageFlagInfo::EDeleted)); + entry.SetDraftIMAP4Flag(aImapFetchResponse->MessageFlagInfo().QueryFlag(TMessageFlagInfo::EDraft)); + entry.SetRecentIMAP4Flag(aImapFetchResponse->MessageFlagInfo().QueryFlag(TMessageFlagInfo::ERecent)); + + // Are we configured to update the \seen flag on the server? + if (iImapSettings.UpdatingSeenFlags()) + { + // Now copy the inverse of the \Seen flag down to the clients Unread flag + if (messageInfoSeen) + { + entry.SetUnread(EFalse); + } + } + + // note that an unread message has been spotted. + if(!entry.SeenIMAP4Flag() || entry.RecentIMAP4Flag()) + { + iSomeUnread = ETrue; + } + + // If sync'ing to download rules is disabled, mark that message as + // "fetched" using the re-used ValidUID flag. This prevents the + // message from being sync'd according to download rules if rules + // are subsequently enabled. + if(!iImapSettings.UseSyncDownloadRules()) + { + entry.SetValidUID(ETrue); + } + + // Create message + User::LeaveIfError(iServerEntry.CreateEntryBulk(entry)); + // The matching CompleteBulk() is called in OnFetchCommit() and the ESyncFlags section of the DoRunL() + // that is called after FetchBodyStructureAndHeadersL() has completed. + __ASSERT_DEBUG(iNextAction == CImapSyncManager::ESyncFlags, User::Invariant()); + CleanupStack::PopAndDestroy(decodedSubjectBuffer); + CleanupStack::PopAndDestroy(decodedFromBuffer); + } + + CleanupStack::PopAndDestroy(aImapFetchResponse); + } + +void CImapFolder::OnFetchCommit() + { + iServerEntry.CompleteBulk(); + } + +/** +Set or clear the \\Seen flags on the server + +@param aUpdateMode +ETrue -> Sets the flag +@return False if no messages need to be processed +*/ +TBool CImapFolder::ProcessSeenFlagsL(TSeenFlagUpdateMode aUpdateMode) + { + RArray* pendingList; + TBool settingFlag = (aUpdateMode == ESetSeenFlag); + + // Point pendingList to the correct list + pendingList = (settingFlag ? iSetSeenList: iClearSeenList); + + // Exit if nothing to process + if(!pendingList->Count()) + { + return EFalse; + } + + __LOG_FORMAT((iSavedSession->LogId(), "CImapFolder : ProcessSeenFlags(%d)", aUpdateMode)); + + _LIT8(KStoreFlagsSetCommand, "+FLAGS"); + _LIT8(KStoreFlagsClearCommand, "-FLAGS"); + _LIT8(KStoreFlagsSeenCommand,"(\\Seen)"); + + iMessageFlagInfoArray.Reset(); + + HBufC8* sequenceSetOnHeap = CImapSession::CreateSequenceSetLC(*pendingList); + + // Call the STORE function in the session + if(settingFlag) + { + iSavedSession->StoreL(iStatus, *sequenceSetOnHeap, KStoreFlagsSetCommand(), KStoreFlagsSeenCommand(), ETrue, iMessageFlagInfoArray); + } + else + { + iSavedSession->StoreL(iStatus, *sequenceSetOnHeap, KStoreFlagsClearCommand(), KStoreFlagsSeenCommand(), ETrue, iMessageFlagInfoArray); + } + + CleanupStack::PopAndDestroy(sequenceSetOnHeap); + + SetActive(); + + // Reset the list + pendingList->Reset(); + + return ETrue; + } + +/** +Construct a full mailbox path for the folder object +This is expensive in memory movement terms, as it works UP the path, +inserting new data at the start. This is based on the principle that it's +more expensive to find an entry in the index with SetEntryL() than it is to +move some bytes about, otherwise we'd find the path upwards then create the +string downwards. +*/ +EXPORT_C HBufC16* CImapFolder::MakePathL(const TBool aIncludeLeaf) + { + // Making a path: we start with nothing + HBufC16 *path = HBufC16::NewLC(256); + TBool skipfirst = ETrue; + TMsvId traverse = iMailboxId; + + // Move to the entry + User::LeaveIfError(iServerEntry.SetEntry(traverse)); + + // Skipping the leaf? + if(!aIncludeLeaf && iServerEntry.Entry().iType != KUidMsvServiceEntry) + { + // Up a level before we generate the path + traverse = iServerEntry.Entry().Parent(); + User::LeaveIfError(iServerEntry.SetEntry(traverse)); + } + + // Check and see if we are dealing with the INBOX, in which case return immediately + if (iSyncManager.EntryIsInbox(iServerEntry.Entry())) + { + path->Des().Insert(0,KIMAP_INBOX); + CleanupStack::Pop(path); + return path; + } + + // While we can still go up within this service... + while(iServerEntry.Entry().iType != KUidMsvServiceEntry) + { + // Make sure the path maxlength is still ok with the added folder name and an extra separator character + if((path->Length() + iServerEntry.Entry().iDetails.Length() + 1) > path->Des().MaxLength()) + { + HBufC16* newpath = path->ReAllocL(path->Length() + iServerEntry.Entry().iDetails.Length() + 1); + if(path != newpath) + { + CleanupStack::Pop(path); + path = newpath; + CleanupStack::PushL(path); + } + } + + // Add the name of this component to the path + if(!skipfirst) + { + path->Des().Insert(0,iImapSettings.PathSeparator()); + } + else + { + skipfirst = EFalse; + } + + // Ensure uppercase 'INBOX' is used in folder name. This allows case + // sensitive searches to be used later. + if (iSyncManager.EntryIsInbox(iServerEntry.Entry())) + { + path->Des().Insert(0,KIMAP_INBOX); + } + else + { + path->Des().Insert(0,iServerEntry.Entry().iDetails); + } + + // Go up a level + SetEntryL(traverse = iServerEntry.Entry().Parent()); + } + + // Add the path at the very start, if it exists + if(iImapSettings.FolderPath().Length()) + { + // Make sure the path maxlength is still ok with the added folder path and an extra separator character + if((path->Length() + iImapSettings.FolderPath().Length() + 1) > path->Des().MaxLength()) + { + HBufC16* newpath = path->ReAllocL(path->Length() + iImapSettings.FolderPath().Length() + 1); + if(path != newpath) + { + CleanupStack::Pop(path); + path = newpath; + CleanupStack::PushL(path); + } + } + + // Anything there already? If not, don't bother with the separator + if(path->Des().Length()) + { + path->Des().Insert(0,iImapSettings.PathSeparator()); + } + + RBuf tempstr; + tempstr.CleanupClosePushL(); + tempstr.CreateL(iImapSettings.FolderPath().Length()); + + tempstr.Copy(iImapSettings.FolderPath()); + path->Des().Insert(0, tempstr); + + CleanupStack::PopAndDestroy(&tempstr); + } + + // Pop it off cleanup stack + CleanupStack::Pop(path); + + // Return the path + return(path); + } + +void CImapFolder::CompleteSelf() + { +// Complete self. + TRequestStatus* status = &iStatus; + iStatus = KRequestPending; + User::RequestComplete(status, KErrNone); + } + +/** +Returns updated progress information on outstanding synchronisation operations. +*/ +EXPORT_C void CImapFolder::Progress(TImap4SyncProgress& aProgress) + { + //copy values from member progress ob into aProgress + aProgress.iMsgsDone=iMsgsDone; + aProgress.iMsgsToDo=iMsgsToDo; + + aProgress.iHeadersFetched = iHeadersFetched; + aProgress.iOrphanedMessages = iOrphanedMessages; + aProgress.iRemoteMessagesDeleteTagged = iRemoteMessagesDeleteTagged; + + if(iImapSettings.SearchString().Length() != 0) + { + aProgress.iMsgsToDo=iMailboxSize; + } + else + { + aProgress.iMsgsToDo=(iSyncLimit<=0)?iMailboxSize:Min(iMailboxSize,iSyncLimit); + } + aProgress.iMsgsDone = Min(iMsgsDone,aProgress.iMsgsToDo); + + } + +/** +Sets various flags in the message entry by doing a quick scan of the +bodystructure. This routine is intended to be relatively straightforward +so as not to impact performance during the sync phase too much. + +@param aEntry Entry to set flags for +@param aBodyStructure Message body structure +*/ +void CImapFolder::SetMessageFlagsL(TMsvEmailEntry& aEntry, CImapBodyStructure* aBodyStructure) + { + TBool hasAttachments(EFalse); + TBool hasHtml(EFalse); + TBool afterRelated(EFalse); + TBool afterAlternative(EFalse); + TBool htmlAfterAltRel(EFalse); + TBool hasICalendar(EFalse); + TBool hasVCalendar(EFalse); + TInt size(0); + + // Check if top level of message contains attachment type + if ((aBodyStructure->BodyStructureType() == CImapBodyStructure::ETypeBasic) && + ((aBodyStructure->Type().CompareF(KImapTxtImage) == 0) || + (aBodyStructure->Type().CompareF(KImapTxtAudio) == 0) || + (aBodyStructure->Type().CompareF(KImapTxtVideo) == 0) || + (aBodyStructure->Type().CompareF(KImapTxtApplication) == 0))) + { + hasAttachments = ETrue; + } + + RPointerArray bodyStructureStack; + bodyStructureStack.AppendL(aBodyStructure); + + TInt count; + + // A body structure stack is maintained. This avoids the use of recursion + // when processing the embedded body structure list at each level, and also + // maintains the order of processing that the old IMAP implementation used. + // As nobody could explain exactly why the old code used this order, it was + // felt that it was safer that the new implementation matched the old. + while (bodyStructureStack.Count() > 0) + { + GetFlagsForBodyStructurePart(bodyStructureStack[0], hasAttachments, hasHtml, afterRelated, afterAlternative, htmlAfterAltRel, hasICalendar, hasVCalendar, size); + + for (count = 0; count < bodyStructureStack[0]->EmbeddedBodyStructureList().Count(); ++count) + { + bodyStructureStack.AppendL(bodyStructureStack[0]->EmbeddedBodyStructureList()[count]); + } + + bodyStructureStack.Remove(0); + } + + aEntry.SetAttachment(hasAttachments); + aEntry.SetMHTMLEmail(hasHtml || htmlAfterAltRel); + aEntry.SetICalendar(hasICalendar); + aEntry.SetVCalendar(hasVCalendar); + aEntry.iSize = size; + bodyStructureStack.Reset(); + } + +/** +Gets a set of flags for a body structure part + +@param aBodyStructure Body structure part +@param aHasAttachments Flag to indicate if message contains attachments +@param aHasHtml Flag to indicate if message has HTML part +@param aAfterRelated Flag to indicate a multipart/related part has been found +@param aAfterAlternative Flag to indicate a multipart/alternative part has been found +@param aHtmlAfterAltRel Flag to indicate a HTML part has been found after a multipart/related or multipart/alternative part +@param aHasICalendar Flag to indicate message contains ICalendar +@param aHasVCalendar Flag to indicate message contains VCalendar +@param aSize Running total of size of message +*/ +void CImapFolder::GetFlagsForBodyStructurePart(CImapBodyStructure* aBodyStructure, + TBool& aHasAttachments, TBool& aHasHtml, + TBool& aAfterRelated, TBool& aAfterAlternative, + TBool& aHtmlAfterAltRel, TBool& aHasICalendar, + TBool& aHasVCalendar, TInt& aSize) + { + switch (aBodyStructure->BodyStructureType()) + { + case CImapBodyStructure::ETypeMultipart: + { + if (aBodyStructure->SubType().CompareF(KImapTxtRelated) == 0) + { + aAfterRelated = ETrue; + } + else if (aBodyStructure->SubType().CompareF(KImapTxtAlternative) == 0) + { + aAfterAlternative = ETrue; + } + else if (aBodyStructure->SubType().CompareF(KImapTxtMixed) == 0) + { + aHasAttachments = ETrue; + } + + break; + } + + case CImapBodyStructure::ETypeText: + { + if (aBodyStructure->SubType().CompareF(KImapTxtHtml) == 0) + { + if (aBodyStructure->ExtDispositionName().CompareF(KImapTxtAttachment) != 0) + { + aHasHtml = ETrue; + } + + if (aAfterRelated || aAfterAlternative) + { + aHtmlAfterAltRel = ETrue; + } + } + else if (aBodyStructure->SubType().CompareF(KImapTxtCalendar) == 0) + { + aHasICalendar = ETrue; + } + else if (aBodyStructure->SubType().CompareF(KImapTxtXVCalendar) == 0) + { + aHasVCalendar = ETrue; + } + + break; + } + + case CImapBodyStructure::ETypeMessageRfc822: + { + return; + } + + default: + { + if (aBodyStructure->SubType().CompareF(KImapTxtDeliveryStatus) == 0) + { + aHasAttachments = ETrue; + } + + break; + } + } + + // Add size of this body part to the running total + TInt size(0); + TLex8 lex(aBodyStructure->BodySizeOctets()); + lex.Val(size); + + // For Base64, use the pre encoding data size + if (aBodyStructure->BodyEncoding().CompareF(KImapTxtBase64) == 0) + { + size = (size * 3) / 4; + } + + aSize += size; + } + +/** +Checks if we need to clear the new flags on the messages in the folder, +and if so clears them. +@pre Current selection (iSelection) contains all the messages in the folder. +*/ +void CImapFolder::ClearNewFlagsIfRequiredL() + { + if (iSyncManager.EntryIsInbox(iServerEntry.Entry())) + { + if (iSyncManager.InboxClearNewFlags()) + { + __LOG_TEXT(iSavedSession->LogId(), "CImapFolder: Clearing new flags (inbox)"); + + // Change attributes on the current selection + User::LeaveIfError(iServerEntry.ChangeAttributes(*iSelection, 0, KMsvNewAttribute)); + + // Set the flag to False to indicate that we have cleared the flags + // on the inbox, and so any subsequent synchronise of the inbox + // will not clear them again. + iSyncManager.ResetInboxClearNewFlags(); + } + } + else + { + if (iSyncManager.NonInboxClearNewFlags()) + { + __LOG_TEXT(iSavedSession->LogId(), "CImapFolder: Clearing new flags (non inbox)"); + + // Change attributes on the current selection + User::LeaveIfError(iServerEntry.ChangeAttributes(*iSelection, 0, KMsvNewAttribute)); + + // Note that we do not clear the flag here as it will be required + // for any subsequent non inbox folders that are yet to be synced. + } + } + } + +/** +Sets the folder matched flag + +@param aFolderMatched Value to set flag to +*/ +void CImapFolder::SetFolderMatched(TBool aFolderMatched) + { + iFolderMatched = aFolderMatched; + } + +/** +Gets the folder matched flag + +@return Folder matched flag value +*/ +TBool CImapFolder::FolderMatched() + { + return iFolderMatched; + } + +/** +Performs comparison between two folders by comparing their folder names. + +@param aFirst The first folder to compare +@param aSecond The second folder to compare + +@return The result of calling Compare on the folder names +*/ +TInt CImapFolder::CompareByFolderName(const CImapFolder& aFirst, const CImapFolder& aSecond) +// static method + { + return aFirst.iFullFolderPath.Compare(aSecond.iFullFolderPath); + }