tdeioslave/imap4

imapparser.cc

00001 /**********************************************************************
00002  *
00003  *   imapparser.cc  - IMAP4rev1 Parser
00004  *   Copyright (C) 2001-2002 Michael Haeckel <haeckel@kde.org>
00005  *   Copyright (C) 2000 s.carstens@gmx.de
00006  *
00007  *   This program is free software; you can redistribute it and/or modify
00008  *   it under the terms of the GNU General Public License as published by
00009  *   the Free Software Foundation; either version 2 of the License, or
00010  *   (at your option) any later version.
00011  *
00012  *   This program is distributed in the hope that it will be useful,
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *   GNU General Public License for more details.
00016  *
00017  *   You should have received a copy of the GNU General Public License
00018  *   along with this program; if not, write to the Free Software
00019  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  *   Send comments and bug fixes to s.carstens@gmx.de
00022  *
00023  *********************************************************************/
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #include <config.h>
00027 #endif
00028 
00029 #include "rfcdecoder.h"
00030 
00031 #include "imapparser.h"
00032 
00033 #include "imapinfo.h"
00034 
00035 #include "mailheader.h"
00036 #include "mimeheader.h"
00037 #include "mailaddress.h"
00038 
00039 #include <sys/types.h>
00040 
00041 #include <stdlib.h>
00042 #include <unistd.h>
00043 
00044 #ifdef HAVE_LIBSASL2
00045 extern "C" {
00046 #include <sasl/sasl.h>
00047 }
00048 #endif
00049 
00050 #include <tqregexp.h>
00051 #include <tqbuffer.h>
00052 #include <tqstring.h>
00053 #include <tqstringlist.h>
00054 
00055 #include <kdebug.h>
00056 #include <kmdcodec.h>
00057 #include <kurl.h>
00058 
00059 #include <kasciistricmp.h>
00060 #include <kasciistringtools.h>
00061 
00062 #ifdef HAVE_LIBSASL2
00063 static sasl_callback_t callbacks[] = {
00064     { SASL_CB_ECHOPROMPT, NULL, NULL },
00065     { SASL_CB_NOECHOPROMPT, NULL, NULL },
00066     { SASL_CB_GETREALM, NULL, NULL },
00067     { SASL_CB_USER, NULL, NULL },
00068     { SASL_CB_AUTHNAME, NULL, NULL },
00069     { SASL_CB_PASS, NULL, NULL },
00070     { SASL_CB_CANON_USER, NULL, NULL },
00071     { SASL_CB_LIST_END, NULL, NULL }
00072 };
00073 #endif
00074 
00075 imapParser::imapParser ()
00076 {
00077   sentQueue.setAutoDelete (false);
00078   completeQueue.setAutoDelete (true);
00079   currentState = ISTATE_NO;
00080   commandCounter = 0;
00081   lastHandled = 0;
00082 }
00083 
00084 imapParser::~imapParser ()
00085 {
00086   delete lastHandled;
00087   lastHandled = 0;
00088 }
00089 
00090 imapCommand *
00091 imapParser::doCommand (imapCommand * aCmd)
00092 {
00093   int pl = 0;
00094   sendCommand (aCmd);
00095   while (pl != -1 && !aCmd->isComplete ()) {
00096     while ((pl = parseLoop ()) == 0)
00097      ;
00098   }
00099 
00100   return aCmd;
00101 }
00102 
00103 imapCommand *
00104 imapParser::sendCommand (imapCommand * aCmd)
00105 {
00106   aCmd->setId (TQString::number(commandCounter++));
00107   sentQueue.append (aCmd);
00108 
00109   continuation.resize(0);
00110   const TQString& command = aCmd->command();
00111 
00112   if (command == "SELECT" || command == "EXAMINE")
00113   {
00114      // we need to know which box we are selecting
00115     parseString p;
00116     p.fromString(aCmd->parameter());
00117     currentBox = parseOneWordC(p);
00118     kdDebug(7116) << "imapParser::sendCommand - setting current box to " << currentBox << endl;
00119   }
00120   else if (command == "CLOSE")
00121   {
00122      // we no longer have a box open
00123     currentBox = TQString();
00124   }
00125   else if (command.find ("SEARCH") != -1
00126            || command == "GETACL"
00127            || command == "LISTRIGHTS"
00128            || command == "MYRIGHTS"
00129            || command == "GETANNOTATION"
00130            || command == "NAMESPACE"
00131            || command == "GETQUOTAROOT"
00132            || command == "GETQUOTA"
00133            || command == "X-GET-OTHER-USERS"
00134            || command == "X-GET-DELEGATES"
00135            || command == "X-GET-OUT-OF-OFFICE")
00136   {
00137     lastResults.clear ();
00138   }
00139   else if (command == "LIST"
00140            || command == "LSUB")
00141   {
00142     listResponses.clear ();
00143   }
00144   parseWriteLine (aCmd->getStr ());
00145   return aCmd;
00146 }
00147 
00148 bool
00149 imapParser::clientLogin (const TQString & aUser, const TQString & aPass,
00150   TQString & resultInfo)
00151 {
00152   imapCommand *cmd;
00153   bool retVal = false;
00154 
00155   cmd =
00156     doCommand (new
00157                imapCommand ("LOGIN", "\"" + rfcDecoder::quoteIMAP(aUser)
00158                + "\" \"" + rfcDecoder::quoteIMAP(aPass) + "\""));
00159 
00160   if (cmd->result () == "OK")
00161   {
00162     currentState = ISTATE_LOGIN;
00163     retVal = true;
00164   }
00165   resultInfo = cmd->resultInfo();
00166   completeQueue.removeRef (cmd);
00167 
00168   return retVal;
00169 }
00170 
00171 #ifdef HAVE_LIBSASL2
00172 static bool sasl_interact( TDEIO::SlaveBase *slave, TDEIO::AuthInfo &ai, void *in )
00173 {
00174   kdDebug(7116) << "sasl_interact" << endl;
00175   sasl_interact_t *interact = ( sasl_interact_t * ) in;
00176 
00177   //some mechanisms do not require username && pass, so it doesn't need a popup
00178   //window for getting this info
00179   for ( ; interact->id != SASL_CB_LIST_END; interact++ ) {
00180     if ( interact->id == SASL_CB_AUTHNAME ||
00181          interact->id == SASL_CB_PASS ) {
00182 
00183       if ( ai.username.isEmpty() || ai.password.isEmpty() ) {
00184         if (!slave->openPassDlg(ai))
00185           return false;
00186       }
00187       break;
00188     }
00189   }
00190 
00191   interact = ( sasl_interact_t * ) in;
00192   while( interact->id != SASL_CB_LIST_END ) {
00193     kdDebug(7116) << "SASL_INTERACT id: " << interact->id << endl;
00194     switch( interact->id ) {
00195       case SASL_CB_USER:
00196       case SASL_CB_AUTHNAME:
00197         kdDebug(7116) << "SASL_CB_[USER|AUTHNAME]: '" << ai.username << "'" << endl;
00198         interact->result = strdup( ai.username.utf8() );
00199         interact->len = strlen( (const char *) interact->result );
00200         break;
00201       case SASL_CB_PASS:
00202         kdDebug(7116) << "SASL_CB_PASS: [hidden] " << endl;
00203         interact->result = strdup( ai.password.utf8() );
00204         interact->len = strlen( (const char *) interact->result );
00205         break;
00206       default:
00207         interact->result = 0;
00208         interact->len = 0;
00209         break;
00210     }
00211     interact++;
00212   }
00213   return true;
00214 }
00215 #endif
00216 
00217 bool
00218 imapParser::clientAuthenticate ( TDEIO::SlaveBase *slave, TDEIO::AuthInfo &ai,
00219   const TQString & aFTQDN, const TQString & aAuth, bool isSSL, TQString & resultInfo)
00220 {
00221   bool retVal = false;
00222 #ifdef HAVE_LIBSASL2
00223   int result;
00224   sasl_conn_t *conn = 0;
00225   sasl_interact_t *client_interact = 0;
00226   const char *out = 0;
00227   uint outlen = 0;
00228   const char *mechusing = 0;
00229   TQByteArray tmp, challenge;
00230 
00231   kdDebug(7116) << "aAuth: " << aAuth << " FTQDN: " << aFTQDN << " isSSL: " << isSSL << endl;
00232 
00233   // see if server supports this authenticator
00234   if (!hasCapability ("AUTH=" + aAuth))
00235     return false;
00236 
00237 //  result = sasl_client_new( isSSL ? "imaps" : "imap",
00238   result = sasl_client_new( "imap", /* FIXME: with cyrus-imapd, even imaps' digest-uri
00239                                        must be 'imap'. I don't know if it's good or bad. */
00240                        aFTQDN.latin1(),
00241                        0, 0, callbacks, 0, &conn );
00242 
00243   if ( result != SASL_OK ) {
00244     kdDebug(7116) << "sasl_client_new failed with: " << result << endl;
00245     resultInfo = TQString::fromUtf8( sasl_errdetail( conn ) );
00246     return false;
00247   }
00248 
00249   do {
00250     result = sasl_client_start(conn, aAuth.latin1(), &client_interact,
00251                        hasCapability("SASL-IR") ? &out : 0, &outlen, &mechusing);
00252 
00253     if ( result == SASL_INTERACT ) {
00254       if ( !sasl_interact( slave, ai, client_interact ) ) {
00255         sasl_dispose( &conn );
00256         return false;
00257       }
00258     }
00259   } while ( result == SASL_INTERACT );
00260 
00261   if ( result != SASL_CONTINUE && result != SASL_OK ) {
00262     kdDebug(7116) << "sasl_client_start failed with: " << result << endl;
00263     resultInfo = TQString::fromUtf8( sasl_errdetail( conn ) );
00264     sasl_dispose( &conn );
00265     return false;
00266   }
00267   imapCommand *cmd;
00268 
00269   tmp.setRawData( out, outlen );
00270   KCodecs::base64Encode( tmp, challenge );
00271   tmp.resetRawData( out, outlen );
00272   // then lets try it
00273   TQString firstCommand = aAuth;
00274   if ( !challenge.isEmpty() ) {
00275     firstCommand += " ";
00276     firstCommand += TQString::fromLatin1( challenge.data(), challenge.size() );
00277   }
00278   cmd = sendCommand (new imapCommand ("AUTHENTICATE", firstCommand.latin1()));
00279 
00280   int pl = 0;
00281   while ( pl != -1 && !cmd->isComplete () )
00282   {
00283     //read the next line
00284     while ((pl = parseLoop()) == 0) ;
00285 
00286     if (!continuation.isEmpty())
00287     {
00288 //      kdDebug(7116) << "S: " << TQCString(continuation.data(),continuation.size()+1) << endl;
00289       if ( continuation.size() > 4 ) {
00290         tmp.setRawData( continuation.data() + 2, continuation.size() - 4 );
00291         KCodecs::base64Decode( tmp, challenge );
00292 //        kdDebug(7116) << "S-1: " << TQCString(challenge.data(),challenge.size()+1) << endl;
00293         tmp.resetRawData( continuation.data() + 2, continuation.size() - 4 );
00294       }
00295 
00296       do {
00297         result = sasl_client_step(conn, challenge.isEmpty() ? 0 : challenge.data(),
00298                                   challenge.size(),
00299                                   &client_interact,
00300                                   &out, &outlen);
00301 
00302         if (result == SASL_INTERACT) {
00303           if ( !sasl_interact( slave, ai, client_interact ) ) {
00304             sasl_dispose( &conn );
00305             return false;
00306           }
00307         }
00308       } while ( result == SASL_INTERACT );
00309 
00310       if ( result != SASL_CONTINUE && result != SASL_OK ) {
00311         kdDebug(7116) << "sasl_client_step failed with: " << result << endl;
00312         resultInfo = TQString::fromUtf8( sasl_errdetail( conn ) );
00313         sasl_dispose( &conn );
00314         return false;
00315       }
00316 
00317       tmp.setRawData( out, outlen );
00318 //      kdDebug(7116) << "C-1: " << TQCString(tmp.data(),tmp.size()+1) << endl;
00319       KCodecs::base64Encode( tmp, challenge );
00320       tmp.resetRawData( out, outlen );
00321 //      kdDebug(7116) << "C: " << TQCString(challenge.data(),challenge.size()+1) << endl;
00322       parseWriteLine (challenge);
00323       continuation.resize(0);
00324     }
00325   }
00326 
00327   if (cmd->result () == "OK")
00328   {
00329     currentState = ISTATE_LOGIN;
00330     retVal = true;
00331   }
00332   resultInfo = cmd->resultInfo();
00333   completeQueue.removeRef (cmd);
00334 
00335   sasl_dispose( &conn ); //we don't use sasl_en/decode(), so it's safe to dispose the connection.
00336 #endif //HAVE_LIBSASL2
00337   return retVal;
00338 }
00339 
00340 void
00341 imapParser::parseUntagged (parseString & result)
00342 {
00343   //kdDebug(7116) << "imapParser::parseUntagged - '" << result.cstr() << "'" << endl;
00344 
00345   parseOneWordC(result);        // *
00346   TQByteArray what = parseLiteral (result); // see whats coming next
00347 
00348   if(!what.isEmpty ()) {
00349   switch (what[0])
00350   {
00351     //the status responses
00352   case 'B':                    // BAD or BYE
00353     if (tqstrncmp(what, "BAD", what.size()) == 0)
00354     {
00355       parseResult (what, result);
00356     }
00357     else if (tqstrncmp(what, "BYE", what.size()) == 0)
00358     {
00359       parseResult (what, result);
00360       if ( sentQueue.count() ) {
00361         // BYE that interrupts a command -> copy the reason for it
00362         imapCommand *current = sentQueue.at (0);
00363         current->setResultInfo(result.cstr());
00364       }
00365       currentState = ISTATE_NO;
00366     }
00367     break;
00368 
00369   case 'N':                    // NO
00370     if (what[1] == 'O' && what.size() == 2)
00371     {
00372       parseResult (what, result);
00373     }
00374     else if (tqstrncmp(what, "NAMESPACE", what.size()) == 0)
00375     {
00376       parseNamespace (result);
00377     }
00378     break;
00379 
00380   case 'O':                    // OK
00381     if (what[1] == 'K' && what.size() == 2)
00382     {
00383       parseResult (what, result);
00384     } else if (tqstrncmp(what, "OTHER-USER", 10) == 0) { // X-GET-OTHER-USER
00385       parseOtherUser (result);
00386     } else if (tqstrncmp(what, "OUT-OF-OFFICE", 13) == 0) { // X-GET-OUT-OF-OFFICE
00387       parseOutOfOffice (result);
00388     }
00389     break;
00390   case 'D':
00391     if (tqstrncmp(what, "DELEGATE", 8) == 0) { // X-GET-DELEGATES
00392       parseDelegate (result);
00393     }
00394     break;
00395 
00396   case 'P':                    // PREAUTH
00397     if (tqstrncmp(what, "PREAUTH", what.size()) == 0)
00398     {
00399       parseResult (what, result);
00400       currentState = ISTATE_LOGIN;
00401     }
00402     break;
00403 
00404     // parse the other responses
00405   case 'C':                    // CAPABILITY
00406     if (tqstrncmp(what, "CAPABILITY", what.size()) == 0)
00407     {
00408       parseCapability (result);
00409     }
00410     break;
00411 
00412   case 'F':                    // FLAGS
00413     if (tqstrncmp(what, "FLAGS", what.size()) == 0)
00414     {
00415       parseFlags (result);
00416     }
00417     break;
00418 
00419   case 'L':                    // LIST or LSUB or LISTRIGHTS
00420     if (tqstrncmp(what, "LIST", what.size()) == 0)
00421     {
00422       parseList (result);
00423     }
00424     else if (tqstrncmp(what, "LSUB", what.size()) == 0)
00425     {
00426       parseLsub (result);
00427     }
00428     else if (tqstrncmp(what, "LISTRIGHTS", what.size()) == 0)
00429     {
00430       parseListRights (result);
00431     }
00432     break;
00433 
00434   case 'M': // MYRIGHTS
00435     if (tqstrncmp(what, "MYRIGHTS", what.size()) == 0)
00436     {
00437       parseMyRights (result);
00438     }
00439     break;
00440   case 'S':                    // SEARCH or STATUS
00441     if (tqstrncmp(what, "SEARCH", what.size()) == 0)
00442     {
00443       parseSearch (result);
00444     }
00445     else if (tqstrncmp(what, "STATUS", what.size()) == 0)
00446     {
00447       parsetStatus (result);
00448     }
00449     break;
00450 
00451   case 'A': // ACL or ANNOTATION
00452     if (tqstrncmp(what, "ACL", what.size()) == 0)
00453     {
00454       parseAcl (result);
00455     }
00456     else if (tqstrncmp(what, "ANNOTATION", what.size()) == 0)
00457     {
00458       parseAnnotation (result);
00459     }
00460     break;
00461   case 'Q': // QUOTA or QUOTAROOT
00462     if ( what.size() > 5 && tqstrncmp(what, "QUOTAROOT", what.size()) == 0)
00463     {
00464       parseQuotaRoot( result );
00465     }
00466     else if (tqstrncmp(what, "QUOTA", what.size()) == 0)
00467     {
00468       parseQuota( result );
00469     }
00470     break;
00471   case 'X': // Custom command
00472     {
00473       parseCustom( result );
00474     }
00475     break;
00476   default:
00477     //better be a number
00478     {
00479       ulong number;
00480       bool valid;
00481 
00482       number = TQCString(what, what.size() + 1).toUInt(&valid);
00483       if (valid)
00484       {
00485         what = parseLiteral (result);
00486     if(!what.isEmpty ()) {
00487         switch (what[0])
00488         {
00489         case 'E':
00490           if (tqstrncmp(what, "EXISTS", what.size()) == 0)
00491           {
00492             parseExists (number, result);
00493           }
00494           else if (tqstrncmp(what, "EXPUNGE", what.size()) == 0)
00495           {
00496             parseExpunge (number, result);
00497           }
00498           break;
00499 
00500         case 'F':
00501           if (tqstrncmp(what, "FETCH", what.size()) == 0)
00502           {
00503             seenUid = TQString();
00504             parseFetch (number, result);
00505           }
00506           break;
00507 
00508         case 'S':
00509           if (tqstrncmp(what, "STORE", what.size()) == 0)  // deprecated store
00510           {
00511             seenUid = TQString();
00512             parseFetch (number, result);
00513           }
00514           break;
00515 
00516         case 'R':
00517           if (tqstrncmp(what, "RECENT", what.size()) == 0)
00518           {
00519             parseRecent (number, result);
00520           }
00521           break;
00522         default:
00523           break;
00524         }
00525     }
00526       }
00527     }
00528     break;
00529   }                             //switch
00530   }
00531 }                               //func
00532 
00533 
00534 void
00535 imapParser::parseResult (TQByteArray & result, parseString & rest,
00536   const TQString & command)
00537 {
00538   if (command == "SELECT")
00539     selectInfo.setReadWrite(true);
00540 
00541   if (rest[0] == '[')
00542   {
00543     rest.pos++;
00544     TQCString option = parseOneWordC(rest, TRUE);
00545 
00546     switch (option[0])
00547     {
00548     case 'A':                  // ALERT
00549       if (option == "ALERT")
00550       {
00551         rest.pos = rest.data.find(']', rest.pos) + 1;
00552         // The alert text is after [ALERT].
00553         // Is this correct or do we need to care about litterals?
00554         selectInfo.setAlert( rest.cstr() );
00555       }
00556       break;
00557 
00558     case 'N':                  // NEWNAME
00559       if (option == "NEWNAME")
00560       {
00561       }
00562       break;
00563 
00564     case 'P':                  //PARSE or PERMANENTFLAGS
00565       if (option == "PARSE")
00566       {
00567       }
00568       else if (option == "PERMANENTFLAGS")
00569       {
00570         uint end = rest.data.find(']', rest.pos);
00571         TQCString flags(rest.data.data() + rest.pos, end - rest.pos);
00572         selectInfo.setPermanentFlags (flags);
00573         rest.pos = end;
00574       }
00575       break;
00576 
00577     case 'R':                  //READ-ONLY or READ-WRITE
00578       if (option == "READ-ONLY")
00579       {
00580         selectInfo.setReadWrite (false);
00581       }
00582       else if (option == "READ-WRITE")
00583       {
00584         selectInfo.setReadWrite (true);
00585       }
00586       break;
00587 
00588     case 'T':                  //TRYCREATE
00589       if (option == "TRYCREATE")
00590       {
00591       }
00592       break;
00593 
00594     case 'U':                  //UIDVALIDITY or UNSEEN
00595       if (option == "UIDVALIDITY")
00596       {
00597         ulong value;
00598         if (parseOneNumber (rest, value))
00599           selectInfo.setUidValidity (value);
00600       }
00601       else if (option == "UNSEEN")
00602       {
00603         ulong value;
00604         if (parseOneNumber (rest, value))
00605           selectInfo.setUnseen (value);
00606       }
00607       else if (option == "UIDNEXT")
00608       {
00609         ulong value;
00610         if (parseOneNumber (rest, value))
00611           selectInfo.setUidNext (value);
00612       }
00613       else
00614       break;
00615 
00616     }
00617     if (rest[0] == ']')
00618       rest.pos++; //tie off ]
00619     skipWS (rest);
00620   }
00621 
00622   if (command.isEmpty())
00623   {
00624     // This happens when parsing an intermediate result line (those that start with '*').
00625     // No state change involved, so we can stop here.
00626     return;
00627   }
00628 
00629   switch (command[0].latin1 ())
00630   {
00631   case 'A':
00632     if (command == "AUTHENTICATE")
00633       if (tqstrncmp(result, "OK", result.size()) == 0)
00634         currentState = ISTATE_LOGIN;
00635     break;
00636 
00637   case 'L':
00638     if (command == "LOGIN")
00639       if (tqstrncmp(result, "OK", result.size()) == 0)
00640         currentState = ISTATE_LOGIN;
00641     break;
00642 
00643   case 'E':
00644     if (command == "EXAMINE")
00645     {
00646       if (tqstrncmp(result, "OK", result.size()) == 0)
00647         currentState = ISTATE_SELECT;
00648       else
00649       {
00650         if (currentState == ISTATE_SELECT)
00651           currentState = ISTATE_LOGIN;
00652         currentBox = TQString();
00653       }
00654       kdDebug(7116) << "imapParser::parseResult - current box is now " << currentBox << endl;
00655     }
00656     break;
00657 
00658   case 'S':
00659     if (command == "SELECT")
00660     {
00661       if (tqstrncmp(result, "OK", result.size()) == 0)
00662         currentState = ISTATE_SELECT;
00663       else
00664       {
00665         if (currentState == ISTATE_SELECT)
00666           currentState = ISTATE_LOGIN;
00667         currentBox = TQString();
00668       }
00669       kdDebug(7116) << "imapParser::parseResult - current box is now " << currentBox << endl;
00670     }
00671     break;
00672 
00673   default:
00674     break;
00675   }
00676 
00677 }
00678 
00679 void imapParser::parseCapability (parseString & result)
00680 {
00681   TQCString temp( result.cstr() );
00682   imapCapabilities = TQStringList::split ( ' ', KPIM::kAsciiToLower( temp.data() ) );
00683 }
00684 
00685 void imapParser::parseFlags (parseString & result)
00686 {
00687   selectInfo.setFlags(result.cstr());
00688 }
00689 
00690 void imapParser::parseList (parseString & result)
00691 {
00692   imapList this_one;
00693 
00694   if (result[0] != '(')
00695     return;                     //not proper format for us
00696 
00697   result.pos++; // tie off (
00698 
00699   this_one.parseAttributes( result );
00700 
00701   result.pos++; // tie off )
00702   skipWS (result);
00703 
00704   this_one.setHierarchyDelimiter(parseLiteralC(result));
00705   this_one.setName (rfcDecoder::fromIMAP(parseLiteralC(result)));  // decode modified UTF7
00706 
00707   listResponses.append (this_one);
00708 }
00709 
00710 void imapParser::parseLsub (parseString & result)
00711 {
00712   imapList this_one (result.cstr(), *this);
00713   listResponses.append (this_one);
00714 }
00715 
00716 void imapParser::parseListRights (parseString & result)
00717 {
00718   parseOneWordC (result); // skip mailbox name
00719   parseOneWordC (result); // skip user id
00720   int outlen = 1;
00721   while ( outlen ) {
00722     TQCString word = parseOneWordC (result, false, &outlen);
00723     lastResults.append (word);
00724   }
00725 }
00726 
00727 void imapParser::parseAcl (parseString & result)
00728 {
00729   parseOneWordC (result); // skip mailbox name
00730   int outlen = 1;
00731   // The result is user1 perm1 user2 perm2 etc. The caller will sort it out.
00732   while ( outlen && !result.isEmpty() ) {
00733     TQCString word = parseLiteralC (result, false, false, &outlen);
00734     lastResults.append (word);
00735   }
00736 }
00737 
00738 void imapParser::parseAnnotation (parseString & result)
00739 {
00740   parseOneWordC (result); // skip mailbox name
00741   skipWS (result);
00742   parseOneWordC (result); // skip entry name (we know it since we don't allow wildcards in it)
00743   skipWS (result);
00744   if (result.isEmpty() || result[0] != '(')
00745     return;
00746   result.pos++;
00747   skipWS (result);
00748   int outlen = 1;
00749   // The result is name1 value1 name2 value2 etc. The caller will sort it out.
00750   while ( outlen && !result.isEmpty() && result[0] != ')' ) {
00751     TQCString word = parseLiteralC (result, false, false, &outlen);
00752     lastResults.append (word);
00753   }
00754 }
00755 
00756 
00757 void imapParser::parseQuota (parseString & result)
00758 {
00759   // quota_response  ::= "QUOTA" SP astring SP quota_list
00760   // quota_list      ::= "(" #quota_resource ")"
00761   // quota_resource  ::= atom SP number SP number
00762   TQCString root = parseOneWordC( result );
00763   if ( root.isEmpty() ) {
00764     lastResults.append( "" );
00765   } else {
00766     lastResults.append( root );
00767   }
00768   if (result.isEmpty() || result[0] != '(')
00769     return;
00770   result.pos++;
00771   skipWS (result);
00772   TQStringList triplet;
00773   int outlen = 1;
00774   while ( outlen && !result.isEmpty() && result[0] != ')' ) {
00775     TQCString word = parseLiteralC (result, false, false, &outlen);
00776     triplet.append(word);
00777   }
00778   lastResults.append( triplet.join(" ") );
00779 }
00780 
00781 void imapParser::parseQuotaRoot (parseString & result)
00782 {
00783   //    quotaroot_response
00784   //         ::= "QUOTAROOT" SP astring *(SP astring)
00785   parseOneWordC (result); // skip mailbox name
00786   skipWS (result);
00787   if ( result.isEmpty() )
00788     return;
00789   TQStringList roots;
00790   int outlen = 1;
00791   while ( outlen && !result.isEmpty() ) {
00792     TQCString word = parseLiteralC (result, false, false, &outlen);
00793     roots.append (word);
00794   }
00795   lastResults.append( roots.isEmpty()? "" : roots.join(" ") );
00796 }
00797 
00798 void imapParser::parseCustom (parseString & result)
00799 {
00800   int outlen = 1;
00801   TQCString word = parseLiteralC (result, false, false, &outlen);
00802   lastResults.append( word );
00803 }
00804 
00805 void imapParser::parseOtherUser (parseString & result)
00806 {
00807   lastResults.append( parseOneWordC( result ) );
00808 }
00809 
00810 void imapParser::parseDelegate (parseString & result)
00811 {
00812   const TQString email = parseOneWordC( result );
00813 
00814   TQStringList rights;
00815   int outlen = 1;
00816   while ( outlen && !result.isEmpty() ) {
00817     TQCString word = parseLiteralC( result, false, false, &outlen );
00818     rights.append( word );
00819   }
00820 
00821   lastResults.append( email + ":" + rights.join( "," ) );
00822 }
00823 
00824 void imapParser::parseOutOfOffice (parseString & result)
00825 {
00826   const TQString state = parseOneWordC (result);
00827   parseOneWordC (result); // skip encoding
00828 
00829   int outlen = 1;
00830   TQCString msg = parseLiteralC (result, false, false, &outlen);
00831 
00832   lastResults.append( state + "^" + TQString::fromUtf8( msg ) );
00833 }
00834 
00835 void imapParser::parseMyRights (parseString & result)
00836 {
00837   parseOneWordC (result); // skip mailbox name
00838   Q_ASSERT( lastResults.isEmpty() ); // we can only be called once
00839   lastResults.append (parseOneWordC (result) );
00840 }
00841 
00842 void imapParser::parseSearch (parseString & result)
00843 {
00844   ulong value;
00845 
00846   while (parseOneNumber (result, value))
00847   {
00848     lastResults.append (TQString::number(value));
00849   }
00850 }
00851 
00852 void imapParser::parsetStatus (parseString & inWords)
00853 {
00854   lasStatus = imapInfo ();
00855 
00856   parseLiteralC(inWords);       // swallow the box
00857   if (inWords.isEmpty() || inWords[0] != '(')
00858     return;
00859 
00860   inWords.pos++;
00861   skipWS (inWords);
00862 
00863   while (!inWords.isEmpty() && inWords[0] != ')')
00864   {
00865     ulong value;
00866 
00867     TQCString label = parseOneWordC(inWords);
00868     if (parseOneNumber (inWords, value))
00869     {
00870       if (label == "MESSAGES")
00871         lasStatus.setCount (value);
00872       else if (label == "RECENT")
00873         lasStatus.setRecent (value);
00874       else if (label == "UIDVALIDITY")
00875         lasStatus.setUidValidity (value);
00876       else if (label == "UNSEEN")
00877         lasStatus.setUnseen (value);
00878       else if (label == "UIDNEXT")
00879         lasStatus.setUidNext (value);
00880     }
00881   }
00882 
00883   if (inWords[0] == ')')
00884     inWords.pos++;
00885   skipWS (inWords);
00886 }
00887 
00888 void imapParser::parseExists (ulong value, parseString & result)
00889 {
00890   selectInfo.setCount (value);
00891   result.pos = result.data.size();
00892 }
00893 
00894 void imapParser::parseExpunge (ulong value, parseString & result)
00895 {
00896   Q_UNUSED(value);
00897   Q_UNUSED(result);
00898 }
00899 
00900 void imapParser::parseAddressList (parseString & inWords, TQPtrList<mailAddress>& list)
00901 {
00902   if (inWords.isEmpty())
00903     return;
00904   if (inWords[0] != '(')
00905   {
00906     parseOneWordC (inWords);     // parse NIL
00907   }
00908   else
00909   {
00910     inWords.pos++;
00911     skipWS (inWords);
00912 
00913     while (!inWords.isEmpty () && inWords[0] != ')')
00914     {
00915       if (inWords[0] == '(') {
00916         mailAddress *addr = new mailAddress;
00917         parseAddress(inWords, *addr);
00918         list.append(addr);
00919       } else {
00920         break;
00921       }
00922     }
00923 
00924     if (!inWords.isEmpty() && inWords[0] == ')')
00925       inWords.pos++;
00926     skipWS (inWords);
00927   }
00928 }
00929 
00930 const mailAddress& imapParser::parseAddress (parseString & inWords, mailAddress& retVal)
00931 {
00932   inWords.pos++;
00933   skipWS (inWords);
00934 
00935   retVal.setFullName(parseLiteralC(inWords));
00936   retVal.setCommentRaw(parseLiteralC(inWords));
00937   retVal.setUser(parseLiteralC(inWords));
00938   retVal.setHost(parseLiteralC(inWords));
00939 
00940   if (!inWords.isEmpty() && inWords[0] == ')')
00941     inWords.pos++;
00942   skipWS (inWords);
00943 
00944   return retVal;
00945 }
00946 
00947 mailHeader * imapParser::parseEnvelope (parseString & inWords)
00948 {
00949   mailHeader *envelope = 0;
00950 
00951   if (inWords[0] != '(')
00952     return envelope;
00953   inWords.pos++;
00954   skipWS (inWords);
00955 
00956   envelope = new mailHeader;
00957 
00958   //date
00959   envelope->setDate(parseLiteralC(inWords));
00960 
00961   //subject
00962   envelope->setSubject(parseLiteralC(inWords));
00963 
00964   TQPtrList<mailAddress> list;
00965   list.setAutoDelete(true);
00966 
00967   //from
00968   parseAddressList(inWords, list);
00969   if (!list.isEmpty()) {
00970       envelope->setFrom(*list.last());
00971       list.clear();
00972   }
00973 
00974   //sender
00975   parseAddressList(inWords, list);
00976   if (!list.isEmpty()) {
00977       envelope->setSender(*list.last());
00978       list.clear();
00979   }
00980 
00981   //reply-to
00982   parseAddressList(inWords, list);
00983   if (!list.isEmpty()) {
00984       envelope->setReplyTo(*list.last());
00985       list.clear();
00986   }
00987 
00988   //to
00989   parseAddressList (inWords, envelope->to());
00990 
00991   //cc
00992   parseAddressList (inWords, envelope->cc());
00993 
00994   //bcc
00995   parseAddressList (inWords, envelope->bcc());
00996 
00997   //in-reply-to
00998   envelope->setInReplyTo(parseLiteralC(inWords));
00999 
01000   //message-id
01001   envelope->setMessageId(parseLiteralC(inWords));
01002 
01003   // see if we have more to come
01004   while (!inWords.isEmpty () && inWords[0] != ')')
01005   {
01006     //eat the extensions to this part
01007     if (inWords[0] == '(')
01008       parseSentence (inWords);
01009     else
01010       parseLiteralC (inWords);
01011   }
01012 
01013   if (!inWords.isEmpty() && inWords[0] == ')')
01014     inWords.pos++;
01015   skipWS (inWords);
01016 
01017   return envelope;
01018 }
01019 
01020 // parse parameter pairs into a dictionary
01021 // caller must clean up the dictionary items
01022 TQAsciiDict < TQString > imapParser::parseDisposition (parseString & inWords)
01023 {
01024   TQCString disposition;
01025   TQAsciiDict < TQString > retVal (17, false);
01026 
01027   // return value is a shallow copy
01028   retVal.setAutoDelete (false);
01029 
01030   if (inWords[0] != '(')
01031   {
01032     //disposition only
01033     disposition = parseOneWordC (inWords);
01034   }
01035   else
01036   {
01037     inWords.pos++;
01038     skipWS (inWords);
01039 
01040     //disposition
01041     disposition = parseOneWordC (inWords);
01042     retVal = parseParameters (inWords);
01043     if (inWords[0] != ')')
01044       return retVal;
01045     inWords.pos++;
01046     skipWS (inWords);
01047   }
01048 
01049   if (!disposition.isEmpty ())
01050   {
01051     retVal.insert ("content-disposition", new TQString(disposition));
01052   }
01053 
01054   return retVal;
01055 }
01056 
01057 // parse parameter pairs into a dictionary
01058 // caller must clean up the dictionary items
01059 TQAsciiDict < TQString > imapParser::parseParameters (parseString & inWords)
01060 {
01061   TQAsciiDict < TQString > retVal (17, false);
01062 
01063   // return value is a shallow copy
01064   retVal.setAutoDelete (false);
01065 
01066   if (inWords[0] != '(')
01067   {
01068     //better be NIL
01069     parseOneWordC (inWords);
01070   }
01071   else
01072   {
01073     inWords.pos++;
01074     skipWS (inWords);
01075 
01076     while (!inWords.isEmpty () && inWords[0] != ')')
01077     {
01078       TQCString l1 = parseLiteralC(inWords);
01079       TQCString l2 = parseLiteralC(inWords);
01080       retVal.insert (l1, new TQString(l2));
01081     }
01082 
01083     if (inWords[0] != ')')
01084       return retVal;
01085     inWords.pos++;
01086     skipWS (inWords);
01087   }
01088 
01089   return retVal;
01090 }
01091 
01092 mimeHeader * imapParser::parseSimplePart (parseString & inWords,
01093   TQString & inSection, mimeHeader * localPart)
01094 {
01095   TQCString subtype;
01096   TQCString typeStr;
01097   TQAsciiDict < TQString > parameters (17, false);
01098   ulong size;
01099 
01100   parameters.setAutoDelete (true);
01101 
01102   if (inWords[0] != '(')
01103     return 0;
01104 
01105   if (!localPart)
01106     localPart = new mimeHeader;
01107 
01108   localPart->setPartSpecifier (inSection);
01109 
01110   inWords.pos++;
01111   skipWS (inWords);
01112 
01113   //body type
01114   typeStr = parseLiteralC(inWords);
01115 
01116   //body subtype
01117   subtype = parseLiteralC(inWords);
01118 
01119   localPart->setType (typeStr + "/" + subtype);
01120 
01121   //body parameter parenthesized list
01122   parameters = parseParameters (inWords);
01123   {
01124     TQAsciiDictIterator < TQString > it (parameters);
01125 
01126     while (it.current ())
01127     {
01128       localPart->setTypeParm (it.currentKey (), *(it.current ()));
01129       ++it;
01130     }
01131     parameters.clear ();
01132   }
01133 
01134   //body id
01135   localPart->setID (parseLiteralC(inWords));
01136 
01137   //body description
01138   localPart->setDescription (parseLiteralC(inWords));
01139 
01140   //body encoding
01141   localPart->setEncoding (parseLiteralC(inWords));
01142 
01143   //body size
01144   if (parseOneNumber (inWords, size))
01145     localPart->setLength (size);
01146 
01147   // type specific extensions
01148   if (localPart->getType().upper() == "MESSAGE/RFC822")
01149   {
01150     //envelope structure
01151     mailHeader *envelope = parseEnvelope (inWords);
01152 
01153     //body structure
01154     parseBodyStructure (inWords, inSection, envelope);
01155 
01156     localPart->setNestedMessage (envelope);
01157 
01158     //text lines
01159     ulong lines;
01160     parseOneNumber (inWords, lines);
01161   }
01162   else
01163   {
01164     if (typeStr ==  "TEXT")
01165     {
01166       //text lines
01167       ulong lines;
01168       parseOneNumber (inWords, lines);
01169     }
01170 
01171     // md5
01172     parseLiteralC(inWords);
01173 
01174     // body disposition
01175     parameters = parseDisposition (inWords);
01176     {
01177       TQString *disposition = parameters["content-disposition"];
01178 
01179       if (disposition)
01180         localPart->setDisposition (disposition->ascii ());
01181       parameters.remove ("content-disposition");
01182       TQAsciiDictIterator < TQString > it (parameters);
01183       while (it.current ())
01184       {
01185         localPart->setDispositionParm (it.currentKey (),
01186                                        *(it.current ()));
01187         ++it;
01188       }
01189 
01190       parameters.clear ();
01191     }
01192 
01193     // body language
01194     parseSentence (inWords);
01195   }
01196 
01197   // see if we have more to come
01198   while (!inWords.isEmpty () && inWords[0] != ')')
01199   {
01200     //eat the extensions to this part
01201     if (inWords[0] == '(')
01202       parseSentence (inWords);
01203     else
01204       parseLiteralC(inWords);
01205   }
01206   if (inWords[0] == ')')
01207     inWords.pos++;
01208   skipWS (inWords);
01209 
01210   return localPart;
01211 }
01212 
01213 mimeHeader * imapParser::parseBodyStructure (parseString & inWords,
01214   TQString & inSection, mimeHeader * localPart)
01215 {
01216   bool init = false;
01217   if (inSection.isEmpty())
01218   {
01219     // first run
01220     init = true;
01221     // assume one part
01222     inSection = "1";
01223   }
01224   int section = 0;
01225 
01226   if (inWords[0] != '(')
01227   {
01228     // skip ""
01229     parseOneWordC (inWords);
01230     return 0;
01231   }
01232   inWords.pos++;
01233   skipWS (inWords);
01234 
01235   if (inWords[0] == '(')
01236   {
01237     TQByteArray subtype;
01238     TQAsciiDict < TQString > parameters (17, false);
01239     TQString outSection;
01240     parameters.setAutoDelete (true);
01241     if (!localPart)
01242       localPart = new mimeHeader;
01243     else
01244     {
01245       // might be filled from an earlier run
01246       localPart->clearNestedParts ();
01247       localPart->clearTypeParameters ();
01248       localPart->clearDispositionParameters ();
01249       // an envelope was passed in so this is the multipart header
01250       outSection = inSection + ".HEADER";
01251     }
01252     if (inWords[0] == '(' && init)
01253       inSection = "0";
01254 
01255     // set the section
01256     if ( !outSection.isEmpty() ) {
01257       localPart->setPartSpecifier(outSection);
01258     } else {
01259       localPart->setPartSpecifier(inSection);
01260     }
01261 
01262     // is multipart (otherwise its a simplepart and handled later)
01263     while (inWords[0] == '(')
01264     {
01265       outSection = TQString::number(++section);
01266       if (!init)
01267         outSection = inSection + "." + outSection;
01268       mimeHeader *subpart = parseBodyStructure (inWords, outSection, 0);
01269       localPart->addNestedPart (subpart);
01270     }
01271 
01272     // fetch subtype
01273     subtype = parseOneWordC (inWords);
01274 
01275     localPart->setType ("MULTIPART/" + b2c(subtype));
01276 
01277     // fetch parameters
01278     parameters = parseParameters (inWords);
01279     {
01280       TQAsciiDictIterator < TQString > it (parameters);
01281 
01282       while (it.current ())
01283       {
01284         localPart->setTypeParm (it.currentKey (), *(it.current ()));
01285         ++it;
01286       }
01287       parameters.clear ();
01288     }
01289 
01290     // body disposition
01291     parameters = parseDisposition (inWords);
01292     {
01293       TQString *disposition = parameters["content-disposition"];
01294 
01295       if (disposition)
01296         localPart->setDisposition (disposition->ascii ());
01297       parameters.remove ("content-disposition");
01298       TQAsciiDictIterator < TQString > it (parameters);
01299       while (it.current ())
01300       {
01301         localPart->setDispositionParm (it.currentKey (),
01302                                        *(it.current ()));
01303         ++it;
01304       }
01305       parameters.clear ();
01306     }
01307 
01308     // body language
01309     parseSentence (inWords);
01310 
01311   }
01312   else
01313   {
01314     // is simple part
01315     inWords.pos--;
01316     inWords.data[inWords.pos] = '('; //fake a sentence
01317     if ( localPart )
01318       inSection = inSection + ".1";
01319     localPart = parseSimplePart (inWords, inSection, localPart);
01320     inWords.pos--;
01321     inWords.data[inWords.pos] = ')'; //remove fake
01322   }
01323 
01324   // see if we have more to come
01325   while (!inWords.isEmpty () && inWords[0] != ')')
01326   {
01327     //eat the extensions to this part
01328     if (inWords[0] == '(')
01329       parseSentence (inWords);
01330     else
01331       parseLiteralC(inWords);
01332   }
01333 
01334   if (inWords[0] == ')')
01335     inWords.pos++;
01336   skipWS (inWords);
01337 
01338   return localPart;
01339 }
01340 
01341 void imapParser::parseBody (parseString & inWords)
01342 {
01343   // see if we got a part specifier
01344   if (inWords[0] == '[')
01345   {
01346     TQCString specifier;
01347     TQCString label;
01348     inWords.pos++;
01349 
01350     specifier = parseOneWordC (inWords, TRUE);
01351 
01352     if (inWords[0] == '(')
01353     {
01354       inWords.pos++;
01355 
01356       while (!inWords.isEmpty () && inWords[0] != ')')
01357       {
01358         label = parseOneWordC (inWords);
01359       }
01360 
01361       if (!inWords.isEmpty () && inWords[0] == ')')
01362         inWords.pos++;
01363     }
01364     if (!inWords.isEmpty () && inWords[0] == ']')
01365       inWords.pos++;
01366     skipWS (inWords);
01367 
01368     // parse the header
01369     if (specifier == "0")
01370     {
01371       mailHeader *envelope = 0;
01372       if (lastHandled)
01373         envelope = lastHandled->getHeader ();
01374 
01375       if (!envelope || seenUid.isEmpty ())
01376       {
01377         kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl;
01378         // don't know where to put it, throw it away
01379         parseLiteralC(inWords, true);
01380       }
01381       else
01382       {
01383         kdDebug(7116) << "imapParser::parseBody - reading " << envelope << " " << seenUid.ascii () << endl;
01384         // fill it up with data
01385         TQString theHeader = parseLiteralC(inWords, true);
01386         mimeIOTQString myIO;
01387 
01388         myIO.setString (theHeader);
01389         envelope->parseHeader (myIO);
01390 
01391       }
01392     }
01393     else if (specifier == "HEADER.FIELDS")
01394     {
01395       // BODY[HEADER.FIELDS (References)] {n}
01396       //kdDebug(7116) << "imapParser::parseBody - HEADER.FIELDS: "
01397       // << TQCString(label.data(), label.size()+1) << endl;
01398       if (label == "REFERENCES")
01399       {
01400        mailHeader *envelope = 0;
01401        if (lastHandled)
01402          envelope = lastHandled->getHeader ();
01403 
01404        if (!envelope || seenUid.isEmpty ())
01405        {
01406          kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl;
01407          // don't know where to put it, throw it away
01408          parseLiteralC (inWords, true);
01409        }
01410        else
01411        {
01412          TQCString references = parseLiteralC(inWords, true);
01413          int start = references.find ('<');
01414          int end = references.findRev ('>');
01415          if (start < end)
01416                  references = references.mid (start, end - start + 1);
01417          envelope->setReferences(references.simplifyWhiteSpace());
01418        }
01419       }
01420       else
01421       { // not a header we care about throw it away
01422         parseLiteralC(inWords, true);
01423       }
01424     }
01425     else
01426     {
01427       if (specifier.find(".MIME") != -1)
01428       {
01429         mailHeader *envelope = new mailHeader;
01430         TQString theHeader = parseLiteralC(inWords, false);
01431         mimeIOTQString myIO;
01432         myIO.setString (theHeader);
01433         envelope->parseHeader (myIO);
01434         if (lastHandled)
01435           lastHandled->setHeader (envelope);
01436         return;
01437       }
01438       // throw it away
01439       kdDebug(7116) << "imapParser::parseBody - discarding " << seenUid.ascii () << endl;
01440       parseLiteralC(inWords, true);
01441     }
01442 
01443   }
01444   else // no part specifier
01445   {
01446     mailHeader *envelope = 0;
01447     if (lastHandled)
01448       envelope = lastHandled->getHeader ();
01449 
01450     if (!envelope || seenUid.isEmpty ())
01451     {
01452       kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl;
01453       // don't know where to put it, throw it away
01454       parseSentence (inWords);
01455     }
01456     else
01457     {
01458       kdDebug(7116) << "imapParser::parseBody - reading " << envelope << " " << seenUid.ascii () << endl;
01459       // fill it up with data
01460       TQString section;
01461       mimeHeader *body = parseBodyStructure (inWords, section, envelope);
01462       if (body != envelope)
01463         delete body;
01464     }
01465   }
01466 }
01467 
01468 void imapParser::parseFetch (ulong /* value */, parseString & inWords)
01469 {
01470   if (inWords[0] != '(')
01471     return;
01472   inWords.pos++;
01473   skipWS (inWords);
01474 
01475   delete lastHandled;
01476   lastHandled = 0;
01477 
01478   while (!inWords.isEmpty () && inWords[0] != ')')
01479   {
01480     if (inWords[0] == '(')
01481       parseSentence (inWords);
01482     else
01483     {
01484       TQCString word = parseLiteralC(inWords, false, true);
01485 
01486       if(!word.isEmpty()) {
01487       switch (word[0])
01488       {
01489       case 'E':
01490         if (word == "ENVELOPE")
01491         {
01492           mailHeader *envelope = 0;
01493 
01494           if (lastHandled)
01495             envelope = lastHandled->getHeader ();
01496           else
01497             lastHandled = new imapCache();
01498 
01499           if (envelope && !envelope->getMessageId ().isEmpty ())
01500           {
01501             // we have seen this one already
01502             // or don't know where to put it
01503             parseSentence (inWords);
01504           }
01505           else
01506           {
01507             envelope = parseEnvelope (inWords);
01508             if (envelope)
01509             {
01510               envelope->setPartSpecifier (seenUid + ".0");
01511               lastHandled->setHeader (envelope);
01512               lastHandled->setUid (seenUid.toULong ());
01513             }
01514           }
01515         }
01516         break;
01517 
01518       case 'B':
01519         if (word == "BODY")
01520         {
01521           parseBody (inWords);
01522         }
01523         else if (word == "BODY[]" )
01524         {
01525           // Do the same as with "RFC822"
01526           parseLiteralC(inWords, true);
01527         }
01528         else if (word == "BODYSTRUCTURE")
01529         {
01530           mailHeader *envelope = 0;
01531 
01532           if (lastHandled)
01533             envelope = lastHandled->getHeader ();
01534 
01535           // fill it up with data
01536           TQString section;
01537           mimeHeader *body =
01538             parseBodyStructure (inWords, section, envelope);
01539           TQByteArray data;
01540           TQDataStream stream( data, IO_WriteOnly );
01541           if (body) body->serialize(stream);
01542           parseRelay(data);
01543 
01544           delete body;
01545         }
01546         break;
01547 
01548       case 'U':
01549         if (word == "UID")
01550         {
01551           seenUid = parseOneWordC(inWords);
01552           mailHeader *envelope = 0;
01553           if (lastHandled)
01554             envelope = lastHandled->getHeader ();
01555           else
01556             lastHandled = new imapCache();
01557 
01558           if (seenUid.isEmpty ())
01559           {
01560             // unknown what to do
01561             kdDebug(7116) << "imapParser::parseFetch - UID empty" << endl;
01562           }
01563           else
01564           {
01565             lastHandled->setUid (seenUid.toULong ());
01566           }
01567           if (envelope)
01568             envelope->setPartSpecifier (seenUid);
01569         }
01570         break;
01571 
01572       case 'R':
01573         if (word == "RFC822.SIZE")
01574         {
01575           ulong size;
01576           parseOneNumber (inWords, size);
01577 
01578           if (!lastHandled) lastHandled = new imapCache();
01579           lastHandled->setSize (size);
01580         }
01581         else if (word.find ("RFC822") == 0)
01582         {
01583           // might be RFC822 RFC822.TEXT RFC822.HEADER
01584           parseLiteralC(inWords, true);
01585         }
01586         break;
01587 
01588       case 'I':
01589         if (word == "INTERNALDATE")
01590         {
01591           TQCString date = parseOneWordC(inWords);
01592           if (!lastHandled) lastHandled = new imapCache();
01593           lastHandled->setDate(date);
01594         }
01595         break;
01596 
01597       case 'F':
01598         if (word == "FLAGS")
01599         {
01600       //kdDebug(7116) << "GOT FLAGS " << inWords.cstr() << endl;
01601           if (!lastHandled) lastHandled = new imapCache();
01602           lastHandled->setFlags (imapInfo::_flags (inWords.cstr()));
01603         }
01604         break;
01605 
01606       default:
01607         parseLiteralC(inWords);
01608         break;
01609       }
01610       } else {
01611         parseLiteralC(inWords);
01612       }
01613     }
01614   }
01615 
01616   // see if we have more to come
01617   while (!inWords.isEmpty () && inWords[0] != ')')
01618   {
01619     //eat the extensions to this part
01620     if (inWords[0] == '(')
01621       parseSentence (inWords);
01622     else
01623       parseLiteralC(inWords);
01624   }
01625 
01626   if (inWords.isEmpty() || inWords[0] != ')')
01627     return;
01628   inWords.pos++;
01629   skipWS (inWords);
01630 }
01631 
01632 
01633 // default parser
01634 void imapParser::parseSentence (parseString & inWords)
01635 {
01636   bool first = true;
01637   int stack = 0;
01638 
01639   //find the first nesting parentheses
01640 
01641   while (!inWords.isEmpty () && (stack != 0 || first))
01642   {
01643     first = false;
01644     skipWS (inWords);
01645 
01646     unsigned char ch = inWords[0];
01647     switch (ch)
01648     {
01649     case '(':
01650       inWords.pos++;
01651       ++stack;
01652       break;
01653     case ')':
01654       inWords.pos++;
01655       --stack;
01656       break;
01657     case '[':
01658       inWords.pos++;
01659       ++stack;
01660       break;
01661     case ']':
01662       inWords.pos++;
01663       --stack;
01664       break;
01665     default:
01666       parseLiteralC(inWords);
01667       skipWS (inWords);
01668       break;
01669     }
01670   }
01671   skipWS (inWords);
01672 }
01673 
01674 void imapParser::parseRecent (ulong value, parseString & result)
01675 {
01676   selectInfo.setRecent (value);
01677   result.pos = result.data.size();
01678 }
01679 
01680 void imapParser::parseNamespace (parseString & result)
01681 {
01682   if ( result[0] != '(' )
01683     return;
01684 
01685   TQString delimEmpty;
01686   if ( namespaceToDelimiter.contains( TQString() ) )
01687     delimEmpty = namespaceToDelimiter[TQString()];
01688 
01689   namespaceToDelimiter.clear();
01690   imapNamespaces.clear();
01691 
01692   // remember what section we're in (user, other users, shared)
01693   int ns = -1;
01694   bool personalAvailable = false;
01695   while ( !result.isEmpty() )
01696   {
01697     if ( result[0] == '(' )
01698     {
01699       result.pos++; // tie off (
01700       if ( result[0] == '(' )
01701       {
01702         // new namespace section
01703         result.pos++; // tie off (
01704         ++ns;
01705       }
01706       // namespace prefix
01707       TQCString prefix = parseOneWordC( result );
01708       // delimiter
01709       TQCString delim = parseOneWordC( result );
01710       kdDebug(7116) << "imapParser::parseNamespace ns='" << prefix <<
01711        "',delim='" << delim << "'" << endl;
01712       if ( ns == 0 )
01713       {
01714         // at least one personal ns
01715         personalAvailable = true;
01716       }
01717       TQString nsentry = TQString::number( ns ) + "=" + TQString(prefix) +
01718         "=" + TQString(delim);
01719       imapNamespaces.append( nsentry );
01720       if ( prefix.right( 1 ) == delim ) {
01721         // strip delimiter to get a correct entry for comparisons
01722         prefix.resize( prefix.length() );
01723       }
01724       namespaceToDelimiter[prefix] = delim;
01725 
01726       result.pos++; // tie off )
01727       skipWS( result );
01728     } else if ( result[0] == ')' )
01729     {
01730       result.pos++; // tie off )
01731       skipWS( result );
01732     } else if ( result[0] == 'N' )
01733     {
01734       // drop NIL
01735       ++ns;
01736       parseOneWordC( result );
01737     } else {
01738       // drop whatever it is
01739       parseOneWordC( result );
01740     }
01741   }
01742   if ( !delimEmpty.isEmpty() ) {
01743     // remember default delimiter
01744     namespaceToDelimiter[TQString()] = delimEmpty;
01745     if ( !personalAvailable )
01746     {
01747       // at least one personal ns would be nice
01748       kdDebug(7116) << "imapParser::parseNamespace - registering own personal ns" << endl;
01749       TQString nsentry = "0==" + delimEmpty;
01750       imapNamespaces.append( nsentry );
01751     }
01752   }
01753 }
01754 
01755 int imapParser::parseLoop ()
01756 {
01757   parseString result;
01758 
01759   if (!parseReadLine(result.data)) return -1;
01760 
01761   //kdDebug(7116) << result.cstr(); // includes \n
01762 
01763   if (result.data.isEmpty())
01764     return 0;
01765   if (!sentQueue.count ())
01766   {
01767     // maybe greeting or BYE everything else SHOULD not happen, use NOOP or IDLE
01768     kdDebug(7116) << "imapParser::parseLoop - unhandledResponse: \n" << result.cstr() << endl;
01769     unhandled << result.cstr();
01770   }
01771   else
01772   {
01773     imapCommand *current = sentQueue.at (0);
01774     switch (result[0])
01775     {
01776     case '*':
01777       result.data.resize(result.data.size() - 2);  // tie off CRLF
01778       parseUntagged (result);
01779       break;
01780     case '+':
01781       continuation.duplicate(result.data);
01782       break;
01783     default:
01784       {
01785         TQCString tag = parseLiteralC(result);
01786         if (current->id() == tag.data())
01787         {
01788           result.data.resize(result.data.size() - 2);  // tie off CRLF
01789           TQByteArray resultCode = parseLiteral (result); //the result
01790           current->setResult (resultCode);
01791           current->setResultInfo(result.cstr());
01792           current->setComplete ();
01793 
01794           sentQueue.removeRef (current);
01795           completeQueue.append (current);
01796           if (result.length())
01797             parseResult (resultCode, result, current->command());
01798         }
01799         else
01800         {
01801           kdDebug(7116) << "imapParser::parseLoop - unknown tag '" << tag << "'" << endl;
01802           TQCString cstr = tag + " " + result.cstr();
01803           result.data = cstr;
01804           result.pos = 0;
01805           result.data.resize(cstr.length());
01806         }
01807       }
01808       break;
01809     }
01810   }
01811 
01812   return 1;
01813 }
01814 
01815 void
01816 imapParser::parseRelay (const TQByteArray & buffer)
01817 {
01818   Q_UNUSED(buffer);
01819   tqWarning
01820     ("imapParser::parseRelay - virtual function not reimplemented - data lost");
01821 }
01822 
01823 void
01824 imapParser::parseRelay (ulong len)
01825 {
01826   Q_UNUSED(len);
01827   tqWarning
01828     ("imapParser::parseRelay - virtual function not reimplemented - announcement lost");
01829 }
01830 
01831 bool imapParser::parseRead (TQByteArray & buffer, ulong len, ulong relay)
01832 {
01833   Q_UNUSED(buffer);
01834   Q_UNUSED(len);
01835   Q_UNUSED(relay);
01836   tqWarning
01837     ("imapParser::parseRead - virtual function not reimplemented - no data read");
01838   return FALSE;
01839 }
01840 
01841 bool imapParser::parseReadLine (TQByteArray & buffer, ulong relay)
01842 {
01843   Q_UNUSED(buffer);
01844   Q_UNUSED(relay);
01845   tqWarning
01846     ("imapParser::parseReadLine - virtual function not reimplemented - no data read");
01847   return FALSE;
01848 }
01849 
01850 void
01851 imapParser::parseWriteLine (const TQString & str)
01852 {
01853   Q_UNUSED(str);
01854   tqWarning
01855     ("imapParser::parseWriteLine - virtual function not reimplemented - no data written");
01856 }
01857 
01858 void
01859 imapParser::parseURL (const KURL & _url, TQString & _box, TQString & _section,
01860                       TQString & _type, TQString & _uid, TQString & _validity, TQString & _info)
01861 {
01862   TQStringList parameters;
01863 
01864   _box = _url.path ();
01865   kdDebug(7116) << "imapParser::parseURL " << _box << endl;
01866   int paramStart = _box.find("/;");
01867   if ( paramStart > -1 )
01868   {
01869     TQString paramString = _box.right( _box.length() - paramStart-2 );
01870     parameters = TQStringList::split (';', paramString);  //split parameters
01871     _box.truncate( paramStart ); // strip parameters
01872   }
01873   // extract parameters
01874   for (TQStringList::ConstIterator it (parameters.begin ());
01875        it != parameters.end (); ++it)
01876   {
01877     TQString temp = (*it);
01878 
01879     int pt = temp.find ('/');
01880     if (pt > 0)
01881     {
01882       if (temp.findRev ('"', pt) == -1 || temp.find('"', pt) == -1)
01883       {
01884         // if we have non-quoted '/' separator we'll just nuke it
01885         temp.truncate(pt);
01886       }
01887     }
01888     if (temp.find ("section=", 0, false) == 0)
01889       _section = temp.right (temp.length () - 8);
01890     else if (temp.find ("type=", 0, false) == 0)
01891       _type = temp.right (temp.length () - 5);
01892     else if (temp.find ("uid=", 0, false) == 0)
01893       _uid = temp.right (temp.length () - 4);
01894     else if (temp.find ("uidvalidity=", 0, false) == 0)
01895       _validity = temp.right (temp.length () - 12);
01896     else if (temp.find ("info=", 0, false) == 0)
01897       _info = temp.right (temp.length () - 5);
01898   }
01899 //  kdDebug(7116) << "URL: section= " << _section << ", type= " << _type << ", uid= " << _uid << endl;
01900 //  kdDebug(7116) << "URL: user() " << _url.user() << endl;
01901 //  kdDebug(7116) << "URL: path() " << _url.path() << endl;
01902 //  kdDebug(7116) << "URL: encodedPathAndQuery() " << _url.encodedPathAndQuery() << endl;
01903 
01904   if (!_box.isEmpty ())
01905   {
01906     // strip /
01907     if (_box[0] == '/')
01908       _box = _box.right (_box.length () - 1);
01909     if (!_box.isEmpty () && _box[_box.length () - 1] == '/')
01910       _box.truncate(_box.length() - 1);
01911   }
01912   kdDebug(7116) << "URL: box= " << _box << ", section= " << _section << ", type= "
01913     << _type << ", uid= " << _uid << ", validity= " << _validity << ", info= " << _info << endl;
01914 }
01915 
01916 
01917 TQCString imapParser::parseLiteralC(parseString & inWords, bool relay, bool stopAtBracket, int *outlen) {
01918 
01919   if (!inWords.isEmpty() && inWords[0] == '{')
01920   {
01921     TQCString retVal;
01922     long srunLen = inWords.find ('}', 1); // Can return -1, so use a signed long
01923     if (srunLen > 0)
01924     {
01925       ulong runLen = (ulong)srunLen;
01926       bool proper;
01927       ulong runLenSave = runLen + 1;
01928       TQCString tmpstr(runLen);
01929       inWords.takeMidNoResize(tmpstr, 1, runLen - 1);
01930       runLen = tmpstr.toULong (&proper);
01931       inWords.pos += runLenSave;
01932       if (proper)
01933       {
01934         //now get the literal from the server
01935         if (relay)
01936           parseRelay (runLen);
01937         TQByteArray rv;
01938         parseRead (rv, runLen, relay ? runLen : 0);
01939         rv.resize(TQMAX(runLen, rv.size())); // what's the point?
01940         retVal = b2c(rv);
01941         inWords.clear();
01942         parseReadLine (inWords.data); // must get more
01943 
01944         // no duplicate data transfers
01945         relay = false;
01946       }
01947       else
01948       {
01949         kdDebug(7116) << "imapParser::parseLiteral - error parsing {} - " /*<< strLen*/ << endl;
01950       }
01951     }
01952     else
01953     {
01954       inWords.clear();
01955       kdDebug(7116) << "imapParser::parseLiteral - error parsing unmatched {" << endl;
01956     }
01957     if (outlen) {
01958       *outlen = retVal.length(); // optimize me
01959     }
01960     skipWS (inWords);
01961     return retVal;
01962   }
01963 
01964   return parseOneWordC(inWords, stopAtBracket, outlen);
01965 }
01966 
01967 // does not know about literals ( {7} literal )
01968 TQCString imapParser::parseOneWordC (parseString & inWords, bool stopAtBracket, int *outLen)
01969 {
01970   uint retValSize = 0;
01971   uint len = inWords.length();
01972   if (len == 0) {
01973     return TQCString();
01974   }
01975 
01976   if (len > 0 && inWords[0] == '"')
01977   {
01978     unsigned int i = 1;
01979     bool quote = FALSE;
01980     while (i < len && (inWords[i] != '"' || quote))
01981     {
01982       if (inWords[i] == '\\') quote = !quote;
01983       else quote = FALSE;
01984       i++;
01985     }
01986     if (i < len)
01987     {
01988       TQCString retVal(i);
01989       inWords.pos++;
01990       inWords.takeLeftNoResize(retVal, i - 1);
01991       len = i - 1;
01992       int offset = 0;
01993       for (unsigned int j = 0; j <= len; j++) {
01994         if (retVal[j] == '\\') {
01995           offset++;
01996           j++;
01997         }
01998         retVal[j - offset] = retVal[j];
01999       }
02000       retVal[len - offset] = 0;
02001       retValSize = len - offset;
02002       inWords.pos += i;
02003       skipWS (inWords);
02004       if (outLen) {
02005         *outLen = retValSize;
02006       }
02007       return retVal;
02008     }
02009     else
02010     {
02011       kdDebug(7116) << "imapParser::parseOneWord - error parsing unmatched \"" << endl;
02012       TQCString retVal = inWords.cstr();
02013       retValSize = len;
02014       inWords.clear();
02015       if (outLen) {
02016         *outLen = retValSize;
02017       }
02018       return retVal;
02019     }
02020   }
02021   else
02022   {
02023     // not quoted
02024     unsigned int i;
02025     // search for end
02026     for (i = 0; i < len; ++i) {
02027         char ch = inWords[i];
02028         if (ch <= ' ' || ch == '(' || ch == ')' ||
02029             (stopAtBracket && (ch == '[' || ch == ']')))
02030             break;
02031     }
02032 
02033     TQCString retVal(i+1);
02034     inWords.takeLeftNoResize(retVal, i);
02035     retValSize = i;
02036     inWords.pos += i;
02037 
02038     if (retVal == "NIL") {
02039       retVal.truncate(0);
02040       retValSize = 0;
02041     }
02042     skipWS (inWords);
02043     if (outLen) {
02044       *outLen = retValSize;
02045     }
02046     return retVal;
02047   }
02048 }
02049 
02050 bool imapParser::parseOneNumber (parseString & inWords, ulong & num)
02051 {
02052   bool valid;
02053   num = parseOneWordC(inWords, TRUE).toULong(&valid);
02054   return valid;
02055 }
02056 
02057 bool imapParser::hasCapability (const TQString & cap)
02058 {
02059   TQString c = cap.lower();
02060 //  kdDebug(7116) << "imapParser::hasCapability - Looking for '" << cap << "'" << endl;
02061   for (TQStringList::ConstIterator it = imapCapabilities.begin ();
02062        it != imapCapabilities.end (); ++it)
02063   {
02064 //    kdDebug(7116) << "imapParser::hasCapability - Examining '" << (*it) << "'" << endl;
02065     if ( !(kasciistricmp(c.ascii(), (*it).ascii())) )
02066     {
02067       return true;
02068     }
02069   }
02070   return false;
02071 }
02072 
02073 void imapParser::removeCapability (const TQString & cap)
02074 {
02075   imapCapabilities.remove(cap.lower());
02076 }
02077 
02078 TQString imapParser::namespaceForBox( const TQString & box )
02079 {
02080   kdDebug(7116) << "imapParse::namespaceForBox " << box << endl;
02081   TQString myNamespace;
02082   if ( !box.isEmpty() )
02083   {
02084     TQValueList<TQString> list = namespaceToDelimiter.keys();
02085     TQString cleanPrefix;
02086     for ( TQValueList<TQString>::Iterator it = list.begin(); it != list.end(); ++it )
02087     {
02088       if ( !(*it).isEmpty() && box.find( *it ) != -1 )
02089         return (*it);
02090     }
02091   }
02092   return myNamespace;
02093 }
02094