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