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