diff -r 6a20128ce557 -r ebfee66fde93 email/imap4mtm/imapsyncmanager/src/cimapsyncmanager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/imap4mtm/imapsyncmanager/src/cimapsyncmanager.cpp Fri Jun 04 10:25:39 2010 +0100 @@ -0,0 +1,1566 @@ +// Copyright (c) 2006-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: +// imapsyncmanager.cpp +// +// + + +#include "cimapsyncmanager.h" +#include "cimapopsyncfoldertree.h" +#include "cimapopsyncsubs.h" +#include "cimapfolder.h" +#include "cimapsessionconsts.h" +#include "cimaplogger.h" + +#ifdef __IMAP_LOGGING +#include +#endif + +// Inbox mailbox name +_LIT(KIMAP_INBOX, "INBOX"); + +// Inbox mailbox name used for display purposes +_LIT(KImapInboxDisplayName, "Inbox"); + +/** +Private default class constructor +*/ +CImapSyncManager::CImapSyncManager(CMsvServerEntry& aEntry, CImapSettings& aImapSettings) +: CMsgActive(EPriorityStandard), + iServerEntry(aEntry), + iImapSettings(aImapSettings), + iInboxClearNewFlags(ETrue), + iNonInboxClearNewFlags(ETrue) + { + } + +/** +Class destructor +*/ +CImapSyncManager::~CImapSyncManager() + { + delete iFolderTreeSync; + delete iSyncSubs; + delete iSubscribeList; + delete iUnsubscribeList; + delete iInbox; + iFolderList.ResetAndDestroy(); + } + +/** +Static factory constructor + +@param aServiceId +The TMsvId of the Imap service that is being accessed. +@param aOffLine The CImapOfflineControl that provides access methods to pending offline operations +@return The constructed CImapSuncManager object pushed onto the cleanup stack +*/ +EXPORT_C CImapSyncManager* CImapSyncManager::NewLC(CMsvServerEntry& aEntry, CImapSettings& aImapSettings) + { + CImapSyncManager* self=new (ELeave) CImapSyncManager( aEntry, aImapSettings); + CleanupStack::PushL(self); + + // Non-trivial constructor + self->ConstructL(); + return self; + } + +/** +Static factory constructor with cleanup + +@param aServiceId +The TMsvId of the Imap service that is being accessed. +@param aOffLine The CImapOfflineControl that provides access methods to pending offline operations +@return The constructed CImapSuncManager object +*/ +EXPORT_C CImapSyncManager* CImapSyncManager::NewL(CMsvServerEntry& aEntry, CImapSettings& aImapSettings) + { + CImapSyncManager* self=NewLC( aEntry, aImapSettings); + CleanupStack::Pop(); + return self; + } + +/** +The non-trival constructor. Initialise the data members and create the subcribed local folder list +*/ +void CImapSyncManager::ConstructL() + { + + // Get the flags from iImapSettings + iSynchroniseStrategy = iImapSettings.Synchronise(); + iSubscribeStrategy = iImapSettings.Subscribe(); + iServiceId = iImapSettings.ServiceId(); + // Find the inbox + GetInboxL(); + // Create the list for the rest of the folders + iSubscribeList=new (ELeave) RArray(4); + iUnsubscribeList=new (ELeave) RArray(4); + CreateLocalFolderListL(); + // We're an active object... + CActiveScheduler::Add(this); + } + + +/** +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 CImapSyncManager::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 CImapSyncManager::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 CImapSyncManager::ChangeEntryBulkL(const TMsvEntry& aEntry) + { + User::LeaveIfError(iServerEntry.ChangeEntryBulk(aEntry)); + } + +/** +Return a list of all child entries of the currently selected entry node. + +@param aSelection +The ids for the found child entries will be put into this parameter +*/ +void CImapSyncManager::GetChildrenL(CMsvEntrySelection& aSelection) + { + User::LeaveIfError(iServerEntry.GetChildren(aSelection)); + } + +/** +Returns a pointer to the CImapFolder object associated with the passed entry ID. +CImapSyncManager maintains an array of CImapFolder objects that are subscribed +according to sync and subscribe strategy settings - if the folder exists in this +array then the pointer to this instance is returned. Otherwise a CImapFolder +instance is created for the specified folder and appended to the list. + +As the folder list is rebuilt on folder tree synscronisation, code using this +API cannot expect a returned pointer to remain valid following an account or +folder tree synchronisation operation. + +This method preserves the context of the shared CMsvServerEntry + +@param aMsvId The TMsvId of the folder as stored in the message server + +@param aAddToSubscribedList holds bool value, ETure adds to subscribed list, EFalse does not add. + +@return NULL if the folder does not exist, pointer to the CImapFolder object otherwise. +*/ +EXPORT_C CImapFolder* CImapSyncManager::GetFolderL(TMsvId aMsvId, TBool aAddToSubscribedList) + { + TMsvId preservedId = iServerEntry.Entry().Id(); + + // Retrieve the folder from the maintained list if it is present. + // Will be present if it is subscribed according to settings. + CImapFolder* folder = GetFolderFromList(aMsvId); + + if (folder==NULL) + { + // Requested folder is not in the list of subscribed mailboxes. + // It may have been subscribed locally since the last folder sync + // operation, or the client application may be attempting to access + // a non-subscribed folder. + // Create an instance for the folder and add it to the folder list. + SetEntryL(aMsvId); + TMsvEmailEntry entry = iServerEntry.Entry(); + folder = CImapFolder::NewLC(*this, iServerEntry, entry, iImapSettings, KNullDesC()); + if(aAddToSubscribedList) + { + iFolderList.AppendL(folder); + } + CleanupStack::Pop(folder); + + // recover original serverentry context + SetEntryL(preservedId); + } + return folder; + } + +/** +Returns a pointer to the CImapFolder object associated with the passed entry ID, +if it appears in the array of CImapFolders. + +@param aMsvId The TMsvId of the folder as stored in the message server +@return NULL if the folder is not in the list, pointer to the CImapFolder object otherwise. +*/ +CImapFolder* CImapSyncManager::GetFolderFromList(TMsvId aMsvId) + { + // Check the inbox first + if(iInbox) + { + if(iInbox->MailboxId() == aMsvId) + { + return iInbox; + } + } + + TInt count = iFolderList.Count(); + + for(TInt i = 0; i < count; i++) + { + if(iFolderList[i]->MailboxId() == aMsvId) + { + return iFolderList[i]; + } + } + + // the folder does not exist + return NULL; + } + +/** +This function populates the CImapfolder object passed in by the caller using the TMsvId given to get the relevent folder from +the message server. + +@param aMsvId. +The TMsvId of the folder as stored in the message server to be use to find the imap folder +@return Returns NULL if the folder is not subscribed or does not exist, pointer to the CImapFolder object otherwise. +*/ +EXPORT_C CImapFolder* CImapSyncManager::GetTempFolderL(TMsvId aMsvId) + { + CImapFolder* tempfolder = NULL; + SetEntryL(aMsvId); + TMsvEmailEntry entry = iServerEntry.Entry(); + tempfolder = CImapFolder::NewLC(*this, iServerEntry, entry, iImapSettings, KNullDesC()); + CleanupStack::Pop(tempfolder); + + return tempfolder; + } + +/** +This function populates the CImapfolder object that represent the current service Inbox + +@return Returns the pointer to the CImapFolder representing the Inbox. +*/ +EXPORT_C CImapFolder* CImapSyncManager::Inbox() + { + return iInbox; + } + +RArray* CImapSyncManager::GetUnSubscribedFolderToDoList() + { + return iUnsubscribeList; + } + +RArray* CImapSyncManager::GetSubscribedFolderToDoList() + { + return iSubscribeList; + } + + +/** +Search through the messaging server to find the entry that represented the inbox and if not already +in the list of CImapFolder objects, create one. +*/ +void CImapSyncManager::GetInboxL() + { + + // Remove the old inbox + delete iInbox; + iInbox = NULL; + + // First of all, synchronise the inbox + CMsvEntrySelection *findinbox=new (ELeave) CMsvEntrySelection; + CleanupStack::PushL(findinbox); + SetEntryL(iServiceId); + GetChildrenL(*findinbox); + + // Find inbox + TMsvId inboxid=0; + for(TInt a=0;aCount();a++) + { + SetEntryL((*findinbox)[a]); + if(iServerEntry.Entry().iDetails.CompareF(KIMAP_INBOX)==0) + { + inboxid=(*findinbox)[a]; + TMsvEmailEntry e = iServerEntry.Entry(); + if((iInbox = GetFolderFromList(inboxid)) == NULL) + { + CImapFolder* tempfolder = CImapFolder::NewLC(*this, iServerEntry, e, iImapSettings, KIMAP_INBOX()); + CleanupStack::Pop(tempfolder); + iInbox = tempfolder; + } + + // Mailbox flag not set? It always should be. This will 'repair' it. + if(!e.Mailbox()) + { + // Set it + e.SetMailbox(ETrue); + ChangeEntryL(e); + } + + // Found the inbox, so no need to search any further + break; // from for loop + } + } + + // Clean up + CleanupStack::PopAndDestroy(findinbox); + findinbox = NULL; + + // Not found/no entries? + if(inboxid == 0) + { + // Create an inbox entry below the service. This is a local-only + // creation as the server *WILL* have an inbox. + SetEntryL(iServiceId); + TMsvEmailEntry entry; + + entry.iType = KUidMsvFolderEntry; + entry.iMtm = KUidMsgTypeIMAP4; + entry.iServiceId = iServiceId; + entry.SetMtmData1(0); + entry.SetMtmData2(0); + entry.SetMtmData3(0); + entry.iSize = 0; + entry.SetUID(0); + entry.SetValidUID(EFalse); + entry.SetMailbox(ETrue); + entry.SetLocalSubscription(ETrue); + entry.SetComplete(ETrue); + entry.iDetails.Set(KImapInboxDisplayName); + User::LeaveIfError(iServerEntry.CreateEntry(entry)); + + // Set inbox + // not there so add it + CImapFolder* tempfolder = CImapFolder::NewLC(*this, iServerEntry, entry, iImapSettings, KIMAP_INBOX()); + CleanupStack::Pop(tempfolder); + iInbox = tempfolder; + } + } + +/** +Synchronise outstanding delete operations on all subscribe folders on the IMAP service + +@param aStatus +The TRequestStatus to complete +@param aSession +The session to be use for the sync operation +*/ +EXPORT_C void CImapSyncManager::SynchroniseDeletesL(TRequestStatus& /*aStatus*/, CImapSession& /*aSession*/) + { + } + +/** +Function to synchronise the inbox only. + +@param aStatus +The TRequestStatus to complete +@param aSession +The session to be use for the sync operation +*/ +EXPORT_C void CImapSyncManager::SynchroniseInboxL(TRequestStatus& aStatus, CImapSession& aSession) + { + //initialise progress + iFoldersToDo=0; + iFoldersDone=0; + iFoldersNotFound=0; + iErrorCode=KErrNone; + iSyncDoSingleStep = ETrue; + + iSession = &aSession; + + // Note the invisibility + iNewFoldersAreInvisible = iImapSettings.UpdatingSeenFlags(); + iPerformDeletes = iImapSettings.DeleteEmailsWhenDisconnecting(); + + // Get the synchronise settings + iSynchroniseStrategy = iImapSettings.Synchronise(); + iSubscribeStrategy = iImapSettings.Subscribe(); + + GetInboxL(); + + // Reset stats: 1 folder remaining (inbox) + iFoldersToDo = 1; + iSyncProgressState = TImap4SyncProgress::ESyncInbox; + + // Synchronise with the inbox + iInbox->SelectL(iStatus, aSession, EFalse); + iSyncState = EInboxSelect; + iNextSyncAction = EInboxSync; + + Queue(aStatus); + SetActive(); + } + +/** +Synchronise the subscription status for the folders on the remote server only. + +@param aStatus +The TRequestStatus to complete +@param aSession +The session to be use for the sync operation +*/ +EXPORT_C void CImapSyncManager::SynchroniseFolderSubscriptionsL(TRequestStatus& aStatus, CImapSession& aSession) + { + // Only do this step + iSyncDoSingleStep = ETrue; + + // Save the session + iSession = &aSession; + + // Run the sync folder subscriptions status step + // set iSyncState when folder subscription is kicked off in DoRunL() + iNextSyncAction = ECheckRemoteSubscription; + + Queue(aStatus); + CompleteSelf(); + SetActive(); + } + +/** +At the first call the value of aFolderId should be set to 0. This parameter will be updated with +each call. Therefore after each calll to this function the value of aFolderCounter should be check, +if the value is -1 then the last folder has already been processed. +The Inbox is not part of this function call. + +@param aFolderCounter +Folder counter to keep track of the current folder to be process. +*/ +EXPORT_C void CImapSyncManager::NextSubscribedFoldersL(TMsvId& aFolderId) + { + // Only do this step + iSyncDoSingleStep = ETrue; + + if(aFolderId > 0) + { + // Get the next folder TMsvId and sets the variable + ++iSyncFolderCounter; + if((iSyncFolderCounter) < iFolderList.Count()) + { + CImapFolder *folder = iFolderList[iSyncFolderCounter]; + aFolderId = folder->MailboxId(); + } + else + { + aFolderId = -1; + } + } + else if(aFolderId == 0) + { + iSyncFolderCounter = 0; + + if(iFolderList.Count() > 0) + { + CImapFolder *folder = iFolderList[0]; + aFolderId = folder->MailboxId(); + } + else + { + aFolderId = -1; + } + } + else + { + aFolderId = -1; + __ASSERT_DEBUG(EFalse,TImapServerPanic::ImapPanic(TImapServerPanic::EImapSyncManagerInvalidFolderID)); + } + } + +/** +Perform the next synchronise step for all subscribed folders on the IMAP service. +This function should only be call after one to NextSubscribedFoldersL to setup the folder to be synchronise. +The Inbox will not be synchronise with this function call. + +@param aStatus +The TRequestStatus to complete +@param aSession +The session to be use for the sync operation +@param aFolderId +Folder id to keep track of the current folder to be process. +*/ +EXPORT_C void CImapSyncManager::SynchroniseSubscribedFoldersL(TRequestStatus& aStatus, CImapSession& aSession, TMsvId& aFolderId) + { + // Only do this step + iSyncDoSingleStep = ETrue; + + // Save the session + iSession = &aSession; + + // Run the sync folder subscriptions status step + if(aFolderId > 0) + { + iNextSyncAction = EFolderSelect; + Queue(aStatus); + CompleteSelf(); + SetActive(); + } + } + +/** +Synchronises the local view of the directory structure on the remote IMAP server + +@param aStatus +The TRequestStatus to complete +@param aSession +The session to be use for the sync operation +*/ +EXPORT_C void CImapSyncManager::SynchroniseFolderTreeL(TRequestStatus& aStatus, CImapSession& aSession) + { + // Only do this step + iSyncDoSingleStep = ETrue; + + // Save the session + iSession = &aSession; + + // Note where this list came from + iServiceId = iImapSettings.ServiceId(); + + // Create the synchronise tree object and starts the sync + if(iFolderTreeSync) + { + delete iFolderTreeSync; + iFolderTreeSync = NULL; + } + + iSyncState = ESynchroniseTree; + iNextSyncAction = EEndSynchroniseTree; + //update the progress object + iSyncProgressState= TImap4SyncProgress::ESyncFolderTree; + + iFolderTreeSync = CImapOpSyncFolderTree::NewL(iServerEntry, *iSession, *this, iImapSettings); + iFolderTreeSync->SynchroniseTreeL(iStatus); + + Queue(aStatus); + SetActive(); + } + +/** +Callback function for the operation class CImapOpSyncSubs. +Cycle through the list of local subscribed folders and creates one from the CImapListFolderInfo if +it does not exist in the list. + +@param aFolderInfo +The CImapListFolderInfo to be processed +*/ +#ifdef __IMAP_LOGGING +void CImapSyncManager::AddSubscribedFolderFromInfoL(CImapListFolderInfo* aFolderInfo, TInt aLogId) +#else //__IMAP_LOGGING +void CImapSyncManager::AddSubscribedFolderFromInfoL(CImapListFolderInfo* aFolderInfo, TInt /*aLogId*/) +#endif //__IMAP_LOGGING + { +#ifdef __IMAP_LOGGING + HBufC8* tempBuf = HBufC8::NewL(aFolderInfo->FolderName().Length()); + TPtr8 tempBufPtr = tempBuf->Des(); + CnvUtfConverter::ConvertFromUnicodeToUtf7(tempBufPtr, aFolderInfo->FolderName(), EFalse); + __LOG_FORMAT((aLogId, "AddSubscribedFolderFromInfoL - %S", &tempBufPtr)); + delete tempBuf; +#endif + + // Don't add the INBOX + if(aFolderInfo->FolderName().CompareF(KIMAP_INBOX) == 0) + { + return; + } + + // Search the folder list and don't add it if it's already there + TInt foldercount = iFolderList.Count(); + for (int i = 0; i < foldercount; i++) + { + if(iFolderList[i]->FullFolderPathL().Compare(aFolderInfo->FolderName()) == 0) + { + // Found the folder object just return + return; + } + } + + TMsvId folderid = FindAndSetFolderL(aFolderInfo->FolderName(), aFolderInfo->iHierarchySeperator); + + if(folderid != -1) + { + // Set subscribed flag + TMsvEmailEntry entry = iServerEntry.Entry(); + if(!entry.Subscribed()) + { + // It needs changing, do it + entry.SetVisible(ETrue); + if (iSubscribeStrategy == EUpdateLocal || iSubscribeStrategy == EUpdateBoth) + { + // set local subscribe flag if updating local flags + entry.SetLocalSubscription(ETrue); + } + entry.SetSubscribed(ETrue); + ChangeEntryL(entry); + } + + // Create and add the CImapfolder object + CImapFolder* tempfolder = CImapFolder::NewLC(*this, iServerEntry, entry, iImapSettings, KNullDesC()); + iFolderList.AppendL(tempfolder); + CleanupStack::Pop(tempfolder); + __LOG_TEXT(aLogId, "Folder Added"); + } + } + +/** +Create a CImapFolder list of local subscribed folder from scratch +*/ +void CImapSyncManager::CreateLocalFolderListL() + { + // Clear list of folders to sync, and subscribe/unsubscribe lists + iFolderList.ResetAndDestroy(); + + AddLocalFoldersL(iImapSettings.ServiceId()); + + // Complete any bulk entry changes that occured during AddLocalFoldersL() + iServerEntry.CompleteBulk(); + } + +/** +For a given folder id cycle through the current list of folders and create a new CImapFolder item +if it's not already in the list. Do not add the Inbox or the service. + +@param aFolder +The TMsvId of the folder to be added. +*/ +void CImapSyncManager::AddIfNotThereL(TMsvId aFolder) + { + // first see if we already have this folder and return if we do + for (int i=0; i < iFolderList.Count(); i++) + { + if( iFolderList[i]->MailboxId() == aFolder) + { + return; + } + } + + // don't add the inbox or service + if(aFolder == iInbox->MailboxId() || aFolder == iServiceId) + { + return; + } + + // visit folder + SetEntryL(aFolder); + TMsvEmailEntry e=iServerEntry.Entry(); + // not there so add it + CImapFolder* tempfolder = CImapFolder::NewLC(*this, iServerEntry, e, iImapSettings, KNullDesC()); + iFolderList.AppendL(tempfolder); + CleanupStack::Pop(tempfolder); + } + +/** +Add local (subscribed) folders to iFolderList - this is the list of +folders that are to be synchronised according to flags and settings. +Recursive. + +Also updates subscription flags on the TMsvEntry for the folder +and adds the folder to iSubscribeList or iUnsubscribeList if remote +update of subscription flag is required. These arrays are used +during subscription synchronisation later in the sync procedure. + + +@param aFolder +TMsvId of the folder in the local message store. +*/ +void CImapSyncManager::AddLocalFoldersL(const TMsvId aFolder) + { + // Select the entry + SetEntryL(aFolder); + TMsvEmailEntry entry = iServerEntry.Entry(); + + // Is it actually a folder or service? If not, ignore it. + if(entry.iType != KUidMsvServiceEntry && + entry.iType != KUidMsvFolderEntry) + { + return; + } + + // What we do now depends on the strategy + + // addthisone indicates this folder should be added to the list of folders + // that are to be synchronised according to subscription flags and settings. + TBool addthisone = EFalse; + + switch(iSynchroniseStrategy) + { + case EUseLocal: + { + // Is it locally subscribed? + if(entry.LocalSubscription()) + { + addthisone = ETrue; + } + } + break; + + case EUseRemote: + { + // Is it remotely subscribed? + if(entry.Subscribed()) + { + addthisone = ETrue; + } + } + break; + + case EUseCombination: + { + // Either will do + if(entry.LocalSubscription() || entry.Subscribed()) + { + addthisone = ETrue; + } + } + break; + default: + { + __ASSERT_DEBUG(EFalse,TImapServerPanic::ImapPanic(TImapServerPanic::EAddLocalFolderInvalidSynchStrategy)); + break; + } + } + + + // Any childrenlist? + CMsvEntrySelection *childrenlist = new (ELeave) CMsvEntrySelection; + CleanupStack::PushL(childrenlist); + GetChildrenL(*childrenlist); + TInt noofchildren = childrenlist->Count(); + + if(aFolder != iInbox->MailboxId() && aFolder != iServiceId) + { + if(addthisone) + { + // Do updating + switch(iSubscribeStrategy) + { + case EUpdateNeither: + break; + + case EUpdateLocal: + { + if(!entry.LocalSubscription()) + { + // Update local subscription flag + entry.SetLocalSubscription(ETrue); + SetEntryL(aFolder); + ChangeEntryBulkL(entry); // bulk op completed by the caller of this method + } + } + break; + case EUpdateRemote: + { + if(!entry.Subscribed()) + { + iSubscribeList->InsertInOrder(entry.Id()); + } + } + break; + case EUpdateBoth: + { + if(entry.LocalSubscription() || entry.Subscribed()) + { + if (!entry.LocalSubscription()) + { + // Update local subscription flag + entry.SetLocalSubscription(ETrue); + SetEntryL(aFolder); + ChangeEntryBulkL(entry); // bulk op completed by the caller of this method + } + if (!entry.Subscribed()) + { + // set the remote subscribed flag... + iSubscribeList->InsertInOrder(entry.Id()); + } + } + } + break; + default: + { + __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EAddLocalFolderInvalidSubscribeStrategy)); + break; + } + } + } + else // if(!addthisone) + { + // Do updating + switch(iSubscribeStrategy) + { + case EUpdateNeither: + break; + + case EUpdateLocal: + { + if(entry.LocalSubscription()) + { + // Update local subscription flag + entry.SetLocalSubscription(EFalse); + SetEntryL(aFolder); + ChangeEntryBulkL(entry); // bulk op completed by the caller of this method + } + } + break; + case EUpdateRemote: + { + if(entry.Subscribed()) + { + iUnsubscribeList->InsertInOrder(entry.Id()); + } + else if (entry.LocalSubscription()) + { + iSubscribeList->InsertInOrder(entry.Id()); + addthisone = ETrue; + } + } + break; + case EUpdateBoth: + { + if(entry.LocalSubscription() || entry.Subscribed()) + { + if (!entry.LocalSubscription()) + { + // Update local subscription flag + entry.SetLocalSubscription(ETrue); + SetEntryL(aFolder); + ChangeEntryBulkL(entry); // bulk op completed by the caller of this method + } + if (!entry.Subscribed()) + { + iSubscribeList->InsertInOrder(entry.Id()); + addthisone = ETrue; + } + } + } + break; + } // end of switch + } // end of if (addthisone) + + // add subscribed afterwards + if(addthisone) + { + AddIfNotThereL(aFolder); + } + else + { + // This folder is not subscribed, but has childrenlist. + // If any childrenlist are messages, then delete. + if(noofchildren) + { + // This folder is not subscribed to, so check if it has any messages. + // Do each in turn + TInt child = childrenlist->Count(); + while (child--) + { + SetEntryL((*childrenlist)[child]); + TMsvEmailEntry deletingentry = iServerEntry.Entry(); + + // Is it a message? + if(deletingentry.iType == KUidMsvMessageEntry) + { + SetEntryL(deletingentry.Id()); + SetEntryL(iServerEntry.Entry().Parent()); + iServerEntry.DeleteEntry(deletingentry.Id()); + } + } + + // If we've some childrenlist then get the childrenlist list again + SetEntryL(aFolder); + GetChildrenL(*childrenlist); + noofchildren=childrenlist->Count(); + } // end of if(noofchildren) + } + } // end of if(aFolder != iInbox->MailboxId() && aFolder != iServiceId) + + + // Any childrenlist? + if(noofchildren) + { + // Do each in turn + for(TInt child=0;childCount();child++) + { + AddLocalFoldersL((*childrenlist)[child]); + } + + // Complete any bulk entry changes that occured during AddLocalFoldersL() + iServerEntry.CompleteBulk(); + } + + CleanupStack::PopAndDestroy(childrenlist); + } + + +/** +Deleting the child messages of the given folder from the message server. + +@param aDeleteFolderId +The TMsvId of the foler whose content is to be deleted. +*/ +EXPORT_C void CImapSyncManager::DeleteFolderContentsL(TMsvId aDeleteFolderId) + { + // Select the entry + SetEntryL(aDeleteFolderId); + TMsvEmailEntry entry = iServerEntry.Entry(); + + // Is it actually a folder or service? If not, ignore it. + if(entry.iType != KUidMsvServiceEntry && + entry.iType != KUidMsvFolderEntry) + { + return; + } + + // Any childrenlist? + CMsvEntrySelection *childrenlist = new (ELeave) CMsvEntrySelection; + CleanupStack::PushL(childrenlist); + GetChildrenL(*childrenlist); + TInt noofchildren = childrenlist->Count(); + + // Any childrenlist? + if(noofchildren) + { + // Do each in turn + for(TInt child = 0; child < childrenlist->Count(); ++child) + { + TMsvEntry* entryPtr; + TMsvId id = (*childrenlist)[child]; + User::LeaveIfError(iServerEntry.GetEntryFromId(id, entryPtr)); + + // Is it a message? And is it real (not shadow) + if(entryPtr->iType == KUidMsvMessageEntry && + entryPtr->iRelatedId == KMsvNullIndexEntryId ) + { + // yes it is... remove it + TInt err (iServerEntry.DeleteEntry(id)); + if(err == KErrInUse) + { + // Dont leave if err = KErrInUse + } + else + { + User::LeaveIfError(err); + } + } + } + } + + CleanupStack::PopAndDestroy(childrenlist); + } + +/** +Remove the given folder from the folder index and delete the contents. + +@param aDeleteFolderId +The TMsvId of the foler to be deleted. +*/ +EXPORT_C void CImapSyncManager::RemoveFolderL(TMsvId aMsvId) + { + // Search the folder list and remove the folder + TInt foldercount = iFolderList.Count(); + for (int i = 0; i < foldercount; i++) + { + if( iFolderList[i]->MailboxId() == aMsvId) + { + // Found the folder object remove then return + delete iFolderList[i]; + iFolderList.Remove(i); + break; + } + } + + DeleteFolderContentsL(aMsvId); + + SetEntryL(aMsvId); + TMsvEmailEntry entry = iServerEntry.Entry(); + SetEntryL(entry.Parent()); + iServerEntry.DeleteEntry(aMsvId); + } + +/** +Called to notify the sync manager of a change in a folder's name. +The TMsvEmailEntry for the folder is updated with the new name. +The CImapFolder object representing the folder and that of any children +folders affected by the namechange is also updated to show the new path. +This must be called after a remote folder has been renamed. + +@param aFolderId id of the folder that has been renamed +@param aNewName the new folder name +*/ +EXPORT_C void CImapSyncManager::RenameLocalL(TMsvId aFolderId, const TDesC& aNewName) + { + + // First rename the folder on the local message server + SetEntryL(aFolderId); + + TMsvEmailEntry entry = iServerEntry.Entry(); + entry.iDetails.Set(aNewName); + ChangeEntryL(entry); + + // Search the folder list and rename the folder if we have it subscribed + // then search the rest of the folder list and resets the full path for any subfolders. + TInt foldercount = iFolderList.Count(); + CImapFolder* folder = NULL; + + for (int i = 0; i < foldercount; i++) + { + if( iFolderList[i]->MailboxId() == aFolderId) + { + folder = iFolderList[i]; + // Found the folder object + break; + } + } + + if(folder) + { + // Save a copy of the old folder name + RBuf folderparentnamebuffer; + folderparentnamebuffer.CleanupClosePushL(); + folderparentnamebuffer.CreateL(folder->FullFolderPathL()); + + // reset the path for the rename folder + // - this will be rebuilt when next requested + folder->SetFullFolderPathL(KNullDesC()); + + TPtrC folderparentpath; + // Try and find the parent folder in the folder list + for (int i = 0; i < foldercount; i++) + { + folderparentpath.Set((iFolderList[i]->FullFolderPathL().Left(folderparentnamebuffer.Length()))); + if( folderparentpath.Compare(folderparentnamebuffer) == 0) + { + // Found the parent name, reset the path + iFolderList[i]->SetFullFolderPathL(KNullDesC()); + } + } + + CleanupStack::PopAndDestroy(&folderparentnamebuffer); + } + } + + +/** +Find the folder in the message server using the given path the sets the server context. + +@param aFullPath +The full pathname of the folder relative to the service. +@param aHierarchySeperator +The character that is use as the path separator for the full pathname. +@return The TMsvId of the folder if it was found in the message server. +*/ +TMsvId CImapSyncManager::FindAndSetFolderL(const TPtrC& aFullPath, TChar& aHierarchySeperator) + { + TPtrC path(aFullPath); + TMsvId foundfolderid = -1; + + if (iImapSettings.FolderPath().Length() != 0) + { + //FolderPath will be 8 bit Descriptor, so we need to convert into 16 bit + HBufC* tempPath = HBufC::NewL(iImapSettings.FolderPath().Length()); + tempPath->Des().Copy(iImapSettings.FolderPath()); + + if (path.Match(tempPath->Des()) == 0) + { + TInt separator = path.Locate(aHierarchySeperator); + if (separator == iImapSettings.FolderPath().Length()) + { + ++separator; + if (separator < path.Length()) + { + path.Set(path.Mid(separator)); + } + else + { + // Path contains only the settings FolderPath and separator. This is an error. + delete tempPath; + return KErrNotFound; + } + } + } + delete tempPath; + } + + // Go down through path + CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection(); + CleanupStack::PushL(selection); + + TMsvId currentpos = iServiceId; + + while(currentpos) + { + // Truncate this bit of path: look for a hierarchy separator + TInt separator = path.Locate(aHierarchySeperator); + TPtrC element(path); + + // Anything? + if(separator != KErrNotFound) + { + // Truncate this element there + element.Set(element.Left(separator)); + } + + // Try to find this element at the search level + SetEntryL(currentpos); + GetChildrenL(*selection); + + // Nothing? Give up. + if(!selection->Count()) + { + break; // out of while + } + + // Check all the children + TInt a; + + for(a = 0; a < selection->Count(); ++a) + { + // This one? + SetEntryL((*selection)[a]); + + // Check if the path element matches the current entry details field. + // We need to do a specific check if the entry is the inbox, because + // inbox is case insensitive. + if ((iServerEntry.Entry().iType == KUidMsvFolderEntry && iServerEntry.Entry().iDetails.Compare(element) == 0 )|| + (element.CompareF(KIMAP_INBOX) == 0 && EntryIsInbox(iServerEntry.Entry()))) + { + // Found it! Are we at the end, ie no more elements? + if(separator == KErrNotFound) + { + // Get the entry TMsvId + TMsvEmailEntry entry = iServerEntry.Entry(); + foundfolderid = entry.Id(); + + // All done: this will exit the loop + currentpos = 0; + break; + } + else + { + // Update path + currentpos = (*selection)[a]; + path.Set(path.Mid(separator+1, path.Length()-separator-1)); + + // Exit loop + break; + } + } + } + + if(a == selection->Count()) + { + // Didn't find it. Humm + // Didn't find entry : tree out of date? + break; // out of while + } + } + CleanupStack::PopAndDestroy(selection); + selection = NULL; + + return foundfolderid; + } + +/** +Returns updated progress information on outstanding synchronisation operations. + +@param aProgress +The reference to the progress object to be fill in with the local progress information. +*/ +EXPORT_C void CImapSyncManager::Progress(TImap4SyncProgress& aProgress) + { + //copy values from member progress ob into aProgress + //this includes the progress information set by CImapFolder + + if(iInbox) + { + iInbox->Progress(aProgress); + } + aProgress.iFoldersToDo=iFoldersToDo; + aProgress.iFoldersDone=iFoldersDone; + aProgress.iFoldersNotFound=iFoldersNotFound; + aProgress.iState=iSyncProgressState; + aProgress.iErrorCode=iErrorCode; + } + +/** +Called when async child completes with >=KErrNone +*/ +void CImapSyncManager::DoRunL() + { + //update the progress object + iErrorCode=iStatus.Int(); + + // Handle any server errors + if(iStatus.Int()!=KErrNone) + { + if(TInt err=ProcessSessionError() != KErrNone) + { + Complete(err); + return; + } + } + + switch(iNextSyncAction) + { + case EInboxSync: + { + // Start the sync of the current folder (ie, the inbox) + iNextSyncAction = EEndSingleSyncStep; + iSyncState = EInboxSync; + iSyncProgressState=TImap4SyncProgress::ESyncInbox; + iInbox->SynchroniseL(iStatus, *iSession, EFalse, 0); + SetActive(); + } + break; + + case ECheckRemoteSubscription: + { + // Create the sync subscriptions object to update the local subscriptions + if(iSyncDoSingleStep) + { + iNextSyncAction = EEndSingleSyncStep; + } + else + { + iNextSyncAction = ESynchronise; + } + + // Update folder tree + if(iSyncSubs) + { + delete iSyncSubs; + iSyncSubs = NULL; + } + + iSyncState = ECheckRemoteSubscription; + iSyncSubs = CImapOpSyncSubs::NewL(iServerEntry, *iSession, *this, iImapSettings); + //update the progress object + iSyncProgressState=TImap4SyncProgress::EUpdateRemoteSubscription; + iSyncSubs->SynchroniseSubscriptionsL(iStatus); + SetActive(); + } + break; + case EFolderSelect: + { + //update the progess object + ++iFoldersDone; + iSyncProgressState=TImap4SyncProgress::ESyncOther; + + // This case is for the picking the next folder for the full sync. + if(iSyncFolderCounter >= iFolderList.Count()) + { + // Ran out of folders. Finishing sync + Complete(iStatus.Int()); + } + else + { + CImapFolder *folder = iFolderList[iSyncFolderCounter]; + iNextSyncAction = EFolderSynchronise; + iSyncState = EFolderSelect; + folder->SelectL(iStatus, *iSession); + SetActive(); + } + } + break; + case EFolderSynchronise: + { + // This case is for synching the selected folder during a full sync. + // Back to select next folder + CImapFolder *folder = iFolderList[iSyncFolderCounter]; + + if(iSyncDoSingleStep) + { + iNextSyncAction = EEndSingleSyncStep; + } + else + { + iNextSyncAction = EFolderSelect; + ++iSyncFolderCounter; + } + + iSyncState = EFolderSynchronise; + iSyncProgressState=TImap4SyncProgress::ESyncOther; + folder->SynchroniseL(iStatus, *iSession, EFalse, 0); + SetActive(); + } + break; + case ESynchronise: + { + // This case is for the full synching start with selecting the inbox. + // Starts the full sync process by reseting the folder counter + iSyncFolderCounter = 0; + + iSyncState = ESynchronise; + iNextSyncAction = EFolderSelect; + CompleteSelf(); + SetActive(); + } + break; + + case EEndSynchroniseTree: + { + // We have finished synchronising the folder tree, so now build the + // list of locally subscribed folders + CreateLocalFolderListL(); + // Fall through to next state + } + + case EEndInboxSync: + // Finishing the Synching of the inbox + case EEndSingleSyncStep: + { + iSyncProgressState = TImap4SyncProgress::EIdle; + Complete(iStatus.Int()); + } + break; + + default: + break; + } + } + +/** +Called when completing a client request. + +@param aErr +The completed error code. +*/ +void CImapSyncManager::DoComplete(TInt& aErr) + { + // clean up the internal states + iSyncState = ENotSyncing; + iNextSyncAction = ENotSyncing; + iErrorCode = aErr; + } + +/** +Handles any positive completion codes +*/ +TInt CImapSyncManager::ProcessSessionError() + { + TInt errCode = iStatus.Int(); + if( errCode==KErrImapNo || errCode==KErrImapBad) + { + ProcessNegativeServerResponse(errCode); + } + return errCode; + } + +/** +Specific handling for NO and BAD server responses. +The error code is updated to KErrNone if it has been successfully +fielded by the sync manager. + +@param aErrCode +Reference to the error code. +*/ +void CImapSyncManager::ProcessNegativeServerResponse(TInt& aErrCode) + { + // iSyncState describes current outstanding operation + switch (iSyncState) + { + case EInboxSelect: + { + ++iFoldersNotFound; + break; + } + case EFolderSelect: + { + // SELECT folder has failed.. + ++iFoldersNotFound; + if(!iSyncDoSingleStep) + { + // Doing all folders... + // ignore error and proceed to the next + iNextSyncAction = EFolderSelect; + aErrCode = KErrNone; + } + break; + } + case EInboxSync: + case ECheckRemoteSubscription: + case EFolderSynchronise: + { + // NO/BAD responses should have been handled in the + // folder or operation classes. + break; + } + case ESynchronise: // self completing state + case EEndInboxSync: + case EEndSynchroniseTree: + case EEndSingleSyncStep: + default: + { + // unexpected states. + break; + } + } // end of switch (iSyncState) + } + + +/** +"Silent" cancel for migration - +Resets the error code to KErrNone following the cancel +*/ +EXPORT_C void CImapSyncManager::CancelForMigrate() + { + Cancel(); + iErrorCode=KErrNone; + } + +/** +Cancels any outstanding asynchronous service requests. +*/ +void CImapSyncManager::DoCancel() + { + // iSyncState describes current outstanding operation + switch (iSyncState) + { + case EInboxSelect: + case EFolderSelect: + { + // Note that although select operations are performed on a folder they + // actually pass our iStatus to the session, so that is why we cancel + // the session for them. + iSession->Cancel(); + break; + } + case EInboxSync: + { + iInbox->Cancel(); + break; + } + case ESynchroniseTree: + { + iFolderTreeSync->Cancel(); + break; + } + + case ECheckRemoteSubscription: + { + iSyncSubs->Cancel(); + break; + } + case EFolderSynchronise: + { + (iFolderList[iSyncFolderCounter])->Cancel(); + break; + } + + case ESynchronise: // self completing state + case EEndInboxSync: + case EEndSynchroniseTree: + case EEndSingleSyncStep: + case ENotSyncing: + { + break; + } + + default: + { + __ASSERT_DEBUG(EFalse, + TImapServerPanic::ImapPanic(TImapServerPanic::ESyncManagerCancelUnexpectedState)); + break; + } + } // end of switch + + CMsgActive::DoCancel(); + } + +void CImapSyncManager::CompleteSelf() + { + TRequestStatus* status = &iStatus; + iStatus = KRequestPending; + User::RequestComplete(status, KErrNone); + } + +TBool CImapSyncManager::InboxClearNewFlags() + { + return iInboxClearNewFlags; + } + +TBool CImapSyncManager::NonInboxClearNewFlags() + { + return iNonInboxClearNewFlags; + } + +EXPORT_C void CImapSyncManager::ResetInboxClearNewFlags() + { + iInboxClearNewFlags = EFalse; + } + +EXPORT_C void CImapSyncManager::ResetNonInboxClearNewFlags() + { + iNonInboxClearNewFlags = EFalse; + } + +/** +Checks if the entry passed in is the inbox. + +@param aEntry Entry to check +@return ETrue if entry is inbox, EFalse otherwise +*/ +TBool CImapSyncManager::EntryIsInbox(const TMsvEntry& aEntry) + { + if (aEntry.Parent() == iImapSettings.ServiceId() && + aEntry.iDetails.CompareF(KIMAP_INBOX) == 0) + { + return ETrue; + } + + return EFalse; + } + +/** +Returns pointer to the nth CImapFolder object in the subscribed folder array. +Also returns aNumSubscribedFolders updated with the total number of subscribed folders. + +@param aFolderId index in the array for requested folder, the nth subscribed folder (0 offset) +@param aNumSubscribedFolders updated with total number of subscribed folders. +@return NULL if aFolderId exceeds the number of subscribed folders, + pointer to subscribed CImapFolder object otherwise +*/ +EXPORT_C CImapFolder* CImapSyncManager::GetSubscribedFolder(TInt32 aFolderId, TInt32& aNumSubscribedFolders) + { + // return updated number of folders for progress reporting + aNumSubscribedFolders = iFolderList.Count(); + + if (aFolderId >= aNumSubscribedFolders) + { + return NULL; + } + else + { + return iFolderList[aFolderId]; + } + } + + +