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