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
45 extern "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
63 static 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 
75 imapParser::imapParser ()
76 {
77  sentQueue.setAutoDelete (false);
78  completeQueue.setAutoDelete (true);
79  currentState = ISTATE_NO;
80  commandCounter = 0;
81  lastHandled = 0;
82 }
83 
84 imapParser::~imapParser ()
85 {
86  delete lastHandled;
87  lastHandled = 0;
88 }
89 
91 imapParser::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 
103 imapCommand *
104 imapParser::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 
148 bool
149 imapParser::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
172 static 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 
217 bool
218 imapParser::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 
340 void
341 imapParser::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 
534 void
535 imapParser::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 
679 void imapParser::parseCapability (parseString & result)
680 {
681  TQCString temp( result.cstr() );
682  imapCapabilities = TQStringList::split ( ' ', KPIM::kAsciiToLower( temp.data() ) );
683 }
684 
685 void imapParser::parseFlags (parseString & result)
686 {
687  selectInfo.setFlags(result.cstr());
688 }
689 
690 void 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 
710 void imapParser::parseLsub (parseString & result)
711 {
712  imapList this_one (result.cstr(), *this);
713  listResponses.append (this_one);
714 }
715 
716 void 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 
727 void 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 
738 void 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 
757 void 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 
781 void 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 
798 void imapParser::parseCustom (parseString & result)
799 {
800  int outlen = 1;
801  TQCString word = parseLiteralC (result, false, false, &outlen);
802  lastResults.append( word );
803 }
804 
805 void imapParser::parseOtherUser (parseString & result)
806 {
807  lastResults.append( parseOneWordC( result ) );
808 }
809 
810 void 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 
824 void 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 
835 void 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 
842 void imapParser::parseSearch (parseString & result)
843 {
844  ulong value;
845 
846  while (parseOneNumber (result, value))
847  {
848  lastResults.append (TQString::number(value));
849  }
850 }
851 
852 void 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 
888 void imapParser::parseExists (ulong value, parseString & result)
889 {
890  selectInfo.setCount (value);
891  result.pos = result.data.size();
892 }
893 
894 void imapParser::parseExpunge (ulong value, parseString & result)
895 {
896  Q_UNUSED(value);
897  Q_UNUSED(result);
898 }
899 
900 void 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 
930 const 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 
947 mailHeader * 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
1022 TQAsciiDict < 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
1059 TQAsciiDict < 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 
1092 mimeHeader * 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 
1213 mimeHeader * 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 
1341 void 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 
1468 void 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
1634 void 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 
1674 void imapParser::parseRecent (ulong value, parseString & result)
1675 {
1676  selectInfo.setRecent (value);
1677  result.pos = result.data.size();
1678 }
1679 
1680 void 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 
1755 int 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 
1815 void
1816 imapParser::parseRelay (const TQByteArray & buffer)
1817 {
1818  Q_UNUSED(buffer);
1819  tqWarning
1820  ("imapParser::parseRelay - virtual function not reimplemented - data lost");
1821 }
1822 
1823 void
1824 imapParser::parseRelay (ulong len)
1825 {
1826  Q_UNUSED(len);
1827  tqWarning
1828  ("imapParser::parseRelay - virtual function not reimplemented - announcement lost");
1829 }
1830 
1831 bool 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 
1841 bool 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 
1850 void
1851 imapParser::parseWriteLine (const TQString & str)
1852 {
1853  Q_UNUSED(str);
1854  tqWarning
1855  ("imapParser::parseWriteLine - virtual function not reimplemented - no data written");
1856 }
1857 
1858 void
1859 imapParser::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 
1917 TQCString 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 )
1968 TQCString 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 
2050 bool imapParser::parseOneNumber (parseString & inWords, ulong & num)
2051 {
2052  bool valid;
2053  num = parseOneWordC(inWords, TRUE).toULong(&valid);
2054  return valid;
2055 }
2056 
2057 bool 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 
2073 void imapParser::removeCapability (const TQString & cap)
2074 {
2075  imapCapabilities.remove(cap.lower());
2076 }
2077 
2078 TQString 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