|
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 "cimapfetchbodystructurebase.h" |
|
17 #include "cimapfetchresponse.h" |
|
18 #include "cimapbodystructurebuilder.h" |
|
19 #include "cimaprfc822headerfieldsparser.h" |
|
20 #include "moutputstream.h" |
|
21 #include "cimapsessionconsts.h" |
|
22 #include "imappaniccodes.h" |
|
23 #include "cimaplogger.h" |
|
24 |
|
25 _LIT8(KCommandFetch, "%d UID FETCH %S (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (%S)])\r\n"); |
|
26 const TInt KCommandFetchFormatLength=6; |
|
27 |
|
28 CImapFetchBodyStructureBase::CImapFetchBodyStructureBase(CImapFolderInfo* aSelectedFolderInfo, TInt aLogId, const TDesC8& aHeaderFields) |
|
29 : CImapCommand(aSelectedFolderInfo, aLogId) |
|
30 , iHeaderFields(aHeaderFields) |
|
31 , iState(EStateDataItemLine) |
|
32 { |
|
33 } |
|
34 |
|
35 CImapFetchBodyStructureBase::~CImapFetchBodyStructureBase() |
|
36 { |
|
37 delete iBodyStructureBuilder; |
|
38 delete iHeaderFieldsParser; |
|
39 } |
|
40 |
|
41 /** |
|
42 @param aTagId Command sequence id which will be send along with the IMAP command. |
|
43 @param aStream A wrapper for the outbound stream of a connected socket, using which |
|
44 the command will be send to the server |
|
45 */ |
|
46 void CImapFetchBodyStructureBase::SendMessageL(TInt aTagId, MOutputStream& aStream) |
|
47 { |
|
48 iTagId = aTagId; |
|
49 |
|
50 TInt bufLen = KCommandFetch().Length() - KCommandFetchFormatLength; |
|
51 bufLen += TagLength(aTagId); |
|
52 bufLen += iSequenceSet->Length(); |
|
53 bufLen += iHeaderFields.Length(); |
|
54 |
|
55 __ASSERT_DEBUG(iOutputBuffer==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandOutputBufferNotNull)); |
|
56 iOutputBuffer = HBufC8::NewL(bufLen); |
|
57 iOutputBuffer->Des().Format(KCommandFetch, aTagId, iSequenceSet, &iHeaderFields); |
|
58 |
|
59 //send the command to the server |
|
60 aStream.SendDataReq(*iOutputBuffer); |
|
61 } |
|
62 |
|
63 /** |
|
64 @param aData Will contain a single line of response from the server for LOGIN command without \r\n. |
|
65 @return will be any one of this |
|
66 1) If the next expected chunk is a literal block, ParseMessageL() will return the size of the block it expects. |
|
67 2) If the next expected chunk is a line, ParseMessageL() will return 0, and Result() will return EImapResponseNone. |
|
68 3) If no further data is expected (e.g. the OK or error tag has been received) then ParseMessageL() will return 0, |
|
69 and Result() will return one of EImapResponseOk, EImapResponseNo or EImapResponseBad. |
|
70 */ |
|
71 CImapCommand::TParseBlockResult CImapFetchBodyStructureBase::DoParseBlockL(const TDesC8& aData) |
|
72 { |
|
73 CImapCommand::TParseBlockResult resultCode(ECompleteUntagged); |
|
74 |
|
75 switch (iState) |
|
76 { |
|
77 case EStateDataItemLine: |
|
78 { |
|
79 // We are the beginning of a new response, so we can't have found any UID data items yet. |
|
80 // So we need to reset the flag here. |
|
81 iUidDataItemFoundInResponse = EFalse; |
|
82 resultCode = ProcessStartL(); |
|
83 |
|
84 __ASSERT_DEBUG(iState == EStateDataItemLine, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState)); |
|
85 |
|
86 // If we get EResponseIncomplete then allow the current EStateDataItemLine state to |
|
87 // drop through to ProcessDataItems() |
|
88 // otherwise, EStateComplete will take us straight to the return. |
|
89 if (resultCode != EResponseIncomplete) |
|
90 { |
|
91 iState = EStateComplete; |
|
92 } |
|
93 } |
|
94 break; |
|
95 case EStateBodyStructureLiteral: |
|
96 case EStateBodyStructureLine: |
|
97 { |
|
98 ProcessBodyStructureL(aData); |
|
99 resultCode = EResponseIncomplete; // always expect more data after bodystructure. |
|
100 } |
|
101 break; |
|
102 case EStateHeaderFieldsLiteral: |
|
103 { |
|
104 ProcessHeaderFieldsL(aData); |
|
105 resultCode = EResponseIncomplete; // always expect more data after header field literal. |
|
106 } |
|
107 break; |
|
108 case EStateFetchNextDataItemLine: |
|
109 { |
|
110 // Fetch is over. Get ready to process next data item. |
|
111 iUnparsedData.Set(aData); |
|
112 if(GetAndStoreNextPart()) |
|
113 { |
|
114 iState = EStateDataItemLine; |
|
115 } |
|
116 } |
|
117 break; |
|
118 default: |
|
119 { |
|
120 // unexpected state |
|
121 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState)); |
|
122 } |
|
123 } |
|
124 |
|
125 // The ProcessXxxL() methods above can change the state. |
|
126 // Now we need to check if there are data items to process... |
|
127 if (iState == EStateDataItemLine) |
|
128 { |
|
129 resultCode = ProcessDataItemsL(); |
|
130 } |
|
131 else if (iState == EStateComplete) |
|
132 { |
|
133 // this response is complete, so the next data (if any) will be a data item line, |
|
134 iState = EStateDataItemLine; |
|
135 } |
|
136 |
|
137 // For complete untagged responses, check whether the UID data item was received. |
|
138 // If it was, then this was a genuine response to the UID FETCH command. |
|
139 // If it was not received, then this was an unsolicited server event, and the data should be discarded. |
|
140 if (resultCode == ECompleteUntagged) |
|
141 { |
|
142 if (iUidDataItemFoundInResponse) |
|
143 { |
|
144 // Genuine UID FETCH response - copy UID and FLAG info into the response object |
|
145 iFetchResponse->SetMessageFlagInfo(iMessageFlagInfo); |
|
146 |
|
147 // Note: iUidDataItemFoundInResponse is NOT reset here as its value is needed by CImapFetchMultiBodyStructures::DoParseBlockL() |
|
148 // Instead iUidDataItemFoundInResponse is set to EFalse just prior to calling ProcessStartL() - i.e. at the start of a new response. |
|
149 } |
|
150 else |
|
151 { |
|
152 // Record that an unsolicited FETCH was received - so that a sync will be triggered after this command. |
|
153 __LOG_TEXT(iLogId, "CImapFetchBodyStructureBase::DoParseBlockL() - Found unsolicited FETCH FLAGS"); |
|
154 SetMessageFlagsChanged(); |
|
155 } |
|
156 } |
|
157 |
|
158 return resultCode; |
|
159 } |
|
160 |
|
161 CImapCommand::TParseBlockResult CImapFetchBodyStructureBase::ProcessStartL() |
|
162 { |
|
163 TParseBlockResult result = ENotRecognised; |
|
164 |
|
165 TInt tagId = 0; |
|
166 TTagType tagged = GetTagTypeL(tagId); |
|
167 switch(tagged) |
|
168 { |
|
169 case ETagged: |
|
170 { |
|
171 // Check the tag id |
|
172 if (tagId != iTagId) |
|
173 { |
|
174 //Unexpected Tagid |
|
175 CorruptDataL(); |
|
176 } |
|
177 |
|
178 // Fetch and check the response code |
|
179 iResponseCode = GetResponseStateCode(); |
|
180 if (iResponseCode == EImapResponseNone) |
|
181 { |
|
182 // Was expecting one of OK/NO/BAD, but didn't get it. This is a parse error. |
|
183 CorruptDataL(); |
|
184 } |
|
185 |
|
186 result = ECompleteTagged; |
|
187 } |
|
188 break; |
|
189 case EUntagged: |
|
190 { |
|
191 // Is this a FETCH response? |
|
192 // Check for Sequence Number followed by "FETCH" |
|
193 |
|
194 TPtrC8 part1 = GetNextPart(); // returns KNullDesC8 if there is no part available |
|
195 TPtrC8 part2 = GetNextPart(); // returns KNullDesC8 if there is no part available |
|
196 |
|
197 // Is part1 a Sequence Number? |
|
198 TInt sequenceNumber = 0; |
|
199 TLex8 lex(part1); |
|
200 if (lex.Val(sequenceNumber) == KErrNone) |
|
201 { |
|
202 // part1 is a Sequence Number. Now check part2 - is it "FETCH"? |
|
203 |
|
204 if(part2.CompareF(KImapTxtFetch) == 0) |
|
205 { |
|
206 if (GetAndStoreNextPart()) |
|
207 { |
|
208 if (iCurrentPart[0] == '(') |
|
209 { |
|
210 iCurrentPart.Set(iCurrentPart.Mid(1)); |
|
211 } |
|
212 else |
|
213 { |
|
214 // was expecting a bracket, got something else |
|
215 CorruptDataL(); |
|
216 } |
|
217 } |
|
218 else |
|
219 { |
|
220 // was expecting a bracket, got nothing |
|
221 CorruptDataL(); |
|
222 } |
|
223 |
|
224 result = EResponseIncomplete; |
|
225 } |
|
226 } |
|
227 } |
|
228 break; |
|
229 case EContinuation: |
|
230 default: |
|
231 { |
|
232 CorruptDataL(); |
|
233 } |
|
234 break; |
|
235 } |
|
236 |
|
237 // result will be ENotRecognised if tagged not found or untagged FETCH not found. |
|
238 return result; |
|
239 } |
|
240 |
|
241 CImapCommand::TParseBlockResult CImapFetchBodyStructureBase::ProcessDataItemsL() |
|
242 { |
|
243 CImapCommand::TParseBlockResult resultCode = EResponseIncomplete; |
|
244 |
|
245 TBool foundPart = ETrue; |
|
246 while (iState == EStateDataItemLine && foundPart) |
|
247 { |
|
248 if (iCurrentPart.CompareF(KImapTxtUid) == 0) |
|
249 { |
|
250 ProcessUidL(); |
|
251 } |
|
252 else if (iCurrentPart.CompareF(KImapTxtFlags) == 0) |
|
253 { |
|
254 ProcessFlagsL(); |
|
255 } |
|
256 else if (iCurrentPart.CompareF(KImapTxtBodyStructure) == 0) |
|
257 { |
|
258 ProcessBodyStructureL(); |
|
259 } |
|
260 else if (iCurrentPart.CompareF(KImapTxtBodyHeaderFields) == 0) |
|
261 { |
|
262 ProcessHeaderFieldsL(); |
|
263 } |
|
264 // Ignore anything else at the moment until we reach |
|
265 // a valid dataItem on the next line. |
|
266 |
|
267 |
|
268 // Only fetch the next part if we're still searching for data items. |
|
269 if (iState == EStateDataItemLine) |
|
270 { |
|
271 foundPart = GetAndStoreNextPart(); |
|
272 } |
|
273 } |
|
274 |
|
275 if (!foundPart && iState == EStateDataItemLine) |
|
276 { |
|
277 resultCode = ECompleteUntagged; |
|
278 } |
|
279 |
|
280 return resultCode; |
|
281 } |
|
282 |
|
283 void CImapFetchBodyStructureBase::ProcessFlagsL() |
|
284 { |
|
285 iUnparsedData.Set(iMessageFlagInfo.ParseFlagsL(iUnparsedData)); |
|
286 } |
|
287 |
|
288 void CImapFetchBodyStructureBase::ProcessUidL() |
|
289 { |
|
290 if (GetAndStoreNextPart()) |
|
291 { |
|
292 TInt err = iMessageFlagInfo.SetMessageUid(iCurrentPart); |
|
293 if (err == KErrNone) |
|
294 { |
|
295 iUidDataItemFoundInResponse = ETrue; |
|
296 } |
|
297 else |
|
298 { |
|
299 // expected iCurrentPart to be a number representing a UID. |
|
300 // but we did not get a number. |
|
301 CorruptDataL(); |
|
302 } |
|
303 } |
|
304 } |
|
305 |
|
306 void CImapFetchBodyStructureBase::ProcessBodyStructureL() |
|
307 { |
|
308 iBodyStructureBuilder = CImapBodyStructureBuilder::NewL(*iFetchResponse, iLogId); |
|
309 |
|
310 ProcessBodyStructureL(iUnparsedData); |
|
311 } |
|
312 |
|
313 void CImapFetchBodyStructureBase::ProcessBodyStructureL(const TDesC8& aData) |
|
314 { |
|
315 TBool needsMore = iBodyStructureBuilder->ProcessBlockL(aData); |
|
316 |
|
317 if (iState == EStateBodyStructureLiteral) |
|
318 { |
|
319 // Bodystructure should always request a line after a literal |
|
320 __ASSERT_DEBUG(needsMore, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureExpectedRequestForLine)); |
|
321 |
|
322 iState = EStateBodyStructureLine; |
|
323 } |
|
324 else if (needsMore) |
|
325 { |
|
326 // Check the previous state was one we expected |
|
327 __ASSERT_DEBUG(iState == EStateDataItemLine || iState == EStateBodyStructureLine, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState)); |
|
328 |
|
329 iState = EStateBodyStructureLiteral; |
|
330 } |
|
331 else |
|
332 { |
|
333 // Check the previous state was one we expected |
|
334 __ASSERT_DEBUG(iState == EStateDataItemLine || iState == EStateBodyStructureLine, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState)); |
|
335 |
|
336 iState = EStateDataItemLine; |
|
337 |
|
338 iUnparsedData.Set(iBodyStructureBuilder->UnparsedData()); |
|
339 |
|
340 delete iBodyStructureBuilder; |
|
341 iBodyStructureBuilder = NULL; |
|
342 } |
|
343 } |
|
344 |
|
345 void CImapFetchBodyStructureBase::ProcessHeaderFieldsL() |
|
346 { |
|
347 // Skip past the field titles |
|
348 while (GetAndStoreNextPart()) |
|
349 { |
|
350 if (iCurrentPart[iCurrentPart.Length() - 1] == ']') |
|
351 { |
|
352 break; |
|
353 } |
|
354 } |
|
355 |
|
356 iHeaderFieldsParser = CImapRfc822HeaderFieldsParser::NewL(*iFetchResponse, iLogId); |
|
357 |
|
358 return ProcessHeaderFieldsL(iUnparsedData); |
|
359 } |
|
360 |
|
361 void CImapFetchBodyStructureBase::ProcessHeaderFieldsL(const TDesC8& aData) |
|
362 { |
|
363 TBool needsMore = iHeaderFieldsParser->ProcessBlockL(aData); |
|
364 |
|
365 if (iState == EStateDataItemLine) |
|
366 { |
|
367 // Always expect a literal for the Header Fields data item |
|
368 if (!needsMore) |
|
369 { |
|
370 CorruptDataL(); |
|
371 } |
|
372 |
|
373 iState = EStateHeaderFieldsLiteral; |
|
374 } |
|
375 else if (iState == EStateHeaderFieldsLiteral) |
|
376 { |
|
377 // Parser should not be expecting more data after a literal |
|
378 if (needsMore) |
|
379 { |
|
380 CorruptDataL(); |
|
381 } |
|
382 |
|
383 iState = EStateFetchNextDataItemLine; |
|
384 |
|
385 iUnparsedData.Set(iHeaderFieldsParser->UnparsedData()); |
|
386 __ASSERT_DEBUG(iUnparsedData.Length() == 0, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnparsedDataExists)); |
|
387 |
|
388 delete iHeaderFieldsParser; |
|
389 iHeaderFieldsParser = NULL; |
|
390 } |
|
391 else |
|
392 { |
|
393 // Recovery from this would depend on the state |
|
394 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EFetchBodyStructureUnexpectedState)); |
|
395 } |
|
396 } |
|
397 |
|
398 /** |
|
399 Move to the next part |
|
400 @return whether a part was found |
|
401 */ |
|
402 TBool CImapFetchBodyStructureBase::GetAndStoreNextPart() |
|
403 { |
|
404 iCurrentPart.Set(GetNextPart()); |
|
405 return (iCurrentPart.Length() > 0) ? ETrue : EFalse; |
|
406 } |
|
407 |
|
408 |
|
409 TBool CImapFetchBodyStructureBase::UidDataItemFoundInResponse() |
|
410 { |
|
411 return iUidDataItemFoundInResponse; |
|
412 } |