tdeioslave/imap4

imapparser.cpp
1/**********************************************************************
2 *
3 * imapparser.cpp - IMAP4rev1 Parser
4 * Copyright (C) 2001-2002 Michael Haeckel <haeckel@kde.org>
5 * Copyright (C) 2000 s.carstens@gmx.de
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 * Send comments and bug fixes to s.carstens@gmx.de
22 *
23 *********************************************************************/
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include "rfcdecoder.h"
30
31#include "imapparser.h"
32
33#include "imapinfo.h"
34
35#include "mailheader.h"
36#include "mimeheader.h"
37#include "mailaddress.h"
38
39#include <sys/types.h>
40
41#include <stdlib.h>
42#include <unistd.h>
43
44#ifdef HAVE_LIBSASL2
45extern "C" {
46#include <sasl/sasl.h>
47}
48#endif
49
50#include <tqregexp.h>
51#include <tqbuffer.h>
52#include <tqstring.h>
53#include <tqstringlist.h>
54
55#include <kdebug.h>
56#include <kmdcodec.h>
57#include <kurl.h>
58
59#include <kasciistricmp.h>
60#include <kasciistringtools.h>
61
62#ifdef HAVE_LIBSASL2
63static sasl_callback_t callbacks[] = {
64 { SASL_CB_ECHOPROMPT, NULL, NULL },
65 { SASL_CB_NOECHOPROMPT, NULL, NULL },
66 { SASL_CB_GETREALM, NULL, NULL },
67 { SASL_CB_USER, NULL, NULL },
68 { SASL_CB_AUTHNAME, NULL, NULL },
69 { SASL_CB_PASS, NULL, NULL },
70 { SASL_CB_CANON_USER, NULL, NULL },
71 { SASL_CB_LIST_END, NULL, NULL }
72};
73#endif
74
75imapParser::imapParser ()
76{
77 sentQueue.setAutoDelete (false);
78 completeQueue.setAutoDelete (true);
79 currentState = ISTATE_NO;
80 commandCounter = 0;
81 lastHandled = 0;
82}
83
84imapParser::~imapParser ()
85{
86 delete lastHandled;
87 lastHandled = 0;
88}
89
91imapParser::doCommand (imapCommand * aCmd)
92{
93 int pl = 0;
94 sendCommand (aCmd);
95 while (pl != -1 && !aCmd->isComplete ()) {
96 while ((pl = parseLoop ()) == 0)
97 ;
98 }
99
100 return aCmd;
101}
102
104imapParser::sendCommand (imapCommand * aCmd)
105{
106 aCmd->setId (TQString::number(commandCounter++));
107 sentQueue.append (aCmd);
108
109 continuation.resize(0);
110 const TQString& command = aCmd->command();
111
112 if (command == "SELECT" || command == "EXAMINE")
113 {
114 // we need to know which box we are selecting
115 parseString p;
116 p.fromString(aCmd->parameter());
117 currentBox = parseOneWordC(p);
118 kdDebug(7116) << "imapParser::sendCommand - setting current box to " << currentBox << endl;
119 }
120 else if (command == "CLOSE")
121 {
122 // we no longer have a box open
123 currentBox = TQString();
124 }
125 else if (command.find ("SEARCH") != -1
126 || command == "GETACL"
127 || command == "LISTRIGHTS"
128 || command == "MYRIGHTS"
129 || command == "GETANNOTATION"
130 || command == "NAMESPACE"
131 || command == "GETQUOTAROOT"
132 || command == "GETQUOTA"
133 || command == "X-GET-OTHER-USERS"
134 || command == "X-GET-DELEGATES"
135 || command == "X-GET-OUT-OF-OFFICE")
136 {
137 lastResults.clear ();
138 }
139 else if (command == "LIST"
140 || command == "LSUB")
141 {
142 listResponses.clear ();
143 }
144 parseWriteLine (aCmd->getStr ());
145 return aCmd;
146}
147
148bool
149imapParser::clientLogin (const TQString & aUser, const TQString & aPass,
150 TQString & resultInfo)
151{
152 imapCommand *cmd;
153 bool retVal = false;
154
155 cmd =
156 doCommand (new
157 imapCommand ("LOGIN", "\"" + rfcDecoder::quoteIMAP(aUser)
158 + "\" \"" + rfcDecoder::quoteIMAP(aPass) + "\""));
159
160 if (cmd->result () == "OK")
161 {
162 currentState = ISTATE_LOGIN;
163 retVal = true;
164 }
165 resultInfo = cmd->resultInfo();
166 completeQueue.removeRef (cmd);
167
168 return retVal;
169}
170
171#ifdef HAVE_LIBSASL2
172static bool sasl_interact( TDEIO::SlaveBase *slave, TDEIO::AuthInfo &ai, void *in )
173{
174 kdDebug(7116) << "sasl_interact" << endl;
175 sasl_interact_t *interact = ( sasl_interact_t * ) in;
176
177 //some mechanisms do not require username && pass, so it doesn't need a popup
178 //window for getting this info
179 for ( ; interact->id != SASL_CB_LIST_END; interact++ ) {
180 if ( interact->id == SASL_CB_AUTHNAME ||
181 interact->id == SASL_CB_PASS ) {
182
183 if ( ai.username.isEmpty() || ai.password.isEmpty() ) {
184 if (!slave->openPassDlg(ai))
185 return false;
186 }
187 break;
188 }
189 }
190
191 interact = ( sasl_interact_t * ) in;
192 while( interact->id != SASL_CB_LIST_END ) {
193 kdDebug(7116) << "SASL_INTERACT id: " << interact->id << endl;
194 switch( interact->id ) {
195 case SASL_CB_USER:
196 case SASL_CB_AUTHNAME:
197 kdDebug(7116) << "SASL_CB_[USER|AUTHNAME]: '" << ai.username << "'" << endl;
198 interact->result = strdup( ai.username.utf8() );
199 interact->len = strlen( (const char *) interact->result );
200 break;
201 case SASL_CB_PASS:
202 kdDebug(7116) << "SASL_CB_PASS: [hidden] " << endl;
203 interact->result = strdup( ai.password.utf8() );
204 interact->len = strlen( (const char *) interact->result );
205 break;
206 default:
207 interact->result = 0;
208 interact->len = 0;
209 break;
210 }
211 interact++;
212 }
213 return true;
214}
215#endif
216
217bool
218imapParser::clientAuthenticate ( TDEIO::SlaveBase *slave, TDEIO::AuthInfo &ai,
219 const TQString & aFTQDN, const TQString & aAuth, bool isSSL, TQString & resultInfo)
220{
221 bool retVal = false;
222#ifdef HAVE_LIBSASL2
223 int result;
224 sasl_conn_t *conn = 0;
225 sasl_interact_t *client_interact = 0;
226 const char *out = 0;
227 uint outlen = 0;
228 const char *mechusing = 0;
229 TQByteArray tmp, challenge;
230
231 kdDebug(7116) << "aAuth: " << aAuth << " FTQDN: " << aFTQDN << " isSSL: " << isSSL << endl;
232
233 // see if server supports this authenticator
234 if (!hasCapability ("AUTH=" + aAuth))
235 return false;
236
237// result = sasl_client_new( isSSL ? "imaps" : "imap",
238 result = sasl_client_new( "imap", /* FIXME: with cyrus-imapd, even imaps' digest-uri
239 must be 'imap'. I don't know if it's good or bad. */
240 aFTQDN.latin1(),
241 0, 0, callbacks, 0, &conn );
242
243 if ( result != SASL_OK ) {
244 kdDebug(7116) << "sasl_client_new failed with: " << result << endl;
245 resultInfo = TQString::fromUtf8( sasl_errdetail( conn ) );
246 return false;
247 }
248
249 do {
250 result = sasl_client_start(conn, aAuth.latin1(), &client_interact,
251 hasCapability("SASL-IR") ? &out : 0, &outlen, &mechusing);
252
253 if ( result == SASL_INTERACT ) {
254 if ( !sasl_interact( slave, ai, client_interact ) ) {
255 sasl_dispose( &conn );
256 return false;
257 }
258 }
259 } while ( result == SASL_INTERACT );
260
261 if ( result != SASL_CONTINUE && result != SASL_OK ) {
262 kdDebug(7116) << "sasl_client_start failed with: " << result << endl;
263 resultInfo = TQString::fromUtf8( sasl_errdetail( conn ) );
264 sasl_dispose( &conn );
265 return false;
266 }
267 imapCommand *cmd;
268
269 tmp.setRawData( out, outlen );
270 KCodecs::base64Encode( tmp, challenge );
271 tmp.resetRawData( out, outlen );
272 // then lets try it
273 TQString firstCommand = aAuth;
274 if ( !challenge.isEmpty() ) {
275 firstCommand += " ";
276 firstCommand += TQString::fromLatin1( challenge.data(), challenge.size() );
277 }
278 cmd = sendCommand (new imapCommand ("AUTHENTICATE", firstCommand.latin1()));
279
280 int pl = 0;
281 while ( pl != -1 && !cmd->isComplete () )
282 {
283 //read the next line
284 while ((pl = parseLoop()) == 0) ;
285
286 if (!continuation.isEmpty())
287 {
288// kdDebug(7116) << "S: " << TQCString(continuation.data(),continuation.size()+1) << endl;
289 if ( continuation.size() > 4 ) {
290 tmp.setRawData( continuation.data() + 2, continuation.size() - 4 );
291 KCodecs::base64Decode( tmp, challenge );
292// kdDebug(7116) << "S-1: " << TQCString(challenge.data(),challenge.size()+1) << endl;
293 tmp.resetRawData( continuation.data() + 2, continuation.size() - 4 );
294 }
295
296 do {
297 result = sasl_client_step(conn, challenge.isEmpty() ? 0 : challenge.data(),
298 challenge.size(),
299 &client_interact,
300 &out, &outlen);
301
302 if (result == SASL_INTERACT) {
303 if ( !sasl_interact( slave, ai, client_interact ) ) {
304 sasl_dispose( &conn );
305 return false;
306 }
307 }
308 } while ( result == SASL_INTERACT );
309
310 if ( result != SASL_CONTINUE && result != SASL_OK ) {
311 kdDebug(7116) << "sasl_client_step failed with: " << result << endl;
312 resultInfo = TQString::fromUtf8( sasl_errdetail( conn ) );
313 sasl_dispose( &conn );
314 return false;
315 }
316
317 tmp.setRawData( out, outlen );
318// kdDebug(7116) << "C-1: " << TQCString(tmp.data(),tmp.size()+1) << endl;
319 KCodecs::base64Encode( tmp, challenge );
320 tmp.resetRawData( out, outlen );
321// kdDebug(7116) << "C: " << TQCString(challenge.data(),challenge.size()+1) << endl;
322 parseWriteLine (challenge);
323 continuation.resize(0);
324 }
325 }
326
327 if (cmd->result () == "OK")
328 {
329 currentState = ISTATE_LOGIN;
330 retVal = true;
331 }
332 resultInfo = cmd->resultInfo();
333 completeQueue.removeRef (cmd);
334
335 sasl_dispose( &conn ); //we don't use sasl_en/decode(), so it's safe to dispose the connection.
336#endif //HAVE_LIBSASL2
337 return retVal;
338}
339
340void
341imapParser::parseUntagged (parseString & result)
342{
343 //kdDebug(7116) << "imapParser::parseUntagged - '" << result.cstr() << "'" << endl;
344
345 parseOneWordC(result); // *
346 TQByteArray what = parseLiteral (result); // see whats coming next
347
348 if(!what.isEmpty ()) {
349 switch (what[0])
350 {
351 //the status responses
352 case 'B': // BAD or BYE
353 if (tqstrncmp(what, "BAD", what.size()) == 0)
354 {
355 parseResult (what, result);
356 }
357 else if (tqstrncmp(what, "BYE", what.size()) == 0)
358 {
359 parseResult (what, result);
360 if ( sentQueue.count() ) {
361 // BYE that interrupts a command -> copy the reason for it
362 imapCommand *current = sentQueue.at (0);
363 current->setResultInfo(result.cstr());
364 }
365 currentState = ISTATE_NO;
366 }
367 break;
368
369 case 'N': // NO
370 if (what[1] == 'O' && what.size() == 2)
371 {
372 parseResult (what, result);
373 }
374 else if (tqstrncmp(what, "NAMESPACE", what.size()) == 0)
375 {
376 parseNamespace (result);
377 }
378 break;
379
380 case 'O': // OK
381 if (what[1] == 'K' && what.size() == 2)
382 {
383 parseResult (what, result);
384 } else if (tqstrncmp(what, "OTHER-USER", 10) == 0) { // X-GET-OTHER-USER
385 parseOtherUser (result);
386 } else if (tqstrncmp(what, "OUT-OF-OFFICE", 13) == 0) { // X-GET-OUT-OF-OFFICE
387 parseOutOfOffice (result);
388 }
389 break;
390 case 'D':
391 if (tqstrncmp(what, "DELEGATE", 8) == 0) { // X-GET-DELEGATES
392 parseDelegate (result);
393 }
394 break;
395
396 case 'P': // PREAUTH
397 if (tqstrncmp(what, "PREAUTH", what.size()) == 0)
398 {
399 parseResult (what, result);
400 currentState = ISTATE_LOGIN;
401 }
402 break;
403
404 // parse the other responses
405 case 'C': // CAPABILITY
406 if (tqstrncmp(what, "CAPABILITY", what.size()) == 0)
407 {
408 parseCapability (result);
409 }
410 break;
411
412 case 'F': // FLAGS
413 if (tqstrncmp(what, "FLAGS", what.size()) == 0)
414 {
415 parseFlags (result);
416 }
417 break;
418
419 case 'L': // LIST or LSUB or LISTRIGHTS
420 if (tqstrncmp(what, "LIST", what.size()) == 0)
421 {
422 parseList (result);
423 }
424 else if (tqstrncmp(what, "LSUB", what.size()) == 0)
425 {
426 parseLsub (result);
427 }
428 else if (tqstrncmp(what, "LISTRIGHTS", what.size()) == 0)
429 {
430 parseListRights (result);
431 }
432 break;
433
434 case 'M': // MYRIGHTS
435 if (tqstrncmp(what, "MYRIGHTS", what.size()) == 0)
436 {
437 parseMyRights (result);
438 }
439 break;
440 case 'S': // SEARCH or STATUS
441 if (tqstrncmp(what, "SEARCH", what.size()) == 0)
442 {
443 parseSearch (result);
444 }
445 else if (tqstrncmp(what, "STATUS", what.size()) == 0)
446 {
447 parsetStatus (result);
448 }
449 break;
450
451 case 'A': // ACL or ANNOTATION
452 if (tqstrncmp(what, "ACL", what.size()) == 0)
453 {
454 parseAcl (result);
455 }
456 else if (tqstrncmp(what, "ANNOTATION", what.size()) == 0)
457 {
458 parseAnnotation (result);
459 }
460 break;
461 case 'Q': // QUOTA or QUOTAROOT
462 if ( what.size() > 5 && tqstrncmp(what, "QUOTAROOT", what.size()) == 0)
463 {
464 parseQuotaRoot( result );
465 }
466 else if (tqstrncmp(what, "QUOTA", what.size()) == 0)
467 {
468 parseQuota( result );
469 }
470 break;
471 case 'X': // Custom command
472 {
473 parseCustom( result );
474 }
475 break;
476 default:
477 //better be a number
478 {
479 ulong number;
480 bool valid;
481
482 number = TQCString(what, what.size() + 1).toUInt(&valid);
483 if (valid)
484 {
485 what = parseLiteral (result);
486 if(!what.isEmpty ()) {
487 switch (what[0])
488 {
489 case 'E':
490 if (tqstrncmp(what, "EXISTS", what.size()) == 0)
491 {
492 parseExists (number, result);
493 }
494 else if (tqstrncmp(what, "EXPUNGE", what.size()) == 0)
495 {
496 parseExpunge (number, result);
497 }
498 break;
499
500 case 'F':
501 if (tqstrncmp(what, "FETCH", what.size()) == 0)
502 {
503 seenUid = TQString();
504 parseFetch (number, result);
505 }
506 break;
507
508 case 'S':
509 if (tqstrncmp(what, "STORE", what.size()) == 0) // deprecated store
510 {
511 seenUid = TQString();
512 parseFetch (number, result);
513 }
514 break;
515
516 case 'R':
517 if (tqstrncmp(what, "RECENT", what.size()) == 0)
518 {
519 parseRecent (number, result);
520 }
521 break;
522 default:
523 break;
524 }
525 }
526 }
527 }
528 break;
529 } //switch
530 }
531} //func
532
533
534void
535imapParser::parseResult (TQByteArray & result, parseString & rest,
536 const TQString & command)
537{
538 if (command == "SELECT")
539 selectInfo.setReadWrite(true);
540
541 if (rest[0] == '[')
542 {
543 rest.pos++;
544 TQCString option = parseOneWordC(rest, TRUE);
545
546 switch (option[0])
547 {
548 case 'A': // ALERT
549 if (option == "ALERT")
550 {
551 rest.pos = rest.data.find(']', rest.pos) + 1;
552 // The alert text is after [ALERT].
553 // Is this correct or do we need to care about litterals?
554 selectInfo.setAlert( rest.cstr() );
555 }
556 break;
557
558 case 'N': // NEWNAME
559 if (option == "NEWNAME")
560 {
561 }
562 break;
563
564 case 'P': //PARSE or PERMANENTFLAGS
565 if (option == "PARSE")
566 {
567 }
568 else if (option == "PERMANENTFLAGS")
569 {
570 uint end = rest.data.find(']', rest.pos);
571 TQCString flags(rest.data.data() + rest.pos, end - rest.pos);
572 selectInfo.setPermanentFlags (flags);
573 rest.pos = end;
574 }
575 break;
576
577 case 'R': //READ-ONLY or READ-WRITE
578 if (option == "READ-ONLY")
579 {
580 selectInfo.setReadWrite (false);
581 }
582 else if (option == "READ-WRITE")
583 {
584 selectInfo.setReadWrite (true);
585 }
586 break;
587
588 case 'T': //TRYCREATE
589 if (option == "TRYCREATE")
590 {
591 }
592 break;
593
594 case 'U': //UIDVALIDITY or UNSEEN
595 if (option == "UIDVALIDITY")
596 {
597 ulong value;
598 if (parseOneNumber (rest, value))
599 selectInfo.setUidValidity (value);
600 }
601 else if (option == "UNSEEN")
602 {
603 ulong value;
604 if (parseOneNumber (rest, value))
605 selectInfo.setUnseen (value);
606 }
607 else if (option == "UIDNEXT")
608 {
609 ulong value;
610 if (parseOneNumber (rest, value))
611 selectInfo.setUidNext (value);
612 }
613 else
614 break;
615
616 }
617 if (rest[0] == ']')
618 rest.pos++; //tie off ]
619 skipWS (rest);
620 }
621
622 if (command.isEmpty())
623 {
624 // This happens when parsing an intermediate result line (those that start with '*').
625 // No state change involved, so we can stop here.
626 return;
627 }
628
629 switch (command[0].latin1 ())
630 {
631 case 'A':
632 if (command == "AUTHENTICATE")
633 if (tqstrncmp(result, "OK", result.size()) == 0)
634 currentState = ISTATE_LOGIN;
635 break;
636
637 case 'L':
638 if (command == "LOGIN")
639 if (tqstrncmp(result, "OK", result.size()) == 0)
640 currentState = ISTATE_LOGIN;
641 break;
642
643 case 'E':
644 if (command == "EXAMINE")
645 {
646 if (tqstrncmp(result, "OK", result.size()) == 0)
647 currentState = ISTATE_SELECT;
648 else
649 {
650 if (currentState == ISTATE_SELECT)
651 currentState = ISTATE_LOGIN;
652 currentBox = TQString();
653 }
654 kdDebug(7116) << "imapParser::parseResult - current box is now " << currentBox << endl;
655 }
656 break;
657
658 case 'S':
659 if (command == "SELECT")
660 {
661 if (tqstrncmp(result, "OK", result.size()) == 0)
662 currentState = ISTATE_SELECT;
663 else
664 {
665 if (currentState == ISTATE_SELECT)
666 currentState = ISTATE_LOGIN;
667 currentBox = TQString();
668 }
669 kdDebug(7116) << "imapParser::parseResult - current box is now " << currentBox << endl;
670 }
671 break;
672
673 default:
674 break;
675 }
676
677}
678
679void imapParser::parseCapability (parseString & result)
680{
681 TQCString temp( result.cstr() );
682 imapCapabilities = TQStringList::split ( ' ', KPIM::kAsciiToLower( temp.data() ) );
683}
684
685void imapParser::parseFlags (parseString & result)
686{
687 selectInfo.setFlags(result.cstr());
688}
689
690void imapParser::parseList (parseString & result)
691{
692 imapList this_one;
693
694 if (result[0] != '(')
695 return; //not proper format for us
696
697 result.pos++; // tie off (
698
699 this_one.parseAttributes( result );
700
701 result.pos++; // tie off )
702 skipWS (result);
703
704 this_one.setHierarchyDelimiter(parseLiteralC(result));
705 this_one.setName (rfcDecoder::fromIMAP(parseLiteralC(result))); // decode modified UTF7
706
707 listResponses.append (this_one);
708}
709
710void imapParser::parseLsub (parseString & result)
711{
712 imapList this_one (result.cstr(), *this);
713 listResponses.append (this_one);
714}
715
716void imapParser::parseListRights (parseString & result)
717{
718 parseOneWordC (result); // skip mailbox name
719 parseOneWordC (result); // skip user id
720 int outlen = 1;
721 while ( outlen ) {
722 TQCString word = parseOneWordC (result, false, &outlen);
723 lastResults.append (word);
724 }
725}
726
727void imapParser::parseAcl (parseString & result)
728{
729 parseOneWordC (result); // skip mailbox name
730 int outlen = 1;
731 // The result is user1 perm1 user2 perm2 etc. The caller will sort it out.
732 while ( outlen && !result.isEmpty() ) {
733 TQCString word = parseLiteralC (result, false, false, &outlen);
734 lastResults.append (word);
735 }
736}
737
738void imapParser::parseAnnotation (parseString & result)
739{
740 parseOneWordC (result); // skip mailbox name
741 skipWS (result);
742 parseOneWordC (result); // skip entry name (we know it since we don't allow wildcards in it)
743 skipWS (result);
744 if (result.isEmpty() || result[0] != '(')
745 return;
746 result.pos++;
747 skipWS (result);
748 int outlen = 1;
749 // The result is name1 value1 name2 value2 etc. The caller will sort it out.
750 while ( outlen && !result.isEmpty() && result[0] != ')' ) {
751 TQCString word = parseLiteralC (result, false, false, &outlen);
752 lastResults.append (word);
753 }
754}
755
756
757void imapParser::parseQuota (parseString & result)
758{
759 // quota_response ::= "QUOTA" SP astring SP quota_list
760 // quota_list ::= "(" #quota_resource ")"
761 // quota_resource ::= atom SP number SP number
762 TQCString root = parseOneWordC( result );
763 if ( root.isEmpty() ) {
764 lastResults.append( "" );
765 } else {
766 lastResults.append( root );
767 }
768 if (result.isEmpty() || result[0] != '(')
769 return;
770 result.pos++;
771 skipWS (result);
772 TQStringList triplet;
773 int outlen = 1;
774 while ( outlen && !result.isEmpty() && result[0] != ')' ) {
775 TQCString word = parseLiteralC (result, false, false, &outlen);
776 triplet.append(word);
777 }
778 lastResults.append( triplet.join(" ") );
779}
780
781void imapParser::parseQuotaRoot (parseString & result)
782{
783 // quotaroot_response
784 // ::= "QUOTAROOT" SP astring *(SP astring)
785 parseOneWordC (result); // skip mailbox name
786 skipWS (result);
787 if ( result.isEmpty() )
788 return;
789 TQStringList roots;
790 int outlen = 1;
791 while ( outlen && !result.isEmpty() ) {
792 TQCString word = parseLiteralC (result, false, false, &outlen);
793 roots.append (word);
794 }
795 lastResults.append( roots.isEmpty()? "" : roots.join(" ") );
796}
797
798void imapParser::parseCustom (parseString & result)
799{
800 int outlen = 1;
801 TQCString word = parseLiteralC (result, false, false, &outlen);
802 lastResults.append( word );
803}
804
805void imapParser::parseOtherUser (parseString & result)
806{
807 lastResults.append( parseOneWordC( result ) );
808}
809
810void imapParser::parseDelegate (parseString & result)
811{
812 const TQString email = parseOneWordC( result );
813
814 TQStringList rights;
815 int outlen = 1;
816 while ( outlen && !result.isEmpty() ) {
817 TQCString word = parseLiteralC( result, false, false, &outlen );
818 rights.append( word );
819 }
820
821 lastResults.append( email + ':' + rights.join( "," ) );
822}
823
824void imapParser::parseOutOfOffice (parseString & result)
825{
826 const TQString state = parseOneWordC (result);
827 parseOneWordC (result); // skip encoding
828
829 int outlen = 1;
830 TQCString msg = parseLiteralC (result, false, false, &outlen);
831
832 lastResults.append( state + '^' + TQString::fromUtf8( msg ) );
833}
834
835void imapParser::parseMyRights (parseString & result)
836{
837 parseOneWordC (result); // skip mailbox name
838 Q_ASSERT( lastResults.isEmpty() ); // we can only be called once
839 lastResults.append (parseOneWordC (result) );
840}
841
842void imapParser::parseSearch (parseString & result)
843{
844 ulong value;
845
846 while (parseOneNumber (result, value))
847 {
848 lastResults.append (TQString::number(value));
849 }
850}
851
852void imapParser::parsetStatus (parseString & inWords)
853{
854 lasStatus = imapInfo ();
855
856 parseLiteralC(inWords); // swallow the box
857 if (inWords.isEmpty() || inWords[0] != '(')
858 return;
859
860 inWords.pos++;
861 skipWS (inWords);
862
863 while (!inWords.isEmpty() && inWords[0] != ')')
864 {
865 ulong value;
866
867 TQCString label = parseOneWordC(inWords);
868 if (parseOneNumber (inWords, value))
869 {
870 if (label == "MESSAGES")
871 lasStatus.setCount (value);
872 else if (label == "RECENT")
873 lasStatus.setRecent (value);
874 else if (label == "UIDVALIDITY")
875 lasStatus.setUidValidity (value);
876 else if (label == "UNSEEN")
877 lasStatus.setUnseen (value);
878 else if (label == "UIDNEXT")
879 lasStatus.setUidNext (value);
880 }
881 }
882
883 if (inWords[0] == ')')
884 inWords.pos++;
885 skipWS (inWords);
886}
887
888void imapParser::parseExists (ulong value, parseString & result)
889{
890 selectInfo.setCount (value);
891 result.pos = result.data.size();
892}
893
894void imapParser::parseExpunge (ulong value, parseString & result)
895{
896 Q_UNUSED(value);
897 Q_UNUSED(result);
898}
899
900void imapParser::parseAddressList (parseString & inWords, TQPtrList<mailAddress>& list)
901{
902 if (inWords.isEmpty())
903 return;
904 if (inWords[0] != '(')
905 {
906 parseOneWordC (inWords); // parse NIL
907 }
908 else
909 {
910 inWords.pos++;
911 skipWS (inWords);
912
913 while (!inWords.isEmpty () && inWords[0] != ')')
914 {
915 if (inWords[0] == '(') {
916 mailAddress *addr = new mailAddress;
917 parseAddress(inWords, *addr);
918 list.append(addr);
919 } else {
920 break;
921 }
922 }
923
924 if (!inWords.isEmpty() && inWords[0] == ')')
925 inWords.pos++;
926 skipWS (inWords);
927 }
928}
929
930const mailAddress& imapParser::parseAddress (parseString & inWords, mailAddress& retVal)
931{
932 inWords.pos++;
933 skipWS (inWords);
934
935 retVal.setFullName(parseLiteralC(inWords));
936 retVal.setCommentRaw(parseLiteralC(inWords));
937 retVal.setUser(parseLiteralC(inWords));
938 retVal.setHost(parseLiteralC(inWords));
939
940 if (!inWords.isEmpty() && inWords[0] == ')')
941 inWords.pos++;
942 skipWS (inWords);
943
944 return retVal;
945}
946
947mailHeader * imapParser::parseEnvelope (parseString & inWords)
948{
949 mailHeader *envelope = 0;
950
951 if (inWords[0] != '(')
952 return envelope;
953 inWords.pos++;
954 skipWS (inWords);
955
956 envelope = new mailHeader;
957
958 //date
959 envelope->setDate(parseLiteralC(inWords));
960
961 //subject
962 envelope->setSubject(parseLiteralC(inWords));
963
964 TQPtrList<mailAddress> list;
965 list.setAutoDelete(true);
966
967 //from
968 parseAddressList(inWords, list);
969 if (!list.isEmpty()) {
970 envelope->setFrom(*list.last());
971 list.clear();
972 }
973
974 //sender
975 parseAddressList(inWords, list);
976 if (!list.isEmpty()) {
977 envelope->setSender(*list.last());
978 list.clear();
979 }
980
981 //reply-to
982 parseAddressList(inWords, list);
983 if (!list.isEmpty()) {
984 envelope->setReplyTo(*list.last());
985 list.clear();
986 }
987
988 //to
989 parseAddressList (inWords, envelope->to());
990
991 //cc
992 parseAddressList (inWords, envelope->cc());
993
994 //bcc
995 parseAddressList (inWords, envelope->bcc());
996
997 //in-reply-to
998 envelope->setInReplyTo(parseLiteralC(inWords));
999
1000 //message-id
1001 envelope->setMessageId(parseLiteralC(inWords));
1002
1003 // see if we have more to come
1004 while (!inWords.isEmpty () && inWords[0] != ')')
1005 {
1006 //eat the extensions to this part
1007 if (inWords[0] == '(')
1008 parseSentence (inWords);
1009 else
1010 parseLiteralC (inWords);
1011 }
1012
1013 if (!inWords.isEmpty() && inWords[0] == ')')
1014 inWords.pos++;
1015 skipWS (inWords);
1016
1017 return envelope;
1018}
1019
1020// parse parameter pairs into a dictionary
1021// caller must clean up the dictionary items
1022TQAsciiDict < TQString > imapParser::parseDisposition (parseString & inWords)
1023{
1024 TQCString disposition;
1025 TQAsciiDict < TQString > retVal (17, false);
1026
1027 // return value is a shallow copy
1028 retVal.setAutoDelete (false);
1029
1030 if (inWords[0] != '(')
1031 {
1032 //disposition only
1033 disposition = parseOneWordC (inWords);
1034 }
1035 else
1036 {
1037 inWords.pos++;
1038 skipWS (inWords);
1039
1040 //disposition
1041 disposition = parseOneWordC (inWords);
1042 retVal = parseParameters (inWords);
1043 if (inWords[0] != ')')
1044 return retVal;
1045 inWords.pos++;
1046 skipWS (inWords);
1047 }
1048
1049 if (!disposition.isEmpty ())
1050 {
1051 retVal.insert ("content-disposition", new TQString(disposition));
1052 }
1053
1054 return retVal;
1055}
1056
1057// parse parameter pairs into a dictionary
1058// caller must clean up the dictionary items
1059TQAsciiDict < TQString > imapParser::parseParameters (parseString & inWords)
1060{
1061 TQAsciiDict < TQString > retVal (17, false);
1062
1063 // return value is a shallow copy
1064 retVal.setAutoDelete (false);
1065
1066 if (inWords[0] != '(')
1067 {
1068 //better be NIL
1069 parseOneWordC (inWords);
1070 }
1071 else
1072 {
1073 inWords.pos++;
1074 skipWS (inWords);
1075
1076 while (!inWords.isEmpty () && inWords[0] != ')')
1077 {
1078 TQCString l1 = parseLiteralC(inWords);
1079 TQCString l2 = parseLiteralC(inWords);
1080 retVal.insert (l1, new TQString(l2));
1081 }
1082
1083 if (inWords[0] != ')')
1084 return retVal;
1085 inWords.pos++;
1086 skipWS (inWords);
1087 }
1088
1089 return retVal;
1090}
1091
1092mimeHeader * imapParser::parseSimplePart (parseString & inWords,
1093 TQString & inSection, mimeHeader * localPart)
1094{
1095 TQCString subtype;
1096 TQCString typeStr;
1097 TQAsciiDict < TQString > parameters (17, false);
1098 ulong size;
1099
1100 parameters.setAutoDelete (true);
1101
1102 if (inWords[0] != '(')
1103 return 0;
1104
1105 if (!localPart)
1106 localPart = new mimeHeader;
1107
1108 localPart->setPartSpecifier (inSection);
1109
1110 inWords.pos++;
1111 skipWS (inWords);
1112
1113 //body type
1114 typeStr = parseLiteralC(inWords);
1115
1116 //body subtype
1117 subtype = parseLiteralC(inWords);
1118
1119 localPart->setType (typeStr + "/" + subtype);
1120
1121 //body parameter parenthesized list
1122 parameters = parseParameters (inWords);
1123 {
1124 TQAsciiDictIterator < TQString > it (parameters);
1125
1126 while (it.current ())
1127 {
1128 localPart->setTypeParm (it.currentKey (), *(it.current ()));
1129 ++it;
1130 }
1131 parameters.clear ();
1132 }
1133
1134 //body id
1135 localPart->setID (parseLiteralC(inWords));
1136
1137 //body description
1138 localPart->setDescription (parseLiteralC(inWords));
1139
1140 //body encoding
1141 localPart->setEncoding (parseLiteralC(inWords));
1142
1143 //body size
1144 if (parseOneNumber (inWords, size))
1145 localPart->setLength (size);
1146
1147 // type specific extensions
1148 if (localPart->getType().upper() == "MESSAGE/RFC822")
1149 {
1150 //envelope structure
1151 mailHeader *envelope = parseEnvelope (inWords);
1152
1153 //body structure
1154 parseBodyStructure (inWords, inSection, envelope);
1155
1156 localPart->setNestedMessage (envelope);
1157
1158 //text lines
1159 ulong lines;
1160 parseOneNumber (inWords, lines);
1161 }
1162 else
1163 {
1164 if (typeStr == "TEXT")
1165 {
1166 //text lines
1167 ulong lines;
1168 parseOneNumber (inWords, lines);
1169 }
1170
1171 // md5
1172 parseLiteralC(inWords);
1173
1174 // body disposition
1175 parameters = parseDisposition (inWords);
1176 {
1177 TQString *disposition = parameters["content-disposition"];
1178
1179 if (disposition)
1180 localPart->setDisposition (disposition->ascii ());
1181 parameters.remove ("content-disposition");
1182 TQAsciiDictIterator < TQString > it (parameters);
1183 while (it.current ())
1184 {
1185 localPart->setDispositionParm (it.currentKey (),
1186 *(it.current ()));
1187 ++it;
1188 }
1189
1190 parameters.clear ();
1191 }
1192
1193 // body language
1194 parseSentence (inWords);
1195 }
1196
1197 // see if we have more to come
1198 while (!inWords.isEmpty () && inWords[0] != ')')
1199 {
1200 //eat the extensions to this part
1201 if (inWords[0] == '(')
1202 parseSentence (inWords);
1203 else
1204 parseLiteralC(inWords);
1205 }
1206 if (inWords[0] == ')')
1207 inWords.pos++;
1208 skipWS (inWords);
1209
1210 return localPart;
1211}
1212
1213mimeHeader * imapParser::parseBodyStructure (parseString & inWords,
1214 TQString & inSection, mimeHeader * localPart)
1215{
1216 bool init = false;
1217 if (inSection.isEmpty())
1218 {
1219 // first run
1220 init = true;
1221 // assume one part
1222 inSection = "1";
1223 }
1224 int section = 0;
1225
1226 if (inWords[0] != '(')
1227 {
1228 // skip ""
1229 parseOneWordC (inWords);
1230 return 0;
1231 }
1232 inWords.pos++;
1233 skipWS (inWords);
1234
1235 if (inWords[0] == '(')
1236 {
1237 TQByteArray subtype;
1238 TQAsciiDict < TQString > parameters (17, false);
1239 TQString outSection;
1240 parameters.setAutoDelete (true);
1241 if (!localPart)
1242 localPart = new mimeHeader;
1243 else
1244 {
1245 // might be filled from an earlier run
1246 localPart->clearNestedParts ();
1247 localPart->clearTypeParameters ();
1248 localPart->clearDispositionParameters ();
1249 // an envelope was passed in so this is the multipart header
1250 outSection = inSection + ".HEADER";
1251 }
1252 if (inWords[0] == '(' && init)
1253 inSection = "0";
1254
1255 // set the section
1256 if ( !outSection.isEmpty() ) {
1257 localPart->setPartSpecifier(outSection);
1258 } else {
1259 localPart->setPartSpecifier(inSection);
1260 }
1261
1262 // is multipart (otherwise it is a simplepart and handled later)
1263 while (inWords[0] == '(')
1264 {
1265 outSection = TQString::number(++section);
1266 if (!init)
1267 outSection = inSection + "." + outSection;
1268 mimeHeader *subpart = parseBodyStructure (inWords, outSection, 0);
1269 localPart->addNestedPart (subpart);
1270 }
1271
1272 // fetch subtype
1273 subtype = parseOneWordC (inWords);
1274
1275 localPart->setType ("MULTIPART/" + b2c(subtype));
1276
1277 // fetch parameters
1278 parameters = parseParameters (inWords);
1279 {
1280 TQAsciiDictIterator < TQString > it (parameters);
1281
1282 while (it.current ())
1283 {
1284 localPart->setTypeParm (it.currentKey (), *(it.current ()));
1285 ++it;
1286 }
1287 parameters.clear ();
1288 }
1289
1290 // body disposition
1291 parameters = parseDisposition (inWords);
1292 {
1293 TQString *disposition = parameters["content-disposition"];
1294
1295 if (disposition)
1296 localPart->setDisposition (disposition->ascii ());
1297 parameters.remove ("content-disposition");
1298 TQAsciiDictIterator < TQString > it (parameters);
1299 while (it.current ())
1300 {
1301 localPart->setDispositionParm (it.currentKey (),
1302 *(it.current ()));
1303 ++it;
1304 }
1305 parameters.clear ();
1306 }
1307
1308 // body language
1309 parseSentence (inWords);
1310
1311 }
1312 else
1313 {
1314 // is simple part
1315 inWords.pos--;
1316 inWords.data[inWords.pos] = '('; //fake a sentence
1317 if ( localPart )
1318 inSection = inSection + ".1";
1319 localPart = parseSimplePart (inWords, inSection, localPart);
1320 inWords.pos--;
1321 inWords.data[inWords.pos] = ')'; //remove fake
1322 }
1323
1324 // see if we have more to come
1325 while (!inWords.isEmpty () && inWords[0] != ')')
1326 {
1327 //eat the extensions to this part
1328 if (inWords[0] == '(')
1329 parseSentence (inWords);
1330 else
1331 parseLiteralC(inWords);
1332 }
1333
1334 if (inWords[0] == ')')
1335 inWords.pos++;
1336 skipWS (inWords);
1337
1338 return localPart;
1339}
1340
1341void imapParser::parseBody (parseString & inWords)
1342{
1343 // see if we got a part specifier
1344 if (inWords[0] == '[')
1345 {
1346 TQCString specifier;
1347 TQCString label;
1348 inWords.pos++;
1349
1350 specifier = parseOneWordC (inWords, TRUE);
1351
1352 if (inWords[0] == '(')
1353 {
1354 inWords.pos++;
1355
1356 while (!inWords.isEmpty () && inWords[0] != ')')
1357 {
1358 label = parseOneWordC (inWords);
1359 }
1360
1361 if (!inWords.isEmpty () && inWords[0] == ')')
1362 inWords.pos++;
1363 }
1364 if (!inWords.isEmpty () && inWords[0] == ']')
1365 inWords.pos++;
1366 skipWS (inWords);
1367
1368 // parse the header
1369 if (specifier == "0")
1370 {
1371 mailHeader *envelope = 0;
1372 if (lastHandled)
1373 envelope = lastHandled->getHeader ();
1374
1375 if (!envelope || seenUid.isEmpty ())
1376 {
1377 kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl;
1378 // don't know where to put it, throw it away
1379 parseLiteralC(inWords, true);
1380 }
1381 else
1382 {
1383 kdDebug(7116) << "imapParser::parseBody - reading " << envelope << " " << seenUid.ascii () << endl;
1384 // fill it up with data
1385 TQString theHeader = parseLiteralC(inWords, true);
1386 mimeIOTQString myIO;
1387
1388 myIO.setString (theHeader);
1389 envelope->parseHeader (myIO);
1390
1391 }
1392 }
1393 else if (specifier == "HEADER.FIELDS")
1394 {
1395 // BODY[HEADER.FIELDS (References)] {n}
1396 //kdDebug(7116) << "imapParser::parseBody - HEADER.FIELDS: "
1397 // << TQCString(label.data(), label.size()+1) << endl;
1398 if (label == "REFERENCES")
1399 {
1400 mailHeader *envelope = 0;
1401 if (lastHandled)
1402 envelope = lastHandled->getHeader ();
1403
1404 if (!envelope || seenUid.isEmpty ())
1405 {
1406 kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl;
1407 // don't know where to put it, throw it away
1408 parseLiteralC (inWords, true);
1409 }
1410 else
1411 {
1412 TQCString references = parseLiteralC(inWords, true);
1413 int start = references.find ('<');
1414 int end = references.findRev ('>');
1415 if (start < end)
1416 references = references.mid (start, end - start + 1);
1417 envelope->setReferences(references.simplifyWhiteSpace());
1418 }
1419 }
1420 else
1421 { // not a header we care about throw it away
1422 parseLiteralC(inWords, true);
1423 }
1424 }
1425 else
1426 {
1427 if (specifier.find(".MIME") != -1)
1428 {
1429 mailHeader *envelope = new mailHeader;
1430 TQString theHeader = parseLiteralC(inWords, false);
1431 mimeIOTQString myIO;
1432 myIO.setString (theHeader);
1433 envelope->parseHeader (myIO);
1434 if (lastHandled)
1435 lastHandled->setHeader (envelope);
1436 return;
1437 }
1438 // throw it away
1439 kdDebug(7116) << "imapParser::parseBody - discarding " << seenUid.ascii () << endl;
1440 parseLiteralC(inWords, true);
1441 }
1442
1443 }
1444 else // no part specifier
1445 {
1446 mailHeader *envelope = 0;
1447 if (lastHandled)
1448 envelope = lastHandled->getHeader ();
1449
1450 if (!envelope || seenUid.isEmpty ())
1451 {
1452 kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl;
1453 // don't know where to put it, throw it away
1454 parseSentence (inWords);
1455 }
1456 else
1457 {
1458 kdDebug(7116) << "imapParser::parseBody - reading " << envelope << " " << seenUid.ascii () << endl;
1459 // fill it up with data
1460 TQString section;
1461 mimeHeader *body = parseBodyStructure (inWords, section, envelope);
1462 if (body != envelope)
1463 delete body;
1464 }
1465 }
1466}
1467
1468void imapParser::parseFetch (ulong /* value */, parseString & inWords)
1469{
1470 if (inWords[0] != '(')
1471 return;
1472 inWords.pos++;
1473 skipWS (inWords);
1474
1475 delete lastHandled;
1476 lastHandled = 0;
1477
1478 while (!inWords.isEmpty () && inWords[0] != ')')
1479 {
1480 if (inWords[0] == '(')
1481 parseSentence (inWords);
1482 else
1483 {
1484 TQCString word = parseLiteralC(inWords, false, true);
1485
1486 if(!word.isEmpty()) {
1487 switch (word[0])
1488 {
1489 case 'E':
1490 if (word == "ENVELOPE")
1491 {
1492 mailHeader *envelope = 0;
1493
1494 if (lastHandled)
1495 envelope = lastHandled->getHeader ();
1496 else
1497 lastHandled = new imapCache();
1498
1499 if (envelope && !envelope->getMessageId ().isEmpty ())
1500 {
1501 // we have seen this one already
1502 // or don't know where to put it
1503 parseSentence (inWords);
1504 }
1505 else
1506 {
1507 envelope = parseEnvelope (inWords);
1508 if (envelope)
1509 {
1510 envelope->setPartSpecifier (seenUid + ".0");
1511 lastHandled->setHeader (envelope);
1512 lastHandled->setUid (seenUid.toULong ());
1513 }
1514 }
1515 }
1516 break;
1517
1518 case 'B':
1519 if (word == "BODY")
1520 {
1521 parseBody (inWords);
1522 }
1523 else if (word == "BODY[]" )
1524 {
1525 // Do the same as with "RFC822"
1526 parseLiteralC(inWords, true);
1527 }
1528 else if (word == "BODYSTRUCTURE")
1529 {
1530 mailHeader *envelope = 0;
1531
1532 if (lastHandled)
1533 envelope = lastHandled->getHeader ();
1534
1535 // fill it up with data
1536 TQString section;
1537 mimeHeader *body =
1538 parseBodyStructure (inWords, section, envelope);
1539 TQByteArray data;
1540 TQDataStream stream( data, IO_WriteOnly );
1541 if (body) body->serialize(stream);
1542 parseRelay(data);
1543
1544 delete body;
1545 }
1546 break;
1547
1548 case 'U':
1549 if (word == "UID")
1550 {
1551 seenUid = parseOneWordC(inWords);
1552 mailHeader *envelope = 0;
1553 if (lastHandled)
1554 envelope = lastHandled->getHeader ();
1555 else
1556 lastHandled = new imapCache();
1557
1558 if (seenUid.isEmpty ())
1559 {
1560 // unknown what to do
1561 kdDebug(7116) << "imapParser::parseFetch - UID empty" << endl;
1562 }
1563 else
1564 {
1565 lastHandled->setUid (seenUid.toULong ());
1566 }
1567 if (envelope)
1568 envelope->setPartSpecifier (seenUid);
1569 }
1570 break;
1571
1572 case 'R':
1573 if (word == "RFC822.SIZE")
1574 {
1575 ulong size;
1576 parseOneNumber (inWords, size);
1577
1578 if (!lastHandled) lastHandled = new imapCache();
1579 lastHandled->setSize (size);
1580 }
1581 else if (word.find ("RFC822") == 0)
1582 {
1583 // might be RFC822 RFC822.TEXT RFC822.HEADER
1584 parseLiteralC(inWords, true);
1585 }
1586 break;
1587
1588 case 'I':
1589 if (word == "INTERNALDATE")
1590 {
1591 TQCString date = parseOneWordC(inWords);
1592 if (!lastHandled) lastHandled = new imapCache();
1593 lastHandled->setDate(date);
1594 }
1595 break;
1596
1597 case 'F':
1598 if (word == "FLAGS")
1599 {
1600 //kdDebug(7116) << "GOT FLAGS " << inWords.cstr() << endl;
1601 if (!lastHandled) lastHandled = new imapCache();
1602 lastHandled->setFlags (imapInfo::_flags (inWords.cstr()));
1603 }
1604 break;
1605
1606 default:
1607 parseLiteralC(inWords);
1608 break;
1609 }
1610 } else {
1611 parseLiteralC(inWords);
1612 }
1613 }
1614 }
1615
1616 // see if we have more to come
1617 while (!inWords.isEmpty () && inWords[0] != ')')
1618 {
1619 //eat the extensions to this part
1620 if (inWords[0] == '(')
1621 parseSentence (inWords);
1622 else
1623 parseLiteralC(inWords);
1624 }
1625
1626 if (inWords.isEmpty() || inWords[0] != ')')
1627 return;
1628 inWords.pos++;
1629 skipWS (inWords);
1630}
1631
1632
1633// default parser
1634void imapParser::parseSentence (parseString & inWords)
1635{
1636 bool first = true;
1637 int stack = 0;
1638
1639 //find the first nesting parentheses
1640
1641 while (!inWords.isEmpty () && (stack != 0 || first))
1642 {
1643 first = false;
1644 skipWS (inWords);
1645
1646 unsigned char ch = inWords[0];
1647 switch (ch)
1648 {
1649 case '(':
1650 inWords.pos++;
1651 ++stack;
1652 break;
1653 case ')':
1654 inWords.pos++;
1655 --stack;
1656 break;
1657 case '[':
1658 inWords.pos++;
1659 ++stack;
1660 break;
1661 case ']':
1662 inWords.pos++;
1663 --stack;
1664 break;
1665 default:
1666 parseLiteralC(inWords);
1667 skipWS (inWords);
1668 break;
1669 }
1670 }
1671 skipWS (inWords);
1672}
1673
1674void imapParser::parseRecent (ulong value, parseString & result)
1675{
1676 selectInfo.setRecent (value);
1677 result.pos = result.data.size();
1678}
1679
1680void imapParser::parseNamespace (parseString & result)
1681{
1682 if ( result[0] != '(' )
1683 return;
1684
1685 TQString delimEmpty;
1686 if ( namespaceToDelimiter.contains( TQString() ) )
1687 delimEmpty = namespaceToDelimiter[TQString()];
1688
1689 namespaceToDelimiter.clear();
1690 imapNamespaces.clear();
1691
1692 // remember what section we're in (user, other users, shared)
1693 int ns = -1;
1694 bool personalAvailable = false;
1695 while ( !result.isEmpty() )
1696 {
1697 if ( result[0] == '(' )
1698 {
1699 result.pos++; // tie off (
1700 if ( result[0] == '(' )
1701 {
1702 // new namespace section
1703 result.pos++; // tie off (
1704 ++ns;
1705 }
1706 // namespace prefix
1707 TQCString prefix = parseOneWordC( result );
1708 // delimiter
1709 TQCString delim = parseOneWordC( result );
1710 kdDebug(7116) << "imapParser::parseNamespace ns='" << prefix <<
1711 "',delim='" << delim << "'" << endl;
1712 if ( ns == 0 )
1713 {
1714 // at least one personal ns
1715 personalAvailable = true;
1716 }
1717 TQString nsentry = TQString::number( ns ) + "=" + TQString(prefix) +
1718 "=" + TQString(delim);
1719 imapNamespaces.append( nsentry );
1720 if ( prefix.right( 1 ) == delim ) {
1721 // strip delimiter to get a correct entry for comparisons
1722 prefix.resize( prefix.length() );
1723 }
1724 namespaceToDelimiter[prefix] = delim;
1725
1726 result.pos++; // tie off )
1727 skipWS( result );
1728 } else if ( result[0] == ')' )
1729 {
1730 result.pos++; // tie off )
1731 skipWS( result );
1732 } else if ( result[0] == 'N' )
1733 {
1734 // drop NIL
1735 ++ns;
1736 parseOneWordC( result );
1737 } else {
1738 // drop whatever it is
1739 parseOneWordC( result );
1740 }
1741 }
1742 if ( !delimEmpty.isEmpty() ) {
1743 // remember default delimiter
1744 namespaceToDelimiter[TQString()] = delimEmpty;
1745 if ( !personalAvailable )
1746 {
1747 // at least one personal ns would be nice
1748 kdDebug(7116) << "imapParser::parseNamespace - registering own personal ns" << endl;
1749 TQString nsentry = "0==" + delimEmpty;
1750 imapNamespaces.append( nsentry );
1751 }
1752 }
1753}
1754
1755int imapParser::parseLoop ()
1756{
1757 parseString result;
1758
1759 if (!parseReadLine(result.data)) return -1;
1760
1761 //kdDebug(7116) << result.cstr(); // includes \n
1762
1763 if (result.data.isEmpty())
1764 return 0;
1765 if (!sentQueue.count ())
1766 {
1767 // maybe greeting or BYE everything else SHOULD not happen, use NOOP or IDLE
1768 kdDebug(7116) << "imapParser::parseLoop - unhandledResponse: \n" << result.cstr() << endl;
1769 unhandled << result.cstr();
1770 }
1771 else
1772 {
1773 imapCommand *current = sentQueue.at (0);
1774 switch (result[0])
1775 {
1776 case '*':
1777 result.data.resize(result.data.size() - 2); // tie off CRLF
1778 parseUntagged (result);
1779 break;
1780 case '+':
1781 continuation.duplicate(result.data);
1782 break;
1783 default:
1784 {
1785 TQCString tag = parseLiteralC(result);
1786 if (current->id() == tag.data())
1787 {
1788 result.data.resize(result.data.size() - 2); // tie off CRLF
1789 TQByteArray resultCode = parseLiteral (result); //the result
1790 current->setResult (resultCode);
1791 current->setResultInfo(result.cstr());
1792 current->setComplete ();
1793
1794 sentQueue.removeRef (current);
1795 completeQueue.append (current);
1796 if (result.length())
1797 parseResult (resultCode, result, current->command());
1798 }
1799 else
1800 {
1801 kdDebug(7116) << "imapParser::parseLoop - unknown tag '" << tag << "'" << endl;
1802 TQCString cstr = tag + " " + result.cstr();
1803 result.data = cstr;
1804 result.pos = 0;
1805 result.data.resize(cstr.length());
1806 }
1807 }
1808 break;
1809 }
1810 }
1811
1812 return 1;
1813}
1814
1815void
1816imapParser::parseRelay (const TQByteArray & buffer)
1817{
1818 Q_UNUSED(buffer);
1819 tqWarning
1820 ("imapParser::parseRelay - virtual function not reimplemented - data lost");
1821}
1822
1823void
1824imapParser::parseRelay (ulong len)
1825{
1826 Q_UNUSED(len);
1827 tqWarning
1828 ("imapParser::parseRelay - virtual function not reimplemented - announcement lost");
1829}
1830
1831bool imapParser::parseRead (TQByteArray & buffer, ulong len, ulong relay)
1832{
1833 Q_UNUSED(buffer);
1834 Q_UNUSED(len);
1835 Q_UNUSED(relay);
1836 tqWarning
1837 ("imapParser::parseRead - virtual function not reimplemented - no data read");
1838 return FALSE;
1839}
1840
1841bool imapParser::parseReadLine (TQByteArray & buffer, ulong relay)
1842{
1843 Q_UNUSED(buffer);
1844 Q_UNUSED(relay);
1845 tqWarning
1846 ("imapParser::parseReadLine - virtual function not reimplemented - no data read");
1847 return FALSE;
1848}
1849
1850void
1851imapParser::parseWriteLine (const TQString & str)
1852{
1853 Q_UNUSED(str);
1854 tqWarning
1855 ("imapParser::parseWriteLine - virtual function not reimplemented - no data written");
1856}
1857
1858void
1859imapParser::parseURL (const KURL & _url, TQString & _box, TQString & _section,
1860 TQString & _type, TQString & _uid, TQString & _validity, TQString & _info)
1861{
1862 TQStringList parameters;
1863
1864 _box = _url.path ();
1865 kdDebug(7116) << "imapParser::parseURL " << _box << endl;
1866 int paramStart = _box.find("/;");
1867 if ( paramStart > -1 )
1868 {
1869 TQString paramString = _box.right( _box.length() - paramStart-2 );
1870 parameters = TQStringList::split (';', paramString); //split parameters
1871 _box.truncate( paramStart ); // strip parameters
1872 }
1873 // extract parameters
1874 for (TQStringList::ConstIterator it (parameters.begin ());
1875 it != parameters.end (); ++it)
1876 {
1877 TQString temp = (*it);
1878
1879 int pt = temp.find ('/');
1880 if (pt > 0)
1881 {
1882 if (temp.findRev ('"', pt) == -1 || temp.find('"', pt) == -1)
1883 {
1884 // if we have non-quoted '/' separator we'll just nuke it
1885 temp.truncate(pt);
1886 }
1887 }
1888 if (temp.find ("section=", 0, false) == 0)
1889 _section = temp.right (temp.length () - 8);
1890 else if (temp.find ("type=", 0, false) == 0)
1891 _type = temp.right (temp.length () - 5);
1892 else if (temp.find ("uid=", 0, false) == 0)
1893 _uid = temp.right (temp.length () - 4);
1894 else if (temp.find ("uidvalidity=", 0, false) == 0)
1895 _validity = temp.right (temp.length () - 12);
1896 else if (temp.find ("info=", 0, false) == 0)
1897 _info = temp.right (temp.length () - 5);
1898 }
1899// kdDebug(7116) << "URL: section= " << _section << ", type= " << _type << ", uid= " << _uid << endl;
1900// kdDebug(7116) << "URL: user() " << _url.user() << endl;
1901// kdDebug(7116) << "URL: path() " << _url.path() << endl;
1902// kdDebug(7116) << "URL: encodedPathAndQuery() " << _url.encodedPathAndQuery() << endl;
1903
1904 if (!_box.isEmpty ())
1905 {
1906 // strip /
1907 if (_box[0] == '/')
1908 _box = _box.right (_box.length () - 1);
1909 if (!_box.isEmpty () && _box[_box.length () - 1] == '/')
1910 _box.truncate(_box.length() - 1);
1911 }
1912 kdDebug(7116) << "URL: box= " << _box << ", section= " << _section << ", type= "
1913 << _type << ", uid= " << _uid << ", validity= " << _validity << ", info= " << _info << endl;
1914}
1915
1916
1917TQCString imapParser::parseLiteralC(parseString & inWords, bool relay, bool stopAtBracket, int *outlen) {
1918
1919 if (!inWords.isEmpty() && inWords[0] == '{')
1920 {
1921 TQCString retVal;
1922 long srunLen = inWords.find ('}', 1); // Can return -1, so use a signed long
1923 if (srunLen > 0)
1924 {
1925 ulong runLen = (ulong)srunLen;
1926 bool proper;
1927 ulong runLenSave = runLen + 1;
1928 TQCString tmpstr(runLen);
1929 inWords.takeMidNoResize(tmpstr, 1, runLen - 1);
1930 runLen = tmpstr.toULong (&proper);
1931 inWords.pos += runLenSave;
1932 if (proper)
1933 {
1934 //now get the literal from the server
1935 if (relay)
1936 parseRelay (runLen);
1937 TQByteArray rv;
1938 parseRead (rv, runLen, relay ? runLen : 0);
1939 rv.resize(TQMAX(runLen, rv.size())); // what's the point?
1940 retVal = b2c(rv);
1941 inWords.clear();
1942 parseReadLine (inWords.data); // must get more
1943
1944 // no duplicate data transfers
1945 relay = false;
1946 }
1947 else
1948 {
1949 kdDebug(7116) << "imapParser::parseLiteral - error parsing {} - " /*<< strLen*/ << endl;
1950 }
1951 }
1952 else
1953 {
1954 inWords.clear();
1955 kdDebug(7116) << "imapParser::parseLiteral - error parsing unmatched {" << endl;
1956 }
1957 if (outlen) {
1958 *outlen = retVal.length(); // optimize me
1959 }
1960 skipWS (inWords);
1961 return retVal;
1962 }
1963
1964 return parseOneWordC(inWords, stopAtBracket, outlen);
1965}
1966
1967// does not know about literals ( {7} literal )
1968TQCString imapParser::parseOneWordC (parseString & inWords, bool stopAtBracket, int *outLen)
1969{
1970 uint retValSize = 0;
1971 uint len = inWords.length();
1972 if (len == 0) {
1973 return TQCString();
1974 }
1975
1976 if (len > 0 && inWords[0] == '"')
1977 {
1978 unsigned int i = 1;
1979 bool quote = FALSE;
1980 while (i < len && (inWords[i] != '"' || quote))
1981 {
1982 if (inWords[i] == '\\') quote = !quote;
1983 else quote = FALSE;
1984 i++;
1985 }
1986 if (i < len)
1987 {
1988 TQCString retVal(i);
1989 inWords.pos++;
1990 inWords.takeLeftNoResize(retVal, i - 1);
1991 len = i - 1;
1992 int offset = 0;
1993 for (unsigned int j = 0; j <= len; j++) {
1994 if (retVal[j] == '\\') {
1995 offset++;
1996 j++;
1997 }
1998 retVal[j - offset] = retVal[j];
1999 }
2000 retVal[len - offset] = 0;
2001 retValSize = len - offset;
2002 inWords.pos += i;
2003 skipWS (inWords);
2004 if (outLen) {
2005 *outLen = retValSize;
2006 }
2007 return retVal;
2008 }
2009 else
2010 {
2011 kdDebug(7116) << "imapParser::parseOneWord - error parsing unmatched \"" << endl;
2012 TQCString retVal = inWords.cstr();
2013 retValSize = len;
2014 inWords.clear();
2015 if (outLen) {
2016 *outLen = retValSize;
2017 }
2018 return retVal;
2019 }
2020 }
2021 else
2022 {
2023 // not quoted
2024 unsigned int i;
2025 // search for end
2026 for (i = 0; i < len; ++i) {
2027 char ch = inWords[i];
2028 if (ch <= ' ' || ch == '(' || ch == ')' ||
2029 (stopAtBracket && (ch == '[' || ch == ']')))
2030 break;
2031 }
2032
2033 TQCString retVal(i+1);
2034 inWords.takeLeftNoResize(retVal, i);
2035 retValSize = i;
2036 inWords.pos += i;
2037
2038 if (retVal == "NIL") {
2039 retVal.truncate(0);
2040 retValSize = 0;
2041 }
2042 skipWS (inWords);
2043 if (outLen) {
2044 *outLen = retValSize;
2045 }
2046 return retVal;
2047 }
2048}
2049
2050bool imapParser::parseOneNumber (parseString & inWords, ulong & num)
2051{
2052 bool valid;
2053 num = parseOneWordC(inWords, TRUE).toULong(&valid);
2054 return valid;
2055}
2056
2057bool imapParser::hasCapability (const TQString & cap)
2058{
2059 TQString c = cap.lower();
2060// kdDebug(7116) << "imapParser::hasCapability - Looking for '" << cap << "'" << endl;
2061 for (TQStringList::ConstIterator it = imapCapabilities.begin ();
2062 it != imapCapabilities.end (); ++it)
2063 {
2064// kdDebug(7116) << "imapParser::hasCapability - Examining '" << (*it) << "'" << endl;
2065 if ( !(kasciistricmp(c.ascii(), (*it).ascii())) )
2066 {
2067 return true;
2068 }
2069 }
2070 return false;
2071}
2072
2073void imapParser::removeCapability (const TQString & cap)
2074{
2075 imapCapabilities.remove(cap.lower());
2076}
2077
2078TQString imapParser::namespaceForBox( const TQString & box )
2079{
2080 kdDebug(7116) << "imapParse::namespaceForBox " << box << endl;
2081 TQString myNamespace;
2082 if ( !box.isEmpty() )
2083 {
2084 TQValueList<TQString> list = namespaceToDelimiter.keys();
2085 TQString cleanPrefix;
2086 for ( TQValueList<TQString>::Iterator it = list.begin(); it != list.end(); ++it )
2087 {
2088 if ( !(*it).isEmpty() && box.find( *it ) != -1 )
2089 return (*it);
2090 }
2091 }
2092 return myNamespace;
2093}
2094
encapulate a IMAP command
Definition: imapcommand.h:38
const TQString & id()
get the id
Definition: imapcommand.cpp:94
const TQString getStr()
returns the data to send to the server The function returns the complete data to be sent to the serve...
void setComplete()
set the completed state
const TQString & parameter()
get the parameter
bool isComplete()
is it complete?
Definition: imapcommand.cpp:76
const TQString & command()
get the command
void setId(const TQString &)
set the id
void setResultInfo(const TQString &)
set the completed state
const TQString & resultInfo()
get information about the result
Definition: imapcommand.cpp:88
void setResult(const TQString &)
set the completed state
const TQString & result()
get the result of the command
Definition: imapcommand.cpp:82
void setDate(const TQCString &_str)
set the date
Definition: mailheader.h:129
void setSubject(const TQString &_str)
set a unicode subject
Definition: mailheader.h:99
a string used during parsing the string allows you to move the effective start of the string using st...
Definition: imapparser.h:53
static TQString quoteIMAP(const TQString &src)
replace " with \" and \ with \\ " and \ characters
Definition: rfcdecoder.cpp:158
static TQString fromIMAP(const TQString &src)
Convert an IMAP mailbox to a Unicode path.
Definition: rfcdecoder.cpp:55