email/imap4mtm/imapprotocolcontroller/src/cimapcompoundcopytolocal.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 "cimapcompoundcopytolocal.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 "imappaniccodes.h"
       
    24 #include "cimapfolderinfo.h"
       
    25 #include <imapset.h>
       
    26 
       
    27 #include "mobilitytestmtmapi.h"
       
    28 
       
    29 _LIT8(KMessageDataItem, "+FLAGS");
       
    30 _LIT8(KDeleteValue,	 "\\Deleted");
       
    31 
       
    32 CImapCompoundCopyToLocal::CImapCompoundCopyToLocal(CImapSyncManager& aSyncManager, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, TBool aIsMove, const TMsvId aDestination)
       
    33  : CImapCompoundBase(aSyncManager, aServerEntry, aImapSettings), 
       
    34    iMailStore(aImapMailStore), iIsMove(aIsMove), iDestinationFolderId(aDestination)
       
    35 	{
       
    36 	iPopulateCommand = EFalse;
       
    37 	UpdatePartialMailInfoToDefaults(aDestination);
       
    38 	}
       
    39 
       
    40 CImapCompoundCopyToLocal::CImapCompoundCopyToLocal(CImapSyncManager& aSyncManager, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, TBool aIsMove, const TMsvId aDestination, const TImImap4GetPartialMailInfo& aGetPartialMailInfo)
       
    41  : CImapCompoundBase(aSyncManager, aServerEntry, aImapSettings), 
       
    42    iMailStore(aImapMailStore), iIsMove(aIsMove), iDestinationFolderId(aDestination), iGetPartialMailInfo(aGetPartialMailInfo)
       
    43 	{
       
    44 	iPopulateCommand = ETrue;
       
    45 	}
       
    46 
       
    47 CImapCompoundCopyToLocal::~CImapCompoundCopyToLocal()
       
    48 	{
       
    49 	iOutMessageFlagInfo.Close();
       
    50 	delete iSourceSel;
       
    51 	delete iBodyFetcher;
       
    52 	delete iMoveEntry;
       
    53 	}
       
    54 	
       
    55 CImapCompoundCopyToLocal* CImapCompoundCopyToLocal::NewL(CImapSyncManager& aSyncManager,
       
    56 														 CMsvServerEntry& aServerEntry, 
       
    57 														 CImapSettings& aImapSettings, 
       
    58 														 CImapMailStore& aImapMailStore, 
       
    59 														 TBool aIsMove, 
       
    60 														 const CMsvEntrySelection& aSourceSel,
       
    61 														 const TMsvId aDestination)
       
    62 	{
       
    63 	CImapCompoundCopyToLocal* self = new (ELeave) CImapCompoundCopyToLocal(aSyncManager, aServerEntry, aImapSettings, aImapMailStore, aIsMove, aDestination);
       
    64 	CleanupStack::PushL(self);
       
    65 	self->ConstructL(aSourceSel);
       
    66 	CleanupStack::Pop(self);
       
    67 	return self;
       
    68 	}
       
    69 
       
    70 CImapCompoundCopyToLocal* CImapCompoundCopyToLocal::NewL(CImapSyncManager& aSyncManager, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, TBool aIsMove, const CMsvEntrySelection& aSourceSel, const TMsvId aDestination, const TImImap4GetPartialMailInfo& aGetPartialMailInfo)
       
    71 	{
       
    72 	CImapCompoundCopyToLocal* self = new (ELeave) CImapCompoundCopyToLocal(aSyncManager, aServerEntry, aImapSettings, aImapMailStore, aIsMove, aDestination, aGetPartialMailInfo);
       
    73 	CleanupStack::PushL(self);
       
    74 	self->ConstructL(aSourceSel);
       
    75 	CleanupStack::Pop(self);
       
    76 	return self;
       
    77 	}
       
    78 	
       
    79 /**
       
    80 Secondary construction
       
    81 */	
       
    82 void CImapCompoundCopyToLocal::ConstructL(const CMsvEntrySelection& aSourceSel)
       
    83 	{
       
    84 	// Local copy of the selection of messages to copy
       
    85 	iSourceSel=new (ELeave) CMsvEntrySelection;
       
    86 	
       
    87 	// Check the source selection for acceptable message types/parts
       
    88 	// Messages         True
       
    89 	// Handle Parts     True
       
    90 	// Handle Folders   False
       
    91 	// Check source     True
       
    92 	// Makes a local copy of the source selection in iSourceSel
       
    93 	User::LeaveIfError(CheckSelectionL(aSourceSel, iSourceSel, ETrue, ETrue, EFalse, ETrue));
       
    94 	
       
    95 	// Create an Imap Body Fetcher object
       
    96 	iBodyFetcher = CImapOpFetchBody::NewL(iSession, iSyncManager, iServerEntry, iImapSettings, iMailStore);
       
    97 	// initialise progress stats
       
    98 	iMessageSelection = iSelectionStillToCopy = iSourceSel->Count();
       
    99 	iTotalSize = CalculateDownloadSizeL(*iSourceSel);
       
   100 	
       
   101 	// Add to the active scheduler
       
   102 	CActiveScheduler::Add(this);
       
   103 	}
       
   104 
       
   105 void CImapCompoundCopyToLocal::StartOperation(TRequestStatus& aStatus, CImapSession& aSession)
       
   106 	{
       
   107 	iSession = &aSession;
       
   108 	__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::StartOperation()");
       
   109 	iNextStep = ESetSourceMailbox;
       
   110 	Queue(aStatus);
       
   111 	CompleteSelf();
       
   112 	}
       
   113 
       
   114 /**
       
   115 Handles the compound operation state machine
       
   116 
       
   117 Sequence of steps:
       
   118 	For each message:
       
   119 		Select source mailbox
       
   120 		Fetch the message to the local mirror
       
   121 	IF copy/move to local service
       
   122 		Copy message to local service
       
   123 		IF move to local service
       
   124 			STORE /deleted flag on remote message
       
   125 			issue EXPUNGE command
       
   126 			delete local copy of the message
       
   127 	
       
   128 	
       
   129 @return ETrue if compound operation is completed, 
       
   130 		EFalse otherwise.
       
   131 */	
       
   132 TBool CImapCompoundCopyToLocal::DoRunLoopL()
       
   133 	{
       
   134 	SetCurrentStep();
       
   135 	switch (iCurrentStep)
       
   136 		{
       
   137 		case ESetSourceMailbox: // synchronous
       
   138 			{
       
   139 			__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(ESetSourceMailbox)");
       
   140 			MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal1); // SELECT source folder
       
   141 
       
   142 			if (iSelectionStillToCopy > 0)
       
   143 				{
       
   144 				if (iStopForMigrate)
       
   145 					{
       
   146 					__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::Stopped for migrate");
       
   147 					iCurrentStep = ESuspendedForMigrate;
       
   148 					iNextStep = ESetSourceMailbox;
       
   149 					Complete(KErrNone);
       
   150 					return ETrue;
       
   151 					}
       
   152 				
       
   153 				// decrement iSelectionStillToCopy - it becomes the index in the list 
       
   154 				// of the current message to operate on.
       
   155 				--iSelectionStillToCopy;
       
   156 					
       
   157 				iCurrentMsgId = (*iSourceSel)[iSelectionStillToCopy];
       
   158 				if (SetSourceFolderL(iCurrentMsgId))
       
   159 					{
       
   160 					// no change in state if folder not acceptable,
       
   161 					// try again with the next message in the selection.
       
   162 					if (iSourceFolderId != NULL && iSourceFolder != NULL)
       
   163 						{
       
   164 						// When iIsMove is EFalse, we still need to have a writable mailbox 
       
   165 						// so that the server can update the \Seen message flag duriung FETCH BODY
       
   166 						
       
   167 						iNextStep=ESelectSourceMailboxRW;
       
   168 						}
       
   169 					}
       
   170 				else
       
   171 					{
       
   172 					iNextStep=EFetchMessage;
       
   173 					}
       
   174 				}
       
   175 			else
       
   176 				{
       
   177 				// All messages copied.. 
       
   178 				iNextStep=EFinished;
       
   179 				}
       
   180 			// immediately proceed to the next step
       
   181 			return EFalse;
       
   182 			}
       
   183 			
       
   184 		case ESelectSourceMailboxRW: // asynchronous
       
   185 			{
       
   186 			__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(ESelectSourceMailboxRW)");
       
   187 			iSourceFolder->SelectL(iStatus, *iSession);
       
   188 			iNextStep=EFetchMessage;
       
   189 			SetActive();
       
   190 			break;
       
   191 			}
       
   192 
       
   193 		case ESelectSourceMailboxRO: // asynchronous
       
   194 			{
       
   195 			__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(ESelectSourceMailboxRO)");
       
   196 			iSourceFolder->ExamineL(iStatus, *iSession);
       
   197 			iNextStep=EFetchMessage;
       
   198 			SetActive();
       
   199 			break;
       
   200 			}
       
   201 			
       
   202 		case EFetchMessage:	// asynchronous
       
   203 			{
       
   204 			__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(EFetchMessage)");
       
   205 			MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal2); // FETCHing the message
       
   206 			SetEntryL(iCurrentMsgId);
       
   207 			TMsvEmailEntry entry = static_cast<TMsvEmailEntry> (iServerEntry.Entry()); 
       
   208 
       
   209 			TBool doFetch(ETrue);
       
   210 
       
   211 			// Don't fetch the message if it is marked for delete
       
   212 			if (entry.DisconnectedOperation() == EDisconnectedDeleteOperation)
       
   213 				{
       
   214 				doFetch = EFalse;
       
   215 				iNextStep = ESetSourceMailbox;
       
   216 				return EFalse;
       
   217 				}
       
   218 			else
       
   219 				{
       
   220 				// Don't attempt to fetch the message if it has either been fully or 
       
   221 				// partially downloaded, unless this is a populate command.
       
   222 				if (entry.Complete() && entry.PartialDownloaded() && !iPopulateCommand)
       
   223 					{
       
   224 					doFetch = EFalse;
       
   225 					}
       
   226 				}
       
   227 
       
   228 			if (doFetch)
       
   229 				{
       
   230 				// Fetch the message...
       
   231 				iBodyFetcher->FetchBodyL(iStatus, iCurrentMsgId, iGetPartialMailInfo);
       
   232 				SetActive();
       
   233 				}
       
   234 
       
   235 			// Set next state
       
   236 			if (iDestinationFolderId == KMsvNullIndexEntryId)
       
   237 				{
       
   238 				// Populating/Copying to local mirror. When fetched, proceed to next
       
   239 				// message in the selection
       
   240 				iNextStep=ESetSourceMailbox;
       
   241 				}
       
   242 			else
       
   243 				{
       
   244 				iNextStep=(iIsMove)?EInboxDuplicateMove:EInboxDuplicateCopy;
       
   245 				}
       
   246 			break;
       
   247 			}
       
   248 		
       
   249 		case EInboxDuplicateCopy: // asynchronous
       
   250 		case EInboxDuplicateMove: // asynchronous
       
   251 			{
       
   252 			__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(EInboxDuplicate)");
       
   253 			MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal3); // local async copy
       
   254 			SetEntryL(iCurrentMsgId);
       
   255 			iNextStep=ELocalCopyComplete;
       
   256 
       
   257 			// CopyMessage() kicks off an asynchronous copy operation if it succeeds
       
   258 			TInt ret = CopyMessage(iCurrentMsgId, iDestinationFolderId, iIsMove);
       
   259 
       
   260 			if (ret != KErrNone) // Asynchronous copy operation failed to start
       
   261 				{
       
   262 				// Store the error in the message entry
       
   263 				TRAPD(err, MessageErrorL(iCurrentMsgId, ret));
       
   264 				if (err != KErrNone)
       
   265 					{
       
   266 					iProgressErrorCode = err;
       
   267 					}
       
   268 
       
   269 				// Move straight on to next step
       
   270 				iNextStep = ESetSourceMailbox;
       
   271 
       
   272 				return EFalse;
       
   273 				}
       
   274 			break;
       
   275 			}
       
   276 
       
   277 		case ELocalCopyComplete: // synchronous
       
   278 			{
       
   279 			__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(ELocalCopyComplete)");
       
   280 			// The copy to local folder has completed.
       
   281 			CopyCompleteL();
       
   282 			// Delete the source message from the IMAP Service if it is a move operation, 
       
   283 			// otherwise proceed to next message in the selection.
       
   284 			iNextStep=(iIsMove)?EDeleteMessage:ESetSourceMailbox;
       
   285 			// straight on to next step...
       
   286 			return EFalse;
       
   287 			}
       
   288 
       
   289 		case EDeleteMessage: // asynchronous
       
   290 			{
       
   291 			__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(EDeleteMessage)");
       
   292 			MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal4); // STORE /deleted flag (move only)
       
   293 			// Deleting a message is a two-stage operation:
       
   294 			// 1. STORE the /deleted flag on the remote server.
       
   295 			// 2.a) EXPUNGE the messages 
       
   296 			//	or
       
   297 			// 2.b) CLOSE the folder and then SELECT the folder
       
   298 			// 2a) or 2b) used is dependent on the KImap4EmailExpungeFlag in CImImap4Settings.
       
   299 			// we haven't changed the TMsvId of moved messages,
       
   300 			// therefore can just use the original TmsvId to identify the UID.
       
   301 			MarkMessageForDeleteL(iCurrentMsgId);
       
   302 
       
   303 			if (iImapSettings.UseExpunge())
       
   304 				{
       
   305 				iNextStep=EExpunge;
       
   306 				}
       
   307 			else
       
   308 				{
       
   309 				iNextStep=ECloseFolder;
       
   310 				}
       
   311 			SetActive();
       
   312 			break;
       
   313 			}
       
   314 
       
   315 		case EExpunge: // asynchronous
       
   316 			{
       
   317 			__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(EExpunge)");
       
   318 			MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal5); // EXPUNGE
       
   319 			// Issue expunge to delete the message now.
       
   320 			iSession->ExpungeL(iStatus);
       
   321 			// This is the end of a move operation - move to EFetchMessage state
       
   322 			// to check for more messages to move.
       
   323 			iNextStep=ESetSourceMailbox;
       
   324 			SetActive();
       
   325 			break;
       
   326 			}
       
   327 
       
   328 		case ECloseFolder:
       
   329 			{
       
   330 			__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(ECloseFolder)");
       
   331 			MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal6); // CLOSE FOLDER
       
   332 			//Permanently removes all messages that have the deleted flag set
       
   333 			//from the currently selected mailbox
       
   334 			iSession->CloseL(iStatus);
       
   335 			iNextStep=ESelectFolderAfterClose;
       
   336 			SetActive();
       
   337 			break;
       
   338 			}
       
   339 
       
   340 		case ESelectFolderAfterClose:
       
   341 			{
       
   342 			__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(ESelectFolderAfterClose)");
       
   343 			MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapCopyToLocal7); // SELECT FOLDER
       
   344 			//Selecting a mailbox so that messages in the mailbox can be accessed. 
       
   345 			iSourceFolder->SelectL(iStatus, *iSession);
       
   346 			iNextStep=ESetSourceMailbox;
       
   347 			SetActive();
       
   348 			break;
       
   349 			}
       
   350 
       
   351 		case EFinished:
       
   352 			{
       
   353 			__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::DoRunLoopL(EFinished OK)");
       
   354 			iProgressState = TImap4GenericProgress::EIdle;
       
   355 			Complete(KErrNone);
       
   356 			return ETrue;		
       
   357 			}
       
   358 
       
   359 		default:
       
   360 			{
       
   361 			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyToLocalCompoundUnexpectedState));
       
   362 			// unexpected state - complete the request
       
   363 			iProgressState = TImap4GenericProgress::EIdle;
       
   364 			return ETrue;
       
   365 			}
       
   366 		} // end of switch (iCurrentStep)
       
   367 	return EFalse;
       
   368 	}
       
   369 
       
   370 /**
       
   371 Special form of cancel, which enables message currently being fetched to be resumed.
       
   372 */
       
   373 void CImapCompoundCopyToLocal::CancelEnableResume()
       
   374 	{
       
   375 	iResume = ETrue;
       
   376 	Cancel();
       
   377 	iResume = EFalse;
       
   378 	}
       
   379 
       
   380 /**
       
   381 May be called in case of a genuine cancel or a cancel for migrate.
       
   382 Following a genuine cancel, the compound operation will be deleted.
       
   383 Following a cancel for migrate, the compound operation will be resumed,
       
   384 so the iNextState is updated here to ensure the operation is
       
   385 correctly restarted.
       
   386 
       
   387 In either case, CMsgActive::DoCancel() is called to complete the
       
   388 user request with KErrCancel.
       
   389 
       
   390 Note that if the default CMsgActive::DoComplete() is overridden,
       
   391 then that must also be modified to handle either case described above.
       
   392 */
       
   393 void CImapCompoundCopyToLocal::DoCancel()
       
   394 	{
       
   395 	__LOG_FORMAT((iSession->LogId(), "CImapCompoundCopyToLocal::DoCancel(iCurrentStep=%d)", iCurrentStep));	
       
   396 	switch (iCurrentStep)
       
   397 		{
       
   398 		case ESelectSourceMailboxRW:
       
   399 			{
       
   400 			iSession->Cancel();
       
   401 			iNextStep=ESelectSourceMailboxRW;	// (already set up - just need to resume here)
       
   402 			break;
       
   403 			}
       
   404 		case ESelectSourceMailboxRO:
       
   405 			{
       
   406 			iSession->Cancel();
       
   407 			iNextStep=ESelectSourceMailboxRO;	// (already set up - just need to resume here)
       
   408 			break;
       
   409 			}
       
   410 		case EFetchMessage:
       
   411 			{
       
   412 			if (iResume)
       
   413 				{
       
   414 				iBodyFetcher->CancelEnableResume();
       
   415 				}
       
   416 			else
       
   417 				{
       
   418 				iBodyFetcher->Cancel();
       
   419 				}
       
   420 			iNextStep=EFetchMessage;			// need to re-SELECT folder before resuming fetch
       
   421 			break;
       
   422 			}
       
   423 		case EInboxDuplicateCopy:
       
   424 		case EInboxDuplicateMove:
       
   425 			{
       
   426 			// outstanding request is on CMsvServerEntry
       
   427 			iMoveEntry->Cancel();
       
   428 			// cancel the copy, restart on re-connect
       
   429 			iNextStep=iCurrentStep;
       
   430 			break;
       
   431 			}
       
   432 		case EDeleteMessage:
       
   433 			{
       
   434 			iSession->Cancel();
       
   435 			iNextStep=EDeleteMessage;         	// need to re-SELECT folder before deleting
       
   436 			break;
       
   437 			}
       
   438 		case EExpunge:
       
   439 			{
       
   440 			iSession->Cancel();
       
   441 			iNextStep=EExpunge;         		// need to re-SELECT folder before deleting
       
   442 			break;
       
   443 			}
       
   444 		case ECloseFolder:
       
   445 			{
       
   446 			iSession->Cancel();
       
   447 			iNextStep=ECloseFolder;
       
   448 			break;
       
   449 			}
       
   450 		case ESelectFolderAfterClose:
       
   451 			{
       
   452 			iSession->Cancel();
       
   453 			iNextStep=ESelectFolderAfterClose;
       
   454 			break;
       
   455 			}
       
   456 		case EDeleteLocalMessage:
       
   457 		case ESetSourceMailbox:
       
   458 		case ELocalCopyComplete:
       
   459 		case EFinished:
       
   460 			{
       
   461 			// self-completed or no outstanding request.
       
   462 			break;
       
   463 			}
       
   464 		case ESuspendedForMigrate:
       
   465 		default:
       
   466 			{
       
   467 			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyToLocalCompoundCancelUnexpectedState));
       
   468 			// attempt to resume from the next message in the array.
       
   469 			iNextStep=ESetSourceMailbox;
       
   470 			break;
       
   471 			}
       
   472 		} // end of switch (iCurrentStep)
       
   473 
       
   474 	if (!iCancelForMigrate)
       
   475 		{
       
   476 		// genuine cancel - update progress
       
   477 		iProgressErrorCode = KErrCancel;
       
   478 		}
       
   479 	CMsgActive::DoCancel();
       
   480 	}
       
   481 	
       
   482 void CImapCompoundCopyToLocal::Progress(TImap4CompoundProgress& aCompoundProgress)
       
   483 	{
       
   484 	if (iPopulateCommand)
       
   485 		{
       
   486 		aCompoundProgress.iGenericProgress.iOperation = TImap4GenericProgress::EPopulate;
       
   487 		}
       
   488 	else
       
   489 		{
       
   490 			
       
   491 		if (iIsMove)
       
   492 			{
       
   493 			aCompoundProgress.iGenericProgress.iOperation = TImap4GenericProgress::EMoveToLocal;
       
   494 			}
       
   495 		else
       
   496 			{
       
   497 			aCompoundProgress.iGenericProgress.iOperation = TImap4GenericProgress::ECopyToLocal;
       
   498 			}
       
   499 		}
       
   500 	// Progress through the received selection
       
   501 	aCompoundProgress.iGenericProgress.iTotalSize = iTotalSize;
       
   502 	aCompoundProgress.iGenericProgress.iMsgsToDo  = iMessageSelection;
       
   503 	aCompoundProgress.iGenericProgress.iMsgsDone  = iMessageSelection - iSelectionStillToCopy;
       
   504 	// current message progress
       
   505 	iBodyFetcher->Progress(aCompoundProgress.iGenericProgress);
       
   506 	
       
   507 	// Put error into progress buffer
       
   508 	if( aCompoundProgress.iGenericProgress.iErrorCode == KErrNone )
       
   509 		{
       
   510 		aCompoundProgress.iGenericProgress.iErrorCode = iProgressErrorCode;
       
   511 		}
       
   512 	}
       
   513 
       
   514 /**
       
   515 Find the parent folder
       
   516 @return ETrue if the source folder has changed since the last message operated on
       
   517 always true on the first call after creation.
       
   518 @post iSourceFolderId - The TMsvId of the source folder for the message. May be NULL
       
   519 @post iSourceFolder - CImapFolder pointer for the parent folder. May be NULL.
       
   520 */
       
   521 TBool CImapCompoundCopyToLocal::SetSourceFolderL(const TMsvId aMessage)
       
   522 	{
       
   523 	TMsvId folderId = KMsvNullIndexEntryId;
       
   524 	TMsvId parentFolder = FindFolderL(aMessage);
       
   525 	CImapFolderInfo* selectedFolder = iSession->SelectedFolderInfo();
       
   526 	if(selectedFolder != NULL)
       
   527 		{
       
   528 		folderId = selectedFolder->MsvId() ;
       
   529 		}
       
   530 	if (parentFolder==iSourceFolderId)
       
   531 		{
       
   532 		// Same folder as last message copied.
       
   533 		return EFalse;
       
   534 		}
       
   535 	// otherwise, update to the new folder.	
       
   536 	iSourceFolderId = parentFolder;
       
   537 	if (iSourceFolderId!=NULL)
       
   538 		{
       
   539 		iSourceFolder = iSyncManager.GetFolderL(iSourceFolderId);
       
   540 		}
       
   541 		//Check the same INBOX folder is selected before
       
   542 	if (parentFolder== folderId)
       
   543 		{
       
   544 		return EFalse;
       
   545 		}
       
   546 	return ETrue;
       
   547 	}
       
   548 	
       
   549 /**
       
   550 Updates iGetPartialMailInfo to default values - (fetch whole message and attachments
       
   551 without limits). This is used for copy/move to local operations (populate operations
       
   552 uses a user-specified partial mail info object.
       
   553 
       
   554 @param aDestination - destination folder.
       
   555 @internalTechnology
       
   556 */
       
   557 void CImapCompoundCopyToLocal::UpdatePartialMailInfoToDefaults(TMsvId aDestination)
       
   558 	{	
       
   559 	iGetPartialMailInfo.iTotalSizeLimit= KMaxTInt;
       
   560 	iGetPartialMailInfo.iBodyTextSizeLimit = KMaxTInt;
       
   561 	iGetPartialMailInfo.iAttachmentSizeLimit = KMaxTInt;
       
   562 	iGetPartialMailInfo.iGetMailBodyParts = EGetImap4EmailBodyTextAndAttachments;		 
       
   563 	iGetPartialMailInfo.iPartialMailOptions=ENoSizeLimits;
       
   564 	iGetPartialMailInfo.iDestinationFolder=aDestination;
       
   565 	}
       
   566 	
       
   567 /**
       
   568 Copy or move a message to the specified local service folder.
       
   569 @param aSource				The message to copy or move
       
   570 @param aDestinationFolder   The id of the destination folder
       
   571 @param aRemoveOriginal      ETrue if this is a move, EFalse for a copy.
       
   572 */
       
   573 TInt CImapCompoundCopyToLocal::CopyMessage(const TMsvId aSource, const TMsvId aDestinationFolder, const TBool aRemoveOriginal)
       
   574 	{
       
   575 	TRAPD(err,CopyMessageL(aSource, aDestinationFolder, aRemoveOriginal));
       
   576 	if (err!=KErrNone)
       
   577 		{
       
   578 		// park moveentry if it fails to get going
       
   579 		if (iMoveEntry)
       
   580 			{
       
   581 			iMoveEntry->SetEntry(NULL);
       
   582 			}
       
   583 		
       
   584 		__LOG_FORMAT((iSession->LogId(), " CopyMessage() failed, err=", err));
       
   585 		}
       
   586 
       
   587 	return err;
       
   588 	}
       
   589 
       
   590 void CImapCompoundCopyToLocal::CopyMessageL(const TMsvId aSource, const TMsvId aDestinationFolder, const TBool aRemoveOriginal)
       
   591 	{
       
   592 	__LOG_FORMAT((iSession->LogId(), "CImapCompoundCopyToLocal::CopyMessageL(%x (in %x) to %x)", aSource, iSourceFolderId, aDestinationFolder));	
       
   593 
       
   594 	// Get a moveentry if we don't already have one
       
   595 	if (!iMoveEntry)
       
   596 		{
       
   597 		// Get a MoveEntry: we need to ask for one as a child of this entry, so
       
   598 		// move it to the root and ask for a child in the local service, which should
       
   599 		// always be there.
       
   600 		SetEntryL(KMsvRootIndexEntryId);
       
   601 
       
   602 		// Get child
       
   603 		iMoveEntry=iServerEntry.NewEntryL(KMsvLocalServiceIndexEntryId);
       
   604 		}
       
   605 
       
   606 	// Do the move, using the iMoveEntry CMsvServerEntry object
       
   607 	SetEntryL(aSource);
       
   608 	User::LeaveIfError(iMoveEntry->SetEntry(iServerEntry.Entry().Parent()));
       
   609 	
       
   610 	// Set context to null because the move / copy tries to lock each entry,
       
   611 	// and it will fail if we have it locked already
       
   612 	SetEntryL(KMsvNullIndexEntryId);
       
   613 
       
   614 	aRemoveOriginal?
       
   615 		iMoveEntry->MoveEntryL(aSource,aDestinationFolder,iStatus):
       
   616 		iMoveEntry->CopyEntryL(aSource,aDestinationFolder,iStatus);
       
   617 	if (!IsActive()) SetActive();
       
   618 	}
       
   619 
       
   620 /**
       
   621 Called when the copy operation is complete.
       
   622 Clears the new flag on the message entry just copied/moved.
       
   623 */
       
   624 void CImapCompoundCopyToLocal::CopyCompleteL()
       
   625 	{
       
   626 	// Park the move entry again
       
   627 	User::LeaveIfError(iMoveEntry->SetEntry(NULL));
       
   628 
       
   629 	SetEntryL(iCurrentMsgId);
       
   630 	TMsvEntry entry=iServerEntry.Entry();
       
   631 	entry.SetNew(EFalse);
       
   632 	iServerEntry.ChangeEntry(entry);
       
   633 	}
       
   634 
       
   635 /**
       
   636 Marks message specified by aTarget for delete locally,
       
   637 and issues STORE command to mark it for delete remotely.
       
   638 
       
   639 Issues an asynchronous request to the CImapSession. SetActive()
       
   640 must be called after calling this function.
       
   641 
       
   642 @param aTarget The message to be marked for delete
       
   643 */
       
   644 void CImapCompoundCopyToLocal::MarkMessageForDeleteL(const TMsvId& aTarget)
       
   645 	{
       
   646 	// Move to the entry in question
       
   647 	SetEntryL(aTarget);
       
   648 
       
   649 	// Set deleted flag on this entry
       
   650 	TMsvEmailEntry entry=iServerEntry.Entry();
       
   651 	entry.SetDeletedIMAP4Flag(ETrue);
       
   652 	User::LeaveIfError(iServerEntry.ChangeEntry(entry));
       
   653 
       
   654 	RArray<TUint> uidArray;
       
   655 	CleanupClosePushL(uidArray);
       
   656 	
       
   657 	uidArray.AppendL(entry.UID());
       
   658 	HBufC8* uids = CImapSession::CreateSequenceSetLC(uidArray);
       
   659 	
       
   660 	iSession->StoreL(iStatus, *uids, KMessageDataItem, KDeleteValue, ETrue, iOutMessageFlagInfo);
       
   661 	CleanupStack::PopAndDestroy(2, &uidArray);	// uids, uidArray
       
   662 	}
       
   663 
       
   664 /**
       
   665 Handles NO/BAD responses according to current step.
       
   666 Negative server responses are not fatal - the error is saved in
       
   667 the message currently being operated on and the state machine pushed
       
   668 on to process the next message in the requested selection.
       
   669 
       
   670 @return KErrNone if the error has been handled
       
   671 		Completion error code otherwise.
       
   672 */
       
   673 TInt CImapCompoundCopyToLocal::ProcessNegativeServerResponse()
       
   674 	{
       
   675 	__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::ProcessNegativeServerResponse");
       
   676 	TInt err = iStatus.Int();
       
   677 	switch (iCurrentStep)
       
   678 		{
       
   679 		case ESelectSourceMailboxRW:
       
   680 		case ESelectSourceMailboxRO:
       
   681 		case ESelectFolderAfterClose:
       
   682 			{
       
   683 			if (err == KErrImapNo)
       
   684 				{
       
   685 				err = KErrNotFound;
       
   686 				}
       
   687 			} // fall through
       
   688 
       
   689 		case EDeleteMessage:
       
   690 		case EExpunge:
       
   691 		case ECloseFolder:
       
   692 		case EFetchMessage:
       
   693 			{
       
   694 			// save the error with the associated message
       
   695 			TRAP_IGNORE(MessageErrorL(iCurrentMsgId, err));
       
   696 			// Skip to the next message or finish
       
   697 			iNextStep = (iSelectionStillToCopy>0)?ESetSourceMailbox:EFinished;
       
   698 			break;
       
   699 			}
       
   700 			
       
   701 		case EInboxDuplicateCopy:
       
   702 		case EInboxDuplicateMove:
       
   703 		case EDeleteLocalMessage:
       
   704 		case ESetSourceMailbox:
       
   705 		case ELocalCopyComplete:
       
   706 		case EFinished:
       
   707 		case ESuspendedForMigrate:
       
   708 		default:
       
   709 			{
       
   710 			// positive error code not expected,
       
   711 			// self-completed states or no outstanding request.
       
   712 			TImapServerPanic::ImapPanic(TImapServerPanic::ECopyToLocalCompoundUnexpectedState);
       
   713 			break;
       
   714 			}
       
   715 		} // end of switch (iCurrentStep)
       
   716 	iProgressErrorCode = err;
       
   717 	return KErrNone;
       
   718 	}
       
   719 	
       
   720 
       
   721 /**
       
   722 Called to resume the compound operation following a bearer migration.
       
   723 */
       
   724 void CImapCompoundCopyToLocal::ResumeOperationL(TRequestStatus& aStatus, CImapSession& aSession)
       
   725 	{
       
   726 	iSession = &aSession;
       
   727 	__LOG_TEXT(iSession->LogId(), "CImapCompoundCopyToLocal::Resuming");
       
   728 	__ASSERT_DEBUG(iCurrentStep==ESuspendedForMigrate, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyToLocalCompoundUnexpectedState));
       
   729 	iStopForMigrate = EFalse;
       
   730 
       
   731 	// Switch on next step - some "next steps" require a SELEECT first...
       
   732 	switch (iNextStep)
       
   733 		{
       
   734 		case ESetSourceMailbox: 	  // clean start on the next message in the array
       
   735 		case ESelectSourceMailboxRW:  // current message already selected... continue...
       
   736 		case ESelectSourceMailboxRO:
       
   737 		case ESelectFolderAfterClose:
       
   738 			{
       
   739 			// just return to the main state machine
       
   740 			CompleteSelf();
       
   741 			break;
       
   742 			}
       
   743 		case EFetchMessage:
       
   744 		case EDeleteMessage:
       
   745 		case EExpunge:
       
   746 		case ECloseFolder:
       
   747 		case EInboxDuplicateCopy: 
       
   748 		case EInboxDuplicateMove: // need to be selected to delete the message if this is a move
       
   749 			{
       
   750 			// select the source folder before returning to main state machine
       
   751 			iSourceFolder->SelectL(iStatus, *iSession);
       
   752 			iCurrentStep = ESelectSourceMailboxRW;
       
   753 			SetActive();
       
   754 			break;
       
   755 			}
       
   756 		case ELocalCopyComplete:
       
   757 		case EDeleteLocalMessage:
       
   758 		case EFinished:
       
   759 			// not expected
       
   760 		default:
       
   761 			{
       
   762 			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyToLocalCompoundCancelUnexpectedState));
       
   763 			// abandon the compound operation
       
   764 			iNextStep=EFinished;
       
   765 			CompleteSelf();
       
   766 			break;
       
   767 			}
       
   768 		} // end switch (iNextStep)
       
   769 	Queue(aStatus);
       
   770 	}
       
   771 
       
   772 /**
       
   773 Removes matching messages from the array of messages to be fetched.
       
   774 This is called when a populate or copy to local operation is requested while
       
   775 a background sync operation is in progress. It is used to remove
       
   776 messages from the array of messages to be auto-fetched that are going to
       
   777 be fetched due to the populate request.
       
   778 
       
   779 Note that this does not impact messages that have already been auto-fetched, or
       
   780 messages that are in the process of being fetched as this function is called.
       
   781 
       
   782 @param aSelection - the selection of messages to be removed from the list
       
   783 */
       
   784 void CImapCompoundCopyToLocal::RemoveFromSelection(CMsvEntrySelection& aDeleteSel)
       
   785 	{
       
   786 	// Check each entry in the delete selection...
       
   787 	TInt source;
       
   788 	for (TInt del=0;del<aDeleteSel.Count();++del)
       
   789 		{
       
   790 		source = iSourceSel->Find(aDeleteSel[del]);
       
   791 		if (source!=KErrNotFound)
       
   792 			{
       
   793 			if (source < iSelectionStillToCopy)
       
   794 				{
       
   795 				// message has not yet been auto-fetched, remove it from the list.
       
   796 				// note that iSelection starts at iSourceSel.Count() and counts down...
       
   797 				iSourceSel->Delete(source);
       
   798 				// update the fetch index.
       
   799 				--iSelectionStillToCopy;
       
   800 				}
       
   801 			// otherwise message has already been auto-fetched. No action necessary.
       
   802 			}
       
   803 		}
       
   804 	}