email/imap4mtm/imapprotocolcontroller/src/cimapidlecontroller.cpp
changeset 0 72b543305e3a
equal deleted inserted replaced
-1:000000000000 0:72b543305e3a
       
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "cimapidlecontroller.h"
       
    17 #include "cimapopfetchbody.h"
       
    18 #include "cimapsessionconsts.h"
       
    19 #include "cimapsession.h"
       
    20 #include "cimapsyncmanager.h"
       
    21 #include "cimapfolder.h"
       
    22 #include "cimaplogger.h"
       
    23 #include "cimapcompoundcopytolocal.h"
       
    24 #include "cimapmailstore.h" 
       
    25 #include <imapset.h>
       
    26 
       
    27 
       
    28 CImapIdleController::CImapIdleController(MImapIdleControllerObserver& aObserver, CImapSession*& aSession, CImapSyncManager& aSyncManager, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore)
       
    29  : CActive(EPriorityStandard), iObserver(aObserver), iSession(aSession), iSyncManager(aSyncManager), iServerEntry(aServerEntry), iImapSettings(aImapSettings), iImapMailStore(aImapMailStore)
       
    30 	{
       
    31 	}
       
    32 
       
    33 CImapIdleController::~CImapIdleController()
       
    34 	{
       
    35 	// This is should be the only place where CImapIdleController should Cancel() on itself
       
    36 	// instead of calling InternalCancelAndSetState()
       
    37 	Cancel();
       
    38 	delete iReissueTimer;
       
    39 	delete iCopyToLocal;
       
    40 	}
       
    41 
       
    42 CImapIdleController* CImapIdleController::NewL(MImapIdleControllerObserver& aObserver, CImapSession*& aSession, CImapSyncManager& aSyncManager, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore)
       
    43 	{
       
    44 	CImapIdleController* self = new (ELeave) CImapIdleController(aObserver, aSession, aSyncManager, aServerEntry, aImapSettings, aImapMailStore);
       
    45 	CleanupStack::PushL(self);
       
    46 	self->ConstructL();
       
    47 	CleanupStack::Pop(self);
       
    48 	return self;
       
    49 	}
       
    50 
       
    51 /**
       
    52 Secondary construction
       
    53 */	
       
    54 void CImapIdleController::ConstructL()
       
    55 	{
       
    56 	iReissueTimer = CImapObservableTimer::NewL(*this);
       
    57 
       
    58 	// Add to the active scheduler
       
    59 	CActiveScheduler::Add(this);
       
    60 	}
       
    61 
       
    62 /**
       
    63 Requests idle in the inbox, using the imap session object
       
    64 passed at construction.
       
    65 Completes synchronously. The IDLE state is entered 
       
    66 asynchronously in the background.
       
    67 */
       
    68 void CImapIdleController::StartIdle()
       
    69 	{
       
    70 	__ASSERT_DEBUG(iSession!=NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleSessionIsNull));
       
    71 	__LOG_TEXT(iSession->LogId(), "CImapIdleController::StartIdle()");
       
    72 	__ASSERT_DEBUG(iState==EFinished, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleControllerUnexpectedStateAtStart));
       
    73 	__ASSERT_DEBUG(!IsActive(), TImapServerPanic::ImapPanic(TImapServerPanic::EIdleControllerIsActiveAtStart));
       
    74 
       
    75 	// Check whether we are going to use IDLE or issue a "dummy read"
       
    76 	TBool idleSupported=iSession->ImapIdleSupported();
       
    77 	TBool idleEnabled=iImapSettings.ImapIdle();
       
    78 	iUsingIdle = idleSupported && idleEnabled;
       
    79 	
       
    80 	iState = EStartingIdle;
       
    81 	iStoppingIdle = EFalse;
       
    82 	
       
    83 	// Complete self to enter state machine
       
    84 	TRequestStatus* status = &iStatus;
       
    85 	User::RequestComplete(status, KErrNone);
       
    86 	SetActive();
       
    87 	}
       
    88 
       
    89 
       
    90 /**
       
    91 Asynchronously stops the outstanding idle request.
       
    92 
       
    93 @param aStatus - Signals completion of the request
       
    94 */
       
    95 void CImapIdleController::StopIdle(TRequestStatus& aStatus)
       
    96 	{
       
    97 	__LOG_FORMAT((iSession->LogId(), "CImapIdleController::StopIdle() State = %d", iState));
       
    98 
       
    99 	iStoppingIdle = ETrue;
       
   100 	Queue(aStatus);
       
   101 	switch (iState)
       
   102 		{
       
   103 	case EFinished:
       
   104 		{
       
   105 		// not in IDLE state, complete immediately
       
   106 		Complete(KErrNone);
       
   107 		break;
       
   108 		}
       
   109 
       
   110 	case EIdling:
       
   111 		{
       
   112 		// currently idling - cancel the outstanding request
       
   113 		// then issue the DONE command to the remote server.
       
   114 		InternalCancelAndSetState(EWaitingForDone);
       
   115 		iSession->DoneIdle(iStatus);
       
   116 		SetActive();
       
   117 		break;
       
   118 		}
       
   119 	
       
   120 	case EWaitingForServerEvent:
       
   121 		{
       
   122 		// "Dummy Read" previously issued. This needs to be
       
   123 		// cancelled and flushed before continuing.
       
   124 		InternalCancelAndSetState(EWaitingForFlush);
       
   125 		iSession->FlushCancelledCommand(iStatus);
       
   126 		SetActive();
       
   127 		break;
       
   128 		}
       
   129 
       
   130 	case ESyncFetching:
       
   131 		{
       
   132 		// bring the download-rules controlled fetch to a
       
   133 		// premature halt... The message currently being fetched
       
   134 		// will not be resumed on next rules-based sync
       
   135 		__LOG_TEXT(iSession->LogId(), "CImapIdleController: Stopping a rules-based fetch to allow requested operation");
       
   136 		InternalCancelAndSetState(EWaitingForFlush);
       
   137 		iSession->FlushCancelledCommand(iStatus);
       
   138 		SetActive();
       
   139 		break;
       
   140 		}
       
   141 	
       
   142 	case EStartingIdle:
       
   143 	case ESelectingInbox:
       
   144 	case EWaitingForContinuation:
       
   145 	case EWaitingForFlush:
       
   146 	case EWaitingForDone:
       
   147 	case ESynchronising:
       
   148 	default:
       
   149 		{
       
   150 		// no action required, stop request will be completed
       
   151 		// when the appropriate state reached.
       
   152 		break;
       
   153 		}
       
   154 		} // end switch (iState)
       
   155 	}
       
   156 
       
   157 
       
   158 /**
       
   159 Called when an asynchronous service request completes.
       
   160 */
       
   161 void CImapIdleController::RunL()
       
   162 	{
       
   163 	// RunL() should never be called while doing an internal cancel
       
   164 	__ASSERT_DEBUG(iInternalCancel==EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleControllerRunLCalledDuringInternalCancel));
       
   165 	
       
   166 	TInt status = iStatus.Int();
       
   167 	if (status!=KErrNone)
       
   168 		{
       
   169 		// Something has gone wrong.
       
   170 		// Tell the Protocol Controller about it.
       
   171 		if (iReport==NULL)
       
   172 			{
       
   173 			// error must have occurred while in IDLE. Inform the idle
       
   174 			// observer. Otherwise errors are reported via Complete().
       
   175 			iObserver.OnIdleError(status);
       
   176 
       
   177 			// We need to exit immediately because reporting the error to the
       
   178 			// protocol controller will mean that it deletes us.
       
   179 			return;
       
   180 			}
       
   181 		}
       
   182 	else
       
   183 		{
       
   184 		TRAPD(error, DoRunL());
       
   185 		__ASSERT_DEBUG(error==KErrNone || !IsActive(), TImapServerPanic::ImapPanic(TImapServerPanic::EIdleControllerLeaveInDoRunL));
       
   186 		if (IsActive())
       
   187 			{
       
   188 			// requeued
       
   189 			return;
       
   190 			}
       
   191 		status=error;
       
   192 		}
       
   193 	Complete(status);
       
   194 	}
       
   195 
       
   196 void CImapIdleController::DoRunL()
       
   197 	{
       
   198 	if (!iStoppingIdle)
       
   199 		{
       
   200 		RunContinueL();
       
   201 		}
       
   202 	else
       
   203 		{
       
   204 		// Stop request has been received
       
   205 		RunStopL();
       
   206 		}
       
   207 	}
       
   208 
       
   209 /**
       
   210 Continue the idle state machine
       
   211 */
       
   212 void CImapIdleController::RunContinueL()
       
   213 	{
       
   214 	__LOG_FORMAT((iSession->LogId(), "CImapIdleController::RunContinueL() State = %d", iState));
       
   215 	switch (iState)
       
   216 		{
       
   217 		case EStartingIdle:
       
   218 			{
       
   219 			// state machine entry point - select the inbox.
       
   220 			iSyncManager.Inbox()->SelectL(iStatus, *iSession);
       
   221 			iState = ESelectingInbox;
       
   222 			break;
       
   223 			}
       
   224 
       
   225 		case ESelectingInbox:
       
   226 			{
       
   227 			// inbox selected
       
   228 			if (SyncRequired())
       
   229 				{
       
   230 				// inbox has changed since last sync, kick off sync
       
   231 				iSyncManager.Inbox()->SynchroniseL(iStatus, *iSession, EFalse, ETrue);
       
   232 				iState = ESynchronising;
       
   233 				}
       
   234 			// resume any fetch operation if there is anything to do...
       
   235 			else if (!DoBodyDownloadL())
       
   236 				{
       
   237 				// otherwise, issue the idle command
       
   238 				GoIdleL();
       
   239 				}
       
   240 			break;
       
   241 			}
       
   242 
       
   243 		case EWaitingForContinuation:
       
   244 			{
       
   245 			// idle continuation response received
       
   246 			if (SyncRequired())
       
   247 				{
       
   248 				// if the folder wants to sync, call DONE first then sync
       
   249 				iSession->DoneIdle(iStatus);
       
   250 				iState = EWaitingForDone;
       
   251 				}
       
   252 			else
       
   253 				{
       
   254 				EnterIdlingState();
       
   255 				}
       
   256 			break;
       
   257 			}
       
   258 		
       
   259 		case EIdling:
       
   260 			{
       
   261 			// idling event has occurred
       
   262 			if (SyncRequired())
       
   263 				{
       
   264 				// if the folder wants to sync, call DONE first then sync
       
   265 				iSession->DoneIdle(iStatus);
       
   266 				iState = EWaitingForDone;
       
   267 				iReissueTimer->Cancel();
       
   268 				}
       
   269 			else
       
   270 				{
       
   271 				// otherwise, re-issue WaitForIdleEvent
       
   272 				iSession->WaitForIdleEvent(iStatus);
       
   273 				iState = EIdling;
       
   274 				}
       
   275 			break;
       
   276 			}
       
   277 
       
   278 		case EWaitingForServerEvent:
       
   279 			{
       
   280 			// an unsolicited event has occurred
       
   281 			if (SyncRequired())
       
   282 				{
       
   283 				// idling event has occurred
       
   284 				iSyncManager.Inbox()->SynchroniseL(iStatus, *iSession, EFalse, ETrue);
       
   285 				iState = ESynchronising;
       
   286 				iReissueTimer->Cancel();
       
   287 				}
       
   288 			else
       
   289 				{
       
   290 				// otherwise, return to dummy read
       
   291 				GoIdleL();
       
   292 				}
       
   293 			break;
       
   294 			}
       
   295 
       
   296 		case EWaitingForFlush:
       
   297 		case EWaitingForDone:
       
   298 			{
       
   299 			// waiting for done/flush but not stopping: either an event has
       
   300 			// occurred in IDLE or the reissue timer must have expired.
       
   301 			if (SyncRequired())
       
   302 				{
       
   303 				// idling event has occurred
       
   304 				iSyncManager.Inbox()->SynchroniseL(iStatus, *iSession, EFalse, ETrue);
       
   305 				iState = ESynchronising;
       
   306 				}
       
   307 			else
       
   308 				{
       
   309 				// No change - re-issue idle. 
       
   310 				// This will occur if the idle timer has expired.
       
   311 				GoIdleL();			
       
   312 				}
       
   313 			break;
       
   314 			}
       
   315 
       
   316 		case ESynchronising:
       
   317 			{
       
   318 			// 1st phase (header) synchronise has completed, do content fetch if configured.
       
   319 			if (!DoBodyDownloadL())
       
   320 				{
       
   321 				// content fetch not configured or nothing to fetch
       
   322 				// Sync any changes, or go idle.
       
   323 				if (SyncRequired())
       
   324 					{
       
   325 					// an event seems to have happened during the sync - sync again
       
   326 					iSyncManager.Inbox()->SynchroniseL(iStatus, *iSession, EFalse, ETrue);
       
   327 					iState = ESynchronising;
       
   328 					}
       
   329 				else
       
   330 					{
       
   331 					// otherwise, re-issue the idle command
       
   332 					GoIdleL();
       
   333 					}
       
   334 				}
       
   335 			break;
       
   336 			}
       
   337 
       
   338 		case ESyncFetching:
       
   339 			{
       
   340 			// 2nd phase synchronise has completed,
       
   341 			delete iCopyToLocal;
       
   342 			iCopyToLocal = NULL;
       
   343 			if (SyncRequired())
       
   344 				{
       
   345 				// an event seems to have happened during the sync - sync again
       
   346 				iSyncManager.Inbox()->SynchroniseL(iStatus, *iSession, EFalse, ETrue);
       
   347 				iState = ESynchronising;
       
   348 				}
       
   349 			else
       
   350 				{
       
   351 				// otherwise, re-issue the idle command
       
   352 				GoIdleL();
       
   353 				}
       
   354 			break;
       
   355 			}
       
   356 
       
   357 		case EFinished:
       
   358 		default:
       
   359 			{
       
   360 			// these are unexpected states.
       
   361 			return;
       
   362 			}
       
   363 		} // end switch (iState)
       
   364 	SetActive();
       
   365 	}
       
   366 
       
   367 
       
   368 /**
       
   369 Starts second-phase download of body content according to provisioned
       
   370 download rules, if enabled, rules defined and any messages to fetch.
       
   371 Otherwise, causes the CImapIdleController object to self-complete.
       
   372 
       
   373 @return ETrue if body content download has been started. 
       
   374         SetActive() must be called following an ETrue return
       
   375 */
       
   376 TBool CImapIdleController::DoBodyDownloadL()
       
   377 	{
       
   378 	TBool fetchStarted = EFalse;
       
   379 	// are download rules being used? otherwise, self complete.
       
   380 	if (iImapSettings.UseSyncDownloadRules())
       
   381 		{
       
   382 		// are rules defined for the inbox? otherwise, self complete
       
   383 		TImImap4GetPartialMailInfo mailInfo;
       
   384 		if (KErrNotFound != iImapSettings.GetSyncDownloadRuleL(CImapSyncDownloadRules::EInboxRulesType, mailInfo))
       
   385 			{
       
   386 			// build a selection of messages to fetch
       
   387 			CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
       
   388 			CleanupStack::PushL(selection);
       
   389 			
       
   390 			// anything to fetch? otherwise, self complete
       
   391 			TInt msgsToFetch = iSyncManager.Inbox()->GetFetchMessageChildrenL(*selection);
       
   392 			if (msgsToFetch>0)
       
   393 				{
       
   394 				__LOG_FORMAT((iSession->LogId(), "CImapIdleController: Starting rules-based Fetch of %d messages", msgsToFetch));
       
   395 				delete iCopyToLocal;
       
   396 				iCopyToLocal = NULL;
       
   397 				// Create the compound operation object
       
   398 				iCopyToLocal = CImapCompoundCopyToLocal::NewL(iSyncManager, 
       
   399 				                    			               iServerEntry, 
       
   400 				                                			   iImapSettings,
       
   401 				                                			   iImapMailStore, 
       
   402 				                                               EFalse,
       
   403 				                                               *selection,
       
   404 				                                               KMsvNullIndexEntryId,
       
   405 				                                               mailInfo);
       
   406 				iCopyToLocal->StartOperation(iStatus, *iSession);
       
   407 				iState = ESyncFetching;
       
   408 				fetchStarted = ETrue;
       
   409 				}
       
   410 			CleanupStack::PopAndDestroy(selection);
       
   411 			}
       
   412 		}
       
   413 	return fetchStarted;
       
   414 	}
       
   415 	
       
   416 /**
       
   417 If IDLE is supported and configured for use, then the IDLE command is
       
   418 issued to the server. Otherwise the session is configured to perform
       
   419 a read of any information that arrives at the server. 
       
   420 SetActive() must be called after calling this method.
       
   421 */
       
   422 void CImapIdleController::GoIdleL()
       
   423 	{
       
   424 	if (iUsingIdle)
       
   425 		{
       
   426 		__LOG_TEXT(iSession->LogId(), "CImapIdleController: Using IDLE");
       
   427 		// issue the idle command
       
   428 		iSession->EnterIdleL(iStatus);
       
   429 		iState = EWaitingForContinuation;
       
   430 		}
       
   431 	else
       
   432 		{
       
   433 		// Otherwise, request notification of unsolicited server events
       
   434 		// This is referred to as a "dummy read".
       
   435 		__LOG_TEXT(iSession->LogId(), "CImapIdleController: Using Dummy Read");
       
   436 		iSession->WaitForServerEventL(iStatus);
       
   437 		iState = EWaitingForServerEvent;
       
   438 		}
       
   439 	}
       
   440 
       
   441 
       
   442 /**
       
   443 Stop the idle state machine
       
   444 */
       
   445 void CImapIdleController::RunStopL()
       
   446 	{
       
   447 	__LOG_FORMAT((iSession->LogId(), "CImapIdleController::RunStopL() State = %d", iState));
       
   448 	switch (iState)
       
   449 		{
       
   450 	case EWaitingForContinuation:
       
   451 		{
       
   452 		// StopIdle() was called after the IDLE was issued, but before
       
   453 		// the continuation response had been received.
       
   454 		// Need to cancel the IDLE state and issue the DONE command;
       
   455 		InternalCancelAndSetState(EWaitingForDone);
       
   456 		iSession->DoneIdle(iStatus);
       
   457 		SetActive();
       
   458 		break;
       
   459 		}
       
   460 
       
   461 	case EWaitingForServerEvent:
       
   462 		{
       
   463 		// Cancel the "dummy read" request and flush any data in the pipes
       
   464 		InternalCancelAndSetState(EWaitingForFlush);
       
   465 		iSession->FlushCancelledCommand(iStatus);
       
   466 		SetActive();
       
   467 		break;
       
   468 		}
       
   469 
       
   470 	case EFinished:			// unexpected
       
   471 	case EStartingIdle:		// almost possible
       
   472 	case ESelectingInbox:	// possible (StopIdle() called in ESelectingInbox)
       
   473 	case EWaitingForFlush:	// expected (StopIdle() called in EWaitingForServerEvent)
       
   474 	case EWaitingForDone:	// expected (StopIdle() called in EIdling)
       
   475 	case EIdling:			// unexpected (state exited in StopIdle())
       
   476 	case ESynchronising:	// possible (StopIdle() called in ESynchronising)
       
   477 	case ESyncFetching:     // possible StopIdle() while doing bg fetch
       
   478 	default:
       
   479 		{
       
   480 		// Nothing more needs to be done in these states - the session is
       
   481 		// now ready to be used.
       
   482 		__LOG_TEXT(iSession->LogId(), "CImapIdleController::Idle stopped OK");
       
   483 		iState=EFinished;
       
   484 		Complete(KErrNone);
       
   485 		break;
       
   486 		}
       
   487 		} // end switch (iState)
       
   488 	}
       
   489 	
       
   490 
       
   491 /**
       
   492 Called by Cancel() to cancel asynchronous service requests
       
   493 */
       
   494 void CImapIdleController::DoCancel()
       
   495 	{
       
   496 	iReissueTimer->Cancel();
       
   497 
       
   498 	switch (iState)
       
   499 		{
       
   500 		case EIdling:
       
   501 		case ESelectingInbox:
       
   502 		case EWaitingForContinuation:
       
   503 		case EWaitingForServerEvent:
       
   504 		case EWaitingForFlush:
       
   505 		case EWaitingForDone:
       
   506 			{
       
   507 			// outstanding request is on the imap session
       
   508 			iSession->Cancel();
       
   509 			break;
       
   510 			}
       
   511 		case ESynchronising:
       
   512 			{
       
   513 			// outstanding request is on the folder
       
   514 			iSyncManager.Inbox()->Cancel();
       
   515 			break;
       
   516 			}
       
   517 		case ESyncFetching:
       
   518 			{
       
   519 			if (iCopyToLocal)
       
   520 				{
       
   521 				iCopyToLocal->CancelEnableResume();
       
   522 				delete iCopyToLocal;
       
   523 				iCopyToLocal = NULL;
       
   524 				break;
       
   525 				}
       
   526 			}
       
   527 		case EStartingIdle:
       
   528 		case EFinished:
       
   529 		default:
       
   530 			{
       
   531 			// self-completed or no outstanding request.
       
   532 			break;
       
   533 			}
       
   534 		} // end switch (iState)
       
   535 		
       
   536 	if (!iInternalCancel)
       
   537 		{
       
   538 		// Notify any waiting active object that the asynchronous operation they were waiting on has been cancelled.
       
   539 		// Note that we only do this if Cancel() was called externally.  
       
   540 		//
       
   541 		// Internal Cancel's() that are used for switching from one internal operation to another should not 
       
   542 		// call Complete, as the external overall operation has not actually completed.
       
   543 		Complete(KErrCancel);	
       
   544 		__LOG_TEXT(KDefaultLog, "CImapIdleController: Cancelled EXTERNALLY while active");
       
   545 		
       
   546 		// No async ops in progress so reset the state
       
   547 		// Note that InternalCancelAndSetState() will set the state for EInternalCancel.
       
   548 		iState = EFinished;
       
   549 		}
       
   550 	}
       
   551 
       
   552 void CImapIdleController::Queue(TRequestStatus& aStatus)
       
   553 	{
       
   554 	__ASSERT_DEBUG(iReport==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleControllerAlreadyInUse));
       
   555 
       
   556 	aStatus=KRequestPending;
       
   557 	iReport=&aStatus;
       
   558 	}
       
   559 
       
   560 void CImapIdleController::Complete(TInt aErr)
       
   561 	{
       
   562 	if (iReport)
       
   563 		{
       
   564 		// only complete once
       
   565 		DoComplete(aErr);
       
   566 		User::RequestComplete(iReport, aErr);
       
   567 		}
       
   568 	}
       
   569 
       
   570 #ifdef __IMAP_LOGGING
       
   571 void CImapIdleController::DoComplete(TInt& aErr)
       
   572 #else
       
   573 void CImapIdleController::DoComplete(TInt& /*aErr*/)
       
   574 #endif //__IMAP_LOGGING
       
   575 	{
       
   576 	__LOG_FORMAT((iSession->LogId(), "CImapIdleController::DoComplete(aErr = %d) State = %d", aErr, iState));
       
   577 	}
       
   578 
       
   579 void CImapIdleController::OnTimerL(const CImapObservableTimer& /*aSourceTimer*/)
       
   580 	{
       
   581 	// The Idle reissue timer has expired.
       
   582 	// If we are currently idling, we need to restart the idle.
       
   583 	__LOG_FORMAT((iSession->LogId(), "CImapIdleController: Reissue idle timer expired. State = %d", iState));
       
   584 	if (iState == EIdling)
       
   585 		{
       
   586 		InternalCancelAndSetState(EWaitingForDone);
       
   587 		iSession->DoneIdle(iStatus);
       
   588 		SetActive();
       
   589 		}
       
   590 	else if (iState == EWaitingForServerEvent)
       
   591 		{
       
   592 		InternalCancelAndSetState(EWaitingForFlush);
       
   593 		iSession->FlushCancelledCommand(iStatus);
       
   594 		SetActive();
       
   595 		}
       
   596 	}
       
   597 
       
   598 void CImapIdleController::EnterIdlingState()
       
   599 	{
       
   600 	__LOG_FORMAT((iSession->LogId(), "CImapIdleController: Entering idle state. Reissue timeout = %d seconds", iImapSettings.ImapIdleTimeout()));
       
   601 
       
   602 	iReissueTimer->Cancel();
       
   603 	iReissueTimer->After(iImapSettings.ImapIdleTimeout() * 1000000);
       
   604 
       
   605 	iSession->WaitForIdleEvent(iStatus);
       
   606 	iState = EIdling;
       
   607 	}
       
   608 
       
   609 /**
       
   610 When internally switching from one async operation to another, it is necessary to perform a Cancel().
       
   611 But when we do this, we don't want to inform the waiting iReport, as the overall operation that it 
       
   612 requested has not itself been cancelled.
       
   613 So this method temporarily sets the state to EInternalCancel, then calls Cancel() - whose DoCancel()
       
   614 knows not to call Complete for this state.  Finally, the state is switched to the state for the next 
       
   615 internal operation.
       
   616 Note that this all happens synchronously, so there is no chance of RunL() being called while the state 
       
   617 is set EInternalCancel
       
   618 @param aNextState the state associated with the next operation.
       
   619 */
       
   620 void CImapIdleController::InternalCancelAndSetState(TIdleControllerState aNextState)
       
   621 	{
       
   622 	// These states should not cause iSesson->Cancel() to be called within DoCancel()
       
   623 	__ASSERT_DEBUG(iState != ESynchronising && iState != EStartingIdle && iState != EFinished, TImapServerPanic::ImapPanic(TImapServerPanic::EIdleControllerInternalCancelOnBadState));
       
   624 	
       
   625 	// Ensure that imminent call to DoCancel() dose not cause iReport to be Completed
       
   626 	iInternalCancel = ETrue;
       
   627 	Cancel();
       
   628 	iInternalCancel = EFalse;
       
   629 	// Now set the real state
       
   630 	iState=aNextState;
       
   631 	}
       
   632 
       
   633 /**
       
   634 Returns true if IDLE is enabled and a change has been detected in the contents of the inbox.
       
   635 */
       
   636 TBool CImapIdleController::SyncRequired()
       
   637 	{
       
   638 	if (!iImapSettings.ImapIdle())
       
   639 		{
       
   640 		return EFalse;
       
   641 		}
       
   642 	if (!iSyncManager.Inbox()->Changed(*iSession))
       
   643 		{
       
   644 		return EFalse;
       
   645 		}
       
   646 	return ETrue;
       
   647 	}