|
1 // Copyright (c) 2006-2010 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include "cimapfetchbody.h" |
|
17 #include "moutputstream.h" |
|
18 #include "cimapsession.h" |
|
19 #include "cimapsessionconsts.h" |
|
20 #include "cimapsettings.h" |
|
21 #include "cimapmimeheaderfieldsparser.h" |
|
22 #include "cimapmimeheaderfields.h" |
|
23 #include "cfetchbodyinfo.h" |
|
24 #include "cimapfetchbodyresponse.h" |
|
25 #include "cimaplogger.h" |
|
26 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
27 #include "cimapcapabilityinfo.h" |
|
28 #endif |
|
29 |
|
30 const TInt KMaxTagIdSize = 8; |
|
31 const TInt KDefaultMaxFetchSize = 20480; |
|
32 const TInt KOutstandingRequests = 2; |
|
33 const TInt KOutstandingBinaryFetchRequests = 1; |
|
34 |
|
35 _LIT8(KCommandFetch, "%S UID FETCH %d (BODY[%S]<%d.%d>)\r\n"); |
|
36 _LIT8(KCommandFetchWithMime, "%S UID FETCH %d (BODY[%S]<%d.%d> BODY[%S.MIME])\r\n"); |
|
37 _LIT8(KCommandFetchPeek, "%S UID FETCH %d (BODY.PEEK[%S]<%d.%d>)\r\n"); |
|
38 _LIT8(KCommandFetchPeekWithMime, "%S UID FETCH %d (BODY.PEEK[%S]<%d.%d> BODY.PEEK[%S.MIME])\r\n"); |
|
39 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
40 _LIT8(KCommandFetchBinary, "%S UID FETCH %d (BINARY[%S]<%d.%d>)\r\n"); |
|
41 _LIT8(KCommandFetchBinaryPeek, "%S UID FETCH %d (BINARY.PEEK[%S]<%d.%d>)\r\n"); |
|
42 #endif |
|
43 _LIT8(KStartSection,"["); |
|
44 _LIT8(KEndSection,"]"); |
|
45 _LIT8(KStartLiteral,"{"); |
|
46 _LIT8(KEndLiteral,"}"); |
|
47 _LIT8(KStartOrigin,"<"); |
|
48 _LIT8(KEndOrigin,">"); |
|
49 |
|
50 CImapFetchBody* CImapFetchBody::NewL(CImapFolderInfo* aSelectedFolderData, TInt aLogId, TUint aMessageUid,TBool aPeek, CFetchBodyInfo& aFetchBodyInfo, CImapFetchBodyResponse& aFetchBodyResponse, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, CImapSession& aParent) |
|
51 { |
|
52 CImapFetchBody* self = new (ELeave) CImapFetchBody(aSelectedFolderData, aLogId, aMessageUid, aPeek, aFetchBodyInfo, aFetchBodyResponse, aImapSettings, aImapMailStore, aParent); |
|
53 CleanupStack::PushL(self); |
|
54 self->ConstructL(); |
|
55 CleanupStack::Pop(self); |
|
56 return self; |
|
57 } |
|
58 |
|
59 CImapFetchBody::CImapFetchBody(CImapFolderInfo* aSelectedFolderData, TInt aLogId, TUint aMessageUid, TBool aPeek, CFetchBodyInfo& aFetchBodyInfo, CImapFetchBodyResponse& aFetchBodyResponse, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, CImapSession& aParent) |
|
60 : CImapCommand(aSelectedFolderData, aLogId), |
|
61 iMessageUid(aMessageUid), |
|
62 iPeek(aPeek), |
|
63 iSizeToFetch(aFetchBodyInfo.SizeToFetch()), |
|
64 iImapSettings(aImapSettings), |
|
65 iFetchBodyInfo(aFetchBodyInfo), |
|
66 iImapMailStore(aImapMailStore), |
|
67 iParent(aParent), |
|
68 iFetchBodyResponse(aFetchBodyResponse), |
|
69 iSendFetch(ETrue) |
|
70 { |
|
71 iPartialFetch=iFetchBodyInfo.PartialDownload(); |
|
72 } |
|
73 |
|
74 CImapFetchBody::~CImapFetchBody() |
|
75 { |
|
76 // ensure that iImapMailStore will not call StoreOperationComplete() |
|
77 // on this object now that it is being destoyed. |
|
78 |
|
79 delete iHeaderFieldsParser; |
|
80 delete iBuf; |
|
81 iTagIds.Reset(); |
|
82 } |
|
83 |
|
84 /** |
|
85 Overrides CImapCommand::Cancel() by cancelling any outstanding mail store operation. |
|
86 */ |
|
87 void CImapFetchBody::Cancel() |
|
88 { |
|
89 __LOG_TEXT(iLogId, "CImapFetchBody::Cancel()"); // Overrides CImapCommand::Cancel() |
|
90 |
|
91 iImapMailStore.CancelRequest(*this); |
|
92 CImapCommand::Cancel(); |
|
93 } |
|
94 |
|
95 void CImapFetchBody::StoreOperationComplete(TMsvId /*aId*/,TInt aErrorCode) |
|
96 { |
|
97 __LOG_FORMAT((iLogId, "CImapFetchBody::StoreOperationComplete aErrorCode = %d",aErrorCode)); |
|
98 iStoreComplete = ETrue; |
|
99 |
|
100 // Complete early if there is an error |
|
101 if (aErrorCode != KErrNone) |
|
102 { |
|
103 __LOG_TEXT(iLogId, "CImapFetchBody::StoreOperationComplete - ERROR: Completing Early"); |
|
104 iParent.FetchBodyOperationComplete(aErrorCode); |
|
105 } |
|
106 // Otherwise complete only if the last tagged OK has been received. |
|
107 else if(ParseState() == ECommandComplete) |
|
108 { |
|
109 // Call the session to let it know we are done |
|
110 __ASSERT_DEBUG(iRequestCount == iTotalRequests && iOutstandingRequests == 0, TImapServerPanic::ImapPanic(TImapServerPanic::EStoreOperationCompleteWithPendingRequest) ); |
|
111 iParent.FetchBodyOperationComplete(aErrorCode); |
|
112 } |
|
113 } |
|
114 |
|
115 TBool CImapFetchBody::IsStoreOperationComplete() |
|
116 { |
|
117 return iStoreComplete; |
|
118 } |
|
119 |
|
120 void CImapFetchBody::ConstructL() |
|
121 { |
|
122 // obtain |
|
123 iImapSettings.GetTransportBufferSizesL(iMaxFetchSize, iMaxOutstandingRequests); |
|
124 |
|
125 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
126 // check for BINARY capability |
|
127 const CImapCapabilityInfo& capabilityInfo = iParent.CapabilityInfo(); |
|
128 TBool binaryCapExist = capabilityInfo.QueryFlag(CImapCapabilityInfo::EBinaryCap); |
|
129 #endif |
|
130 |
|
131 // if either parameter is undefined, resort to using the defaults. |
|
132 if(iMaxFetchSize==0) |
|
133 { |
|
134 iMaxFetchSize=KDefaultMaxFetchSize; |
|
135 } |
|
136 |
|
137 if(iMaxOutstandingRequests==0) |
|
138 { |
|
139 iMaxOutstandingRequests=KOutstandingRequests; |
|
140 } |
|
141 |
|
142 //calculate the number of chunks that will be needed |
|
143 iTotalRequests=TotalRequestsRequired(iSizeToFetch); |
|
144 |
|
145 __LOG_FORMAT((iLogId, "CImapFetchBody::CImapFetchBody iTotalRequests = %d",iTotalRequests)); |
|
146 |
|
147 |
|
148 if(iFetchBodyInfo.IsText()) |
|
149 { |
|
150 // check if chunk storage mechanism is enabled. |
|
151 if(iImapSettings.StorePlainText()) |
|
152 { |
|
153 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
154 if(binaryCapExist && iFetchBodyInfo.IsText()) |
|
155 { |
|
156 iImapMailStore.InitialiseStorePlainBodyTextL(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this,*this, ETrue); |
|
157 } |
|
158 else |
|
159 #endif |
|
160 { |
|
161 iImapMailStore.InitialiseStorePlainBodyTextL(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this,*this); |
|
162 } |
|
163 } |
|
164 else |
|
165 { |
|
166 if(iImapSettings.Store8BitData()) |
|
167 { |
|
168 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
169 if(binaryCapExist && iFetchBodyInfo.IsText()) |
|
170 { |
|
171 iImapMailStore.InitialiseStoreBody8L(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this,ETrue); |
|
172 } |
|
173 else |
|
174 #endif |
|
175 { |
|
176 iImapMailStore.InitialiseStoreBody8L(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this); |
|
177 } |
|
178 } |
|
179 else |
|
180 { |
|
181 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
182 if(binaryCapExist && iFetchBodyInfo.IsText()) |
|
183 { |
|
184 iImapMailStore.InitialiseStoreBody16L(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this,ETrue); |
|
185 } |
|
186 else |
|
187 #endif |
|
188 { |
|
189 iImapMailStore.InitialiseStoreBody16L(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this); |
|
190 } |
|
191 } |
|
192 } |
|
193 } |
|
194 else |
|
195 { |
|
196 iImapMailStore.InitialiseStoreAttachmentL(iTotalRequests,iImapSettings,iFetchBodyInfo,iLogId,*this); |
|
197 } |
|
198 } |
|
199 |
|
200 TInt CImapFetchBody::TotalRequestsRequired(TInt aSize) |
|
201 { |
|
202 |
|
203 TInt chunkNumber = aSize / iMaxFetchSize; |
|
204 |
|
205 if( (aSize % iMaxFetchSize)>0) |
|
206 { |
|
207 //add a chunk for the last bit of data |
|
208 ++chunkNumber; |
|
209 } |
|
210 |
|
211 return chunkNumber; |
|
212 } |
|
213 |
|
214 |
|
215 TInt CImapFetchBody::CalculateChunk(TInt aPos) |
|
216 { |
|
217 return aPos / iMaxFetchSize; |
|
218 } |
|
219 |
|
220 /** |
|
221 Formats and sends the IMAP UID FETCH command. |
|
222 @param aTagId Command sequence id which will be send along with the IMAP command. |
|
223 @param aStream A wrapper for the outbound stream of a connected socket, using which |
|
224 the command will be send to the server |
|
225 */ |
|
226 void CImapFetchBody::SendMessageL(TInt aTagId, MOutputStream& aStream) |
|
227 { |
|
228 iOutStream=&aStream; |
|
229 |
|
230 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
231 // check for BINARY capability |
|
232 const CImapCapabilityInfo& capabilityInfo = iParent.CapabilityInfo(); |
|
233 TBool binaryCapExist = capabilityInfo.QueryFlag(CImapCapabilityInfo::EBinaryCap); |
|
234 if(binaryCapExist && iFetchBodyInfo.IsText()) |
|
235 { |
|
236 // if message body part is downloaded using FETCH BINARY, then there should be only one |
|
237 // Outstanding Request |
|
238 iOutstandingRequests = KOutstandingBinaryFetchRequests; |
|
239 } |
|
240 else |
|
241 #endif |
|
242 { |
|
243 //iOutstandingRequests is the smaller of iMaxOutstandingRequests and the total chunk count |
|
244 iOutstandingRequests = iTotalRequests>iMaxOutstandingRequests ? iMaxOutstandingRequests : iTotalRequests; |
|
245 } |
|
246 // SendMessageL will increment the tag ID as the first thing it does, so we |
|
247 // should set it to 1 lower than the fist tag we want to send. |
|
248 iTagId = aTagId - 1; |
|
249 |
|
250 SendMessageL(); |
|
251 } |
|
252 |
|
253 void CImapFetchBody::SendMessageL() |
|
254 { |
|
255 // Set the tag ID to use in the next message |
|
256 ++iTagId; |
|
257 |
|
258 _LIT8(KTagFormatId, "%d"); |
|
259 TBuf8<KMaxTagIdSize>tagIdBuffer; |
|
260 tagIdBuffer.Format(KTagFormatId,iTagId); |
|
261 |
|
262 iTagIds.InsertInOrder(iTagId); |
|
263 |
|
264 |
|
265 //create fetch command based on settings |
|
266 //the offset from which we want to fetch data |
|
267 TInt offset = iRequestCount*iMaxFetchSize; |
|
268 |
|
269 // calclulate the size to fetch in this request, |
|
270 // default to max fetch size. |
|
271 TUint sizeToFetch = iMaxFetchSize; |
|
272 |
|
273 if(iPartialFetch) |
|
274 { |
|
275 if ((iRequestCount == (iTotalRequests-1)) && (iSizeToFetch<iMaxFetchSize)) |
|
276 sizeToFetch = iSizeToFetch; |
|
277 } |
|
278 |
|
279 TInt bufLength = 0; |
|
280 bufLength += iFetchBodyInfo.RelativePath()->Length(); |
|
281 bufLength += TagLength(offset); |
|
282 bufLength += TagLength(sizeToFetch); |
|
283 bufLength += tagIdBuffer.Length(); |
|
284 |
|
285 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
286 // check for BINARY capability |
|
287 // Issue binary fetch for plain/text part only |
|
288 const CImapCapabilityInfo& capabilityInfo = iParent.CapabilityInfo(); |
|
289 TBool binaryCapExist = capabilityInfo.QueryFlag(CImapCapabilityInfo::EBinaryCap); |
|
290 #endif |
|
291 if (iRequestCount == 0 && !iFetchBodyInfo.iEmbed) |
|
292 { |
|
293 bufLength += iFetchBodyInfo.RelativePath()->Length(); |
|
294 |
|
295 if (iPeek) |
|
296 { |
|
297 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
298 if(binaryCapExist && iFetchBodyInfo.IsText()) |
|
299 { |
|
300 bufLength += KCommandFetchBinaryPeek().Length(); |
|
301 } |
|
302 else |
|
303 #endif |
|
304 { |
|
305 bufLength += KCommandFetchPeekWithMime().Length(); |
|
306 } |
|
307 } |
|
308 else |
|
309 { |
|
310 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
311 if(binaryCapExist && iFetchBodyInfo.IsText()) |
|
312 { |
|
313 bufLength += KCommandFetchBinary().Length(); |
|
314 } |
|
315 else |
|
316 #endif |
|
317 { |
|
318 bufLength += KCommandFetchWithMime().Length(); |
|
319 } |
|
320 } |
|
321 } |
|
322 else |
|
323 { |
|
324 if(iPeek) |
|
325 { |
|
326 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
327 if(binaryCapExist && iFetchBodyInfo.IsText()) |
|
328 { |
|
329 bufLength += KCommandFetchBinaryPeek().Length(); |
|
330 } |
|
331 else |
|
332 #endif |
|
333 { |
|
334 bufLength += KCommandFetchPeek().Length(); |
|
335 } |
|
336 } |
|
337 else |
|
338 { |
|
339 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
340 if(binaryCapExist && iFetchBodyInfo.IsText()) |
|
341 { |
|
342 bufLength += KCommandFetchBinary().Length(); |
|
343 } |
|
344 else |
|
345 #endif |
|
346 { |
|
347 bufLength += KCommandFetch().Length(); |
|
348 } |
|
349 } |
|
350 } |
|
351 |
|
352 //now create the command |
|
353 HBufC8* buffer = HBufC8::NewL(bufLength); |
|
354 delete iOutputBuffer; |
|
355 iOutputBuffer=buffer; |
|
356 |
|
357 if (iRequestCount == 0 && !iFetchBodyInfo.iEmbed) |
|
358 { |
|
359 if(iPeek) |
|
360 { |
|
361 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
362 if(binaryCapExist && iFetchBodyInfo.IsText()) |
|
363 { |
|
364 iOutputBuffer->Des().Format(KCommandFetchBinaryPeek, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, sizeToFetch, iFetchBodyInfo.RelativePath()); |
|
365 } |
|
366 else |
|
367 #endif |
|
368 { |
|
369 iOutputBuffer->Des().Format(KCommandFetchPeekWithMime, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, sizeToFetch, iFetchBodyInfo.RelativePath()); |
|
370 } |
|
371 } |
|
372 else |
|
373 { |
|
374 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
375 if(binaryCapExist && iFetchBodyInfo.IsText()) |
|
376 { |
|
377 iOutputBuffer->Des().Format(KCommandFetchBinary, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, sizeToFetch, iFetchBodyInfo.RelativePath()); |
|
378 } |
|
379 else |
|
380 #endif |
|
381 { |
|
382 iOutputBuffer->Des().Format(KCommandFetchWithMime, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, sizeToFetch, iFetchBodyInfo.RelativePath()); |
|
383 } |
|
384 } |
|
385 } |
|
386 else |
|
387 { |
|
388 iFetchBodyInfo.iEmbed = EFalse; |
|
389 if(iPeek) |
|
390 { |
|
391 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
392 if(binaryCapExist && iFetchBodyInfo.IsText()) |
|
393 { |
|
394 iOutputBuffer->Des().Format(KCommandFetchBinaryPeek, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, sizeToFetch); |
|
395 } |
|
396 else |
|
397 #endif |
|
398 { |
|
399 iOutputBuffer->Des().Format(KCommandFetchPeek, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, sizeToFetch); |
|
400 } |
|
401 } |
|
402 else |
|
403 { |
|
404 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
405 if(binaryCapExist && iFetchBodyInfo.IsText()) |
|
406 { |
|
407 iOutputBuffer->Des().Format(KCommandFetchBinary, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, iMaxFetchSize); |
|
408 } |
|
409 else |
|
410 #endif |
|
411 { |
|
412 iOutputBuffer->Des().Format(KCommandFetch, &tagIdBuffer, iMessageUid, iFetchBodyInfo.RelativePath(), offset, iMaxFetchSize); |
|
413 } |
|
414 } |
|
415 } |
|
416 |
|
417 delete iBuf; |
|
418 iBuf = NULL; |
|
419 iReceivedMimeHeaders = EFalse; |
|
420 |
|
421 //send the command to the server |
|
422 iOutStream->SendDataReq(*iOutputBuffer); |
|
423 ++iRequestCount; |
|
424 } |
|
425 |
|
426 /** |
|
427 @param aData Will contain a single line of response from the server for LOGIN command without \r\n. |
|
428 @return will be any one of this |
|
429 1) If the next expected chunk is a literal block, ParseMessageL() will return the size of the block it expects. |
|
430 2) If the next expected chunk is a line, ParseMessageL() will return 0, and Result() will return EImapResponseNone. |
|
431 3) If no further data is expected (e.g. the OK or error tag has been received) then ParseMessageL() will return 0, |
|
432 and Result() will return one of EImapResponseOk, EImapResponseNo or EImapResponseBad. |
|
433 */ |
|
434 CImapCommand::TParseBlockResult CImapFetchBody::DoParseBlockL(const TDesC8& aData) |
|
435 { |
|
436 |
|
437 CImapCommand::TParseBlockResult resultCode(ECompleteUntagged); |
|
438 |
|
439 switch (iState) |
|
440 { |
|
441 case EStateDataItemLine: |
|
442 { |
|
443 // We are the beginning of a new response, so we can't have found any UID data items yet. |
|
444 // So we need to reset the flag here. |
|
445 iUidDataItemFoundInResponse = EFalse; |
|
446 resultCode = ProcessStartL(); |
|
447 |
|
448 ASSERT(iState == EStateDataItemLine); |
|
449 |
|
450 // If we get EResponseIncomplete then allow the current EStateDataItemLine state to |
|
451 // drop through to ProcessDataItems() |
|
452 // otherwise, EStateComplete will take us straight to the return. |
|
453 if (resultCode != EResponseIncomplete) |
|
454 { |
|
455 iState = EStateComplete; |
|
456 } |
|
457 } |
|
458 break; |
|
459 case EStateBodyLiteral: |
|
460 { |
|
461 // Bump progress: bytesdone is *encoded* length, so we just use the encoded length |
|
462 iFetchBodyInfo.IncrementBytesFetched(aData.Length()); |
|
463 |
|
464 __ASSERT_DEBUG(aData.Length() == iLiteralSize, TImapServerPanic::ImapPanic(TImapServerPanic::ETParseBlockResultInvalidLiteralSize) ); |
|
465 |
|
466 ProcessBodyLiteralL(aData); |
|
467 resultCode = EResponseIncomplete; // always expect more data after a literal |
|
468 } |
|
469 break; |
|
470 case EStateMime: |
|
471 { |
|
472 ProcessRestOfMimeL(iUnparsedData); |
|
473 resultCode = EResponseIncomplete; |
|
474 } |
|
475 break; |
|
476 case EStateFetchNextDataItemLine: |
|
477 { |
|
478 // Fetch is over. Get ready to process next data item. |
|
479 iUnparsedData.Set(aData); |
|
480 GetAndStoreNextPart(); |
|
481 |
|
482 iState = EStateDataItemLine; |
|
483 } |
|
484 break; |
|
485 default: |
|
486 { |
|
487 // unexpected state |
|
488 ASSERT(EFalse); |
|
489 } |
|
490 } |
|
491 |
|
492 // The ProcessXxxL() methods above can change the state. |
|
493 // Now we need to check if there are data items to process... |
|
494 if (iState == EStateDataItemLine) |
|
495 { |
|
496 resultCode = ProcessDataItemsL(); |
|
497 } |
|
498 else if (iState == EStateComplete) |
|
499 { |
|
500 //if we still have more requests to issue send the next one now |
|
501 |
|
502 if(resultCode == ECompleteTagged) |
|
503 { |
|
504 if (iResponseCode == EImapResponseOk) |
|
505 { |
|
506 ++iResponseCount; |
|
507 // If we received some MIME headers, we may need to store them |
|
508 // with the CAF framework |
|
509 if ((iReceivedMimeHeaders) && (iFetchBodyInfo.Caf() != NULL) && (iFetchBodyInfo.Caf()->Registered())) |
|
510 { |
|
511 WriteMimeHeadersToCafL(); |
|
512 } |
|
513 |
|
514 // Store the body data if we received it |
|
515 if (iBuf != NULL) |
|
516 { |
|
517 TInt extraFetchRequestCount = 0; |
|
518 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
519 // check for BINARY capability |
|
520 const CImapCapabilityInfo& capabilityInfo = iParent.CapabilityInfo(); |
|
521 TBool binaryCapExist = capabilityInfo.QueryFlag(CImapCapabilityInfo::EBinaryCap); |
|
522 if(binaryCapExist && iFetchBodyInfo.IsText() && iPreviousLiteralBytesReceived < KDefaultMaxFetchSize) |
|
523 { |
|
524 extraFetchRequestCount = iTotalRequests - iRequestCount; |
|
525 // iTotalRequests, iRequestCount and iResponseCount should be same at this stage |
|
526 // iResponseCount will be same as iRequestCount |
|
527 iTotalRequests=iRequestCount; |
|
528 //reset iPreviousLiteralBytesReceived to zero |
|
529 iPreviousLiteralBytesReceived=0; |
|
530 } |
|
531 #endif |
|
532 |
|
533 StoreBodyDataL(extraFetchRequestCount); |
|
534 |
|
535 if (iRequestCount<iTotalRequests) |
|
536 { |
|
537 // if there are outstanding requests already then just add this request to the count |
|
538 ++iOutstandingRequests; |
|
539 |
|
540 // If iOutstandingRequests is greater than one, this means a write operation is in progress |
|
541 // and the message will be sent when SendDataCnf() is called. |
|
542 if (iSendFetch && (iOutstandingRequests == 1)) |
|
543 { |
|
544 SendMessageL(); |
|
545 } |
|
546 |
|
547 resultCode=ECompleteUntagged; |
|
548 iResponseCode=EImapResponseNone; |
|
549 } |
|
550 // if there are remaining requests yet to be received then tell the session were not finished. |
|
551 else if(iResponseCount<iTotalRequests) |
|
552 { |
|
553 resultCode = ECompleteUntagged; |
|
554 iResponseCode=EImapResponseNone; |
|
555 } |
|
556 } |
|
557 // if there is no body part to be stored but server sent a OK responsethen set |
|
558 // iStoreComplete to ETrue and Cancel the Request.If more than one FETCH was sent |
|
559 // then Flush the state so that all incoming server data is discarded. |
|
560 else |
|
561 { |
|
562 iStoreComplete = ETrue; |
|
563 iImapMailStore.CancelRequest(*this); |
|
564 |
|
565 // Check the tag id |
|
566 if(iTagIds.Count()) |
|
567 { |
|
568 EnterFlushingState(); |
|
569 resultCode = ECompleteUntagged; |
|
570 iResponseCode = EImapResponseNone; |
|
571 } |
|
572 } |
|
573 } |
|
574 else |
|
575 { |
|
576 iImapMailStore.CancelRequest(*this); |
|
577 } |
|
578 } |
|
579 |
|
580 // this response is complete, so the next data (if any) will be a data item line, |
|
581 iState = EStateDataItemLine; |
|
582 } |
|
583 |
|
584 // For complete untagged responses, check whether the UID data item was received. |
|
585 // If it was, then this was a genuine response to the UID FETCH command. |
|
586 // If it was not received, then this was an unsolicited server event, and the data should be discarded. |
|
587 if (resultCode == ECompleteUntagged) |
|
588 { |
|
589 if (iUidDataItemFoundInResponse) |
|
590 { |
|
591 // Genuine UID FETCH response - copy UID and FLAG info into the response object |
|
592 iFetchBodyResponse.SetMessageFlagInfo(iMessageFlagInfo); |
|
593 |
|
594 // Note: iUidDataItemFoundInResponse is NOT reset here |
|
595 // Instead iUidDataItemFoundInResponse is set to EFalse just prior to calling ProcessStartL() - i.e. at the start of a new response. |
|
596 } |
|
597 else |
|
598 { |
|
599 // Record that an unsolicited FETCH was received - so that a sync will be triggered after this command. |
|
600 __LOG_TEXT(iLogId, "CImapFetchBody::DoParseBlockL() - Found unsolicited FETCH FLAGS"); |
|
601 SetMessageFlagsChanged(); |
|
602 } |
|
603 } |
|
604 |
|
605 return resultCode; |
|
606 } |
|
607 |
|
608 /** |
|
609 Returns the number of tagged responses that are currently expected. |
|
610 Because CImapFetchBody uses pipelining - with many simulataneous request running at once |
|
611 it will be expect a tagged response for each request that is still running on the server. |
|
612 @return the number of tagged responses that are currently expected. |
|
613 */ |
|
614 TInt CImapFetchBody::NumberOfTaggedResponsesExpected() const |
|
615 { |
|
616 return iTagIds.Count(); |
|
617 } |
|
618 |
|
619 CImapCommand::TParseBlockResult CImapFetchBody::ProcessStartL() |
|
620 { |
|
621 TParseBlockResult result = ENotRecognised; |
|
622 |
|
623 TInt tagId = 0; |
|
624 TTagType tagged = GetTagTypeL(tagId); |
|
625 switch(tagged) |
|
626 { |
|
627 case ETagged: |
|
628 { |
|
629 // Check the tag id |
|
630 TInt tagPos=iTagIds.FindInOrder(tagId); |
|
631 if(tagPos!=KErrNotFound) |
|
632 { |
|
633 iTagIds.Remove(tagPos); |
|
634 } |
|
635 else |
|
636 { |
|
637 //Unexpected Tagid |
|
638 CorruptDataL(); |
|
639 } |
|
640 |
|
641 // Fetch and check the response code |
|
642 iResponseCode = GetResponseStateCode(); |
|
643 if (iResponseCode == EImapResponseNone) |
|
644 { |
|
645 // Was expecting one of OK/NO/BAD, but didn't get it. This is a parse error. |
|
646 CorruptDataL(); |
|
647 } |
|
648 |
|
649 result =ECompleteTagged; |
|
650 } |
|
651 break; |
|
652 |
|
653 case EUntagged: |
|
654 { |
|
655 // Is this a FETCH response? |
|
656 // Check for Sequence Number followed by "FETCH" |
|
657 |
|
658 TPtrC8 part1 = GetNextPart(); // returns KNullDesC8 if there is no part available |
|
659 TPtrC8 part2 = GetNextPart(); // returns KNullDesC8 if there is no part available |
|
660 |
|
661 // Is part1 a Sequence Number? |
|
662 TInt sequenceNumber = 0; |
|
663 TLex8 lex(part1); |
|
664 if (lex.Val(sequenceNumber) == KErrNone) |
|
665 { |
|
666 // part1 is a Sequence Number. Now check part2 - is it "FETCH"? |
|
667 |
|
668 if(part2.CompareF(KImapTxtFetch) == 0) |
|
669 { |
|
670 |
|
671 if (GetAndStoreNextPart()) |
|
672 { |
|
673 if (iCurrentPart[0] == '(') |
|
674 { |
|
675 iCurrentPart.Set(iCurrentPart.Mid(1)); |
|
676 } |
|
677 else |
|
678 { |
|
679 // was expecting a bracket, got something else |
|
680 CorruptDataL(); |
|
681 } |
|
682 } |
|
683 else |
|
684 { |
|
685 // was expecting a bracket, got nothing |
|
686 CorruptDataL(); |
|
687 } |
|
688 |
|
689 result = EResponseIncomplete; |
|
690 } |
|
691 } |
|
692 } |
|
693 break; |
|
694 case EContinuation: |
|
695 default: |
|
696 { |
|
697 CorruptDataL(); |
|
698 } |
|
699 break; |
|
700 } |
|
701 |
|
702 // result will be ENotRecognised if tagged not found or untagged FETCH not found. |
|
703 return result; |
|
704 } |
|
705 |
|
706 CImapCommand::TParseBlockResult CImapFetchBody::ProcessDataItemsL() |
|
707 { |
|
708 CImapCommand::TParseBlockResult resultCode = EResponseIncomplete; |
|
709 |
|
710 TBool foundPart = ETrue; |
|
711 while (iState == EStateDataItemLine && foundPart) |
|
712 { |
|
713 if (iCurrentPart.CompareF(KImapTxtUid) == 0) |
|
714 { |
|
715 ProcessUidL(); |
|
716 } |
|
717 else if (iCurrentPart.CompareF(KImapTxtFlags) == 0) |
|
718 { |
|
719 ProcessFlagsL(); |
|
720 } |
|
721 |
|
722 // check if the part starts 'BODY[' |
|
723 else if (iCurrentPart.Find(KImapTxtBody)==0) |
|
724 { |
|
725 //is it the body or the body.mime? |
|
726 if(iCurrentPart.Find(KImapTxtMime) != KErrNotFound ) |
|
727 { |
|
728 ProcessStartOfMimeL(); |
|
729 } |
|
730 else |
|
731 { |
|
732 ProcessBodyL(); |
|
733 } |
|
734 } |
|
735 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
736 // check if the part starts 'BINARY[' |
|
737 else if (iCurrentPart.Find(KImapTxtBinary)==0) |
|
738 { |
|
739 // no mime to process for fetch binary |
|
740 ProcessBodyL(); |
|
741 } |
|
742 #endif |
|
743 |
|
744 // Only fetch the next part if we're still searching for data items. |
|
745 if (iState == EStateDataItemLine) |
|
746 { |
|
747 foundPart = GetAndStoreNextPart(); |
|
748 } |
|
749 } |
|
750 |
|
751 if (!foundPart && iState == EStateDataItemLine) |
|
752 { |
|
753 if(iBuf && iUnexpectedFormat) |
|
754 { |
|
755 iState = EStateFetchNextDataItemLine; |
|
756 iUnexpectedFormat = EFalse; |
|
757 } |
|
758 resultCode = ECompleteUntagged; |
|
759 } |
|
760 iUnexpectedFormat = EFalse; |
|
761 return resultCode; |
|
762 } |
|
763 |
|
764 |
|
765 void CImapFetchBody::ProcessStartOfMimeL() |
|
766 { |
|
767 //look for the body section that is being returned |
|
768 TInt secStart=iCurrentPart.Find(KStartSection); |
|
769 TInt secEnd=iCurrentPart.Find(KImapTxtMime); |
|
770 |
|
771 if(secStart==KErrNotFound || secEnd==KErrNotFound) |
|
772 { |
|
773 CorruptDataL(); |
|
774 } |
|
775 |
|
776 TPtrC8 section = iCurrentPart.Mid(secStart + 1, secEnd - secStart - 1); |
|
777 |
|
778 //check the section is what we asked for |
|
779 if(section.CompareF(*iFetchBodyInfo.RelativePath()) != 0) |
|
780 { |
|
781 CorruptDataL(); |
|
782 } |
|
783 |
|
784 // Peek the next part. We don't want to consume it as we may need |
|
785 // to pass it to the header fields parser. |
|
786 iCurrentPart.Set(PeekNextPart()); |
|
787 if (iCurrentPart.Length() == 0) |
|
788 { |
|
789 CorruptDataL(); |
|
790 } |
|
791 |
|
792 iReceivedMimeHeaders = ETrue; |
|
793 |
|
794 // If the last character is ')' then we're at the last data item in the list. |
|
795 // Consume the character so that the rest of the data item can be interpreted. |
|
796 if (iCurrentPart.Right(1).CompareF(KImapTxtCloseBracket) == 0) |
|
797 { |
|
798 iCurrentPart.Set(iCurrentPart.Left(iCurrentPart.Length() - 1)); |
|
799 } |
|
800 |
|
801 // Check if data part is NIL or "" for empty string |
|
802 if (iCurrentPart.CompareF(KImapTxtNil) == 0 || iCurrentPart.CompareF(KImapTxtEmptyStringAsDoubleQuotePair) == 0) |
|
803 { |
|
804 // Consume the NIL part |
|
805 GetAndStoreNextPart(); |
|
806 |
|
807 // Create empty MIME header fields |
|
808 CImapMimeHeaderFields* fields = CImapMimeHeaderFields::NewL(); |
|
809 iFetchBodyResponse.SetHeaderFields(fields); |
|
810 |
|
811 // May be more data items coming up |
|
812 iState = EStateDataItemLine; |
|
813 } |
|
814 else |
|
815 { |
|
816 // Pass the rest of the line to the header fields parser |
|
817 iHeaderFieldsParser = CImapMimeHeaderFieldsParser::NewL(iFetchBodyResponse, iLogId); |
|
818 iState = EStateMime; |
|
819 ProcessRestOfMimeL(iUnparsedData); |
|
820 } |
|
821 } |
|
822 |
|
823 void CImapFetchBody::ProcessRestOfMimeL(const TDesC8& aData) |
|
824 { |
|
825 TBool wantsMore = iHeaderFieldsParser->ProcessBlockL(aData); |
|
826 |
|
827 if (!wantsMore) |
|
828 { |
|
829 delete iHeaderFieldsParser; |
|
830 iHeaderFieldsParser = NULL; |
|
831 iState = EStateFetchNextDataItemLine; |
|
832 } |
|
833 } |
|
834 |
|
835 void CImapFetchBody::ProcessBodyL() |
|
836 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
837 {//BODY[1]<0> or BINARY[1]<0> |
|
838 #else |
|
839 {//BODY[1]<0> |
|
840 #endif |
|
841 //look for the body section that is being returned |
|
842 TInt secStart=iCurrentPart.Find(KStartSection); |
|
843 TInt secEnd=iCurrentPart.Find(KEndSection); |
|
844 |
|
845 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
846 const CImapCapabilityInfo& capabilityInfo = iParent.CapabilityInfo(); |
|
847 if(capabilityInfo.QueryFlag(CImapCapabilityInfo::EBinaryCap) && iFetchBodyInfo.IsText()) |
|
848 { |
|
849 if(secStart!=KImapTxtBinary().Length() - 1 || secEnd <= secStart + 1) |
|
850 { |
|
851 CorruptDataL(); |
|
852 } |
|
853 } |
|
854 else |
|
855 #endif |
|
856 { |
|
857 if(secStart!=KImapTxtBody().Length() - 1 || secEnd <= secStart + 1) |
|
858 { |
|
859 CorruptDataL(); |
|
860 } |
|
861 } |
|
862 |
|
863 TPtrC8 section=iCurrentPart.Mid(secStart+1,secEnd-secStart-1); |
|
864 |
|
865 //check the section is what we asked for |
|
866 if(section.CompareF(*iFetchBodyInfo.RelativePath()) != 0) |
|
867 { |
|
868 CorruptDataL(); |
|
869 } |
|
870 |
|
871 //get the origin octet of the form <origin>, this may not exist, if its not |
|
872 //then the origin is at the start of the data |
|
873 TInt originStart=iCurrentPart.Find(KStartOrigin); |
|
874 if(originStart==KErrNotFound) |
|
875 { |
|
876 //the origin octet will be 0, the data will go in the first chunk |
|
877 iCurrentChunk=0; |
|
878 } |
|
879 else |
|
880 { |
|
881 if(originStart != secEnd + 1) |
|
882 { |
|
883 CorruptDataL(); |
|
884 } |
|
885 TInt originEnd=iCurrentPart.Find(KEndOrigin); |
|
886 if(originEnd==KErrNotFound || originEnd != iCurrentPart.Length() - 1 ) |
|
887 { |
|
888 CorruptDataL(); |
|
889 } |
|
890 if(originEnd <= originStart + 1) |
|
891 { |
|
892 CorruptDataL(); |
|
893 } |
|
894 |
|
895 TPtrC8 originPtr=iCurrentPart.Mid(originStart+1,originEnd-originStart-1); |
|
896 TLex8 originToInt(originPtr); |
|
897 |
|
898 TInt origin=0; |
|
899 TInt err = originToInt.Val(origin); |
|
900 if (err != KErrNone) |
|
901 { |
|
902 // Was expecting originPtr to be a number |
|
903 CorruptDataL(); |
|
904 } |
|
905 |
|
906 //set the chunk number |
|
907 //if the origin was blank ie. <> then this is set to 0, the start of the data |
|
908 iCurrentChunk = CalculateChunk(origin); |
|
909 } |
|
910 |
|
911 //now look for the size of the literal |
|
912 TBool foundPart = GetAndStoreNextPart(); |
|
913 if(!foundPart) |
|
914 { |
|
915 CorruptDataL(); |
|
916 } |
|
917 |
|
918 TInt litStart=iCurrentPart.Find(KStartLiteral); |
|
919 TInt litEnd=iCurrentPart.Find(KEndLiteral); |
|
920 |
|
921 if(litStart==KErrNotFound && litEnd==KErrNotFound) |
|
922 { |
|
923 // This may be the data item |
|
924 ProcessBodyLiteralL(iCurrentPart); |
|
925 iState = EStateDataItemLine; |
|
926 iUnexpectedFormat = ETrue; |
|
927 } |
|
928 else |
|
929 { |
|
930 if(litStart==KErrNotFound || litEnd==KErrNotFound) |
|
931 { |
|
932 CorruptDataL(); |
|
933 } |
|
934 |
|
935 if(litEnd <= litStart + 1) |
|
936 { |
|
937 CorruptDataL(); |
|
938 } |
|
939 |
|
940 TPtrC8 litPtr=iCurrentPart.Mid(litStart+1,litEnd-litStart-1); |
|
941 TLex8 litSizeToInt(litPtr); |
|
942 |
|
943 TInt err = litSizeToInt.Val(iLiteralSize); |
|
944 if (err != KErrNone) |
|
945 { |
|
946 // Was expecting litPtr to be a number |
|
947 CorruptDataL(); |
|
948 } |
|
949 |
|
950 if(GetAndStoreNextPart()) |
|
951 { |
|
952 ProcessBodyLiteralL(iCurrentPart); |
|
953 iState = EStateDataItemLine; |
|
954 iUnexpectedFormat = ETrue; |
|
955 } |
|
956 else |
|
957 { |
|
958 //now wait for the litereral |
|
959 iState = EStateBodyLiteral; |
|
960 } |
|
961 } |
|
962 } |
|
963 |
|
964 void CImapFetchBody::ProcessBodyLiteralL(const TDesC8& aData) |
|
965 { |
|
966 delete iBuf; |
|
967 iBuf = NULL; |
|
968 iBuf = aData.AllocL(); |
|
969 |
|
970 //now wait for the line that always follows a literal |
|
971 iState = EStateFetchNextDataItemLine; |
|
972 } |
|
973 |
|
974 /** |
|
975 Move to the next part |
|
976 @return whether a part was found |
|
977 */ |
|
978 TBool CImapFetchBody::GetAndStoreNextPart() |
|
979 { |
|
980 iCurrentPart.Set(GetNextPart()); |
|
981 return (iCurrentPart.Length() > 0) ? ETrue : EFalse; |
|
982 } |
|
983 |
|
984 void CImapFetchBody::ProcessFlagsL() |
|
985 { |
|
986 iUnparsedData.Set(iMessageFlagInfo.ParseFlagsL(iUnparsedData)); |
|
987 } |
|
988 |
|
989 void CImapFetchBody::ProcessUidL() |
|
990 { |
|
991 if (GetAndStoreNextPart()) |
|
992 { |
|
993 TInt err = iMessageFlagInfo.SetMessageUid(iCurrentPart); |
|
994 if (err == KErrNone) |
|
995 { |
|
996 iUidDataItemFoundInResponse = ETrue; |
|
997 } |
|
998 else |
|
999 { |
|
1000 // expected iCurrentPart to be a number representing a UID. |
|
1001 // but we did not get a number. |
|
1002 CorruptDataL(); |
|
1003 } |
|
1004 } |
|
1005 } |
|
1006 |
|
1007 void CImapFetchBody::WriteMimeHeadersToCafL() |
|
1008 { |
|
1009 CImapMimeHeaderFields* fields = iFetchBodyResponse.HeaderFields(); |
|
1010 if (fields != NULL) |
|
1011 { |
|
1012 TPtrC8 name; |
|
1013 TPtrC8 value; |
|
1014 |
|
1015 fields->RestartGetNextField(); |
|
1016 while (fields->GetNextField(name, value)) |
|
1017 { |
|
1018 __LOG_FORMAT((iLogId, "Add CAF metadata: %S %S", &name, &value)); |
|
1019 iFetchBodyInfo.Caf()->AddToMetaDataL(name, value); |
|
1020 } |
|
1021 } |
|
1022 } |
|
1023 |
|
1024 void CImapFetchBody::StoreBodyDataL(TBool aExtraFetchRequestCount) |
|
1025 { |
|
1026 // We are going to pass the buffer to the mail store, so set our |
|
1027 // buffer to NULL so that we don't try to delete it if the store |
|
1028 // routine leaves. |
|
1029 HBufC8* buf(iBuf); |
|
1030 iBuf = NULL; |
|
1031 |
|
1032 if(iFetchBodyInfo.IsText()) |
|
1033 { |
|
1034 if(iImapSettings.StorePlainText()) |
|
1035 { |
|
1036 iSendFetch = iImapMailStore.StorePlainBodyTextL(buf,iFetchBodyInfo.PartId(),iCurrentChunk,aExtraFetchRequestCount); |
|
1037 } |
|
1038 else |
|
1039 { |
|
1040 if(iImapSettings.Store8BitData()) |
|
1041 { |
|
1042 iImapMailStore.StoreBodyChunk8L(buf,iFetchBodyInfo.PartId(),iCurrentChunk,aExtraFetchRequestCount); |
|
1043 } |
|
1044 else |
|
1045 { |
|
1046 iImapMailStore.StoreBodyChunk16L(buf,iFetchBodyInfo.PartId(),iCurrentChunk,aExtraFetchRequestCount); |
|
1047 } |
|
1048 } |
|
1049 } |
|
1050 else //attachments |
|
1051 { |
|
1052 iImapMailStore.StoreAttachmentChunkL(buf,iFetchBodyInfo.PartId(),iCurrentChunk); |
|
1053 } |
|
1054 } |
|
1055 |
|
1056 /** |
|
1057 If pipelining is enabled then this method will send the next fetch request to the server after confirmation of the last request having been sent. |
|
1058 @return void |
|
1059 */ |
|
1060 |
|
1061 |
|
1062 void CImapFetchBody::SendDataCnfL() |
|
1063 { |
|
1064 ASSERT(iOutstandingRequests>0); |
|
1065 --iOutstandingRequests; |
|
1066 //if we want more requests outstanding then send the next one now |
|
1067 if(iOutstandingRequests>0 && iSendFetch) |
|
1068 { |
|
1069 SendMessageL(); |
|
1070 } |
|
1071 } |
|
1072 /** |
|
1073 This method will enable the FETCH command to be send to the server if it was |
|
1074 disabled by CImapMailStore due to reciept of out-of-order chunks. |
|
1075 @return void |
|
1076 */ |
|
1077 void CImapFetchBody::EnableSendFetch() |
|
1078 { |
|
1079 iSendFetch = ETrue; |
|
1080 } |