email/pop3andsmtpmtm/imapservermtm/src/FLDSYNC.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 folder list synchronise
       
    15 // This class deals with keeping the local mirror folder tree in sync with the
       
    16 // remote server's directory structure.
       
    17 // 980914 - Modifications to local tree traverse so that it will not
       
    18 // look for children below message level: previously, it was
       
    19 // picking up attachment folders in messages!
       
    20 // 990304 - Orphaned folder check no longer orphans the inbox: we
       
    21 // basically never touch it. The problem with orphaning it
       
    22 // comes about with servers which have a namespace (ie
       
    23 // folder path) of INBOX, which means we never "see" the
       
    24 // actual inbox as we are inside it. We still know it
       
    25 // exists, though.
       
    26 // 990502 - Was calling CMsgActive::Cancel() as opposed to DoCancel().
       
    27 // 
       
    28 //
       
    29 
       
    30 #include <e32base.h>
       
    31 #include <e32cons.h>
       
    32 #include <mentact.h>
       
    33 #include "fldsync.h"
       
    34 #include "impspan.h"
       
    35 #include "imapsess.h"
       
    36 
       
    37 #ifdef _DEBUG
       
    38 #define LOG_COMMANDS(a) a
       
    39 #define DBG(a) a
       
    40 #define PRINTING
       
    41 #else
       
    42 #define LOG_COMMANDS(a)
       
    43 #define DBG(a)
       
    44 #undef PRINTING
       
    45 #endif
       
    46 
       
    47 // Priority of MsgActive object
       
    48 const TInt EFolderSyncPriority=1;
       
    49 
       
    50 
       
    51 // Debugging of folder tree traversal
       
    52 #undef DEBUG_FOLDERSYNC
       
    53 
       
    54 CImImap4FolderSync::CImImap4FolderSync():CMsgActive(EFolderSyncPriority)
       
    55 	{
       
    56 	__DECLARE_NAME(_S("CImImap4FolderSync"));
       
    57 	}
       
    58 
       
    59 CImImap4FolderSync::~CImImap4FolderSync()
       
    60 	{
       
    61 	// Global bits
       
    62 	delete iFolderContents;
       
    63 
       
    64 	if (iFolderList)
       
    65 		iFolderList->ResetAndDestroy();
       
    66 	delete iFolderList;
       
    67 
       
    68 	// Get rid of bits at each level
       
    69 	for(TInt a=0;a<KFolderDepth;a++)
       
    70 		delete iFolderIds[a];
       
    71 
       
    72 	// Delete local folder list
       
    73 	delete iLocalFolders;
       
    74 	}
       
    75 
       
    76 CImImap4FolderSync* CImImap4FolderSync::NewLC(CImImap4Session *aSession)
       
    77 	{
       
    78 	CImImap4FolderSync* self=new (ELeave) CImImap4FolderSync();
       
    79 	CleanupStack::PushL(self);
       
    80 
       
    81 	// Non-trivial constructor
       
    82 	self->ConstructL(aSession);
       
    83 	return self;
       
    84 	}
       
    85 
       
    86 CImImap4FolderSync* CImImap4FolderSync::NewL(CImImap4Session *aSession)
       
    87 	{
       
    88 	CImImap4FolderSync* self=NewLC(aSession);
       
    89 	CleanupStack::Pop();
       
    90 	return self;
       
    91 	}
       
    92 
       
    93 // The non-trivial constructor
       
    94 void CImImap4FolderSync::ConstructL(CImImap4Session *aSession)
       
    95 	{
       
    96 	// Save session
       
    97 	iSession=aSession;
       
    98 
       
    99 	// We're an active object...
       
   100 	CActiveScheduler::Add(this);
       
   101 
       
   102 	// One-off bits
       
   103 	iFolderList=new (ELeave) CArrayPtrFlat<CImImap4DirStruct>(8);
       
   104 	iFolderContents=new (ELeave) CMsvEntrySelection;
       
   105 
       
   106 	// ...and per-level bits
       
   107 	for(TInt a=0;a<KFolderDepth;a++)
       
   108 		iFolderIds[a]=new (ELeave) CArrayFixFlat<TMsvId>(8);
       
   109 
       
   110 	// Local folder list
       
   111 	iLocalFolders=new (ELeave) CArrayFixFlat<TMsvId>(16);
       
   112 	}
       
   113 
       
   114 // Do setentry, leave if there is an error
       
   115 void CImImap4FolderSync::SetEntryL(const TMsvId aId)
       
   116 	{
       
   117 	User::LeaveIfError(iEntry->SetEntry(aId));
       
   118 	}
       
   119 
       
   120 // Change entry, leave if error
       
   121 void CImImap4FolderSync::ChangeEntryL(const TMsvEntry& aEntry)
       
   122 	{
       
   123 	User::LeaveIfError(iEntry->ChangeEntry(aEntry));
       
   124 	}
       
   125 
       
   126 // Change entry in bulk mode (i.e. no index file commit), leave if error
       
   127 void CImImap4FolderSync::ChangeEntryBulkL(const TMsvEntry& aEntry)
       
   128 	{
       
   129 	User::LeaveIfError(iEntry->ChangeEntryBulk(aEntry));
       
   130 	}
       
   131 // Get children, leave if error
       
   132 void CImImap4FolderSync::GetChildrenL(CMsvEntrySelection& aSelection)
       
   133 	{
       
   134 	User::LeaveIfError(iEntry->GetChildren(aSelection));
       
   135 	}
       
   136 
       
   137 // Set the entry to use to talk to the server
       
   138 void CImImap4FolderSync::SetEntry(CMsvServerEntry* aEntry)
       
   139 	{
       
   140 	// Save it
       
   141 	iEntry=aEntry;
       
   142 	}
       
   143 
       
   144 // Build list of local folders
       
   145 void CImImap4FolderSync::BuildLocalL(const TMsvId aFolder, const TBool aDoThisOne)
       
   146 	{
       
   147 	// Select it
       
   148 	SetEntryL(aFolder);
       
   149 	TMsvEmailEntry entry=iEntry->Entry();
       
   150 
       
   151 	// If we're a folder, set the flag
       
   152 	if (entry.iType==KUidMsvFolderEntry && aDoThisOne)
       
   153 		{
       
   154 		// Add to list
       
   155 		iLocalFolders->AppendL(iEntry->Entry().Id());
       
   156 		}
       
   157 
       
   158 	// If current entry is a message, don't look for children
       
   159 	if (entry.iType==KUidMsvMessageEntry)
       
   160 		return;
       
   161 
       
   162 	// Any children?
       
   163 	CMsvEntrySelection *children=new (ELeave) CMsvEntrySelection;
       
   164 	CleanupStack::PushL(children);
       
   165 	GetChildrenL(*children);
       
   166 	if (children->Count())
       
   167 		{
       
   168 		// Do each in turn
       
   169 		for(TInt child=0;child<children->Count();child++)
       
   170 			BuildLocalL((*children)[child],ETrue);
       
   171 		}
       
   172 	CleanupStack::PopAndDestroy();
       
   173 	}
       
   174 
       
   175 // Called when async child completes
       
   176 void CImImap4FolderSync::DoRunL()
       
   177 	{
       
   178 	if (ProcessDirListL())
       
   179 		{
       
   180 		// Done whole list, check to see what has vanished on the
       
   181 		// server with regard to what's in the mirror: basically,
       
   182 		// see what's still flagged in the tree.
       
   183 		OrphanedFolderCheckL();
       
   184 
       
   185 		// All done!
       
   186 		Complete(KErrNone);
       
   187 		}
       
   188 	else
       
   189 		{
       
   190 		// Otherwise, another command has been issued
       
   191 		SetActive();
       
   192 		}
       
   193 	}
       
   194 
       
   195 // Cancel this operation
       
   196 void CImImap4FolderSync::DoCancel()
       
   197 	{
       
   198 	DBG((iSession->LogText(_L8("CImImap4FolderSync::DoCancel()"))));
       
   199 
       
   200 	// Cancel any outstanding session operation
       
   201 	iSession->Cancel();
       
   202 
       
   203 	// ...and parent
       
   204 	CMsgActive::DoCancel();
       
   205 	}
       
   206 
       
   207 // Check for orphaned folders
       
   208 void CImImap4FolderSync::OrphanedFolderCheckL()
       
   209 	{
       
   210 	DBG((iSession->LogText(_L8("CImImap4FolderSync::OrphanedFolderCheckL()"))));
       
   211 
       
   212 	// Set stats
       
   213 	iOrphanedFolders=iLocalFolders->Count();
       
   214 
       
   215 	// Selection for storing children
       
   216 	CMsvEntrySelection *children=new CMsvEntrySelection;
       
   217 	CleanupStack::PushL(children);
       
   218 
       
   219 	// Any folders left in iLocalFolders are orphans
       
   220 	for(TInt a=0;a<iLocalFolders->Count();a++)
       
   221 		{
       
   222 		// Go to this folder
       
   223 		if (iEntry->SetEntry((*iLocalFolders)[a])!=KErrNone)
       
   224 			{
       
   225 			// Probably been removed already (nested?)
       
   226 			continue;
       
   227 			}
       
   228 		TMsvEmailEntry entry=iEntry->Entry();
       
   229 
       
   230 		// Is it the INBOX? If so, ignore it
       
   231 		if (entry.Parent()==iServiceId &&
       
   232 			entry.iDetails.CompareF(KIMAP_INBOX)==0)
       
   233 			continue;
       
   234 
       
   235 		// Check all the entries in the folder
       
   236 		TInt subfolders=0;
       
   237 		children->Reset();
       
   238 		GetChildrenL(*children);
       
   239 		if (children->Count())
       
   240 			{
       
   241 			// Check each message
       
   242 			for(TInt a=0;a<children->Count();a++)
       
   243 				{
       
   244 				SetEntryL((*children)[a]);
       
   245 				TMsvEmailEntry child=iEntry->Entry();
       
   246 				if (child.iType==KUidMsvMessageEntry)
       
   247 					{
       
   248 					// Orphan this message: it may delete it, if there
       
   249 					// are no downloaded bodyparts
       
   250 
       
   251 					DBG((iSession->LogText(_L8("Orphaning message %x"),(*children)[a])));
       
   252 
       
   253 					iSession->OrphanMessageL((*children)[a]);
       
   254 					}
       
   255 				else if (child.iType==KUidMsvFolderEntry)
       
   256 					{
       
   257 					// There's a subfolder
       
   258 					subfolders++;
       
   259 					}
       
   260 				}
       
   261 			}
       
   262 
       
   263 		// Check number of children again: if all the orphaned children
       
   264 		// have been deleted (ie, they had no downloaded body parts)
       
   265 		// then we can delete the folder locally, otherwise we just have
       
   266 		// to mark it as orphaned.
       
   267 		SetEntryL((*iLocalFolders)[a]);
       
   268 		GetChildrenL(*children);
       
   269 		if ((children->Count() || subfolders>0) && !entry.Orphan())
       
   270 			{
       
   271 
       
   272 			DBG((iSession->LogText(_L8("Orphaning folder %x"),(*iLocalFolders)[a])));
       
   273 
       
   274 			// Orphan folder
       
   275 			TMsvEmailEntry message=iEntry->Entry();
       
   276 			message.SetOrphan(ETrue);
       
   277 			ChangeEntryL(message);
       
   278 			}
       
   279 		else
       
   280 			{
       
   281 
       
   282 			DBG((iSession->LogText(_L8("Deleting folder %x"),(*iLocalFolders)[a])));
       
   283 
       
   284 			// Delete folder
       
   285 			SetEntryL(iEntry->Entry().Parent());
       
   286 			iEntry->DeleteEntry((*iLocalFolders)[a]);
       
   287 			}
       
   288 		}
       
   289 
       
   290 	// Get rid of children list
       
   291 	CleanupStack::PopAndDestroy();
       
   292 
       
   293 	// Remove list
       
   294 	iLocalFolders->Reset();
       
   295 
       
   296 	DBG((iSession->LogText(_L8("CImImap4FolderSync::OrphanedFolderCheckL() done"))));
       
   297 	}
       
   298 
       
   299 // Process returned directory list
       
   300 TBool CImImap4FolderSync::ProcessDirListL()
       
   301 	{
       
   302 	// We've got a list, and the mirror folder ID that the contents belong to:
       
   303 	// process it, and mirror the folder structure
       
   304 
       
   305 	// First, find the existing children of this folder from the message server
       
   306 	SetEntryL(iFolderId);
       
   307 	GetChildrenL(*iFolderContents);
       
   308 	TInt noofchildren=iFolderContents->Count();
       
   309 
       
   310 	// Number of items in reply from server
       
   311 	TInt noofreplies=iFolderList->Count();
       
   312 
       
   313 #ifdef DEBUG_FOLDERSYNC
       
   314 	iSession->LogText(_L("Level %d: Server has %d entries, mirror has %d"),
       
   315 					  iFolderLevel,noofreplies,noofchildren);
       
   316 #endif
       
   317 
       
   318 	// Empty the 'to do' array
       
   319 	iFolderIds[iFolderLevel]->Reset();
       
   320 
       
   321 	// ...for each entry in reply
       
   322 	for(TInt replyentry=0;replyentry<noofreplies;replyentry++)
       
   323 		{
       
   324 		// Does this already exist?
       
   325 		TInt a;
       
   326 		TMsvId thisentry;
       
   327 		for(a=0;a<noofchildren;a++)
       
   328 			{
       
   329 			// See if the details field matches the server folder name.
       
   330 			// For the INBOX folder the match should be case insensitive, all other folders
       
   331 			// we must perform a case sensitive match
       
   332 			SetEntryL(thisentry=(*iFolderContents)[a]);
       
   333 			TPtrC entryName = iEntry->Entry().iDetails;
       
   334 			TPtrC svrFolderName = (*iFolderList)[replyentry]->Leafname();
       
   335 			TBool foldersMatch = EFalse;
       
   336 			if(svrFolderName.CompareF(KIMAP_INBOX)==0)
       
   337 				{
       
   338 				// Server folder is the INBOX
       
   339 				foldersMatch = (entryName.CompareF(svrFolderName)==0);
       
   340 				}
       
   341 			else
       
   342 				{
       
   343 				// Server folder is not the INBOX
       
   344 				foldersMatch = (entryName.Compare(svrFolderName)==0);
       
   345 				}
       
   346 				
       
   347 			if (foldersMatch)
       
   348 				{
       
   349 				// Update mailbox flag as necessary, but NOT if it's the inbox, which is
       
   350 				// *ALWAYS* a mailbox
       
   351 				TMsvEmailEntry folder=iEntry->Entry();
       
   352 				if ((*iFolderList)[replyentry]->iIsMailbox!=folder.Mailbox())
       
   353 					{
       
   354 					if (folder.Parent()!=iServiceId ||
       
   355 					    folder.iDetails.CompareF(KIMAP_INBOX)!=0)
       
   356 						{
       
   357 						folder.SetMailbox((*iFolderList)[replyentry]->iIsMailbox);
       
   358 						ChangeEntryBulkL(folder);
       
   359 						}
       
   360 					else
       
   361 						{
       
   362 						DBG((iSession->LogText(_L8("Skipping Mailbox flag twiddle for folder %x (=%d)"),
       
   363 						  folder.Id(),(*iFolderList)[replyentry]->iIsMailbox)));
       
   364 						}
       
   365 					}
       
   366 
       
   367 				// Exists: remove from local folders list
       
   368 				for(TInt b=0;b<iLocalFolders->Count();b++)
       
   369 					{
       
   370 					// Found it?
       
   371 					if ((*iLocalFolders)[b]==thisentry)
       
   372 						{
       
   373 						// Remove from list
       
   374 						iLocalFolders->Delete(b,1);
       
   375 						break;
       
   376 						}
       
   377 					}
       
   378 
       
   379 				// Exit loop
       
   380 				break;
       
   381 				}
       
   382 			}
       
   383 
       
   384 		// Found a match?
       
   385 		if (a==noofchildren)
       
   386 			{
       
   387 			// No, create it
       
   388 			TMsvEmailEntry message;
       
   389 			
       
   390 			// All flags unset (urrgh!)
       
   391 			message.SetMtmData1(0);
       
   392 			message.SetMtmData2(0);
       
   393 			message.SetMtmData3(0);
       
   394 
       
   395 			message.iType=KUidMsvFolderEntry;
       
   396 			message.iMtm=iEntry->Entry().iMtm;
       
   397 			message.iServiceId=iServiceId;
       
   398 			message.iSize=0;
       
   399 			message.iDetails.Set((*iFolderList)[replyentry]->Leafname());
       
   400 			message.SetValidUID(EFalse);
       
   401 			message.SetComplete(ETrue);
       
   402 
       
   403 			// Visibility
       
   404 			if (iNewFoldersAreInvisible)
       
   405 				message.SetVisible(EFalse);
       
   406 
       
   407 			// Is a mailbox? (basically, is it selectable?).
       
   408 			message.SetMailbox((*iFolderList)[replyentry]->iIsMailbox);
       
   409 
       
   410 #ifdef DEBUG_FOLDERSYNC
       
   411 			iSession->LogText(_L("Creating folder '%S'"),&message.iDetails);
       
   412 #endif
       
   413 			SetEntryL(iFolderId);
       
   414 			User::LeaveIfError(iEntry->CreateEntryBulk(message));
       
   415 
       
   416 			// Increment stats
       
   417 			iNewFolders++;
       
   418 			
       
   419 			// Get the ID, incase we're going into this level
       
   420 			thisentry=message.Id();
       
   421 			}
       
   422 
       
   423 		// Does this have children? (and are we at the end of our nesting levels?)
       
   424 		if ((*iFolderList)[replyentry]->iIsFolder && iFolderLevel<(KFolderDepth-1))
       
   425 			{
       
   426 #ifdef DEBUG_FOLDERSYNC
       
   427 			iSession->LogText(_L("Adding folder to todolist, level %d"),iFolderLevel);
       
   428 #endif
       
   429 			// Add it to the list to scan at this level
       
   430 			iFolderIds[iFolderLevel]->AppendL(thisentry);
       
   431 			}
       
   432 		}
       
   433 
       
   434 #ifdef DEBUG_FOLDERSYNC
       
   435 	iSession->LogText(_L("Level %d: %d items in todolist"),iFolderLevel,iFolderIds[iFolderLevel]->Count());
       
   436 #endif
       
   437 
       
   438 	// Anything in our 'to do' queue?
       
   439 	while(!iFolderIds[iFolderLevel]->Count())
       
   440 		{
       
   441 #ifdef DEBUG_FOLDERSYNC
       
   442 		iSession->LogText(_L("Nothing to do at level %d"),iFolderLevel);
       
   443 #endif
       
   444 		// Nothing at this level: are we at the top?
       
   445 		if (iFolderLevel==0)
       
   446 			{
       
   447 #ifdef DEBUG_FOLDERSYNC
       
   448 			iSession->LogText(_L("At level 0: completing"));
       
   449 #endif
       
   450 			// All done!
       
   451 			// Commit any outstanding bulk creates
       
   452 			SetEntryL(iFolderId);
       
   453 			iEntry->CompleteBulk();
       
   454 			return(ETrue);
       
   455 			}
       
   456 
       
   457 #ifdef DEBUG_FOLDERSYNC
       
   458 		iSession->LogText(_L("Going up a level"));
       
   459 #endif
       
   460 		// Not at the top: back up a level
       
   461 		iFolderLevel--;
       
   462 		}
       
   463 
       
   464 	// Something to process: remove it from todo list
       
   465 	iFolderId=(*iFolderIds[iFolderLevel])[0];
       
   466 	iFolderIds[iFolderLevel]->Delete(0,1);
       
   467 
       
   468 	// Nest & issue command
       
   469 	iFolderLevel++;
       
   470 	iSession->ListL(iStatus,iFolderId,iFolderList);
       
   471 
       
   472 #ifdef DEBUG_FOLDERSYNC
       
   473 	iSession->LogText(_L("Stuff to do at level %d - scanning dir %x"),iFolderLevel,iFolderId);
       
   474 #endif
       
   475 	return(EFalse);
       
   476 	}
       
   477 
       
   478 // Synchronise mirror tree with remote server
       
   479 void CImImap4FolderSync::SynchroniseTreeL(TRequestStatus& aStatus, const TMsvId aService, const TBool aNewFoldersAreInvisible)
       
   480 	{
       
   481 	LOG_COMMANDS((iSession->LogText(_L8("COMMAND CImImap4FolderSync::SychroniseTree (serviceid=%x)"),aService)));
       
   482 
       
   483 	Queue(aStatus);
       
   484 
       
   485 	// Note where this list came from
       
   486 	iServiceId=aService;
       
   487 	iFolderId=iServiceId;
       
   488 	iFolderLevel=0;
       
   489 
       
   490 	// Save invisibility state
       
   491 	iNewFoldersAreInvisible=aNewFoldersAreInvisible;
       
   492 
       
   493 	// Set flags on all folders, so we can tell which ones aren't matched by the
       
   494 	// list returned from the server.
       
   495 	iLocalFolders->Reset();
       
   496 	BuildLocalL(iServiceId,EFalse);
       
   497 
       
   498 	// List this tree
       
   499 	iSession->ListL(iStatus,iFolderId,iFolderList);
       
   500 	SetActive();
       
   501 	}
       
   502 
       
   503 void CImImap4FolderSync::IncProgress(TImap4SyncProgress& aProgress)
       
   504 	{
       
   505 	aProgress.iNewFolders+=iNewFolders;
       
   506 	aProgress.iOrphanedFolders+=iOrphanedFolders;
       
   507 	}
       
   508 
       
   509 void CImImap4FolderSync::ResetStats()
       
   510 	{
       
   511 	// initialise counts
       
   512 	iNewFolders=0;
       
   513 	iOrphanedFolders=0;
       
   514 	}