diff -r 6a20128ce557 -r ebfee66fde93 email/pop3andsmtpmtm/imapservermtm/src/FLDSYNC.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/pop3andsmtpmtm/imapservermtm/src/FLDSYNC.CPP Fri Jun 04 10:25:39 2010 +0100 @@ -0,0 +1,514 @@ +// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// IMAP4 folder list synchronise +// This class deals with keeping the local mirror folder tree in sync with the +// remote server's directory structure. +// 980914 - Modifications to local tree traverse so that it will not +// look for children below message level: previously, it was +// picking up attachment folders in messages! +// 990304 - Orphaned folder check no longer orphans the inbox: we +// basically never touch it. The problem with orphaning it +// comes about with servers which have a namespace (ie +// folder path) of INBOX, which means we never "see" the +// actual inbox as we are inside it. We still know it +// exists, though. +// 990502 - Was calling CMsgActive::Cancel() as opposed to DoCancel(). +// +// + +#include +#include +#include +#include "fldsync.h" +#include "impspan.h" +#include "imapsess.h" + +#ifdef _DEBUG +#define LOG_COMMANDS(a) a +#define DBG(a) a +#define PRINTING +#else +#define LOG_COMMANDS(a) +#define DBG(a) +#undef PRINTING +#endif + +// Priority of MsgActive object +const TInt EFolderSyncPriority=1; + + +// Debugging of folder tree traversal +#undef DEBUG_FOLDERSYNC + +CImImap4FolderSync::CImImap4FolderSync():CMsgActive(EFolderSyncPriority) + { + __DECLARE_NAME(_S("CImImap4FolderSync")); + } + +CImImap4FolderSync::~CImImap4FolderSync() + { + // Global bits + delete iFolderContents; + + if (iFolderList) + iFolderList->ResetAndDestroy(); + delete iFolderList; + + // Get rid of bits at each level + for(TInt a=0;aConstructL(aSession); + return self; + } + +CImImap4FolderSync* CImImap4FolderSync::NewL(CImImap4Session *aSession) + { + CImImap4FolderSync* self=NewLC(aSession); + CleanupStack::Pop(); + return self; + } + +// The non-trivial constructor +void CImImap4FolderSync::ConstructL(CImImap4Session *aSession) + { + // Save session + iSession=aSession; + + // We're an active object... + CActiveScheduler::Add(this); + + // One-off bits + iFolderList=new (ELeave) CArrayPtrFlat(8); + iFolderContents=new (ELeave) CMsvEntrySelection; + + // ...and per-level bits + for(TInt a=0;a(8); + + // Local folder list + iLocalFolders=new (ELeave) CArrayFixFlat(16); + } + +// Do setentry, leave if there is an error +void CImImap4FolderSync::SetEntryL(const TMsvId aId) + { + User::LeaveIfError(iEntry->SetEntry(aId)); + } + +// Change entry, leave if error +void CImImap4FolderSync::ChangeEntryL(const TMsvEntry& aEntry) + { + User::LeaveIfError(iEntry->ChangeEntry(aEntry)); + } + +// Change entry in bulk mode (i.e. no index file commit), leave if error +void CImImap4FolderSync::ChangeEntryBulkL(const TMsvEntry& aEntry) + { + User::LeaveIfError(iEntry->ChangeEntryBulk(aEntry)); + } +// Get children, leave if error +void CImImap4FolderSync::GetChildrenL(CMsvEntrySelection& aSelection) + { + User::LeaveIfError(iEntry->GetChildren(aSelection)); + } + +// Set the entry to use to talk to the server +void CImImap4FolderSync::SetEntry(CMsvServerEntry* aEntry) + { + // Save it + iEntry=aEntry; + } + +// Build list of local folders +void CImImap4FolderSync::BuildLocalL(const TMsvId aFolder, const TBool aDoThisOne) + { + // Select it + SetEntryL(aFolder); + TMsvEmailEntry entry=iEntry->Entry(); + + // If we're a folder, set the flag + if (entry.iType==KUidMsvFolderEntry && aDoThisOne) + { + // Add to list + iLocalFolders->AppendL(iEntry->Entry().Id()); + } + + // If current entry is a message, don't look for children + if (entry.iType==KUidMsvMessageEntry) + return; + + // Any children? + CMsvEntrySelection *children=new (ELeave) CMsvEntrySelection; + CleanupStack::PushL(children); + GetChildrenL(*children); + if (children->Count()) + { + // Do each in turn + for(TInt child=0;childCount();child++) + BuildLocalL((*children)[child],ETrue); + } + CleanupStack::PopAndDestroy(); + } + +// Called when async child completes +void CImImap4FolderSync::DoRunL() + { + if (ProcessDirListL()) + { + // Done whole list, check to see what has vanished on the + // server with regard to what's in the mirror: basically, + // see what's still flagged in the tree. + OrphanedFolderCheckL(); + + // All done! + Complete(KErrNone); + } + else + { + // Otherwise, another command has been issued + SetActive(); + } + } + +// Cancel this operation +void CImImap4FolderSync::DoCancel() + { + DBG((iSession->LogText(_L8("CImImap4FolderSync::DoCancel()")))); + + // Cancel any outstanding session operation + iSession->Cancel(); + + // ...and parent + CMsgActive::DoCancel(); + } + +// Check for orphaned folders +void CImImap4FolderSync::OrphanedFolderCheckL() + { + DBG((iSession->LogText(_L8("CImImap4FolderSync::OrphanedFolderCheckL()")))); + + // Set stats + iOrphanedFolders=iLocalFolders->Count(); + + // Selection for storing children + CMsvEntrySelection *children=new CMsvEntrySelection; + CleanupStack::PushL(children); + + // Any folders left in iLocalFolders are orphans + for(TInt a=0;aCount();a++) + { + // Go to this folder + if (iEntry->SetEntry((*iLocalFolders)[a])!=KErrNone) + { + // Probably been removed already (nested?) + continue; + } + TMsvEmailEntry entry=iEntry->Entry(); + + // Is it the INBOX? If so, ignore it + if (entry.Parent()==iServiceId && + entry.iDetails.CompareF(KIMAP_INBOX)==0) + continue; + + // Check all the entries in the folder + TInt subfolders=0; + children->Reset(); + GetChildrenL(*children); + if (children->Count()) + { + // Check each message + for(TInt a=0;aCount();a++) + { + SetEntryL((*children)[a]); + TMsvEmailEntry child=iEntry->Entry(); + if (child.iType==KUidMsvMessageEntry) + { + // Orphan this message: it may delete it, if there + // are no downloaded bodyparts + + DBG((iSession->LogText(_L8("Orphaning message %x"),(*children)[a]))); + + iSession->OrphanMessageL((*children)[a]); + } + else if (child.iType==KUidMsvFolderEntry) + { + // There's a subfolder + subfolders++; + } + } + } + + // Check number of children again: if all the orphaned children + // have been deleted (ie, they had no downloaded body parts) + // then we can delete the folder locally, otherwise we just have + // to mark it as orphaned. + SetEntryL((*iLocalFolders)[a]); + GetChildrenL(*children); + if ((children->Count() || subfolders>0) && !entry.Orphan()) + { + + DBG((iSession->LogText(_L8("Orphaning folder %x"),(*iLocalFolders)[a]))); + + // Orphan folder + TMsvEmailEntry message=iEntry->Entry(); + message.SetOrphan(ETrue); + ChangeEntryL(message); + } + else + { + + DBG((iSession->LogText(_L8("Deleting folder %x"),(*iLocalFolders)[a]))); + + // Delete folder + SetEntryL(iEntry->Entry().Parent()); + iEntry->DeleteEntry((*iLocalFolders)[a]); + } + } + + // Get rid of children list + CleanupStack::PopAndDestroy(); + + // Remove list + iLocalFolders->Reset(); + + DBG((iSession->LogText(_L8("CImImap4FolderSync::OrphanedFolderCheckL() done")))); + } + +// Process returned directory list +TBool CImImap4FolderSync::ProcessDirListL() + { + // We've got a list, and the mirror folder ID that the contents belong to: + // process it, and mirror the folder structure + + // First, find the existing children of this folder from the message server + SetEntryL(iFolderId); + GetChildrenL(*iFolderContents); + TInt noofchildren=iFolderContents->Count(); + + // Number of items in reply from server + TInt noofreplies=iFolderList->Count(); + +#ifdef DEBUG_FOLDERSYNC + iSession->LogText(_L("Level %d: Server has %d entries, mirror has %d"), + iFolderLevel,noofreplies,noofchildren); +#endif + + // Empty the 'to do' array + iFolderIds[iFolderLevel]->Reset(); + + // ...for each entry in reply + for(TInt replyentry=0;replyentryEntry().iDetails; + TPtrC svrFolderName = (*iFolderList)[replyentry]->Leafname(); + TBool foldersMatch = EFalse; + if(svrFolderName.CompareF(KIMAP_INBOX)==0) + { + // Server folder is the INBOX + foldersMatch = (entryName.CompareF(svrFolderName)==0); + } + else + { + // Server folder is not the INBOX + foldersMatch = (entryName.Compare(svrFolderName)==0); + } + + if (foldersMatch) + { + // Update mailbox flag as necessary, but NOT if it's the inbox, which is + // *ALWAYS* a mailbox + TMsvEmailEntry folder=iEntry->Entry(); + if ((*iFolderList)[replyentry]->iIsMailbox!=folder.Mailbox()) + { + if (folder.Parent()!=iServiceId || + folder.iDetails.CompareF(KIMAP_INBOX)!=0) + { + folder.SetMailbox((*iFolderList)[replyentry]->iIsMailbox); + ChangeEntryBulkL(folder); + } + else + { + DBG((iSession->LogText(_L8("Skipping Mailbox flag twiddle for folder %x (=%d)"), + folder.Id(),(*iFolderList)[replyentry]->iIsMailbox))); + } + } + + // Exists: remove from local folders list + for(TInt b=0;bCount();b++) + { + // Found it? + if ((*iLocalFolders)[b]==thisentry) + { + // Remove from list + iLocalFolders->Delete(b,1); + break; + } + } + + // Exit loop + break; + } + } + + // Found a match? + if (a==noofchildren) + { + // No, create it + TMsvEmailEntry message; + + // All flags unset (urrgh!) + message.SetMtmData1(0); + message.SetMtmData2(0); + message.SetMtmData3(0); + + message.iType=KUidMsvFolderEntry; + message.iMtm=iEntry->Entry().iMtm; + message.iServiceId=iServiceId; + message.iSize=0; + message.iDetails.Set((*iFolderList)[replyentry]->Leafname()); + message.SetValidUID(EFalse); + message.SetComplete(ETrue); + + // Visibility + if (iNewFoldersAreInvisible) + message.SetVisible(EFalse); + + // Is a mailbox? (basically, is it selectable?). + message.SetMailbox((*iFolderList)[replyentry]->iIsMailbox); + +#ifdef DEBUG_FOLDERSYNC + iSession->LogText(_L("Creating folder '%S'"),&message.iDetails); +#endif + SetEntryL(iFolderId); + User::LeaveIfError(iEntry->CreateEntryBulk(message)); + + // Increment stats + iNewFolders++; + + // Get the ID, incase we're going into this level + thisentry=message.Id(); + } + + // Does this have children? (and are we at the end of our nesting levels?) + if ((*iFolderList)[replyentry]->iIsFolder && iFolderLevel<(KFolderDepth-1)) + { +#ifdef DEBUG_FOLDERSYNC + iSession->LogText(_L("Adding folder to todolist, level %d"),iFolderLevel); +#endif + // Add it to the list to scan at this level + iFolderIds[iFolderLevel]->AppendL(thisentry); + } + } + +#ifdef DEBUG_FOLDERSYNC + iSession->LogText(_L("Level %d: %d items in todolist"),iFolderLevel,iFolderIds[iFolderLevel]->Count()); +#endif + + // Anything in our 'to do' queue? + while(!iFolderIds[iFolderLevel]->Count()) + { +#ifdef DEBUG_FOLDERSYNC + iSession->LogText(_L("Nothing to do at level %d"),iFolderLevel); +#endif + // Nothing at this level: are we at the top? + if (iFolderLevel==0) + { +#ifdef DEBUG_FOLDERSYNC + iSession->LogText(_L("At level 0: completing")); +#endif + // All done! + // Commit any outstanding bulk creates + SetEntryL(iFolderId); + iEntry->CompleteBulk(); + return(ETrue); + } + +#ifdef DEBUG_FOLDERSYNC + iSession->LogText(_L("Going up a level")); +#endif + // Not at the top: back up a level + iFolderLevel--; + } + + // Something to process: remove it from todo list + iFolderId=(*iFolderIds[iFolderLevel])[0]; + iFolderIds[iFolderLevel]->Delete(0,1); + + // Nest & issue command + iFolderLevel++; + iSession->ListL(iStatus,iFolderId,iFolderList); + +#ifdef DEBUG_FOLDERSYNC + iSession->LogText(_L("Stuff to do at level %d - scanning dir %x"),iFolderLevel,iFolderId); +#endif + return(EFalse); + } + +// Synchronise mirror tree with remote server +void CImImap4FolderSync::SynchroniseTreeL(TRequestStatus& aStatus, const TMsvId aService, const TBool aNewFoldersAreInvisible) + { + LOG_COMMANDS((iSession->LogText(_L8("COMMAND CImImap4FolderSync::SychroniseTree (serviceid=%x)"),aService))); + + Queue(aStatus); + + // Note where this list came from + iServiceId=aService; + iFolderId=iServiceId; + iFolderLevel=0; + + // Save invisibility state + iNewFoldersAreInvisible=aNewFoldersAreInvisible; + + // Set flags on all folders, so we can tell which ones aren't matched by the + // list returned from the server. + iLocalFolders->Reset(); + BuildLocalL(iServiceId,EFalse); + + // List this tree + iSession->ListL(iStatus,iFolderId,iFolderList); + SetActive(); + } + +void CImImap4FolderSync::IncProgress(TImap4SyncProgress& aProgress) + { + aProgress.iNewFolders+=iNewFolders; + aProgress.iOrphanedFolders+=iOrphanedFolders; + } + +void CImImap4FolderSync::ResetStats() + { + // initialise counts + iNewFolders=0; + iOrphanedFolders=0; + }