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