email/imap4mtm/imapprotocolcontroller/src/cimapopfetchbody.cpp
changeset 80 8b14b30db193
equal deleted inserted replaced
79:2981cb3aa489 80:8b14b30db193
       
     1 // Copyright (c) 2006-2010 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 // cimapopfetchbody.cpp
       
    15 //
       
    16 
       
    17 #include <imapset.h>
       
    18 #include <mentact.h>
       
    19 
       
    20 #include "cimapopfetchbody.h"
       
    21 #include "cimapbodystructure.h"
       
    22 #include "cimapenvelope.h"
       
    23 #include "cimapfetchresponse.h"
       
    24 #include "cfetchbodyinfo.h"
       
    25 #include "cimapfetchbodyresponse.h"
       
    26 #include "cimaprfc822headerfields.h"
       
    27 #include "cimapmimeheaderfields.h"
       
    28 #include "cimapsession.h"
       
    29 #include "cimapsettings.h"
       
    30 #include "cimapmailstore.h"
       
    31 #include "cimaputils.h"
       
    32 #include "cimaplogger.h"
       
    33 #include <imcvtext.h>
       
    34 #include <imcvcodc.h>
       
    35 #include <imcvutil.h>
       
    36 #include <cimcaf.h>
       
    37 #include "cimapsyncmanager.h"
       
    38 #include "cimapfolder.h"
       
    39 
       
    40 #include <charconv.h>
       
    41 #include <miuthdr.h>
       
    42 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS  
       
    43 #include "timrfc822datefield.h"
       
    44 #include "cimconvertheader.h"
       
    45 #endif
       
    46 
       
    47 _LIT8(KIMAP_NIL, "NIL");
       
    48 
       
    49 // MIME message types
       
    50 _LIT8(KMIME_TEXT, "TEXT");
       
    51 _LIT8(KMIME_HTML, "HTML");
       
    52 _LIT8(KMIME_XVCARD, "X-VCARD");
       
    53 _LIT8(KMIME_VCALENDAR, "X-VCALENDAR");
       
    54 _LIT8(KMIME_ICALENDAR, "CALENDAR");
       
    55 _LIT8(KMIME_RTF, "RTF");
       
    56 _LIT8(KMIME_NAME, "NAME");
       
    57 _LIT8(KMIME_NAME_RFC2231, "NAME*");
       
    58 _LIT8(KMIME_ATTACHMENT, "ATTACHMENT");
       
    59 _LIT8(KMIME_FILENAME, "FILENAME");
       
    60 _LIT8(KMIME_FILENAME_RFC2231, "FILENAME*");
       
    61 
       
    62 // Encoding types
       
    63 _LIT8(KMIME_BASE64, "BASE64");
       
    64 
       
    65 _LIT8(KIMAP_REL_PATH_SEPARATOR, ".");
       
    66 
       
    67 // Header fields to fetch when fetching the body structure.
       
    68 _LIT8(KImapFetchLargeHeaderFields, "From Subject Reply-to To Cc Bcc Message-ID Return-Receipt-To X-Return-Receipt-To Disposition-Notification-To Disposition-Notification-Options");
       
    69 
       
    70 // No longer fetch the following fields
       
    71 // Received - date set on sync
       
    72 // Date     - date set on sync
       
    73 // Subject  - fetched during sync - copy from TMsvEmailEntry into CImHeader
       
    74 // From		- fetched during sync - copy from TMsvEmailEntry into CImHeader
       
    75 
       
    76 // Positive completion errors for FindFilenameL()
       
    77 const TInt KErrRFC2231Encoded = 2;
       
    78 
       
    79 
       
    80 // Efficient and SAFE way of comparing TBools which might have different integers representing TRUE
       
    81 inline TBool BoolsAreEqual( TBool aA, TBool aB )
       
    82 	{
       
    83 	return ((aA && aB) || (!aA && !aB));
       
    84 	}
       
    85 	
       
    86 inline TBool BoolsAreNotEqual( TBool aA, TBool aB )
       
    87 	{
       
    88 	return ((!aA || !aB) && (aA || aB));
       
    89 	}
       
    90 
       
    91 CImapOpFetchBody::CImapOpFetchBody( CImapSession*& aSession,
       
    92 									CImapSyncManager& aSyncManager, 
       
    93 					  				CMsvServerEntry& aServerEntry,
       
    94 					  				CImapSettings& aImapSettings, 
       
    95 					  				CImapMailStore& aMailStore)
       
    96 : CImapOperation(aSession, aServerEntry, EPriorityStandard),
       
    97   iSyncManager(aSyncManager),
       
    98   iImapSettings(aImapSettings), iMailStore(aMailStore)
       
    99   
       
   100    	{
       
   101 	
       
   102 	}
       
   103 
       
   104 CImapOpFetchBody::~CImapOpFetchBody()
       
   105 	{
       
   106 	Cancel();
       
   107 	// Fetch Response objects
       
   108 	delete iFetchResponse;
       
   109 	delete iFetchBodyResponse;
       
   110 	iFetchList.ResetAndDestroy();
       
   111 
       
   112 	// CAF support
       
   113 	delete iCaf;
       
   114 
       
   115 	// Characterset conversion
       
   116 	delete iHeaderConverter;
       
   117 	delete iCharConv;
       
   118 	delete iCharacterConverter;
       
   119 	}
       
   120 
       
   121 /**
       
   122 Static construction for CImapOpFetchBody class.
       
   123 
       
   124 @param aSession      connected IMAP Session object to use.
       
   125 @param aServerEntry  access to the message entry array.
       
   126 @param aImapSettings access to the settings for the IMAP Service.
       
   127 @return the newly created CImapOpFetchBody object. The caller is responsible for
       
   128 deletion.
       
   129 */
       
   130 CImapOpFetchBody* CImapOpFetchBody::NewL(CImapSession*& aSession,CImapSyncManager& aSyncManager, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings, CImapMailStore& aMailStore)
       
   131 	{
       
   132 	CImapOpFetchBody* self = new (ELeave) CImapOpFetchBody(aSession, aSyncManager, aServerEntry, aImapSettings, aMailStore);
       
   133 	CleanupStack::PushL(self);
       
   134 	self->ConstructL();
       
   135 	CleanupStack::Pop(self);
       
   136 	return self;
       
   137 	}
       
   138 
       
   139 void CImapOpFetchBody::ConstructL()
       
   140 	{
       
   141 	// Caf utils
       
   142 	iCaf = new (ELeave) CImCaf(CImapUtils::GetRef().Fs());
       
   143 	
       
   144 	// Create converter objects
       
   145 	iCharacterConverter=CCnvCharacterSetConverter::NewL();
       
   146 	iCharConv=CImConvertCharconv::NewL(*iCharacterConverter, CImapUtils::GetRef().Fs());
       
   147 	iHeaderConverter=CImConvertHeader::NewL(*iCharConv); 
       
   148 	
       
   149 	// we assume that this message is MIME as we have no way of
       
   150 	// detecting otherwise (without sending a new FETCH to the
       
   151 	// server to get the MIME-Version).
       
   152 	iHeaderConverter->SetMessageType(ETrue);
       
   153 	
       
   154 	CActiveScheduler::Add(this);
       
   155 	}
       
   156 
       
   157 /**
       
   158 Fetches the specified bodypart and all parts that exist below it that match
       
   159 the specified fetch criteria. Thus if called with the root of a message as
       
   160 the specified part, then all message parts that satisfy the criteria will
       
   161 be fetched, up to the entire message contents.
       
   162 
       
   163 @param aStatus request status of the Proctocol Controller
       
   164 @param aPart the ID in the local message store of the message part to fetch.
       
   165 @param aGetPartialMailInfo: partial fetch settings.
       
   166 */
       
   167 void CImapOpFetchBody::FetchBodyL(TRequestStatus& aRequestStatus, const TMsvId aPart, const TImImap4GetPartialMailInfo& aGetPartialMailInfo)
       
   168 	{
       
   169 	__LOG_TEXT(iSession->LogId(), "CImapOpFetchBody::FetchBodyL(): fetching message");
       
   170 
       
   171 	iRequestedPart=aPart;
       
   172 	iGetPartialMailInfo=aGetPartialMailInfo;
       
   173 	Queue(aRequestStatus);
       
   174 	// sets the next state and sets active
       
   175 	DoFetchL();
       
   176 	}
       
   177 
       
   178 /**
       
   179 Initialises the fetch of the requested body parts.
       
   180 If there is nothing to fetch (ie the client has requested a fetch of headers 
       
   181 only, which are fetched during synchronise) the user request is completed.
       
   182 Otherwise, if the body structure representation for the message already exists
       
   183 in the message store, the tree structure is parsed to identify parts to fetch.
       
   184 Otherwise, a request is issued for the required message headers and bodystructure.
       
   185 */
       
   186 void CImapOpFetchBody::DoFetchL()
       
   187 	{
       
   188  	// Reset stats
       
   189  	iBytesToDo=0;
       
   190  	iBytesDone=0;
       
   191  	iFetchList.ResetAndDestroy();
       
   192 	iHtmlEntryPart = 0;
       
   193 	iBodyTextSize = 0;
       
   194 	iHtmlEntrySize = 0;
       
   195 	iBodyPartRemainingSize = 0;
       
   196 	iSizeOfToBeFetchedAttachments=0;
       
   197 	iHasTextParts = 0;
       
   198 
       
   199 	CheckForPartialPopulate();
       
   200 	
       
   201 	// if we only want headers then there is nothing to do as they
       
   202 	// have already been fetched on synchronisation. We complete here.
       
   203 	if(!iFetchPartialMail && iGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailHeaders)
       
   204 		{
       
   205 		__LOG_TEXT(iSession->LogId(), "CImapOpFetchBody:DoFetchL(): Nothing to fetch, completing");
       
   206 		Complete(KErrNone);
       
   207 		return;
       
   208 		}
       
   209 
       
   210 	// Get info on what is being fetching
       
   211 	SetEntryL(iRequestedPart);
       
   212 	
       
   213 	// Find the root entry for the message this part belongs to.
       
   214 	// No need to check the selected folder is correct - the 
       
   215 	// compound operation checks this.
       
   216   	TBool messageFound = EFalse;
       
   217   	while(!messageFound)
       
   218 		{
       
   219 		// Reached the message ?
       
   220 		if (iServerEntry.Entry().iType==KUidMsvMessageEntry)
       
   221 			{
       
   222 			messageFound = ETrue;
       
   223 			TMsvEmailEntry entry = iServerEntry.Entry();
       
   224 			iMessageUid  = entry.UID();
       
   225 			iMessageMsvId  = entry.Id();
       
   226 			
       
   227 			// indicate that a fetch operation has been performed on this message.
       
   228 			// This is used to prevent subsequent fetches during 2-phase sync.
       
   229 			// ((TMsvEmailEntry&)iServerEntry.Entry()).
       
   230 			entry.SetValidUID(ETrue);
       
   231 			User::LeaveIfError(iServerEntry.ChangeEntry(entry));
       
   232 
       
   233   			if (!(iServerEntry.Entry().Owner()))
       
   234   				{
       
   235 				// If there are no child entries then we need to fetch 
       
   236 				// the envelope and body structure
       
   237   				FetchLargeHeaderL();
       
   238   				}
       
   239   			else
       
   240   				{
       
   241   				// If the structure is already present then build up an array of
       
   242   				// parts to be fetched from the data in the message server entry
       
   243   				// array and associated mime parts. Do this in the RunL
       
   244   				iCurrentStep=iNextStep=EBuildFetchList;
       
   245   				// complete self
       
   246   				SetActive();
       
   247 				TRequestStatus* status = &iStatus;
       
   248 				User::RequestComplete(status, KErrNone);
       
   249   				}
       
   250    			}
       
   251 		// Up a level
       
   252 		SetEntryL(iServerEntry.Entry().Parent());
       
   253 		} // end while(!messageFound)
       
   254 	}
       
   255 
       
   256 /**
       
   257 Checks if the partial mail download parameters are set to default 
       
   258 and the full download mail option is set, then this is a request for full download.
       
   259 Allows for efficiency when identifying message parts to fetch.
       
   260 */
       
   261 void CImapOpFetchBody::CheckForPartialPopulate()
       
   262 	{
       
   263 	if(iGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits &&
       
   264 		iGetPartialMailInfo.iTotalSizeLimit == KMaxTInt &&
       
   265 		iGetPartialMailInfo.iBodyTextSizeLimit == KMaxTInt && 
       
   266 		iGetPartialMailInfo.iAttachmentSizeLimit == KMaxTInt && 
       
   267 		(iGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailHeaders || 
       
   268 		iGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailBodyText ||
       
   269 		iGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailBodyTextAndAttachments ||
       
   270 		iGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailAttachments ||
       
   271 		iGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailBodyAlternativeText))
       
   272 		{
       
   273 		__LOG_TEXT(iSession->LogId(), "CImapOpFetchBody: Doing non-partial fetch");
       
   274 		iFetchPartialMail = EFalse;
       
   275 		}
       
   276 	else
       
   277 		{
       
   278 		__LOG_TEXT(iSession->LogId(), "CImapOpFetchBody: Doing partial fetch");
       
   279 		iFetchPartialMail = ETrue;
       
   280 		}
       
   281 	}
       
   282 
       
   283 /**
       
   284 Issues a request to IMAP Session to fetch the IMAP Header details and bodystructure.
       
   285 */
       
   286 void CImapOpFetchBody::FetchLargeHeaderL()
       
   287  	{
       
   288   	// Issue the fetch request to the imap session
       
   289   	delete iFetchResponse;
       
   290   	iFetchResponse = NULL;
       
   291   	iFetchResponse = CImapFetchResponse::NewL();
       
   292   	iCurrentStep = EGetBodyStructure;
       
   293   	iNextStep = EProcessBodyStructure;
       
   294   	iSession->FetchBodyStructureAndHeadersL(iStatus, iMessageUid, KImapFetchLargeHeaderFields, *iFetchResponse);
       
   295   	SetActive();
       
   296    	}
       
   297 
       
   298 
       
   299 
       
   300 /**
       
   301 Called when asynchronous service requests complete.
       
   302 Handles errors returned by aSynchronous services. 
       
   303 Implements the state handling for message fetching.
       
   304 */
       
   305 void CImapOpFetchBody::DoRunL()
       
   306 	{
       
   307 	// Handle any server errors
       
   308 	if (iStatus.Int()!=KErrNone)
       
   309 		{
       
   310 		if (TInt err=ProcessServerError() != KErrNone)
       
   311 			{
       
   312 			Complete(err);
       
   313 			return;
       
   314 			}
       
   315 		}
       
   316 	
       
   317 	iCurrentStep = iNextStep;
       
   318 
       
   319 	switch (iCurrentStep)
       
   320 		{
       
   321 	case EProcessBodyStructure:
       
   322 		{
       
   323 		if (ProcessBodyStructureL())
       
   324 			{
       
   325 			iNextStep = EFetchFirstPart;
       
   326 			// complete self
       
   327   			SetActive();
       
   328 			TRequestStatus* status = &iStatus;
       
   329 			User::RequestComplete(status, KErrNone);
       
   330 			}
       
   331 		else
       
   332 			{
       
   333 			// No body structure. Message has probably been deleted from server but local
       
   334 			// copy has not yet been removed.
       
   335 			Complete(KErrNone);
       
   336 			return;
       
   337 			}
       
   338 		break;
       
   339 		}
       
   340 	case EBuildFetchList:
       
   341 		{
       
   342 		DoBuildFetchListL();
       
   343 		iCurrentStep = EFetchFirstPart;
       
   344 		// falls through to fetch the first item in the fetch array.
       
   345 		}
       
   346 	case EFetchFirstPart:
       
   347 		{
       
   348 		// initialise progress counters:
       
   349 		iPartsToDo=iFetchList.Count();
       
   350 		iPartsDone=0;
       
   351 
       
   352  		if (FetchPartL())
       
   353  			{
       
   354  			//DBG((LogText(_L8("Starting body fetch of %d parts (%d bytes)"),
       
   355 	 		iNextStep = EFetchNext;
       
   356  			}
       
   357  		else
       
   358  			{
       
   359  			// No parts identified for fetch - complete the user request.
       
   360  			Complete(KErrNone);
       
   361  			return;
       
   362  			}
       
   363 	 	break;
       
   364 		}
       
   365 	case EFetchNext:
       
   366 		{
       
   367 		FetchPartCompleteL();
       
   368 		
       
   369  		// Issue fetch for the next part in the array
       
   370 		if (FetchPartL())
       
   371  			{
       
   372 	 		iNextStep = EFetchNext;
       
   373  			}
       
   374  		else
       
   375  			{
       
   376  			// No more parts to fetch - complete the user request.
       
   377  			iCurrentStep=EFinished;
       
   378  			Complete(KErrNone);
       
   379  			return;
       
   380  			}
       
   381  		break;
       
   382 		}
       
   383 	default:
       
   384 		{
       
   385 		User::Leave(KErrNotSupported);
       
   386 		}
       
   387 		}
       
   388 
       
   389 	if (!IsActive())
       
   390 		{
       
   391 		SetActive();
       
   392 		}
       
   393 	}
       
   394 
       
   395 /**
       
   396 Special form of Cancel(), which enables message currently being fetched to be resumed.
       
   397 */
       
   398 void CImapOpFetchBody::CancelEnableResume()
       
   399 	{
       
   400 	Cancel();
       
   401 	TRAP_IGNORE(ClearFetchAttemptedL());
       
   402 	}
       
   403 
       
   404 /** 
       
   405 Clears the fetch-attempted flag (re-use of ValidUID flag) to indicate that the
       
   406 cancelled fetch may be resumed according to download rules.
       
   407 */
       
   408 void CImapOpFetchBody::ClearFetchAttemptedL()
       
   409 	{
       
   410 	SetEntryL(iMessageMsvId);
       
   411 	TMsvEmailEntry entry = iServerEntry.Entry();
       
   412 	entry.SetValidUID(EFalse);
       
   413 	User::LeaveIfError(iServerEntry.ChangeEntry(entry));
       
   414 	}
       
   415 
       
   416 /**
       
   417 Called by Cancel() to cancel asynchronous service requests
       
   418 */
       
   419 void CImapOpFetchBody::DoCancel()
       
   420 	{
       
   421 	switch (iCurrentStep)
       
   422 		{
       
   423 	// States involving a session request
       
   424 	case EGetBodyStructure:
       
   425 	case EFetchFirstPart:
       
   426 	case EFetchNext:
       
   427 		{
       
   428 		iSession->Cancel();
       
   429 		break;
       
   430 		}
       
   431 	// Self completing or synchronous states	
       
   432 	case EProcessBodyStructure:
       
   433 	case EBuildFetchList:
       
   434 	default:
       
   435 		{
       
   436 		break;
       
   437 		}
       
   438 		} // end of switch (iCurrentStep)
       
   439 	CMsgActive::DoCancel();
       
   440 	}
       
   441 
       
   442 /**
       
   443 Called when a user request is completed.
       
   444 */
       
   445 #ifdef __IMAP_LOGGING
       
   446 void CImapOpFetchBody::DoComplete(TInt& aErr)
       
   447 #else
       
   448 void CImapOpFetchBody::DoComplete(TInt& /*aErr*/)
       
   449 #endif //__IMAP_LOGGING
       
   450 	{
       
   451 	__LOG_FORMAT((iSession->LogId(), "CImapOpFetchBody::DoComplete(aErr = %d) CurrentStep = %d", aErr, iCurrentStep));
       
   452 	}
       
   453 
       
   454 /**
       
   455 Builds a list of parts to fetch from the server entry array.
       
   456 This method is used when the mailstore representation of the bodystructure 
       
   457 for the message has previously been constructed.
       
   458 */
       
   459 void CImapOpFetchBody::DoBuildFetchListL()
       
   460 	{
       
   461  	SetEntryL(iRequestedPart);
       
   462  	TMsvEmailEntry temp = (TMsvEmailEntry)iServerEntry.Entry();
       
   463  	TUid type=temp.iType;
       
   464  
       
   465  	// if we are not asking for a Message type then override the get
       
   466 	// options to ensure that this is fetched
       
   467  	if(!iFetchPartialMail)
       
   468 		{
       
   469 		if (type != KUidMsvMessageEntry)
       
   470  			iGetPartialMailInfo.iGetMailBodyParts = EGetImap4EmailBodyTextAndAttachments;
       
   471 		}
       
   472  				
       
   473  	// Build a list of parts to fetch: 
       
   474  	BuildFetchListL(iRequestedPart);
       
   475 
       
   476 	UpdateBodyTextRemainingSizeForHtml();
       
   477 	}
       
   478 
       
   479 
       
   480 /**
       
   481 Add the message part identified by aMessage and aMimeHeader to the array of 
       
   482 parts to fetch.
       
   483 
       
   484 Creates a CFetchBodyInfo object for each part to be fetched. This is the data
       
   485 that the session requires to fetch each message part.
       
   486 
       
   487 Note that this function should only be called when the part has been correctly
       
   488 identified as a part for fetching.
       
   489 
       
   490 @param aMessage     the TMsvEmailEntry for the part.
       
   491 @param aMimeHeader  the mime header for the part.
       
   492 @param aIsText	    bool indicating whether this is a text part.
       
   493 @param aSizeToFetch	amount of data to fetch - this may indicate a partial fetch
       
   494                     if less than the total size of the part.
       
   495 */
       
   496 void CImapOpFetchBody::AddFetchItemL(CImMimeHeader& aMimeHeader, TMsvEmailEntry& aMessage, TBool aIsText, TInt32 aSizeToFetch)
       
   497 	{
       
   498 	CFetchBodyInfo* fetchInfo=CFetchBodyInfo::NewLC(aMessage.Id());
       
   499 	fetchInfo->SetSizeToFetch(aSizeToFetch);
       
   500 	fetchInfo->SetBodyPartRemainingSize(aMessage.iBioType - aSizeToFetch);
       
   501 	fetchInfo->SetRelativePathL(aMimeHeader.RelativePath()); 
       
   502 	fetchInfo->SetIsText(aIsText);
       
   503 	fetchInfo->SetContentTransferEncoding(aMimeHeader.ContentTransferEncoding());
       
   504 	fetchInfo->iEmbed = iEmbed;
       
   505 	if (aIsText)
       
   506 		{
       
   507 		fetchInfo->SetCharsetId(aMimeHeader.MimeCharset());
       
   508 		}
       
   509 	else if (aMimeHeader.ContentType().MatchF(KImcvApplication) == 0)
       
   510 		{
       
   511 		// Set the CAF pointer if the attachment is an application.
       
   512 		// This is not registered at this stage, this is done just
       
   513 		// prior to issuing the fetch request. This allows a single
       
   514 		// CImCaf instance to be re-used.
       
   515 		fetchInfo->SetCaf(iCaf);
       
   516 		}
       
   517 
       
   518 	iFetchList.AppendL(fetchInfo);
       
   519 	CleanupStack::Pop(fetchInfo);
       
   520 
       
   521 	// Add this part's size to the size total
       
   522 	iBytesToDo+=aMessage.iBioType;
       
   523 	}
       
   524 
       
   525 /**
       
   526 Determines if an item is to be fetched, depending on the parttypes specified.
       
   527 Items are also rejected if they have been marked complete due to having 0 
       
   528 length. An attachment info object is created for attachment items rejected 
       
   529 this way.
       
   530 
       
   531 @param aMessage    the TMsvEmailEntry for the part.
       
   532 @param aMimeHeader the mime header for the part.
       
   533 @param aPartTypes  Get-Mail options specifying the parts to be fetched.
       
   534 */
       
   535 void CImapOpFetchBody::AddFilterFetchItemL(CImMimeHeader& aMimeHeader, TMsvEmailEntry& aEntry, TImap4GetMailOptions& aPartTypes)
       
   536 	{
       
   537 	__LOG_FORMAT((iSession->LogId(), "CImapOpFetchBody::AddFilterFetchItemL(entry id = %x) using get mail options", aEntry.Id()));
       
   538 	TUid type = aEntry.iType;
       
   539 	
       
   540 	// Defensive - nothing to fetch for folder and message entry types...
       
   541 	if (type == KUidMsvFolderEntry || type == KUidMsvMessageEntry)
       
   542 		{
       
   543 		return;
       
   544 		}
       
   545 	
       
   546 	// Part may be marked complete because it has 0 size,
       
   547 	// therefore we don't need to fetch anything.
       
   548 	if (aEntry.Complete() && !aEntry.PartialDownloaded())
       
   549 		{
       
   550 		__LOG_TEXT(iSession->LogId(), "  Skipping, already complete.");
       
   551 		
       
   552 		// If this is an attachment which has been marked complete because it has
       
   553   		// zero size, we still need to add it to the attachment manager.
       
   554  		if ((type == KUidMsvAttachmentEntry || type == KUidMsvEmailExternalBodyEntry) &&
       
   555 		     (aEntry.iSize == 0))
       
   556 			{
       
   557 			CreateAttachmentInfoL(iServerEntry.Entry());
       
   558  			}
       
   559 		return;
       
   560 		}
       
   561 	
       
   562 	// All other entry types may require fetching
       
   563 	TBool addPart = EFalse;
       
   564 	TBool isText  = EFalse;
       
   565 	
       
   566 	TBool addingTextParts = (aPartTypes == EGetImap4EmailBodyText ||
       
   567 							 aPartTypes == EGetImap4EmailBodyTextAndAttachments ||
       
   568 							 aPartTypes == EGetImap4EmailBodyAlternativeText);
       
   569 	
       
   570 	if (type == KUidMsvEmailTextEntry || type == KUidMsvEmailHtmlEntry || type == KUidMsvEmailRtfEntry)
       
   571 		{
       
   572 		if (type == KUidMsvEmailTextEntry)
       
   573 			{
       
   574 			isText = ETrue;
       
   575 			}
       
   576 
       
   577 		iHasTextParts = ETrue;
       
   578 
       
   579 		if (addingTextParts)
       
   580 			{
       
   581 			addPart = ETrue;
       
   582 			}
       
   583 		}
       
   584 	else if (type == KUidMsvAttachmentEntry || type == KUidMsvEmailExternalBodyEntry)
       
   585 		{
       
   586 		if (aPartTypes == EGetImap4EmailBodyTextAndAttachments ||
       
   587 			aPartTypes == EGetImap4EmailAttachments)
       
   588 			{
       
   589 			addPart = ETrue;
       
   590 			}
       
   591 		else
       
   592 			{
       
   593 			SetEntryL(aEntry.Parent());
       
   594 			TImEmailFolderType folderType = static_cast<TMsvEmailEntry>((iServerEntry.Entry())).MessageFolderType();
       
   595 			SetEntryL(aEntry.Id());
       
   596 			
       
   597 			if(folderType==EFolderTypeRelated && addingTextParts)
       
   598 				{
       
   599 				// if asked for bodytext and it is an attachment then fetch it if 
       
   600 				// attachment is in a folder of type Multipart/Related as it is most 
       
   601 				// likely part of an MHTML document
       
   602 				addPart = ETrue;
       
   603 				}
       
   604 			else if( ( folderType == EFolderTypeAlternative || folderType == EFolderTypeUnknown )
       
   605 			        && aPartTypes == EGetImap4EmailBodyAlternativeText)
       
   606 				{
       
   607 				// if non-HTML text alternative parts are requested, the alternative
       
   608 				// folder is checked and get the mime content type for the part
       
   609 				if(aMimeHeader.ContentType().CompareF(KMIME_TEXT)==0)
       
   610 					{
       
   611 					// This is a alternative text part, and should be treated
       
   612 					// as a text part
       
   613 					addPart = ETrue;
       
   614 					}
       
   615 				}
       
   616 
       
   617 			// Create attachment info in the messaging store
       
   618 			if(!addPart)
       
   619 				{
       
   620 				CreateAttachmentInfoL(iServerEntry.Entry());
       
   621 				}		
       
   622 			}
       
   623 		}
       
   624 	else
       
   625 		{
       
   626 		__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyUnknownMsvType));
       
   627 		addPart = ETrue;
       
   628 		}
       
   629 
       
   630 	if (addPart)
       
   631 		{
       
   632 		AddFetchItemL(aMimeHeader, aEntry, isText, aEntry.iBioType);
       
   633 		}
       
   634 	}
       
   635 
       
   636 /**
       
   637 Determines if an item is to be fetched, depending on the partial mail 
       
   638 options specified.
       
   639 Items are also rejected if they have been marked complete due to having 0 
       
   640 length. An attachment info object is created for attachment items rejected 
       
   641 this way.
       
   642 
       
   643 @param aMessage    the TMsvEmailEntry for the part.
       
   644 @param aMimeHeader the mime header for the part.
       
   645 @param aGetPartialMailInfo partial mail options describes parts to fetch.
       
   646 */
       
   647 void CImapOpFetchBody::AddFilterFetchItemL(CImMimeHeader& aMimeHeader, TMsvEmailEntry& aEntry, TImImap4GetPartialMailInfo& aGetPartialMailInfo)
       
   648 	{
       
   649 	__LOG_FORMAT((iSession->LogId(), "CImapOpFetchBody::AddFilterFetchItemL(entry id = %x) using partial mail options", aEntry.Id()));
       
   650 	TUid type = aEntry.iType;
       
   651 	
       
   652 	// Defensive - nothing to fetch for folder and message entry types...
       
   653 	if (type == KUidMsvFolderEntry || type == KUidMsvMessageEntry)
       
   654 		{
       
   655 		return;
       
   656 		}
       
   657 	
       
   658 	// Part may be marked complete because it has 0 size,
       
   659 	// therefore we don't need to fetch anything.
       
   660 	if (aEntry.Complete() && !aEntry.PartialDownloaded())
       
   661 		{
       
   662 		__LOG_TEXT(iSession->LogId(), "  Skipping, already complete.");
       
   663 		
       
   664 		// If this is an attachment which has been marked complete because it has
       
   665   		// zero size, we still need to add it to the attachment manager.
       
   666  		if ((aEntry.iType == KUidMsvAttachmentEntry || aEntry.iType == KUidMsvEmailExternalBodyEntry) 
       
   667  			&& (aEntry.iSize == 0))
       
   668 			{
       
   669 			CreateAttachmentInfoL(iServerEntry.Entry());
       
   670  			}
       
   671 		return;
       
   672 		}
       
   673 	
       
   674 	// All other entry types may require fetching
       
   675 	TBool addPart = EFalse;
       
   676 	TBool isText  = EFalse;
       
   677 	TInt  sizeToFetch = aEntry.iBioType;
       
   678 	
       
   679 	if (type == KUidMsvEmailTextEntry)
       
   680 		{
       
   681 		iHasTextParts = ETrue;
       
   682 		isText = ETrue;
       
   683 		if(aGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits)
       
   684 			{
       
   685 			addPart = ETrue;
       
   686 			}
       
   687 		else if (aGetPartialMailInfo.iPartialMailOptions == ECumulative)
       
   688 			{
       
   689 			if(iGetPartialMailInfo.iTotalSizeLimit > 0)
       
   690 				{
       
   691 				addPart = ETrue;
       
   692 				sizeToFetch = Minimum(aEntry.iBioType, iGetPartialMailInfo.iTotalSizeLimit);
       
   693 				iBodyTextSize = aEntry.iBioType;
       
   694 				}
       
   695 			}
       
   696 		else if (aGetPartialMailInfo.iPartialMailOptions == EBodyTextOnly ||
       
   697 			 	 aGetPartialMailInfo.iPartialMailOptions == EBodyTextAndAttachments ||
       
   698 				 aGetPartialMailInfo.iPartialMailOptions == EBodyAlternativeText )
       
   699 			{
       
   700 			addPart = ETrue;
       
   701 			sizeToFetch = Minimum(aEntry.iBioType, iGetPartialMailInfo.iBodyTextSizeLimit);
       
   702 			iBodyTextSize = aEntry.iBioType;
       
   703 			}
       
   704 		}
       
   705 	else if (type == KUidMsvEmailHtmlEntry)
       
   706 		{
       
   707 		iHasTextParts = ETrue;
       
   708 		iHtmlEntrySize = aEntry.iBioType;
       
   709 		if(aGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits)
       
   710 			{
       
   711 			addPart = ETrue;
       
   712 			}
       
   713 		else if (aGetPartialMailInfo.iPartialMailOptions == ECumulative)
       
   714 			{
       
   715 			if((iGetPartialMailInfo.iTotalSizeLimit > 0 ) && 
       
   716 				((iBodyTextSize + aEntry.iBioType) <= iGetPartialMailInfo.iTotalSizeLimit))
       
   717 				{
       
   718 				addPart = ETrue;
       
   719 				}
       
   720 			}
       
   721 		else if (aGetPartialMailInfo.iPartialMailOptions == EBodyTextOnly ||
       
   722 			 	 aGetPartialMailInfo.iPartialMailOptions == EBodyTextAndAttachments ||
       
   723 				 aGetPartialMailInfo.iPartialMailOptions == EBodyAlternativeText )
       
   724 				
       
   725 			{	
       
   726 			if(iBodyTextSize + aEntry.iBioType <=
       
   727 				Minimum(iGetPartialMailInfo.iBodyTextSizeLimit,iGetPartialMailInfo.iTotalSizeLimit))
       
   728 				{
       
   729 				addPart = ETrue;
       
   730 				}
       
   731 			}
       
   732 		// In case of html entry, store html entry id to check later,(when attaching partial footer 
       
   733 		// message)if whole body text is downloaded and the html size is not to be downloaded 
       
   734 		if(addPart)
       
   735 			{
       
   736 			iHtmlEntryPart = aEntry.Id();
       
   737 			}
       
   738 		}
       
   739 	else if (type == KUidMsvAttachmentEntry || type == KUidMsvEmailExternalBodyEntry)
       
   740 		{
       
   741 		if(aGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits)
       
   742 			{
       
   743 			addPart = ETrue;
       
   744 			}
       
   745 		else if (aGetPartialMailInfo.iPartialMailOptions == ECumulative)
       
   746 			{
       
   747 			if(iGetPartialMailInfo.iTotalSizeLimit > 0 && ((iBodyTextSize + iSizeOfToBeFetchedAttachments + aEntry.iBioType) <= iGetPartialMailInfo.iTotalSizeLimit))
       
   748 				{
       
   749 				addPart = ETrue;
       
   750 				if((iBodyTextSize + iSizeOfToBeFetchedAttachments + aEntry.iBioType + iHtmlEntrySize) >= iGetPartialMailInfo.iTotalSizeLimit)
       
   751 					{
       
   752 					RemoveHtmlPart(iHtmlEntryPart);
       
   753 					}
       
   754 				iSizeOfToBeFetchedAttachments+=aEntry.iBioType;
       
   755 				}	
       
   756 			else
       
   757 				{
       
   758 				CreateAttachmentInfoL(aEntry);
       
   759 				// for Ecumulative option ,after the body part downloading, check if there is any 
       
   760 				// attachment which can be downloaded , then check if the html part can be included.
       
   761 				if((iBodyTextSize + iSizeOfToBeFetchedAttachments + iHtmlEntrySize) >= iGetPartialMailInfo.iTotalSizeLimit)
       
   762 					{
       
   763 					RemoveHtmlPart(iHtmlEntryPart);
       
   764 					}
       
   765 				}
       
   766 			}
       
   767 		else if (aGetPartialMailInfo.iPartialMailOptions == EAttachmentsOnly ||
       
   768 				 aGetPartialMailInfo.iPartialMailOptions == EBodyTextAndAttachments)
       
   769 			{
       
   770 			if(aEntry.iBioType <= Minimum(iGetPartialMailInfo.iAttachmentSizeLimit,iGetPartialMailInfo.iTotalSizeLimit))
       
   771 				{
       
   772 				addPart = ETrue;
       
   773 				}
       
   774 			else
       
   775 				{
       
   776 				CreateAttachmentInfoL(aEntry);
       
   777 				}
       
   778 			}
       
   779 		else
       
   780 			{
       
   781 			SetEntryL(iServerEntry.Entry().Parent());
       
   782 			TImEmailFolderType folderType = static_cast<TMsvEmailEntry>((iServerEntry.Entry())).MessageFolderType();
       
   783 			SetEntryL(aEntry.Id());
       
   784 			
       
   785 			if( folderType==EFolderTypeRelated )
       
   786 				{
       
   787 				// if asked for bodytext and it is an attachment then fetch it
       
   788 				// if attachment is in a folder of Multipart/Related as it is 
       
   789 				// most likely part of an MHTML document
       
   790 				addPart = ETrue;
       
   791 				}
       
   792 			else if( folderType==EFolderTypeAlternative && 
       
   793 					 aGetPartialMailInfo.iPartialMailOptions==EBodyAlternativeText &&
       
   794 					 aEntry.iBioType <= Minimum(iGetPartialMailInfo.iAttachmentSizeLimit,
       
   795 					 									 iGetPartialMailInfo.iTotalSizeLimit) )
       
   796 				{
       
   797 				// if non-HTML text alternative parts are requested, the alternative
       
   798 				// folder is checked and get the mime content type for the part
       
   799 				if( aMimeHeader.ContentType().CompareF(KMIME_TEXT)==0 )
       
   800 					{
       
   801 					// This is a alternative text part, and should be treated
       
   802 					// as a text part
       
   803 					addPart = ETrue;
       
   804 					}
       
   805 				}
       
   806 				
       
   807 			if(!addPart)
       
   808 				{
       
   809 				CreateAttachmentInfoL(aEntry);
       
   810 				}
       
   811 			}
       
   812 		}
       
   813 	else
       
   814 		{
       
   815 		__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyUnknownMsvType));
       
   816 
       
   817 		// for anything else, if not debug mode then fetch anyway
       
   818 		addPart = ETrue;
       
   819 		}
       
   820 
       
   821 	if (addPart)
       
   822 		{
       
   823 		AddFetchItemL(aMimeHeader, aEntry, isText, sizeToFetch);	
       
   824 		}
       
   825 	}
       
   826 
       
   827 /**
       
   828 Builds an array of message parts to fetch.
       
   829 This is a function is recursive, calling itself for entry parts that have
       
   830 children, thus building up an array of parts to fetch for the entire message.
       
   831 
       
   832 This is a simple form of this function, used when performing a non-partial
       
   833 fetch of the message.
       
   834 
       
   835 @param aPart         The part to process
       
   836 @param aPartTypes    Specifies which components to fetch
       
   837 */
       
   838 void CImapOpFetchBody::BuildFetchListL(TMsvId aPart)
       
   839 	{
       
   840 	__LOG_FORMAT((iSession->LogId(), "CImapOpFetchBody::BuildFetchListL(%x)", aPart));
       
   841 
       
   842 	// Is this part fetchable?
       
   843 	SetEntryL(aPart);
       
   844 
       
   845 	TUid type = iServerEntry.Entry().iType;
       
   846 	
       
   847 	// Folder and Message Entry entry types...
       
   848 	if (type == KUidMsvFolderEntry || type == KUidMsvMessageEntry)
       
   849 		{
       
   850 		// Nothing to fetch at this entry - check child entries
       
   851 		CMsvEntrySelection *selection=new (ELeave) CMsvEntrySelection;
       
   852 		CleanupStack::PushL(selection);
       
   853 		User::LeaveIfError(iServerEntry.GetChildren(*selection));
       
   854 		
       
   855 		__LOG_FORMAT((iSession->LogId(), "...ID %x has %d children", iServerEntry.Entry().Id(),selection->Count()));
       
   856 		
       
   857 		TInt count = selection->Count();	
       
   858 		for(TInt a=0;a<count;++a)
       
   859 			{
       
   860 			// Process child
       
   861 			BuildFetchListL((*selection)[a]);
       
   862 			}
       
   863 		CleanupStack::PopAndDestroy(selection);
       
   864 		return;
       
   865 		}
       
   866 
       
   867 
       
   868 	// All other entry types may require fetching
       
   869 	// load the mime header for the entry.
       
   870 	// Information will be required for parts to be fetched.
       
   871 	CImMimeHeader* mimeHeader = CImMimeHeader::NewLC();
       
   872 	CMsvStore* store = iServerEntry.ReadStoreL();
       
   873 	CleanupStack::PushL(store);
       
   874 	mimeHeader->RestoreL(*store);
       
   875 	// finished with the read store
       
   876 	CleanupStack::PopAndDestroy(store);
       
   877 	
       
   878 	// get the entry for the part
       
   879 	TMsvEmailEntry entry = (TMsvEmailEntry)iServerEntry.Entry();
       
   880 	
       
   881 	// Apply filters and add to fetch list
       
   882 	if (iFetchPartialMail)
       
   883 		{
       
   884 		AddFilterFetchItemL(*mimeHeader, entry, iGetPartialMailInfo);
       
   885 		}
       
   886 	else
       
   887 		{
       
   888 		AddFilterFetchItemL(*mimeHeader, entry, iGetPartialMailInfo.iGetMailBodyParts);
       
   889 		}
       
   890 
       
   891 	// tidyup
       
   892 	CleanupStack::PopAndDestroy(mimeHeader);
       
   893 	}
       
   894 	
       
   895 
       
   896 	
       
   897 /** 
       
   898 Removes the html part from the download list if it exists in the list
       
   899 Used when partial fetching.
       
   900 
       
   901 @param aPart  ID of the HTML part to be removed from the fetch list
       
   902 */
       
   903 void CImapOpFetchBody::RemoveHtmlPart(TMsvId aPart)
       
   904 	{
       
   905 	if(aPart)
       
   906 		{
       
   907 		TInt count = iFetchList.Count();
       
   908 		for (TInt i=0 ; i<count ; ++i)
       
   909 			{
       
   910 			CFetchBodyInfo* info = iFetchList[i];
       
   911 			if (info->PartId()==aPart)
       
   912 				{
       
   913 				iFetchList.Remove(i);
       
   914 				delete info;
       
   915 				break;
       
   916 				}
       
   917 			}
       
   918 		}
       
   919 	}
       
   920 
       
   921 /**
       
   922 Checks for the minimum size limit between message type size limit 
       
   923 (attachment size limit/body text sizelimit)
       
   924 
       
   925 @param aThisPartTypeSizeLimit the size limit for the part-type in question
       
   926 @param aTotalMailSizeLimit    the total mail size limit
       
   927 */
       
   928 TInt32 CImapOpFetchBody::Minimum(TInt32 aThisPartTypeSizeLimit,TInt32 aTotalMailSizeLimit)
       
   929 	{
       
   930 	if(aTotalMailSizeLimit > 0)
       
   931 		{
       
   932 		if(aThisPartTypeSizeLimit > aTotalMailSizeLimit)
       
   933 			return aTotalMailSizeLimit;
       
   934 		else
       
   935 			return aThisPartTypeSizeLimit;
       
   936 		}
       
   937 	else
       
   938 		return aThisPartTypeSizeLimit;
       
   939 	}
       
   940 
       
   941 /**
       
   942 Entry point for Protocol Controller parsing of the received body structure
       
   943 and message header information.
       
   944 
       
   945 This method builds the a TMsvEntry tree representing the bodystructure.
       
   946 This method and the methods it calls use CreateEntryBulk() and ChangeEntryBulk() 
       
   947 in place of CreateEntry() and ChangeEntry().
       
   948 This means that the TMsvEntry tree is not left half-built should a leave occur.
       
   949 The tree is commited using CompleteBulk as the final step of the method.
       
   950 
       
   951 @return ETrue if complete body structure exists, EFalse if not
       
   952 */
       
   953 TBool CImapOpFetchBody::ProcessBodyStructureL()
       
   954 	{
       
   955 	// We expect to get the header fields and bodystructure in the response. If we
       
   956 	// don't get them, then that probably indicates that the message we are fetching
       
   957 	// is no longer on the server. If so, we should exit straight away.
       
   958 	if ((iFetchResponse->HeaderFields() == NULL) ||
       
   959 	    (iFetchResponse->BodyStructure() == NULL))
       
   960 		{
       
   961 		delete iFetchResponse;
       
   962 		iFetchResponse = NULL;
       
   963 		return EFalse;
       
   964 		}
       
   965 
       
   966 	SetEntryL(iRequestedPart);
       
   967 
       
   968 	// defensive - check that the structure has not already been constructed
       
   969 	if (!(iServerEntry.Entry().Owner()))
       
   970 		{
       
   971 		TMsvEmailEntry entry = iServerEntry.Entry();
       
   972 
       
   973 		// Build a CImHeader object from the returned header fields data.
       
   974 		CImHeader* imHeader = CImHeader::NewLC();
       
   975 		PopulateImHeaderL(*(iFetchResponse->HeaderFields()), entry, *imHeader);
       
   976 		User::LeaveIfError(iServerEntry.ChangeEntry(entry));   
       
   977 		StoreHeadersL(imHeader, NULL);
       
   978 		CleanupStack::PopAndDestroy(imHeader);
       
   979 	
       
   980 		TInt attachments=0;
       
   981 		TInt relatedAttachments=0;
       
   982 		TBool isMHTML=EFalse;
       
   983 		iDecodedSizeOfAllParts = 0;
       
   984 
       
   985 		// Create the message entry structure under the root message
       
   986 		CImapBodyStructure* bodystructure = iFetchResponse->BodyStructure();
       
   987 		BuildTreeL(entry.Id(),bodystructure,KNullDesC8,entry.Id(),attachments,isMHTML,relatedAttachments);
       
   988 
       
   989 		UpdateBodyTextRemainingSizeForHtml();
       
   990 
       
   991 		if(isMHTML==EFalse)
       
   992 			{
       
   993 			attachments+=relatedAttachments;
       
   994 			}
       
   995 			
       
   996 		// recover server entry context
       
   997 		SetEntryL(iRequestedPart);
       
   998 		entry = iServerEntry.Entry();
       
   999 
       
  1000 		// Now that the structure has been created we can set the real message attributes.
       
  1001 		// The MHTML, attachment flags and size were estimated (hopefully correctly) when the envelope was downloaded.
       
  1002 		entry.iSize = iDecodedSizeOfAllParts;
       
  1003 		entry.SetMHTMLEmail(isMHTML);
       
  1004 		entry.SetAttachment(attachments);
       
  1005 		entry.SetICalendar(iIsICalendar);
       
  1006 		entry.SetVCalendar(iIsVCalendar);
       
  1007 		//Resetting the values
       
  1008 		iIsICalendar=EFalse;
       
  1009 		iIsVCalendar=EFalse;
       
  1010 		
       
  1011 		if( !iHasTextParts && entry.iType == KUidMsvMessageEntry )
       
  1012 			{
       
  1013 			// There are no text parts to this message - need to set body text 
       
  1014 			// complete flag to true otherwise UI may allow such a message to 
       
  1015 			// repeatedly be 'fetched' even though there is no text to fetch!
       
  1016 			//
       
  1017 			// So, set body text message complete flags on the entry
       
  1018 			__LOG_FORMAT((iSession->LogId(), "Message %d has no text parts - setting body text complete flag to ETrue", iRequestedPart));
       
  1019 			entry.SetBodyTextComplete(ETrue);
       
  1020 			}
       
  1021 		
       
  1022 		SetEntryL(entry.Id());
       
  1023 		User::LeaveIfError(iServerEntry.ChangeEntryBulk(entry));
       
  1024 		}
       
  1025 
       
  1026 	// Commit the tree created during this method();
       
  1027 	iServerEntry.CompleteBulk();
       
  1028 
       
  1029 	// No longer need the fetch response
       
  1030 	delete iFetchResponse;
       
  1031 	iFetchResponse = NULL;
       
  1032 	
       
  1033 	return ETrue;
       
  1034 	}
       
  1035 
       
  1036 
       
  1037 /**
       
  1038 Populates a CImHeader object with data returned in the header and bodystructure 
       
  1039 fetch.
       
  1040 
       
  1041 @param aHeaderFields Header field data structure returned by the header fetch operation
       
  1042 @param aEntry        Message server entry for the message being fetched
       
  1043 @param aImHeader     Target CImHeader object to be populated
       
  1044 */
       
  1045 void CImapOpFetchBody::PopulateImHeaderL(CImapRfc822HeaderFields& aHeaderFields, TMsvEmailEntry& aEntry, CImHeader& aImHeader)
       
  1046 	{
       
  1047 	// Subject, From 
       
  1048 	// Don't use the fields in aEntry as they may be in Unicode, which causes problems for
       
  1049 	// the call to iHeaderConverter->DecodeAllHeaderFieldsL() below - which expects 8 bit data
       
  1050 	aImHeader.SetSubjectL(aHeaderFields.FieldValue(CImapRfc822HeaderFields::EImapSubject));
       
  1051 	aImHeader.SetFromL(aHeaderFields.FieldValue(CImapRfc822HeaderFields::EImapFrom));
       
  1052 
       
  1053 	// Return-Receipt-To, X-Return-Receipt-To	
       
  1054 	const TDesC8& returnReceipt = aHeaderFields.ReturnReceiptField();
       
  1055 	if (returnReceipt != KNullDesC8())
       
  1056 		{
       
  1057 		aImHeader.SetReceiptAddressL(returnReceipt);
       
  1058 		if (!aEntry.SeenIMAP4Flag())
       
  1059 			{
       
  1060 			aEntry.SetReceipt(ETrue);
       
  1061 			}
       
  1062 		}
       
  1063 
       
  1064 	// Reply-to
       
  1065 	if (aHeaderFields.FieldExists(CImapRfc822HeaderFields::EImapReplyTo))
       
  1066 		{
       
  1067 		aImHeader.SetReplyToL(aHeaderFields.FieldValue(CImapRfc822HeaderFields::EImapReplyTo));
       
  1068 		}
       
  1069 	else
       
  1070 		{
       
  1071 		aImHeader.SetReplyToL(aHeaderFields.FieldValue(CImapRfc822HeaderFields::EImapFrom));
       
  1072 		}
       
  1073 
       
  1074 	// Message-ID
       
  1075 	if(aHeaderFields.FieldExists(CImapRfc822HeaderFields::EImapMessageId))
       
  1076 		{
       
  1077 		aImHeader.SetImMsgIdL(aHeaderFields.FieldValue(CImapRfc822HeaderFields::EImapMessageId));
       
  1078 		}
       
  1079 	
       
  1080 	// To
       
  1081 	if(aHeaderFields.FieldExists(CImapRfc822HeaderFields::EImapTo))
       
  1082 		{
       
  1083 		ProcessAddressListL(aImHeader.ToRecipients(), aHeaderFields.FieldValue(CImapRfc822HeaderFields::EImapTo));
       
  1084 		}
       
  1085 	
       
  1086 	// Cc
       
  1087 	if(aHeaderFields.FieldExists(CImapRfc822HeaderFields::EImapCc))
       
  1088 		{
       
  1089 		ProcessAddressListL(aImHeader.CcRecipients(), aHeaderFields.FieldValue(CImapRfc822HeaderFields::EImapCc));
       
  1090 		}
       
  1091 	
       
  1092 	// Bcc
       
  1093 	if(aHeaderFields.FieldExists(CImapRfc822HeaderFields::EImapBcc))
       
  1094 		{
       
  1095 		ProcessAddressListL(aImHeader.BccRecipients(), aHeaderFields.FieldValue(CImapRfc822HeaderFields::EImapBcc));
       
  1096 		}
       
  1097 	
       
  1098 	// Decode any QP encoding in header fields
       
  1099 	iHeaderConverter->DecodeAllHeaderFieldsL(aImHeader);
       
  1100 	}
       
  1101 
       
  1102 
       
  1103 /**
       
  1104 Builds attachment tree for an email message.
       
  1105 
       
  1106 Messaging stores email messages in the message store as a tree structure of
       
  1107 TMsvEntrys representing the mime structure of the message as described by the
       
  1108 BODYSTRUCTURE returned by the imap server. Multipart bodystructure parts are 
       
  1109 represented by an entry of type KUidMsvFolderEntry and TImEmailFolderType as
       
  1110 appropriate to the multipart type string. Content parts (text, attachments etc)
       
  1111 are built by method BuildContentEntryL() below. For each mime part an associated
       
  1112 CImMimeHeader object is created and streamed to the messaging store.
       
  1113 
       
  1114 An array of parts to fetch according to message get-options is built up as the
       
  1115 bodystructure is parsed. The information gathered is that which the IMAP Session
       
  1116 requires to successfully fetch a specific message part.
       
  1117 
       
  1118 @param aParent         The parent-part to be processed
       
  1119 @param aBodyStructure  Parsed bodystructure data for the current part
       
  1120 @param aPath           The IMAP relative path for message parts.
       
  1121 @param aThisMessage    TMsvId of the root entry for the message.
       
  1122 @param aAttachments    Counter for attachments.
       
  1123 @param aIsMHTML        Flag indicating that the message has been identified as MHTML
       
  1124 @param aRelatedAttachments Counter for related attachments. 
       
  1125 */
       
  1126 void CImapOpFetchBody::BuildTreeL(TMsvId aParent, 
       
  1127 								  CImapBodyStructure* aBodyStructure, 
       
  1128 								  const TDesC8& aPath,
       
  1129 								  const TMsvId aThisMessage, 
       
  1130 								  TInt& aAttachments, 
       
  1131 								  TBool& aIsMHTML, 
       
  1132 								  TInt& aRelatedAttachments, TBool aIsEmbed)
       
  1133 	{
       
  1134 	__LOG_FORMAT((iSession->LogId(), "CImapOpFetchBody::BuildTreeL(message=%x, parent=%x", aThisMessage, aParent));
       
  1135 
       
  1136 	// Is this a content entry? (ie, not a multipart entry)
       
  1137 	if (aBodyStructure->BodyStructureType()!=CImapBodyStructure::ETypeMultipart)
       
  1138 		{
       
  1139 		// Build the content entry for the message part
       
  1140 		HBufC8* newpath=HBufC8::NewLC(aPath.Length()+4);
       
  1141 		if(aIsEmbed && aBodyStructure->BodyStructureType() == CImapBodyStructure::ETypeText)
       
  1142             {
       
  1143             *newpath=aPath;
       
  1144 		    iEmbed = ETrue;
       
  1145             }
       
  1146 		else
       
  1147             {
       
  1148 		    *newpath=aPath;
       
  1149 		    if (aPath.Length())
       
  1150 		    newpath->Des().Append(KIMAP_REL_PATH_SEPARATOR);
       
  1151 		    newpath->Des().AppendNum(1);		                
       
  1152             }
       
  1153 		
       
  1154 		
       
  1155 		BuildContentEntryL(aParent, aBodyStructure, newpath->Des(), aThisMessage, aAttachments, aIsMHTML, aRelatedAttachments);
       
  1156 		CleanupStack::PopAndDestroy(newpath);
       
  1157 		}
       
  1158 	// otherwise is a multipart entry
       
  1159 	else
       
  1160 		{
       
  1161 		// Nest down a level: create a folder entry
       
  1162 		SetEntryL(aParent);
       
  1163 		TMsvEmailEntry message;
       
  1164 		message.iMtm=KUidMsgTypeIMAP4;
       
  1165 		message.iServiceId=iImapSettings.ServiceId();
       
  1166 		message.iType=KUidMsvFolderEntry;
       
  1167 		message.iSize=0;
       
  1168 		message.SetComplete(EFalse);
       
  1169 		User::LeaveIfError(iServerEntry.CreateEntryBulk(message)); // bulk op completed at the end of ProcessBodyStructureL()
       
  1170 
       
  1171 		__LOG_FORMAT((iSession->LogId(), "   Created attachment folder id %x as child of %x", message.Id(), aParent));
       
  1172 
       
  1173 		aParent=message.Id();
       
  1174 
       
  1175 		TPtrC8 multipart = aBodyStructure->SubType();
       
  1176 		// Got anything?
       
  1177 		if (multipart.Size()>0)
       
  1178 			{
       
  1179 			// Parse multipart type string, do this first so
       
  1180 			// information is available when parsing children
       
  1181 			TImEmailFolderType ft=EFolderTypeUnknown;
       
  1182 			if (multipart.CompareF(KImcvRelated)==0)
       
  1183 				{
       
  1184 				__LOG_TEXT(iSession->LogId(), "   Folder type MULTIPART/RELATED");
       
  1185 				ft=EFolderTypeRelated;
       
  1186 				}
       
  1187 			else if (multipart.CompareF(KImcvMixed)==0)
       
  1188 				{
       
  1189 				__LOG_TEXT(iSession->LogId(), "   Folder type MULTIPART/MIXED");
       
  1190 				ft=EFolderTypeMixed;
       
  1191 				}
       
  1192 			else if (multipart.CompareF(KImcvParallel)==0)
       
  1193 				{
       
  1194 				__LOG_TEXT(iSession->LogId(), "   Folder type MULTIPART/PARALLEL");
       
  1195 				ft=EFolderTypeParallel;
       
  1196 				}
       
  1197 			else if (multipart.CompareF(KImcvAlternative)==0)
       
  1198 				{
       
  1199 				__LOG_TEXT(iSession->LogId(), "   Folder type MULTIPART/ALTERNATIVE");
       
  1200 				ft=EFolderTypeAlternative;
       
  1201 				}
       
  1202 			else if (multipart.CompareF(KImcvDigest)==0)
       
  1203 				{
       
  1204 				__LOG_TEXT(iSession->LogId(), "   Folder type MULTIPART/DIGEST");
       
  1205 				ft=EFolderTypeDigest;
       
  1206 				}
       
  1207 
       
  1208 			SetEntryL(aParent);
       
  1209 			
       
  1210 			// ...and save it
       
  1211 			TMsvEmailEntry folder=iServerEntry.Entry();
       
  1212 			folder.SetMessageFolderType(ft);
       
  1213 #if SET_RELATED_ID
       
  1214 			// Set message's iRelatedId to messageId
       
  1215 			folder.iRelatedId=folder.Id();
       
  1216 #endif
       
  1217 			User::LeaveIfError(iServerEntry.ChangeEntryBulk(folder));
       
  1218 
       
  1219 			// Process each of the multi-parts...
       
  1220 			TInt subnr=1;
       
  1221 			TInt numParts=aBodyStructure->EmbeddedBodyStructureList().Count();
       
  1222 			for (TInt i=0;i<numParts;++i)
       
  1223 				{
       
  1224 				// Process item (doesn't use AllocL)
       
  1225 				HBufC8* newpath=HBufC8::NewLC(aPath.Length()+4);
       
  1226 				*newpath=aPath;
       
  1227 				if (aPath.Length())
       
  1228 					newpath->Des().Append(KIMAP_REL_PATH_SEPARATOR);
       
  1229 				newpath->Des().AppendNum(subnr++);
       
  1230 				BuildContentEntryL(aParent, 
       
  1231 								   aBodyStructure->EmbeddedBodyStructureList()[i],
       
  1232 								   newpath->Des(),
       
  1233 								   aThisMessage, 
       
  1234 								   aAttachments, 
       
  1235 								   aIsMHTML, 
       
  1236 								   aRelatedAttachments);
       
  1237 				CleanupStack::PopAndDestroy(newpath);
       
  1238 				}
       
  1239 			}
       
  1240 		}
       
  1241 	}
       
  1242 
       
  1243 
       
  1244 
       
  1245 /**
       
  1246 Builds the TMsvEmailEntry and associated CImMimeHeader for an individual message content part. 
       
  1247 
       
  1248 @param aParent         TMsvId of the parent of the current part. When called
       
  1249                          initially, this is the root entry representing the message
       
  1250 @param aBodyStructure  Parsed bodystructure data for the current part
       
  1251 @param aPath           The IMAP relative path for message parts.
       
  1252 @param aThisMessage    TMsvId of the root entry for the message.
       
  1253 @param aAttachments    Counter for attachments.
       
  1254 @param aIsMHTML        Flag indicating that the message has been identified as MHTML
       
  1255 @param aRelatedAttachments Counter for related attachments. 
       
  1256 */
       
  1257 void CImapOpFetchBody::BuildContentEntryL(const TMsvId aParent, CImapBodyStructure* aBodyStructure, const TDesC8& aPath, const TMsvId aThisMessage, TInt& aAttachments, TBool& aIsMHTML, TInt& aRelatedAttachments)
       
  1258 	{
       
  1259 
       
  1260 	// First, is this actually an entry, or another level of nesting?
       
  1261 	if (aBodyStructure->BodyStructureType()==CImapBodyStructure::ETypeMultipart)
       
  1262 		{
       
  1263 		// Another level of nesting? Call BuildTreeL()
       
  1264 		BuildTreeL(aParent, aBodyStructure, aPath, aThisMessage, aAttachments, aIsMHTML, aRelatedAttachments);
       
  1265 		return;
       
  1266 		}
       
  1267 
       
  1268 	// Skeleton for new entry
       
  1269 	SetEntryL(aParent);
       
  1270 
       
  1271 	TFileName attachmentFilename;	// somewhere to store an attachment filename
       
  1272 	TMsvEmailEntry message;
       
  1273 	message.iSize=0;
       
  1274 	message.iMtm=KUidMsgTypeIMAP4;
       
  1275 	message.iServiceId=iImapSettings.ServiceId();
       
  1276 	message.SetUID(iMessageUid);
       
  1277 	message.SetValidUID(EFalse);  // ValidUID Flag used to indicate if the message has ever been fetched
       
  1278 	message.SetComplete(EFalse);
       
  1279 
       
  1280 	// Save mime TYPE/SUBTYPE
       
  1281 	CImMimeHeader* mime=CImMimeHeader::NewLC();
       
  1282 	TPtrC8 type    = aBodyStructure->Type();
       
  1283 	TPtrC8 subtype = aBodyStructure->SubType();
       
  1284 	mime->SetContentTypeL(type);
       
  1285 	mime->SetContentSubTypeL(subtype);
       
  1286 
       
  1287 	__LOG_FORMAT((iSession->LogId(), "   MIME: %S/%S", &type, &subtype));
       
  1288 
       
  1289 	// initialise the data type to be an attachment
       
  1290 	message.iType=KUidMsvAttachmentEntry;
       
  1291 	
       
  1292 	// Store the relative path for the content part
       
  1293 	mime->SetRelativePathL(aPath);
       
  1294 
       
  1295 	if (aBodyStructure->BodyStructureType()==CImapBodyStructure::ETypeText)
       
  1296 		{
       
  1297 		ProcessTextSubtypeL(aBodyStructure, *mime, message, aIsMHTML);
       
  1298 		}
       
  1299 
       
  1300 	// Process the Parameter list
       
  1301 	ProcessParameterListL(aBodyStructure->ParameterList(), *mime, message, attachmentFilename);
       
  1302 
       
  1303 	// ID: save it
       
  1304 	TPtrC8 id = aBodyStructure->BodyId();
       
  1305 	if (id.Length() > 0)
       
  1306 		{
       
  1307 		__LOG_FORMAT((iSession->LogId(), "   Content ID: %S", &id));
       
  1308 		mime->SetContentIDL(StripAngledBrackets(id));
       
  1309 		}
       
  1310 
       
  1311 	// Description: save it
       
  1312 	TPtrC8 description = aBodyStructure->BodyDescription();
       
  1313 	if (description.Length() > 0)
       
  1314 		{
       
  1315 		__LOG_FORMAT((iSession->LogId(), "   Content Description: %S", &description));
       
  1316 		mime->SetContentDescriptionL(description);
       
  1317 		}
       
  1318 
       
  1319 	// Encoding
       
  1320 	TPtrC8 encoding = aBodyStructure->BodyEncoding();
       
  1321 	mime->SetContentTransferEncodingL(encoding);
       
  1322 	__LOG_FORMAT((iSession->LogId(), "   Content Transfer Encoding: %S", &encoding));
       
  1323 
       
  1324 	// Octets (encoded form)
       
  1325 	TInt actualsize;
       
  1326 	TLex8 lex(aBodyStructure->BodySizeOctets());
       
  1327 	lex.Val(actualsize);
       
  1328 
       
  1329     // Some servers gives a negative size value when the body text is empty. Need to reset this to
       
  1330  	// zero to prevent corruption error later on.
       
  1331  	if(actualsize < 0)
       
  1332  		{
       
  1333  		actualsize = 0;
       
  1334  		}
       
  1335 
       
  1336 	// Modify actualsize to show *decoded* size: this is basically the size of
       
  1337 	// this part, multiplied by 6/8 if it's BASE64 encoded. For all other
       
  1338 	// encodings, we leave the size as-is as there's no hard & fast rule
       
  1339 	// which can be applied.
       
  1340 	if (encoding.CompareF(KMIME_BASE64)==0)
       
  1341 		{
       
  1342 		message.iSize=(actualsize*6)/8;
       
  1343 		}
       
  1344 	else
       
  1345 		{
       
  1346 		message.iSize=actualsize;
       
  1347 		}
       
  1348 
       
  1349 	// Add into total message size
       
  1350 	iDecodedSizeOfAllParts+=message.iSize;
       
  1351 
       
  1352 	// Use iBioType message entry data member to store size on remote server
       
  1353 	message.iBioType=actualsize;
       
  1354 
       
  1355 	//If any part of email (text/plain mime, text/html mime, attachment....) 
       
  1356 	// is empty then should not fetch it.
       
  1357 	if(actualsize == 0)
       
  1358 		{
       
  1359 		message.SetComplete(ETrue);
       
  1360 		}
       
  1361 
       
  1362 	// mark attached emails as such
       
  1363 	if (aBodyStructure->BodyStructureType()==CImapBodyStructure::ETypeMessageRfc822)
       
  1364 		{
       
  1365 		// embedded message - marked as a message
       
  1366 		// NB needs to be set before ProcessExtendedFieldsL() can be called
       
  1367 		message.iType=KUidMsvMessageEntry;
       
  1368 		iDecodedSizeOfAllParts-=message.iSize;
       
  1369 		}
       
  1370 
       
  1371 	// Process any extended fields
       
  1372 	ProcessExtendedFieldsL(aBodyStructure, *mime, message, attachmentFilename);
       
  1373 
       
  1374 	// Now we're working on the type
       
  1375 	if (message.iType==KUidMsvMessageEntry)
       
  1376 		{
       
  1377 		BuildEmbeddedMessageL(aBodyStructure, *mime, message, aPath, aAttachments);
       
  1378 		}
       
  1379 	else
       
  1380 		{
       
  1381 		BuildNonMessageEntryL(aParent, *mime, message, aAttachments, aRelatedAttachments);
       
  1382 		}
       
  1383 	CleanupStack::PopAndDestroy(mime);
       
  1384 	__LOG_FORMAT((iSession->LogId(), "  BuildContentEntryL done: created id %x, attachments so far %d", message.Id(), aAttachments));
       
  1385 	}
       
  1386 
       
  1387 /**
       
  1388 Sets flags in the TMsvEmailEntry according to the subtype for TEXT/xxx mime parts.
       
  1389 
       
  1390 @param aBodyStructure bodystructure component being processed
       
  1391 @param aMime	(partially) parsed mime information
       
  1392 @param aMessage the email entry under construction
       
  1393 @param aIsMHTML Flag indicating that the message has been identified as MHTML
       
  1394 */
       
  1395 void CImapOpFetchBody::ProcessTextSubtypeL(CImapBodyStructure* aBodyStructure, CImMimeHeader& aMime, TMsvEmailEntry& aMessage, TBool& aIsMHTML)
       
  1396 	{
       
  1397 	// text/rtf?
       
  1398 	if (aMime.ContentSubType().CompareF(KMIME_RTF)==0)
       
  1399 		{
       
  1400 		if (!(aBodyStructure->ExtDispositionName().CompareF(KMIME_ATTACHMENT)==0))
       
  1401 			{
       
  1402 			aMessage.iType=KUidMsvEmailRtfEntry;
       
  1403 			}	
       
  1404 		}
       
  1405 	// text/html?
       
  1406 	else if (aMime.ContentSubType().CompareF(KMIME_HTML)==0)
       
  1407 		{
       
  1408 		// If this is not an attachment (ie disposition field 
       
  1409 		// is not "attachment") then this is a MHTML Message.
       
  1410 		if (!(aBodyStructure->ExtDispositionName().CompareF(KMIME_ATTACHMENT)==0))
       
  1411 			{
       
  1412 			aMessage.iType=KUidMsvEmailHtmlEntry;
       
  1413 			aIsMHTML=ETrue;	
       
  1414 			}	
       
  1415 		}
       
  1416 	// text/x-vcard?
       
  1417 	else if (aMime.ContentSubType().CompareF(KMIME_XVCARD)==0)
       
  1418 		{
       
  1419 		// Set vCard flag in message
       
  1420 		aMessage.SetVCard(ETrue);
       
  1421 		}
       
  1422 	// text/x-vcalendar
       
  1423 	else if (aMime.ContentSubType().CompareF(KMIME_VCALENDAR)==0)
       
  1424 		{
       
  1425 		// Set vCalendar flag in message
       
  1426 		aMessage.SetVCalendar(ETrue);
       
  1427 		iIsVCalendar = ETrue;
       
  1428 		}
       
  1429 	// text/calendar
       
  1430 	else if (aMime.ContentSubType().CompareF(KMIME_ICALENDAR)==0)
       
  1431 		{
       
  1432 		// Set iCalendar flag in message
       
  1433 		aMessage.SetICalendar(ETrue);
       
  1434 		iIsICalendar = ETrue;
       
  1435 		}
       
  1436 	else
       
  1437 		aMessage.iType=KUidMsvEmailTextEntry;
       
  1438 	}
       
  1439 
       
  1440 
       
  1441 
       
  1442 /**
       
  1443 Populates the parameter lists of the CImMimeHeader object 
       
  1444 
       
  1445 @param aParamList parameter list, retrieved from the bodystructure
       
  1446 @param aMime	  mime header object to be populated
       
  1447 @param aMessage   the email entry under construction
       
  1448 @param aAttachmentFilename updated with a file name, if one is found
       
  1449 */
       
  1450 void CImapOpFetchBody::ProcessParameterListL(const CImapBodyStructure::RAttributeValuePairList& aParamList, CImMimeHeader& aMime, TMsvEmailEntry& aMessage, TFileName& aAttachmentFilename)
       
  1451 	{
       
  1452 	TUint charsetId = KUidMsvCharsetNone;
       
  1453 
       
  1454 	TInt paramCount = aParamList.Count();
       
  1455 	if (paramCount > 0)
       
  1456 		{
       
  1457 		__LOG_TEXT(iSession->LogId(), "   Parameter list:");
       
  1458 
       
  1459 		for(TInt i=0;i<paramCount;++i)
       
  1460 			{
       
  1461 			TPtrC8 param=aParamList[i].iAttribute;
       
  1462 			TPtrC8 value=aParamList[i].iValue;
       
  1463 			
       
  1464 			__LOG_FORMAT((iSession->LogId(), "    %S %S", &param, &value));
       
  1465 
       
  1466 			aMime.ContentTypeParams().AppendL(param);
       
  1467 			aMime.ContentTypeParams().AppendL(value);
       
  1468 
       
  1469 			// Have we come across a 'NAME' tuple? If so, force the MIME type of this
       
  1470 			// entry to be an attachment.
       
  1471 			if ((param.CompareF(KMIME_NAME)==0)
       
  1472 				|| (param.CompareF(KMIME_NAME_RFC2231)==0))
       
  1473 				{
       
  1474 				__LOG_TEXT(iSession->LogId(), "   Attachment filename found, therefore this is an attachment");
       
  1475 
       
  1476 				FindFilenameDecodeL(aMime,aAttachmentFilename);
       
  1477 				StripIllegalCharactersFromFileName(aAttachmentFilename);
       
  1478 				aMessage.iDetails.Set(aAttachmentFilename);
       
  1479 
       
  1480 				// If embedded message do not save as an attachment
       
  1481 				if (aMessage.iType!=KUidMsvMessageEntry)
       
  1482 					{
       
  1483 					aMessage.iType=KUidMsvAttachmentEntry;
       
  1484 					}
       
  1485 				}
       
  1486 			else if (param.CompareF(KImcvCharset) == 0)
       
  1487 				{
       
  1488 				// We have found a charset parameter tuple. Convert the value to a
       
  1489 				// charset ID for storage in the MIME headers.
       
  1490 				if (value.Length() > 0)
       
  1491 					{
       
  1492 					charsetId = iCharConv->GetMimeCharsetUidL(value);
       
  1493 					}
       
  1494 				}
       
  1495 			}
       
  1496 		}
       
  1497 
       
  1498 	aMime.SetMimeCharset(charsetId);
       
  1499 	}
       
  1500 
       
  1501 /**
       
  1502 Populates extended header fields in the CImMimeHeader object 
       
  1503 
       
  1504 @param aBodyStructure bodystructure object returned by the imap session
       
  1505 @param aMime	 mime header object to be populated
       
  1506 @param aMessage  the email entry under construction
       
  1507 @param aAttachmentFilename updated with a file name, if one is found
       
  1508 */
       
  1509 void CImapOpFetchBody::ProcessExtendedFieldsL(CImapBodyStructure* aBodyStructure, CImMimeHeader& aMime, TMsvEmailEntry& aMessage, TFileName& aAttachmentFilename)
       
  1510 	{
       
  1511 	// Add the diposition name as the first pair of parameters.
       
  1512 	TPtrC8 dispositionName = aBodyStructure->ExtDispositionName();
       
  1513 	if (dispositionName.Size() != 0)
       
  1514 		{
       
  1515 		__LOG_FORMAT((iSession->LogId(), "    adding disp name: %S", &dispositionName));
       
  1516 		aMime.ContentDispositionParams().AppendL(dispositionName);
       
  1517 		aMime.ContentDispositionParams().AppendL(KNullDesC8);
       
  1518 		}
       
  1519 	
       
  1520 	// Now add the actual diposition parameters as name-value pairs
       
  1521 	const CImapBodyStructure::RAttributeValuePairList& dispParams = aBodyStructure->ExtDispositionParameterList();
       
  1522 	TInt dispParamsCount = dispParams.Count(); // Note that this will be 0 if there is no disposition available
       
  1523 	
       
  1524 	for (TInt i=0; i < dispParamsCount; ++i)
       
  1525 		{
       
  1526 		__LOG_FORMAT((iSession->LogId(), "    disp: %S %S", &dispParams[i].iAttribute, &dispParams[i].iValue));
       
  1527 		
       
  1528 		aMime.ContentDispositionParams().AppendL(dispParams[i].iAttribute);
       
  1529 		aMime.ContentDispositionParams().AppendL(dispParams[i].iValue);
       
  1530 
       
  1531 		// Filename? If so, force this as an attachment
       
  1532 		if ((dispParams[i].iAttribute.CompareF(KMIME_FILENAME)==0)
       
  1533 		 || (dispParams[i].iAttribute.CompareF(KMIME_FILENAME_RFC2231)==0))
       
  1534 			{
       
  1535 			__LOG_TEXT(iSession->LogId(), "   Attachment filename found in extended fields.");
       
  1536 			FindFilenameDecodeL(aMime,aAttachmentFilename);
       
  1537 			StripIllegalCharactersFromFileName(aAttachmentFilename);
       
  1538 			aMessage.iDetails.Set(aAttachmentFilename);
       
  1539 
       
  1540 			// If embedded message do not save as an attachment
       
  1541 			if (aMessage.iType!=KUidMsvMessageEntry)
       
  1542 				{
       
  1543 				aMessage.iType=KUidMsvAttachmentEntry;
       
  1544 				}
       
  1545 			}
       
  1546 		
       
  1547 		} // end for
       
  1548 	}
       
  1549 
       
  1550 
       
  1551 
       
  1552 
       
  1553 /**
       
  1554 Performs final parsing and construction of an embedded MESSAGE/RFC822 message
       
  1555 
       
  1556 As an MESSAGE/RFC822 part, the structure will contain envelope info. This is
       
  1557 parsed via ProcessEnvelopeL to construct a CImHeader object for the embedded
       
  1558 message. This is streamed to file along with the mime header information for 
       
  1559 the message part.
       
  1560 
       
  1561 The CImapBodyStructure referred to via aBodyStructure->iMultiParts is the body 
       
  1562 structure of the embedded message. This is an entire message-within-a-message 
       
  1563 and so gets treated as a whole new mail.
       
  1564 
       
  1565 @param aBodyStructure  Parsed bodystructure data for the current MESSAGE/RFC822 part
       
  1566 @param aMime	 	   mime header object to be populated
       
  1567 @param aMessage  	   the email entry under construction
       
  1568 @param aPath           The IMAP relative path for message parts.
       
  1569 @param aAttachments    Counter for attachments.
       
  1570 */
       
  1571 void CImapOpFetchBody::BuildEmbeddedMessageL(CImapBodyStructure* aBodyStructure, CImMimeHeader& aMime, TMsvEmailEntry& aMessage, const TDesC8& aPath, TInt& aAttachments)
       
  1572 	{
       
  1573 	// Prepare a CImHeader object for the message,
       
  1574 	// update flags as appropriate on the message entry.
       
  1575 	CImHeader *messageheader=CImHeader::NewLC();
       
  1576 	ProcessEnvelopeL(messageheader, aMessage, aBodyStructure->GetRfc822EnvelopeStructureL());
       
  1577 
       
  1578 	if(aBodyStructure->EmbeddedBodyStructureList().Count() == 0)
       
  1579 	    {
       
  1580         aMessage.iType=KUidMsvAttachmentEntry;
       
  1581 	    }
       
  1582 	
       
  1583 	// Create message
       
  1584 	User::LeaveIfError(iServerEntry.CreateEntryBulk(aMessage)); // bulk op completed at the end of ProcessBodyStructureL()
       
  1585 	SetEntryL(aMessage.Id());
       
  1586 
       
  1587 	// Store CImHeader and CImMimeHeader for the MESSAGE/RFC822
       
  1588 	StoreHeadersL(messageheader, &aMime);
       
  1589 	CleanupStack::PopAndDestroy(messageheader);
       
  1590 
       
  1591 #if SET_RELATED_ID
       
  1592 	// Set message's iRelatedId to messageId
       
  1593 	TMsvEntry entryToChange(iServerEntry.Entry());
       
  1594 	entryToChange.iRelatedId=entryToChange.Id();
       
  1595 	ChangeEntryBulkL(entryToChange); // bulk op completed at the end of ProcessBodyStructureL()
       
  1596 #endif
       
  1597 	// Process the bodystructure for this embedded message...
       
  1598 	TInt attachments=0;
       
  1599 	TInt relatedAttachments;
       
  1600 	TBool isMHTML=EFalse;
       
  1601 	CImapBodyStructure* bodystructure = NULL;
       
  1602 	
       
  1603 	if(aBodyStructure->EmbeddedBodyStructureList().Count() > 0)
       
  1604 	    {
       
  1605 	    bodystructure = aBodyStructure->EmbeddedBodyStructureList()[0];
       
  1606         }	
       
  1607 	if (bodystructure)
       
  1608 	    {
       
  1609         BuildTreeL(aMessage.Id(), bodystructure, aPath, aMessage.Id(), attachments, isMHTML, relatedAttachments, ETrue);
       
  1610 	    }
       
  1611 	
       
  1612 	__LOG_FORMAT((iSession->LogId(), "  Built embedded message id %x attachments %d MHTML %d", aMessage.Id(), attachments, isMHTML));
       
  1613 
       
  1614 	// Save attachment and MHTML flags
       
  1615 	if (attachments>0 || isMHTML)
       
  1616 		{
       
  1617 		SetEntryL(aMessage.Id());
       
  1618 		TMsvEmailEntry thisMessage = iServerEntry.Entry();
       
  1619 
       
  1620 		if (attachments>0)
       
  1621 			{
       
  1622 			thisMessage.SetAttachment(ETrue);
       
  1623 			}
       
  1624 
       
  1625 		if (isMHTML)
       
  1626 			{
       
  1627 			thisMessage.SetMHTMLEmail(ETrue);
       
  1628 			}
       
  1629 
       
  1630 		User::LeaveIfError(iServerEntry.ChangeEntryBulk(thisMessage));
       
  1631 		}
       
  1632 
       
  1633 	// embedded messages are counted as attachments
       
  1634 	++aAttachments;
       
  1635 	}
       
  1636 
       
  1637 
       
  1638 
       
  1639 
       
  1640 /**
       
  1641 Performs final processing and construction of non-MESSAGE/RFC822 parts.
       
  1642 Stores the mime header information for the message part.
       
  1643 
       
  1644 This method is called for all message parts that are not multi-part and
       
  1645 not message/rfc822. Thus, each part for which this method is called is
       
  1646 eligible for fetch. This method calls AddFilterFetchItem to build up
       
  1647 the array of parts to fetch.
       
  1648 
       
  1649 @param aParent         TMsvId of the parent of the current part.
       
  1650 @param aMime	 	   mime header object to be populated
       
  1651 @param aMessage  	   the email entry under construction
       
  1652 @param aAttachments    Counter for attachments.
       
  1653 @param aRelatedAttachments Counter for related attachments. 
       
  1654 */
       
  1655 void CImapOpFetchBody::BuildNonMessageEntryL(const TMsvId& aParent, CImMimeHeader& aMime, TMsvEmailEntry& aMessage, TInt& aAttachments, TInt& aRelatedAttachments)
       
  1656 	{
       
  1657 	// Some other type of entry... 
       
  1658 	SetEntryL(aParent);
       
  1659 		
       
  1660 	// save parent folder type
       
  1661 	TImEmailFolderType parentFolderType = ((TMsvEmailEntry)iServerEntry.Entry()).MessageFolderType();
       
  1662 
       
  1663 	// set attachment and HTML flags on item
       
  1664 	if (aMessage.iType==KUidMsvAttachmentEntry)
       
  1665 		{
       
  1666 		aMessage.SetAttachment(ETrue);
       
  1667 		}
       
  1668 
       
  1669 	if (aMessage.iType==KUidMsvEmailHtmlEntry)
       
  1670 		{
       
  1671 		aMessage.SetMHTMLEmail(ETrue);
       
  1672 		}
       
  1673 
       
  1674 	// ensure there is a filename if it is a non-text item 
       
  1675 	if (aMessage.iType!=KUidMsvEmailTextEntry && aMessage.iDetails.Length() == 0)
       
  1676 		{
       
  1677 		// use iAttachmentName for temporary buffer
       
  1678 		GetDefaultFilenameL(iAttachmentName, aMessage, &aMime);
       
  1679 		aMessage.iDetails.Set(iAttachmentName);
       
  1680 		}
       
  1681 	
       
  1682 	// Create the Entry
       
  1683 	User::LeaveIfError(iServerEntry.CreateEntryBulk(aMessage)); // bulk op completed at the end of ProcessBodyStructureL()
       
  1684 	SetEntryL(aMessage.Id());
       
  1685 	
       
  1686 	__LOG_FORMAT((iSession->LogId(), "   Created attachment id %x as child of %x - type %d", aMessage.Id(), aParent, parentFolderType));
       
  1687 
       
  1688 #if SET_RELATED_ID
       
  1689 	// Set message's iRelatedId to messageId
       
  1690 	TMsvEntry entryToChange(iServerEntry.Entry());
       
  1691 	entryToChange.iRelatedId=entryToChange.Id();
       
  1692 	ChangeEntryBulkL(entryToChange); // bulk op completed at the end of ProcessBodyStructureL()
       
  1693 #endif
       
  1694 
       
  1695 	// Stream the MIME info out into the newly created attachment entry.
       
  1696 	StoreHeadersL(NULL, &aMime);
       
  1697 
       
  1698 	// This entry is NOT an attachment in the following cases - 
       
  1699 	// 1)	This is an attachment whose parent is a MULTIPART/RELATED folder.
       
  1700 	//		In this case, this entry could be a image entity for an MHTML
       
  1701 	//		entry with the same parent.
       
  1702 	// 2)	This is an MHTML entry whose parent is a MULTIPART/ALTERNATIVE 
       
  1703 	//		folder. In this case, this entry is the MHTML alternative to a
       
  1704 	//		text entry with the same parent.
       
  1705 	// 3)	This is an MHTML entry whose parent is MESSAGE folder. In this
       
  1706 	//		case, the message is a simple MHTML message with no text 
       
  1707 	//		alternative or embedded image.
       
  1708 	// 4)	This is an MHTML entry whose parent is a MULTIPART/RELATED folder.
       
  1709 	//		In this case, this entry is the MHTML for the message.
       
  1710 	// 5)	This is an MHTML entry whose parent is a MULTIPART/MIXED folder.
       
  1711 	//		In this case, this entry is the MHTML for the message. It cannot
       
  1712 	//		be the attachment it self as then it would be of type attachment.
       
  1713 	// Therefore, an entry is only an attachment if is of type attachment and
       
  1714 	// its parent is not a MULTIPART/RELATED folder.
       
  1715 	if(aMessage.iType==KUidMsvAttachmentEntry && parentFolderType!=EFolderTypeRelated)
       
  1716 		{
       
  1717 		++aAttachments;
       
  1718 		}
       
  1719 	// if it is related we might want to include it if the message
       
  1720 	// turns out not to be MHTML
       
  1721 	else if (aMessage.iType==KUidMsvAttachmentEntry &&	parentFolderType==EFolderTypeRelated)
       
  1722 		{
       
  1723 		++aRelatedAttachments;
       
  1724 		}
       
  1725 	
       
  1726 	// Apply filters and add to fetch list
       
  1727 	if (iFetchPartialMail)
       
  1728 		{
       
  1729 		AddFilterFetchItemL(aMime, aMessage, iGetPartialMailInfo);
       
  1730 		}
       
  1731 	else
       
  1732 		{
       
  1733 		AddFilterFetchItemL(aMime, aMessage, iGetPartialMailInfo.iGetMailBodyParts);
       
  1734 		}
       
  1735 	}
       
  1736 
       
  1737 
       
  1738 
       
  1739 /**
       
  1740 Issues a request to fetch the next item in the array of parts to fetch.
       
  1741 @return EFalse if no parts remaining in the array.
       
  1742 */
       
  1743 TBool CImapOpFetchBody::FetchPartL()
       
  1744 	{
       
  1745  	// Anything to do?
       
  1746  	if (iFetchList.Count() <= 0)
       
  1747  		{
       
  1748  		return EFalse;
       
  1749  		}
       
  1750 	else
       
  1751 		{
       
  1752 		// Create a new body response store to hold any response information
       
  1753 		// from the fetch command.
       
  1754 		delete iFetchBodyResponse;
       
  1755 		iFetchBodyResponse = CImapFetchBodyResponse::NewL();
       
  1756 		
       
  1757 		// If part to fetch was of type attachment, this must be registered
       
  1758 		// with the CAF framework. This is indicated in the FetchBodyInfo by
       
  1759 		// a non-NULL CAF pointer.
       
  1760 		if ((iFetchList[0])->Caf()!=NULL)
       
  1761 			{
       
  1762 			RegisterWithCafL(*(iFetchList[0]));
       
  1763 			}
       
  1764 
       
  1765 		// UpdatingSeenFlags == ETrue means that we want to set the seen flag explicitly, 
       
  1766 		//						using CImapSession::StoreL(), so peek should be ETrue too.
       
  1767 		// UpdatingSeenFlags == EFalse meanse that we want the server to set the seen flag
       
  1768 		//						implicitly when the message is downloaded,
       
  1769 		//						- i.e. peek should be EFalse too.			
       
  1770 		TBool peek = iImapSettings.UpdatingSeenFlags();
       
  1771 		iFetchList[0]->SetPartialDownload(iFetchPartialMail);		
       
  1772 		iSession->FetchBodyL(iStatus, iMessageUid, peek, *(iFetchList[0]), *iFetchBodyResponse);
       
  1773  		}
       
  1774  	return ETrue;
       
  1775 	}
       
  1776 	
       
  1777 	
       
  1778 /**
       
  1779 Registers the part with the CAF framework.
       
  1780 
       
  1781 This method is called for all parts of type Application that are
       
  1782 to be fetched. CAF registration requires concatenated content-type 
       
  1783 and subtype. The type and subtype have previously been received 
       
  1784 and stored in the CImMimeHeader for the part.
       
  1785 
       
  1786 If the part is not registered as a result of the call to 
       
  1787 CImCaf::RegisterL(), the FetchBodyInfo object is updated to show
       
  1788 this.
       
  1789 
       
  1790 @param 	fetchInfo information about the part to be fetched.
       
  1791 */
       
  1792 void CImapOpFetchBody::RegisterWithCafL(CFetchBodyInfo& fetchInfo)
       
  1793 	{		
       
  1794 	// load the mime header for the entry.
       
  1795 	// Information will be required for parts to be fetched.
       
  1796 	SetEntryL(fetchInfo.PartId());
       
  1797 	CImMimeHeader* mimeHeader = CImMimeHeader::NewLC();
       
  1798 	CMsvStore* store = iServerEntry.ReadStoreL();
       
  1799 	CleanupStack::PushL(store);
       
  1800 	mimeHeader->RestoreL(*store);
       
  1801 	// finished with the read store
       
  1802 	CleanupStack::PopAndDestroy(store);
       
  1803 	
       
  1804 	// Create buffer for concatenating. + 1 creates space for '/' 
       
  1805 	HBufC8* buf = HBufC8::NewLC(mimeHeader->ContentSubType().Length() + mimeHeader->ContentType().Length() + 1);
       
  1806 	TPtr8 ptr(buf->Des());
       
  1807 	ptr.Copy(mimeHeader->ContentType());
       
  1808 	ptr.Append(KImcvForwardSlash);
       
  1809 	ptr.Append(mimeHeader->ContentSubType());
       
  1810 
       
  1811 	// Attempt to register with CAF - this may not succeed.
       
  1812 	iCaf->RegisterL(ptr);
       
  1813 	CleanupStack::PopAndDestroy(buf);
       
  1814 	CleanupStack::PopAndDestroy(mimeHeader);
       
  1815 	
       
  1816 	// Clear the iCaf pointer in fetchInfo if CAF is not interested.
       
  1817 	if(!iCaf->Registered())
       
  1818 		{
       
  1819 		fetchInfo.ResetCaf();
       
  1820 		}
       
  1821 	}
       
  1822 	
       
  1823 	
       
  1824 /**
       
  1825 Populates a CImHeader object given a CImapEnvelope abject.
       
  1826 Updates the passed TMsvEntry with appropriate information (date, from and subject)
       
  1827 
       
  1828 @param aHeader       Header information object to populate
       
  1829 @param aServerEntry        Server entry id associated with the envelope object
       
  1830 @param aImapEnvelope Parsed IMAP Envelope object.
       
  1831 */
       
  1832 void CImapOpFetchBody::ProcessEnvelopeL(CImHeader* aHeader, TMsvEntry& aEntry, CImapEnvelope& aImapEnvelope)
       
  1833 	{
       
  1834 	__LOG_FORMAT((iSession->LogId(), "   Processing envelope data, id=%x", aEntry.Id()));
       
  1835 
       
  1836 	TPtrC8  tptr;
       
  1837 	// Parse date information
       
  1838 	tptr.Set(aImapEnvelope.EnvDate());
       
  1839 	TImRfc822DateField date;
       
  1840 	date.ParseDateField(tptr, aEntry.iDate);
       
  1841 
       
  1842 	// Subject in CImHeader (TMsvEntry is later on after post-processing)
       
  1843 	if (aImapEnvelope.EnvSubject().CompareF(KIMAP_NIL)!=0)
       
  1844 		{
       
  1845 		aHeader->SetSubjectL(aImapEnvelope.EnvSubject());
       
  1846 		}
       
  1847 	
       
  1848 	// From information: both in CImHeader and TMsvEntry
       
  1849 	if (aImapEnvelope.EnvFrom().Count() != 0)
       
  1850 		{
       
  1851 		__LOG_TEXT(iSession->LogId(), "   Processing 'From' information");
       
  1852 		HBufC16* fromAddr = aImapEnvelope.EnvFrom()[0].CreateAddressStringL();
       
  1853 		CleanupStack::PushL(fromAddr);
       
  1854 		aHeader->SetFromL(*fromAddr);
       
  1855 		CleanupStack::PopAndDestroy(fromAddr);
       
  1856 		}
       
  1857 	else
       
  1858 		{
       
  1859 		// No From information. Set blank
       
  1860 		aHeader->SetFromL(KNullDesC);
       
  1861 		}
       
  1862 
       
  1863 	// Sender information is ignored.
       
  1864 
       
  1865 	// ReplyTo information
       
  1866 	if (aImapEnvelope.EnvReplyTo().Count()!=0)
       
  1867 		{
       
  1868 		__LOG_TEXT(iSession->LogId(), "   Processing 'ReplyTo' information");
       
  1869 		HBufC16* replyToAddr = aImapEnvelope.EnvFrom()[0].CreateAddressStringL();
       
  1870 		CleanupStack::PushL(replyToAddr);
       
  1871 		aHeader->SetReplyToL(*replyToAddr);
       
  1872 		CleanupStack::PopAndDestroy(replyToAddr);
       
  1873 		}
       
  1874 	else
       
  1875 		{
       
  1876 		// No replyto. Use From info
       
  1877 		aHeader->SetReplyToL(aHeader->From());
       
  1878 		}
       
  1879 
       
  1880 	// To information
       
  1881 	ProcessAddressListL(aHeader->ToRecipients(), aImapEnvelope.EnvTo());
       
  1882 	
       
  1883 	// CC list
       
  1884 	ProcessAddressListL(aHeader->CcRecipients(),aImapEnvelope.EnvCc());
       
  1885 
       
  1886 	// BCC list
       
  1887 	ProcessAddressListL(aHeader->BccRecipients(),aImapEnvelope.EnvBcc());
       
  1888 
       
  1889 	// Message-Id
       
  1890 	aHeader->SetImMsgIdL(StripAngledBrackets(aImapEnvelope.EnvMessageId()));
       
  1891 
       
  1892 	// Decode any QP encoding in header fields
       
  1893 	iHeaderConverter->DecodeAllHeaderFieldsL(*aHeader);
       
  1894 
       
  1895 	// Set from line in TMsvEntry
       
  1896 	if(aHeader->From().Length() > 0)
       
  1897 	    {
       
  1898         aEntry.iDetails.Set(aHeader->From());
       
  1899 	    }
       
  1900 	if(aHeader->Subject().Length() > 0)
       
  1901 	    {
       
  1902 	    // Set subject in TMsvEntry
       
  1903 	    aEntry.iDescription.Set(aHeader->Subject());
       
  1904 	    }
       
  1905 
       
  1906 	__LOG_TEXT(iSession->LogId(), "   Finished processing envelope information");
       
  1907 	}
       
  1908 
       
  1909 
       
  1910 /**
       
  1911 Retrieves the default filename and appends the appropriate extension
       
  1912 
       
  1913 @param  aWhere         Destination descriptor array
       
  1914 @param  aAddressArray  Source address array
       
  1915 */
       
  1916 void CImapOpFetchBody::GetDefaultFilenameL(TDes& aName, const TMsvEmailEntry& aMessage, const CImMimeHeader* mime)
       
  1917 	{
       
  1918 	aName = iImapSettings.DefaultAttachmentName();
       
  1919 
       
  1920 	// Add on appropriate extension
       
  1921 	if (aMessage.iType == KUidMsvEmailTextEntry)
       
  1922 		{
       
  1923 		aName.Append(KTextExtension);
       
  1924 		}
       
  1925 	else if(aMessage.iType == KUidMsvEmailRtfEntry)
       
  1926 		{
       
  1927 		aName.Append(KRtfExtension);	
       
  1928 		}
       
  1929 	else if (aMessage.MHTMLEmail())
       
  1930 		{
       
  1931 		aName.Append(KHtmlExtension);
       
  1932 		}
       
  1933 	else if (aMessage.VCard() || aMessage.VCalendar())
       
  1934 		{
       
  1935 		aName.Append(KVCardExtension);
       
  1936 		}
       
  1937 	else if (aMessage.ICalendar())
       
  1938 		{
       
  1939 		aName.Append(KICalExtension);
       
  1940 		}
       
  1941 	else if ( aMessage.iType == KUidMsvAttachmentEntry )
       
  1942 		{
       
  1943  		if ( (mime->ContentSubType()==KImcvBmp) ||
       
  1944 			 (mime->ContentSubType()==KImcvGif) ||
       
  1945 			 (mime->ContentSubType()==KImcvJpeg) ||
       
  1946 			 (mime->ContentSubType()==KImcvTiff) ||
       
  1947 			 (mime->ContentSubType()==KImcvWav) )
       
  1948 			{
       
  1949 			// Copy the 8-bit ContentSubType into the 16-bit buf
       
  1950 			RBuf buf;
       
  1951 			buf.CleanupClosePushL();
       
  1952 			buf.CreateL(mime->ContentSubType().Length());
       
  1953 			
       
  1954 			buf.Copy(mime->ContentSubType()); // 16-bit <== 8-bit
       
  1955 			
       
  1956 			aName.Append(KImcvFullStop);
       
  1957 			aName.Append(buf);
       
  1958 			
       
  1959 			CleanupStack::PopAndDestroy(&buf);
       
  1960 			}
       
  1961 		}
       
  1962 	}
       
  1963 
       
  1964 /**
       
  1965 Copies addresses from an array of addresses into a target descriptor array.
       
  1966 The source array is of the form returned by the CImapEnvelope class
       
  1967 
       
  1968 @param  aWhere         Destination descriptor array
       
  1969 @param  aAddressArray  Source address array.
       
  1970 */
       
  1971 void CImapOpFetchBody::ProcessAddressListL(CDesCArray& aWhere, const CImapEnvelope::RArrayTAddress& aAddressArray)
       
  1972 	{
       
  1973 	for(TInt i=0;i < aAddressArray.Count();++i)
       
  1974 		{
       
  1975 		HBufC16* address = aAddressArray[i].CreateAddressStringL();
       
  1976 		CleanupStack::PushL(address);
       
  1977 		aWhere.AppendL(address->Des());
       
  1978 		CleanupStack::PopAndDestroy(address);
       
  1979 		}
       
  1980 	}
       
  1981 
       
  1982 /**
       
  1983 Copies addresses from a Source descriptor containing one or more addresses 
       
  1984 into a target descriptor array of individual addresses
       
  1985 
       
  1986 @param  aWhere      Destination descriptor array
       
  1987 @param  aAddresses	Source descriptor containing one or more addresses.
       
  1988 */
       
  1989 void CImapOpFetchBody::ProcessAddressListL(CDesCArray& aWhere, const TDesC8& aAddresses)
       
  1990  	{
       
  1991 	TInt length(aAddresses.Length());
       
  1992  	HBufC8* pBuf=HBufC8::NewLC(length);
       
  1993  	TPtrC8 source(aAddresses.Ptr(), length);
       
  1994  	const TUint8* ptr(source.Ptr());
       
  1995  	const TUint8* lastCharPtr(ptr + source.Length() - 1);
       
  1996  	TUint8 lookFor(0);
       
  1997  	TInt count(0);
       
  1998  	TBool finishedEntry(EFalse);
       
  1999  
       
  2000  	// get past white space
       
  2001  	while(*ptr&&((*ptr==KImcvSP)||(*ptr==KImcvSemiColon))) ptr++;
       
  2002  
       
  2003  	// Entries are separated by commas or semicolons.
       
  2004  	// Separators do not count if they appear within
       
  2005  	// "", <>, () or embedded series of these, eg "(one, two)"
       
  2006  	// so we need to keep track of these, including nesting.
       
  2007  	while(*ptr && ptr <= lastCharPtr)
       
  2008  		{
       
  2009  		if(pBuf->Length()==0)
       
  2010  			{
       
  2011  			finishedEntry = EFalse;
       
  2012  			}
       
  2013  
       
  2014  		switch(*ptr)
       
  2015  			{
       
  2016  			case KImcvLeftBracket:
       
  2017  				if(lookFor==KImcvRightBracket)
       
  2018  					{ // We've already had a "(", so now we need another one
       
  2019  					count++;
       
  2020  					}
       
  2021  				else if(lookFor==0)
       
  2022  					{ //We weren't looking for anything else, now we need to
       
  2023  					lookFor = KImcvRightBracket;
       
  2024  					count = 1;
       
  2025  					}
       
  2026  				// else we were already looking for something else, ignore this
       
  2027  				break;
       
  2028  			case KImcvLeftChevron:
       
  2029  				if(lookFor==KImcvRightChevron)
       
  2030  					{ //We've already had a "<", so now we need another one
       
  2031  					count++;
       
  2032  					}
       
  2033  				else if(lookFor==0)
       
  2034  					{ //We weren't looking for anything else
       
  2035  					lookFor = KImcvRightChevron;
       
  2036  					count = 1;
       
  2037  					}
       
  2038  				// else we were already looking for something else, ignore this
       
  2039  				break;
       
  2040  			case KImcvDoubleQuote:
       
  2041  				if(lookFor==KImcvDoubleQuote)
       
  2042  					{ // We already had a quote, so this matches it
       
  2043  					lookFor = 0;
       
  2044  					}
       
  2045  				else if(lookFor==0)
       
  2046  					{ //We weren't looking for anything else
       
  2047  					lookFor = KImcvDoubleQuote;
       
  2048  					}
       
  2049  				// else we were already looking for something else, ignore this
       
  2050  				break;
       
  2051  			case KImcvRightBracket:
       
  2052  			case KImcvRightChevron:
       
  2053  				if(*ptr == lookFor)
       
  2054  					{ //If we have found what we were looking for, decrease the count
       
  2055  					count--;
       
  2056  					if(count==0)
       
  2057  						{ // Got everything, now we're not looking for anything
       
  2058  						lookFor = 0;
       
  2059  						}
       
  2060  					// else keep looking for the same thing	again
       
  2061  					}
       
  2062  				// else we're looking for something else, ignore it
       
  2063  				break;
       
  2064  			case KImcvComma:
       
  2065  			case KImcvSemiColon:
       
  2066  				// If we're not looking for anything, we're finished
       
  2067  				if (lookFor == 0)
       
  2068  					finishedEntry = ETrue;
       
  2069  				// else this comma or semicolon is part of a different token, ignore it
       
  2070  				break;
       
  2071  			}
       
  2072  
       
  2073  		if(!finishedEntry)
       
  2074  			{
       
  2075  			pBuf->Des().Append((TChar)*ptr);
       
  2076  			// move to the next character
       
  2077  			ptr++;
       
  2078  			}
       
  2079  		else
       
  2080  			{
       
  2081  			// that's it! store the address away
       
  2082  			HBufC16* pBuf16 = HBufC16::NewLC(pBuf->Des().Length());
       
  2083  			pBuf16->Des().Copy(pBuf->Des());
       
  2084  			aWhere.AppendL( (HBufC16&) *pBuf16 );
       
  2085  			CleanupStack::PopAndDestroy(pBuf16); // pBuf16
       
  2086  			pBuf->Des().SetLength(0);
       
  2087  			finishedEntry = EFalse; //Ready for next entry
       
  2088  
       
  2089  			// get past the separator
       
  2090  			ptr++;
       
  2091  
       
  2092  			// get past white space (& any other separators)
       
  2093  			while(*ptr && (*ptr==KImcvSP || *ptr==KImcvTab || *ptr==KImcvComma || *ptr==KImcvSemiColon)) ptr++;
       
  2094  			}
       
  2095  		}
       
  2096  		// catch the last name in the list
       
  2097  		if (pBuf)
       
  2098  			{
       
  2099  			TInt recipientLength(pBuf->Length());
       
  2100  			if (recipientLength > 0)
       
  2101  				{
       
  2102  				HBufC16* pBuf16 = HBufC16::NewLC(recipientLength);
       
  2103  				pBuf16->Des().Copy(*pBuf);
       
  2104  				aWhere.AppendL(*pBuf16);
       
  2105  				CleanupStack::PopAndDestroy(pBuf16); // pBuf16
       
  2106  				}
       
  2107  			}
       
  2108  		CleanupStack::PopAndDestroy(pBuf); // pBuf
       
  2109  	}
       
  2110  	
       
  2111 	
       
  2112 /**
       
  2113 Searches mime information for a filename for an attachment. 
       
  2114 If not found, the default attachment name is used.
       
  2115 If found but encoded according to RFC2231, the default attachment name is used 
       
  2116   with the appropriate file extension appended.
       
  2117 Otherwise the found filename is QP decoded and cropped to be within max filename size.
       
  2118 
       
  2119 @param  aMimeInfo  The mime header associated with the attachment
       
  2120 @param  aFileName  Returns the filename, if found.
       
  2121 @return KErrNotFound       if no filename found
       
  2122         KErrRFC2231Encoded if RFC2231 encoded filename found
       
  2123         KErrNone           otherwise
       
  2124 */
       
  2125 TInt CImapOpFetchBody::FindFilenameL(const CImMimeHeader& aMimeInfo, TPtrC8& aFilename)
       
  2126 	{
       
  2127 	// Look in content-type list
       
  2128 	const CDesC8Array& ctype=aMimeInfo.ContentTypeParams();
       
  2129 
       
  2130 	__LOG_FORMAT((iSession->LogId(), "CImapOpFetchBody::FindFilename: Checking %d entries in content-type list", ctype.Count()));
       
  2131 
       
  2132 	TInt tuple=0;
       
  2133 	TInt count = ctype.Count(); 
       
  2134 	while (tuple+1 < count) // we step in pairs, and use tuple and tuple+1, so tuple+1 needs to be less than count in order
       
  2135 		{
       
  2136 
       
  2137 #ifdef PRINTING
       
  2138 		TPtrC8 t1 = ctype[tuple];
       
  2139 		TPtrC8 t2 = (tuple+1 < count) ? ctype[tuple+1] : KNullDesC8();
       
  2140 		__LOG_FORMAT((iSession->LogId(),"    [%S] [%S]", &t1, &t2));
       
  2141 #endif
       
  2142 
       
  2143 		// Look for "name xxx"
       
  2144 		if (ctype[tuple].CompareF(KMIME_NAME)==0)
       
  2145 			{
       
  2146 			// Got it: report that we found it
       
  2147 			aFilename.Set(ctype[tuple+1]);
       
  2148 
       
  2149 			// Check whether aFilename contains a meaningful file name
       
  2150 			RBuf8 buf;
       
  2151 			buf.CleanupClosePushL();
       
  2152 			buf.CreateL(aFilename);
       
  2153 			buf.Trim();
       
  2154 			if(buf.Length()==0)
       
  2155 				{
       
  2156 				CleanupStack::PopAndDestroy(&buf);
       
  2157 				return KErrNotFound;
       
  2158 				}
       
  2159 
       
  2160 			CleanupStack::PopAndDestroy(&buf);
       
  2161 			return KErrNone;
       
  2162 			}
       
  2163 		else if (ctype[tuple].CompareF(KMIME_NAME_RFC2231)==0)
       
  2164 			{
       
  2165 			// Got it: report that we found it
       
  2166 			aFilename.Set(ctype[tuple+1]);
       
  2167 			return KErrRFC2231Encoded;
       
  2168 			}
       
  2169 		tuple+=2;
       
  2170 		}
       
  2171 
       
  2172 	// Not found in the content type, try content disposition
       
  2173 	const CDesC8Array& cdisp=aMimeInfo.ContentDispositionParams();
       
  2174 	__LOG_FORMAT((iSession->LogId(), "CImapOpFetchBody::FindFilename: Checking %d entries in disposition list", cdisp.Count()));
       
  2175 	
       
  2176 	tuple=0;
       
  2177 	count = cdisp.Count();
       
  2178 	while(tuple+1 < count) // we step in pairs, and use tuple and tuple+1, so tuple+1 needs to be less than count in order
       
  2179 		{
       
  2180 
       
  2181 #ifdef PRINTING
       
  2182 		TPtrC8 t1 = cdisp[tuple];
       
  2183 		TPtrC8 t2 = (tuple+1 < count) ? cdisp[tuple+1] : KNullDesC8();
       
  2184 		__LOG_FORMAT((iSession->LogId(),"disp    [%S] [%S]", &t1, &t2));
       
  2185 #endif
       
  2186 
       
  2187 		// Look for "filename xxx"
       
  2188 		if (cdisp[tuple].CompareF(KMIME_FILENAME)==0)
       
  2189 			{
       
  2190 			// Got it: report that we found it
       
  2191 			aFilename.Set(cdisp[tuple+1]);
       
  2192 			return KErrNone;
       
  2193 			}
       
  2194 		else if (cdisp[tuple].CompareF(KMIME_FILENAME_RFC2231)==0)
       
  2195 			{
       
  2196 			// Got it: report that we found it
       
  2197 			aFilename.Set(cdisp[tuple+1]);
       
  2198 			return KErrRFC2231Encoded;
       
  2199 			}
       
  2200 
       
  2201 		tuple+=2;
       
  2202 		}
       
  2203 
       
  2204 	// Didn't find it
       
  2205 	return KErrNotFound;
       
  2206 	}
       
  2207 
       
  2208 
       
  2209 /**
       
  2210 Builds attachment filename, using data from the mime header information, if available.
       
  2211 If not found, the default attachment name is used.
       
  2212 If found but encoded according to RFC2231, the default attachment name is used 
       
  2213   with the appropriate file extension appended.
       
  2214 Otherwise the found filename is QP decoded and cropped to be within max filename size.
       
  2215 
       
  2216 @param  aMimeInfo  The mime header associated with the attachment
       
  2217 @return aFileName  The decoded filename for the attachment.
       
  2218 */
       
  2219 void CImapOpFetchBody::FindFilenameDecodeL(const CImMimeHeader& aMimeInfo, TFileName& aFileName)
       
  2220 	{
       
  2221 	// Make an attachment name
       
  2222 	aFileName.Zero();
       
  2223 
       
  2224 	TPtrC8 origFileName;
       
  2225 
       
  2226 	// Look for filename in Content-Type list
       
  2227 	TInt err = FindFilenameL(aMimeInfo, origFileName);
       
  2228 	if (KErrNotFound == err)
       
  2229 		{
       
  2230 		// Fall back to simple "attachment" (language specific)
       
  2231 		aFileName=iImapSettings.DefaultAttachmentName();
       
  2232 		}
       
  2233 	else if (KErrRFC2231Encoded == err)
       
  2234 		{
       
  2235 		// A file name has been found but it is encoded (RFC2231)
       
  2236 		// Use the default file name but append the file extension so that its type can be recognised
       
  2237 		aFileName=iImapSettings.DefaultAttachmentName();
       
  2238 		TInt dotPos = origFileName.Length() - 1;
       
  2239 		TBool dotFound = EFalse;
       
  2240 		
       
  2241 		// Find the extension
       
  2242 		while ((dotPos != 0) && (!dotFound))
       
  2243 			{
       
  2244 			if (origFileName[dotPos] == '.')
       
  2245 				{
       
  2246 				dotFound = ETrue;
       
  2247 				// Extension found: append it to the filename
       
  2248 				TInt extensionLength = origFileName.Length() - dotPos;
       
  2249 				if ((aFileName.Length() + extensionLength) <= aFileName.MaxLength())
       
  2250 					{
       
  2251 					HBufC* extension = HBufC::NewLC(extensionLength);
       
  2252 					extension->Des().Copy(origFileName.Right(extensionLength));
       
  2253 					aFileName.Append(*extension);
       
  2254 					CleanupStack::PopAndDestroy(extension);
       
  2255 					}
       
  2256 				}
       
  2257 			--dotPos;
       
  2258 			} // end while ((dotPos != 0) && (!dotFound))
       
  2259 		}
       
  2260 	else
       
  2261 		{
       
  2262 		// Run it through the QP decoder
       
  2263 		HBufC *decoded=HBufC::NewLC(origFileName.Length());
       
  2264 		TPtr decoded_ptr(decoded->Des());
       
  2265 
       
  2266 		// Decode filename from the header
       
  2267 		iHeaderConverter->DecodeHeaderFieldL(origFileName, decoded_ptr);
       
  2268 		
       
  2269 		__LOG_FORMAT((iSession->LogId(), "FindFilenameDecode: '%S' to '%S' ", &origFileName, &decoded_ptr));
       
  2270 
       
  2271 		// Need to do a check on the filename length here.
       
  2272 		// If it is too long, set to the max possible, keeping extension.
       
  2273 		TFileName path;
       
  2274 		TInt fileNameLength = path.Length() + decoded_ptr.Length();
       
  2275 		if( fileNameLength > KMaxFileName)
       
  2276 			{
       
  2277 			TInt prefixLen = 0;
       
  2278 			// Crop the Old File Name
       
  2279 			TInt lengthToCrop = (fileNameLength - KMaxFileName) + prefixLen;
       
  2280 			// Use LocateReverse rather than TParsePtr as decoded_ptr may be > 256 chars
       
  2281 			TInt dot = decoded_ptr.LocateReverse( '.' );
       
  2282 			TPtrC extension = decoded_ptr.Mid(dot != KErrNotFound ? dot : decoded_ptr.Length());
       
  2283 			TInt newFileNameLength = decoded_ptr.Length() - extension.Length() - lengthToCrop;
       
  2284 			TPtrC newFileName=decoded_ptr.Left(newFileNameLength);
       
  2285 
       
  2286 			// Create the New File Name (ie File Name & Extension)
       
  2287 			aFileName.Zero();
       
  2288 			aFileName.Append(newFileName);
       
  2289 			aFileName.Append(extension);
       
  2290 			}
       
  2291 		else
       
  2292 			{
       
  2293 			aFileName.Copy(decoded_ptr);
       
  2294 			}
       
  2295 		CleanupStack::PopAndDestroy(decoded);
       
  2296 		}
       
  2297 	}
       
  2298 
       
  2299 
       
  2300 /**
       
  2301 Replaces illegal characters in a filename with a default
       
  2302 @param aName  The filename to check.
       
  2303 */
       
  2304 void CImapOpFetchBody::StripIllegalCharactersFromFileName(TDes16& aName)
       
  2305     {
       
  2306     TInt length=aName.Length();
       
  2307     for(TInt index=0; index < length; ++index)
       
  2308         {
       
  2309         TUint charr=(TUint)aName[index];
       
  2310         if(	charr == '*' || charr == '\\' || charr == '<' || charr == '>' ||
       
  2311             charr == ':'  || charr == '"' || charr == '/' || charr == '|' ||
       
  2312 			charr == '?' || charr < ' ')
       
  2313             {
       
  2314             aName[index] = KImcvDefaultChar;
       
  2315             }
       
  2316         }
       
  2317    }
       
  2318 
       
  2319 
       
  2320 /**
       
  2321 Synchronously stores IM Headers and Mime Headers
       
  2322 @param aImHeader   an IM header to store. May be NULL.
       
  2323 @param aMimeHeader a Mime part header to store. May be NULL.
       
  2324 */
       
  2325 void CImapOpFetchBody::StoreHeadersL(CImHeader* aImHeader, CImMimeHeader* aMimeHeader)
       
  2326 	{
       
  2327 	CMsvStore* entryStore=iServerEntry.EditStoreL();
       
  2328 	CleanupStack::PushL(entryStore);
       
  2329 	if (aImHeader != NULL)
       
  2330 		{
       
  2331 		__LOG_FORMAT((iSession->LogId(), "   Streaming ImHeader info into id %x", iServerEntry.Entry().Id()));
       
  2332 		aImHeader->StoreL(*entryStore);
       
  2333 		}
       
  2334 	if (aMimeHeader != NULL)
       
  2335 		{
       
  2336 		__LOG_FORMAT((iSession->LogId(), "   Streaming MIME info into id %x", iServerEntry.Entry().Id()));
       
  2337 		aMimeHeader->StoreL(*entryStore);
       
  2338 		}
       
  2339 	entryStore->CommitL();
       
  2340 	CleanupStack::PopAndDestroy(entryStore);
       
  2341 	}
       
  2342 
       
  2343 /**
       
  2344 Check if the partial download options mean that we will download a text plain
       
  2345 part, but the text HTML alternative will not be downloaded. In this case, we
       
  2346 need to make sure the footer message is displayed on the text plain part to
       
  2347 show that the text HTML part is not being downloaded.
       
  2348 */
       
  2349 void CImapOpFetchBody::UpdateBodyTextRemainingSizeForHtml()
       
  2350 	{
       
  2351 	if ((iHtmlEntryPart == 0) && (iHtmlEntrySize > 0) && (iFetchList.Count() > 0))
       
  2352 		{
       
  2353 		for (TInt part(0); part < iFetchList.Count(); ++part)
       
  2354 			{
       
  2355 			if (iFetchList[part]->IsText())
       
  2356 				{
       
  2357 				iFetchList[part]->SetBodyPartRemainingSize(iFetchList[part]->BodyPartRemainingSize() + iHtmlEntrySize);
       
  2358 				return;
       
  2359 				}
       
  2360 			}
       
  2361 		}
       
  2362 	}
       
  2363 
       
  2364 /** 
       
  2365 Static method that strips the given string of any enclosing angled brackets < >
       
  2366 @param aString the descriptor that will have its brackets removed
       
  2367 @return a descriptor that points into aString, excluding any enclosing angled brackets
       
  2368 */
       
  2369 TPtrC8 CImapOpFetchBody::StripAngledBrackets(const TDesC8& aString)
       
  2370 // static method
       
  2371 	{
       
  2372 	TInt strLen = aString.Length();
       
  2373 
       
  2374 	if (strLen>2 && aString[0]==KImcvLeftChevron && aString[strLen-1]==KImcvRightChevron)
       
  2375 		{
       
  2376 		return aString.Mid(1, strLen-2);
       
  2377 		}
       
  2378 	
       
  2379 	// If the string was not enclosed with angled brackets then just return the original string.
       
  2380 	return aString;
       
  2381 	}
       
  2382 
       
  2383 /**
       
  2384 Creates an empty attachment to store the attachment infomation, for the case
       
  2385 where the attachment is not downloaded due to download limits.
       
  2386 @param aMsvEmailEntry The email entry the attachment is associate with.
       
  2387 */
       
  2388 void CImapOpFetchBody::CreateAttachmentInfoL(const TMsvEntry& aMsvEmailEntry)
       
  2389 	{
       
  2390 	__LOG_TEXT(iSession->LogId(), "Creating zero length attachment");
       
  2391 	iMailStore.CreateAttachmentInfoL(aMsvEmailEntry.Id());
       
  2392 	}
       
  2393 
       
  2394 
       
  2395 /**
       
  2396 Called when a requsted part has been sucessfully fetched.
       
  2397 Updates flags on the TMsvEntry indicating that it is complete or
       
  2398 partially fetched as appropriate.
       
  2399 
       
  2400 Note - deletes the 0th entry from the array of parts to fetch.
       
  2401 */
       
  2402 void CImapOpFetchBody::FetchPartCompleteL()
       
  2403 	{
       
  2404 	CFetchBodyInfo* fetchInfo = iFetchList[0];
       
  2405 	CleanupStack::PushL(fetchInfo);
       
  2406 	iFetchList.Remove(0);
       
  2407 	SetEntryL(fetchInfo->PartId());
       
  2408 	TMsvEmailEntry message = iServerEntry.Entry();
       
  2409 	message.SetComplete(ETrue);
       
  2410 
       
  2411 	// Store mime information returned from N.MIME
       
  2412 
       
  2413 	CImapMimeHeaderFields* headerFields = iFetchBodyResponse->HeaderFields();
       
  2414 
       
  2415 	if (headerFields != NULL)
       
  2416 		{
       
  2417 		CImMimeHeader* mimeHeader = CImMimeHeader::NewLC();
       
  2418 		CMsvStore* store = iServerEntry.ReadStoreL();
       
  2419 		CleanupStack::PushL(store);
       
  2420 		mimeHeader->RestoreL(*store);
       
  2421 		CleanupStack::PopAndDestroy(store);
       
  2422 
       
  2423 		if (headerFields->FieldExists(CImapMimeHeaderFields::EImapContentBase))
       
  2424 			{
       
  2425 			mimeHeader->SetContentBaseL(headerFields->FieldValue(CImapMimeHeaderFields::EImapContentBase));
       
  2426 			}
       
  2427 
       
  2428 		if (headerFields->FieldExists(CImapMimeHeaderFields::EImapContentLocation))
       
  2429 			{
       
  2430 			const TDesC8& fieldValue = headerFields->FieldValue(CImapMimeHeaderFields::EImapContentLocation);
       
  2431 
       
  2432 			HBufC* decodedBuffer = HBufC::NewLC(fieldValue.Length());
       
  2433 			TPtr decodedPtr(decodedBuffer->Des());
       
  2434 
       
  2435 			iHeaderConverter->DecodeHeaderFieldL(fieldValue, decodedPtr);
       
  2436 			mimeHeader->SetContentLocationL(decodedPtr);
       
  2437 			CleanupStack::PopAndDestroy(decodedBuffer);
       
  2438 			}
       
  2439 
       
  2440 		StoreHeadersL(NULL, mimeHeader);
       
  2441 		CleanupStack::PopAndDestroy(mimeHeader);
       
  2442 		}
       
  2443 
       
  2444 	TBool hasBodyText = message.iType == KUidMsvEmailTextEntry || message.iType == KUidMsvEmailHtmlEntry;
       
  2445 	TBool partiallyDownloaded = EFalse;
       
  2446 
       
  2447 	if(iFetchPartialMail && 
       
  2448 	   fetchInfo->BodyPartRemainingSize() != 0 && 
       
  2449 	   message.iType == KUidMsvEmailTextEntry) 
       
  2450 		{
       
  2451 		message.SetPartialDownloaded(ETrue);
       
  2452 		partiallyDownloaded = ETrue;
       
  2453 		message.SetComplete(EFalse);
       
  2454 		}
       
  2455 	else
       
  2456 		{
       
  2457 		message.SetPartialDownloaded(EFalse);
       
  2458 		}
       
  2459 
       
  2460 	if (hasBodyText)
       
  2461 		{
       
  2462         if(!partiallyDownloaded)
       
  2463             message.SetBodyTextComplete(ETrue);
       
  2464 		}
       
  2465 	User::LeaveIfError(iServerEntry.ChangeEntry(message));
       
  2466 
       
  2467 
       
  2468 	// Checking the flags
       
  2469 	SetEntryL(iMessageMsvId);
       
  2470 	TMsvEmailEntry messageentry = iServerEntry.Entry();
       
  2471 	CImapFolder* folder = iSyncManager.GetFolderL(messageentry.Parent());
       
  2472 	
       
  2473 	if(folder!=NULL)
       
  2474 		{
       
  2475 		TMessageFlagInfo& flaginfo = iFetchBodyResponse->MessageFlagInfo();
       
  2476 	
       
  2477 		// Flags from the fetch message
       
  2478 	    TBool seen = flaginfo.QueryFlag(TMessageFlagInfo::ESeen);
       
  2479 		TBool answered = flaginfo.QueryFlag(TMessageFlagInfo::EAnswered);
       
  2480 		TBool flagged = flaginfo.QueryFlag(TMessageFlagInfo::EFlagged);
       
  2481 		TBool deleted = flaginfo.QueryFlag(TMessageFlagInfo::EDeleted);
       
  2482 		TBool draft = flaginfo.QueryFlag(TMessageFlagInfo::EDraft);
       
  2483 		TBool recent = flaginfo.QueryFlag(TMessageFlagInfo::ERecent);
       
  2484 
       
  2485 		// Flags in the local message
       
  2486 		TBool oUnread, oSeen, oAnswered, oFlagged, oDeleted, oDraft, oRecent;
       
  2487 		messageentry.GetIMAP4Flags(oUnread, oSeen, oAnswered, oFlagged, oDeleted, oDraft, oRecent);
       
  2488 
       
  2489 		// Are we configured to update the \seen flag on the server?
       
  2490 		if (iImapSettings.UpdatingSeenFlags())
       
  2491 			{
       
  2492 			// Make a note to update the servers \Seen flag if CHANGED on the client
       
  2493 			//  and different to the servers version
       
  2494 			if (BoolsAreEqual(messageentry.Unread(), seen) && BoolsAreEqual(oSeen, seen))
       
  2495 				{
       
  2496 				// The read flag has changed, but not on the server.  So this must be a local change.
       
  2497 				if (messageentry.Unread())
       
  2498 					{
       
  2499 					folder->AppendClearSeenL(messageentry.UID());
       
  2500 					}					
       
  2501 				else
       
  2502 					{
       
  2503 					folder->AppendSetSeenL(messageentry.UID());
       
  2504 					}					
       
  2505 				}
       
  2506 			}
       
  2507 
       
  2508 		if (BoolsAreNotEqual(oSeen, seen) || 
       
  2509 			BoolsAreNotEqual(oAnswered, answered) || 
       
  2510 			BoolsAreNotEqual(oFlagged, flagged) || 
       
  2511 			BoolsAreNotEqual(oDeleted, deleted) || 
       
  2512 			BoolsAreNotEqual(oDraft, draft) || 
       
  2513 			BoolsAreNotEqual(oRecent, recent) )
       
  2514 			{
       
  2515 			messageentry.SetIMAP4Flags(oUnread, seen, (answered || oAnswered), flagged, deleted, draft, recent);
       
  2516 			}
       
  2517 
       
  2518 		// update context
       
  2519 		SetEntryL(iMessageMsvId);
       
  2520 		User::LeaveIfError(iServerEntry.ChangeEntry(messageentry));	
       
  2521 		}
       
  2522 
       
  2523 
       
  2524 	// Finished with the fetch body response now
       
  2525 	delete iFetchBodyResponse;
       
  2526 	iFetchBodyResponse = NULL;
       
  2527 
       
  2528 	PropagateCompleteFlagL(fetchInfo->PartId(), hasBodyText, partiallyDownloaded);
       
  2529 	
       
  2530 	// increment progress counters
       
  2531 	++iPartsDone;
       
  2532 	iBytesDone+=fetchInfo->BytesFetched();
       
  2533 
       
  2534 	// fetch of part complete - delete the info
       
  2535 	CleanupStack::PopAndDestroy(fetchInfo);
       
  2536 	}
       
  2537 
       
  2538 
       
  2539 
       
  2540 /**
       
  2541 Propagates flags indicating message completeness up the tree structure for the 
       
  2542 fetched message part. Popagates the Complete and Partial-Fetch status.
       
  2543 
       
  2544 @param aId             ID of the message part that has just been fetched.
       
  2545 @param aDoBodyText     indicates that the part is text or contains text parts
       
  2546 @param aPartialFetched indicates that the part is partially fetched.
       
  2547 */
       
  2548 void CImapOpFetchBody::PropagateCompleteFlagL(TMsvId aId, TBool aDoBodyText, TBool aPartialFetched)
       
  2549 	{
       
  2550 	CMsvEntrySelection* selection=new (ELeave) CMsvEntrySelection;
       
  2551 	CleanupStack::PushL(selection);
       
  2552 	
       
  2553 	// get the siblings of this id
       
  2554 	SetEntryL(aId);
       
  2555 	TMsvId parent = iServerEntry.Entry().Parent();
       
  2556 
       
  2557 	// finish if we've managed to reach the top
       
  2558 	if (parent == KMsvRootIndexEntryId)
       
  2559 		return;
       
  2560 
       
  2561 	SetEntryL(parent);
       
  2562 
       
  2563 	// finish if we've reached a service
       
  2564 	if (iServerEntry.Entry().iType == KUidMsvServiceEntry)
       
  2565 		{
       
  2566 		return;
       
  2567 		}
       
  2568 
       
  2569 	User::LeaveIfError(iServerEntry.GetChildren(*selection));
       
  2570 
       
  2571 	TBool complete=ETrue;
       
  2572 	TBool bodyTextComplete=ETrue;
       
  2573 	TBool partiallyFetched=EFalse;
       
  2574 
       
  2575 	TBool related=((TMsvEmailEntry) iServerEntry.Entry()).MessageFolderType()==EFolderTypeRelated ? 
       
  2576 																					ETrue:EFalse;
       
  2577 	for (TInt i=0; i < selection->Count(); i++)
       
  2578 		{
       
  2579 		SetEntryL((*selection)[i]);
       
  2580 		if (!iServerEntry.Entry().Complete())
       
  2581 			{
       
  2582 			complete=EFalse;
       
  2583 			if((iServerEntry.Entry().iType==KUidMsvFolderEntry) && aPartialFetched)
       
  2584 				{
       
  2585 				complete=ETrue;
       
  2586 				}				
       
  2587 			// The current part is not complete so...
       
  2588 			// if it is either a text part or a HTML part then the body
       
  2589 			// text is marked as being incomplete.
       
  2590 			//
       
  2591 			// This code means that, if present, then both the text/plain
       
  2592 			// and text/html alternatives need to be downloaded before
       
  2593 			// the body text is marked as being complete.
       
  2594 			if ((iServerEntry.Entry().iType == KUidMsvEmailTextEntry)
       
  2595 				|| (iServerEntry.Entry().iType == KUidMsvEmailHtmlEntry ) || related )
       
  2596 				{
       
  2597 				if(aPartialFetched)
       
  2598 					{
       
  2599 					complete = ETrue;
       
  2600 					bodyTextComplete=ETrue;
       
  2601 					}
       
  2602 				else
       
  2603 				    {
       
  2604 					bodyTextComplete=EFalse;
       
  2605 				    }
       
  2606 				}
       
  2607 
       
  2608 			break;
       
  2609 			}
       
  2610 		}
       
  2611 	
       
  2612 	CleanupStack::PopAndDestroy(selection);
       
  2613 
       
  2614 	// if all the siblings were complete then make the parent
       
  2615 	// complete and continue up.
       
  2616 	if (complete || ((aDoBodyText || related) && bodyTextComplete))
       
  2617 		{
       
  2618 		SetEntryL(parent);
       
  2619 		TMsvEmailEntry entry = iServerEntry.Entry();
       
  2620 
       
  2621 		// check whether parent is complete, this will prevent us
       
  2622 		// checking all the messages in a real folder as they will all
       
  2623 		// be initialised to Complete
       
  2624 		if (!entry.Complete())
       
  2625 			{
       
  2626 			if (complete || ((iServerEntry.Entry().iType==KUidMsvFolderEntry) && aPartialFetched))
       
  2627 				{
       
  2628 				if((iServerEntry.Entry().iType==KUidMsvMessageEntry||iServerEntry.Entry().iType==KUidMsvEmailTextEntry || iServerEntry.Entry().iType==KUidMsvEmailHtmlEntry ) && aPartialFetched)
       
  2629 					{
       
  2630 					entry.SetComplete(EFalse);
       
  2631 					}
       
  2632 				else
       
  2633 					{
       
  2634 					entry.SetComplete(ETrue);
       
  2635 					}
       
  2636 				}
       
  2637 								
       
  2638 			if(aPartialFetched)
       
  2639 				{	
       
  2640 				if((iServerEntry.Entry().iType != KUidMsvAttachmentEntry) &&
       
  2641 						(iServerEntry.Entry().iType != KUidMsvEmailExternalBodyEntry))
       
  2642 					{
       
  2643 					entry.SetPartialDownloaded(ETrue);
       
  2644 					}
       
  2645 				partiallyFetched = ETrue;
       
  2646 				}
       
  2647 			else
       
  2648 				{
       
  2649 				entry.SetPartialDownloaded(EFalse);
       
  2650 				partiallyFetched = EFalse;
       
  2651 				
       
  2652 				if(entry.iType == KUidMsvFolderEntry)
       
  2653 				    entry.SetComplete(ETrue);
       
  2654 				
       
  2655 				if(entry.iType == KUidMsvAttachmentEntry)
       
  2656 				    entry.SetComplete(ETrue);
       
  2657 				
       
  2658 				
       
  2659 				}
       
  2660 			if((entry.Complete()) && (iServerEntry.Entry().iType == KUidMsvEmailTextEntry) || (iServerEntry.Entry().iType == KUidMsvEmailHtmlEntry ))
       
  2661 			    {
       
  2662 			    entry.SetBodyTextComplete(ETrue);
       
  2663                 entry.SetPartialDownloaded(EFalse);
       
  2664 			    }
       
  2665 			
       
  2666 			User::LeaveIfError(iServerEntry.ChangeEntry(entry));
       
  2667 		
       
  2668 			PropagateCompleteFlagL(parent, related|aDoBodyText,partiallyFetched);
       
  2669 			}
       
  2670 		else if (entry.PartialDownloaded())
       
  2671 			{
       
  2672 			entry.SetPartialDownloaded(EFalse);
       
  2673 			User::LeaveIfError(iServerEntry.ChangeEntry(entry));
       
  2674  			PropagateCompleteFlagL(parent, related|aDoBodyText,partiallyFetched);
       
  2675 			}
       
  2676 		else
       
  2677 		    {
       
  2678             
       
  2679             if (complete || ((iServerEntry.Entry().iType==KUidMsvFolderEntry) && aPartialFetched))
       
  2680                 {
       
  2681                 entry.SetComplete(ETrue);
       
  2682                 }
       
  2683             if (complete && (iServerEntry.Entry().iType==KUidMsvMessageEntry))
       
  2684                 {
       
  2685                 entry.SetComplete(ETrue);
       
  2686                 }
       
  2687             
       
  2688 		    }
       
  2689 		}
       
  2690 	}
       
  2691 	
       
  2692 
       
  2693 /**
       
  2694 Populates the passed progress object with progress information on the 
       
  2695 current message fetch operation.
       
  2696 @param aGenericProgressn - progress information object to be populated.
       
  2697 */
       
  2698 void CImapOpFetchBody::Progress(TImap4GenericProgress& aGenericProgress)
       
  2699 	{
       
  2700 	aGenericProgress.iPartsToDo = iPartsToDo;
       
  2701 	aGenericProgress.iPartsDone = iPartsDone;
       
  2702 	aGenericProgress.iBytesToDo = iBytesToDo;
       
  2703 	
       
  2704 	// iBytesDone is the byte count of completed parts 
       
  2705 	// - need increase this by the parts done on the current part,
       
  2706 	// if a fetch is outstanding.
       
  2707 	TInt32 tempBytesDone = iBytesDone;
       
  2708 	if ((  iCurrentStep == EFetchFirstPart || iCurrentStep == EFetchNext)
       
  2709 		&& iFetchList.Count()>0)
       
  2710 		{
       
  2711 		tempBytesDone += (iFetchList[0])->BytesFetched();
       
  2712 		}
       
  2713 	aGenericProgress.iBytesDone = tempBytesDone;
       
  2714 	}
       
  2715 
       
  2716 
       
  2717 /**
       
  2718 Handles server error responses according to current step
       
  2719 
       
  2720 @return TInt error code for completion (if error fatal)
       
  2721 */
       
  2722 TInt CImapOpFetchBody::ProcessServerError()
       
  2723 	{
       
  2724 	switch(iCurrentStep)
       
  2725 		{
       
  2726 	case EGetBodyStructure:
       
  2727 	case EProcessBodyStructure:
       
  2728 	case EBuildFetchList:
       
  2729 	case EFetchFirstPart:
       
  2730 	case EFetchNext:
       
  2731 	default:
       
  2732 		return (iStatus.Int());
       
  2733 		}
       
  2734 	// return KErrNone;
       
  2735 	}
       
  2736