|
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", ¶m, &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 |