--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/email/pop3andsmtpmtm/imapservermtm/src/FLDSYNC.CPP Mon May 03 12:29:07 2010 +0300
@@ -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 <e32base.h>
+#include <e32cons.h>
+#include <mentact.h>
+#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;a<KFolderDepth;a++)
+ delete iFolderIds[a];
+
+ // Delete local folder list
+ delete iLocalFolders;
+ }
+
+CImImap4FolderSync* CImImap4FolderSync::NewLC(CImImap4Session *aSession)
+ {
+ CImImap4FolderSync* self=new (ELeave) CImImap4FolderSync();
+ CleanupStack::PushL(self);
+
+ // Non-trivial constructor
+ self->ConstructL(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<CImImap4DirStruct>(8);
+ iFolderContents=new (ELeave) CMsvEntrySelection;
+
+ // ...and per-level bits
+ for(TInt a=0;a<KFolderDepth;a++)
+ iFolderIds[a]=new (ELeave) CArrayFixFlat<TMsvId>(8);
+
+ // Local folder list
+ iLocalFolders=new (ELeave) CArrayFixFlat<TMsvId>(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;child<children->Count();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;a<iLocalFolders->Count();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;a<children->Count();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;replyentry<noofreplies;replyentry++)
+ {
+ // Does this already exist?
+ TInt a;
+ TMsvId thisentry;
+ for(a=0;a<noofchildren;a++)
+ {
+ // See if the details field matches the server folder name.
+ // For the INBOX folder the match should be case insensitive, all other folders
+ // we must perform a case sensitive match
+ SetEntryL(thisentry=(*iFolderContents)[a]);
+ TPtrC entryName = iEntry->Entry().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;b<iLocalFolders->Count();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;
+ }