--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/email/pop3andsmtpmtm/clientmtms/src/CONSYNC.CPP Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,633 @@
+// 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 <miutset.h>
+#include <mtclreg.h>
+#include <msvids.h>
+#include <miutset.h>
+#include <commdb.h>
+#include <mtclbase.h>
+#include "IMAPSET.H"
+#include "IMAPCMDS.H"
+#include "CONSYNC.H"
+#include "MIUT_ERR.H"
+#include "ImapConnectionObserver.H" // MMsvImapConnectionObserver
+#include <cemailaccounts.h>
+
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include "msvconsts.h"
+#include "miut_errconsts.h"
+#endif
+
+const TInt KImapIdleProgressRate =300000000; // 5 minutes
+const TInt KImapDefaultProgressRate =0xF0000; // approx 1sex
+
+// static
+CImapConnectAndSyncOp* CImapConnectAndSyncOp::NewL( CMsvSession& aSession, const CMsvEntrySelection& aSelection,
+ CBaseMtm& aBaseMtm, TInt aPriority,
+ TRequestStatus& aStatus,
+ TImapConnectionCompletionState aCompletionState,
+ MMsvImapConnectionObserver* aConnectionObserver)
+//
+//
+//
+ {
+ CImapConnectAndSyncOp* self=new(ELeave) CImapConnectAndSyncOp(aSession, aSelection, aBaseMtm, aPriority, aStatus, aCompletionState, aConnectionObserver);
+ CleanupStack::PushL(self);
+ self->ConstructL(aSelection);
+ CleanupStack::Pop(); // self
+ return self;
+ }
+
+CImapConnectAndSyncOp::CImapConnectAndSyncOp(CMsvSession& aSession, const CMsvEntrySelection& aSelection,
+ CBaseMtm& aBaseMtm, TInt aPriority,
+ TRequestStatus& aStatus,
+ TImapConnectionCompletionState aCompletionState,
+ MMsvImapConnectionObserver* aConnectionObserver)
+: CMsvOperation(aSession, aPriority, aStatus),
+ iBaseMtm(aBaseMtm),
+ iConnectionObserver(aConnectionObserver),
+ iCompletionState(aCompletionState)
+//
+//
+//
+ {
+ iService=aSelection.At(0);
+ iMtm=KUidMsgTypeIMAP4;
+ }
+
+void CImapConnectAndSyncOp::ConstructL(const CMsvEntrySelection& aSelection)
+//
+//
+//
+ {
+ iServiceEntry = CMsvEntry::NewL(iMsvSession, iService, TMsvSelectionOrdering());
+ iServiceEntry->SetEntryL(iService);
+ iServiceEntry->AddObserverL(*this);
+ CActiveScheduler::Add(this);
+
+ iProgressTimer = CProgressTimer::NewL(*this);
+ iRefreshTimer = CRefreshTimer::NewL(*this);
+
+ // Need to restore the imap settings to get the refresh rate. This rate will
+ // persist for the life time of this connect and sync op.
+ CImImap4Settings* settings = new (ELeave) CImImap4Settings();
+ CleanupStack::PushL(settings);
+
+ CEmailAccounts* account = CEmailAccounts::NewLC();
+ TImapAccount id;
+ account->GetImapAccountL(iServiceEntry->Entry().Id(), id);
+ account->LoadImapSettingsL(id, *settings);
+
+ iRefreshRate = settings->SyncRate();
+ CleanupStack::PopAndDestroy(2, settings); // account, settings
+
+ // Start the connection operation
+ iSelection = aSelection.CopyL();
+ TBuf8<4> buf;
+ iOperation = iBaseMtm.InvokeAsyncFunctionL(KIMAP4MTMConnectAndSynchronise, *iSelection, buf, iStatus);
+ SetActive();
+ iState = EConnecting;
+ iObserverRequestStatus=KRequestPending;
+ iProgress().iSyncProgress.iState=TImap4SyncProgress::EConnecting;
+ }
+
+CImapConnectAndSyncOp::~CImapConnectAndSyncOp()
+//
+//
+//
+ {
+ if (!IsActive() && iState!=ENotStarted && iState!=ECompleted)
+ Completed(KErrCancel);
+ Cancel();
+ delete iServiceEntry;
+ delete iOperation;
+ delete iSelection;
+ delete iRefreshTimer;//.Close();
+ delete iProgressTimer;
+ };
+
+
+//
+//
+//
+void CImapConnectAndSyncOp::DoRefreshInboxL()
+ {
+ // If this operation is not already doing something,
+ // make it check the local inbox for new mail.
+ //
+ if( !iOperation && iState==EWaiting )
+ {
+ // check that we can do a forced sync
+ if( iMsvSession.ServiceActive(iService) )
+ {
+ // The server MTM is active and therefore the forced sync cannot be
+ // done - set off the refresh timer again.
+ ResetRefreshTimer();
+ }
+ else
+ {
+ // start a forced sync of inbox
+ TBuf8<4> buf;
+ TRAPD(err,iOperation=iBaseMtm.InvokeAsyncFunctionL(KIMAP4MTMInboxNewSync, *iSelection, buf, iStatus));
+ if( err )
+ {
+ // Couldn't start operation, but we are active and pending -
+ // take the connection down.
+ Cancel();
+ return;
+ }
+ iState = EForcedSyncing;
+ }
+ }
+ }
+
+
+const TDesC8& CImapConnectAndSyncOp::ProgressL()
+//
+//
+//
+ {
+ // Deal with states where we don't need to do anything particularly special
+ switch(iState)
+ {
+ case ENotStarted:
+ case ECompleted:
+ case EDisconnectingOnTimeout:
+ return iProgress;
+ default: // Keep GCC happy
+ break;
+ };
+
+ TInt serviceErr=GetServiceProgress();
+ if(serviceErr || (iProgress().iGenericProgress.iState==TImap4GenericProgress::EDisconnected &&
+ iProgress().iGenericProgress.iOperation!=TImap4GenericProgress::EDisconnect))
+ {
+ // Check to see if the error is due to this operation.
+ if( iState == EWaiting && iMsvSession.ServiceActive(iService) )
+ {
+ // Ok there's an operation in the service which is not for this
+ // service (as in Waiting state) - therefore error was for them
+ // and not this operation.
+ iProgress().iGenericProgress.iErrorCode = KErrNone;
+ }
+
+ // Need to'cancel' ourselves here - this will NOT complete the observer
+ // do that later.
+ iForcedCancel = ETrue;
+ Cancel();
+ iStatus = serviceErr;
+ Completed(serviceErr);
+ }
+
+ TInt syncProgressState=iProgress().iSyncProgress.iState;
+
+ switch(iState)
+ {
+ case EConnecting:
+ case EForcedSyncing:
+ // active - return the operation's progress
+ return iOperation->ProgressL();
+
+ // to provide sufficient information to our observer (if we have
+ // one), the first sync is broken into three stages.
+
+ case EFirstSyncingUpdatingInbox:
+ // syncProgressState ought to be ESyncInbox at this point
+ if(syncProgressState==TImap4SyncProgress::ESyncInbox)
+ {
+ // as expected - just reset progress timer and return
+ ResetProgressTimer();
+ break;
+ }
+
+ // We're not syncing the inbox...perhaps we're updating the folder list
+ iState=EFirstSyncingUpdatingFolderList;
+ UpdateObserver(); // We've changed state - let our observer know.
+ // Fall through...
+
+ case EFirstSyncingUpdatingFolderList:
+ // progressState ought to be EFolderTreeSync at this point
+ if(syncProgressState==TImap4SyncProgress::ESyncFolderTree)
+ {
+ // as expected - just reset progress timer and return
+ ResetProgressTimer();
+ break;
+ }
+
+ // We're not updating the folder list...perhaps we're updating the folders
+ iState=EFirstSyncingUpdatingFolders;
+ UpdateObserver(); // We've changed state - let our observer know.
+ // Fall through...
+
+ case EFirstSyncingUpdatingFolders:
+ ResetProgressTimer();
+ if(syncProgressState==TImap4SyncProgress::EIdle)
+ {
+ // Transition between end of background sync and
+ // periodic inbox checking state
+ if(iCompletionState==EAfterFullSync)
+ {
+ iState=ECompletingSelf;
+ TRequestStatus* status=&iStatus;
+ iStatus=KRequestPending;
+ User::RequestComplete(status,KErrNone);
+
+ // NOTE - the active object is already active and so no need
+ // to call SetActive() again.
+ __ASSERT_DEBUG( IsActive(), User::Invariant() );
+ }
+ else
+ {
+ ResetRefreshTimer();
+ iState=EWaiting;
+ UpdateObserver(); // notify observer as state has changed
+ }
+ };
+ break;
+ case EWaiting:
+ {
+ // check for idle timeout
+ if(!iTimeout || iProgress().iGenericProgress.iState!=TImap4GenericProgress::EIdle)
+ {
+ // Either...
+ // !iTimeout, in which case the timeout needs to be set for the first time
+ // or.. Server is doing something, so reset the timer
+ if( iIdleTimeout.Int()<=0 )
+ {
+ // As idle timeout is less zero or -ve, don't timeout at all!
+ iTimeout=EFalse;
+ }
+ else
+ {
+ iTimeout=ETrue;
+ iTimeoutAt.UniversalTime();
+ iTimeoutAt=iTimeoutAt+iIdleTimeout;
+ }
+ }
+ TTime currentTime;
+ currentTime.UniversalTime();
+ if(currentTime>iTimeoutAt && iTimeout)
+ {
+ // Timed out - try to perform a disconnection.
+ //
+ TBuf8<1> buf;
+ TRAPD(err,iOperation=iBaseMtm.InvokeAsyncFunctionL(KIMAP4MTMDisconnect, *iSelection, buf, iStatus));
+ if(err)
+ Cancel();
+ else
+ {
+ iState=EDisconnectingOnTimeout;
+ // NB no need to SetActive - this operation is ALREADY active.
+ UpdateObserver();
+ }
+ }
+ else
+ {
+ if(iTimeout != EFalse)
+ {
+ // reset the progress timer
+ iProgressTimer->Cancel();
+ iProgressTimer->After(KImapDefaultProgressRate);
+ }
+ else
+ {
+ iProgressTimer->Cancel();
+ iProgressTimer->After(KImapIdleProgressRate);
+ }
+ }
+ };
+ break;
+ default:
+ break;
+ }
+
+ iSyncProgress()=iProgress().iSyncProgress;
+ return iSyncProgress;
+ }
+
+void CImapConnectAndSyncOp::ResetProgressTimer()
+ {
+ // reset the progress timer
+ iProgressTimer->Cancel();
+ iProgressTimer->After(KImapDefaultProgressRate);
+ }
+
+void CImapConnectAndSyncOp::UpdateObserver() const
+//
+//
+//
+ {
+ if(iConnectionObserver)
+ {
+ // We have an observer - signal them regarding our current state
+ TImapConnectionEvent event;
+ event=EConnectingToServer; // Assume connecting
+
+ // Translate our new internal state into an event for the observer
+ //
+ switch(iState)
+ {
+ case ENotStarted:
+ case EConnecting:
+ // iState is already EConnectingToServer
+ break;
+
+ case EDisconnectingOnTimeout:
+ event=EDisconnecting;
+ break;
+
+ case ECompleted:
+ event=EConnectionCompleted;
+ break;
+
+ case EForcedSyncing:
+ case EFirstSyncingUpdatingInbox:
+ event=ESynchronisingInbox;
+ break;
+
+ case EFirstSyncingUpdatingFolderList:
+ event=ESynchronisingFolderList;
+ break;
+
+ case EFirstSyncingUpdatingFolders:
+ event=ESynchronisingFolders;
+ break;
+
+ case EWaiting:
+ event=ESynchronisationComplete;
+ break;
+
+ default:
+ // Should never get here
+ gPanic(ESmtcMTMOperationNULL); //DS EImcmBadStateInConnectionOp
+ break;
+ }
+
+ iConnectionObserver->HandleImapConnectionEvent(event);
+ }
+ }
+
+TInt CImapConnectAndSyncOp::GetServiceProgress()
+ {
+ TInt error = iMsvSession.ServiceProgress(iService, iProgress);
+ if(error == KErrNone)
+ error=iProgress().iGenericProgress.iErrorCode;
+ if(error == KErrNone)
+ error=iProgress().iSyncProgress.iErrorCode;
+
+ if (error!= KErrNone)
+ {
+ switch (iState)
+ {
+ case EWaiting:
+ case ECompleted:
+ case ENotStarted:
+ // CImapConnectAndSyncOp not really doing anything, so this error
+ // comes from some other IMPS command and so should not be handled
+ // by this operation.
+
+ // Possible KErrIMAPNO response from server - warning, not error.
+ if (error==KErrNotSupported)
+ error=KErrNone;
+ break;
+ default:
+ // Genuine error in CImapConnectAndSyncOp as it is currently doing
+ // something useful...
+ break;
+ }
+ }
+
+ return error;
+ }
+
+
+void CImapConnectAndSyncOp::Completed(TInt aError)
+ {
+ if(iState==ECompleted)
+ return;
+
+ TRequestStatus* observer=&iObserverRequestStatus;
+ User::RequestComplete(observer, aError);
+ iState=ECompleted;
+ UpdateObserver();
+ }
+
+
+void CImapConnectAndSyncOp::DoCancel()
+ {
+ switch (iState)
+ {
+ case EConnecting:
+ case EForcedSyncing:
+ __ASSERT_DEBUG(iOperation, gPanic(ESmtcMTMOperationNULL)); //DS EImcmNullOperation
+ iOperation->Cancel();
+ // Stop the service, because it's possible that iOperation has completed and so we have
+ // no handle on the server MTM behaviour which may be performing background sync
+ iMsvSession.StopService(iService);
+ break;
+ case EFirstSyncingUpdatingInbox:
+ case EFirstSyncingUpdatingFolderList:
+ case EFirstSyncingUpdatingFolders:
+ case EWaiting:
+ {
+ if( !iForcedCancel )
+ iMsvSession.StopService(iService);
+
+ // Complete ourselves (no one else will)
+ TRequestStatus* myStatus=&iStatus;
+ iStatus=KRequestPending;
+ User::RequestComplete(myStatus,KErrCancel);
+ };
+ break;
+ default:
+ break;
+ }
+ if (!iForcedCancel)
+ Completed(KErrCancel);
+
+ iProgressTimer->Cancel();
+ iRefreshTimer->Cancel();
+ }
+
+
+void CImapConnectAndSyncOp::RunL()
+//
+//
+//
+ {
+ if (iOperation)
+ {
+ iProgress.Copy(iOperation->ProgressL());
+ delete iOperation;
+ iOperation=NULL;
+
+ TInt error = iStatus.Int();
+ if (error==KErrNone)
+ error=iProgress().iSyncProgress.iErrorCode;
+ if (error==KErrNone)
+ error=iProgress().iGenericProgress.iErrorCode;
+ if (error!=KErrNone)
+ {
+ iProgress().iSyncProgress.iErrorCode=error;
+ User::Leave(error);
+ }
+ }
+
+ switch (iState)
+ {
+ case EConnecting:
+ {
+ if(iCompletionState==EAfterConnect)
+ Completed(KErrNone);
+ else
+ {
+ iMtm = KUidMsgTypeIMAP4;
+ iState = EFirstSyncingUpdatingInbox; // IMAP server may or may not still be synchronising - assume that it is.
+ UpdateObserver();
+ iStatus = KRequestPending; // so we still look pending to the OpWatcher
+ ResetProgressTimer(); // Kick off the progress timer...
+ SetActive(); // At next change of state, MUST NOT SetActive again.
+ }
+
+ // The socket timeout value was stored in iMtmData1 from Imps.
+ iBaseMtm.Entry().SetEntryL(iService);
+ TMsvEntry entry = iBaseMtm.Entry().Entry();
+ TInt32 socketTimeout = entry.MtmData1();
+
+ // Adjust the timeout to make sure that we disconnect the IMAP layer
+ // *before* the socket underneath us times out.
+ const TUint KTimeOutPercentage = 80; // Arbitrarily choose 80% - should be enough
+ iIdleTimeout = (socketTimeout * KTimeOutPercentage)/100;
+ break;
+ }
+ case EForcedSyncing:
+ // move back to waiting
+ iState = EWaiting;
+ UpdateObserver();
+ iStatus = KRequestPending;
+ SetActive(); // At next change of state, MUST NOT SetActive again.
+ ResetRefreshTimer();
+ ResetProgressTimer();
+ break;
+ case EDisconnectingOnTimeout:
+ iProgress().iSyncProgress.iErrorCode=KErrTimedOut;
+ Completed(KErrNone);
+ break;
+ case EFirstSyncingUpdatingInbox:
+ case EFirstSyncingUpdatingFolderList:
+ case EFirstSyncingUpdatingFolders:
+ case EWaiting:
+ iProgress().iSyncProgress.iErrorCode=KErrCancel;
+ Completed(KErrCancel);
+ break;
+ case ECompletingSelf:
+ Completed(KErrNone);
+ break;
+ default: // i.e. ECompleted:
+ __ASSERT_DEBUG(EFalse, gPanic(ESmtcMTMOperationNULL)); //DS EImcmBadStateInConnectionOp
+ break;
+ }
+ }
+
+TInt CImapConnectAndSyncOp::RunError(TInt aError)
+ {
+ Completed(aError);
+ return KErrNone;
+ }
+
+void CImapConnectAndSyncOp::HandleEntryEventL(TMsvEntryEvent aEvent, TAny* /*aArg1*/, TAny* /*aArg2*/, TAny* /*aArg3*/)
+ {
+ if(aEvent == MMsvEntryObserver::EMsvEntryChanged)
+ {
+ ProgressL();
+ }
+ }
+
+void CImapConnectAndSyncOp::ResetRefreshTimer()
+ {
+ // Only start the timer if the rate is greater than zero. A value of zero
+ // (or less) indicates that the inbox should not be refreshed.
+ if( iRefreshRate.Int() > 0 )
+ {
+ TTime refreshTime;
+ refreshTime.UniversalTime();
+ refreshTime += iRefreshRate;
+
+ iRefreshTimer->Cancel();
+ iRefreshTimer->AtUTC(refreshTime);
+ }
+ }
+
+/*
+ * CProgressTimer
+ */
+
+CProgressTimer::CProgressTimer(CImapConnectAndSyncOp& aOperation)
+: CTimer(EPriorityLow), iOperation(aOperation)
+ {}
+
+void CProgressTimer::RunL()
+ {
+ // we ignore error, as this is just used ensure that progress is called
+ iOperation.ProgressL();
+ }
+
+TInt CProgressTimer::RunError(TInt /*aError*/)
+ {
+ // Do nothing...
+ return KErrNone;
+ }
+
+CProgressTimer* CProgressTimer::NewL(CImapConnectAndSyncOp& aOperation)
+ {
+ CProgressTimer* self = new(ELeave) CProgressTimer(aOperation);
+ CleanupStack::PushL(self);
+ self->ConstructL(); // CTimer
+ CActiveScheduler::Add(self);
+ CleanupStack::Pop();
+ return self;
+ }
+
+/*
+ * CRefreshTimer
+ */
+
+CRefreshTimer::CRefreshTimer(CImapConnectAndSyncOp& aOperation)
+: CTimer(EPriorityLow), iOperation(aOperation)
+ {}
+
+void CRefreshTimer::RunL()
+ {
+ // we ignore error, as this is just used ensure that progress is called
+ iOperation.DoRefreshInboxL();
+ }
+
+TInt CRefreshTimer::RunError(TInt /*aError*/)
+ {
+ // Do nothing...
+ return KErrNone;
+ }
+
+CRefreshTimer* CRefreshTimer::NewL(CImapConnectAndSyncOp& aOperation)
+ {
+ CRefreshTimer* self = new(ELeave) CRefreshTimer(aOperation);
+ CleanupStack::PushL(self);
+ self->ConstructL(); // CTimer
+ CActiveScheduler::Add(self);
+ CleanupStack::Pop();
+ return self;
+ }
+