email/pop3andsmtpmtm/imapservermtm/src/IMAPOFFL.CPP
changeset 25 84d9eb65b26f
equal deleted inserted replaced
23:238255e8b033 25:84d9eb65b26f
       
     1 // Copyright (c) 1998-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 // IMAP4 Offline operations.
       
    15 // 
       
    16 //
       
    17 
       
    18 #include "impspan.h"
       
    19 
       
    20 #include <msventry.h>
       
    21 #include <imapset.h>
       
    22 #include <miutset.h>
       
    23 #include <offop.h>
       
    24 #include <msvreg.h>
       
    25 #include <imapcmds.h>
       
    26 
       
    27 #include "imapsess.h"
       
    28 #include "imapoffl.h"
       
    29 
       
    30 #ifdef _DEBUG
       
    31 #define DBG(a) iSession->LogText a
       
    32 #define PRINTING
       
    33 #else
       
    34 #define DBG(a)
       
    35 #undef PRINTING
       
    36 #endif
       
    37 
       
    38 // ----------------------------------------------------------------------
       
    39 
       
    40 #ifdef PRINTING
       
    41 
       
    42 LOCAL_D TPtrC8 OffLineOpTypeString(const CImOffLineOperation& aOp)
       
    43 	{
       
    44 	switch (aOp.OpType())
       
    45 		{
       
    46 	case CImOffLineOperation::EOffLineOpNone:
       
    47 		return _L8("None");
       
    48 
       
    49 	case CImOffLineOperation::EOffLineOpCopyToLocal:
       
    50 		return _L8("CopyToLocal");
       
    51 	case CImOffLineOperation::EOffLineOpCopyFromLocal:
       
    52 		return _L8("CopyFromLocal");
       
    53 	case CImOffLineOperation::EOffLineOpCopyWithinService:
       
    54 		return _L8("CopyWithinService");
       
    55 
       
    56 	case CImOffLineOperation::EOffLineOpMoveToLocal:
       
    57 		return _L8("MoveToLocal");
       
    58 	case CImOffLineOperation::EOffLineOpMoveFromLocal:
       
    59 		return _L8("MoveFromLocal");
       
    60 	case CImOffLineOperation::EOffLineOpMoveWithinService:
       
    61 		return _L8("MoveWithinService");
       
    62 
       
    63 	case CImOffLineOperation::EOffLineOpDelete:
       
    64 		return _L8("Delete");
       
    65 
       
    66 	case CImOffLineOperation::EOffLineOpChange:
       
    67 		return _L8("Change");
       
    68 	case CImOffLineOperation::EOffLineOpCreate:
       
    69 		return _L8("Create");
       
    70 
       
    71 	case CImOffLineOperation::EOffLineOpMtmSpecific:
       
    72 		switch (aOp.MtmFunctionId())
       
    73 			{
       
    74 		case EFnOffLineOpMoveDelete:
       
    75 			return _L8("MoveDelete");
       
    76 		case EFnOffLineOpPopulate:
       
    77 			return _L8("Populate");
       
    78 		default:
       
    79 			return _L8("UnknownMtmSpecific");
       
    80 			}
       
    81 	default:
       
    82 		break;
       
    83 		}
       
    84 	return _L8("Unknown");
       
    85 	}
       
    86 
       
    87 LOCAL_D TPtrC8 Imap4OpTypeString(CImap4OffLineControl::TImap4OpType aOpType)
       
    88 	{
       
    89 	switch (aOpType)
       
    90 		{
       
    91 	case CImap4OffLineControl::EImap4OpCopyToLocal:
       
    92 		return _L8("CopyToLocal");
       
    93 	case CImap4OffLineControl::EImap4OpCopyFromLocal:
       
    94 		return _L8("CopyFromLocal");
       
    95 	case CImap4OffLineControl::EImap4OpCopyWithinService:
       
    96 		return _L8("CopyWithinService");
       
    97 
       
    98 	case CImap4OffLineControl::EImap4OpMoveToLocal:
       
    99 		return _L8("MoveToLocal");
       
   100 	case CImap4OffLineControl::EImap4OpMoveFromLocal:
       
   101 		return _L8("MoveFromLocal");
       
   102 	case CImap4OffLineControl::EImap4OpMoveWithinService:
       
   103 		return _L8("MoveWithinService");
       
   104 
       
   105 	case CImap4OffLineControl::EImap4OpDelete:
       
   106 		return _L8("Delete");
       
   107 
       
   108 	case CImap4OffLineControl::EImap4OpMoveTypeDelete:
       
   109 		return _L8("MoveDelete");
       
   110 	case CImap4OffLineControl::EImap4OpPopulate:
       
   111 		return _L8("Populate");
       
   112 
       
   113 	default:
       
   114 		break;
       
   115 		}
       
   116 	return _L8("Unknown");
       
   117 	}
       
   118 #endif
       
   119 
       
   120 // ----------------------------------------------------------------------
       
   121 // construction/destruction routines
       
   122 
       
   123 CImap4OffLineControl* CImap4OffLineControl::NewL(CMsvServerEntry* aEntry, CImImap4Session *aSession)
       
   124 	{
       
   125 	CImap4OffLineControl* self = NewLC(aEntry,aSession);
       
   126 	CleanupStack::Pop(); // self
       
   127 	return self;
       
   128 	}
       
   129 
       
   130 CImap4OffLineControl* CImap4OffLineControl::NewLC(CMsvServerEntry* aEntry, CImImap4Session *aSession)
       
   131 	{
       
   132 	CImap4OffLineControl* self = new (ELeave) CImap4OffLineControl(aEntry,aSession);
       
   133 	CleanupStack::PushL(self);
       
   134 	CActiveScheduler::Add(self);
       
   135 
       
   136 	self->ConstructL();
       
   137 
       
   138 	return self;
       
   139 	}
       
   140 
       
   141 CImap4OffLineControl::CImap4OffLineControl(CMsvServerEntry* aEntry, CImImap4Session *aSession)
       
   142 	: CMsgActive(EPriorityStandard), iEntry(aEntry), iSession(aSession)
       
   143 	{
       
   144 	}
       
   145 
       
   146 void CImap4OffLineControl::ConstructL()
       
   147 	{
       
   148 	iCopyDirect = new (ELeave) CMsvEntrySelection;
       
   149 	iMoveDirect = new (ELeave) CMsvEntrySelection;
       
   150 	iMoveToLocalDirect = new (ELeave) CMsvEntrySelection;
       
   151 	}
       
   152 
       
   153 CImap4OffLineControl::~CImap4OffLineControl()
       
   154 	{
       
   155 	delete iCopyDirect;
       
   156 	delete iMoveDirect;
       
   157 	delete iMoveToLocalDirect;
       
   158 	}
       
   159 
       
   160 // ----------------------------------------------------------------------
       
   161 
       
   162 // public routines
       
   163 
       
   164 // Store an offline copy/move/delete command: we need to determine which
       
   165 // folder the offline command should be stored in dependent on the
       
   166 // source of the command.
       
   167 
       
   168 // CopyToLocal can contain whole messages or parts (but not embedded
       
   169 // messages). It can also be a copy to NULL, in which case it means
       
   170 // just populate the mirror
       
   171 
       
   172 // TODO: Pass in the GetMailOptions to the copy to mirror option
       
   173 
       
   174 // Any item can contain whole messages, but not folders, and can
       
   175 // contain shadow ids
       
   176 
       
   177 void CImap4OffLineControl::StoreOfflineCommandL(TImap4OpType aOperation,
       
   178 												const CMsvEntrySelection& aSelection,
       
   179 												TMsvId aDestination,
       
   180 												TRequestStatus& aStatus)
       
   181 	{
       
   182 	TBuf8<128> params = _L8("");
       
   183 	StoreOfflineCommandL( aOperation, aSelection, aDestination, params, aStatus );
       
   184 	}
       
   185 
       
   186 void CImap4OffLineControl::StoreOfflineCommandL(TImap4OpType aOperation,
       
   187 												const CMsvEntrySelection& aSelection,
       
   188 												TMsvId aDestination,
       
   189 												const TDesC8& aParams,
       
   190 												TRequestStatus& aStatus)
       
   191 	{
       
   192 #ifdef PRINTING
       
   193 	TPtrC8 p = Imap4OpTypeString(aOperation);
       
   194 	DBG((_L8("StoreOfflineCommand: op %S %d entries to %x param bytes %d"),
       
   195 		 &p, aSelection.Count(), aDestination, aParams.Length()));
       
   196 #endif
       
   197 		
       
   198 	Queue(aStatus);
       
   199 
       
   200 	iDestination = aDestination;
       
   201 
       
   202 	// work our which service we are dealing with
       
   203 	iServiceId = ServiceOfL( aOperation == EImap4OpCopyFromLocal ||
       
   204 							 aOperation == EImap4OpMoveFromLocal ?
       
   205 							 aDestination : aSelection[0] );
       
   206 
       
   207 	// clear list of Direct operations to do after storing
       
   208 	// commands
       
   209 	iCopyDirect->Reset();
       
   210 	iMoveDirect->Reset();
       
   211 	iMoveToLocalDirect->Reset();
       
   212 	
       
   213 	for (TInt i = 0; i < aSelection.Count(); i++)
       
   214 		{
       
   215 		CImOffLineOperation op;
       
   216 			
       
   217 		// See if the message is in fact a shadow
       
   218 		TMsvId origId = aSelection[i];
       
   219 		SetEntryL(origId);
       
   220 
       
   221 		TMsvId shadowId = KMsvNullIndexEntryId;
       
   222 		TMsvId shadowParentId = KMsvNullIndexEntryId;
       
   223 		TMsvEmailEntry entry = iEntry->Entry();
       
   224 		if (entry.iRelatedId)
       
   225 			{
       
   226 			shadowId = origId;
       
   227 			shadowParentId = entry.Parent();
       
   228 			origId = entry.iRelatedId;
       
   229 
       
   230 			// it is possible that the original has been deleted by
       
   231 			// now (if it were local). If so then skip this operation
       
   232 			TInt err = iEntry->SetEntry(origId);
       
   233 			if (err != KErrNone)
       
   234 				origId = KMsvNullIndexEntryId;
       
   235 			else
       
   236 				entry = iEntry->Entry();
       
   237 			}
       
   238 
       
   239 		if (origId != KMsvNullIndexEntryId)
       
   240 			{
       
   241 			// entry contains original (not shadow) message details
       
   242 		
       
   243 			// it is an undo type operation if we are copying or moving a
       
   244 			// shadow back to its original folder and the original is
       
   245 			// invisible or deleted
       
   246 			TBool undeleteOp = shadowId != KMsvNullIndexEntryId &&
       
   247 				entry.Parent() == iDestination &&
       
   248 				(!entry.Visible() || entry.DisconnectedOperation() == EDisconnectedDeleteOperation);
       
   249 
       
   250 			// Make operation & save it
       
   251 			switch(aOperation)
       
   252 				{
       
   253 			case EImap4OpCopyToLocal:
       
   254 				if (undeleteOp)
       
   255 					{
       
   256 					UndeleteOperationL(origId, shadowParentId, ETrue);
       
   257 					}
       
   258 				else if (IdIsLocalL(origId) || entry.Complete())
       
   259 					{
       
   260 					// either direct local copy or copy from mirror of completely populated message
       
   261 					// either way, add new entry to todo array
       
   262 					iCopyDirect->AppendL(origId);
       
   263 					}
       
   264 				else
       
   265 					{
       
   266 					op.SetCopyToLocal(origId,iDestination);
       
   267 					SaveOperationL(op);
       
   268 					}
       
   269 				break;
       
   270 
       
   271 			case EImap4OpCopyFromLocal:
       
   272 			case EImap4OpCopyWithinService:
       
   273 				if (undeleteOp)
       
   274 					{
       
   275 					UndeleteOperationL(origId, shadowParentId, ETrue);
       
   276 					}
       
   277 				else if (IdIsLocalL(origId))
       
   278 					{
       
   279 					op.SetCopyFromLocal(origId,iDestination);
       
   280 					SaveOperationL(op);
       
   281 					}
       
   282 				else
       
   283 					{
       
   284 					op.SetCopyWithinService(origId,iDestination);
       
   285 					SaveOperationL(op);
       
   286 					}
       
   287 				break;
       
   288 
       
   289 			case EImap4OpMoveToLocal:
       
   290 				if (undeleteOp)
       
   291 					{
       
   292 					UndeleteOperationL(origId, shadowParentId, EFalse);
       
   293 					DeleteEntryL(shadowId);
       
   294 					}
       
   295 				else if (IdIsLocalL(origId))
       
   296 					{
       
   297 					CImOffLineOperation origOp;
       
   298 					if (FindOffLineOpByIdL( origId, shadowParentId, origOp, ETrue /* delete op */) == 0)
       
   299 						User::Leave(KErrNotSupported);
       
   300 
       
   301 					if ( OffLineOpIsCopy(origOp) )
       
   302 						// add new local to local copy op
       
   303 						iCopyDirect->AppendL(origId);
       
   304 					else
       
   305 						// direct local move
       
   306 						iMoveDirect->AppendL(origId);
       
   307 
       
   308 					DeleteEntryL(shadowId);
       
   309 					}
       
   310 				else if (entry.Complete())
       
   311 					{
       
   312 					//	Not local, but completely populated
       
   313 					iMoveToLocalDirect->AppendL(origId);
       
   314 					}
       
   315 				else
       
   316 					{
       
   317 					op.SetMoveToLocal(origId,iDestination);
       
   318 					SaveOperationL(op);
       
   319 					}
       
   320 				break;
       
   321 
       
   322 			case EImap4OpMoveFromLocal:
       
   323 			case EImap4OpMoveWithinService:
       
   324 				if (undeleteOp)
       
   325 					{
       
   326 					UndeleteOperationL(origId, shadowParentId, EFalse);
       
   327 
       
   328 					// this one can fail depending on what kind of
       
   329 					// undelete operation it was
       
   330 					CImOffLineOperation origOp;
       
   331 					FindOffLineOpByIdL( origId, shadowParentId, origOp, ETrue /* delete op */);
       
   332 
       
   333 					DeleteEntryL(shadowId);
       
   334 					}
       
   335 				else if (shadowId)
       
   336 					{
       
   337 					CImOffLineOperation origOp;
       
   338 					if (FindOffLineOpByIdL( origId, shadowParentId, origOp, ETrue /* delete op */) == 0)
       
   339 						User::Leave(KErrNotSupported);
       
   340 			
       
   341 					// Clean disconnected flags
       
   342 					SetEntryL(origId);
       
   343 					TMsvEmailEntry entry = iEntry->Entry();
       
   344 					if (entry.DisconnectedOperation() != EDisconnectedMultipleOperation)
       
   345 						{
       
   346 						entry.SetDisconnectedOperation(ENoDisconnectedOperations);
       
   347 						ChangeEntryL(entry);
       
   348 						}
       
   349 					
       
   350 					// if shadow was the result of a copy then change
       
   351 					// original copy to point to new destination
       
   352 
       
   353 					// if shadow was result of a move then change move to
       
   354 					// point to new destination
       
   355 					if ( OffLineOpIsCopy(origOp) )
       
   356 						{
       
   357 						if (IdIsLocalL(origId))
       
   358 							op.SetCopyFromLocal(origId,iDestination);
       
   359 						else
       
   360 							op.SetCopyWithinService(origId,iDestination);
       
   361 						}
       
   362 					else
       
   363 						{
       
   364 						if (IdIsLocalL(origId))
       
   365 							op.SetMoveFromLocal(origId,iDestination);
       
   366 						else
       
   367 							op.SetMoveWithinService(origId,iDestination);
       
   368 						}
       
   369 
       
   370 					SaveOperationL(op);
       
   371 					DeleteEntryL(shadowId);
       
   372 					}
       
   373 				else
       
   374 					{
       
   375 					if (IdIsLocalL(origId))
       
   376 						op.SetMoveFromLocal(origId,iDestination);
       
   377 					else
       
   378 						op.SetMoveWithinService(origId,iDestination);
       
   379 					SaveOperationL(op);
       
   380 					}
       
   381 				break;
       
   382 
       
   383 			case EImap4OpDelete:
       
   384 				// we treat shadows and real items the same for deletion
       
   385 				// currently
       
   386 				op.SetDelete( shadowId ? shadowId : origId );
       
   387 				SaveOperationL(op);
       
   388 				break;
       
   389 			
       
   390 			case EImap4OpUndelete:
       
   391 				if (shadowId)
       
   392 					{
       
   393 					UndeleteOperationL(shadowId, shadowParentId, EFalse);
       
   394 					}
       
   395 				else
       
   396 					{
       
   397 					// if the entry is not a shadow then we need to
       
   398 					// replace the disconnected op flags with the original
       
   399 					// flags before it was deleted.
       
   400 					CImOffLineOperation origOp;
       
   401 
       
   402 					// this searches the list before the delete is
       
   403 					// removed.  However since deletes are stored at
       
   404 					// the end of the list then if there are any other
       
   405 					// operations it will return the other, and a
       
   406 					// count of 2 or greater.
       
   407 					TInt count = FindOffLineOpByIdL(origId, KMsvNullIndexEntryId, origOp, EFalse);
       
   408 
       
   409 					TImDisconnectedOperationType disconnectedType = ENoDisconnectedOperations;
       
   410 					if (count == 2)
       
   411 						disconnectedType = OffLineOpToDisconnectedOp( origOp );
       
   412 					else if (count > 2)
       
   413 						disconnectedType = EDisconnectedMultipleOperation;
       
   414 
       
   415 					UndeleteOperationL(origId, KMsvNullIndexEntryId, EFalse, disconnectedType);
       
   416 					}
       
   417 				break;
       
   418 
       
   419 			case EImap4OpPopulate:
       
   420 				/* easy one, just populate the original */
       
   421 				op.SetMtmSpecificCommandL(origId, iDestination, EFnOffLineOpPopulate, aParams);
       
   422 				SaveOperationL(op);
       
   423 				break;
       
   424 
       
   425 			case EImap4OpMoveTypeDelete:
       
   426 				__ASSERT_DEBUG(0, gPanic(EBadUseOfImap4Op));
       
   427 				break;
       
   428 				}
       
   429 			}
       
   430 		}
       
   431 
       
   432 	// if there are entries left over then they are ones we added to
       
   433 	// be done immediately
       
   434 	if (!DoLocalOpL())
       
   435 		{
       
   436 		// Request has been queued, complete immediately
       
   437 		Complete(KErrNone);
       
   438 		}
       
   439 	}
       
   440 
       
   441 // Cancel offline operations queued in the folders/service mentioned
       
   442 // in the selection
       
   443 
       
   444 void CImap4OffLineControl::CancelOffLineOperationsL(const CMsvEntrySelection& aSelection)
       
   445 	{
       
   446 	DBG((_L8("CancelOfflineOperations: %d entries"), aSelection.Count()));
       
   447 		
       
   448 	for (TInt i = 0; i < aSelection.Count(); i++)
       
   449 		{
       
   450 		TMsvId id = aSelection[i];
       
   451 
       
   452 		SetEntryL(id);
       
   453 		TMsvEmailEntry entry = iEntry->Entry();
       
   454 		if (entry.iType == KUidMsvFolderEntry)
       
   455 			{
       
   456 			CImOffLineOperationArray* array = OffLineOpArrayL(id);
       
   457 			CleanupStack::PushL(array);
       
   458 
       
   459 			if (array->CountOperations())
       
   460 				{
       
   461 				// remove the queued ops
       
   462 				while (array->CountOperations())
       
   463 					{
       
   464 					CImOffLineOperation thisOp;
       
   465 					thisOp.CopyL(array->Operation(0));
       
   466 					
       
   467 					UndoOfflineOpL(thisOp, ETrue);
       
   468 					
       
   469 					array->Delete(0);
       
   470 					}
       
   471 				
       
   472 				// write back empty array to store
       
   473 				SetOffLineOpArrayL(id, *array);
       
   474 				}
       
   475 
       
   476 			CleanupStack::PopAndDestroy(); // array
       
   477 			}
       
   478 #if 0
       
   479 		else
       
   480 			{
       
   481 			CImOffLineOperation op;
       
   482 			while (FindOffLineOpByIdL(id, KMsvNullIndexEntryId, op, ETrue))
       
   483 				{
       
   484 				CMsvEntrySelection* selection=new (ELeave) CMsvEntrySelection;
       
   485 				CleanupStack::PushL(selection);
       
   486 				}
       
   487 		
       
   488 			CleanupStack::PopAndDestroy(); // selection
       
   489 			}
       
   490 #endif
       
   491 		
       
   492 		}
       
   493 	}
       
   494 
       
   495 // ----------------------------------------------------------------------
       
   496 
       
   497 TImDisconnectedOperationType CImap4OffLineControl::OffLineOpToDisconnectedOp(const CImOffLineOperation& aOp)
       
   498 	{
       
   499 	TImDisconnectedOperationType type;
       
   500 	switch (aOp.OpType())
       
   501 		{
       
   502 	case CImOffLineOperation::EOffLineOpMoveToLocal:
       
   503 		type = EDisconnectedMoveToOperation;
       
   504 		break;
       
   505 	case CImOffLineOperation::EOffLineOpMoveFromLocal:
       
   506 		type = EDisconnectedMoveFromOperation;
       
   507 		break;
       
   508 	case CImOffLineOperation::EOffLineOpMoveWithinService:
       
   509 		type = EDisconnectedMoveWithinServiceOperation;
       
   510 		break;
       
   511 
       
   512 	case CImOffLineOperation::EOffLineOpCopyToLocal:
       
   513 		type = EDisconnectedCopyToOperation;
       
   514 		break;
       
   515 	case CImOffLineOperation::EOffLineOpCopyFromLocal:
       
   516 		type = EDisconnectedCopyFromOperation;
       
   517 		break;
       
   518 	case CImOffLineOperation::EOffLineOpCopyWithinService:
       
   519 		type = EDisconnectedCopyWithinServiceOperation;
       
   520 		break;
       
   521 		
       
   522 	case CImOffLineOperation::EOffLineOpDelete:
       
   523 		type = EDisconnectedDeleteOperation;
       
   524 		break;
       
   525 
       
   526 	case CImOffLineOperation::EOffLineOpMtmSpecific:
       
   527 		type = EDisconnectedSpecialOperation;
       
   528 		break;
       
   529 	default:
       
   530 		type = EDisconnectedUnknownOperation;
       
   531 		break;
       
   532 		}
       
   533 	return type;
       
   534 	}
       
   535 
       
   536 // This returns TRUE is it is a strict copy operation. Populate can be
       
   537 // considered False by the callers of this function.
       
   538 
       
   539 TBool CImap4OffLineControl::OffLineOpIsCopy(const CImOffLineOperation& aOp)
       
   540 	{
       
   541 	switch (aOp.OpType())
       
   542 		{
       
   543 	case CImOffLineOperation::EOffLineOpCopyToLocal:
       
   544 	case CImOffLineOperation::EOffLineOpCopyFromLocal:
       
   545 	case CImOffLineOperation::EOffLineOpCopyWithinService:
       
   546 		return ETrue;
       
   547 	case CImOffLineOperation::EOffLineOpMtmSpecific:
       
   548 		if (aOp.MtmFunctionId() == EFnOffLineOpPopulate)
       
   549 			{
       
   550 			return ETrue;
       
   551 			}
       
   552 	    break; 
       
   553 	
       
   554 	default:
       
   555 		break;
       
   556 		}
       
   557 	return EFalse;
       
   558 	}
       
   559 
       
   560 TInt CImap4OffLineControl::PosVal(const CImOffLineOperation& aOp)
       
   561 	{
       
   562 	switch (aOp.OpType())
       
   563 		{	
       
   564 	case CImOffLineOperation::EOffLineOpMtmSpecific: // populate
       
   565 		switch (aOp.MtmFunctionId())
       
   566 			{
       
   567 		case EFnOffLineOpMoveDelete:
       
   568 			return 5;
       
   569 		case EFnOffLineOpPopulate:
       
   570 			return 0;
       
   571 			}
       
   572 		break;
       
   573 
       
   574 	case CImOffLineOperation::EOffLineOpCopyToLocal:
       
   575 	case CImOffLineOperation::EOffLineOpCopyWithinService:
       
   576 		return 1;
       
   577 	case CImOffLineOperation::EOffLineOpCopyFromLocal:
       
   578 		return 2;
       
   579 		
       
   580 	case CImOffLineOperation::EOffLineOpMoveToLocal:
       
   581 	case CImOffLineOperation::EOffLineOpMoveWithinService:
       
   582 		return 3;
       
   583 
       
   584 	case CImOffLineOperation::EOffLineOpMoveFromLocal:
       
   585 		return 4;	
       
   586 
       
   587 	case CImOffLineOperation::EOffLineOpDelete:
       
   588 		return 6;
       
   589 	default:
       
   590 		break;
       
   591 		}
       
   592 	return 6;
       
   593 	}
       
   594 
       
   595 // ----------------------------------------------------------------------
       
   596 
       
   597 // Do setentry, leave if there is an error
       
   598 void CImap4OffLineControl::SetEntryL(TMsvId aId)
       
   599 	{
       
   600 	User::LeaveIfError(iEntry->SetEntry(aId));
       
   601 	}
       
   602 
       
   603 // Change entry, leave if error
       
   604 void CImap4OffLineControl::ChangeEntryL(TMsvEntry& aEntry)
       
   605 	{
       
   606 	User::LeaveIfError(iEntry->ChangeEntry(aEntry));
       
   607 	}
       
   608 
       
   609 // remove an id, leave if error, moves to the parent first
       
   610 void CImap4OffLineControl::DeleteEntryL(TMsvId aId)
       
   611 	{
       
   612 	SetEntryL(aId);
       
   613 	SetEntryL(iEntry->Entry().Parent());
       
   614 	User::LeaveIfError(iEntry->DeleteEntry(aId));
       
   615 	}
       
   616 
       
   617 // Find the folder that encloses this message or message part. Note
       
   618 // that this must be a real folder, not a folder component of a
       
   619 // message, and that it may not be in our service.
       
   620 TMsvId CImap4OffLineControl::FolderOfL(TMsvId aId)
       
   621 	{
       
   622 	SetEntryL( MessageOfL(aId) );
       
   623 	return iEntry->Entry().Parent();
       
   624 	}
       
   625 
       
   626 // If the message is not in our service then return the destination
       
   627 // folder. Otherwise return its own parent folder.
       
   628 TMsvId CImap4OffLineControl::FindOffLineSaveFolderL(TMsvId aId, TMsvId aDestId)
       
   629 	{
       
   630 	TMsvId folder = FolderOfL(aId);
       
   631 	if (ServiceOfL(folder) == iServiceId)
       
   632 		return folder;
       
   633 	return aDestId;
       
   634 	}
       
   635 
       
   636 // Find the top level message that holds this message part. Can be
       
   637 // itself if it is a real message itself. This is located by finding
       
   638 // the message that is highest up the tree.
       
   639 TMsvId CImap4OffLineControl::MessageOfL(TMsvId aId)
       
   640 	{
       
   641 	TMsvId current=aId;
       
   642 	TMsvId msg=aId;
       
   643 	while(current!=KMsvRootIndexEntryIdValue)
       
   644 		{
       
   645 		// Visit this entry
       
   646 		SetEntryL(current);
       
   647 
       
   648 		TMsvEmailEntry entry = iEntry->Entry();
       
   649 		
       
   650 		// if service then searched far enough
       
   651 		if (entry.iType==KUidMsvServiceEntry)
       
   652 			break;
       
   653 
       
   654 		// if message type then store it
       
   655 		if (entry.iType==KUidMsvMessageEntry)
       
   656 			msg = entry.Id();
       
   657 		
       
   658 		// Go upwards
       
   659 		current=entry.Parent();
       
   660 		}
       
   661 
       
   662 	return msg;
       
   663 	}
       
   664 
       
   665 // return the id of the service containing this id
       
   666 TMsvId CImap4OffLineControl::ServiceOfL(TMsvId aId)
       
   667 	{
       
   668 	TMsvId current=aId;
       
   669 	while(current!=KMsvRootIndexEntryIdValue)
       
   670 		{
       
   671 		// Visit this entry
       
   672 		SetEntryL(current);
       
   673 
       
   674 		TMsvEmailEntry entry = iEntry->Entry();
       
   675 		
       
   676 		// if service then searched far enough
       
   677 		if (entry.iType==KUidMsvServiceEntry)
       
   678 			break;
       
   679 
       
   680 		// Go upwards
       
   681 		current=entry.Parent();
       
   682 		}
       
   683 
       
   684 	return current;
       
   685 	}
       
   686 
       
   687 // is this id in the local service?
       
   688 TMsvId CImap4OffLineControl::IdIsLocalL(TMsvId aId)
       
   689 	{
       
   690 	return ServiceOfL(aId) == KMsvLocalServiceIndexEntryIdValue;
       
   691 	}
       
   692 
       
   693 // ----------------------------------------------------------------------
       
   694 
       
   695 // simple functions to get and set the offline array on an id. More
       
   696 // efficient open and modify versions are possible and used elsewhere
       
   697 
       
   698 CImOffLineOperationArray* CImap4OffLineControl::OffLineOpArrayL(TMsvId aId)
       
   699 	{
       
   700 	SetEntryL(aId);
       
   701 
       
   702 	CImOffLineOperationArray* array = CImOffLineOperationArray::NewL();
       
   703 
       
   704 	// if no store then return an empty array (easier for higher
       
   705 	// layers than a NULL pointer).
       
   706 	if (iEntry->HasStoreL())
       
   707 		{
       
   708 		CleanupStack::PushL(array);
       
   709 		
       
   710 		CMsvStore* store = iEntry->ReadStoreL();
       
   711 		CleanupStack::PushL(store);
       
   712 	
       
   713 		CImOffLineArrayStore arraystore(*array);
       
   714 		arraystore.RestoreL(*store);
       
   715 
       
   716 		CleanupStack::PopAndDestroy(); // store
       
   717 		CleanupStack::Pop();		   // array
       
   718 		}
       
   719 	
       
   720 	DBG((_L8("OffLineOpArrayL: folder 0x%x count %d"), aId, array->CountOperations()));
       
   721 
       
   722 	return array;
       
   723 	}
       
   724 
       
   725 void CImap4OffLineControl::SetOffLineOpArrayL(TMsvId aId, CImOffLineOperationArray& aArray)
       
   726 	{
       
   727 	DBG((_L8("SetOffLineOpArrayL: folder 0x%x count %d"), aId, aArray.CountOperations()));
       
   728 
       
   729 	SetEntryL( aId );
       
   730 
       
   731 	CMsvStore* store=iEntry->EditStoreL();
       
   732 	CleanupStack::PushL(store);
       
   733 
       
   734 	CImOffLineArrayStore arraystore(aArray);
       
   735 	arraystore.StoreL(*store);
       
   736 
       
   737 	store->CommitL();
       
   738 
       
   739 	CleanupStack::PopAndDestroy(); // store
       
   740 	}
       
   741 
       
   742 // ----------------------------------------------------------------------
       
   743 
       
   744 // Save offline operation
       
   745 void CImap4OffLineControl::SaveOperationL(const CImOffLineOperation& aOperation)
       
   746 	{
       
   747 	DBG((_L8("SaveOperation:")));
       
   748 
       
   749 	// We need an array, to store the current offline operations of this folder
       
   750     CImOffLineOperationArray *array=CImOffLineOperationArray::NewL();
       
   751 	CleanupStack::PushL(array);
       
   752 	CImOffLineArrayStore arraystore(*array);
       
   753 
       
   754 	// find where to store the op
       
   755 	TMsvId storehere = FindOffLineSaveFolderL(aOperation.MessageId(), aOperation.TargetMessageId());
       
   756 	SetEntryL(storehere);
       
   757 
       
   758 	// open the store
       
   759 	CMsvStore *store=iEntry->EditStoreL();
       
   760 	CleanupStack::PushL(store);
       
   761 
       
   762 	arraystore.RestoreL(*store);
       
   763 
       
   764 	// we add this operation after others of the same type
       
   765 	TInt insertBefore = PosVal(aOperation) + 1;
       
   766 	TBool done = EFalse;
       
   767 	
       
   768 	for(TInt a=0; a<array->CountOperations(); a++)
       
   769 		{
       
   770 		if (insertBefore <= PosVal(array->Operation(a)))
       
   771 			{
       
   772 			array->InsertOperationL(MUTABLE_CAST(CImOffLineOperation&, aOperation), a);
       
   773 			done = ETrue;
       
   774 			break;
       
   775 			}
       
   776 		}
       
   777 	
       
   778 	if (!done)
       
   779 		array->AppendOperationL(aOperation);
       
   780 
       
   781 	// write back
       
   782 	arraystore.StoreL(*store);
       
   783 	store->CommitL();
       
   784 
       
   785 	// Dispose of store & array
       
   786 	CleanupStack::PopAndDestroy(2);
       
   787 
       
   788 	// make the shadow
       
   789 	MakeShadowL(aOperation);
       
   790 	}
       
   791 
       
   792 // returns ETrue if a matching Op was found
       
   793 
       
   794 TInt CImap4OffLineControl::FindOffLineOpByIdL(TMsvId aId, TMsvId aDestFolder,
       
   795 										  CImOffLineOperation& aOp, TBool aDelete)
       
   796 	{
       
   797     CImOffLineOperationArray *array=CImOffLineOperationArray::NewL();
       
   798 	CleanupStack::PushL(array);
       
   799 	CImOffLineArrayStore arraystore(*array);
       
   800 
       
   801 	SetEntryL(FindOffLineSaveFolderL(aId, aDestFolder));
       
   802 	CMsvStore *store=aDelete ? iEntry->EditStoreL() : iEntry->ReadStoreL();
       
   803 	CleanupStack::PushL(store);
       
   804 
       
   805 	arraystore.RestoreL(*store);
       
   806 
       
   807 	// look in the array for an operation on this Id and optionally to
       
   808 	// the matching folder
       
   809 	TInt found = 0;
       
   810 	TInt foundAt = -1;
       
   811 	for(TInt a=0; a<array->CountOperations(); a++)
       
   812 		{
       
   813 		if (array->Operation(a).MessageId() == aId &&
       
   814 			(aDestFolder == KMsvNullIndexEntryId ||
       
   815 			 aDestFolder == array->Operation(a).TargetMessageId()) )
       
   816 			{
       
   817 			// only write out the first operation found
       
   818 			if (found == 0)
       
   819 				{
       
   820 				foundAt = a;
       
   821 				aOp.CopyL( array->Operation(a) );
       
   822 				}
       
   823 			found++;
       
   824 			}
       
   825 		}
       
   826 
       
   827 	// optionally now delete the operation from the array
       
   828 	if (aDelete && foundAt != -1)
       
   829 		{
       
   830 		array->Delete(foundAt);
       
   831 		
       
   832 		arraystore.StoreL(*store);
       
   833 		store->CommitL();
       
   834 		}
       
   835 	
       
   836 	CleanupStack::PopAndDestroy(2);	// store, array
       
   837 
       
   838 	return found;
       
   839 	}
       
   840 
       
   841 // this means remove the cause of the delete, ie remove delete or
       
   842 // change move to copy, unless ConvertToCopy is False in which case
       
   843 // delete any move operation rather than convert it.
       
   844 
       
   845 // there can only be one relevant operation in the array as the UI or
       
   846 // MTM should have prevented further operations
       
   847 
       
   848 // Deleting any shadow entry should be done outside this function
       
   849 
       
   850 void CImap4OffLineControl::UndeleteOperationL(TMsvId aId, TMsvId aDestId, TBool aConvertMoveToCopy,
       
   851 										 TImDisconnectedOperationType aDisconnected)
       
   852 	{
       
   853 	DBG((_L8("UndeleteOperation: Id %x CvtMove %d type %d"),
       
   854 		 aId, aConvertMoveToCopy, aDisconnected));
       
   855 
       
   856 	// We need an array, to store the current offline operations of this folder
       
   857     CImOffLineOperationArray *array=CImOffLineOperationArray::NewL();
       
   858 	CleanupStack::PushL(array);
       
   859 	CImOffLineArrayStore arraystore(*array);
       
   860 
       
   861 	SetEntryL(FindOffLineSaveFolderL(aId, aDestId));
       
   862 	DBG((_L8("UndeleteOperation: opending savefolder store %x"), iEntry->Entry().Id() ));
       
   863 	CMsvStore *store=iEntry->EditStoreL();
       
   864 	CleanupStack::PushL(store);
       
   865 
       
   866 	arraystore.RestoreL(*store);
       
   867 
       
   868 	// look in the array for a delete or move operation on this Id
       
   869 	CImOffLineOperation thisOp;
       
   870 	for(TInt a=0; a<array->CountOperations(); a++)
       
   871 		{
       
   872 		thisOp.CopyL(array->Operation(a));
       
   873 
       
   874 		if (thisOp.MessageId() == aId)
       
   875 			{
       
   876 			TBool finish = ETrue;
       
   877 			TBool isDelete = EFalse;
       
   878 			
       
   879 			switch (thisOp.OpType())
       
   880 				{
       
   881 				// if move then convert it to an equivalent copy
       
   882 			case CImOffLineOperation::EOffLineOpMoveToLocal:
       
   883 				thisOp.SetCopyToLocal(aId, thisOp.TargetMessageId());
       
   884 				break;
       
   885 
       
   886 			case CImOffLineOperation::EOffLineOpMoveFromLocal:
       
   887 				thisOp.SetCopyFromLocal(aId, thisOp.TargetMessageId());
       
   888 				break;
       
   889 
       
   890 			case CImOffLineOperation::EOffLineOpMoveWithinService:
       
   891 				thisOp.SetCopyWithinService(aId, thisOp.TargetMessageId());
       
   892 				break;
       
   893 
       
   894 				// if delete then get rid of the pending operation
       
   895 			case CImOffLineOperation::EOffLineOpDelete:
       
   896 				isDelete = ETrue;
       
   897 				break;
       
   898 
       
   899 			default:
       
   900 				finish = EFalse;
       
   901 				break;
       
   902 				}
       
   903 
       
   904 			if (finish)
       
   905 				{
       
   906 				// remove the existing operation
       
   907 				array->Delete(a);
       
   908 
       
   909 				// potentially add a new one
       
   910 				if (!isDelete)
       
   911 					{
       
   912 					// it's become a copy so insert at head of list
       
   913 					if (aConvertMoveToCopy)
       
   914 						array->InsertOperationL(thisOp, 0);
       
   915 					}
       
   916 
       
   917 				// exit 'for' loop and so we don't need to fix up the
       
   918 				// iterator
       
   919 				break;
       
   920 				}
       
   921 			}
       
   922 		}
       
   923 
       
   924 	DBG((_L8("UndeleteOperation: write store")));
       
   925 
       
   926 	// write back offline op array
       
   927 	arraystore.StoreL(*store);
       
   928 	store->CommitL();
       
   929 	
       
   930 	CleanupStack::PopAndDestroy(2);	// store, array
       
   931 
       
   932 	DBG((_L8("UndeleteOperation: ensure visible")));
       
   933 
       
   934 	// then make the item visible and update its pending operation
       
   935 	// type
       
   936 	SetEntryL(aId);
       
   937 	TMsvEmailEntry entry = iEntry->Entry();
       
   938 
       
   939 	entry.SetDisconnectedOperation(aDisconnected);
       
   940 	entry.SetVisible(ETrue);
       
   941 
       
   942 	ChangeEntryL(entry);
       
   943 
       
   944 	DBG((_L8("UndeleteOperation: done")));
       
   945 	}
       
   946 
       
   947 // Make shadow for offline operation - this shadow indicates what
       
   948 // *will* happen at the next sync
       
   949 
       
   950 // Note if we want to copy the entire structure of the message then
       
   951 // there is a ready made function Imap4Session->CopyMessageL() to do
       
   952 // this
       
   953 void CImap4OffLineControl::MakeCopyMoveShadowL(const CImOffLineOperation& aOp)
       
   954 	{
       
   955 	// get copy of the original message
       
   956 	SetEntryL(aOp.MessageId());
       
   957 	TMsvEmailEntry origMsg = iEntry->Entry();
       
   958 
       
   959 	// check this is a real message, we don't make shadows of parts
       
   960 	if (origMsg.iType != KUidMsvMessageEntry)
       
   961 		return;
       
   962 
       
   963 	// if this is not a copy to mirror only operation then make shadow
       
   964 	if ( aOp.OpType() != CImOffLineOperation::EOffLineOpMtmSpecific )
       
   965 		{
       
   966 		// copy out the non embedded data
       
   967 		HBufC* details = origMsg.iDetails.AllocL();
       
   968 		CleanupStack::PushL(details);
       
   969 		HBufC* description = origMsg.iDescription.AllocL();
       
   970 		CleanupStack::PushL(description);
       
   971 
       
   972 		// set up the new message, clearing any disconnected op flags
       
   973 		// it may have
       
   974 		TMsvEmailEntry newMsg = origMsg;
       
   975 		newMsg.iRelatedId = aOp.MessageId();
       
   976 		newMsg.SetComplete(EFalse);
       
   977 		newMsg.SetDisconnectedOperation(ENoDisconnectedOperations);
       
   978 		// ensure that this one is visible (may be copied from one
       
   979 		// that wasn't)
       
   980 		newMsg.SetVisible(ETrue);
       
   981 		
       
   982 		// create shadow entry
       
   983 		SetEntryL(aOp.TargetMessageId());
       
   984 
       
   985 		newMsg.iDetails.Set(details->Des());
       
   986 		newMsg.iDescription.Set(description->Des());
       
   987 		User::LeaveIfError(iEntry->CreateEntry(newMsg));
       
   988 		
       
   989 		CleanupStack::PopAndDestroy(2);	// description, details
       
   990 		}
       
   991 	
       
   992 	// set flags on the original message
       
   993 	SetEntryL(origMsg.Id());
       
   994 
       
   995 	if (origMsg.DisconnectedOperation() == ENoDisconnectedOperations)
       
   996 		origMsg.SetDisconnectedOperation( OffLineOpToDisconnectedOp(aOp) );
       
   997 	else
       
   998 		origMsg.SetDisconnectedOperation( EDisconnectedMultipleOperation );
       
   999 
       
  1000 	// make original invisible if this was a move operation
       
  1001 	if (!OffLineOpIsCopy(aOp))
       
  1002 		origMsg.SetVisible(EFalse);
       
  1003 
       
  1004 	// write back changes
       
  1005 	ChangeEntryL(origMsg);
       
  1006 	}
       
  1007 
       
  1008 void CImap4OffLineControl::MakeShadowL(const CImOffLineOperation& aOp)
       
  1009 	{
       
  1010 	DBG((_L8("MakeShadow: of %x in folder %x"), aOp.MessageId(), aOp.TargetMessageId()));
       
  1011 
       
  1012 	switch (aOp.OpType())
       
  1013 		{
       
  1014 	case CImOffLineOperation::EOffLineOpMtmSpecific: // populate
       
  1015 	case CImOffLineOperation::EOffLineOpMoveToLocal:
       
  1016 	case CImOffLineOperation::EOffLineOpMoveFromLocal:
       
  1017 	case CImOffLineOperation::EOffLineOpMoveWithinService:
       
  1018 	case CImOffLineOperation::EOffLineOpCopyToLocal:
       
  1019 	case CImOffLineOperation::EOffLineOpCopyFromLocal:
       
  1020 	case CImOffLineOperation::EOffLineOpCopyWithinService:
       
  1021 		MakeCopyMoveShadowL(aOp);
       
  1022 		break;
       
  1023 		
       
  1024 	case CImOffLineOperation::EOffLineOpDelete:
       
  1025 		// Set the pending operation to Delete, we don't care if there
       
  1026 		// were other operations already pending
       
  1027 		{
       
  1028 		SetEntryL(aOp.MessageId());
       
  1029 		TMsvEmailEntry msg = iEntry->Entry();
       
  1030 		msg.SetDisconnectedOperation(EDisconnectedDeleteOperation);
       
  1031 		ChangeEntryL(msg);
       
  1032 		}
       
  1033 		break;
       
  1034 	
       
  1035 	case CImOffLineOperation::EOffLineOpNone:
       
  1036 	case CImOffLineOperation::EOffLineOpChange:
       
  1037 	case CImOffLineOperation::EOffLineOpCreate:
       
  1038 		__ASSERT_DEBUG(0, gPanic(EBadUseOfOffLineOp));
       
  1039 		break;
       
  1040 		}
       
  1041 
       
  1042 	}
       
  1043 
       
  1044 // look in the folder for an item whose iRelatedId matches
       
  1045 TBool CImap4OffLineControl::FindShadowIdsL(const CImOffLineOperation& aOp, CMsvEntrySelection& aSelection)
       
  1046 	{
       
  1047 	CMsvEntrySelection* selection=new (ELeave) CMsvEntrySelection;
       
  1048 	CleanupStack::PushL(selection);
       
  1049 
       
  1050 	SetEntryL(aOp.TargetMessageId());
       
  1051 	User::LeaveIfError(iEntry->GetChildren(*selection));
       
  1052 
       
  1053 	TBool foundOne = EFalse;
       
  1054 	for(TInt child=0;child<selection->Count();child++)
       
  1055 		{
       
  1056 		TMsvId childId = (*selection)[child];
       
  1057 		SetEntryL(childId);
       
  1058 		TMsvEntry message = iEntry->Entry();
       
  1059 		if (message.iRelatedId == aOp.MessageId())
       
  1060 			{
       
  1061 			aSelection.InsertL(0, childId);
       
  1062 			foundOne = ETrue;
       
  1063 			}
       
  1064 		}
       
  1065 
       
  1066 	CleanupStack::PopAndDestroy();
       
  1067 
       
  1068 	return foundOne;
       
  1069 	}
       
  1070 
       
  1071 TMsvId CImap4OffLineControl::FindShadowIdL(const CImOffLineOperation& aOp)
       
  1072 	{
       
  1073 	CMsvEntrySelection* selection=new (ELeave) CMsvEntrySelection;
       
  1074 	CleanupStack::PushL(selection);
       
  1075 
       
  1076 	TMsvId id = KMsvNullIndexEntryId;
       
  1077 
       
  1078 	// the target folder might have been deleted - in which case just
       
  1079 	// return that the id was not found
       
  1080 	if (iEntry->SetEntry(aOp.TargetMessageId()) == KErrNone)
       
  1081 		{
       
  1082 		User::LeaveIfError(iEntry->GetChildren(*selection));
       
  1083 		for(TInt child=0;child<selection->Count();child++)
       
  1084 			{
       
  1085 			TMsvId childId = (*selection)[child];
       
  1086 			SetEntryL(childId);
       
  1087 			TMsvEntry message = iEntry->Entry();
       
  1088 			if (message.iRelatedId == aOp.MessageId())
       
  1089 				{
       
  1090 				id = childId;
       
  1091 				break;
       
  1092 				}
       
  1093 			}
       
  1094 		}
       
  1095 
       
  1096 	CleanupStack::PopAndDestroy();
       
  1097 
       
  1098 	return id;
       
  1099 	}
       
  1100 
       
  1101 void CImap4OffLineControl::UndoOfflineOpL(const CImOffLineOperation& aOp, TBool aClearMultiples)
       
  1102 	{
       
  1103 #ifdef PRINTING
       
  1104 	TPtrC8 p = OffLineOpTypeString(aOp);
       
  1105 	DBG((_L8("UndoOfflineOp: %S Id %x TargetFolder %x"),
       
  1106 		 &p, aOp.MessageId(), aOp.TargetMessageId()));
       
  1107 #endif
       
  1108 	
       
  1109 	// get the first id related to the source of this message, unless
       
  1110 	// it has no destination (ie it is a delete op)
       
  1111 	if (aOp.TargetMessageId())
       
  1112 		{
       
  1113 		TMsvId id = FindShadowIdL(aOp);
       
  1114 		if (id != KMsvNullIndexEntryId)
       
  1115 			{
       
  1116 			SetEntryL(aOp.TargetMessageId());
       
  1117 			iEntry->DeleteEntry(id);
       
  1118 			}
       
  1119 		}
       
  1120 
       
  1121 	// remove the disconnected op flags from the source entry and make
       
  1122 	// it visible (does't harm if it was visible anyway), if it has
       
  1123 	// multiple ops then we leave it as we don't know what to do.
       
  1124 
       
  1125 	// entry might not exist if it was a shadow
       
  1126 	if (iEntry->SetEntry(aOp.MessageId()) == KErrNone)
       
  1127 		{
       
  1128 		TMsvEmailEntry entry = iEntry->Entry();
       
  1129 		if (!entry.Visible() || aClearMultiples ||
       
  1130 			entry.DisconnectedOperation() != EDisconnectedMultipleOperation)
       
  1131 			{
       
  1132 			entry.SetDisconnectedOperation(ENoDisconnectedOperations);
       
  1133 			entry.SetVisible(ETrue);
       
  1134 			ChangeEntryL(entry);
       
  1135 			}
       
  1136 		}
       
  1137 	}
       
  1138 
       
  1139 void CImap4OffLineControl::PrepareLocalOpL(TMsvId aId)
       
  1140 	{
       
  1141 	SetEntryL(aId);
       
  1142 
       
  1143 	// clear the disconnected op flag
       
  1144 	TMsvEmailEntry entry = iEntry->Entry();
       
  1145 	entry.SetDisconnectedOperation(ENoDisconnectedOperations);
       
  1146 	ChangeEntryL(entry);
       
  1147 		
       
  1148 	SetEntryL(iEntry->Entry().Parent());
       
  1149 	}
       
  1150 
       
  1151 TBool CImap4OffLineControl::DoLocalOpL()
       
  1152 	{
       
  1153 	if (iCopyDirect->Count())
       
  1154 		{
       
  1155 		TMsvId id = (*iCopyDirect)[0];
       
  1156 
       
  1157 		DBG((_L8("CImap4OffLineControl::DoLocalOp Copy id %x to do %d"),
       
  1158 			 id, iCopyDirect->Count()));
       
  1159 
       
  1160 		PrepareLocalOpL(id);
       
  1161 		
       
  1162 		SetActive();
       
  1163 		iEntry->CopyEntryL(id, iDestination, iStatus);
       
  1164 		return ETrue;
       
  1165 		}
       
  1166 
       
  1167 	if (iMoveDirect->Count())
       
  1168 		{
       
  1169 		TMsvId id = (*iMoveDirect)[0];
       
  1170 
       
  1171 		DBG((_L8("CImap4OffLineControl::DoLocalOp Move id %x to do %d"),
       
  1172 			 id, iMoveDirect->Count()));
       
  1173 
       
  1174 		PrepareLocalOpL(id);
       
  1175 
       
  1176 		SetActive();
       
  1177 		iEntry->MoveEntryL(id, iDestination, iStatus);
       
  1178 		return ETrue;
       
  1179 		}
       
  1180 
       
  1181 	if (iMoveToLocalDirect->Count())
       
  1182 		{
       
  1183 		TMsvId id = (*iMoveToLocalDirect)[0];
       
  1184 	
       
  1185 		DBG((_L8("CImap4OffLineControl::DoDirectMoveToLocalOp Move id %x to do %d"),
       
  1186 			 id, iMoveToLocalDirect->Count()));
       
  1187 	
       
  1188 		PrepareLocalOpL(id);
       
  1189 	
       
  1190 		SetActive();
       
  1191 		iEntry->CopyEntryL(id, iDestination, iStatus);	//	I do mean Copy
       
  1192 		return ETrue;
       
  1193 		}
       
  1194 	
       
  1195 	return EFalse;
       
  1196 	}
       
  1197 
       
  1198 // ----------------------------------------------------------------------
       
  1199 
       
  1200 void CImap4OffLineControl::DoCancel()
       
  1201 	{
       
  1202 	CMsgActive::DoCancel();
       
  1203 	}
       
  1204 
       
  1205 void CImap4OffLineControl::DoComplete(TInt& /*aStatus*/)
       
  1206 	{
       
  1207 
       
  1208 	}
       
  1209 
       
  1210 void CImap4OffLineControl::DoRunL()
       
  1211 	{
       
  1212 	DBG((_L8("CImap4OffLineControl::DoRunL")));
       
  1213 
       
  1214 	// successfully copied/moved the item
       
  1215 	
       
  1216 	// Remove completed item from selection
       
  1217 	if (iCopyDirect->Count())
       
  1218 		iCopyDirect->Delete(0,1);
       
  1219 	else if (iMoveDirect->Count())
       
  1220 		iMoveDirect->Delete(0,1);
       
  1221 	else
       
  1222 		{
       
  1223 		//	We managed to do the copy portion of a move to local
       
  1224 		//	Now we need to queue up a delete of the original which
       
  1225 		//	is still in the remote mailbox.
       
  1226 		CImOffLineOperation op;
       
  1227 		op.SetDelete((*iMoveToLocalDirect)[0]);
       
  1228 		iMoveToLocalDirect->Delete(0,1);
       
  1229 		SaveOperationL(op);
       
  1230 		}
       
  1231 
       
  1232 	// Operation done. Do next one in selection
       
  1233 	DoLocalOpL();
       
  1234 	}
       
  1235 
       
  1236 // ----------------------------------------------------------------------