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