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