--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/email/pop3andsmtpmtm/imapservermtm/src/IMPSMTM.CPP Mon May 03 12:29:07 2010 +0300
@@ -0,0 +1,2246 @@
+// 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:
+//
+
+#include "impspan.h"
+
+#include "impsmtm.h"
+#include <msventry.h>
+#include <imapset.h>
+#include <miutset.h>
+#include <offop.h>
+#include <msvreg.h>
+#include <imapcmds.h>
+#include <logwrap.h>
+#include <imcvutil.h>
+#include <cemailaccounts.h>
+
+#include "imapsess.h"
+#include "fldsync.h"
+#include "imapsync.h"
+#include "imapcomp.h"
+#include "imapoffl.h"
+#include "impsutil.h"
+
+#ifdef _DEBUG
+#define LOG_COMMANDS(a) iPrimarySession->iSession->LogText a
+#define DBG(a) a
+#define PRINTING
+#else
+#define LOG_COMMANDS(a)
+#define DBG(a)
+#undef PRINTING
+#endif
+
+// Type of MTM request: used only in this file
+enum
+ {
+ EMtmCopyToLocal=1,
+ EMtmMoveToLocal,
+ EMtmCopyFromLocal,
+ EMtmMoveFromLocal,
+ EMtmCopyWithinService,
+ EMtmMoveWithinService,
+ EMtmPopulate
+ };
+
+// Code for the active wrapper
+CActiveWrapper* CActiveWrapper::NewL(TInt aID)
+ {
+ CActiveWrapper* self=new(ELeave)CActiveWrapper(aID);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CActiveScheduler::Add( self );
+ CleanupStack::Pop();
+ return self;
+ }
+
+CActiveWrapper::CActiveWrapper(TInt aID) : CActive(0), iID(aID)
+ {
+ }
+
+void CActiveWrapper::ConstructL()
+ {
+ // The object is a session
+ iSession=CImImap4Session::NewL(iID, *this);
+
+ // Get a compound operation
+ iCompound=CImImap4Compound::NewL(iSession);
+
+ // If we're the primary session, we get a fullsync object too
+ if (iID==1)
+ iFullSync=CImImap4Synchronise::NewL(iSession);
+ }
+
+void CActiveWrapper::StartL(MActiveWrapperObserver* aManager)
+ {
+ DBG((iSession->LogText(_L8("CActiveWrapper::StartL(ID=%d)"),iID)));
+
+ // The request has already been issued with our iStatus, so here all we
+ // do is note down the completion information and do a SetActive()
+ iParentPtr=aManager;
+ // Activate the observer as well, in case the request is cancelled.
+ iParentPtr->Activate();
+ SetActive();
+ }
+
+// Pointer to ActiveWrapper's iStatus, so direct commands to session (see above)
+// use the correct iStatus.
+TRequestStatus* CActiveWrapper::SessionStatus()
+ {
+ // Return the pointer
+ return(&iStatus);
+ }
+
+// Set entry pointers for classes the wrapper owns
+void CActiveWrapper::SetEntry(CMsvServerEntry* aEntry)
+ {
+ iSession->SetEntry(aEntry);
+ iCompound->SetEntry(aEntry);
+ if (iFullSync)
+ iFullSync->SetEntry(aEntry);
+ }
+
+void CActiveWrapper::DoCancel()
+ {
+ DBG((iSession->LogText(_L8("CActiveWrapper::DoCancel(ID=%d)"),iID)));
+
+ // Cancel anything we might have outstanding
+ if (iFullSync)
+ iFullSync->Cancel();
+ iCompound->Cancel();
+ if( !iSession->IsCancelling())
+ {
+ // Can only cancel the session if we're not waiting for a cancelled fetch
+ // to complete.
+ iSession->Cancel();
+ }
+
+ // Tell parent we've completed: DON'T complete it ourselves.
+ iParentPtr->RequestCompleted(iID,KErrCancel);
+ }
+
+#ifdef PRINTING
+void CActiveWrapper::DoComplete(TInt aStatus)
+#else
+void CActiveWrapper::DoComplete(TInt /*aStatus*/)
+#endif
+ {
+ DBG((iSession->LogText(_L8("CActiveWrapper::DoComplete(%d, id=%d)"),aStatus,iID)));
+ }
+
+void CActiveWrapper::RunL()
+ {
+ // IMPORTANT: The contents of this CActiveWrapper::RunL() MUST NOT ever User::Leave()
+ DBG((iSession->LogText(_L8("ActiveWrapper RunL(ID=%d, result=%d)"),iID,iStatus.Int())));
+
+ TInt error=iStatus.Int();
+ Cancel(); // Obvious to do it here
+ iParentPtr->RequestCompleted(iID,error);
+ }
+
+CActiveWrapper::~CActiveWrapper()
+ {
+ Cancel();
+ delete iSession;
+ delete iCompound;
+ delete iFullSync;
+ }
+
+void CActiveWrapper::NonCompletedFailure()
+ {
+ DBG((iSession->LogText(_L8("ActiveWrapper::NonCompletedFailure (%d)"), iID)));
+
+ // A failure has occured on the session, but there is no outstanding asynchronous
+ // request on it. This can happen if for instance we get a disconnect while doing
+ // a cancel and idle operation. Pass the failure on to the parent.
+
+ iParentPtr->NonCompletedFailureOnSession(iID);
+ }
+
+// Return of current imap session
+CImImap4Session* CActiveWrapper::GetImap4Session()
+ {
+ return iSession;
+ }
+
+// -----------------------------------------------------------------------
+
+// The actual MTM
+EXPORT_C CImap4ServerMtm* CImap4ServerMtm::NewL(CRegisteredMtmDll& aRegisteredMtmDll,CMsvServerEntry* aEntry)
+ {
+ CImap4ServerMtm* self=new CImap4ServerMtm(aRegisteredMtmDll,aEntry);
+ if (self==NULL)
+ {
+ aRegisteredMtmDll.ReleaseLibrary();
+ User::Leave(KErrNoMemory);
+ }
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+ return self;
+ }
+
+void CImap4ServerMtm::ConstructL()
+ {
+ // Make an Imap4 session: make it under the activewrapper, so we can have
+ // more than one at a time
+
+ // The primary session is the one that does the full sync
+ iPrimarySession=CActiveWrapper::NewL(1);
+
+ // The secondary session is used if we get fetch requests while the
+ // primary session is busy
+ iSecondarySession=CActiveWrapper::NewL(2);
+
+ // create an offline op controller
+ iOffLineControl=CImap4OffLineControl::NewL(iServerEntry, iPrimarySession->iSession);
+
+ // create the utils object
+ iUtils = CImap4Utils::NewL(iServerEntry);
+
+ // tell the FullSync session about them
+ iPrimarySession->iFullSync->SetOffLineControl(iOffLineControl);
+ iPrimarySession->iFullSync->SetUtils(iUtils);
+
+ // Get an entry selection
+ iSelection=new (ELeave) CMsvEntrySelection;
+
+ // We're disconnected at first
+ iLastSessionState=TImap4GenericProgress::EDisconnected;
+
+ // We need to see invisible entries
+ TMsvSelectionOrdering invisible;
+ invisible=iServerEntry->Sort();
+ invisible.SetShowInvisibleEntries(ETrue);
+ iServerEntry->SetSort(invisible);
+
+ // Tell sessions and foldersync about it
+ iPrimarySession->SetEntry(iServerEntry);
+ iSecondarySession->SetEntry(iServerEntry);
+ }
+
+// MTM destructor: clean up - this involves removing the sessions.
+CImap4ServerMtm::~CImap4ServerMtm()
+ {
+ // We may still be active
+ Cancel();
+
+ // Ensure service is marked as offline (if the serviceid was initialised, anyway)
+ if (iServiceId && iServiceSettings)
+ {
+ // Make stuff invisible as necessary
+ TRAP_IGNORE(MarkOnOrOfflineL(EFalse));
+
+ // Get rid of settings
+ delete iServiceSettings;
+ }
+
+ // Clean up
+ delete iSelection;
+ delete iOneSelection;
+ delete iPrimarySession;
+ delete iSecondarySession;
+ delete iOffLineControl;
+ delete iUtils;
+ }
+
+// Do setentry, leave if there is an error
+void CImap4ServerMtm::SetEntryL(const TMsvId aId)
+ {
+ User::LeaveIfError(iServerEntry->SetEntry(aId));
+ }
+
+// Change entry, leave if error
+void CImap4ServerMtm::ChangeEntryL(const TMsvEntry& aEntry)
+ {
+ User::LeaveIfError(iServerEntry->ChangeEntry(aEntry));
+ }
+
+// Get children, leave if error
+void CImap4ServerMtm::GetChildrenL(CMsvEntrySelection& aSelection)
+ {
+ User::LeaveIfError(iServerEntry->GetChildren(aSelection));
+ }
+
+// remove an id, leave if error, moves to the parent first
+void CImap4ServerMtm::DeleteEntryL(TMsvId aId)
+ {
+ SetEntryL(aId);
+ SetEntryL(iServerEntry->Entry().Parent());
+ User::LeaveIfError(iServerEntry->DeleteEntry(aId));
+ }
+
+// Hierarchically make all folders visible/invisible: we go down until we can't
+// find any more folders, so this won't affect messages with subfolders.
+//
+// We don't use the bulk functions as we don't want to affect messages in the
+// folder, and we have to scan the entries to check for recursion anyway.
+void CImap4ServerMtm::ChangeVisibilityL(TMsvId aParent, TBool aInvisible)
+ {
+ ChangeVisibilityL(aParent, aInvisible, ETrue, KUidMsvFolderEntry);
+ }
+
+void CImap4ServerMtm::ChangeVisibilityL(TMsvId aParent, TBool aInvisible, TBool aRecurse, TUid aType)
+ {
+ DBG((iPrimarySession->iSession->LogText(_L8("ChangeVisibilityL(%x, %d)"),aParent,aInvisible)));
+
+ // Get children at this level
+ CMsvEntrySelection* selection=new (ELeave) CMsvEntrySelection;
+ CleanupStack::PushL(selection);
+
+ CMsvEntrySelection* folders=new (ELeave) CMsvEntrySelection;
+ CleanupStack::PushL(folders);
+
+ SetEntryL(aParent);
+ GetChildrenL(*selection);
+
+ if (selection->Count())
+ {
+ DBG((iPrimarySession->iSession->LogText(_L8(" Found %d children"),selection->Count())));
+
+ for(TInt child=0;child<selection->Count();child++)
+ {
+ // Move to this child
+ SetEntryL((*selection)[child]);
+ TMsvEntry message=iServerEntry->Entry();
+
+ DBG((iPrimarySession->iSession->LogText(_L8(" type %x visible %d"),message.iType,message.Visible())));
+
+ // Is this the type we want to change?
+ if (message.iType==aType)
+ {
+ // Add to selection to do bulk change on, if necessary
+ if ((message.Visible() && aInvisible) ||
+ (!message.Visible() && !aInvisible))
+ {
+ DBG((iPrimarySession->iSession->LogText(_L8(" Adding %x to change list"),message.Id())));
+
+ folders->AppendL(message.Id());
+ }
+ }
+
+ // Recurse downwards
+ if (aRecurse && message.iType==KUidMsvFolderEntry)
+ ChangeVisibilityL(message.Id(),aInvisible,aRecurse,aType);
+ }
+
+ // Change its visibility off all children if necessary
+ if (folders->Count())
+ {
+ // Do the change to the invisible flag (actual constant for the
+ // flag we want is private :( )
+ SetEntryL(aParent);
+ User::LeaveIfError(iServerEntry->ChangeAttributes(*folders,
+ aInvisible?0:KMsvVisibilityAttribute,
+ aInvisible?KMsvVisibilityAttribute:0));
+ }
+ }
+
+ // Release the service entry
+ SetEntryL(KMsvNullIndexEntryId);
+
+ // Get rid of selection
+ CleanupStack::PopAndDestroy(2);
+ }
+void CImap4ServerMtm::ClearNewFlagL(TMsvId aParent)
+ {
+ // Get children at this level
+ CMsvEntrySelection* selection=new (ELeave) CMsvEntrySelection;
+ CleanupStack::PushL(selection);
+ CMsvEntrySelection* msgs=new (ELeave) CMsvEntrySelection;
+ CleanupStack::PushL(msgs);
+ SetEntryL(aParent);
+ GetChildrenL(*selection);
+ // for each child
+ TInt count=selection->Count();
+ for(TInt child=0;child<count;child++)
+ {
+ SetEntryL((*selection)[child]);
+ TMsvEntry entry=iServerEntry->Entry();
+ if (entry.New()&& entry.iType==KUidMsvMessageEntry)
+ msgs->AppendL(entry.Id());
+ // if this is a folder then recurse
+ if (entry.iType==KUidMsvFolderEntry)
+ ClearNewFlagL((*selection)[child]);
+ }
+ if (msgs->Count())// change attribute only when it has finished looking at all the children
+ {
+ SetEntryL(aParent);
+ User::LeaveIfError(iServerEntry->ChangeAttributes(*msgs,0,KMsvNewAttribute));
+ iPrimarySession->iSession->LogText(_L8("number of new msgs = (%d)"),msgs->Count());
+ }
+ CleanupStack::PopAndDestroy(2); // selection,msgs
+ }
+
+
+
+// Mark service as on or offline
+void CImap4ServerMtm::MarkOnOrOfflineL(const TBool aOnline)
+ {
+ // Mark service entry as on/offline
+ SetEntryL(iServiceId);
+ TMsvEntry entry=iServerEntry->Entry();
+ entry.SetConnected(aOnline);
+ ChangeEntryL(entry);
+
+ // Release the service entry
+ SetEntryL(KMsvNullIndexEntryId);
+
+ // Going offline?
+ if (!aOnline && iServiceSettings->DisconnectedUserMode())
+ {
+ // We're an expert user going offline: don't touch anything
+ return;
+ }
+
+ // Mark all immediate children of the service as invisible
+ if (!aOnline)
+ ChangeVisibilityL(iServiceId,!aOnline);
+ }
+
+// Do the misc bits we need to when we've gone offline
+void CImap4ServerMtm::GoneOffline()
+ {
+ DBG((iPrimarySession->iSession->LogText(_L8("GoneOffline called"))));
+
+ // Cancel sessions: parking might be dangerous otherwise
+ iPrimarySession->Cancel();
+ iSecondarySession->Cancel();
+
+ // Park entries (in case a move entry has been left somewhere dangerous by a
+ // leave)
+ iPrimarySession->iSession->Park();
+ iSecondarySession->iSession->Park();
+
+ // Only do this is we have a valid service - we may not, due to being called
+ // both by the state machine and the progress entry
+ if (iServiceId)
+ {
+ // Offline, make folders invisible if we're not a disconnected mode user
+ TRAP_IGNORE(MarkOnOrOfflineL(EFalse));
+
+ // Clear serviceid, it's not valid anymore
+ iServiceId=0;
+ }
+
+ // Settings can be dumped now
+ delete iServiceSettings;
+ iServiceSettings=NULL;
+
+ // We can now be deleted
+ iCanBeDeletedNow=ETrue;
+ }
+
+// Check selection: all entries should be of appropriate
+// type. Messages in this context means complete messages, not RFC888
+// parts of another message. parts means attachment or text
+// bodies. Make a local copy of valid entries
+
+TInt CImap4ServerMtm::CheckSelectionL(const CMsvEntrySelection& aSelection,
+ CMsvEntrySelection* aLocalCopy,
+ const TBool aMessages,
+ const TBool aParts,
+ const TBool aFolders,
+ const TBool aIsInService)
+ {
+ // Reset copy selection
+ aLocalCopy->Reset();
+
+ // Check all entries are messages
+ for(TInt a=0;a<aSelection.Count();a++)
+ {
+ // Does entry exist?
+ TBool addIt = EFalse;
+
+ if (iServerEntry->SetEntry(aSelection[a])==KErrNone)
+ {
+ TUid type = iServerEntry->Entry().iType;
+ if ((aMessages && type==KUidMsvMessageEntry) ||
+ (aParts && (type==KUidMsvEmailTextEntry || type==KUidMsvAttachmentEntry || type==KUidMsvMessageEntry)) ||
+ (aFolders && type==KUidMsvFolderEntry))
+ {
+ TBool inEnclosingMessage=EFalse;
+
+ // Do we need to check if it's in the local service or
+ // if it is a complete message
+ if (aIsInService || (!aParts && type==KUidMsvMessageEntry))
+ {
+ // Work up the tree until we get to the service or the root
+ do
+ {
+ SetEntryL(iServerEntry->Entry().Parent());
+ if (iServerEntry->Entry().iType==KUidMsvMessageEntry)
+ inEnclosingMessage=ETrue;
+ }
+ while(iServerEntry->Entry().iType!=KUidMsvServiceEntry &&
+ iServerEntry->Entry().Id()!=KMsvRootIndexEntryId);
+
+ // Are we at the service that this MTM referrs to?
+ // SJM: if offline iServiceId==0 so allow all
+ if (!aIsInService || iServiceId==0 || iServerEntry->Entry().Id()==iServiceId)
+ {
+ // it's OK if it is not a message type (in
+ // which case it has already been checked and
+ // passed) or it is not within an enclosing message
+ if (type!=KUidMsvMessageEntry || !inEnclosingMessage || aParts)
+ addIt = ETrue;
+ }
+ }
+ else
+ {
+ // Add to local copy
+ addIt = ETrue;
+ }
+ }
+ }
+
+ if (addIt)
+ aLocalCopy->AppendL(aSelection[a]);
+#ifdef _DEBUG
+ // UI shouldn't really be giving us bogus items so panic
+ else
+ gPanic(EInvalidMsvTypeToCommand);
+#endif
+ }
+
+ // Anything to do?
+ if (!aLocalCopy->Count())
+ {
+ // Nothing valid to work with
+ User::RequestComplete(iRequest,KErrNotSupported);
+ return(KErrNotSupported);
+ }
+
+ // All OK, the selection isn't empty
+ return(KErrNone);
+ }
+
+// aId has been unsubscribed. If it has no visible child folders then
+// make it invisible and check its parent with the same test
+void CImap4ServerMtm::PropagateInvisibleFlagL(TMsvId aId)
+ {
+ DBG((iPrimarySession->iSession->LogText(_L8("PropagateInvisibleFlagL: 0x%x"), aId)));
+
+ // finish if we've reached the top
+ if (aId == KMsvRootIndexEntryId)
+ return;
+
+ SetEntryL(aId);
+
+ // finish if we've reached a service
+ if (iServerEntry->Entry().iType == KUidMsvServiceEntry)
+ return;
+
+ // return if we've found a subscribed folder since we can't make
+ // it invisible
+ if (((TMsvEmailEntry)iServerEntry->Entry()).LocalSubscription())
+ return;
+
+ // check the children of this unsubscribed folder
+ CMsvEntrySelection* selection=new (ELeave) CMsvEntrySelection;
+ CleanupStack::PushL(selection);
+
+ GetChildrenL(*selection);
+
+ TBool visible=EFalse;
+ for (TInt i=0; i < selection->Count(); i++)
+ {
+ SetEntryL((*selection)[i]);
+
+ // look for a visible folder
+ TMsvEmailEntry entry = (TMsvEmailEntry)iServerEntry->Entry();
+ if (entry.iType == KUidMsvFolderEntry && entry.Visible())
+ {
+ visible=ETrue;
+ break;
+ }
+ }
+
+ CleanupStack::PopAndDestroy(); // selection
+
+ // if no child folders were visible then make this folder
+ // invisible and continue up
+ if (!visible)
+ {
+ SetEntryL(aId);
+
+ // make this invisible
+ TMsvEntry entry = iServerEntry->Entry();
+ entry.SetVisible(EFalse);
+ ChangeEntryL(entry);
+
+ // go up
+ PropagateInvisibleFlagL(entry.Parent());
+ }
+ }
+
+// Simply change local subscription flag on a folder immediately: This is used
+// as opposed to ChangeL() as this operation can occur offline.
+
+// SJM: If SubscribeStrategy is UpdateRemote or UpdateBoth then doing
+// this should trigger a RemoteSubscription?
+
+TInt CImap4ServerMtm::SetLocalSubscription(const TMsvId aFolder, TBool aSubscribed)
+ {
+ TInt err;
+
+ // Move to the entry
+ if ((err=iServerEntry->SetEntry(aFolder))!=KErrNone)
+ return(err);
+
+ // Check it's a folder
+ if (iServerEntry->Entry().iType!=KUidMsvFolderEntry)
+ return(KErrNotSupported);
+
+ DBG((iPrimarySession->iSession->LogText(_L8("SetLocalSubscription: 0x%x %d"), aFolder, aSubscribed)));
+
+ // Twiddle flag
+ TMsvEmailEntry entry=iServerEntry->Entry();
+ entry.SetLocalSubscription(aSubscribed);
+ return(iServerEntry->ChangeEntry(entry));
+ }
+
+
+// Perform a general move/copy function: this tidies up a lot of the Copy/Move
+// MTM specifics into one place
+void CImap4ServerMtm::MtmCommandL(const TInt aType,const CMsvEntrySelection& aSelection,
+ TMsvId aDestination, TRequestStatus& aStatus)
+ {
+ iRequest=&aStatus;
+ // reset error code
+ iProgressErrorCode=KErrNone;
+
+ // Is source remote? Ensure we check it's from our service
+ TBool checksource=ETrue;
+ if (aType==EMtmCopyFromLocal || aType==EMtmMoveFromLocal)
+ checksource=EFalse;
+
+ // we can only handle parts of messages on Populate
+ TBool handleParts=EFalse;
+ if (aType==EMtmPopulate||aType==EMtmCopyToLocal)
+ handleParts=ETrue;
+
+ // Check selection contains messages, or maybe parts
+ if (CheckSelectionL(aSelection,iSelection,ETrue,handleParts,EFalse,checksource))
+ return;
+
+ // Save destination
+ iDestination=aDestination;
+
+ // Are we online?
+ if (!iPrimarySession->iSession->Connected())
+ {
+ iProgressMsgsToDo=iSelection->Count();
+ iProgressMsgsDone=0;
+
+ // set state for dorunl
+ switch(aType)
+ {
+ case EMtmCopyToLocal:
+ iState=iSavedState=EMtmStateOffLineCopyToLocal;
+ iRequestedOperation=TImap4GenericProgress::EOffLineCopyToLocal;
+ break;
+
+ case EMtmCopyFromLocal:
+ iState=iSavedState=EMtmStateOffLineCopyFromLocal;
+ iRequestedOperation=TImap4GenericProgress::EOffLineCopyFromLocal;
+ break;
+
+ case EMtmCopyWithinService:
+ iState=iSavedState=EMtmStateOffLineCopyWithinService;
+ iRequestedOperation=TImap4GenericProgress::EOffLineCopyWithinService;
+ break;
+
+ case EMtmMoveToLocal:
+ iState=iSavedState=EMtmStateOffLineMoveToLocal;
+ iRequestedOperation=TImap4GenericProgress::EOffLineMoveToLocal;
+ break;
+
+ case EMtmMoveFromLocal:
+ iState=iSavedState=EMtmStateOffLineMoveFromLocal;
+ iRequestedOperation=TImap4GenericProgress::EOffLineMoveFromLocal;
+ break;
+
+ case EMtmMoveWithinService:
+ iState=iSavedState=EMtmStateOffLineMoveWithinService;
+ iRequestedOperation=TImap4GenericProgress::EOffLineMoveWithinService;
+ break;
+
+ case EMtmPopulate:
+ {
+ iState=iSavedState=EMtmStateOffLinePopulate;
+ iRequestedOperation=TImap4GenericProgress::EOffLinePopulate;
+ break;
+ }
+ default:
+ break;
+ }
+ TRequestStatus* pS=(&iStatus);
+ User::RequestComplete(pS,KErrNone);
+ SetActive();
+ }
+ else
+ {
+ TRequestStatus* status;
+
+ // Default to primary session
+ iCurrentSession=iPrimarySession;
+ status=iCurrentSession->SessionStatus();
+
+ // Initialise messages to do, etc
+ iProgressMsgsToDo=iSelection->Count();
+ iProgressMsgsDone=0;
+
+ switch(aType)
+ {
+ case EMtmPopulate:
+ case EMtmCopyToLocal:
+ // We're online: is the primary session busy?
+ if( iBackgroundSyncInProgress || iCurrentSession->iSession->IsCancelling() ||
+ iPrimarySession->iSession->Busy())
+ {
+ DBG((iPrimarySession->iSession->LogText(_L8("Primary session busy, using secondary"))));
+
+ // We'll have to use the secondary session
+ iCurrentSession=iSecondarySession;
+
+ // Is the secondary session connected?
+ if (!iCurrentSession->iSession->Connected())
+ {
+ DBG((iPrimarySession->iSession->LogText(_L8("Connecting secondary"))));
+
+ // We need to connect the secondary session before
+ // continuing with the fetch. Do it. The source message
+ // selection has been checked and stored in iSelection,
+ // so it should be safe until the connect goes through
+ // for later issue.
+ iState=EMtmStateSecondaryConnect;
+ iSavedState=aType == EMtmPopulate ? EMtmStatePopulate : EMtmStateCopyToLocal;
+
+ // Issue the connect
+ iCurrentSession->StartL(this);
+ status=iCurrentSession->SessionStatus();
+ // Providing the reference of primary session
+ iCurrentSession->iSession->SetPrimarySession(iPrimarySession);
+ iCurrentSession->iSession->ConnectL(*status,iServiceId);
+ break;
+ }
+ else if( iCurrentSession->iSession->IsCancelling() )
+ {
+ // Opps! Cannot do the populate currently - server busy.
+ DBG((iPrimarySession->iSession->LogText(_L8("Secondary already cancelling"))));
+
+ User::RequestComplete(iRequest,KErrServerBusy);
+ return;
+ }
+
+ DBG((iPrimarySession->iSession->LogText(_L8("Secondary already connected, reusing"))));
+ }
+
+ // Calculate the total size of the messages to be downloaded in this selection.
+ iTotalSize = iCurrentSession->iSession->CalculateDownloadSizeL(*iSelection);
+
+ // Select relevant folder
+ iCurrentSession->StartL(this);
+ status=iCurrentSession->SessionStatus();
+ iUtils->SetUpLogMessageL((*iSelection)[0]);
+ if (aType == EMtmPopulate)
+ {
+ iState = EMtmStatePopulate;
+ // compound need to know the number of msgs to suspend imap idle calls if > 1
+ if(iProgressMsgsToDo > 0)
+ {
+ iCurrentSession->iCompound->SetMessageCount(iProgressMsgsToDo);
+ }
+ DBG((iPrimarySession->iSession->LogText(_L8("CImapMTM::MTMCommandL(): Calling iCurrentSession->iCompound->Populate()"))));
+ iCurrentSession->iCompound->PopulateL(*status,(*iSelection)[0],iPartialMailInfo);
+ }
+ else
+ {
+ iState = EMtmStateCopyToLocal;
+ iCurrentSession->iCompound->CopyToLocalL(*status,(*iSelection)[0],iDestination);
+ }
+ break;
+
+ case EMtmMoveToLocal:
+ // We're online, are we busy? (it's a write op, and so we must use the
+ // primary session)
+ if( iBackgroundSyncInProgress || iCurrentSession->iSession->IsCancelling() )
+ {
+ // Can't do it, we're busy
+ User::RequestComplete(iRequest,KErrServerBusy);
+ return;
+ }
+
+ // Calculate the total size of the messages to be downloaded in this selection.
+ iTotalSize = iCurrentSession->iSession->CalculateDownloadSizeL(*iSelection);
+
+ // Select relevant folder
+ iCurrentSession->StartL(this);
+ iState=EMtmStateMoveToLocal;
+ iUtils->SetUpLogMessageL((*iSelection)[0]);
+ iCurrentSession->iCompound->MoveToLocalL(*status,(*iSelection)[0],iDestination);
+ break;
+
+ case EMtmCopyFromLocal:
+ case EMtmMoveFromLocal:
+
+ // As this is a write operation, is must be done on the primary session:
+ // is it free?
+ if( iBackgroundSyncInProgress || iCurrentSession->iSession->IsCancelling() )
+ {
+ // Can't do it
+ User::RequestComplete(iRequest,KErrServerBusy);
+ return;
+ }
+
+ // Calculate the total size of the messages to be downloaded in this selection.
+ iTotalSize = iCurrentSession->iSession->CalculateDownloadSizeL(*iSelection);
+
+ iState=(aType==EMtmCopyFromLocal)?EMtmStateCopyFromLocal:EMtmStateMoveFromLocal;
+
+
+ // Start the first operation
+ iCurrentSession->StartL(this);
+ if (aType==EMtmCopyFromLocal)
+ iCurrentSession->iCompound->CopyFromLocalL(*status,(*iSelection)[0],iDestination);
+ else
+ iCurrentSession->iCompound->MoveFromLocalL(*status,(*iSelection)[0],iDestination);
+ break;
+
+ case EMtmCopyWithinService:
+ case EMtmMoveWithinService:
+ // As this is a write operation, is must be done on the primary session:
+ // is it free?
+ if( iBackgroundSyncInProgress || iCurrentSession->iSession->IsCancelling() )
+ {
+ // Can't do it
+ User::RequestComplete(iRequest,KErrServerBusy);
+ return;
+ }
+
+ // Set the state, as we may be dealing with multiple entries
+ iState=(aType==EMtmCopyWithinService)?EMtmStateCopyWithinService:
+ EMtmStateMoveWithinService;
+
+ // Do the copy with the compound
+ iCurrentSession->StartL(this);
+ if (aType==EMtmCopyWithinService)
+ iCurrentSession->iCompound->CopyWithinServiceL(*status,*iSelection,iDestination);
+ else
+ iCurrentSession->iCompound->MoveWithinServiceL(*status,*iSelection,iDestination);
+ break;
+ }
+ }
+ }
+
+
+// CopyToLocal fetches message parts into the mirror: aDestination is currently
+// not used. This is the only command which can cause the second session to
+// connect, as it is for fetching *only* (not 'modifiable' commands which may
+// cause the current synchronisation in the primary session to hiccup). This
+// also rules out MoveToLocal, as it involves a delete on the remote server.
+void CImap4ServerMtm::CopyToLocalL(const CMsvEntrySelection& aSelection,TMsvId aDestination, TRequestStatus& aStatus)
+ {
+ aStatus=KRequestPending;
+
+ LOG_COMMANDS((_L8("MTMCOMMAND CopyToLocal(%d items to %x)"),aSelection.Count(),aDestination));
+
+ iRequestedOperation=TImap4GenericProgress::ECopyToLocal;
+ iGetMailOptions=EGetImap4EmailBodyTextAndAttachments;
+ MtmCommandL(EMtmCopyToLocal,aSelection,aDestination,aStatus);
+ }
+
+// Move To Local moves a message from the remote server to a local folder:
+// this is performed as a fetch, then a delete on the remote server.
+void CImap4ServerMtm::MoveToLocalL(const CMsvEntrySelection& aSelection,TMsvId aDestination, TRequestStatus& aStatus)
+ {
+ aStatus=KRequestPending;
+
+ LOG_COMMANDS((_L8("MTMCOMMAND MoveToLocal(%d items to %x)"),aSelection.Count(),aDestination));
+
+ // We can't do a 'movetolocal' with the destination being the same as the
+ // source, as then the mirror will be out of sync. We must be moving to somewhere
+ // outside this service. Check this.
+ if (!iOffLineControl->IdIsLocalL(aDestination))
+ {
+ TRequestStatus* status=&aStatus;
+ User::RequestComplete(status,KErrNotSupported);
+ }
+ else
+ {
+ iRequestedOperation=TImap4GenericProgress::EMoveToLocal;
+ iGetMailOptions=EGetImap4EmailBodyTextAndAttachments;
+ MtmCommandL(EMtmMoveToLocal,aSelection,aDestination,aStatus);
+ }
+ }
+
+// CopyFromLocal appends entire messages in the mirror to the server.
+void CImap4ServerMtm::CopyFromLocalL(const CMsvEntrySelection& aSelection,TMsvId aDestination, TRequestStatus& aStatus)
+ {
+ aStatus=KRequestPending;
+
+ LOG_COMMANDS((_L8("MTMCOMMAND CopyFromLocal(%d items to %x)"),aSelection.Count(),aDestination));
+
+ iRequestedOperation=TImap4GenericProgress::ECopyFromLocal;
+ MtmCommandL(EMtmCopyFromLocal,aSelection,aDestination,aStatus);
+ }
+
+// Does a CopyFromLocal (ie, IMAP APPEND of the message), then deletes the local message
+// if it was sucessful.
+void CImap4ServerMtm::MoveFromLocalL(const CMsvEntrySelection& aSelection,TMsvId aDestination, TRequestStatus& aStatus)
+ {
+ aStatus=KRequestPending;
+
+ LOG_COMMANDS((_L8("MTMCOMMAND MoveFromLocal(%d items to %x)"),aSelection.Count(),aDestination));
+
+ iRequestedOperation=TImap4GenericProgress::EMoveFromLocal;
+ MtmCommandL(EMtmMoveFromLocal,aSelection,aDestination,aStatus);
+ }
+
+// CopyWithinService copies entire messages to other folders on the server, using
+// the IMAP COPY command.
+void CImap4ServerMtm::CopyWithinServiceL(const CMsvEntrySelection& aSelection,TMsvId aDestination, TRequestStatus& aStatus)
+ {
+ aStatus=KRequestPending;
+
+ LOG_COMMANDS((_L8("MTMCOMMAND CopyWithinService(%d items to %x)"),aSelection.Count(),aDestination));
+
+ iRequestedOperation=TImap4GenericProgress::ECopyWithinService;
+ MtmCommandL(EMtmCopyWithinService,aSelection,aDestination,aStatus);
+ }
+
+// MoveWithinService copies entire messages to other folders on the server, using
+// the IMAP COPY command, then deletes the original if the copy was sucessful,
+// so performing a move.
+void CImap4ServerMtm::MoveWithinServiceL(const CMsvEntrySelection& aSelection,TMsvId aDestination, TRequestStatus& aStatus)
+ {
+ aStatus=KRequestPending;
+
+ LOG_COMMANDS((_L8("MTMCOMMAND MoveWithinService(%d items to %x)"),aSelection.Count(),aDestination));
+
+ iRequestedOperation=TImap4GenericProgress::EMoveWithinService;
+ MtmCommandL(EMtmMoveWithinService,aSelection,aDestination,aStatus);
+ }
+
+void CImap4ServerMtm::UndeleteAllL(const CMsvEntrySelection& aSelection, TRequestStatus& aStatus)
+ {
+ aStatus = KRequestPending;
+ LOG_COMMANDS((_L8("MTMCOMMAND UndeleteAll(%d items)"),aSelection.Count()));
+ if(IsActive())
+ {
+ TRequestStatus* request = &aStatus;
+ User::RequestComplete(request,KErrImapServerBusy);
+ return;
+ }
+
+ iRequest=&aStatus;
+ iProgressErrorCode=KErrNone;
+
+ // Check selection contains only messages in the service
+ if (CheckSelectionL(aSelection,iSelection,ETrue,EFalse,EFalse,ETrue))
+ return;
+
+ iRequestedOperation=TImap4GenericProgress::EOffLineUndelete;
+
+ iProgressMsgsToDo=iSelection->Count();
+ iProgressMsgsDone=0;
+ iState=iSavedState=EMtmStateOffLineUndelete;
+
+ TRequestStatus* pS=(&iStatus);
+ User::RequestComplete(pS,KErrNone);
+ SetActive();
+ }
+
+
+void CImap4ServerMtm::DeleteAllL(const CMsvEntrySelection& aSelection, TRequestStatus& aStatus)
+ {
+ aStatus=KRequestPending;
+
+ LOG_COMMANDS((_L8("MTMCOMMAND DeleteAll(%d items)"),aSelection.Count()));
+
+ iRequest=&aStatus;
+ // reset error code
+ iProgressErrorCode=KErrNone;
+
+ if (PruneMessages(aSelection))
+ // Was this call to DeleteAllL an instruction to delete local parts of a message ?
+ // If so then we don't need to continue.
+ return;
+
+ // Are we online?
+ if (!iPrimarySession->iSession->Connected())
+ {
+ // SJM: Surely we need something like...
+ // Check selection contains only messages - not folders
+ if (CheckSelectionL(aSelection,iSelection,ETrue,EFalse,EFalse,EFalse))
+ return;
+
+ iRequestedOperation=TImap4GenericProgress::EOffLineDelete;
+
+ iProgressMsgsToDo=iSelection->Count();
+ iProgressMsgsDone=0;
+ iState=iSavedState=EMtmStateOffLineDelete;
+
+ TRequestStatus* pS=(&iStatus);
+ User::RequestComplete(pS,KErrNone);
+ SetActive();
+ }
+ else
+ {
+ iRequestedOperation=TImap4GenericProgress::EDelete;
+
+ // We need to do this in the primary session: is it busy?
+ if (iBackgroundSyncInProgress || iPrimarySession->iSession->IsCancelling() ||
+ iPrimarySession->iSession->Busy())
+ {
+ // It's busy, we can't do it
+ User::RequestComplete(iRequest,KErrServerBusy);
+ return;
+ }
+
+ // Message or folder to delete?
+ DBG((iPrimarySession->iSession->LogText(_L8("First is %x"),aSelection[0])));
+
+ SetEntryL(aSelection[0]);
+ if (iServerEntry->Entry().iType==KUidMsvMessageEntry)
+ {
+ DBG((iCurrentSession->iSession->LogText(_L8("Delete message"))));
+
+ // Check selection contains only messages
+ if (CheckSelectionL(aSelection,iSelection,ETrue,EFalse,EFalse,ETrue))
+ return;
+
+ iProgressMsgsToDo=iSelection->Count();
+ iProgressMsgsDone=0;
+ // Select parent folder of message
+ iCurrentSession=iPrimarySession;
+ iState=iSavedState=EMtmStateDelete;
+ TRequestStatus* status=iCurrentSession->SessionStatus();
+ iCurrentSession->iCompound->DeleteL(*status,*iSelection);
+ iCurrentSession->StartL(this);
+ }
+ else if (iServerEntry->Entry().iType==KUidMsvFolderEntry)
+ {
+ DBG((iCurrentSession->iSession->LogText(_L8("Delete folder"))));
+
+ // Check selection contains only folders
+ if (CheckSelectionL(aSelection,iSelection,EFalse,EFalse,ETrue,ETrue))
+ return;
+
+ // Delete folder
+ iCurrentSession=iPrimarySession;
+ iState=iSavedState=EMtmStateDeleteFolder;
+ TRequestStatus* status=iCurrentSession->SessionStatus();
+ iCurrentSession->iCompound->DeleteFolderL(*status,(*iSelection)[0]);
+ iCurrentSession->StartL(this);
+ }
+ else
+ gPanic(EDeleteOfUnknownType);
+ }
+ }
+
+// Create: make a folder or mailbox. Can only happen online
+void CImap4ServerMtm::CreateL(TMsvEntry aNewEntry, TRequestStatus& aStatus)
+ {
+ LOG_COMMANDS((_L8("MTMCOMMAND Create(parent %x)"),aNewEntry.Parent()));
+
+ aStatus=KRequestPending;
+ iRequest=&aStatus;
+ // reset error code
+ iProgressErrorCode=KErrNone;
+
+ // Creating a folder?
+ if (aNewEntry.iType!=KUidMsvFolderEntry)
+ {
+ // No - illegal op
+ User::RequestComplete(iRequest,KErrNotSupported);
+ return;
+ }
+
+ // Are we online and not busy?
+ if( iBackgroundSyncInProgress || iPrimarySession->iSession->IsCancelling() )
+ {
+ User::RequestComplete(iRequest,KErrServerBusy);
+ return;
+ }
+
+ // Folder or mailbox?
+ TBool isfolder=ETrue;
+ if (((TMsvEmailEntry)aNewEntry).Mailbox())
+ isfolder=EFalse;
+
+ // Use primary session: issue create folder command
+ iState=EMtmStateCreateFolder;
+ iCurrentSession=iPrimarySession;
+ TRequestStatus* status=iCurrentSession->SessionStatus();
+ iCurrentSession->iCompound->CreateL(*status,aNewEntry.Parent(),aNewEntry.iDetails,isfolder);
+ iCurrentSession->StartL(this);
+ }
+
+void CImap4ServerMtm::ChangeL(TMsvEntry aNewEntry, TRequestStatus& aStatus)
+ {
+ LOG_COMMANDS((_L8("MTMCOMMAND Change(%x)"),aNewEntry.Id()));
+
+ User::LeaveIfError(iServerEntry->SetEntry( aNewEntry.Id() ));
+ User::LeaveIfError(iServerEntry->ChangeEntry( aNewEntry ));
+
+ iRequest=&aStatus;
+ // reset error code
+ iProgressErrorCode=KErrNone;
+
+ User::RequestComplete(iRequest, KErrNone);
+ }
+
+// SJM: Old ChangeL entry. used to be able to update flags as well as
+// rename folder. Change to only rename folder as its not obvious how
+// you'd pass the flags across
+TBool CImap4ServerMtm::RenameFolderL(TMsvId aId, const TImap4RenameFolder& aRename)
+ {
+ LOG_COMMANDS((_L8("MTMCOMMAND RenameFolder(%x) to %S"),aId,&aRename.iNewName));
+
+ // Are we online and not busy?
+ if( iBackgroundSyncInProgress || iPrimarySession->iSession->IsCancelling() )
+ {
+ User::RequestComplete(iRequest,KErrServerBusy);
+ return EFalse;
+ }
+
+ // Issue a rename command on this object
+ iState=EMtmStateRenameFolder;
+ iCurrentSession=iPrimarySession;
+ TRequestStatus* status=iCurrentSession->SessionStatus();
+ iCurrentSession->iCompound->RenameL(*status, aId, aRename.iNewName);
+ return ETrue;
+ }
+
+// id can be either the service or anything below it. If it is not the
+// service then we move up the tree until we find the service.
+void CImap4ServerMtm::LoadSettingsL(TMsvId aId)
+ {
+ // Old settings shouldn't be hanging about, but...
+ if (iServiceSettings)
+ {
+ delete iServiceSettings;
+ iServiceSettings=NULL;
+ }
+
+ // Get somewhere to store service settings
+ iServiceSettings=new (ELeave) CImImap4Settings;
+
+ // find the service
+ SetEntryL(aId);
+ while (iServerEntry->Entry().iType != KUidMsvServiceEntry)
+ SetEntryL(iServerEntry->Entry().Parent());
+
+ // Get settings for this service
+ CEmailAccounts* account = CEmailAccounts::NewLC();
+ TImapAccount id;
+ id.iImapAccountId = iServerEntry->Entry().MtmData2(); // iMtmData2 of the service entry contains TImapAccountId
+ id.iImapAccountName = iServerEntry->Entry().iDetails;
+ id.iImapService = iServerEntry->Entry().iServiceId;
+ id.iSmtpService = iServerEntry->Entry().iRelatedId;
+
+ account->LoadImapSettingsL(id, *iServiceSettings);
+ CleanupStack::PopAndDestroy(account);
+ }
+
+void CImap4ServerMtm::StartCommandL(CMsvEntrySelection& aSelection, TInt aCommand, const TDesC8& aParameter, TRequestStatus& aStatus)
+ {
+ LOG_COMMANDS((_L8("MTMCOMMAND StartCommand(%x)"),aCommand));
+
+ iTotalSize = 0;
+ aStatus=KRequestPending;
+ iRequest=&aStatus;
+ // reset error code
+ iProgressErrorCode=KErrNone;
+
+ if(iPrimarySession->iSession->IsActive())
+ LOG_COMMANDS((_L8("CImap4ServerMTM::StartCommand(): iPrimarySession->iSession is ACTIVE")));
+ else
+ LOG_COMMANDS((_L8("CImap4ServerMTM::StartCommand(): iPrimarySession->iSession is NOT ACTIVE")));
+
+
+ // Who are we sending this request to? Get their session
+ iCurrentSession=iPrimarySession;
+ TRequestStatus* status=iCurrentSession->SessionStatus();
+
+ // Certain commands require that we are online: check that we're online (the command may have
+ // been queued when we were online, but we've lost the connection now)
+ if (aCommand==KIMAP4MTMSynchronise || aCommand==KIMAP4MTMFullSync ||
+ aCommand==KIMAP4MTMInboxNewSync || aCommand==KIMAP4MTMFolderFullSync ||
+ aCommand==KIMAP4MTMRenameFolder || aCommand==KIMAP4MTMDisconnect ||
+ aCommand==KIMAP4MTMSyncTree || aCommand==KIMAP4MTMSelect)
+ {
+ if (!iPrimarySession->iSession->Connected())
+ {
+ // Can't hack this!
+ User::RequestComplete(iRequest,KErrDisconnected);
+ return;
+ }
+ }
+
+ // Save command, as when it completes we would like to know what happened...
+ iState=EMtmStateMiscCommand;
+ TBool notComplete=EFalse;
+ switch(iLastCommand=aCommand)
+ {
+ case KIMAP4MTMIsConnected:
+ // Are we connected?
+ User::RequestComplete(iRequest,
+ iPrimarySession->iSession->Connected()?KErrNone:KErrDisconnected);
+ break;
+
+ case KIMAP4MTMBusy:
+ // Are we busy?
+ if (iBackgroundSyncInProgress ||
+ iSecondarySession->iSession->Busy())
+ User::RequestComplete(iRequest,KErrServerBusy);
+ else
+ User::RequestComplete(iRequest,KErrNone);
+ break;
+
+ case KIMAP4MTMConnect:
+ case KIMAP4MTMConnectAndSynchronise:
+ if (iPrimarySession->iSession->Busy() ||
+ iPrimarySession->iSession->Connected())
+ {
+ User::RequestComplete(iRequest,KErrServerBusy);
+ }
+ else
+ {
+ iServiceId=aSelection[0];
+
+ LoadSettingsL(iServiceId);
+
+ // We want to hang around for this one
+ iCanBeDeletedNow=EFalse;
+
+ // The selection passed through will be any messages selected for download.
+ // Inform the session about these as they may potentially be deleted during sync.
+ iCurrentSession->iSession->SetSynchronisationSelectionL(aSelection);
+
+ // Queue a connect
+ iRequestedOperation=TImap4GenericProgress::EConnect;
+ iCurrentSession->iSession->ConnectL(*status,iServiceId);
+
+ notComplete = ETrue;
+ }
+ break;
+
+ case KIMAP4MTMStartBatch:
+ // We're about to receive multiple commands from the client: ensure we
+ // stay loaded by faking the CommandExpected() result to be ETrue
+ iBatchInProgress=ETrue;
+ User::RequestComplete(iRequest,KErrNone);
+ break;
+
+ case KIMAP4MTMEndBatch:
+ // Stop faking the CommandExpected(), the client has finished with their
+ // batch of commands.
+ iBatchInProgress=EFalse;
+ User::RequestComplete(iRequest,KErrNone);
+ break;
+
+ case KIMAP4MTMCancelBackgroundSynchronise:
+ // Anything to cancel?
+ if (iBackgroundSyncInProgress)
+ {
+ // Cancel it
+ iPrimarySession->Cancel();
+ iBackgroundSyncInProgress=EFalse;
+ User::RequestComplete(iRequest,KErrCancel);
+ }
+ else
+ User::RequestComplete(iRequest,KErrNone);
+ break;
+
+ case KIMAP4MTMSelect:
+ // Queue a selection command
+ iRequestedOperation=TImap4GenericProgress::ESelect;
+ iCurrentSession->iCompound->SelectL(*status,aSelection[0]);
+ notComplete = ETrue;
+ break;
+
+ case KIMAP4MTMSynchronise:
+ // Queue a synchronise operation
+ iRequestedOperation=TImap4GenericProgress::ESync;
+ if (iClearNewFlagOnNextSync)
+ {
+ ClearNewFlagL(iServiceId);
+ iClearNewFlagOnNextSync = EFalse;
+ }
+
+ iCurrentSession->iCompound->SynchroniseL(*status);
+ notComplete = ETrue;
+ break;
+
+ case KIMAP4MTMSyncTree:
+ // Queue a Synchronise Tree operation
+ if (iBackgroundSyncInProgress)
+ {
+ User::RequestComplete(iRequest,KErrServerBusy);
+ return;
+ }
+
+ iRequestedOperation=TImap4GenericProgress::ESync;
+ iCurrentSession->iFullSync->SynchroniseTreeL(*status,iServiceId,ETrue);
+ notComplete = ETrue;
+ break;
+
+ case KIMAP4MTMLocalSubscribe:
+ // Change local subscription flag on a folder
+ User::RequestComplete(iRequest,SetLocalSubscription(aSelection[0],ETrue));
+ break;
+
+ case KIMAP4MTMLocalUnsubscribe:
+ {
+ // Change local subscription flag on a folder
+ TMsvId folder = aSelection[0];
+ TInt err=SetLocalSubscription(folder,EFalse);
+ if(err==KErrNone)
+ {
+ // if we don't have any service settings then load
+ // them
+ TBool loadedSettings = EFalse;
+ if (iServiceSettings==NULL)
+ {
+ LoadSettingsL(folder);
+ loadedSettings = ETrue;
+ }
+
+ // if synchronisation setting is not remote only then
+ // update the invisibility flags
+ if (iServiceSettings->Synchronise() != EUseRemote)
+ {
+ PropagateInvisibleFlagL(folder);
+ ChangeVisibilityL(folder,ETrue,EFalse,KUidMsvMessageEntry);
+ }
+
+ // if we loaded settings especially then free them
+ // again
+ if (loadedSettings)
+ {
+ delete iServiceSettings;
+ iServiceSettings=NULL;
+ }
+ }
+ User::RequestComplete(iRequest,err);
+ break;
+ }
+
+ case KIMAP4MTMFullSync:
+ // Do a full synchronise if we're not doing one already
+ if (iBackgroundSyncInProgress)
+ {
+ // We're busy, go away
+ User::RequestComplete(iRequest,KErrServerBusy);
+ }
+ else
+ {
+ iRequestedOperation=TImap4GenericProgress::ESync;
+ if (iClearNewFlagOnNextSync)
+ {
+ ClearNewFlagL(iServiceId);
+ iClearNewFlagOnNextSync = EFalse;
+ }
+
+ // SJM: Note new folders should always be invisible as
+ // they are not subscribed
+ iCurrentSession->iFullSync->SynchroniseL(*status,iServiceId, ETrue,
+ !iServiceSettings->DeleteEmailsWhenDisconnecting());
+ iState=EMtmStateForegroundSync;
+ notComplete = ETrue;
+ }
+ break;
+
+ case KIMAP4MTMPopulate:
+ // this function is just a copy to mirror.
+ {
+ iRequestedOperation=TImap4GenericProgress::EPopulate;
+ if (aParameter.Length() > 0)
+ {
+ TImImap4GetPartialMailInfo imap4GetPartialMailInfo;
+ TPckgC<TImImap4GetPartialMailInfo> paramPartialPack(imap4GetPartialMailInfo);
+ paramPartialPack.Set(aParameter);
+ iPartialMailInfo = paramPartialPack();
+ }
+
+ MtmCommandL(EMtmPopulate,aSelection,KMsvNullIndexEntryId,aStatus);
+ break;
+ }
+
+ case KIMAP4MTMInboxNewSync:
+ if (iBackgroundSyncInProgress)
+ {
+ // We're busy, go away
+ User::RequestComplete(iRequest,KErrServerBusy);
+ }
+ else
+ {
+ // First of all, find the inbox
+ CMsvEntrySelection *findinbox=new (ELeave) CMsvEntrySelection;
+ CleanupStack::PushL(findinbox);
+ SetEntryL(iServiceId);
+ GetChildrenL(*findinbox);
+ TMsvId inbox=0;
+ for(TInt a=0;a<findinbox->Count();a++)
+ {
+ SetEntryL((*findinbox)[a]);
+ if (iServerEntry->Entry().iDetails.CompareF(KIMAP_INBOX)==0)
+ {
+ inbox=(*findinbox)[a];
+ break;
+ }
+ }
+
+ // Clean up
+ CleanupStack::PopAndDestroy();
+
+ // Found it?
+ if (inbox)
+ {
+ // Start a new-only sync of the inbox
+ iRequestedOperation=TImap4GenericProgress::ESync;
+ iCurrentSession->iCompound->NewOnlySyncL(*status,inbox);
+ notComplete = ETrue;
+ }
+ else
+ {
+ // Couldn't find it!
+ User::RequestComplete(iRequest,KErrNotFound);
+ }
+ }
+ break;
+
+ case KIMAP4MTMFolderFullSync:
+ // Start a full sync of the folder
+ iRequestedOperation=TImap4GenericProgress::ESync;
+ iCurrentSession->iCompound->FullSyncL(*status,aSelection[0]);
+ notComplete = ETrue;
+ break;
+
+ case KIMAP4MTMWaitForBackground:
+ // Wait for background operation to complete: is one running?
+ if (!iBackgroundSyncInProgress)
+ {
+ // No, just return
+ User::RequestComplete(iRequest,KErrNone);
+ }
+ else
+ {
+ // Otherwise, wait for completion
+ iState=EMtmStateWaitingForBackgroundToFinish;
+ // Activate instead of SetActive()
+ Activate();
+ }
+ break;
+
+ case KIMAP4MTMDisconnect:
+ // if we want to delete emails on disconnect and we've
+ // finished the background sync
+ if ( iServiceSettings->DeleteEmailsWhenDisconnecting() &&
+ !(iBackgroundSyncInProgress || iPrimarySession->iSession->IsCancelling()) )
+ {
+ // Disconnecting
+ iRequestedOperation=TImap4GenericProgress::EDelete;
+
+ iPrimarySession->iFullSync->SynchroniseDeletesL(*status, iServiceId);
+ iPrimarySession->StartL(this);
+ }
+ else
+ {
+ // Disconnecting
+ iRequestedOperation=TImap4GenericProgress::EDisconnect;
+
+ // Cancel both sessions
+ iPrimarySession->Cancel();
+
+ // No sync in progress now...
+ if (iBackgroundSyncInProgress)
+ iBackgroundSyncInProgress=EFalse;
+
+ // Disconnect both sessions
+ status=iPrimarySession->SessionStatus();
+ iPrimarySession->iSession->DisconnectL(*status);
+ iPrimarySession->StartL(this);
+ }
+
+ // kill off the secondary session whatever
+ iSecondarySession->Cancel();
+ status=iSecondarySession->SessionStatus();
+ iSecondarySession->iSession->DisconnectL(*status);
+ iSecondarySession->StartL(this);
+ break;
+
+ case KIMAP4MTMRenameFolder:
+ {
+ TImap4RenameFolder cmd;
+ TPckgC<TImap4RenameFolder> package(cmd);
+ package.Set(aParameter);
+ if (RenameFolderL(aSelection[0], package()))
+ notComplete = ETrue;
+ break;
+ }
+
+ case KIMAP4MTMUndeleteAll:
+ UndeleteAllL(aSelection, aStatus);
+ break;
+
+ case KIMAP4MTMCancelOffLineOperations:
+ iOffLineControl->CancelOffLineOperationsL(aSelection);
+ User::RequestComplete(iRequest,KErrNone);
+ break;
+
+ default:
+ User::RequestComplete(iRequest,KErrNotSupported);
+ break;
+ }
+
+ if (notComplete)
+ {
+ // Stuff to do!
+ iCurrentSession->StartL(this);
+ }
+ }
+
+// Are we finished yet?
+TBool CImap4ServerMtm::CommandExpected()
+ {
+ // ...basically, when we're disconnected we can be deleted
+ return (!iCanBeDeletedNow || iBatchInProgress);
+ }
+
+// Report progress information back to client
+const TDesC8& CImap4ServerMtm::Progress()
+ {
+ TImap4CompoundProgress progress;
+
+ // get generic status from the current session, this writes a
+ // complete GenericProgressState out apart from iOperation. note
+ // that if we do a populate whilst background syncing (for
+ // example) then this will give details of the populate. If we are
+ // only syncing then this will give details about the sync
+ // operation
+ CActiveWrapper *session = NULL;
+
+ if(iState==EMtmStateSecondarySessionIdle)
+ {
+ session = iPrimarySession;
+ }
+ else
+ {
+ session = iCurrentSession ? iCurrentSession : iPrimarySession;
+ }
+
+ progress.iGenericProgress = session->iCompound->Progress();
+
+ // read the sync status whatever state we are in to ensure that
+ // the structure is not left uninitialised
+ progress.iSyncProgress=iPrimarySession->iFullSync->Progress();
+
+ // Have we gone from non-disconnected to disconnected all of a sudden? If we have, this
+ // means that the lower layers have detected a disconnection.
+ // ignore this transition if the last thing we did is ask for a disconnection.
+ if (iLastSessionState!=TImap4GenericProgress::EDisconnected &&
+ progress.iGenericProgress.iState==TImap4GenericProgress::EDisconnected &&
+ iRequestedOperation!=TImap4GenericProgress::EDisconnect)
+ {
+ // Kick ourselves so we know we're disconnected now
+ LOG_COMMANDS((_L8("CImap4ServerMtm::Progress: Session %d suddenly disconnected"), iCurrentSession->iID));
+
+ if(iCurrentSession->iID != 2)
+ {
+ GoneOffline();
+ }
+ }
+
+ // Save this operation to check next time
+ iLastSessionState=progress.iGenericProgress.iState;
+
+ // Put error into progress buffer
+ if( progress.iGenericProgress.iErrorCode == KErrNone )
+ progress.iGenericProgress.iErrorCode=iProgressErrorCode;
+
+ // If we're copying or moving, *we* (the mtm) keep track of the
+ // messages done: this is because these operations involve many
+ // more operations at MTM level (ie, syncs, etc) which also fiddle
+ // with the msgsdone total, as they would during a full sync.
+ switch (iRequestedOperation)
+ {
+ case TImap4GenericProgress::EMoveWithinService:
+ case TImap4GenericProgress::ECopyWithinService:
+ break;
+ case TImap4GenericProgress::EMoveToLocal:
+ case TImap4GenericProgress::ECopyToLocal:
+ case TImap4GenericProgress::EMoveFromLocal:
+ case TImap4GenericProgress::ECopyFromLocal:
+ case TImap4GenericProgress::EPopulate:
+ case TImap4GenericProgress::EDelete:
+ case TImap4GenericProgress::EOffLineDelete:
+ case TImap4GenericProgress::EOffLineUndelete:
+ case TImap4GenericProgress::EOffLineCopyToLocal:
+ case TImap4GenericProgress::EOffLineMoveToLocal:
+ case TImap4GenericProgress::EOffLineCopyFromLocal:
+ case TImap4GenericProgress::EOffLineMoveFromLocal:
+ case TImap4GenericProgress::EOffLineCopyWithinService:
+ case TImap4GenericProgress::EOffLineMoveWithinService:
+ case TImap4GenericProgress::EOffLinePopulate:
+ progress.iGenericProgress.iMsgsToDo=iProgressMsgsToDo;
+ progress.iGenericProgress.iMsgsDone=iProgressMsgsDone;
+ break;
+ default:
+ break;
+ }
+
+#ifdef PRINTING
+ // Log the error we're returning
+ if (iProgressErrorCode!=KErrNone)
+ {
+ iPrimarySession->iSession->LogText(_L8("Progress errorcode=%d, laststate=%d currentstate=%d"),
+ iProgressErrorCode,iLastSessionState,progress.iGenericProgress.iState);
+ }
+#endif
+
+ // put in the operation we've been asked to perform
+ progress.iGenericProgress.iOperation=iRequestedOperation;
+
+ // Copy the Sync iTotalSize flag into the Generic Total Size field, if the progress val is 0;
+ progress.iGenericProgress.iTotalSize = iTotalSize;
+
+ // construct the progress buffer
+ iProgressBuf=TImap4ProgressBuf(progress);
+ return iProgressBuf;
+ }
+
+void CImap4ServerMtm::DoCancel()
+ {
+ DBG((iPrimarySession->iSession->LogText(_L8("CImap4ServerMtm::DoCancel() when in state %d"),iState)));
+
+ // What are we doing?
+ switch(iState)
+ {
+ case EMtmStateCopyFromLocal:
+ case EMtmStateMoveFromLocal:
+ case EMtmStateCopyToLocal:
+ case EMtmStateMoveToLocal:
+ case EMtmStateCopyWithinService:
+ case EMtmStateMoveWithinService:
+ case EMtmStatePopulate:
+ // These all use the compound objects: cancel this
+ iPrimarySession->Cancel();
+ iSecondarySession->Cancel();
+ break;
+
+ case EMtmStateMiscCommand:
+ // What was the actual misc command?
+ switch(iLastCommand)
+ {
+ case KIMAP4MTMConnect:
+ case KIMAP4MTMConnectAndSynchronise:
+ // Kill primary only
+ iPrimarySession->Cancel();
+ break;
+
+ default:
+ // Cancel both sessions...
+ iPrimarySession->Cancel();
+ iSecondarySession->Cancel();
+ }
+ break;
+
+ case EMtmStateSecondaryConnect:
+ // Cancel secondary session
+ iSecondarySession->Cancel();
+ break;
+
+ case EMtmStateOffLineCopyToLocal:
+ case EMtmStateOffLineMoveToLocal:
+ iServerEntry->Cancel();
+ iOffLineControl->Cancel();
+ break;
+ case EMtmStateSecondarySessionIdle:
+ return;
+
+ case EMtmStateDelete:
+ case EMtmStateDeleteFolder:
+ default:
+ // Cancel everything in sight
+ iPrimarySession->Cancel();
+ iSecondarySession->Cancel();
+ break;
+ }
+
+ DBG((iPrimarySession->iSession->LogText(_L8("CImap4ServerMtm::DoCancel() finished"))));
+
+ // Park entries
+ iPrimarySession->iSession->Park();
+ iSecondarySession->iSession->Park();
+
+ if (iRequest)
+ User::RequestComplete(iRequest,KErrCancel);
+ }
+
+void CImap4ServerMtm::DoRunL()
+ {
+#ifdef PRINTING
+ CImImap4Session* logsession=(iId==2)?iSecondarySession->iSession:iPrimarySession->iSession;
+ logsession->LogText(_L8("CImap4ServerMtm::DoRunL(id=%d, status=%d, state=%d)"),
+ iId,iStatus.Int(),iState);
+#endif
+
+ // Status for kicking off next command
+ TRequestStatus* status=iCurrentSession->SessionStatus();
+
+ // What were we doing?
+ switch(iState)
+ {
+ case EMtmStateMiscCommand:
+ // A misc command has completed: do we need to do anything next?
+ switch(iLastCommand)
+ {
+ case KIMAP4MTMConnect:
+ case KIMAP4MTMConnectAndSynchronise:
+ {
+ // Problems?
+ if (iCode!=KErrNone)
+ {
+ // Mark ourselves as offline, and unloadable
+ GoneOffline();
+
+ // Tell world about the error, via progress and
+ // Completion code
+ iProgressErrorCode=iCode;
+
+ // If doing a connect and sync, the caller is completed after the connect
+ // phase and the sync continues in the background. If we get an error during that
+ // sync we need to ensure we don't try to complete the caller again by checking
+ // that iRequest is not null.
+ if (iRequest != NULL)
+ {
+ User::RequestComplete(iRequest,iCode);
+ }
+ return;
+ }
+
+ // Connect was successful: update connected bit in the service entry
+ // Ignore errors, it's not the end of the world
+ TRAP_IGNORE(MarkOnOrOfflineL(ETrue));
+
+ // Mark that we need to clear new flag before next sync
+ iClearNewFlagOnNextSync = ETrue;
+
+ // Do we need to kick off sync?
+ if (iLastCommand==KIMAP4MTMConnectAndSynchronise)
+ {
+ DBG((logsession->LogText(_L8("Kicking off background synchronise"))));
+
+ // Start full sync
+ if (iClearNewFlagOnNextSync)
+ {
+ ClearNewFlagL(iServiceId);
+ iClearNewFlagOnNextSync = EFalse;
+ }
+ // SJM: New folders should always be invisible as they are not subscribed
+ iCurrentSession->iFullSync->SynchroniseL(*status,iServiceId,ETrue,
+ !iServiceSettings->DeleteEmailsWhenDisconnecting(), ETrue);
+ iCurrentSession->StartL(this);
+
+ // Carry on as normal, completing request back to client.
+ // The synchronise will continue in the background.
+ iBackgroundSyncInProgress=ETrue;
+ }
+
+ break;
+ }
+
+ case KIMAP4MTMDisconnect:
+ // Disconnecting but marked as busy - that means we were
+ // carrying out the pending deletes
+ if (iRequestedOperation == TImap4GenericProgress::EDelete)
+ {
+ iRequestedOperation = TImap4GenericProgress::EDisconnect;
+
+ // Disconnect primary sessions
+ status=iPrimarySession->SessionStatus();
+ iPrimarySession->iSession->DisconnectL(*status);
+ iPrimarySession->StartL(this);
+ return;
+ }
+
+ // Disconnecting session 1? (primary session). If so, then clear the
+ // connected bit in the service entry.
+ if (iId==1 && iServiceId && iServiceSettings)
+ {
+ // Do "we've gone offline" stuff
+ GoneOffline();
+ }
+ break;
+ }
+ break;
+
+ case EMtmStateCopyToLocal:
+ case EMtmStateMoveToLocal:
+ case EMtmStatePopulate:
+ // if no message sent then trigger null event to get to next
+ // state
+ SetActive();
+ if (!iUtils->SendLogMessageL(iCode,iStatus))
+ {
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrNone);
+ }
+
+ // send log message
+ iSavedState = iState;
+ iState = EMtmStateLogging;
+ return;
+
+ case EMtmStateLogging:
+ // done logging, restore old state
+ iState = iSavedState;
+ // and deliberately fall through to next case
+
+ case EMtmStateCopyFromLocal:
+ case EMtmStateMoveFromLocal:
+ case EMtmStateCopyWithinService:
+ case EMtmStateMoveWithinService:
+ // Note any error in the appropriate message
+ MessageErrorL((*iSelection)[0],iCode);
+
+ // Note any error in the appropriate message
+ MessageErrorL((*iSelection)[0],iCode);
+
+ // Remove completed item from selection
+ if (iState==EMtmStateCopyWithinService || iState==EMtmStateMoveWithinService)
+ {
+ TInt count=iSelection->Count();
+ iSelection->Delete(0,count);
+ iProgressMsgsDone+=count;
+ }
+ else
+ {
+ iSelection->Delete(0,1);
+ iProgressMsgsDone++;
+ }
+
+ // One more message done
+
+ // Operation done. Do next one in selection
+ if (iSelection->Count())
+ {
+ // Do the copy with the compound
+ status=iCurrentSession->SessionStatus();
+ switch(iState)
+ {
+ case EMtmStateCopyFromLocal:
+ iCurrentSession->iCompound->CopyFromLocalL(*status,(*iSelection)[0],iDestination);
+ break;
+
+ case EMtmStateMoveFromLocal:
+ iCurrentSession->iCompound->MoveFromLocalL(*status,(*iSelection)[0],iDestination);
+ break;
+
+ case EMtmStateCopyToLocal:
+ iUtils->SetUpLogMessageL((*iSelection)[0]);
+ iCurrentSession->iCompound->CopyToLocalL(*status,(*iSelection)[0],iDestination);
+ break;
+
+ case EMtmStateMoveToLocal:
+ iUtils->SetUpLogMessageL((*iSelection)[0]);
+ iCurrentSession->iCompound->MoveToLocalL(*status,(*iSelection)[0],iDestination);
+ break;
+
+ case EMtmStateCopyWithinService:
+ // Will copy all messages in one go.
+ break;
+
+ case EMtmStateMoveWithinService:
+ // Will copy all messages in one go.
+ break;
+
+ case EMtmStatePopulate:
+ iUtils->SetUpLogMessageL((*iSelection)[0]);
+ iCurrentSession->iCompound->PopulateL(*status,(*iSelection)[0],iPartialMailInfo);
+ break;
+
+ default: // Keep gcc quiet
+ break;
+ }
+
+ iCurrentSession->StartL(this);
+ return;
+ }
+ else if(iCurrentSession == iSecondarySession)
+ {
+ iSecondarySession->Cancel();
+ status = iSecondarySession->SessionStatus();
+ iSecondarySession->iSession->DisconnectL(*status);
+ iSecondarySession->StartL(this);
+ iState = EMtmStateSecondarySessionIdle;
+ iSavedState = EMtmStateSecondarySessionIdle;
+ }
+
+ break;
+
+ case EMtmStateDelete:
+ {
+ // Problems?
+ if (iCode!=KErrNone)
+ {
+ // Store the error on this one
+ MessageErrorL((*iSelection)[0],iCode);
+
+ // Continue through selection
+ }
+
+ // Delete completed.
+ TInt count=iSelection->Count();
+ iSelection->Delete(0,count);
+ iProgressMsgsDone+=count;
+
+ DBG((iCurrentSession->iSession->LogText(_L8("iMsgsDone now %d"),iProgressMsgsDone)));
+ break;
+ }
+ case EMtmStateDeleteFolder:
+ // Delete completed: do the next one
+ iSelection->Delete(0,1);
+
+ // One more message done
+ iProgressMsgsDone++;
+
+ DBG((iCurrentSession->iSession->LogText(_L8("iMsgsDone now %d"),iProgressMsgsDone)));
+
+ // Anything left to do?
+ if (!iSelection->Count())
+ break;
+
+ iCurrentSession->iCompound->DeleteFolderL(*status,(*iSelection)[0]);
+ iCurrentSession->StartL(this);
+ return;
+
+ case EMtmStateOffLineDelete:
+ case EMtmStateOffLineUndelete:
+ case EMtmStateOffLineCopyToLocal:
+ case EMtmStateOffLineMoveToLocal:
+ case EMtmStateOffLineCopyFromLocal:
+ case EMtmStateOffLineMoveFromLocal:
+ case EMtmStateOffLineCopyWithinService:
+ case EMtmStateOffLineMoveWithinService:
+ case EMtmStateOffLinePopulate:
+ {
+ if(iProgressMsgsDone == iProgressMsgsToDo)
+ break;
+
+ if(iOneSelection)
+ delete iOneSelection;
+ iOneSelection=new (ELeave) CMsvEntrySelection;
+ iOneSelection->AppendL((*iSelection)[iProgressMsgsDone]);
+
+ CImap4OffLineControl::TImap4OpType opType=CImap4OffLineControl::EImap4OpDelete; //have to initialise to something!
+ switch(iState)
+ {
+ case EMtmStateOffLineDelete:
+ opType=CImap4OffLineControl::EImap4OpDelete;
+ break;
+ case EMtmStateOffLineUndelete:
+ opType=CImap4OffLineControl::EImap4OpUndelete;
+ break;
+ case EMtmStateOffLineCopyToLocal:
+ opType=CImap4OffLineControl::EImap4OpCopyToLocal;
+ break;
+ case EMtmStateOffLineMoveToLocal:
+ opType=CImap4OffLineControl::EImap4OpMoveToLocal;
+ break;
+ case EMtmStateOffLineCopyFromLocal:
+ opType=CImap4OffLineControl::EImap4OpCopyFromLocal;
+ break;
+ case EMtmStateOffLineMoveFromLocal:
+ opType=CImap4OffLineControl::EImap4OpMoveFromLocal;
+ break;
+ case EMtmStateOffLineCopyWithinService:
+ opType=CImap4OffLineControl::EImap4OpCopyWithinService;
+ break;
+ case EMtmStateOffLineMoveWithinService:
+ opType=CImap4OffLineControl::EImap4OpMoveWithinService;
+ break;
+ case EMtmStateOffLinePopulate:
+ opType=CImap4OffLineControl::EImap4OpPopulate;
+ break;
+ default:
+ break;
+ }
+
+ if(iState == EMtmStateOffLineDelete || iState == EMtmStateOffLineUndelete)
+ {
+ iOffLineControl->StoreOfflineCommandL(opType, *iOneSelection, KMsvNullIndexEntryId, iStatus);
+ }
+ else if(iState == EMtmStateOffLinePopulate)
+ {
+ TPckgBuf<TImap4GetMailOptions> package(iGetMailOptions);
+ iOffLineControl->StoreOfflineCommandL(opType, *iOneSelection, iDestination, package, iStatus);
+ }
+ else
+ {
+ iOffLineControl->StoreOfflineCommandL(opType, *iOneSelection, iDestination, iStatus);
+ }
+
+ iProgressMsgsDone++;
+ SetActive();
+ return;
+ }
+ case EMtmStateSecondaryConnect:
+ // We've now connected the secondary session. Issue the
+ // saved command and continue;
+ switch(iState=iSavedState)
+ {
+ case EMtmStateCopyToLocal:
+ // We've just connected, so first thing to do is to do the
+ // folder selection. No need to check the selection, as
+ // we know it contains at least 1 item from the originally
+ // issued command.
+ iUtils->SetUpLogMessageL((*iSelection)[0]);
+ iCurrentSession->iCompound->CopyToLocalL(*status,(*iSelection)[0],iDestination);
+ iCurrentSession->StartL(this);
+ return;
+
+ case EMtmStatePopulate:
+ iUtils->SetUpLogMessageL((*iSelection)[0]);
+ iCurrentSession->iCompound->PopulateL(*status,(*iSelection)[0],iPartialMailInfo);
+ iCurrentSession->StartL(this);
+ return;
+
+ default:
+ break;
+ }
+ break;
+
+ case EMtmStateSyncCompleted: // FALL THROUGH
+ default:
+ break;
+ }
+
+ // Async request completed (from wrapper): also return it in the progress
+ DBG((logsession->LogText(_L8("ID %d completed with code %d"),iId,iCode)));
+
+ iProgressErrorCode=iCode;
+
+ // Park entry
+ iServerEntry->SetEntry(KMsvNullIndexEntryId);
+
+ // Only complete if we have an iRequest: we may not (ie, 2 sessions
+ // disconnecting at once)
+ if (iRequest)
+ User::RequestComplete(iRequest,KErrNone);
+ }
+
+void CImap4ServerMtm::DoComplete(TInt aStatus)
+ {
+#ifdef PRINTING
+ iPrimarySession->iSession->LogText(_L8("CImap4ServerMtm::DoComplete(%d)"),aStatus);
+#endif
+
+ // Park entry
+ iServerEntry->SetEntry(KMsvNullIndexEntryId);
+ if (iRequest)
+ User::RequestComplete(iRequest,aStatus);
+ }
+
+// Save error code in a message
+void CImap4ServerMtm::MessageErrorL(const TMsvId aMessageId, const TInt aError)
+ {
+ // Save error code: if we can't access this entry, then it's probably something to do
+ // with the error we're trying to report: ignore it silently
+ // SJM: this used to be != KerrNone - surely shome mistake
+ if (iServerEntry->SetEntry(aMessageId)==KErrNone)
+ {
+ TMsvEntry entry=iServerEntry->Entry();
+
+ // Save unnecessary writes...
+ if (entry.iError!=aError)
+ {
+ entry.iError=aError;
+ ChangeEntryL(entry);
+ }
+ }
+ }
+
+// Mixin - a child has completed
+void CImap4ServerMtm::RequestCompleted(TInt aId, TInt aCode)
+ {
+#ifdef PRINTING
+ CImImap4Session* logsession=(aId==2)?iSecondarySession->iSession:iPrimarySession->iSession;
+ logsession->LogText(_L8("CImap4ServerMtm::RequestCompleted(id=%d, result=%d, state=%d)"),aId,aCode,iState);
+#endif
+ // Is this the background sync completeing?
+ if (aId==1 && iBackgroundSyncInProgress)
+ {
+ // Not in progress any more, boyo
+ iBackgroundSyncInProgress=EFalse;
+
+ // Is anyone in the forground waiting for this to happen?
+ if (iState==EMtmStateWaitingForBackgroundToFinish)
+ {
+ // Yes: we need to complete ourselves by falling through
+ // Reset state first
+ iState=EMtmStateIdle;
+
+ // Any errors from the background sync aren't interesting to us
+ aCode=KErrNone;
+ }
+ // Makes the secondary session request, when primary completes background sync
+ else if (iState == EMtmStateSecondaryConnect || iState == EMtmStateCopyToLocal || iState == EMtmStatePopulate)
+ {
+ iProgressErrorCode=aCode;
+ return;
+ }
+ else if( aCode == KErrNone )
+ {
+ // Complete with the state EMtmStateSyncCompleted.
+ // When this is caught in the DoRunL, it does nothing.
+ iState = EMtmStateSyncCompleted;
+
+ // NOTE - only do this if there are no errors - if there is an error
+ // then DoRunL must handle appropriately in the previous state.
+ }
+ }
+
+ // Are we doing a disconnect? If so, we will get completions from both sessions, and we're only
+ // interested in the primary one, really
+ if (iLastCommand==KIMAP4MTMDisconnect && aId!=1)
+ {
+ // Silently ignore
+ return;
+ }
+
+ // Is the code >0? If so, truncate it to 0 as it's IMPS-specific info
+ if (aCode>KErrNone)
+ aCode=KErrNone;
+
+ // Save stuff
+ iCode=aCode;
+ iId=aId;
+
+ // Set the current session to the one which has just completed so
+ // that when DoRunL() is called it knows which to deal with
+ if (aId==1)
+ iCurrentSession=iPrimarySession;
+ else
+ iCurrentSession=iSecondarySession;
+
+ // Complete *ourselves*
+ TRequestStatus *a=&iStatus;
+ User::RequestComplete(a,aCode);
+
+ // The activation may need to be done here.
+ // e.g. the primary session already completed and then the secondary completes.
+ // e.g. EMtmCopyToLocal is started whilst we are online and a background
+ // sync is in progress. One completes and then the other.
+ if(!IsActive())
+ {
+ SetActive();
+ }
+ }
+
+// This function allows a CActiveWrapper to set the CImap4ServerMtm active
+// when it starts a request, as opposed to just when one finishes. This means
+// that Cancel()s are handled properly.
+void CImap4ServerMtm::Activate()
+ {
+ // Object may already be active as there is a primary and secondary session.
+ if(!IsActive())
+ {
+ iStatus = KRequestPending;
+ SetActive();
+ }
+ }
+
+#ifdef PRINTING
+void CImap4ServerMtm::NonCompletedFailureOnSession(TInt aId)
+#else
+void CImap4ServerMtm::NonCompletedFailureOnSession(TInt /*aId*/)
+#endif
+ {
+#ifdef PRINTING
+ CImImap4Session* logsession=(aId==2)?iSecondarySession->iSession:iPrimarySession->iSession;
+ logsession->LogText(_L8("CImap4ServerMtm::NonCompletedFailureOnSession (id=%d)"), aId);
+#endif
+
+ // A failure has occured on the session, but there is no outstanding asynchronous
+ // request on it. This can happen if for instance we get a disconnect while doing
+ // a cancel and idle operation. We need to go offline immediately.
+ GoneOffline();
+ }
+
+CImap4ServerMtm::CImap4ServerMtm(CRegisteredMtmDll& aRegisteredMtmDll, CMsvServerEntry* aEntry):
+ CBaseServerMtm(aRegisteredMtmDll,aEntry)
+ {
+ __DECLARE_NAME(_S("CImap4ServerMtm"));
+
+ // We can be deleted: at the start, we're not connected
+ iCanBeDeletedNow=ETrue;
+
+ // Add us to the scheduler
+ CActiveScheduler::Add(this);
+ }
+TBool CImap4ServerMtm::PruneMessages(const CMsvEntrySelection& aSelection)
+ {
+ TInt index = aSelection.Count();
+
+ // See if the parent of the first entry is a message.
+ // If it is then we need to prune the entries, ie. delete them locally.
+ if (index == 0)
+ return EFalse;
+
+ TInt err = iServerEntry->SetEntry(aSelection[0]);
+
+ if (err == KErrNone)
+ {
+ err = iServerEntry->SetEntry(iServerEntry->Entry().Parent());
+ if (KUidMsvMessageEntry != iServerEntry->Entry().iType)
+ // The parent of the given entry was not a message, so we don't prune it.
+ return EFalse;
+ }
+
+ while ((index--) && (err==KErrNone))
+ {
+ // Go to the required entry
+ err = iServerEntry->SetEntry(aSelection[index]);
+
+ if (KErrNone == err)
+ {
+ // Go to the parent entry to see if it is a message entry
+ iServerEntry->SetEntry(iServerEntry->Entry().Parent());
+ TMsvEmailEntry entry = iServerEntry->Entry();
+
+ // assert that (KUidMsvMessageEntry == entry.iType)
+
+ // Clear the complete flag because we are about to delete the child parts.
+ entry.SetComplete(EFalse);
+ entry.SetBodyTextComplete(EFalse);
+ err = iServerEntry->ChangeEntry(entry);
+
+ if (KErrNone == err)
+ {
+ // Delete the body of the message.
+ iServerEntry->DeleteEntry(aSelection[index]);
+ }
+ }
+ }
+
+ User::RequestComplete(iRequest, err);
+
+ return ETrue;
+ }