--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/email/imap4mtm/imapsyncmanager/src/cimapopsyncfoldertree.cpp Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,641 @@
+// Copyright (c) 1999-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 "cimapopsyncfoldertree.h"
+
+#include "cimapsettings.h"
+#include "cimapsession.h"
+#include "cimapfolder.h"
+#include "cimaplogger.h"
+
+_LIT(KImapInbox, "INBOX");
+
+/**
+Factory constructor
+
+@param aEntry Server entry
+@param aSession IMAP session
+@param aSyncMan Sync manager
+@param aImapSettings IMAP settings
+
+@return Instance of class created on heap
+*/
+CImapOpSyncFolderTree* CImapOpSyncFolderTree::NewL(CMsvServerEntry& aEntry, CImapSession& aSession, CImapSyncManager& aSyncMan, CImapSettings& aImapSettings)
+ {
+ CImapOpSyncFolderTree* self=NewLC(aEntry, aSession, aSyncMan, aImapSettings);
+ CleanupStack::Pop();
+
+ return self;
+ }
+
+/**
+Factory constructor
+
+@param aEntry Server entry
+@param aSession IMAP session
+@param aSyncMan Sync manager
+@param aImapSettings IMAP settings
+
+@return Instance of class created on heap
+*/
+CImapOpSyncFolderTree* CImapOpSyncFolderTree::NewLC(CMsvServerEntry& aEntry, CImapSession& aSession, CImapSyncManager& aSyncMan, CImapSettings& aImapSettings)
+ {
+ CImapOpSyncFolderTree* self=new (ELeave) CImapOpSyncFolderTree(aEntry, aSession, aSyncMan, aImapSettings);
+ CleanupStack::PushL(self);
+
+ // Non-trivial constructor
+ self->ConstructL();
+
+ return self;
+ }
+
+/**
+Constructor
+*/
+CImapOpSyncFolderTree::CImapOpSyncFolderTree(CMsvServerEntry& aEntry, CImapSession& aSession, CImapSyncManager& aSyncMan, CImapSettings& aImapSettings)
+: CImapSyncOperation(aEntry, aSession, aSyncMan, aImapSettings, EPriorityLow)
+ {
+ }
+
+/**
+Second phase constructor
+*/
+void CImapOpSyncFolderTree::ConstructL()
+ {
+ // Create some paths that are used in folder name pattern matching
+
+ // Create a path constisting of the inbox followed by the hierarchy
+ // separator
+ iInboxPathWithSeparator = HBufC::NewL(KImapInbox().Length() + iImapSettings.PathSeparator().Length());
+ TPtr inboxPathPtr = iInboxPathWithSeparator->Des();
+ inboxPathPtr.Copy(KImapInbox());
+ inboxPathPtr.Append(iImapSettings.PathSeparator());
+
+ // If the settings folder path is defined, create one path consisting of
+ // the folder path followed by the hierarchy separator, and one path
+ // consisting of the folder path only.
+ if (iImapSettings.FolderPath().Length() > 0)
+ {
+ iFolderPathWithSeparator = HBufC::NewL(iImapSettings.FolderPath().Length() + iImapSettings.PathSeparator().Length());
+ TPtr folderPathPtr = iFolderPathWithSeparator->Des();
+ folderPathPtr.Copy(iImapSettings.FolderPath());
+
+ iFolderPath.Set(folderPathPtr);
+
+ folderPathPtr.Append(iImapSettings.PathSeparator());
+ }
+
+ // We're an active object...
+ CActiveScheduler::Add(this);
+ }
+
+/**
+Destructor
+*/
+CImapOpSyncFolderTree::~CImapOpSyncFolderTree()
+ {
+ Cancel();
+
+ iImapListFolderInfo.ResetAndDestroy();
+ iFolderList.ResetAndDestroy();
+
+ delete iSelection;
+ delete iSearchFolder;
+ delete iInboxPathWithSeparator;
+ delete iFolderPathWithSeparator;
+ }
+
+/**
+Start tree synchronisation
+
+@param aStatus Caller's request status
+*/
+void CImapOpSyncFolderTree::SynchroniseTreeL(TRequestStatus& aStatus)
+ {
+ __LOG_TEXT(iSession.LogId(), "CImapOpSyncFolderTree::SynchroniseTreeL");
+ StartBuildingLocalFolderListL();
+ Queue(aStatus);
+ }
+
+/**
+Called when async child completes with >=KErrNone
+*/
+void CImapOpSyncFolderTree::DoRunL()
+ {
+ // Finish if any server errors.
+ if (iStatus.Int()!=KErrNone)
+ {
+ Complete(iStatus.Int());
+ return ;
+ }
+
+ switch (iState)
+ {
+ case EStateBuildingLocalFolderList:
+ {
+ ProcessNextFolderInLocalFolderListL();
+ break;
+ }
+
+ case EStateFetchingFolderList:
+ {
+ StartProcessingReceivedFolderListL();
+ break;
+ }
+
+ case EStateProcessingReceivedFolders:
+ {
+ ProcessNextFolderInReceivedFolderListL();
+ break;
+ }
+
+ case EStateIdle:
+ default:
+ {
+ __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EUnexpectedStateSyncFolderTreeDoRunL));
+ break;
+ }
+ }
+ }
+
+/**
+Cancels any outstanding asynchronous service requests.
+*/
+void CImapOpSyncFolderTree::DoCancel()
+ {
+ __LOG_TEXT(iSession.LogId(), "CImapOpSyncFolderTree::DoCancel");
+
+ switch (iState)
+ {
+ case EStateBuildingLocalFolderList:
+ case EStateProcessingReceivedFolders:
+ {
+ // Self completing states
+ break;
+ }
+
+ case EStateFetchingFolderList:
+ {
+ iSession.Cancel();
+ break;
+ }
+
+ case EStateIdle:
+ default:
+ {
+ __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EUnexpectedStateSyncFolderTreeDoCancel));
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrCancel);
+ break;
+ }
+ }
+
+ CMsgActive::DoCancel();
+ }
+
+/**
+Called when completing a client request
+
+@param aStatus Completion code
+*/
+#ifdef __IMAP_LOGGING
+void CImapOpSyncFolderTree::DoComplete(TInt& aStatus)
+#else //__IMAP_LOGGING
+void CImapOpSyncFolderTree::DoComplete(TInt& /*aStatus*/)
+#endif //__IMAP_LOGGING
+ {
+ __LOG_FORMAT((iSession.LogId(), "CImapOpSyncFolderTree::DoComplete - %d", aStatus));
+
+ iImapListFolderInfo.ResetAndDestroy();
+ iFolderList.ResetAndDestroy();
+
+ delete iSelection;
+ iSelection = NULL;
+
+ delete iSearchFolder;
+ iSearchFolder = NULL;
+
+ delete iInboxPathWithSeparator;
+ iInboxPathWithSeparator = NULL;
+
+ delete iFolderPathWithSeparator;
+ iFolderPathWithSeparator = NULL;
+
+ iState = EStateIdle;
+ }
+
+/**
+Starts building a list of folders for this service that are in the
+message store
+*/
+void CImapOpSyncFolderTree::StartBuildingLocalFolderListL()
+ {
+ __LOG_TEXT(iSession.LogId(), "CImapOpSyncFolderTree::StartBuildingLocalFolderListL");
+
+ iState = EStateBuildingLocalFolderList;
+
+ iFolderList.ResetAndDestroy();
+ iCurrentFolder = 0;
+
+ User::LeaveIfError(iServerEntry.SetEntry(iImapSettings.ServiceId()));
+
+ iSelection = new (ELeave) CMsvEntrySelection;
+
+ // Get all the folders directly under the service entry
+ User::LeaveIfError(iServerEntry.GetChildrenWithType(KUidMsvFolderEntry, *iSelection));
+
+ // If the inbox is in the selection, we want to remove it from the selction and
+ // specifically add it to the folder list. This ensures that we use the uppercase
+ // spelling of inbox and helps in the folder matching that is done against the list
+ // of folders we receive from the server.
+ for (TInt selLoop = 0; selLoop < iSelection->Count(); ++selLoop)
+ {
+ User::LeaveIfError(iServerEntry.SetEntry((*iSelection)[selLoop]));
+
+ TMsvEmailEntry entry = iServerEntry.Entry();
+ if (entry.iDetails.CompareF(KImapInbox) == 0)
+ {
+ iSelection->Delete(selLoop);
+
+ CImapFolder* tempFolder = CImapFolder::NewLC(iSyncMan, iServerEntry, entry, iImapSettings, KImapInbox());
+ tempFolder->SetFolderMatched(ETrue);
+ iFolderList.AppendL(tempFolder);
+ CleanupStack::Pop(tempFolder);
+ break;
+ }
+ }
+
+ // If the settings has a defined folder path, then append this at the start
+ // of each new folder path. Where the settings has no folder path defined,
+ // iFolderPath will just be an empty pointer.
+ iCurrentPath.Set(iFolderPath);
+
+ // We can now self complete to keep the state machine running. This will
+ // cause the current selection of folders to be processed
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrNone);
+ SetActive();
+ }
+
+/**
+Process the current selection of folders.
+For each entry in the selection, add a new folder in the folder list. Then
+take the first entry in the folder list and create a selection of all its
+children folders. This process is then repeated until all the folders under
+the service have been found.
+*/
+void CImapOpSyncFolderTree::ProcessNextFolderInLocalFolderListL()
+ {
+ __LOG_TEXT(iSession.LogId(), "CImapOpSyncFolderTree::ProcessNextFolderInLocalFolderListL");
+
+ HBufC* newPath = NULL;
+ TInt curPathLen = iCurrentPath.Length();
+
+ // Loop through every entry in the selection. Each one is a folder.
+ for (TInt selLoop = 0; selLoop < iSelection->Count(); ++selLoop)
+ {
+ User::LeaveIfError(iServerEntry.SetEntry((*iSelection)[selLoop]));
+
+ TMsvEmailEntry entry = iServerEntry.Entry();
+
+ // Create the full path name for this folder. This will be the path
+ // name passed in (which is the path name of the parent) with the
+ // folder name from the message store entry appended.
+ newPath = HBufC::NewLC(curPathLen + 1 + entry.iDetails.Length());
+ newPath->Des().Copy(iCurrentPath);
+ if (curPathLen > 0)
+ {
+ newPath->Des().Append(iImapSettings.PathSeparator());
+ }
+ newPath->Des().Append(entry.iDetails);
+
+#ifdef __IMAP_LOGGING
+ HBufC8* tempBuf = HBufC8::NewL(newPath->Length());
+ TPtr8 tempBufPtr = tempBuf->Des();
+ tempBufPtr.Copy(*newPath);
+ __LOG_FORMAT((iSession.LogId(), "Adding Local Folder: - %S", &tempBufPtr));
+ delete tempBuf;
+#endif
+
+ // Create new IMAP folder and insert into the list.
+ CImapFolder* tempFolder = CImapFolder::NewLC(iSyncMan, iServerEntry, entry, iImapSettings, *newPath);
+ iFolderList.AppendL(tempFolder);
+ CleanupStack::Pop(tempFolder);
+ CleanupStack::PopAndDestroy(newPath);
+ newPath = NULL;
+ }
+
+ if (iCurrentFolder < iFolderList.Count())
+ {
+ iCurrentPath.Set(iFolderList[iCurrentFolder]->FullFolderPathL());
+ iSelection->Reset();
+
+ User::LeaveIfError(iServerEntry.SetEntry(iFolderList[iCurrentFolder]->MailboxId()));
+ User::LeaveIfError(iServerEntry.GetChildrenWithType(KUidMsvFolderEntry, *iSelection));
+
+ ++iCurrentFolder;
+
+ // Self complete to go around again
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrNone);
+ SetActive();
+ }
+ else
+ {
+ // We have created the folder list. Need to sort it into alphabetical
+ // order.
+ TLinearOrder<CImapFolder> sorter(CImapFolder::CompareByFolderName);
+ iFolderList.Sort(sorter);
+
+ delete iSelection;
+ iSelection = NULL;
+
+ // Now get the list of folders from the server
+ SendListRequestL();
+ }
+ }
+
+/**
+Sends the request to the server to get the list of folders
+*/
+void CImapOpSyncFolderTree::SendListRequestL()
+ {
+ __LOG_TEXT(iSession.LogId(), "CImapOpSyncFolderTree::SendListRequestL");
+
+ iState = EStateFetchingFolderList;
+
+ iSession.ListL(iStatus, KNullDesC, KStarChar(), iImapListFolderInfo);
+ SetActive();
+ }
+
+/**
+Starts processing the received list of folders on the server
+*/
+void CImapOpSyncFolderTree::StartProcessingReceivedFolderListL()
+ {
+ __LOG_TEXT(iSession.LogId(), "CImapOpSyncFolderTree::StartProcessingReceivedFolderListL");
+
+ iState = EStateProcessingReceivedFolders;
+
+ iCurrentFolder = 0;
+
+ // Create a temporary folder which will be used for subsequent searches
+ User::LeaveIfError(iServerEntry.SetEntry(iImapSettings.ServiceId()));
+ TMsvEmailEntry entry = iServerEntry.Entry();
+ iSearchFolder = CImapFolder::NewL(iSyncMan, iServerEntry, entry, iImapSettings, KNullDesC());
+
+ // Self complete to get the process moving
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrNone);
+ SetActive();
+ }
+
+/**
+Process next folder from the received folder list.
+Check if we know about the folder already or not. If we don't know about
+it, then we need to add a new folder.
+*/
+void CImapOpSyncFolderTree::ProcessNextFolderInReceivedFolderListL()
+ {
+ __LOG_TEXT(iSession.LogId(), "CImapOpSyncFolderTree::ProcessNextFolderInReceivedFolderListL");
+
+ if (iCurrentFolder < iImapListFolderInfo.Count())
+ {
+ TPtrC currentFolderFullPath = iImapListFolderInfo[iCurrentFolder]->FolderName();
+
+#ifdef __IMAP_LOGGING
+ HBufC8* tempBuf = HBufC8::NewL(currentFolderFullPath.Length());
+ TPtr8 tempBufPtr = tempBuf->Des();
+ tempBufPtr.Copy(currentFolderFullPath);
+ __LOG_FORMAT((iSession.LogId(), "Processing received folder: - %S", &tempBufPtr));
+ delete tempBuf;
+#endif
+
+ if (currentFolderFullPath.Length() > 0)
+ {
+ TBool processFolder = ETrue;
+
+ // If the settings has a defined folder path, don't process this
+ // folder unless the first part of the folder name is the settings
+ // folder path or is the inbox.
+ if (iFolderPathWithSeparator)
+ {
+ // We don't need to do a case insensitive search for the inbox
+ // because the iInboxPathWithSeparator was created using uppercase,
+ // and the received folder list is change by the IMAP session to
+ // ensure that the inbox folder is converted to uppercase.
+ // Note also that this bit of code will result in the actual inbox
+ // folder being removed as it does not have a separator character
+ // on the end of it. This will not cause a problem because the
+ // inbox has already been specifically added to the list of local
+ // folders.
+ if ((iFolderPathWithSeparator->Compare(currentFolderFullPath.Left(iFolderPathWithSeparator->Length())) != 0) &&
+ (iInboxPathWithSeparator->Compare(currentFolderFullPath.Left(iInboxPathWithSeparator->Length())) != 0))
+ {
+ __LOG_TEXT(iSession.LogId(), "Ignoring non folder path folder");
+ processFolder = EFalse;
+ }
+ }
+
+ if (processFolder)
+ {
+ // Try to find the received folder in the local folder list
+ TLinearOrder<CImapFolder> sorter(CImapFolder::CompareByFolderName);
+ iSearchFolder->SetFullFolderPathL(currentFolderFullPath);
+ TInt pos = iFolderList.FindInOrder(iSearchFolder, sorter);
+
+ if (pos != KErrNotFound)
+ {
+ // We already know about the folder. Just set its flag to indicate
+ // we have matched it.
+ iFolderList[pos]->SetFolderMatched(ETrue);
+ __LOG_TEXT(iSession.LogId(), "Folder already known");
+ }
+ else
+ {
+ AddReceivedFolderL(iImapListFolderInfo[iCurrentFolder], currentFolderFullPath);
+ }
+ }
+ }
+
+ ++iCurrentFolder;
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrNone);
+ SetActive();
+ }
+ else
+ {
+ // All the folders have been done. We now need to remove any folders
+ // that we have in the message store, that are no longer on the server.
+ RemoveMissingFolders();
+
+ delete iSearchFolder;
+ iSearchFolder = NULL;
+
+ delete iInboxPathWithSeparator;
+ iInboxPathWithSeparator = NULL;
+
+ delete iFolderPathWithSeparator;
+ iFolderPathWithSeparator = NULL;
+ }
+ }
+
+/**
+Add a new folder that is on the server but which we don't have stored locally
+
+@param aCurrentFolderInfo Received folder info of new folder
+@param aCurrentFolderFullPath Full path of new folder
+*/
+void CImapOpSyncFolderTree::AddReceivedFolderL(CImapListFolderInfo* aCurrentFolderInfo, const TDesC& aCurrentFolderFullPath)
+ {
+ __LOG_TEXT(iSession.LogId(), "CImapOpSyncFolderTree::AddReceivedFolderL");
+
+ CImapFolder* parentFolder = NULL;
+ TBool rootFolder = EFalse;
+ TPtrC parentFolderFullPath = aCurrentFolderFullPath;
+ TPtrC folderName = aCurrentFolderFullPath;
+ TLinearOrder<CImapFolder> sorter(CImapFolder::CompareByFolderName);
+
+ // Try to split the folder path into the folder name and the path of
+ // the parent folder by searching backwards from the end for the
+ // hierarchy separator character
+ TInt sepPos = parentFolderFullPath.LocateReverseF(aCurrentFolderInfo->iHierarchySeperator);
+
+ if (sepPos != KErrNotFound)
+ {
+ // We found the hierarchy separator. Split the path into the folder name
+ // and the parent path
+ parentFolderFullPath.Set(parentFolderFullPath.Left(sepPos));
+ folderName.Set(folderName.Mid(sepPos + 1));
+
+#ifdef __IMAP_LOGGING
+ HBufC8* tempBuf = HBufC8::NewL(parentFolderFullPath.Length());
+ TPtr8 tempBufPtr = tempBuf->Des();
+ tempBufPtr.Copy(parentFolderFullPath);
+ HBufC8* tempBuf2 = HBufC8::NewL(folderName.Length());
+ TPtr8 tempBufPtr2 = tempBuf2->Des();
+ tempBufPtr2.Copy(folderName);
+ __LOG_FORMAT((iSession.LogId(), "Split path into %S and %S", &tempBufPtr, &tempBufPtr2));
+ delete tempBuf;
+ delete tempBuf2;
+#endif
+
+ // Check if this is directly under inbox
+ if (parentFolderFullPath.CompareF(KImapInbox) == 0)
+ {
+ __LOG_TEXT(iSession.LogId(), "Parent is the inbox");
+ parentFolder = iSyncMan.Inbox();
+ }
+ // Check if the parent path is the same as the settings folder path.
+ // If so this is a root folder
+ else if ((iFolderPath.Length() > 0) &&
+ (parentFolderFullPath.Compare(iFolderPath) == 0))
+ {
+ __LOG_TEXT(iSession.LogId(), "Parent is settings folder path");
+ rootFolder = ETrue;
+ }
+ else
+ {
+ // Search for the parent folder in the folder list.
+ // Note that the received list of folders from the server is in
+ // alphabetical order, so the parent folder will have already
+ // been created so we should be able to find it.
+ iSearchFolder->SetFullFolderPathL(parentFolderFullPath);
+ TLinearOrder<CImapFolder> sorter(CImapFolder::CompareByFolderName);
+ TInt parentPos = iFolderList.FindInOrder(iSearchFolder, sorter);
+
+ if (parentPos != KErrNotFound)
+ {
+ __LOG_FORMAT((iSession.LogId(), "Parent found at position %d", parentPos));
+ parentFolder = iFolderList[parentPos];
+ }
+ }
+ }
+ else
+ {
+ // No hierarchy separator so this must be a root folder
+ folderName.Set(aCurrentFolderFullPath);
+ rootFolder = ETrue;
+ }
+
+ if (rootFolder || parentFolder)
+ {
+ if (rootFolder)
+ {
+ User::LeaveIfError(iServerEntry.SetEntry(iImapSettings.ServiceId()));
+ }
+ else
+ {
+ User::LeaveIfError(iServerEntry.SetEntry(parentFolder->MailboxId()));
+ }
+
+ TMsvEmailEntry entry;
+ entry.iDetails.Set(folderName);
+ entry.iType = KUidMsvFolderEntry;
+ entry.iMtm = KUidMsgTypeIMAP4;
+ entry.iServiceId = iImapSettings.ServiceId();
+ entry.iSize = 0;
+ entry.SetUID(0);
+ entry.SetValidUID(EFalse);
+ entry.SetMailbox(!(aCurrentFolderInfo->QueryFlag(CImapListFolderInfo::ENoselect)));
+ entry.SetLocalSubscription(EFalse);
+ entry.SetComplete(ETrue);
+ entry.SetVisible(EFalse);
+
+ User::LeaveIfError(iServerEntry.CreateEntry(entry));
+
+ CImapFolder* tempFolder = CImapFolder::NewLC(iSyncMan, iServerEntry, entry, iImapSettings, aCurrentFolderInfo->FolderName());
+ tempFolder->SetFolderMatched(ETrue);
+ iFolderList.InsertInOrderL(tempFolder, sorter);
+ CleanupStack::Pop(tempFolder);
+
+ __LOG_TEXT(iSession.LogId(), "Added folder");
+ }
+ }
+
+/**
+Remove any local folders that are no longer on the server
+*/
+void CImapOpSyncFolderTree::RemoveMissingFolders()
+ {
+ __LOG_TEXT(iSession.LogId(), "CImapOpSyncFolderTree::RemoveMissingFolders");
+
+ // Remove in reverse order to ensure that children folders are removed
+ // before their parent
+ for (TInt folderLoop = iFolderList.Count() - 1; folderLoop >= 0; --folderLoop)
+ {
+ if (!iFolderList[folderLoop]->FolderMatched())
+ {
+#ifdef __IMAP_LOGGING
+ HBufC8* tempBuf = HBufC8::NewL(iFolderList[folderLoop]->FullFolderPathL().Length());
+ TPtr8 tempBufPtr = tempBuf->Des();
+ tempBufPtr.Copy(iFolderList[folderLoop]->FullFolderPathL());
+ __LOG_FORMAT((iSession.LogId(), "Removing folder %S", &tempBufPtr));
+ delete tempBuf;
+#endif
+
+ if (iServerEntry.SetEntry(iFolderList[folderLoop]->MailboxId()) == KErrNone)
+ {
+ if (iServerEntry.SetEntry(iServerEntry.Entry().Parent()) == KErrNone)
+ {
+ iServerEntry.DeleteEntry(iFolderList[folderLoop]->MailboxId());
+ }
+ }
+
+ delete iFolderList[folderLoop];
+ iFolderList.Remove(folderLoop);
+ }
+ }
+ }