|
1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // 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 |
|
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) |
|
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 *newpath=aPath; |
|
1142 if (aPath.Length()) |
|
1143 newpath->Des().Append(KIMAP_REL_PATH_SEPARATOR); |
|
1144 newpath->Des().AppendNum(1); |
|
1145 BuildContentEntryL(aParent, aBodyStructure, newpath->Des(), aThisMessage, aAttachments, aIsMHTML, aRelatedAttachments); |
|
1146 CleanupStack::PopAndDestroy(newpath); |
|
1147 } |
|
1148 // otherwise is a multipart entry |
|
1149 else |
|
1150 { |
|
1151 // Nest down a level: create a folder entry |
|
1152 SetEntryL(aParent); |
|
1153 TMsvEmailEntry message; |
|
1154 message.iMtm=KUidMsgTypeIMAP4; |
|
1155 message.iServiceId=iImapSettings.ServiceId(); |
|
1156 message.iType=KUidMsvFolderEntry; |
|
1157 message.iSize=0; |
|
1158 message.SetComplete(EFalse); |
|
1159 User::LeaveIfError(iServerEntry.CreateEntryBulk(message)); // bulk op completed at the end of ProcessBodyStructureL() |
|
1160 |
|
1161 __LOG_FORMAT((iSession->LogId(), " Created attachment folder id %x as child of %x", message.Id(), aParent)); |
|
1162 |
|
1163 aParent=message.Id(); |
|
1164 |
|
1165 TPtrC8 multipart = aBodyStructure->SubType(); |
|
1166 // Got anything? |
|
1167 if (multipart.Size()>0) |
|
1168 { |
|
1169 // Parse multipart type string, do this first so |
|
1170 // information is available when parsing children |
|
1171 TImEmailFolderType ft=EFolderTypeUnknown; |
|
1172 if (multipart.CompareF(KImcvRelated)==0) |
|
1173 { |
|
1174 __LOG_TEXT(iSession->LogId(), " Folder type MULTIPART/RELATED"); |
|
1175 ft=EFolderTypeRelated; |
|
1176 } |
|
1177 else if (multipart.CompareF(KImcvMixed)==0) |
|
1178 { |
|
1179 __LOG_TEXT(iSession->LogId(), " Folder type MULTIPART/MIXED"); |
|
1180 ft=EFolderTypeMixed; |
|
1181 } |
|
1182 else if (multipart.CompareF(KImcvParallel)==0) |
|
1183 { |
|
1184 __LOG_TEXT(iSession->LogId(), " Folder type MULTIPART/PARALLEL"); |
|
1185 ft=EFolderTypeParallel; |
|
1186 } |
|
1187 else if (multipart.CompareF(KImcvAlternative)==0) |
|
1188 { |
|
1189 __LOG_TEXT(iSession->LogId(), " Folder type MULTIPART/ALTERNATIVE"); |
|
1190 ft=EFolderTypeAlternative; |
|
1191 } |
|
1192 else if (multipart.CompareF(KImcvDigest)==0) |
|
1193 { |
|
1194 __LOG_TEXT(iSession->LogId(), " Folder type MULTIPART/DIGEST"); |
|
1195 ft=EFolderTypeDigest; |
|
1196 } |
|
1197 |
|
1198 SetEntryL(aParent); |
|
1199 |
|
1200 // ...and save it |
|
1201 TMsvEmailEntry folder=iServerEntry.Entry(); |
|
1202 folder.SetMessageFolderType(ft); |
|
1203 #if SET_RELATED_ID |
|
1204 // Set message's iRelatedId to messageId |
|
1205 folder.iRelatedId=folder.Id(); |
|
1206 #endif |
|
1207 User::LeaveIfError(iServerEntry.ChangeEntryBulk(folder)); |
|
1208 |
|
1209 // Process each of the multi-parts... |
|
1210 TInt subnr=1; |
|
1211 TInt numParts=aBodyStructure->EmbeddedBodyStructureList().Count(); |
|
1212 for (TInt i=0;i<numParts;++i) |
|
1213 { |
|
1214 // Process item (doesn't use AllocL) |
|
1215 HBufC8* newpath=HBufC8::NewLC(aPath.Length()+4); |
|
1216 *newpath=aPath; |
|
1217 if (aPath.Length()) |
|
1218 newpath->Des().Append(KIMAP_REL_PATH_SEPARATOR); |
|
1219 newpath->Des().AppendNum(subnr++); |
|
1220 BuildContentEntryL(aParent, |
|
1221 aBodyStructure->EmbeddedBodyStructureList()[i], |
|
1222 newpath->Des(), |
|
1223 aThisMessage, |
|
1224 aAttachments, |
|
1225 aIsMHTML, |
|
1226 aRelatedAttachments); |
|
1227 CleanupStack::PopAndDestroy(newpath); |
|
1228 } |
|
1229 } |
|
1230 } |
|
1231 } |
|
1232 |
|
1233 |
|
1234 |
|
1235 /** |
|
1236 Builds the TMsvEmailEntry and associated CImMimeHeader for an individual message content part. |
|
1237 |
|
1238 @param aParent TMsvId of the parent of the current part. When called |
|
1239 initially, this is the root entry representing the message |
|
1240 @param aBodyStructure Parsed bodystructure data for the current part |
|
1241 @param aPath The IMAP relative path for message parts. |
|
1242 @param aThisMessage TMsvId of the root entry for the message. |
|
1243 @param aAttachments Counter for attachments. |
|
1244 @param aIsMHTML Flag indicating that the message has been identified as MHTML |
|
1245 @param aRelatedAttachments Counter for related attachments. |
|
1246 */ |
|
1247 void CImapOpFetchBody::BuildContentEntryL(const TMsvId aParent, CImapBodyStructure* aBodyStructure, const TDesC8& aPath, const TMsvId aThisMessage, TInt& aAttachments, TBool& aIsMHTML, TInt& aRelatedAttachments) |
|
1248 { |
|
1249 |
|
1250 // First, is this actually an entry, or another level of nesting? |
|
1251 if (aBodyStructure->BodyStructureType()==CImapBodyStructure::ETypeMultipart) |
|
1252 { |
|
1253 // Another level of nesting? Call BuildTreeL() |
|
1254 BuildTreeL(aParent, aBodyStructure, aPath, aThisMessage, aAttachments, aIsMHTML, aRelatedAttachments); |
|
1255 return; |
|
1256 } |
|
1257 |
|
1258 // Skeleton for new entry |
|
1259 SetEntryL(aParent); |
|
1260 |
|
1261 TFileName attachmentFilename; // somewhere to store an attachment filename |
|
1262 TMsvEmailEntry message; |
|
1263 message.iSize=0; |
|
1264 message.iMtm=KUidMsgTypeIMAP4; |
|
1265 message.iServiceId=iImapSettings.ServiceId(); |
|
1266 message.SetUID(iMessageUid); |
|
1267 message.SetValidUID(EFalse); // ValidUID Flag used to indicate if the message has ever been fetched |
|
1268 message.SetComplete(EFalse); |
|
1269 |
|
1270 // Save mime TYPE/SUBTYPE |
|
1271 CImMimeHeader* mime=CImMimeHeader::NewLC(); |
|
1272 TPtrC8 type = aBodyStructure->Type(); |
|
1273 TPtrC8 subtype = aBodyStructure->SubType(); |
|
1274 mime->SetContentTypeL(type); |
|
1275 mime->SetContentSubTypeL(subtype); |
|
1276 |
|
1277 __LOG_FORMAT((iSession->LogId(), " MIME: %S/%S", &type, &subtype)); |
|
1278 |
|
1279 // initialise the data type to be an attachment |
|
1280 message.iType=KUidMsvAttachmentEntry; |
|
1281 |
|
1282 // Store the relative path for the content part |
|
1283 mime->SetRelativePathL(aPath); |
|
1284 |
|
1285 if (aBodyStructure->BodyStructureType()==CImapBodyStructure::ETypeText) |
|
1286 { |
|
1287 ProcessTextSubtypeL(aBodyStructure, *mime, message, aIsMHTML); |
|
1288 } |
|
1289 |
|
1290 // Process the Parameter list |
|
1291 ProcessParameterListL(aBodyStructure->ParameterList(), *mime, message, attachmentFilename); |
|
1292 |
|
1293 // ID: save it |
|
1294 TPtrC8 id = aBodyStructure->BodyId(); |
|
1295 if (id.Length() > 0) |
|
1296 { |
|
1297 __LOG_FORMAT((iSession->LogId(), " Content ID: %S", &id)); |
|
1298 mime->SetContentIDL(StripAngledBrackets(id)); |
|
1299 } |
|
1300 |
|
1301 // Description: save it |
|
1302 TPtrC8 description = aBodyStructure->BodyDescription(); |
|
1303 if (description.Length() > 0) |
|
1304 { |
|
1305 __LOG_FORMAT((iSession->LogId(), " Content Description: %S", &description)); |
|
1306 mime->SetContentDescriptionL(description); |
|
1307 } |
|
1308 |
|
1309 // Encoding |
|
1310 TPtrC8 encoding = aBodyStructure->BodyEncoding(); |
|
1311 mime->SetContentTransferEncodingL(encoding); |
|
1312 __LOG_FORMAT((iSession->LogId(), " Content Transfer Encoding: %S", &encoding)); |
|
1313 |
|
1314 // Octets (encoded form) |
|
1315 TInt actualsize; |
|
1316 TLex8 lex(aBodyStructure->BodySizeOctets()); |
|
1317 lex.Val(actualsize); |
|
1318 |
|
1319 // Some servers gives a negative size value when the body text is empty. Need to reset this to |
|
1320 // zero to prevent corruption error later on. |
|
1321 if(actualsize < 0) |
|
1322 { |
|
1323 actualsize = 0; |
|
1324 } |
|
1325 |
|
1326 // Modify actualsize to show *decoded* size: this is basically the size of |
|
1327 // this part, multiplied by 6/8 if it's BASE64 encoded. For all other |
|
1328 // encodings, we leave the size as-is as there's no hard & fast rule |
|
1329 // which can be applied. |
|
1330 if (encoding.CompareF(KMIME_BASE64)==0) |
|
1331 { |
|
1332 message.iSize=(actualsize*6)/8; |
|
1333 } |
|
1334 else |
|
1335 { |
|
1336 message.iSize=actualsize; |
|
1337 } |
|
1338 |
|
1339 // Add into total message size |
|
1340 iDecodedSizeOfAllParts+=message.iSize; |
|
1341 |
|
1342 // Use iBioType message entry data member to store size on remote server |
|
1343 message.iBioType=actualsize; |
|
1344 |
|
1345 //If any part of email (text/plain mime, text/html mime, attachment....) |
|
1346 // is empty then should not fetch it. |
|
1347 if(actualsize == 0) |
|
1348 { |
|
1349 message.SetComplete(ETrue); |
|
1350 } |
|
1351 |
|
1352 // mark attached emails as such |
|
1353 if (aBodyStructure->BodyStructureType()==CImapBodyStructure::ETypeMessageRfc822) |
|
1354 { |
|
1355 // embedded message - marked as a message |
|
1356 // NB needs to be set before ProcessExtendedFieldsL() can be called |
|
1357 message.iType=KUidMsvMessageEntry; |
|
1358 iDecodedSizeOfAllParts-=message.iSize; |
|
1359 } |
|
1360 |
|
1361 // Process any extended fields |
|
1362 ProcessExtendedFieldsL(aBodyStructure, *mime, message, attachmentFilename); |
|
1363 |
|
1364 // Now we're working on the type |
|
1365 if (message.iType==KUidMsvMessageEntry) |
|
1366 { |
|
1367 BuildEmbeddedMessageL(aBodyStructure, *mime, message, aPath, aAttachments); |
|
1368 } |
|
1369 else |
|
1370 { |
|
1371 BuildNonMessageEntryL(aParent, *mime, message, aAttachments, aRelatedAttachments); |
|
1372 } |
|
1373 CleanupStack::PopAndDestroy(mime); |
|
1374 __LOG_FORMAT((iSession->LogId(), " BuildContentEntryL done: created id %x, attachments so far %d", message.Id(), aAttachments)); |
|
1375 } |
|
1376 |
|
1377 /** |
|
1378 Sets flags in the TMsvEmailEntry according to the subtype for TEXT/xxx mime parts. |
|
1379 |
|
1380 @param aBodyStructure bodystructure component being processed |
|
1381 @param aMime (partially) parsed mime information |
|
1382 @param aMessage the email entry under construction |
|
1383 @param aIsMHTML Flag indicating that the message has been identified as MHTML |
|
1384 */ |
|
1385 void CImapOpFetchBody::ProcessTextSubtypeL(CImapBodyStructure* aBodyStructure, CImMimeHeader& aMime, TMsvEmailEntry& aMessage, TBool& aIsMHTML) |
|
1386 { |
|
1387 // text/rtf? |
|
1388 if (aMime.ContentSubType().CompareF(KMIME_RTF)==0) |
|
1389 { |
|
1390 if (!(aBodyStructure->ExtDispositionName().CompareF(KMIME_ATTACHMENT)==0)) |
|
1391 { |
|
1392 aMessage.iType=KUidMsvEmailRtfEntry; |
|
1393 } |
|
1394 } |
|
1395 // text/html? |
|
1396 else if (aMime.ContentSubType().CompareF(KMIME_HTML)==0) |
|
1397 { |
|
1398 // If this is not an attachment (ie disposition field |
|
1399 // is not "attachment") then this is a MHTML Message. |
|
1400 if (!(aBodyStructure->ExtDispositionName().CompareF(KMIME_ATTACHMENT)==0)) |
|
1401 { |
|
1402 aMessage.iType=KUidMsvEmailHtmlEntry; |
|
1403 aIsMHTML=ETrue; |
|
1404 } |
|
1405 } |
|
1406 // text/x-vcard? |
|
1407 else if (aMime.ContentSubType().CompareF(KMIME_XVCARD)==0) |
|
1408 { |
|
1409 // Set vCard flag in message |
|
1410 aMessage.SetVCard(ETrue); |
|
1411 } |
|
1412 // text/x-vcalendar |
|
1413 else if (aMime.ContentSubType().CompareF(KMIME_VCALENDAR)==0) |
|
1414 { |
|
1415 // Set vCalendar flag in message |
|
1416 aMessage.SetVCalendar(ETrue); |
|
1417 iIsVCalendar = ETrue; |
|
1418 } |
|
1419 // text/calendar |
|
1420 else if (aMime.ContentSubType().CompareF(KMIME_ICALENDAR)==0) |
|
1421 { |
|
1422 // Set iCalendar flag in message |
|
1423 aMessage.SetICalendar(ETrue); |
|
1424 iIsICalendar = ETrue; |
|
1425 } |
|
1426 else |
|
1427 aMessage.iType=KUidMsvEmailTextEntry; |
|
1428 } |
|
1429 |
|
1430 |
|
1431 |
|
1432 /** |
|
1433 Populates the parameter lists of the CImMimeHeader object |
|
1434 |
|
1435 @param aParamList parameter list, retrieved from the bodystructure |
|
1436 @param aMime mime header object to be populated |
|
1437 @param aMessage the email entry under construction |
|
1438 @param aAttachmentFilename updated with a file name, if one is found |
|
1439 */ |
|
1440 void CImapOpFetchBody::ProcessParameterListL(const CImapBodyStructure::RAttributeValuePairList& aParamList, CImMimeHeader& aMime, TMsvEmailEntry& aMessage, TFileName& aAttachmentFilename) |
|
1441 { |
|
1442 TUint charsetId = KUidMsvCharsetNone; |
|
1443 |
|
1444 TInt paramCount = aParamList.Count(); |
|
1445 if (paramCount > 0) |
|
1446 { |
|
1447 __LOG_TEXT(iSession->LogId(), " Parameter list:"); |
|
1448 |
|
1449 for(TInt i=0;i<paramCount;++i) |
|
1450 { |
|
1451 TPtrC8 param=aParamList[i].iAttribute; |
|
1452 TPtrC8 value=aParamList[i].iValue; |
|
1453 |
|
1454 __LOG_FORMAT((iSession->LogId(), " %S %S", ¶m, &value)); |
|
1455 |
|
1456 aMime.ContentTypeParams().AppendL(param); |
|
1457 aMime.ContentTypeParams().AppendL(value); |
|
1458 |
|
1459 // Have we come across a 'NAME' tuple? If so, force the MIME type of this |
|
1460 // entry to be an attachment. |
|
1461 if ((param.CompareF(KMIME_NAME)==0) |
|
1462 || (param.CompareF(KMIME_NAME_RFC2231)==0)) |
|
1463 { |
|
1464 __LOG_TEXT(iSession->LogId(), " Attachment filename found, therefore this is an attachment"); |
|
1465 |
|
1466 FindFilenameDecodeL(aMime,aAttachmentFilename); |
|
1467 StripIllegalCharactersFromFileName(aAttachmentFilename); |
|
1468 aMessage.iDetails.Set(aAttachmentFilename); |
|
1469 |
|
1470 // If embedded message do not save as an attachment |
|
1471 if (aMessage.iType!=KUidMsvMessageEntry) |
|
1472 { |
|
1473 aMessage.iType=KUidMsvAttachmentEntry; |
|
1474 } |
|
1475 } |
|
1476 else if (param.CompareF(KImcvCharset) == 0) |
|
1477 { |
|
1478 // We have found a charset parameter tuple. Convert the value to a |
|
1479 // charset ID for storage in the MIME headers. |
|
1480 if (value.Length() > 0) |
|
1481 { |
|
1482 charsetId = iCharConv->GetMimeCharsetUidL(value); |
|
1483 } |
|
1484 } |
|
1485 } |
|
1486 } |
|
1487 |
|
1488 aMime.SetMimeCharset(charsetId); |
|
1489 } |
|
1490 |
|
1491 /** |
|
1492 Populates extended header fields in the CImMimeHeader object |
|
1493 |
|
1494 @param aBodyStructure bodystructure object returned by the imap session |
|
1495 @param aMime mime header object to be populated |
|
1496 @param aMessage the email entry under construction |
|
1497 @param aAttachmentFilename updated with a file name, if one is found |
|
1498 */ |
|
1499 void CImapOpFetchBody::ProcessExtendedFieldsL(CImapBodyStructure* aBodyStructure, CImMimeHeader& aMime, TMsvEmailEntry& aMessage, TFileName& aAttachmentFilename) |
|
1500 { |
|
1501 // Add the diposition name as the first pair of parameters. |
|
1502 TPtrC8 dispositionName = aBodyStructure->ExtDispositionName(); |
|
1503 if (dispositionName.Size() != 0) |
|
1504 { |
|
1505 __LOG_FORMAT((iSession->LogId(), " adding disp name: %S", &dispositionName)); |
|
1506 aMime.ContentDispositionParams().AppendL(dispositionName); |
|
1507 aMime.ContentDispositionParams().AppendL(KNullDesC8); |
|
1508 } |
|
1509 |
|
1510 // Now add the actual diposition parameters as name-value pairs |
|
1511 const CImapBodyStructure::RAttributeValuePairList& dispParams = aBodyStructure->ExtDispositionParameterList(); |
|
1512 TInt dispParamsCount = dispParams.Count(); // Note that this will be 0 if there is no disposition available |
|
1513 |
|
1514 for (TInt i=0; i < dispParamsCount; ++i) |
|
1515 { |
|
1516 __LOG_FORMAT((iSession->LogId(), " disp: %S %S", &dispParams[i].iAttribute, &dispParams[i].iValue)); |
|
1517 |
|
1518 aMime.ContentDispositionParams().AppendL(dispParams[i].iAttribute); |
|
1519 aMime.ContentDispositionParams().AppendL(dispParams[i].iValue); |
|
1520 |
|
1521 // Filename? If so, force this as an attachment |
|
1522 if ((dispParams[i].iAttribute.CompareF(KMIME_FILENAME)==0) |
|
1523 || (dispParams[i].iAttribute.CompareF(KMIME_FILENAME_RFC2231)==0)) |
|
1524 { |
|
1525 __LOG_TEXT(iSession->LogId(), " Attachment filename found in extended fields."); |
|
1526 FindFilenameDecodeL(aMime,aAttachmentFilename); |
|
1527 StripIllegalCharactersFromFileName(aAttachmentFilename); |
|
1528 aMessage.iDetails.Set(aAttachmentFilename); |
|
1529 |
|
1530 // If embedded message do not save as an attachment |
|
1531 if (aMessage.iType!=KUidMsvMessageEntry) |
|
1532 { |
|
1533 aMessage.iType=KUidMsvAttachmentEntry; |
|
1534 } |
|
1535 } |
|
1536 |
|
1537 } // end for |
|
1538 } |
|
1539 |
|
1540 |
|
1541 |
|
1542 |
|
1543 /** |
|
1544 Performs final parsing and construction of an embedded MESSAGE/RFC822 message |
|
1545 |
|
1546 As an MESSAGE/RFC822 part, the structure will contain envelope info. This is |
|
1547 parsed via ProcessEnvelopeL to construct a CImHeader object for the embedded |
|
1548 message. This is streamed to file along with the mime header information for |
|
1549 the message part. |
|
1550 |
|
1551 The CImapBodyStructure referred to via aBodyStructure->iMultiParts is the body |
|
1552 structure of the embedded message. This is an entire message-within-a-message |
|
1553 and so gets treated as a whole new mail. |
|
1554 |
|
1555 @param aBodyStructure Parsed bodystructure data for the current MESSAGE/RFC822 part |
|
1556 @param aMime mime header object to be populated |
|
1557 @param aMessage the email entry under construction |
|
1558 @param aPath The IMAP relative path for message parts. |
|
1559 @param aAttachments Counter for attachments. |
|
1560 */ |
|
1561 void CImapOpFetchBody::BuildEmbeddedMessageL(CImapBodyStructure* aBodyStructure, CImMimeHeader& aMime, TMsvEmailEntry& aMessage, const TDesC8& aPath, TInt& aAttachments) |
|
1562 { |
|
1563 // Prepare a CImHeader object for the message, |
|
1564 // update flags as appropriate on the message entry. |
|
1565 CImHeader *messageheader=CImHeader::NewLC(); |
|
1566 ProcessEnvelopeL(messageheader, aMessage, aBodyStructure->GetRfc822EnvelopeStructureL()); |
|
1567 |
|
1568 // Create message |
|
1569 User::LeaveIfError(iServerEntry.CreateEntryBulk(aMessage)); // bulk op completed at the end of ProcessBodyStructureL() |
|
1570 SetEntryL(aMessage.Id()); |
|
1571 |
|
1572 // Store CImHeader and CImMimeHeader for the MESSAGE/RFC822 |
|
1573 StoreHeadersL(messageheader, &aMime); |
|
1574 CleanupStack::PopAndDestroy(messageheader); |
|
1575 |
|
1576 #if SET_RELATED_ID |
|
1577 // Set message's iRelatedId to messageId |
|
1578 TMsvEntry entryToChange(iServerEntry.Entry()); |
|
1579 entryToChange.iRelatedId=entryToChange.Id(); |
|
1580 ChangeEntryBulkL(entryToChange); // bulk op completed at the end of ProcessBodyStructureL() |
|
1581 #endif |
|
1582 // Process the bodystructure for this embedded message... |
|
1583 TInt attachments=0; |
|
1584 TInt relatedAttachments; |
|
1585 TBool isMHTML=EFalse; |
|
1586 |
|
1587 CImapBodyStructure* bodystructure = aBodyStructure->EmbeddedBodyStructureList()[0]; |
|
1588 BuildTreeL(aMessage.Id(), bodystructure, aPath, aMessage.Id(), attachments, isMHTML, relatedAttachments); |
|
1589 __LOG_FORMAT((iSession->LogId(), " Built embedded message id %x attachments %d MHTML %d", aMessage.Id(), attachments, isMHTML)); |
|
1590 |
|
1591 // Save attachment and MHTML flags |
|
1592 if (attachments>0 || isMHTML) |
|
1593 { |
|
1594 SetEntryL(aMessage.Id()); |
|
1595 TMsvEmailEntry thisMessage = iServerEntry.Entry(); |
|
1596 |
|
1597 if (attachments>0) |
|
1598 { |
|
1599 thisMessage.SetAttachment(ETrue); |
|
1600 } |
|
1601 |
|
1602 if (isMHTML) |
|
1603 { |
|
1604 thisMessage.SetMHTMLEmail(ETrue); |
|
1605 } |
|
1606 |
|
1607 User::LeaveIfError(iServerEntry.ChangeEntryBulk(thisMessage)); |
|
1608 } |
|
1609 |
|
1610 // embedded messages are counted as attachments |
|
1611 ++aAttachments; |
|
1612 } |
|
1613 |
|
1614 |
|
1615 |
|
1616 |
|
1617 /** |
|
1618 Performs final processing and construction of non-MESSAGE/RFC822 parts. |
|
1619 Stores the mime header information for the message part. |
|
1620 |
|
1621 This method is called for all message parts that are not multi-part and |
|
1622 not message/rfc822. Thus, each part for which this method is called is |
|
1623 eligible for fetch. This method calls AddFilterFetchItem to build up |
|
1624 the array of parts to fetch. |
|
1625 |
|
1626 @param aParent TMsvId of the parent of the current part. |
|
1627 @param aMime mime header object to be populated |
|
1628 @param aMessage the email entry under construction |
|
1629 @param aAttachments Counter for attachments. |
|
1630 @param aRelatedAttachments Counter for related attachments. |
|
1631 */ |
|
1632 void CImapOpFetchBody::BuildNonMessageEntryL(const TMsvId& aParent, CImMimeHeader& aMime, TMsvEmailEntry& aMessage, TInt& aAttachments, TInt& aRelatedAttachments) |
|
1633 { |
|
1634 // Some other type of entry... |
|
1635 SetEntryL(aParent); |
|
1636 |
|
1637 // save parent folder type |
|
1638 TImEmailFolderType parentFolderType = ((TMsvEmailEntry)iServerEntry.Entry()).MessageFolderType(); |
|
1639 |
|
1640 // set attachment and HTML flags on item |
|
1641 if (aMessage.iType==KUidMsvAttachmentEntry) |
|
1642 { |
|
1643 aMessage.SetAttachment(ETrue); |
|
1644 } |
|
1645 |
|
1646 if (aMessage.iType==KUidMsvEmailHtmlEntry) |
|
1647 { |
|
1648 aMessage.SetMHTMLEmail(ETrue); |
|
1649 } |
|
1650 |
|
1651 // ensure there is a filename if it is a non-text item |
|
1652 if (aMessage.iType!=KUidMsvEmailTextEntry && aMessage.iDetails.Length() == 0) |
|
1653 { |
|
1654 // use iAttachmentName for temporary buffer |
|
1655 GetDefaultFilenameL(iAttachmentName, aMessage, &aMime); |
|
1656 aMessage.iDetails.Set(iAttachmentName); |
|
1657 } |
|
1658 |
|
1659 // Create the Entry |
|
1660 User::LeaveIfError(iServerEntry.CreateEntryBulk(aMessage)); // bulk op completed at the end of ProcessBodyStructureL() |
|
1661 SetEntryL(aMessage.Id()); |
|
1662 |
|
1663 __LOG_FORMAT((iSession->LogId(), " Created attachment id %x as child of %x - type %d", aMessage.Id(), aParent, parentFolderType)); |
|
1664 |
|
1665 #if SET_RELATED_ID |
|
1666 // Set message's iRelatedId to messageId |
|
1667 TMsvEntry entryToChange(iServerEntry.Entry()); |
|
1668 entryToChange.iRelatedId=entryToChange.Id(); |
|
1669 ChangeEntryBulkL(entryToChange); // bulk op completed at the end of ProcessBodyStructureL() |
|
1670 #endif |
|
1671 |
|
1672 // Stream the MIME info out into the newly created attachment entry. |
|
1673 StoreHeadersL(NULL, &aMime); |
|
1674 |
|
1675 // This entry is NOT an attachment in the following cases - |
|
1676 // 1) This is an attachment whose parent is a MULTIPART/RELATED folder. |
|
1677 // In this case, this entry could be a image entity for an MHTML |
|
1678 // entry with the same parent. |
|
1679 // 2) This is an MHTML entry whose parent is a MULTIPART/ALTERNATIVE |
|
1680 // folder. In this case, this entry is the MHTML alternative to a |
|
1681 // text entry with the same parent. |
|
1682 // 3) This is an MHTML entry whose parent is MESSAGE folder. In this |
|
1683 // case, the message is a simple MHTML message with no text |
|
1684 // alternative or embedded image. |
|
1685 // 4) This is an MHTML entry whose parent is a MULTIPART/RELATED folder. |
|
1686 // In this case, this entry is the MHTML for the message. |
|
1687 // 5) This is an MHTML entry whose parent is a MULTIPART/MIXED folder. |
|
1688 // In this case, this entry is the MHTML for the message. It cannot |
|
1689 // be the attachment it self as then it would be of type attachment. |
|
1690 // Therefore, an entry is only an attachment if is of type attachment and |
|
1691 // its parent is not a MULTIPART/RELATED folder. |
|
1692 if(aMessage.iType==KUidMsvAttachmentEntry && parentFolderType!=EFolderTypeRelated) |
|
1693 { |
|
1694 ++aAttachments; |
|
1695 } |
|
1696 // if it is related we might want to include it if the message |
|
1697 // turns out not to be MHTML |
|
1698 else if (aMessage.iType==KUidMsvAttachmentEntry && parentFolderType==EFolderTypeRelated) |
|
1699 { |
|
1700 ++aRelatedAttachments; |
|
1701 } |
|
1702 |
|
1703 // Apply filters and add to fetch list |
|
1704 if (iFetchPartialMail) |
|
1705 { |
|
1706 AddFilterFetchItemL(aMime, aMessage, iGetPartialMailInfo); |
|
1707 } |
|
1708 else |
|
1709 { |
|
1710 AddFilterFetchItemL(aMime, aMessage, iGetPartialMailInfo.iGetMailBodyParts); |
|
1711 } |
|
1712 } |
|
1713 |
|
1714 |
|
1715 |
|
1716 /** |
|
1717 Issues a request to fetch the next item in the array of parts to fetch. |
|
1718 @return EFalse if no parts remaining in the array. |
|
1719 */ |
|
1720 TBool CImapOpFetchBody::FetchPartL() |
|
1721 { |
|
1722 // Anything to do? |
|
1723 if (iFetchList.Count() <= 0) |
|
1724 { |
|
1725 return EFalse; |
|
1726 } |
|
1727 else |
|
1728 { |
|
1729 // Create a new body response store to hold any response information |
|
1730 // from the fetch command. |
|
1731 delete iFetchBodyResponse; |
|
1732 iFetchBodyResponse = CImapFetchBodyResponse::NewL(); |
|
1733 |
|
1734 // If part to fetch was of type attachment, this must be registered |
|
1735 // with the CAF framework. This is indicated in the FetchBodyInfo by |
|
1736 // a non-NULL CAF pointer. |
|
1737 if ((iFetchList[0])->Caf()!=NULL) |
|
1738 { |
|
1739 RegisterWithCafL(*(iFetchList[0])); |
|
1740 } |
|
1741 |
|
1742 // UpdatingSeenFlags == ETrue means that we want to set the seen flag explicitly, |
|
1743 // using CImapSession::StoreL(), so peek should be ETrue too. |
|
1744 // UpdatingSeenFlags == EFalse meanse that we want the server to set the seen flag |
|
1745 // implicitly when the message is downloaded, |
|
1746 // - i.e. peek should be EFalse too. |
|
1747 TBool peek = iImapSettings.UpdatingSeenFlags(); |
|
1748 |
|
1749 iSession->FetchBodyL(iStatus, iMessageUid, peek, *(iFetchList[0]), *iFetchBodyResponse); |
|
1750 } |
|
1751 return ETrue; |
|
1752 } |
|
1753 |
|
1754 |
|
1755 /** |
|
1756 Registers the part with the CAF framework. |
|
1757 |
|
1758 This method is called for all parts of type Application that are |
|
1759 to be fetched. CAF registration requires concatenated content-type |
|
1760 and subtype. The type and subtype have previously been received |
|
1761 and stored in the CImMimeHeader for the part. |
|
1762 |
|
1763 If the part is not registered as a result of the call to |
|
1764 CImCaf::RegisterL(), the FetchBodyInfo object is updated to show |
|
1765 this. |
|
1766 |
|
1767 @param fetchInfo information about the part to be fetched. |
|
1768 */ |
|
1769 void CImapOpFetchBody::RegisterWithCafL(CFetchBodyInfo& fetchInfo) |
|
1770 { |
|
1771 // load the mime header for the entry. |
|
1772 // Information will be required for parts to be fetched. |
|
1773 SetEntryL(fetchInfo.PartId()); |
|
1774 CImMimeHeader* mimeHeader = CImMimeHeader::NewLC(); |
|
1775 CMsvStore* store = iServerEntry.ReadStoreL(); |
|
1776 CleanupStack::PushL(store); |
|
1777 mimeHeader->RestoreL(*store); |
|
1778 // finished with the read store |
|
1779 CleanupStack::PopAndDestroy(store); |
|
1780 |
|
1781 // Create buffer for concatenating. + 1 creates space for '/' |
|
1782 HBufC8* buf = HBufC8::NewLC(mimeHeader->ContentSubType().Length() + mimeHeader->ContentType().Length() + 1); |
|
1783 TPtr8 ptr(buf->Des()); |
|
1784 ptr.Copy(mimeHeader->ContentType()); |
|
1785 ptr.Append(KImcvForwardSlash); |
|
1786 ptr.Append(mimeHeader->ContentSubType()); |
|
1787 |
|
1788 // Attempt to register with CAF - this may not succeed. |
|
1789 iCaf->RegisterL(ptr); |
|
1790 CleanupStack::PopAndDestroy(buf); |
|
1791 CleanupStack::PopAndDestroy(mimeHeader); |
|
1792 |
|
1793 // Clear the iCaf pointer in fetchInfo if CAF is not interested. |
|
1794 if(!iCaf->Registered()) |
|
1795 { |
|
1796 fetchInfo.ResetCaf(); |
|
1797 } |
|
1798 } |
|
1799 |
|
1800 |
|
1801 /** |
|
1802 Populates a CImHeader object given a CImapEnvelope abject. |
|
1803 Updates the passed TMsvEntry with appropriate information (date, from and subject) |
|
1804 |
|
1805 @param aHeader Header information object to populate |
|
1806 @param aServerEntry Server entry id associated with the envelope object |
|
1807 @param aImapEnvelope Parsed IMAP Envelope object. |
|
1808 */ |
|
1809 void CImapOpFetchBody::ProcessEnvelopeL(CImHeader* aHeader, TMsvEntry& aEntry, CImapEnvelope& aImapEnvelope) |
|
1810 { |
|
1811 __LOG_FORMAT((iSession->LogId(), " Processing envelope data, id=%x", aEntry.Id())); |
|
1812 |
|
1813 TPtrC8 tptr; |
|
1814 // Parse date information |
|
1815 tptr.Set(aImapEnvelope.EnvDate()); |
|
1816 TImRfc822DateField date; |
|
1817 date.ParseDateField(tptr, aEntry.iDate); |
|
1818 |
|
1819 // Subject in CImHeader (TMsvEntry is later on after post-processing) |
|
1820 if (aImapEnvelope.EnvSubject().CompareF(KIMAP_NIL)!=0) |
|
1821 { |
|
1822 aHeader->SetSubjectL(aImapEnvelope.EnvSubject()); |
|
1823 } |
|
1824 |
|
1825 // From information: both in CImHeader and TMsvEntry |
|
1826 if (aImapEnvelope.EnvFrom().Count() != 0) |
|
1827 { |
|
1828 __LOG_TEXT(iSession->LogId(), " Processing 'From' information"); |
|
1829 HBufC16* fromAddr = aImapEnvelope.EnvFrom()[0].CreateAddressStringL(); |
|
1830 CleanupStack::PushL(fromAddr); |
|
1831 aHeader->SetFromL(*fromAddr); |
|
1832 CleanupStack::PopAndDestroy(fromAddr); |
|
1833 } |
|
1834 else |
|
1835 { |
|
1836 // No From information. Set blank |
|
1837 aHeader->SetFromL(KNullDesC); |
|
1838 } |
|
1839 |
|
1840 // Sender information is ignored. |
|
1841 |
|
1842 // ReplyTo information |
|
1843 if (aImapEnvelope.EnvReplyTo().Count()!=0) |
|
1844 { |
|
1845 __LOG_TEXT(iSession->LogId(), " Processing 'ReplyTo' information"); |
|
1846 HBufC16* replyToAddr = aImapEnvelope.EnvFrom()[0].CreateAddressStringL(); |
|
1847 CleanupStack::PushL(replyToAddr); |
|
1848 aHeader->SetReplyToL(*replyToAddr); |
|
1849 CleanupStack::PopAndDestroy(replyToAddr); |
|
1850 } |
|
1851 else |
|
1852 { |
|
1853 // No replyto. Use From info |
|
1854 aHeader->SetReplyToL(aHeader->From()); |
|
1855 } |
|
1856 |
|
1857 // To information |
|
1858 ProcessAddressListL(aHeader->ToRecipients(), aImapEnvelope.EnvTo()); |
|
1859 |
|
1860 // CC list |
|
1861 ProcessAddressListL(aHeader->CcRecipients(),aImapEnvelope.EnvCc()); |
|
1862 |
|
1863 // BCC list |
|
1864 ProcessAddressListL(aHeader->BccRecipients(),aImapEnvelope.EnvBcc()); |
|
1865 |
|
1866 // Message-Id |
|
1867 aHeader->SetImMsgIdL(StripAngledBrackets(aImapEnvelope.EnvMessageId())); |
|
1868 |
|
1869 // Decode any QP encoding in header fields |
|
1870 iHeaderConverter->DecodeAllHeaderFieldsL(*aHeader); |
|
1871 |
|
1872 // Set from line in TMsvEntry |
|
1873 aEntry.iDetails.Set(aHeader->From()); |
|
1874 |
|
1875 // Set subject in TMsvEntry |
|
1876 aEntry.iDescription.Set(aHeader->Subject()); |
|
1877 |
|
1878 __LOG_TEXT(iSession->LogId(), " Finished processing envelope information"); |
|
1879 } |
|
1880 |
|
1881 |
|
1882 /** |
|
1883 Retrieves the default filename and appends the appropriate extension |
|
1884 |
|
1885 @param aWhere Destination descriptor array |
|
1886 @param aAddressArray Source address array |
|
1887 */ |
|
1888 void CImapOpFetchBody::GetDefaultFilenameL(TDes& aName, const TMsvEmailEntry& aMessage, const CImMimeHeader* mime) |
|
1889 { |
|
1890 aName = iImapSettings.DefaultAttachmentName(); |
|
1891 |
|
1892 // Add on appropriate extension |
|
1893 if (aMessage.iType == KUidMsvEmailTextEntry) |
|
1894 { |
|
1895 aName.Append(KTextExtension); |
|
1896 } |
|
1897 else if(aMessage.iType == KUidMsvEmailRtfEntry) |
|
1898 { |
|
1899 aName.Append(KRtfExtension); |
|
1900 } |
|
1901 else if (aMessage.MHTMLEmail()) |
|
1902 { |
|
1903 aName.Append(KHtmlExtension); |
|
1904 } |
|
1905 else if (aMessage.VCard() || aMessage.VCalendar()) |
|
1906 { |
|
1907 aName.Append(KVCardExtension); |
|
1908 } |
|
1909 else if (aMessage.ICalendar()) |
|
1910 { |
|
1911 aName.Append(KICalExtension); |
|
1912 } |
|
1913 else if ( aMessage.iType == KUidMsvAttachmentEntry ) |
|
1914 { |
|
1915 if ( (mime->ContentSubType()==KImcvBmp) || |
|
1916 (mime->ContentSubType()==KImcvGif) || |
|
1917 (mime->ContentSubType()==KImcvJpeg) || |
|
1918 (mime->ContentSubType()==KImcvTiff) || |
|
1919 (mime->ContentSubType()==KImcvWav) ) |
|
1920 { |
|
1921 // Copy the 8-bit ContentSubType into the 16-bit buf |
|
1922 RBuf buf; |
|
1923 buf.CleanupClosePushL(); |
|
1924 buf.CreateL(mime->ContentSubType().Length()); |
|
1925 |
|
1926 buf.Copy(mime->ContentSubType()); // 16-bit <== 8-bit |
|
1927 |
|
1928 aName.Append(KImcvFullStop); |
|
1929 aName.Append(buf); |
|
1930 |
|
1931 CleanupStack::PopAndDestroy(&buf); |
|
1932 } |
|
1933 } |
|
1934 } |
|
1935 |
|
1936 /** |
|
1937 Copies addresses from an array of addresses into a target descriptor array. |
|
1938 The source array is of the form returned by the CImapEnvelope class |
|
1939 |
|
1940 @param aWhere Destination descriptor array |
|
1941 @param aAddressArray Source address array. |
|
1942 */ |
|
1943 void CImapOpFetchBody::ProcessAddressListL(CDesCArray& aWhere, const CImapEnvelope::RArrayTAddress& aAddressArray) |
|
1944 { |
|
1945 for(TInt i=0;i < aAddressArray.Count();++i) |
|
1946 { |
|
1947 HBufC16* address = aAddressArray[i].CreateAddressStringL(); |
|
1948 CleanupStack::PushL(address); |
|
1949 aWhere.AppendL(address->Des()); |
|
1950 CleanupStack::PopAndDestroy(address); |
|
1951 } |
|
1952 } |
|
1953 |
|
1954 /** |
|
1955 Copies addresses from a Source descriptor containing one or more addresses |
|
1956 into a target descriptor array of individual addresses |
|
1957 |
|
1958 @param aWhere Destination descriptor array |
|
1959 @param aAddresses Source descriptor containing one or more addresses. |
|
1960 */ |
|
1961 void CImapOpFetchBody::ProcessAddressListL(CDesCArray& aWhere, const TDesC8& aAddresses) |
|
1962 { |
|
1963 TInt length(aAddresses.Length()); |
|
1964 HBufC8* pBuf=HBufC8::NewLC(length); |
|
1965 TPtrC8 source(aAddresses.Ptr(), length); |
|
1966 const TUint8* ptr(source.Ptr()); |
|
1967 const TUint8* lastCharPtr(ptr + source.Length() - 1); |
|
1968 TUint8 lookFor(0); |
|
1969 TInt count(0); |
|
1970 TBool finishedEntry(EFalse); |
|
1971 |
|
1972 // get past white space |
|
1973 while(*ptr&&((*ptr==KImcvSP)||(*ptr==KImcvSemiColon))) ptr++; |
|
1974 |
|
1975 // Entries are separated by commas or semicolons. |
|
1976 // Separators do not count if they appear within |
|
1977 // "", <>, () or embedded series of these, eg "(one, two)" |
|
1978 // so we need to keep track of these, including nesting. |
|
1979 while(*ptr && ptr <= lastCharPtr) |
|
1980 { |
|
1981 if(pBuf->Length()==0) |
|
1982 { |
|
1983 finishedEntry = EFalse; |
|
1984 } |
|
1985 |
|
1986 switch(*ptr) |
|
1987 { |
|
1988 case KImcvLeftBracket: |
|
1989 if(lookFor==KImcvRightBracket) |
|
1990 { // We've already had a "(", so now we need another one |
|
1991 count++; |
|
1992 } |
|
1993 else if(lookFor==0) |
|
1994 { //We weren't looking for anything else, now we need to |
|
1995 lookFor = KImcvRightBracket; |
|
1996 count = 1; |
|
1997 } |
|
1998 // else we were already looking for something else, ignore this |
|
1999 break; |
|
2000 case KImcvLeftChevron: |
|
2001 if(lookFor==KImcvRightChevron) |
|
2002 { //We've already had a "<", so now we need another one |
|
2003 count++; |
|
2004 } |
|
2005 else if(lookFor==0) |
|
2006 { //We weren't looking for anything else |
|
2007 lookFor = KImcvRightChevron; |
|
2008 count = 1; |
|
2009 } |
|
2010 // else we were already looking for something else, ignore this |
|
2011 break; |
|
2012 case KImcvDoubleQuote: |
|
2013 if(lookFor==KImcvDoubleQuote) |
|
2014 { // We already had a quote, so this matches it |
|
2015 lookFor = 0; |
|
2016 } |
|
2017 else if(lookFor==0) |
|
2018 { //We weren't looking for anything else |
|
2019 lookFor = KImcvDoubleQuote; |
|
2020 } |
|
2021 // else we were already looking for something else, ignore this |
|
2022 break; |
|
2023 case KImcvRightBracket: |
|
2024 case KImcvRightChevron: |
|
2025 if(*ptr == lookFor) |
|
2026 { //If we have found what we were looking for, decrease the count |
|
2027 count--; |
|
2028 if(count==0) |
|
2029 { // Got everything, now we're not looking for anything |
|
2030 lookFor = 0; |
|
2031 } |
|
2032 // else keep looking for the same thing again |
|
2033 } |
|
2034 // else we're looking for something else, ignore it |
|
2035 break; |
|
2036 case KImcvComma: |
|
2037 case KImcvSemiColon: |
|
2038 // If we're not looking for anything, we're finished |
|
2039 if (lookFor == 0) |
|
2040 finishedEntry = ETrue; |
|
2041 // else this comma or semicolon is part of a different token, ignore it |
|
2042 break; |
|
2043 } |
|
2044 |
|
2045 if(!finishedEntry) |
|
2046 { |
|
2047 pBuf->Des().Append((TChar)*ptr); |
|
2048 // move to the next character |
|
2049 ptr++; |
|
2050 } |
|
2051 else |
|
2052 { |
|
2053 // that's it! store the address away |
|
2054 HBufC16* pBuf16 = HBufC16::NewLC(pBuf->Des().Length()); |
|
2055 pBuf16->Des().Copy(pBuf->Des()); |
|
2056 aWhere.AppendL( (HBufC16&) *pBuf16 ); |
|
2057 CleanupStack::PopAndDestroy(pBuf16); // pBuf16 |
|
2058 pBuf->Des().SetLength(0); |
|
2059 finishedEntry = EFalse; //Ready for next entry |
|
2060 |
|
2061 // get past the separator |
|
2062 ptr++; |
|
2063 |
|
2064 // get past white space (& any other separators) |
|
2065 while(*ptr && (*ptr==KImcvSP || *ptr==KImcvTab || *ptr==KImcvComma || *ptr==KImcvSemiColon)) ptr++; |
|
2066 } |
|
2067 } |
|
2068 // catch the last name in the list |
|
2069 if (pBuf) |
|
2070 { |
|
2071 TInt recipientLength(pBuf->Length()); |
|
2072 if (recipientLength > 0) |
|
2073 { |
|
2074 HBufC16* pBuf16 = HBufC16::NewLC(recipientLength); |
|
2075 pBuf16->Des().Copy(*pBuf); |
|
2076 aWhere.AppendL(*pBuf16); |
|
2077 CleanupStack::PopAndDestroy(pBuf16); // pBuf16 |
|
2078 } |
|
2079 } |
|
2080 CleanupStack::PopAndDestroy(pBuf); // pBuf |
|
2081 } |
|
2082 |
|
2083 |
|
2084 /** |
|
2085 Searches mime information for a filename for an attachment. |
|
2086 If not found, the default attachment name is used. |
|
2087 If found but encoded according to RFC2231, the default attachment name is used |
|
2088 with the appropriate file extension appended. |
|
2089 Otherwise the found filename is QP decoded and cropped to be within max filename size. |
|
2090 |
|
2091 @param aMimeInfo The mime header associated with the attachment |
|
2092 @param aFileName Returns the filename, if found. |
|
2093 @return KErrNotFound if no filename found |
|
2094 KErrRFC2231Encoded if RFC2231 encoded filename found |
|
2095 KErrNone otherwise |
|
2096 */ |
|
2097 TInt CImapOpFetchBody::FindFilenameL(const CImMimeHeader& aMimeInfo, TPtrC8& aFilename) |
|
2098 { |
|
2099 // Look in content-type list |
|
2100 const CDesC8Array& ctype=aMimeInfo.ContentTypeParams(); |
|
2101 |
|
2102 __LOG_FORMAT((iSession->LogId(), "CImapOpFetchBody::FindFilename: Checking %d entries in content-type list", ctype.Count())); |
|
2103 |
|
2104 TInt tuple=0; |
|
2105 TInt count = ctype.Count(); |
|
2106 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 |
|
2107 { |
|
2108 |
|
2109 #ifdef PRINTING |
|
2110 TPtrC8 t1 = ctype[tuple]; |
|
2111 TPtrC8 t2 = (tuple+1 < count) ? ctype[tuple+1] : KNullDesC8(); |
|
2112 __LOG_FORMAT((iSession->LogId()," [%S] [%S]", &t1, &t2)); |
|
2113 #endif |
|
2114 |
|
2115 // Look for "name xxx" |
|
2116 if (ctype[tuple].CompareF(KMIME_NAME)==0) |
|
2117 { |
|
2118 // Got it: report that we found it |
|
2119 aFilename.Set(ctype[tuple+1]); |
|
2120 |
|
2121 // Check whether aFilename contains a meaningful file name |
|
2122 RBuf8 buf; |
|
2123 buf.CleanupClosePushL(); |
|
2124 buf.CreateL(aFilename); |
|
2125 buf.Trim(); |
|
2126 if(buf.Length()==0) |
|
2127 { |
|
2128 CleanupStack::PopAndDestroy(&buf); |
|
2129 return KErrNotFound; |
|
2130 } |
|
2131 |
|
2132 CleanupStack::PopAndDestroy(&buf); |
|
2133 return KErrNone; |
|
2134 } |
|
2135 else if (ctype[tuple].CompareF(KMIME_NAME_RFC2231)==0) |
|
2136 { |
|
2137 // Got it: report that we found it |
|
2138 aFilename.Set(ctype[tuple+1]); |
|
2139 return KErrRFC2231Encoded; |
|
2140 } |
|
2141 tuple+=2; |
|
2142 } |
|
2143 |
|
2144 // Not found in the content type, try content disposition |
|
2145 const CDesC8Array& cdisp=aMimeInfo.ContentDispositionParams(); |
|
2146 __LOG_FORMAT((iSession->LogId(), "CImapOpFetchBody::FindFilename: Checking %d entries in disposition list", cdisp.Count())); |
|
2147 |
|
2148 tuple=0; |
|
2149 count = cdisp.Count(); |
|
2150 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 |
|
2151 { |
|
2152 |
|
2153 #ifdef PRINTING |
|
2154 TPtrC8 t1 = cdisp[tuple]; |
|
2155 TPtrC8 t2 = (tuple+1 < count) ? cdisp[tuple+1] : KNullDesC8(); |
|
2156 __LOG_FORMAT((iSession->LogId(),"disp [%S] [%S]", &t1, &t2)); |
|
2157 #endif |
|
2158 |
|
2159 // Look for "filename xxx" |
|
2160 if (cdisp[tuple].CompareF(KMIME_FILENAME)==0) |
|
2161 { |
|
2162 // Got it: report that we found it |
|
2163 aFilename.Set(cdisp[tuple+1]); |
|
2164 return KErrNone; |
|
2165 } |
|
2166 else if (cdisp[tuple].CompareF(KMIME_FILENAME_RFC2231)==0) |
|
2167 { |
|
2168 // Got it: report that we found it |
|
2169 aFilename.Set(cdisp[tuple+1]); |
|
2170 return KErrRFC2231Encoded; |
|
2171 } |
|
2172 |
|
2173 tuple+=2; |
|
2174 } |
|
2175 |
|
2176 // Didn't find it |
|
2177 return KErrNotFound; |
|
2178 } |
|
2179 |
|
2180 |
|
2181 /** |
|
2182 Builds attachment filename, using data from the mime header information, if available. |
|
2183 If not found, the default attachment name is used. |
|
2184 If found but encoded according to RFC2231, the default attachment name is used |
|
2185 with the appropriate file extension appended. |
|
2186 Otherwise the found filename is QP decoded and cropped to be within max filename size. |
|
2187 |
|
2188 @param aMimeInfo The mime header associated with the attachment |
|
2189 @return aFileName The decoded filename for the attachment. |
|
2190 */ |
|
2191 void CImapOpFetchBody::FindFilenameDecodeL(const CImMimeHeader& aMimeInfo, TFileName& aFileName) |
|
2192 { |
|
2193 // Make an attachment name |
|
2194 aFileName.Zero(); |
|
2195 |
|
2196 TPtrC8 origFileName; |
|
2197 |
|
2198 // Look for filename in Content-Type list |
|
2199 TInt err = FindFilenameL(aMimeInfo, origFileName); |
|
2200 if (KErrNotFound == err) |
|
2201 { |
|
2202 // Fall back to simple "attachment" (language specific) |
|
2203 aFileName=iImapSettings.DefaultAttachmentName(); |
|
2204 } |
|
2205 else if (KErrRFC2231Encoded == err) |
|
2206 { |
|
2207 // A file name has been found but it is encoded (RFC2231) |
|
2208 // Use the default file name but append the file extension so that its type can be recognised |
|
2209 aFileName=iImapSettings.DefaultAttachmentName(); |
|
2210 TInt dotPos = origFileName.Length() - 1; |
|
2211 TBool dotFound = EFalse; |
|
2212 |
|
2213 // Find the extension |
|
2214 while ((dotPos != 0) && (!dotFound)) |
|
2215 { |
|
2216 if (origFileName[dotPos] == '.') |
|
2217 { |
|
2218 dotFound = ETrue; |
|
2219 // Extension found: append it to the filename |
|
2220 TInt extensionLength = origFileName.Length() - dotPos; |
|
2221 if ((aFileName.Length() + extensionLength) <= aFileName.MaxLength()) |
|
2222 { |
|
2223 HBufC* extension = HBufC::NewLC(extensionLength); |
|
2224 extension->Des().Copy(origFileName.Right(extensionLength)); |
|
2225 aFileName.Append(*extension); |
|
2226 CleanupStack::PopAndDestroy(extension); |
|
2227 } |
|
2228 } |
|
2229 --dotPos; |
|
2230 } // end while ((dotPos != 0) && (!dotFound)) |
|
2231 } |
|
2232 else |
|
2233 { |
|
2234 // Run it through the QP decoder |
|
2235 HBufC *decoded=HBufC::NewLC(origFileName.Length()); |
|
2236 TPtr decoded_ptr(decoded->Des()); |
|
2237 |
|
2238 // Decode filename from the header |
|
2239 iHeaderConverter->DecodeHeaderFieldL(origFileName, decoded_ptr); |
|
2240 |
|
2241 __LOG_FORMAT((iSession->LogId(), "FindFilenameDecode: '%S' to '%S' ", &origFileName, &decoded_ptr)); |
|
2242 |
|
2243 // Need to do a check on the filename length here. |
|
2244 // If it is too long, set to the max possible, keeping extension. |
|
2245 TFileName path; |
|
2246 TInt fileNameLength = path.Length() + decoded_ptr.Length(); |
|
2247 if( fileNameLength > KMaxFileName) |
|
2248 { |
|
2249 TInt prefixLen = 0; |
|
2250 // Crop the Old File Name |
|
2251 TInt lengthToCrop = (fileNameLength - KMaxFileName) + prefixLen; |
|
2252 // Use LocateReverse rather than TParsePtr as decoded_ptr may be > 256 chars |
|
2253 TInt dot = decoded_ptr.LocateReverse( '.' ); |
|
2254 TPtrC extension = decoded_ptr.Mid(dot != KErrNotFound ? dot : decoded_ptr.Length()); |
|
2255 TInt newFileNameLength = decoded_ptr.Length() - extension.Length() - lengthToCrop; |
|
2256 TPtrC newFileName=decoded_ptr.Left(newFileNameLength); |
|
2257 |
|
2258 // Create the New File Name (ie File Name & Extension) |
|
2259 aFileName.Zero(); |
|
2260 aFileName.Append(newFileName); |
|
2261 aFileName.Append(extension); |
|
2262 } |
|
2263 else |
|
2264 { |
|
2265 aFileName.Copy(decoded_ptr); |
|
2266 } |
|
2267 CleanupStack::PopAndDestroy(decoded); |
|
2268 } |
|
2269 } |
|
2270 |
|
2271 |
|
2272 /** |
|
2273 Replaces illegal characters in a filename with a default |
|
2274 @param aName The filename to check. |
|
2275 */ |
|
2276 void CImapOpFetchBody::StripIllegalCharactersFromFileName(TDes16& aName) |
|
2277 { |
|
2278 TInt length=aName.Length(); |
|
2279 for(TInt index=0; index < length; ++index) |
|
2280 { |
|
2281 TUint charr=(TUint)aName[index]; |
|
2282 if( charr == '*' || charr == '\\' || charr == '<' || charr == '>' || |
|
2283 charr == ':' || charr == '"' || charr == '/' || charr == '|' || |
|
2284 charr == '?' || charr < ' ') |
|
2285 { |
|
2286 aName[index] = KImcvDefaultChar; |
|
2287 } |
|
2288 } |
|
2289 } |
|
2290 |
|
2291 |
|
2292 /** |
|
2293 Synchronously stores IM Headers and Mime Headers |
|
2294 @param aImHeader an IM header to store. May be NULL. |
|
2295 @param aMimeHeader a Mime part header to store. May be NULL. |
|
2296 */ |
|
2297 void CImapOpFetchBody::StoreHeadersL(CImHeader* aImHeader, CImMimeHeader* aMimeHeader) |
|
2298 { |
|
2299 CMsvStore* entryStore=iServerEntry.EditStoreL(); |
|
2300 CleanupStack::PushL(entryStore); |
|
2301 if (aImHeader != NULL) |
|
2302 { |
|
2303 __LOG_FORMAT((iSession->LogId(), " Streaming ImHeader info into id %x", iServerEntry.Entry().Id())); |
|
2304 aImHeader->StoreL(*entryStore); |
|
2305 } |
|
2306 if (aMimeHeader != NULL) |
|
2307 { |
|
2308 __LOG_FORMAT((iSession->LogId(), " Streaming MIME info into id %x", iServerEntry.Entry().Id())); |
|
2309 aMimeHeader->StoreL(*entryStore); |
|
2310 } |
|
2311 entryStore->CommitL(); |
|
2312 CleanupStack::PopAndDestroy(entryStore); |
|
2313 } |
|
2314 |
|
2315 /** |
|
2316 Check if the partial download options mean that we will download a text plain |
|
2317 part, but the text HTML alternative will not be downloaded. In this case, we |
|
2318 need to make sure the footer message is displayed on the text plain part to |
|
2319 show that the text HTML part is not being downloaded. |
|
2320 */ |
|
2321 void CImapOpFetchBody::UpdateBodyTextRemainingSizeForHtml() |
|
2322 { |
|
2323 if ((iHtmlEntryPart == 0) && (iHtmlEntrySize > 0) && (iFetchList.Count() > 0)) |
|
2324 { |
|
2325 for (TInt part(0); part < iFetchList.Count(); ++part) |
|
2326 { |
|
2327 if (iFetchList[part]->IsText()) |
|
2328 { |
|
2329 iFetchList[part]->SetBodyPartRemainingSize(iFetchList[part]->BodyPartRemainingSize() + iHtmlEntrySize); |
|
2330 return; |
|
2331 } |
|
2332 } |
|
2333 } |
|
2334 } |
|
2335 |
|
2336 /** |
|
2337 Static method that strips the given string of any enclosing angled brackets < > |
|
2338 @param aString the descriptor that will have its brackets removed |
|
2339 @return a descriptor that points into aString, excluding any enclosing angled brackets |
|
2340 */ |
|
2341 TPtrC8 CImapOpFetchBody::StripAngledBrackets(const TDesC8& aString) |
|
2342 // static method |
|
2343 { |
|
2344 TInt strLen = aString.Length(); |
|
2345 |
|
2346 if (strLen>2 && aString[0]==KImcvLeftChevron && aString[strLen-1]==KImcvRightChevron) |
|
2347 { |
|
2348 return aString.Mid(1, strLen-2); |
|
2349 } |
|
2350 |
|
2351 // If the string was not enclosed with angled brackets then just return the original string. |
|
2352 return aString; |
|
2353 } |
|
2354 |
|
2355 /** |
|
2356 Creates an empty attachment to store the attachment infomation, for the case |
|
2357 where the attachment is not downloaded due to download limits. |
|
2358 @param aMsvEmailEntry The email entry the attachment is associate with. |
|
2359 */ |
|
2360 void CImapOpFetchBody::CreateAttachmentInfoL(const TMsvEntry& aMsvEmailEntry) |
|
2361 { |
|
2362 __LOG_TEXT(iSession->LogId(), "Creating zero length attachment"); |
|
2363 iMailStore.CreateAttachmentInfoL(aMsvEmailEntry.Id()); |
|
2364 } |
|
2365 |
|
2366 |
|
2367 /** |
|
2368 Called when a requsted part has been sucessfully fetched. |
|
2369 Updates flags on the TMsvEntry indicating that it is complete or |
|
2370 partially fetched as appropriate. |
|
2371 |
|
2372 Note - deletes the 0th entry from the array of parts to fetch. |
|
2373 */ |
|
2374 void CImapOpFetchBody::FetchPartCompleteL() |
|
2375 { |
|
2376 CFetchBodyInfo* fetchInfo = iFetchList[0]; |
|
2377 CleanupStack::PushL(fetchInfo); |
|
2378 iFetchList.Remove(0); |
|
2379 SetEntryL(fetchInfo->PartId()); |
|
2380 TMsvEmailEntry message = iServerEntry.Entry(); |
|
2381 message.SetComplete(ETrue); |
|
2382 |
|
2383 // Store mime information returned from N.MIME |
|
2384 |
|
2385 CImapMimeHeaderFields* headerFields = iFetchBodyResponse->HeaderFields(); |
|
2386 |
|
2387 if (headerFields != NULL) |
|
2388 { |
|
2389 CImMimeHeader* mimeHeader = CImMimeHeader::NewLC(); |
|
2390 CMsvStore* store = iServerEntry.ReadStoreL(); |
|
2391 CleanupStack::PushL(store); |
|
2392 mimeHeader->RestoreL(*store); |
|
2393 CleanupStack::PopAndDestroy(store); |
|
2394 |
|
2395 if (headerFields->FieldExists(CImapMimeHeaderFields::EImapContentBase)) |
|
2396 { |
|
2397 mimeHeader->SetContentBaseL(headerFields->FieldValue(CImapMimeHeaderFields::EImapContentBase)); |
|
2398 } |
|
2399 |
|
2400 if (headerFields->FieldExists(CImapMimeHeaderFields::EImapContentLocation)) |
|
2401 { |
|
2402 const TDesC8& fieldValue = headerFields->FieldValue(CImapMimeHeaderFields::EImapContentLocation); |
|
2403 |
|
2404 HBufC* decodedBuffer = HBufC::NewLC(fieldValue.Length()); |
|
2405 TPtr decodedPtr(decodedBuffer->Des()); |
|
2406 |
|
2407 iHeaderConverter->DecodeHeaderFieldL(fieldValue, decodedPtr); |
|
2408 mimeHeader->SetContentLocationL(decodedPtr); |
|
2409 CleanupStack::PopAndDestroy(decodedBuffer); |
|
2410 } |
|
2411 |
|
2412 StoreHeadersL(NULL, mimeHeader); |
|
2413 CleanupStack::PopAndDestroy(mimeHeader); |
|
2414 } |
|
2415 |
|
2416 TBool hasBodyText = message.iType == KUidMsvEmailTextEntry || message.iType == KUidMsvEmailHtmlEntry; |
|
2417 TBool partiallyDownloaded = EFalse; |
|
2418 |
|
2419 if(iFetchPartialMail && |
|
2420 fetchInfo->BodyPartRemainingSize() != 0 && |
|
2421 message.iType == KUidMsvEmailTextEntry) |
|
2422 { |
|
2423 message.SetPartialDownloaded(ETrue); |
|
2424 partiallyDownloaded = ETrue; |
|
2425 } |
|
2426 else |
|
2427 { |
|
2428 message.SetPartialDownloaded(EFalse); |
|
2429 } |
|
2430 |
|
2431 if (hasBodyText) |
|
2432 { |
|
2433 message.SetBodyTextComplete(ETrue); |
|
2434 } |
|
2435 |
|
2436 User::LeaveIfError(iServerEntry.ChangeEntry(message)); |
|
2437 |
|
2438 |
|
2439 // Checking the flags |
|
2440 SetEntryL(iMessageMsvId); |
|
2441 TMsvEmailEntry messageentry = iServerEntry.Entry(); |
|
2442 CImapFolder* folder = iSyncManager.GetFolderL(messageentry.Parent()); |
|
2443 |
|
2444 if(folder!=NULL) |
|
2445 { |
|
2446 TMessageFlagInfo& flaginfo = iFetchBodyResponse->MessageFlagInfo(); |
|
2447 |
|
2448 // Flags from the fetch message |
|
2449 TBool seen = flaginfo.QueryFlag(TMessageFlagInfo::ESeen); |
|
2450 TBool answered = flaginfo.QueryFlag(TMessageFlagInfo::EAnswered); |
|
2451 TBool flagged = flaginfo.QueryFlag(TMessageFlagInfo::EFlagged); |
|
2452 TBool deleted = flaginfo.QueryFlag(TMessageFlagInfo::EDeleted); |
|
2453 TBool draft = flaginfo.QueryFlag(TMessageFlagInfo::EDraft); |
|
2454 TBool recent = flaginfo.QueryFlag(TMessageFlagInfo::ERecent); |
|
2455 |
|
2456 // Flags in the local message |
|
2457 TBool oUnread, oSeen, oAnswered, oFlagged, oDeleted, oDraft, oRecent; |
|
2458 messageentry.GetIMAP4Flags(oUnread, oSeen, oAnswered, oFlagged, oDeleted, oDraft, oRecent); |
|
2459 |
|
2460 // Are we configured to update the \seen flag on the server? |
|
2461 if (iImapSettings.UpdatingSeenFlags()) |
|
2462 { |
|
2463 // Make a note to update the servers \Seen flag if CHANGED on the client |
|
2464 // and different to the servers version |
|
2465 if (BoolsAreEqual(messageentry.Unread(), seen) && BoolsAreEqual(oSeen, seen)) |
|
2466 { |
|
2467 // The read flag has changed, but not on the server. So this must be a local change. |
|
2468 if (messageentry.Unread()) |
|
2469 { |
|
2470 folder->AppendClearSeenL(messageentry.UID()); |
|
2471 } |
|
2472 else |
|
2473 { |
|
2474 folder->AppendSetSeenL(messageentry.UID()); |
|
2475 } |
|
2476 } |
|
2477 } |
|
2478 |
|
2479 if (BoolsAreNotEqual(oSeen, seen) || |
|
2480 BoolsAreNotEqual(oAnswered, answered) || |
|
2481 BoolsAreNotEqual(oFlagged, flagged) || |
|
2482 BoolsAreNotEqual(oDeleted, deleted) || |
|
2483 BoolsAreNotEqual(oDraft, draft) || |
|
2484 BoolsAreNotEqual(oRecent, recent) ) |
|
2485 { |
|
2486 messageentry.SetIMAP4Flags(oUnread, seen, (answered || oAnswered), flagged, deleted, draft, recent); |
|
2487 } |
|
2488 |
|
2489 // update context |
|
2490 SetEntryL(iMessageMsvId); |
|
2491 User::LeaveIfError(iServerEntry.ChangeEntry(messageentry)); |
|
2492 } |
|
2493 |
|
2494 |
|
2495 // Finished with the fetch body response now |
|
2496 delete iFetchBodyResponse; |
|
2497 iFetchBodyResponse = NULL; |
|
2498 |
|
2499 PropagateCompleteFlagL(fetchInfo->PartId(), hasBodyText, partiallyDownloaded); |
|
2500 |
|
2501 // increment progress counters |
|
2502 ++iPartsDone; |
|
2503 iBytesDone+=fetchInfo->BytesFetched(); |
|
2504 |
|
2505 // fetch of part complete - delete the info |
|
2506 CleanupStack::PopAndDestroy(fetchInfo); |
|
2507 } |
|
2508 |
|
2509 |
|
2510 |
|
2511 /** |
|
2512 Propagates flags indicating message completeness up the tree structure for the |
|
2513 fetched message part. Popagates the Complete and Partial-Fetch status. |
|
2514 |
|
2515 @param aId ID of the message part that has just been fetched. |
|
2516 @param aDoBodyText indicates that the part is text or contains text parts |
|
2517 @param aPartialFetched indicates that the part is partially fetched. |
|
2518 */ |
|
2519 void CImapOpFetchBody::PropagateCompleteFlagL(TMsvId aId, TBool aDoBodyText, TBool aPartialFetched) |
|
2520 { |
|
2521 CMsvEntrySelection* selection=new (ELeave) CMsvEntrySelection; |
|
2522 CleanupStack::PushL(selection); |
|
2523 |
|
2524 // get the siblings of this id |
|
2525 SetEntryL(aId); |
|
2526 TMsvId parent = iServerEntry.Entry().Parent(); |
|
2527 |
|
2528 // finish if we've managed to reach the top |
|
2529 if (parent == KMsvRootIndexEntryId) |
|
2530 return; |
|
2531 |
|
2532 SetEntryL(parent); |
|
2533 |
|
2534 // finish if we've reached a service |
|
2535 if (iServerEntry.Entry().iType == KUidMsvServiceEntry) |
|
2536 { |
|
2537 return; |
|
2538 } |
|
2539 |
|
2540 User::LeaveIfError(iServerEntry.GetChildren(*selection)); |
|
2541 |
|
2542 TBool complete=ETrue; |
|
2543 TBool bodyTextComplete=ETrue; |
|
2544 TBool partiallyFetched=EFalse; |
|
2545 |
|
2546 TBool related=((TMsvEmailEntry) iServerEntry.Entry()).MessageFolderType()==EFolderTypeRelated ? |
|
2547 ETrue:EFalse; |
|
2548 for (TInt i=0; i < selection->Count(); i++) |
|
2549 { |
|
2550 SetEntryL((*selection)[i]); |
|
2551 if (!iServerEntry.Entry().Complete()) |
|
2552 { |
|
2553 complete=EFalse; |
|
2554 if((iServerEntry.Entry().iType==KUidMsvFolderEntry) && aPartialFetched) |
|
2555 { |
|
2556 complete=ETrue; |
|
2557 } |
|
2558 // The current part is not complete so... |
|
2559 // if it is either a text part or a HTML part then the body |
|
2560 // text is marked as being incomplete. |
|
2561 // |
|
2562 // This code means that, if present, then both the text/plain |
|
2563 // and text/html alternatives need to be downloaded before |
|
2564 // the body text is marked as being complete. |
|
2565 if ((iServerEntry.Entry().iType == KUidMsvEmailTextEntry) |
|
2566 || (iServerEntry.Entry().iType == KUidMsvEmailHtmlEntry ) || related ) |
|
2567 { |
|
2568 if(aPartialFetched) |
|
2569 { |
|
2570 complete = ETrue; |
|
2571 bodyTextComplete=ETrue; |
|
2572 } |
|
2573 else |
|
2574 bodyTextComplete=EFalse; |
|
2575 } |
|
2576 |
|
2577 break; |
|
2578 } |
|
2579 } |
|
2580 |
|
2581 CleanupStack::PopAndDestroy(selection); |
|
2582 |
|
2583 // if all the siblings were complete then make the parent |
|
2584 // complete and continue up. |
|
2585 if (complete || ((aDoBodyText || related) && bodyTextComplete)) |
|
2586 { |
|
2587 SetEntryL(parent); |
|
2588 TMsvEmailEntry entry = iServerEntry.Entry(); |
|
2589 |
|
2590 // check whether parent is complete, this will prevent us |
|
2591 // checking all the messages in a real folder as they will all |
|
2592 // be initialised to Complete |
|
2593 if (!entry.Complete()) |
|
2594 { |
|
2595 if (complete || ((iServerEntry.Entry().iType==KUidMsvFolderEntry) && aPartialFetched)) |
|
2596 { |
|
2597 entry.SetComplete(ETrue); |
|
2598 } |
|
2599 |
|
2600 if(aPartialFetched) |
|
2601 { |
|
2602 if((iServerEntry.Entry().iType != KUidMsvAttachmentEntry) && |
|
2603 (iServerEntry.Entry().iType != KUidMsvEmailExternalBodyEntry)) |
|
2604 { |
|
2605 entry.SetPartialDownloaded(ETrue); |
|
2606 } |
|
2607 partiallyFetched = ETrue; |
|
2608 } |
|
2609 else |
|
2610 { |
|
2611 entry.SetPartialDownloaded(EFalse); |
|
2612 partiallyFetched = EFalse; |
|
2613 } |
|
2614 |
|
2615 entry.SetBodyTextComplete(ETrue); |
|
2616 User::LeaveIfError(iServerEntry.ChangeEntry(entry)); |
|
2617 |
|
2618 PropagateCompleteFlagL(parent, related|aDoBodyText,partiallyFetched); |
|
2619 } |
|
2620 else if (entry.PartialDownloaded()) |
|
2621 { |
|
2622 entry.SetPartialDownloaded(EFalse); |
|
2623 User::LeaveIfError(iServerEntry.ChangeEntry(entry)); |
|
2624 PropagateCompleteFlagL(parent, related|aDoBodyText,partiallyFetched); |
|
2625 } |
|
2626 } |
|
2627 } |
|
2628 |
|
2629 |
|
2630 /** |
|
2631 Populates the passed progress object with progress information on the |
|
2632 current message fetch operation. |
|
2633 @param aGenericProgressn - progress information object to be populated. |
|
2634 */ |
|
2635 void CImapOpFetchBody::Progress(TImap4GenericProgress& aGenericProgress) |
|
2636 { |
|
2637 aGenericProgress.iPartsToDo = iPartsToDo; |
|
2638 aGenericProgress.iPartsDone = iPartsDone; |
|
2639 aGenericProgress.iBytesToDo = iBytesToDo; |
|
2640 |
|
2641 // iBytesDone is the byte count of completed parts |
|
2642 // - need increase this by the parts done on the current part, |
|
2643 // if a fetch is outstanding. |
|
2644 TInt32 tempBytesDone = iBytesDone; |
|
2645 if (( iCurrentStep == EFetchFirstPart || iCurrentStep == EFetchNext) |
|
2646 && iFetchList.Count()>0) |
|
2647 { |
|
2648 tempBytesDone += (iFetchList[0])->BytesFetched(); |
|
2649 } |
|
2650 aGenericProgress.iBytesDone = tempBytesDone; |
|
2651 } |
|
2652 |
|
2653 |
|
2654 /** |
|
2655 Handles server error responses according to current step |
|
2656 |
|
2657 @return TInt error code for completion (if error fatal) |
|
2658 */ |
|
2659 TInt CImapOpFetchBody::ProcessServerError() |
|
2660 { |
|
2661 switch(iCurrentStep) |
|
2662 { |
|
2663 case EGetBodyStructure: |
|
2664 case EProcessBodyStructure: |
|
2665 case EBuildFetchList: |
|
2666 case EFetchFirstPart: |
|
2667 case EFetchNext: |
|
2668 default: |
|
2669 return (iStatus.Int()); |
|
2670 } |
|
2671 // return KErrNone; |
|
2672 } |
|
2673 |