|
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 |
|
17 #include "cimapcommand.h" |
|
18 #include "cimapsessionconsts.h" |
|
19 #include "cimapfolderinfo.h" |
|
20 #include "cimaputils.h" |
|
21 #include "cimapcharconv.h" |
|
22 #include "cimaplogger.h" |
|
23 |
|
24 static const TInt KDefaultNumberOfTaggedResponsesExpected = 1; |
|
25 |
|
26 CImapCommand::CImapCommand(CImapFolderInfo* aSelectedFolderData, TInt aLogId) |
|
27 : iSelectedFolderData(aSelectedFolderData) |
|
28 , iLogId(aLogId) |
|
29 {} |
|
30 |
|
31 CImapCommand::~CImapCommand() |
|
32 { |
|
33 delete iOutputBuffer; |
|
34 } |
|
35 |
|
36 void CImapCommand::SendDataCnfL() |
|
37 {} |
|
38 |
|
39 CImapCommand::TParseState CImapCommand::ParseBlockL(const TDesC8& aData) |
|
40 { |
|
41 #ifdef __IMAP_LOGGING |
|
42 // Log the first few bytes only - but enough to tell the difference between untagged FETCH's |
|
43 TPtrC8 truncatedData = aData.Left(30); |
|
44 __LOG_FORMAT((iLogId, "CImapCommand::ParseBlockL(): [%S] %d octets", &truncatedData, aData.Length())); |
|
45 #endif //__IMAP_LOGGING |
|
46 |
|
47 __ASSERT_DEBUG(iParseState != ECommandComplete, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState2)); |
|
48 iUnparsedData.Set(aData); |
|
49 iLiteralBytesExpected = 0; // Initialise to zero, and only peek in states that read a line. |
|
50 |
|
51 switch (iParseState) |
|
52 { |
|
53 case EWaitStartResponse: |
|
54 { |
|
55 if (Flushing()) |
|
56 { |
|
57 ParseStartResponseFlushL(aData); |
|
58 } |
|
59 else |
|
60 { |
|
61 ParseLineL(aData); |
|
62 } |
|
63 break; |
|
64 } |
|
65 case EWaitLineParse: |
|
66 { |
|
67 ParseLineL(aData); |
|
68 } |
|
69 break; |
|
70 case EWaitLiteralParse: |
|
71 { |
|
72 ParseLiteralL(aData); |
|
73 } |
|
74 break; |
|
75 case EWaitLiteralIngore: |
|
76 { |
|
77 __LOG_TEXT(iLogId, "CImapCommand Ignore Literal"); |
|
78 // Ignore the literal. |
|
79 // A literal is always followed by a line (as part of the same response) |
|
80 SetParseState(EWaitLineIgnore); |
|
81 } |
|
82 break; |
|
83 case EWaitLineIgnore: |
|
84 { |
|
85 __LOG_TEXT(iLogId, "CImapCommand Ignore Line following Literal"); |
|
86 // Ignore the line, but check whether it indicates that another literal is on its way. |
|
87 PeekLiteralRequest(aData); |
|
88 |
|
89 TParseState newParseState = (iLiteralBytesExpected > 0) |
|
90 ? EWaitLiteralIngore // a literal has been indicated on the line. We need to ignore that too! |
|
91 : EWaitStartResponse; // we are not expecting any more data for this response, so wait for the next one. |
|
92 SetParseState(newParseState); |
|
93 } |
|
94 break; |
|
95 default: |
|
96 { |
|
97 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState3)); |
|
98 } |
|
99 } |
|
100 |
|
101 if (iCompleteOnAnyResponse && iParseState==EWaitStartResponse) |
|
102 { |
|
103 __LOG_TEXT(iLogId, "CImapCommand - Complete on Any Response"); |
|
104 __ASSERT_DEBUG(iResponseCode == EImapResponseNone, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidResponseCode)); |
|
105 |
|
106 // Simulate command completion so that the session closes this command. |
|
107 iResponseCode = EImapResponseOk; |
|
108 SetParseState(ECommandComplete); |
|
109 } |
|
110 |
|
111 // check some post-conditions: is session getting consistent data? |
|
112 |
|
113 // (iParseState == ECommandComplete) implies (iResponseCode != EImapResponseNone) |
|
114 __ASSERT_DEBUG(iParseState == ECommandComplete ? iResponseCode != EImapResponseNone : ETrue, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandMismatchedParseStateAndResponseCode)); |
|
115 |
|
116 // (iParseState != ECommandComplete) implies (iResponseCode == EImapResponseNone) |
|
117 __ASSERT_DEBUG(iParseState != ECommandComplete ? iResponseCode == EImapResponseNone : ETrue, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandMismatchedParseStateAndResponseCode)); |
|
118 |
|
119 return iParseState; |
|
120 } |
|
121 |
|
122 /** |
|
123 Parses incoming line data. |
|
124 @param aLine the line to parse. |
|
125 */ |
|
126 void CImapCommand::ParseLineL(const TDesC8& aLine) |
|
127 { |
|
128 |
|
129 // Should only be calling this method when parsing a line. |
|
130 __ASSERT_DEBUG(iParseState == EWaitStartResponse || iParseState == EWaitLineParse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState4)); |
|
131 |
|
132 PeekLiteralRequest(aLine); |
|
133 |
|
134 TParseBlockResult parseBlockResult = DoParseBlockL(aLine); |
|
135 |
|
136 if(parseBlockResult == ECompleteUntagged && iLiteralBytesExpected != 0) |
|
137 return; |
|
138 |
|
139 switch (parseBlockResult) |
|
140 { |
|
141 case ECompleteTagged: |
|
142 // No more data expected |
|
143 __ASSERT_DEBUG(iLiteralBytesExpected == 0, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandResponseLiteralDataNotZero)); |
|
144 __ASSERT_DEBUG(iResponseCode != EImapResponseNone, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidResponseCode)); |
|
145 |
|
146 SetParseState(ECommandComplete); |
|
147 break; |
|
148 case ECompleteUntagged: |
|
149 // Wait for the next response line to be received |
|
150 __ASSERT_DEBUG(iLiteralBytesExpected == 0, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandResponseLiteralDataNotZero)); |
|
151 |
|
152 SetParseState(EWaitStartResponse); |
|
153 break; |
|
154 case EResponseIncomplete: |
|
155 // Literal data is expected - this needs to be delivered to the subclass |
|
156 __ASSERT_DEBUG(iLiteralBytesExpected > 0, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandResponseLiteralDataNotNonZero)); |
|
157 |
|
158 SetParseState(EWaitLiteralParse); |
|
159 break; |
|
160 case ENotRecognised: |
|
161 if (iParseState == EWaitLineParse) |
|
162 { |
|
163 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState5)); |
|
164 } |
|
165 else // EWaitStartResponse |
|
166 { |
|
167 // The line was not understood by the subclass, so try the generic parsing |
|
168 parseBlockResult = ParseUnhandledBlockL(aLine); |
|
169 __ASSERT_DEBUG(parseBlockResult != EResponseIncomplete, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseBlockResult)); // Not expected or currently supported |
|
170 |
|
171 // If we're swallowing an unsupported response, then we may need to swallow a literal block next |
|
172 TParseState newParseState = (iLiteralBytesExpected > 0) |
|
173 ? EWaitLiteralIngore // a literal has been indicated on the line. We need to ignore that too! |
|
174 : EWaitStartResponse; // we are not expecting any more data for this response |
|
175 SetParseState(newParseState); |
|
176 } |
|
177 break; |
|
178 default: |
|
179 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseBlockResult)); |
|
180 break; |
|
181 } |
|
182 } |
|
183 |
|
184 /** |
|
185 Parses incoming literal data. |
|
186 @param aLiteralBlock the literal block to parse. |
|
187 */ |
|
188 void CImapCommand::ParseLiteralL(const TDesC8& aLiteralBlock) |
|
189 { |
|
190 // Should only be calling this method when parsing a literal blcok. |
|
191 __ASSERT_DEBUG(iParseState == EWaitLiteralParse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState6)); |
|
192 |
|
193 TParseBlockResult parseBlockResult = DoParseBlockL(aLiteralBlock); |
|
194 switch (parseBlockResult) |
|
195 { |
|
196 case ECompleteTagged: |
|
197 case ECompleteUntagged: |
|
198 // Literals are always followed by a line, so the response can't be complete yet. |
|
199 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseBlockResult)); |
|
200 break; |
|
201 case EResponseIncomplete: |
|
202 SetParseState(EWaitLineParse); |
|
203 break; |
|
204 case ENotRecognised: |
|
205 // Subclass asked for the literal block, so it ought to have parsed it. |
|
206 // Fall through to default __ASSERT_DEBUG below. |
|
207 default: |
|
208 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseBlockResult)); |
|
209 break; |
|
210 } |
|
211 } |
|
212 |
|
213 /** |
|
214 During cancel this method is called whenever the start of a new response is received. |
|
215 It doesn't do any parsing, other than to detect the closing tagged response, |
|
216 which completes the command and means there is no more data to receive. |
|
217 @param aLine the line containing the start of a new response. |
|
218 */ |
|
219 void CImapCommand::ParseStartResponseFlushL(const TDesC8& aLine) |
|
220 { |
|
221 __LOG_TEXT(iLogId, "CImapCommand::ParseStartResponseFlushL()"); |
|
222 |
|
223 // should only call this method when parsing an incoming response but not cancelling |
|
224 __ASSERT_DEBUG(iParseState == EWaitStartResponse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState7)); |
|
225 __ASSERT_DEBUG(iTaggedResponsesToFlush > 0, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandNoTaggedResponsesToFlush)); |
|
226 |
|
227 TInt tagId = 0; |
|
228 switch (GetTagTypeL(tagId)) |
|
229 { |
|
230 case ETagged: |
|
231 { |
|
232 --iTaggedResponsesToFlush; |
|
233 if (iTaggedResponsesToFlush == 0) |
|
234 { |
|
235 // Don't check the tagId during cancel. |
|
236 iResponseCode = GetResponseStateCode(); |
|
237 SetParseState(ECommandComplete); |
|
238 } |
|
239 else |
|
240 { |
|
241 __LOG_FORMAT((iLogId, "CImapCommand - Still have %d tagged response(s) to flush", iTaggedResponsesToFlush)); |
|
242 } |
|
243 } |
|
244 break; |
|
245 case EUntagged: |
|
246 { |
|
247 // Ignore the untagged response, but check whether it indicates that another literal is on its way. |
|
248 PeekLiteralRequest(aLine); |
|
249 TParseState newParseState = (iLiteralBytesExpected > 0) |
|
250 ? EWaitLiteralIngore // a literal has been indicated on the line. We need to ignore that too! |
|
251 : EWaitStartResponse; // we are not expecting any more data for this response, so wait for the next one. |
|
252 SetParseState(newParseState); |
|
253 } |
|
254 break; |
|
255 case EContinuation: |
|
256 { |
|
257 // Not exepecting a continuation response |
|
258 CorruptDataL(); |
|
259 } |
|
260 break; |
|
261 } |
|
262 } |
|
263 |
|
264 /** |
|
265 Looks for {nnn} at the end of the line, determine whether literal bytes are expected. |
|
266 If literal bytes are expected, then iLiteralBytesExpected is updated to store the |
|
267 number of bytes expected. |
|
268 @return KErrNone if literal bytes are expected, otherwise a system-wide error code. |
|
269 */ |
|
270 TInt CImapCommand::PeekLiteralRequest(const TDesC8& aLine) |
|
271 { |
|
272 // Minimum of 3 chars needed for literal specifier: {n} |
|
273 TInt lineLength = aLine.Length(); |
|
274 if (lineLength < 3) |
|
275 { |
|
276 return KErrNotFound; // no request found. |
|
277 } |
|
278 |
|
279 // Last char MUST be a } |
|
280 if (aLine[lineLength-1] != '}') |
|
281 { |
|
282 return KErrNotFound; // no request found. |
|
283 } |
|
284 |
|
285 // Reverse-Find matching '{' |
|
286 TPtrC8 ptrLiteralRequest; |
|
287 for (TInt i=lineLength-1; i>=0; --i) |
|
288 { |
|
289 if (aLine[i] == '{') |
|
290 { |
|
291 // Found matching brace. Point at its contents only! |
|
292 ptrLiteralRequest.Set(aLine.Mid(i+1, lineLength-i-2)); |
|
293 break; // from for loop |
|
294 } |
|
295 } |
|
296 |
|
297 if (ptrLiteralRequest.Length() == 0) |
|
298 { |
|
299 return KErrNotFound; // No matching brace found |
|
300 } |
|
301 |
|
302 // Extract the value |
|
303 TLex8 lex(ptrLiteralRequest); |
|
304 TInt err = lex.Val(iLiteralBytesExpected); |
|
305 #if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) |
|
306 // If message body is downloaded using FETCH BINARY. The number of bytes received for previous fetch is recorded here. |
|
307 // This valued is used to make sure that there are no extra body fetch command are issued |
|
308 iPreviousLiteralBytesReceived = iLiteralBytesExpected; |
|
309 #endif |
|
310 |
|
311 #ifdef __IMAP_LOGGING |
|
312 if (iLiteralBytesExpected > 0) |
|
313 { |
|
314 __LOG_FORMAT((iLogId, "CImapCommand - Found Literal Request: %d", iLiteralBytesExpected)); |
|
315 } |
|
316 #endif //__IMAP_LOGGING |
|
317 |
|
318 return err; |
|
319 } |
|
320 |
|
321 /** |
|
322 This is called when the session has processed all the blocks in its input buffer, but the |
|
323 command is not yet complete (so it is expecting more data). |
|
324 This allows subclasses of CImapCommand to commit any bulk operations while waiting for the next set of data. |
|
325 By default, this method does nothing. |
|
326 */ |
|
327 void CImapCommand::WaitingForMoreDataL() |
|
328 {} |
|
329 |
|
330 /** |
|
331 Call this method to stop fully parsing the command. |
|
332 Instead, discard all incoming server data until no more data is expected for this command. |
|
333 By default this means that the tagged response has been received. |
|
334 This method can be overridden. |
|
335 */ |
|
336 void CImapCommand::Cancel() |
|
337 { |
|
338 __LOG_TEXT(iLogId, "CImapCommand::Cancel()"); |
|
339 EnterFlushingState(); |
|
340 } |
|
341 |
|
342 void CImapCommand::EnterFlushingState() |
|
343 { |
|
344 iFlushing = ETrue; |
|
345 iTaggedResponsesToFlush = NumberOfTaggedResponsesExpected(); |
|
346 |
|
347 __LOG_FORMAT((iLogId, "CImapCommand::EnterFlushingState() - %d tagged response(s) to flush", iTaggedResponsesToFlush)); |
|
348 |
|
349 if (iParseState == EWaitLiteralParse) |
|
350 { |
|
351 SetParseState(EWaitLiteralIngore); |
|
352 } |
|
353 else if (iParseState == EWaitLineParse) |
|
354 { |
|
355 SetParseState(EWaitLineIgnore); |
|
356 } |
|
357 } |
|
358 |
|
359 TBool CImapCommand::Flushing() const |
|
360 { |
|
361 return iFlushing; |
|
362 } |
|
363 |
|
364 /** |
|
365 This method tells the session whether the Flush operation is able to complete |
|
366 the command straight away. |
|
367 By default, a command that has not already been completed (and so destoyed) |
|
368 cannot be completed early by Flush, as it is still waiting for the tagged |
|
369 response. |
|
370 This method can be overridden by commands that can be completed earlier, such |
|
371 as commands that do not wait for a tagged response. |
|
372 @return EFalse. |
|
373 */ |
|
374 TBool CImapCommand::CanCompleteFlushNow() const |
|
375 { |
|
376 // Any command that has completed should have been destroyed before this |
|
377 // method is called. |
|
378 __ASSERT_DEBUG(iParseState != ECommandComplete, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState8)); |
|
379 __ASSERT_DEBUG(Flushing(), TImapServerPanic::ImapPanic(TImapServerPanic::ECommandNotFlushing)); |
|
380 |
|
381 return EFalse; |
|
382 } |
|
383 |
|
384 /** |
|
385 Returns the number of tagged responses that are currently expected. |
|
386 This default implementation always returns one, as most commands on send one request |
|
387 and expect one tagged response. |
|
388 Pipelined commands that send more than one request at a time should override this method. |
|
389 @return one. |
|
390 */ |
|
391 TInt CImapCommand::NumberOfTaggedResponsesExpected() const |
|
392 { |
|
393 return KDefaultNumberOfTaggedResponsesExpected; |
|
394 } |
|
395 |
|
396 /** |
|
397 This API will return the result returned by the remote server to the issued IMAP command. |
|
398 |
|
399 @return The IMAP result code extracted from the incoming response or EImapResponseNone |
|
400 if the response has not yet been parsed. |
|
401 */ |
|
402 CImapCommand::TResponseCode CImapCommand::ResponseCode() const |
|
403 { |
|
404 return iResponseCode; |
|
405 } |
|
406 |
|
407 /** |
|
408 @return the number of bytes |
|
409 */ |
|
410 TInt CImapCommand::LiteralBytesExpected() const |
|
411 { |
|
412 return iLiteralBytesExpected; |
|
413 } |
|
414 |
|
415 /** |
|
416 Static method that should a be called by all classes internal to CImapSession whenever they |
|
417 detect corrupt data - e.g. missing fields - that means that they can no longer process the |
|
418 incoming data. |
|
419 |
|
420 This method will always leave with KErrCorrupt. |
|
421 */ |
|
422 #ifdef __IMAP_LOGGING |
|
423 void CImapCommand::CorruptDataL(TInt aLogId) |
|
424 #else |
|
425 void CImapCommand::CorruptDataL(TInt /*aLogId*/) |
|
426 #endif //__IMAP_LOGGING |
|
427 // static method |
|
428 { |
|
429 __LOG_TEXT(aLogId, "CImapCommand::CorruptDataL() !!!"); |
|
430 User::Leave(KErrImapCorrupt); |
|
431 } |
|
432 |
|
433 /** |
|
434 Non-static version of CorruptDataL() that provides the current log id |
|
435 */ |
|
436 void CImapCommand::CorruptDataL() |
|
437 { |
|
438 CorruptDataL(iLogId); |
|
439 } |
|
440 |
|
441 /** |
|
442 Static method that stores each part of the supplied data into an array based on |
|
443 the delimiter value supplied in the aDelimiter argument. |
|
444 |
|
445 @param aDelimiter The character that delimits the end of one part and the begining of the next. |
|
446 @param aData The data that is to be split. |
|
447 @param aOutDelimitedPartsList Output array that recieves the list of parts. |
|
448 @param aMaxParts If greater than zero, this stops the search when aOutDelimitedPartsList |
|
449 has the specified number of parts. |
|
450 */ |
|
451 void CImapCommand::GetDelimitedPartsL(TChar aDelimiter, const TDesC8& aData, RDesParts& aOutDelimitedPartsList, TInt aMaxParts /*=0*/) |
|
452 // static method |
|
453 { |
|
454 aOutDelimitedPartsList.Reset(); |
|
455 TInt dataLength = aData.Length(); |
|
456 |
|
457 if (dataLength == 0) |
|
458 { |
|
459 // no data to parse, so nothing to do. |
|
460 return; |
|
461 } |
|
462 |
|
463 TInt currentPos = 0; |
|
464 TPtrC8 currentSegment(aData); |
|
465 |
|
466 for(;;) |
|
467 { |
|
468 TInt found = currentSegment.Locate(aDelimiter); |
|
469 if (found == KErrNotFound) |
|
470 { |
|
471 aOutDelimitedPartsList.AppendL(currentSegment); |
|
472 break; |
|
473 } |
|
474 |
|
475 currentSegment.Set(currentSegment.Left(found)); |
|
476 aOutDelimitedPartsList.AppendL(currentSegment); |
|
477 |
|
478 // Check if whether have enough parts yet |
|
479 if (aMaxParts > 0 && aOutDelimitedPartsList.Count() >= aMaxParts) |
|
480 { |
|
481 break; |
|
482 } |
|
483 |
|
484 // Move past the delimiter and set next segment |
|
485 currentPos += (found+1); |
|
486 if (currentPos<dataLength) |
|
487 { |
|
488 currentSegment.Set(aData.Mid(currentPos)); |
|
489 } |
|
490 else |
|
491 { |
|
492 // No more data |
|
493 break; |
|
494 } |
|
495 } |
|
496 } |
|
497 |
|
498 /** |
|
499 Static method that calculates the length in characters of the given tag id |
|
500 |
|
501 @param aTagId The tag id whose length in characters is to be calculated |
|
502 @return The length in characters of the given tag id |
|
503 */ |
|
504 TInt CImapCommand::TagLength(TInt aTagId) |
|
505 // static method |
|
506 { |
|
507 // Count how many characters would be needed to represent aTagId as a string. |
|
508 // Do this by count how many times aTagId can be divided by 10. |
|
509 |
|
510 TInt tagLength = 1; // values 0 to 9 have a length of 1 character |
|
511 |
|
512 while (aTagId > 9) |
|
513 { |
|
514 aTagId = aTagId / 10; |
|
515 ++tagLength; |
|
516 } |
|
517 |
|
518 return tagLength; |
|
519 } |
|
520 |
|
521 /** |
|
522 Encodes a Unicode mailbox name by: |
|
523 - Converting it into ImapUtf7 format |
|
524 - Quoting it if necessary |
|
525 @param aMailboxName the mailbox name in Unicode format. |
|
526 @return the mailbox name in ImapUtf7, quoted if necessary. |
|
527 */ |
|
528 HBufC8* CImapCommand::EncodeMailboxNameForSendL(const TDesC16& aMailboxName) |
|
529 // static method |
|
530 { |
|
531 // According to section 9 of RFC3501, mailbox is an astring. |
|
532 // |
|
533 // That means that if it cannot be represented as an atom, then it must be |
|
534 // represented as either a quoted string or literal. |
|
535 // We can rule out literal as explained below. |
|
536 // |
|
537 // An atom is a string that contains any 7-bit character other than "atom-specials" |
|
538 // |
|
539 // atom-specials consists of |
|
540 // |
|
541 // "(" ")" "{" "%" "*" DQUOTE "\" "]" SP CTL |
|
542 // |
|
543 // |
|
544 // The ImapUtf7 encoding has the following characteristics (from RFC3501). |
|
545 // |
|
546 // "printable US-ASCII characters, except for "&", represent |
|
547 // themselves; that is, characters with octet values 0x20-0x25 and 0x27-0x7e. |
|
548 // All other characters (octet values 0x00-0x1f and 0x7f-0xff) are |
|
549 // represented in modified BASE64, with a further modification from |
|
550 // [UTF-7] that "," is used instead of "/". Modified BASE64 MUST NOT be |
|
551 // used to represent any printing US-ASCII character which can represent |
|
552 // itself."" |
|
553 // |
|
554 // "&" means start "Modified BASE64" block |
|
555 // "-" means end "Modified BASE64" block |
|
556 // "&-" represents the & character |
|
557 // |
|
558 // "Modified BASE64" can contain the following characters |
|
559 // "a" to "z", "A" to "Z", "0" to "9" and then "+" "," and "=". The last is the pad character. |
|
560 // |
|
561 // |
|
562 // So an Imap-UTF7 mailbox string can always be sent as quoted (and hence no need for literal) |
|
563 // because it conforms to the RFC3501 definition of a quoted string, which |
|
564 // may only contain 7-bit US-ASCII characters other than NUL, CR and LF (which are encoded as Modified BASE64) |
|
565 // |
|
566 // |
|
567 // To decide whether to send the encoded mailbox as an atom or as quoted, we need to test whether |
|
568 // the encoded string contains printable atom-specials (CTL characters including CR and LF have |
|
569 // already been dealt with by the ImapUtf7 encoding) |
|
570 // |
|
571 // Usefully, the set of atom-specials and the set of characters used in Modified BASE64 |
|
572 // DO NOT intersect. That makes it safe to search for (and escape) atom-specials in an |
|
573 // encoded ImapUtf7 string without inadvertantly finding or modifying a BASE64 encoding. |
|
574 // |
|
575 // Note that section 5.1 of RFC3501 allows mailbox names to include all the atom-specials, including |
|
576 // list wildcards such as "*" and "%". So we need to search for all printable atom-specials, not a subset. |
|
577 |
|
578 if (aMailboxName.Length()==0) |
|
579 { |
|
580 // aMailboxName is an empty string so just return an empty string. |
|
581 _LIT8(KEmptyString, "\"\""); |
|
582 HBufC8* hBuf = KEmptyString().AllocL(); |
|
583 return hBuf; |
|
584 } |
|
585 |
|
586 // Convert into ImapUtf7. |
|
587 HBufC8* mailboxNameUtf7 = CImapUtils::GetRef().Charconv().ConvertFromUnicodeToImapUtf7L(aMailboxName); |
|
588 CleanupStack::PushL(mailboxNameUtf7); |
|
589 |
|
590 // Search for printable atom-specials and quote the mailbox if any are found |
|
591 TInt countQuotedSpecials = 0; |
|
592 if (CheckForPrintableAtomSpecial(*mailboxNameUtf7, countQuotedSpecials)) |
|
593 { |
|
594 // Make enough room to escape each of the quoted-specials and to insert the start and end quote |
|
595 TInt mailboxNameLength = mailboxNameUtf7->Length() + countQuotedSpecials + 2; |
|
596 HBufC8 *expandedBuffer = mailboxNameUtf7->ReAllocL(mailboxNameLength); |
|
597 |
|
598 // Been moved due to realloc? |
|
599 if (expandedBuffer != mailboxNameUtf7) |
|
600 { |
|
601 // Update mailboxNameUtf7 with the new address |
|
602 mailboxNameUtf7 = expandedBuffer; |
|
603 expandedBuffer = NULL; |
|
604 |
|
605 // Update the cleanup stack with the new address of mailboxNameUtf7 |
|
606 CleanupStack::Pop(); |
|
607 CleanupStack::PushL(mailboxNameUtf7); |
|
608 } |
|
609 |
|
610 // Insert the opening DQUOTE |
|
611 mailboxNameUtf7->Des().Insert(0, KImapTxtDoubleQuote); |
|
612 |
|
613 // Perform escaping if there are any quoted-specials |
|
614 if (countQuotedSpecials > 0) |
|
615 { |
|
616 // Use the length of the newly expanded buffer, |
|
617 // but do not include the first or last characters, which are non-escaped DQUOTE's |
|
618 // Hence start at index 1, and reduce the length by 2 |
|
619 mailboxNameLength -= 2; |
|
620 for(TInt i=1; i<mailboxNameLength; ++i) |
|
621 { |
|
622 if ((*mailboxNameUtf7)[i]=='\\' || (*mailboxNameUtf7)[i]=='\"') |
|
623 { |
|
624 // We have found one of the quoted-specials, this needs to be escaped |
|
625 mailboxNameUtf7->Des().Insert(i, KImapTxtEscape); |
|
626 ++i; |
|
627 } |
|
628 } |
|
629 } |
|
630 |
|
631 // Append the closing DQUOTE |
|
632 mailboxNameUtf7->Des().Append(KImapTxtDoubleQuote); |
|
633 } |
|
634 |
|
635 CleanupStack::Pop(mailboxNameUtf7); |
|
636 return mailboxNameUtf7; |
|
637 } |
|
638 |
|
639 /** |
|
640 Checks whether the given string contains printable atom-specials. |
|
641 An atom special is one of "(" ")" "{" "%" "*" DQUOTE "\" "]" SP |
|
642 @param aString The string that is to be checked |
|
643 @param aCountQuotedSpecials Output parameter that counts the number of quoted-specials characters that need escaping |
|
644 @return ETrue is any of the atom-specials are present in the string |
|
645 */ |
|
646 TBool CImapCommand::CheckForPrintableAtomSpecial(const TDesC8& aString, TInt& aCountQuotedSpecials) |
|
647 // static method |
|
648 { |
|
649 aCountQuotedSpecials = 0; |
|
650 TBool foundPrintableAtomSpecial = EFalse; |
|
651 |
|
652 TInt stringLength = aString.Length(); |
|
653 for(TInt i=0; i<stringLength; ++i) |
|
654 { |
|
655 if (aString[i]=='(' || aString[i]==')' || aString[i]=='{' || aString[i]==' ' || // <<< specific printable atom-specials |
|
656 aString[i]=='%' || aString[i]=='*' || // <<< list-wildcards |
|
657 aString[i]==']') // <<< resp-specials |
|
658 { |
|
659 foundPrintableAtomSpecial = ETrue; |
|
660 } |
|
661 else if (aString[i]=='\\' || aString[i]=='\"') // <<< quoted-specials |
|
662 { |
|
663 foundPrintableAtomSpecial = ETrue; |
|
664 ++aCountQuotedSpecials; |
|
665 } |
|
666 } |
|
667 |
|
668 return foundPrintableAtomSpecial; |
|
669 } |
|
670 |
|
671 /** |
|
672 Helper that is to be called by subclasses when they receive some data that is not specific to the class. |
|
673 This indicates that the data is probably unilateral notification data, such as EXISTS and RECENT |
|
674 This method will parse and handle the notification. |
|
675 @param aLine The line to be parsed and handled (not including CRLF). |
|
676 @return Whether the data was successfully parsed, recognised, etc |
|
677 */ |
|
678 CImapCommand::TParseBlockResult CImapCommand::ParseUnhandledBlockL(const TDesC8& aLine) |
|
679 { |
|
680 TParseBlockResult result = ENotRecognised; |
|
681 |
|
682 iUnparsedData.Set(aLine); |
|
683 |
|
684 // Check the tag type. This method should only be called for untagged resposnes. |
|
685 TInt tagId = 0; |
|
686 TTagType tagType = GetTagTypeL(tagId); |
|
687 __ASSERT_DEBUG(tagType == EUntagged, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidTagType)); |
|
688 |
|
689 // Looking for one of |
|
690 // |
|
691 // number "EXISTS" |
|
692 // number "RECENT" |
|
693 // nz-number "EXPUNGE" |
|
694 // |
|
695 // Note that untagged "BYE" is ignored. |
|
696 // This can occur during LOGOUT or prior to the server terminating the connection. |
|
697 // For LOGOUT, we wait for the tagged OK. |
|
698 // And transport handler detects server termination - so we don't need to respond to the early warning. |
|
699 |
|
700 TPtrC8 numberPart = GetNextPart(); |
|
701 TLex8 desToInt(numberPart); |
|
702 |
|
703 TInt messageCountOrId = 0; // This is a message count for all except EXPUNGE where it is a message id |
|
704 TInt err = desToInt.Val(messageCountOrId); |
|
705 |
|
706 if (err == KErrNone) |
|
707 { |
|
708 TPtrC8 secondPart = GetNextPart(); |
|
709 |
|
710 if((secondPart.CompareF(KImapTxtExpunge) == 0)) |
|
711 { |
|
712 __LOG_FORMAT((iLogId, "CImapCommand::ParseUnhandledBlockL() - Found EXPUNGE: %d", messageCountOrId)); |
|
713 if (iSelectedFolderData != NULL && iSelectedFolderData->Exists()) |
|
714 { |
|
715 iSelectedFolderData->AddExpungedMessageL(messageCountOrId); |
|
716 } |
|
717 result = ECompleteUntagged; |
|
718 } |
|
719 else if((secondPart.CompareF(KImapTxtExists) == 0)) |
|
720 { |
|
721 __LOG_FORMAT((iLogId, "CImapCommand::ParseUnhandledBlockL() - Found EXISTS: %d", messageCountOrId)); |
|
722 if (iSelectedFolderData != NULL) |
|
723 { |
|
724 iSelectedFolderData->SetExists(messageCountOrId); |
|
725 } |
|
726 result = ECompleteUntagged; |
|
727 } |
|
728 else if((secondPart.CompareF(KImapTxtRecent) == 0)) |
|
729 { |
|
730 __LOG_FORMAT((iLogId, "CImapCommand::ParseUnhandledBlockL() - Found RECENT: %d", messageCountOrId)); |
|
731 if (iSelectedFolderData != NULL) |
|
732 { |
|
733 iSelectedFolderData->SetRecent(messageCountOrId); |
|
734 } |
|
735 result = ECompleteUntagged; |
|
736 } |
|
737 else if ((secondPart.CompareF(KImapTxtFetch) == 0)) |
|
738 { |
|
739 // If a FETCH has been requested, then the FETCH response will be handled by the subclass. |
|
740 // So getting here means that the FETCH was unsolicited. |
|
741 // When means that it is LIKELY that this is a FETCH FLAGS response. |
|
742 // We don't need to parse the resposne fully (a complex task), but instead, |
|
743 // just record the notification that flags have changed for a message. |
|
744 // This will cause a resync of flags when the Protocol Controller next attempts to go IDLE. |
|
745 __LOG_FORMAT((iLogId, "CImapCommand::ParseUnhandledBlockL() - Found unsolicited FETCH: %d", messageCountOrId)); |
|
746 SetMessageFlagsChanged(); |
|
747 result = ECompleteUntagged; |
|
748 } |
|
749 } |
|
750 |
|
751 #ifdef __IMAP_LOGGING |
|
752 if (result == ENotRecognised) |
|
753 { |
|
754 __LOG_TEXT(iLogId, "CImapCommand::ParseUnhandledBlockL() - Ignoring Unhandled Response"); |
|
755 } |
|
756 #endif //__IMAP_LOGGING |
|
757 |
|
758 return result; |
|
759 } |
|
760 |
|
761 /** |
|
762 Helper function to return the tag of the response |
|
763 @param aTagId Will contain the Tag if response is tagged. |
|
764 @return Returns the tag type as an enumeration. |
|
765 */ |
|
766 CImapCommand::TTagType CImapCommand::GetTagTypeL(TInt& aTagId) |
|
767 { |
|
768 TPtrC8 nextPart = GetNextPart() ; |
|
769 if(nextPart.Find(KImapTxtContinuation) >= 0) |
|
770 { |
|
771 __LOG_TEXT(iLogId, "CImapCommand::GetTagTypeL() - Found Continuation"); |
|
772 return EContinuation ; |
|
773 } |
|
774 else if(nextPart.Find(KImapTxtUntagged) >= 0) |
|
775 { |
|
776 // Don't log this, as it's the most common, and causes bloat! |
|
777 return EUntagged; |
|
778 } |
|
779 else |
|
780 { |
|
781 TLex8 desToInt(nextPart); |
|
782 if (desToInt.Val(aTagId) != KErrNone) |
|
783 { |
|
784 // We were expecting a numeric tag id here. |
|
785 __LOG_TEXT(iLogId, "CImapCommand::GetTagTypeL() - Non-numeric tag id"); |
|
786 CorruptDataL(); |
|
787 } |
|
788 __LOG_FORMAT((iLogId, "CImapCommand::GetTagTypeL() - Found Tagged: %d", aTagId)); |
|
789 } |
|
790 return ETagged; |
|
791 } |
|
792 |
|
793 /** |
|
794 Helper method to return the next part of the response from server. |
|
795 @return nextpart from the iUnparsedData |
|
796 KNullDesC8 if no more data to return. |
|
797 */ |
|
798 TPtrC8 CImapCommand::GetNextPart() |
|
799 { |
|
800 TPtrC8 nextPart = iUnparsedData; |
|
801 |
|
802 // If the first character is a space, then skip it. |
|
803 if (iUnparsedData.Length() > 0) |
|
804 { |
|
805 if (iUnparsedData[0] == ' ') |
|
806 { |
|
807 iUnparsedData.Set(iUnparsedData.Mid(1)); |
|
808 } |
|
809 } |
|
810 |
|
811 if (iUnparsedData.Length() > 0) |
|
812 { |
|
813 TInt length = iUnparsedData.Length(); |
|
814 // Some server may send n FETCH (UID n BODY[1]<0> "Message Body" BODY[1.MIME] {nnn} |
|
815 // So extract the body from this response. |
|
816 if(iUnparsedData[0] == '"') |
|
817 { |
|
818 for (TInt i=length-1; i>=1; --i) |
|
819 { |
|
820 if (iUnparsedData[i] == '"') |
|
821 { |
|
822 // Found matching brace. Point at its contents only! |
|
823 nextPart.Set(iUnparsedData.Mid(0, i + 1)); |
|
824 iUnparsedData.Set(iUnparsedData.Mid(i+1)); |
|
825 if(iUnparsedData.Length() > 0) |
|
826 { |
|
827 if (iUnparsedData[0] == ' ') |
|
828 { |
|
829 iUnparsedData.Set(iUnparsedData.Mid(1)); |
|
830 } |
|
831 } |
|
832 break; // from for loop |
|
833 } |
|
834 } |
|
835 } |
|
836 else |
|
837 { |
|
838 TInt offset = iUnparsedData.Locate(' '); |
|
839 if (offset != KErrNotFound) |
|
840 { |
|
841 nextPart.Set(iUnparsedData.Left(offset)); |
|
842 if(iUnparsedData.Length() > offset + 1) |
|
843 { |
|
844 iUnparsedData.Set(iUnparsedData.Mid(offset + 1)); |
|
845 } |
|
846 else |
|
847 { |
|
848 iUnparsedData.Set(KNullDesC8); |
|
849 } |
|
850 } |
|
851 else |
|
852 { |
|
853 iUnparsedData.Set(KNullDesC8); |
|
854 } |
|
855 } |
|
856 } |
|
857 return nextPart; |
|
858 } |
|
859 |
|
860 /** |
|
861 Helper method to return the next part of the response from server. |
|
862 The Unparsed Data pointer is not updated by this routine |
|
863 @return nextpart from the iUnparsedData |
|
864 KNullDesC8 if no more data to return. |
|
865 */ |
|
866 TPtrC8 CImapCommand::PeekNextPart() |
|
867 { |
|
868 TPtrC8 nextPart = iUnparsedData; |
|
869 |
|
870 while ((nextPart.Length() > 0) && (nextPart[0] == ' ')) |
|
871 { |
|
872 nextPart.Set(nextPart.Mid(1)); |
|
873 } |
|
874 |
|
875 if (nextPart.Length() > 0) |
|
876 { |
|
877 TInt offset = nextPart.Locate(' '); |
|
878 if (offset != KErrNotFound) |
|
879 { |
|
880 nextPart.Set(nextPart.Left(offset)); |
|
881 } |
|
882 } |
|
883 else |
|
884 { |
|
885 nextPart.Set(KNullDesC8); |
|
886 } |
|
887 |
|
888 return nextPart; |
|
889 } |
|
890 |
|
891 /** |
|
892 Remainder() |
|
893 @return the remaining unparsed data |
|
894 */ |
|
895 TPtrC8 CImapCommand::Remainder() |
|
896 { |
|
897 return iUnparsedData; |
|
898 } |
|
899 |
|
900 /** |
|
901 Helper method to parse the "["resp-text-code"]" type response from server. |
|
902 iUnparsedData is updated to next part of response only if [] is found. |
|
903 @return resp-text-code without the square brackets([]). |
|
904 KNullDesC8 if "[" not found. |
|
905 */ |
|
906 TPtrC8 CImapCommand::GetResponseTextCodeL() |
|
907 { |
|
908 if(iUnparsedData.Length()==0) |
|
909 { |
|
910 iUnparsedData.Set(KNullDesC8); |
|
911 return iUnparsedData; |
|
912 } |
|
913 |
|
914 TPtrC8 response = iUnparsedData; |
|
915 if(response[0] == '[') |
|
916 { |
|
917 TInt offset = iUnparsedData.Locate(']'); |
|
918 // No closing "]" ,possibly data may be corrupt. |
|
919 if(offset== KErrNotFound) |
|
920 { |
|
921 __LOG_TEXT(iLogId, "CImapCommand::GetResponseTextCodeL() - No Closing ']'"); |
|
922 CorruptDataL(); |
|
923 } |
|
924 |
|
925 response.Set(iUnparsedData.Mid(1,offset-1)); |
|
926 if (iUnparsedData.Length() > offset + 1) |
|
927 { |
|
928 // Move to next non-space part after "]". |
|
929 iUnparsedData.Set(iUnparsedData.Mid(offset+1)); |
|
930 } |
|
931 else |
|
932 { |
|
933 iUnparsedData.Set(KNullDesC8); |
|
934 } |
|
935 |
|
936 __LOG_FORMAT((iLogId, "CImapCommand::GetResponseTextCodeL() - Found %S", &response)); |
|
937 } |
|
938 |
|
939 //Case where there is no [ ] for Server Response from servers like tuukka |
|
940 //(ie) * OK PERMANENTFLAGS (\seen \answered \flagged \deleted \draft \priority) |
|
941 //we would be returning iUnparsedData, without editing the data |
|
942 |
|
943 return response; |
|
944 } |
|
945 |
|
946 /** |
|
947 Helper method to return the state of the server response. |
|
948 iUnparsedData is updated to next part of response only if OK/NO/BAD is found. |
|
949 @return EImapResponseOk if OK is returned by server |
|
950 EImapResponseNo if NO is returned by server |
|
951 EImapResponseBad if BAD is returned by server |
|
952 EImapResponseNone in all other cases. |
|
953 */ |
|
954 CImapCommand::TResponseCode CImapCommand::GetResponseStateCode() |
|
955 { |
|
956 TResponseCode result = EImapResponseNone; |
|
957 TInt offset = 0; |
|
958 TBool update = EFalse; |
|
959 offset = iUnparsedData.Locate(' '); |
|
960 |
|
961 TPtrC8 nextPart = iUnparsedData; |
|
962 if (offset != KErrNotFound) |
|
963 { |
|
964 nextPart.Set(iUnparsedData.Left(offset)); |
|
965 } |
|
966 |
|
967 if(nextPart.CompareF(KImapTxtOk) == 0) |
|
968 { |
|
969 __LOG_TEXT(iLogId, "CImapCommand::GetResponseStateCode() - Found \"OK\""); |
|
970 result = EImapResponseOk ; |
|
971 update = ETrue; |
|
972 } |
|
973 else if(nextPart.CompareF(KImapTxtNo) == 0) |
|
974 { |
|
975 __LOG_TEXT(iLogId, "CImapCommand::GetResponseStateCode() - Found \"NO\""); |
|
976 result = EImapResponseNo; |
|
977 update = ETrue; |
|
978 } |
|
979 else if(nextPart.CompareF(KImapTxtBad) == 0) |
|
980 { |
|
981 __LOG_TEXT(iLogId, "CImapCommand::GetResponseStateCode() - Found \"BAD\""); |
|
982 result = EImapResponseBad; |
|
983 update = ETrue; |
|
984 } |
|
985 |
|
986 if(update) |
|
987 { |
|
988 if(iUnparsedData.Length() > offset + 1) |
|
989 { |
|
990 iUnparsedData.Set(iUnparsedData.Mid(offset+1)); |
|
991 } |
|
992 else |
|
993 { |
|
994 iUnparsedData.Set(KNullDesC8); |
|
995 } |
|
996 |
|
997 } |
|
998 return result; |
|
999 } |
|
1000 |
|
1001 void CImapCommand::SetParseState(TParseState aParseState) |
|
1002 { |
|
1003 if (aParseState == iParseState) |
|
1004 { |
|
1005 // Nothing to do. |
|
1006 return; |
|
1007 } |
|
1008 |
|
1009 #ifdef __IMAP_LOGGING |
|
1010 |
|
1011 _LIT8(KTxtWaitStartResponse, "EWaitStartResponse"); |
|
1012 _LIT8(KTxtWaitLiteralParse, "EWaitLiteralParse"); |
|
1013 _LIT8(KTxtWaitLineParse, "EWaitLineParse"); |
|
1014 _LIT8(KTxtWaitLiteralIngore, "EWaitLiteralIngore"); |
|
1015 _LIT8(KTxtWaitLineIgnore, "EWaitLineIgnore"); |
|
1016 _LIT8(KTxtCommandComplete, "ECommandComplete"); |
|
1017 _LIT8(KTxtParseStateUnknown, "Unknown"); |
|
1018 |
|
1019 TPtrC8 ptrOldCommandParseState(KTxtParseStateUnknown); |
|
1020 TPtrC8 ptrNewCommandParseState(KTxtParseStateUnknown); |
|
1021 |
|
1022 switch(iParseState) |
|
1023 { |
|
1024 case EWaitStartResponse: ptrOldCommandParseState.Set(KTxtWaitStartResponse); break; |
|
1025 case EWaitLiteralParse: ptrOldCommandParseState.Set(KTxtWaitLiteralParse); break; |
|
1026 case EWaitLineParse: ptrOldCommandParseState.Set(KTxtWaitLineParse); break; |
|
1027 case EWaitLiteralIngore: ptrOldCommandParseState.Set(KTxtWaitLiteralIngore); break; |
|
1028 case EWaitLineIgnore: ptrOldCommandParseState.Set(KTxtWaitLineIgnore); break; |
|
1029 case ECommandComplete: ptrOldCommandParseState.Set(KTxtCommandComplete); break; |
|
1030 } |
|
1031 |
|
1032 switch(aParseState) |
|
1033 { |
|
1034 case EWaitStartResponse: ptrNewCommandParseState.Set(KTxtWaitStartResponse); break; |
|
1035 case EWaitLiteralParse: ptrNewCommandParseState.Set(KTxtWaitLiteralParse); break; |
|
1036 case EWaitLineParse: ptrNewCommandParseState.Set(KTxtWaitLineParse); break; |
|
1037 case EWaitLiteralIngore: ptrNewCommandParseState.Set(KTxtWaitLiteralIngore); break; |
|
1038 case EWaitLineIgnore: ptrNewCommandParseState.Set(KTxtWaitLineIgnore); break; |
|
1039 case ECommandComplete: ptrNewCommandParseState.Set(KTxtCommandComplete); break; |
|
1040 } |
|
1041 |
|
1042 _LIT8(KLogFormat, "CImapCommand::iParseState %S ==> %S"); |
|
1043 __LOG_FORMAT((iLogId, KLogFormat, &ptrOldCommandParseState, &ptrNewCommandParseState)); |
|
1044 |
|
1045 #endif //__IMAP_LOGGING |
|
1046 |
|
1047 iParseState = aParseState; |
|
1048 } |
|
1049 |
|
1050 CImapCommand::TParseState CImapCommand::ParseState() const |
|
1051 { |
|
1052 return iParseState; |
|
1053 } |
|
1054 |
|
1055 void CImapCommand::SetMessageFlagsChanged() |
|
1056 { |
|
1057 if (iSelectedFolderData != NULL) |
|
1058 { |
|
1059 __LOG_TEXT(iLogId, "CImapCommand - SetMessageFlagsChanged"); |
|
1060 iSelectedFolderData->SetMessageFlagsChanged(ETrue); |
|
1061 } |
|
1062 } |
|
1063 |
|
1064 // |
|
1065 // |
|
1066 // |
|
1067 |
|
1068 CImapCommandEx::CImapCommandEx(CImapFolderInfo* aSelectedFolderData, TInt aLogId) |
|
1069 : CImapCommand(aSelectedFolderData, aLogId) |
|
1070 {} |
|
1071 |
|
1072 CImapCommand::TParseBlockResult CImapCommandEx::DoParseBlockL(const TDesC8& /*aData*/) |
|
1073 { |
|
1074 TParseBlockResult result = ENotRecognised; |
|
1075 |
|
1076 switch (ParseState()) |
|
1077 { |
|
1078 case EWaitStartResponse: |
|
1079 { |
|
1080 result = ParseStartResponseL(); |
|
1081 } |
|
1082 break; |
|
1083 case EWaitLiteralParse: |
|
1084 { |
|
1085 __LOG_TEXT(iLogId, "CImapCommand - calling ParseLiteralBlockL()"); |
|
1086 ParseLiteralBlockL(); |
|
1087 result = EResponseIncomplete; |
|
1088 } |
|
1089 break; |
|
1090 case EWaitLineParse: |
|
1091 { |
|
1092 __LOG_TEXT(iLogId, "CImapCommand - calling ParseLineFollowingLiteralL()"); |
|
1093 TBool wantMoreData = ParseLineFollowingLiteralL(); |
|
1094 result = wantMoreData ? EResponseIncomplete : ECompleteUntagged; |
|
1095 } |
|
1096 break; |
|
1097 default: |
|
1098 { |
|
1099 // This method should not be called for any other parse state |
|
1100 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState9)); |
|
1101 } |
|
1102 break; |
|
1103 } |
|
1104 |
|
1105 return result; |
|
1106 } |
|
1107 |
|
1108 CImapCommand::TParseBlockResult CImapCommandEx::ParseStartResponseL() |
|
1109 { |
|
1110 TParseBlockResult result = ENotRecognised; |
|
1111 |
|
1112 TInt tagId = 0; |
|
1113 switch (GetTagTypeL(tagId)) |
|
1114 { |
|
1115 case ETagged: |
|
1116 { |
|
1117 __LOG_TEXT(iLogId, "CImapCommand - calling ParseTaggedResponseL()"); |
|
1118 TBool commandComplete = ParseTaggedResponseL(tagId); |
|
1119 |
|
1120 // If this tagged response does not complete the command (e.g. during fetch body) |
|
1121 // then treat it as if it were an untagged resopnse. |
|
1122 result = commandComplete ? ECompleteTagged : ECompleteUntagged; |
|
1123 } |
|
1124 break; |
|
1125 case EUntagged: |
|
1126 { |
|
1127 __LOG_TEXT(iLogId, "CImapCommand - calling ParseUntaggedResponseL()"); |
|
1128 result = ParseUntaggedResponseL(); |
|
1129 } |
|
1130 break; |
|
1131 case EContinuation: |
|
1132 { |
|
1133 __LOG_TEXT(iLogId, "CImapCommand - calling ParseContinuationResponseL()"); |
|
1134 ParseContinuationResponseL(); |
|
1135 result = ECompleteUntagged; |
|
1136 } |
|
1137 break; |
|
1138 default: |
|
1139 { |
|
1140 // unexpected tag type value |
|
1141 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidTagType)); |
|
1142 CorruptDataL(); |
|
1143 break; |
|
1144 } |
|
1145 } |
|
1146 |
|
1147 return result; |
|
1148 } |
|
1149 |
|
1150 /** |
|
1151 Calls the Default handler with no further processing. |
|
1152 @see BaseParseTaggedResponseL() |
|
1153 @param aTagId The incoming tag id. |
|
1154 @return ETrue - indicating that the command is completed by this tagged response. |
|
1155 */ |
|
1156 TBool CImapCommandEx::ParseTaggedResponseL(TInt aTagId) |
|
1157 { |
|
1158 BaseParseTaggedResponseL(aTagId); |
|
1159 return ETrue; |
|
1160 } |
|
1161 |
|
1162 /** |
|
1163 Default handler for a tagged response. |
|
1164 Records the response status code (OK/NO/BAD) |
|
1165 And checks that the incoming tag id matches the sent tag id. |
|
1166 Can be called by subclasses. |
|
1167 @param aTagId The incoming tag id. |
|
1168 */ |
|
1169 void CImapCommandEx::BaseParseTaggedResponseL(TInt aTagId) |
|
1170 { |
|
1171 // Check the tag id |
|
1172 if (aTagId != iTagId) |
|
1173 { |
|
1174 // Unexpected tag id |
|
1175 CorruptDataL(); |
|
1176 } |
|
1177 |
|
1178 // Fetch and check the response code |
|
1179 iResponseCode = GetResponseStateCode(); |
|
1180 if (iResponseCode == EImapResponseNone) |
|
1181 { |
|
1182 // Was expecting one of OK/NO/BAD, but didn't get it. This is a parse error. |
|
1183 CorruptDataL(); |
|
1184 } |
|
1185 } |
|
1186 |
|
1187 /** |
|
1188 Default handler for a continuation response. |
|
1189 Always leaves with KErrImapCorrupt, because by default commands do not expect continuation responses. |
|
1190 */ |
|
1191 void CImapCommandEx::ParseContinuationResponseL() |
|
1192 { |
|
1193 CorruptDataL(); |
|
1194 } |
|
1195 |
|
1196 /** |
|
1197 Default handler for an untagged response. |
|
1198 Always treats the response as unrecognised, causing ParseUnhandledBlockL() to be called later. |
|
1199 This default is used by commands that expect no specific responses. |
|
1200 @return ENotRecognised |
|
1201 */ |
|
1202 CImapCommand::TParseBlockResult CImapCommandEx::ParseUntaggedResponseL() |
|
1203 { |
|
1204 return ENotRecognised; |
|
1205 } |
|
1206 |
|
1207 /** |
|
1208 Default handler for a block of literal data. |
|
1209 Always leaves with KErrImapCorrupt, because by default commands do not expect literal data. |
|
1210 */ |
|
1211 void CImapCommandEx::ParseLiteralBlockL() |
|
1212 { |
|
1213 CorruptDataL(); |
|
1214 } |
|
1215 |
|
1216 /** |
|
1217 Default handler for a line following a literal block. |
|
1218 Always leaves with KErrImapCorrupt, because by default commands do not expect literal data, let alone lines following literal data. |
|
1219 @return because this implementation of the method always leaves, no value can ever be returned. |
|
1220 */ |
|
1221 TBool CImapCommandEx::ParseLineFollowingLiteralL() |
|
1222 { |
|
1223 CorruptDataL(); |
|
1224 return EFalse; |
|
1225 } |
|
1226 |
|
1227 /*** |
|
1228 To get the present TagId of the Command object. |
|
1229 @return the TagId of the Command object. |
|
1230 */ |
|
1231 TInt CImapCommand::GetTagId() |
|
1232 { |
|
1233 return iTagId ; |
|
1234 } |
|
1235 |