|
1 // Copyright (c) 1998-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 "cimapatomparser.h" |
|
17 #include "cimapatom.h" |
|
18 #include "cimapcommand.h" |
|
19 #include "imappaniccodes.h" |
|
20 |
|
21 // Initial parser buffer size & granularity |
|
22 const TInt KIOBufferSize=1280; |
|
23 const TInt KIOBufferGranularity=256; |
|
24 |
|
25 |
|
26 CImapAtomParser::CImapAtomParser(TBool aAllowParseAtTopLevel, TInt aLogId) |
|
27 : iAllowParseAtTopLevel(aAllowParseAtTopLevel) |
|
28 , iBufferSize(KIOBufferSize) |
|
29 , iLogId(aLogId) |
|
30 {} |
|
31 |
|
32 CImapAtomParser::~CImapAtomParser() |
|
33 { |
|
34 // Dispose of buffer |
|
35 delete iBuffer; |
|
36 |
|
37 // Delete any atom tree that may still exist |
|
38 delete iRootAtom; |
|
39 |
|
40 // delete the atom array |
|
41 iAtomArray.ResetAndDestroy(); |
|
42 iAtomStack.Reset(); |
|
43 } |
|
44 |
|
45 // Create and call non-trivial contructor |
|
46 CImapAtomParser *CImapAtomParser::NewL(TBool aAllowParseAtTopLevel, TInt aLogId) |
|
47 { |
|
48 CImapAtomParser* self=new (ELeave) CImapAtomParser(aAllowParseAtTopLevel, aLogId); |
|
49 CleanupStack::PushL(self); |
|
50 self->ConstructL(); |
|
51 CleanupStack::Pop(); |
|
52 return self; |
|
53 } |
|
54 |
|
55 // The non-trivial constructor |
|
56 void CImapAtomParser::ConstructL() |
|
57 { |
|
58 // Get initial buffer |
|
59 iBuffer=HBufC8::NewL(iBufferSize); |
|
60 |
|
61 iRootAtom=CImapAtom::NewLC(); |
|
62 CleanupStack::Pop(iRootAtom); |
|
63 |
|
64 iAtom=iRootAtom; |
|
65 iNextIsChild=ETrue; |
|
66 } |
|
67 |
|
68 // Return the root atom |
|
69 CImapAtom* CImapAtomParser::RootAtom() |
|
70 { |
|
71 // Return it |
|
72 return(iRootAtom); |
|
73 } |
|
74 |
|
75 /** |
|
76 @return a descriptor that points at the unparsed portion of the last line to be processed. |
|
77 This pointer descriptor is only valid while the descriptor it points at is valid |
|
78 This pointer descriptor will only be set after ProcessLineL() has returned |
|
79 EFalse to indicate that no more data is expected. |
|
80 */ |
|
81 const TPtrC8& CImapAtomParser::UnparsedData() |
|
82 { |
|
83 return iUnparsedData; |
|
84 } |
|
85 |
|
86 /** |
|
87 Returns the internal data buffer, transferring ownership of the buffer to the caller. |
|
88 The internal data buffer pointer is set to NULL. |
|
89 This method should only be called after parsing is complete. |
|
90 @return the data buffer. |
|
91 */ |
|
92 HBufC8* CImapAtomParser::DetachBuffer() |
|
93 // Note that the iBuffer pointer can change during parsing (via ReAllocL) |
|
94 // So we only want to expose iBuffer after parsing. |
|
95 { |
|
96 // Check for internal programming error |
|
97 __ASSERT_DEBUG(iParserState == EStateParseComplete, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserInvalidParserState)); |
|
98 |
|
99 HBufC8* detachedBuffer = iBuffer; |
|
100 iBuffer = NULL; |
|
101 |
|
102 return detachedBuffer; |
|
103 } |
|
104 |
|
105 // Deal with the atom stack: this is used when building the atom tree |
|
106 void CImapAtomParser::PushL(CImapAtom* aAtom) |
|
107 { |
|
108 iAtomStack.AppendL(aAtom); |
|
109 } |
|
110 |
|
111 CImapAtom* CImapAtomParser::PopL() |
|
112 { |
|
113 if (iAtomStack.Count() <= 0) |
|
114 { |
|
115 // Mismatched bracket |
|
116 CImapCommand::CorruptDataL(iLogId); |
|
117 } |
|
118 |
|
119 TInt count = iAtomStack.Count(); |
|
120 CImapAtom* atom = iAtomStack[count-1]; |
|
121 iAtomStack.Remove(count-1); |
|
122 return(atom); |
|
123 } |
|
124 |
|
125 // Add to the parsed buffer |
|
126 void CImapAtomParser::BufferAppendL(const TChar aChar) |
|
127 { |
|
128 // Check for internal programming error |
|
129 __ASSERT_DEBUG(iBuffer != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserBufferIsNull)); |
|
130 |
|
131 // Does buffer need extending? |
|
132 if (iBuffer->Length()==iBufferSize) |
|
133 { |
|
134 HBufC8 *oldbuffer=iBuffer; |
|
135 const TText8 *oldbufptr=iBuffer->Ptr(); |
|
136 |
|
137 // Extend by granularity amount |
|
138 iBufferSize+=KIOBufferGranularity; |
|
139 iBuffer=iBuffer->ReAllocL(iBufferSize); |
|
140 |
|
141 // Buffer moved? |
|
142 if (iBuffer!=oldbuffer) |
|
143 { |
|
144 // Fixup buffer tree pointers |
|
145 iRootAtom->FixupL(iBuffer,oldbufptr); |
|
146 } |
|
147 } |
|
148 |
|
149 // Append the data |
|
150 iBuffer->Des().Append(aChar); |
|
151 } |
|
152 |
|
153 // Add the last atom appended to the buffer to the tree. |
|
154 void CImapAtomParser::AddAtomL() |
|
155 { |
|
156 // Check for internal programming error |
|
157 __ASSERT_DEBUG(iBuffer != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserBufferIsNull)); |
|
158 |
|
159 TPtrC8 bufptr(iBuffer->Ptr()+iAtomStart, iBuffer->Length()-iAtomStart); |
|
160 |
|
161 // Make a new current atom |
|
162 CImapAtom* newAtom=CImapAtom::NewLC(); |
|
163 iAtomArray.AppendL(newAtom); |
|
164 CleanupStack::Pop(newAtom); |
|
165 |
|
166 // Set pointers in it |
|
167 newAtom->Set(bufptr, iParserQuoted); |
|
168 |
|
169 // Add it as a child/sibling to the current atom |
|
170 if (iNextIsChild) |
|
171 { |
|
172 iAtom->AddChild(newAtom); |
|
173 } |
|
174 else |
|
175 { |
|
176 iAtom->AddNext(newAtom); |
|
177 } |
|
178 |
|
179 // The next item should be a sibling |
|
180 iNextIsChild=EFalse; |
|
181 |
|
182 // Make new current |
|
183 iAtom=newAtom; |
|
184 } |
|
185 |
|
186 /** |
|
187 Receives a line of data. |
|
188 If parsing completes before the end of the line, then aLine is set to point |
|
189 at the octet immediately after the last parsed octet |
|
190 @return ETrue if more data is expected. |
|
191 */ |
|
192 TBool CImapAtomParser::ProcessLineL(const TDesC8& aLine) |
|
193 { |
|
194 // Check for internal programming errors |
|
195 __ASSERT_DEBUG(iParserState == EStateAtomWait, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserInvalidParserState)); |
|
196 __ASSERT_DEBUG(iBuffer != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserBufferIsNull)); |
|
197 |
|
198 // Process the buffer |
|
199 TChar octet; |
|
200 |
|
201 TInt lineLength = aLine.Length(); |
|
202 |
|
203 TBool bReachedTopLevel = EFalse; |
|
204 |
|
205 TInt pos; // used after the loop |
|
206 for(pos=0; pos<lineLength; ++pos) |
|
207 { |
|
208 // Octet to process |
|
209 octet=aLine[pos]; |
|
210 |
|
211 switch(iParserState) |
|
212 { |
|
213 case EStateAtomWait: |
|
214 switch(octet) |
|
215 { |
|
216 case '(': |
|
217 { |
|
218 // Make a new current atom |
|
219 CImapAtom* newAtom=CImapAtom::NewLC(); |
|
220 iAtomArray.AppendL(newAtom); |
|
221 CleanupStack::Pop(newAtom); |
|
222 |
|
223 // Add it as a sibling to the current atom |
|
224 if (iNextIsChild) |
|
225 { |
|
226 iAtom->AddChild(newAtom); |
|
227 } |
|
228 else |
|
229 { |
|
230 iAtom->AddNext(newAtom); |
|
231 } |
|
232 |
|
233 // The next item should be a child |
|
234 iNextIsChild=ETrue; |
|
235 |
|
236 // Push current atom onto atom stack, make new current |
|
237 iAtom=newAtom; |
|
238 PushL(iAtom); |
|
239 |
|
240 // Store the open bracket in the buffer, so we can tell what it is |
|
241 BufferAppendL(octet); |
|
242 iAtom->Set(iBuffer->Right(1), EFalse); |
|
243 |
|
244 break; |
|
245 } |
|
246 |
|
247 case ')': |
|
248 // End of this nesting level: pop last atom off stack and |
|
249 // make it the new current one |
|
250 iAtom=PopL(); |
|
251 if (iAtomStack.Count() == 0) |
|
252 { |
|
253 bReachedTopLevel = ETrue; |
|
254 } |
|
255 |
|
256 // Any new atoms will be siblings, not children |
|
257 iNextIsChild=EFalse; |
|
258 |
|
259 break; |
|
260 |
|
261 case '{': |
|
262 // Start of a literal length |
|
263 iLiteralLength=0; |
|
264 iParserQuoted=EFalse; |
|
265 iParserState=EStateLiteralLength; |
|
266 break; |
|
267 |
|
268 case ' ': |
|
269 case '\r': |
|
270 // Whitespace. Ignore! This state only happens with whitespace |
|
271 // after a close )] or a endquote " |
|
272 // Note that the CRLF will already have been stripped out by CImapSession |
|
273 // so we are treating a CR on its own as whitespace |
|
274 break; |
|
275 case '\"': |
|
276 // Quotes: we don't keep them, so the atom starts at the next |
|
277 // character. |
|
278 iAtomStart=iBuffer->Length(); |
|
279 iParserState=EStateInAtom; |
|
280 iParserQuoted=ETrue; |
|
281 iGotEscape=EFalse; |
|
282 break; |
|
283 |
|
284 default: |
|
285 // Start new atom in buffer |
|
286 iAtomStart=iBuffer->Length(); |
|
287 BufferAppendL(octet); |
|
288 iParserState=EStateInAtom; |
|
289 iParserQuoted=EFalse; |
|
290 break; |
|
291 } |
|
292 break; |
|
293 |
|
294 case EStateInAtom: |
|
295 if (iParserQuoted) |
|
296 { |
|
297 // Look for another quote |
|
298 if (octet=='\"') |
|
299 { |
|
300 // Just had an escape character? |
|
301 if (iGotEscape) |
|
302 { |
|
303 // Add the character |
|
304 BufferAppendL(octet); |
|
305 iGotEscape=EFalse; |
|
306 } |
|
307 else |
|
308 { |
|
309 // It's the terminator: Add the atom, minus the quotes |
|
310 AddAtomL(); |
|
311 iParserState=EStateAtomWait; |
|
312 } |
|
313 } |
|
314 else |
|
315 { |
|
316 // Escape character? |
|
317 if (!iGotEscape && octet=='\\') |
|
318 { |
|
319 // Got one |
|
320 iGotEscape=ETrue; |
|
321 } |
|
322 else |
|
323 { |
|
324 // Add to buffer |
|
325 BufferAppendL(octet); |
|
326 iGotEscape=EFalse; |
|
327 } |
|
328 } |
|
329 } |
|
330 else |
|
331 { |
|
332 switch(octet) |
|
333 { |
|
334 case ' ': |
|
335 case '\r': |
|
336 // Whitespace: end of atom |
|
337 // Note that the CRLF will already have been stripped out by CImapSession |
|
338 // so we are treating a CR on its own as whitespace |
|
339 { |
|
340 AddAtomL(); |
|
341 |
|
342 // Either go back to looking for an atom, or a LF |
|
343 //iParserState=(octet=='\r')?EStateWaitLF:EStateAtomWait; |
|
344 iParserState = EStateAtomWait; |
|
345 } |
|
346 break; |
|
347 case '(': |
|
348 { |
|
349 // Add this atom |
|
350 AddAtomL(); |
|
351 |
|
352 // Make a new current atom |
|
353 CImapAtom* newAtom=CImapAtom::NewLC(); |
|
354 iAtomArray.AppendL(newAtom); |
|
355 CleanupStack::Pop(newAtom); |
|
356 |
|
357 // Add it as a sibling to the current atom |
|
358 if (iNextIsChild) |
|
359 { |
|
360 iAtom->AddChild(newAtom); |
|
361 } |
|
362 else |
|
363 { |
|
364 iAtom->AddNext(newAtom); |
|
365 } |
|
366 |
|
367 // The next item should be a child |
|
368 iNextIsChild=ETrue; |
|
369 |
|
370 // Push current atom onto atom stack, make new current |
|
371 iAtom=newAtom; |
|
372 PushL(iAtom); |
|
373 |
|
374 // Store the open bracket in the buffer, so we can tell what it is |
|
375 BufferAppendL(octet); |
|
376 iAtom->Set(iBuffer->Right(1), EFalse); |
|
377 |
|
378 iParserState=EStateAtomWait; |
|
379 } |
|
380 break; |
|
381 case ')': |
|
382 { |
|
383 // Although these bytes usually indicate the end of an atom, |
|
384 // they can also legitimately appear in a text field. |
|
385 // If this is the end of an atom, then it must be a child or |
|
386 // sibling atom in which case there will be an entry on the atom |
|
387 // stack. If there is no entry on the atom stack, then this must |
|
388 // be a text field so just add the octet to the buffer. |
|
389 if (iAtomStack.Count() > 0) |
|
390 { |
|
391 // Add this atom |
|
392 AddAtomL(); |
|
393 |
|
394 // End of this nesting level: pop last atom off stack and |
|
395 // make it the new current one |
|
396 iAtom=PopL(); |
|
397 if (iAtomStack.Count() == 0) |
|
398 { |
|
399 bReachedTopLevel = ETrue; |
|
400 } |
|
401 |
|
402 // Any new atoms will be siblings, not children |
|
403 iNextIsChild=EFalse; |
|
404 |
|
405 iParserState=EStateAtomWait; |
|
406 } |
|
407 else |
|
408 { |
|
409 BufferAppendL(octet); |
|
410 } |
|
411 } |
|
412 break; |
|
413 default: |
|
414 { |
|
415 // Add to buffer |
|
416 BufferAppendL(octet); |
|
417 } |
|
418 break; |
|
419 } |
|
420 } |
|
421 break; |
|
422 case EStateLiteralLength: |
|
423 // Digit? |
|
424 if (octet.IsDigit()) |
|
425 { |
|
426 // Add it to the total |
|
427 iLiteralLength=(iLiteralLength*10)+(octet-(TChar)'0'); |
|
428 if (iLiteralLength <0) |
|
429 { |
|
430 User::Leave(KErrCorrupt); |
|
431 } |
|
432 } |
|
433 else if (octet=='}') |
|
434 { |
|
435 // Need to skip CR, LF |
|
436 iLiteralSkip=2; |
|
437 iParserState=EStateLiteralSkip; |
|
438 |
|
439 // Add the atom (with the length we know, but no data) to the |
|
440 // structure now, so that the partial structure can be parsed. |
|
441 iAtomStart=iBuffer->Length(); |
|
442 } |
|
443 break; |
|
444 case EStateLiteralSkip: |
|
445 // Skipping... |
|
446 if (--iLiteralSkip==0) |
|
447 { |
|
448 // Is literal 0 bytes long? |
|
449 if (iLiteralLength==0) |
|
450 { |
|
451 // Nothing to follow |
|
452 iParserState=EStateAtomWait; |
|
453 } |
|
454 else |
|
455 { |
|
456 // In some cases {nnn} would be present in the middle of FETCH header info ALSO |
|
457 // so need to process the array further |
|
458 // Eg: *43 FETCH (FLAGS (\Seen $LuukkuClean $NotJunk JunkRecorded) UID 6285 BODYSTRUCTURE (("TEXT" "PLAIN" ("CHARSET" "ISO-8859-1") NIL NIL "7BIT" 4 0 NIL NIL NIL)("APPLICATION" "PDF" ("NAME" {25} |
|
459 // Kopio_paperikirjeestä.pdf) NIL NIL "BASE64" 187346 NIL ("ATTACHMENT" ("FILENAME" {25} |
|
460 // Kopio_paperikirjeestä.pdf)) NIL) "MIXED" ("BOUNDARY" "----=_Part_7226_1401000605.1221158570821") NIL NIL) BODY[HEADER.FIELDS (Received Date Subject From Priority X-MSMail-Priority X-Priority Importance)] {977} |
|
461 |
|
462 // Checking for pos>=2, so that array is not out of bounds |
|
463 if ((pos>=2) && (aLine[pos-2] != ']')) |
|
464 { |
|
465 iParserState=EStateAtomWait; |
|
466 } |
|
467 else |
|
468 { |
|
469 iParserState=EStateLiteralFetch; |
|
470 // Add the atom (with the length we know, but no data) to the |
|
471 // structure now, so that the partial structure can be parsed. |
|
472 iAtomStart=iBuffer->Length(); |
|
473 AddAtomL(); |
|
474 } |
|
475 } |
|
476 } |
|
477 break; |
|
478 case EStateLiteralFetch: |
|
479 // Fetching |
|
480 { |
|
481 // Internal programming error: |
|
482 // This state should only be handled in ProcessLiteralBlockL() |
|
483 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserInvalidParserState)); |
|
484 } |
|
485 break; |
|
486 } // switch(iParserState) |
|
487 |
|
488 if (bReachedTopLevel && !iAllowParseAtTopLevel) |
|
489 { |
|
490 break; // from for loop |
|
491 } |
|
492 }// for(...pos...) |
|
493 |
|
494 // Exited early. Record the un-parsed data |
|
495 if (bReachedTopLevel) |
|
496 { |
|
497 // skip the close-bracket that caused us to reach the top |
|
498 ++pos; |
|
499 // and record anything that remains |
|
500 if (pos < lineLength) |
|
501 { |
|
502 iUnparsedData.Set(aLine.Right(lineLength-pos)); |
|
503 } |
|
504 } |
|
505 |
|
506 // The whole buffer has been read now |
|
507 if (iParserState == EStateInAtom) |
|
508 { |
|
509 AddAtomL(); |
|
510 iParserState = EStateAtomWait; |
|
511 } |
|
512 else if (iParserState == EStateLiteralLength) |
|
513 { |
|
514 // the line contained a '{' but did not have a matching '}' |
|
515 CImapCommand::CorruptDataL(iLogId); |
|
516 } |
|
517 |
|
518 // Check for internal programming error |
|
519 __ASSERT_DEBUG(iParserState == EStateAtomWait || iParserState == EStateLiteralFetch, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserInvalidParserState)); |
|
520 |
|
521 TBool bWantMoreData = ETrue; |
|
522 |
|
523 if (iParserState == EStateAtomWait) |
|
524 { |
|
525 bWantMoreData = EFalse; |
|
526 iParserState = EStateParseComplete; |
|
527 } |
|
528 |
|
529 return bWantMoreData; |
|
530 } |
|
531 |
|
532 void CImapAtomParser::ProcessLiteralBlockL(const TDesC8& aLiteralBlock) |
|
533 { |
|
534 // Check for internal programming errors |
|
535 __ASSERT_DEBUG(iParserState == EStateLiteralFetch, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserInvalidParserState)); |
|
536 __ASSERT_DEBUG(aLiteralBlock.Length() == iLiteralLength, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserLiteralBlockLengthMismatch)); |
|
537 __ASSERT_DEBUG(iBuffer != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EAtomParserBufferIsNull)); |
|
538 |
|
539 // BufferAppendL() appends a character, whereas this appends a whole string |
|
540 if (iBuffer->Length() + iLiteralLength > iBufferSize) |
|
541 { |
|
542 HBufC8 *oldbuffer=iBuffer; |
|
543 const TText8 *oldbufptr=iBuffer->Ptr(); |
|
544 |
|
545 // Extend by extra amount + round up by KIOBufferGranularity |
|
546 iBufferSize += iLiteralLength; |
|
547 iBufferSize += (KIOBufferGranularity - (iBufferSize % KIOBufferGranularity)); |
|
548 iBuffer=iBuffer->ReAllocL(iBufferSize); |
|
549 |
|
550 // Buffer moved? |
|
551 if (iBuffer!=oldbuffer) |
|
552 { |
|
553 // Fixup buffer tree pointers |
|
554 iRootAtom->FixupL(iBuffer,oldbufptr); |
|
555 } |
|
556 } |
|
557 |
|
558 iBuffer->Des().Append(aLiteralBlock); |
|
559 AddAtomL(); |
|
560 |
|
561 // literal is always followed by a line, so we need to be waiting for the next atom |
|
562 iParserState=EStateAtomWait; |
|
563 } |
|
564 |