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());
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       retVal.insert (parseLiteralC(inWords), new QString(parseLiteralC(inWords)));
00953     }
00954 
00955     if (inWords[0] != ')')
00956       return retVal;
00957     inWords.pos++;
00958     skipWS (inWords);
00959   }
00960 
00961   return retVal;
00962 }
00963 
00964 mimeHeader * imapParser::parseSimplePart (parseString & inWords,
00965   QString & inSection, mimeHeader * localPart)
00966 {
00967   QCString subtype;
00968   QCString typeStr;
00969   QAsciiDict < QString > parameters (17, false);
00970   ulong size;
00971 
00972   parameters.setAutoDelete (true);
00973 
00974   if (inWords[0] != '(')
00975     return 0;
00976 
00977   if (!localPart)
00978     localPart = new mimeHeader;
00979 
00980   localPart->setPartSpecifier (inSection);
00981 
00982   inWords.pos++;
00983   skipWS (inWords);
00984 
00985   //body type
00986   typeStr = parseLiteralC(inWords);
00987 
00988   //body subtype
00989   subtype = parseLiteralC(inWords);
00990 
00991   localPart->setType (typeStr + "/" + subtype);
00992 
00993   //body parameter parenthesized list
00994   parameters = parseParameters (inWords);
00995   {
00996     QAsciiDictIterator < QString > it (parameters);
00997 
00998     while (it.current ())
00999     {
01000       localPart->setTypeParm (it.currentKey (), *(it.current ()));
01001       ++it;
01002     }
01003     parameters.clear ();
01004   }
01005 
01006   //body id
01007   localPart->setID (parseLiteralC(inWords));
01008 
01009   //body description
01010   localPart->setDescription (parseLiteralC(inWords));
01011 
01012   //body encoding
01013   localPart->setEncoding (parseLiteralC(inWords));
01014 
01015   //body size
01016   if (parseOneNumber (inWords, size))
01017     localPart->setLength (size);
01018 
01019   // type specific extensions
01020   if (localPart->getType().upper() == "MESSAGE/RFC822")
01021   {
01022     //envelope structure
01023     mailHeader *envelope = parseEnvelope (inWords);
01024 
01025     //body structure
01026     parseBodyStructure (inWords, inSection, envelope);
01027 
01028     localPart->setNestedMessage (envelope);
01029 
01030     //text lines
01031     ulong lines;
01032     parseOneNumber (inWords, lines);
01033   }
01034   else
01035   {
01036     if (typeStr ==  "TEXT")
01037     {
01038       //text lines
01039       ulong lines;
01040       parseOneNumber (inWords, lines);
01041     }
01042 
01043     // md5
01044     parseLiteralC(inWords);
01045 
01046     // body disposition
01047     parameters = parseDisposition (inWords);
01048     {
01049       QString *disposition = parameters["content-disposition"];
01050 
01051       if (disposition)
01052         localPart->setDisposition (disposition->ascii ());
01053       parameters.remove ("content-disposition");
01054       QAsciiDictIterator < QString > it (parameters);
01055       while (it.current ())
01056       {
01057         localPart->setDispositionParm (it.currentKey (),
01058                                        *(it.current ()));
01059         ++it;
01060       }
01061       parameters.clear ();
01062     }
01063 
01064     // body language
01065     parseSentence (inWords);
01066   }
01067 
01068   // see if we have more to come
01069   while (!inWords.isEmpty () && inWords[0] != ')')
01070   {
01071     //eat the extensions to this part
01072     if (inWords[0] == '(')
01073       parseSentence (inWords);
01074     else
01075       parseLiteralC(inWords);
01076   }
01077 
01078   if (inWords[0] == ')')
01079     inWords.pos++;
01080   skipWS (inWords);
01081 
01082   return localPart;
01083 }
01084 
01085 mimeHeader * imapParser::parseBodyStructure (parseString & inWords,
01086   QString & inSection, mimeHeader * localPart)
01087 {
01088   bool init = false;
01089   if (inSection.isEmpty())
01090   {
01091     // first run
01092     init = true;
01093     // assume one part
01094     inSection = "1";
01095   }
01096   int section = 0;
01097 
01098   if (inWords[0] != '(')
01099   {
01100     // skip ""
01101     parseOneWord (inWords);
01102     return 0;
01103   }
01104   inWords.pos++;
01105   skipWS (inWords);
01106 
01107   if (inWords[0] == '(')
01108   {
01109     QByteArray subtype;
01110     QAsciiDict < QString > parameters (17, false);
01111     QString outSection;
01112     parameters.setAutoDelete (true);
01113     if (!localPart)
01114       localPart = new mimeHeader;
01115     else
01116     {
01117       // might be filled from an earlier run
01118       localPart->clearNestedParts ();
01119       localPart->clearTypeParameters ();
01120       localPart->clearDispositionParameters ();
01121       // an envelope was passed in so this is the multipart header
01122       outSection = inSection + ".HEADER";
01123     }
01124     if (inWords[0] == '(' && init)
01125       inSection = "0";
01126 
01127     // set the section
01128     if ( !outSection.isEmpty() ) {
01129       localPart->setPartSpecifier(outSection);
01130     } else {
01131       localPart->setPartSpecifier(inSection);
01132     }
01133 
01134     // is multipart (otherwise its a simplepart and handled later)
01135     while (inWords[0] == '(')
01136     {
01137       outSection = QString::number(++section);
01138       if (!init)
01139         outSection = inSection + "." + outSection;
01140       mimeHeader *subpart = parseBodyStructure (inWords, outSection, 0);
01141       localPart->addNestedPart (subpart);
01142     }
01143 
01144     // fetch subtype
01145     subtype = parseOneWord (inWords);
01146 
01147     localPart->setType ("MULTIPART/" + b2c(subtype));
01148 
01149     // fetch parameters
01150     parameters = parseParameters (inWords);
01151     {
01152       QAsciiDictIterator < QString > it (parameters);
01153 
01154       while (it.current ())
01155       {
01156         localPart->setTypeParm (it.currentKey (), *(it.current ()));
01157         ++it;
01158       }
01159       parameters.clear ();
01160     }
01161 
01162     // body disposition
01163     parameters = parseDisposition (inWords);
01164     {
01165       QString *disposition = parameters["content-disposition"];
01166 
01167       if (disposition)
01168         localPart->setDisposition (disposition->ascii ());
01169       parameters.remove ("content-disposition");
01170       QAsciiDictIterator < QString > it (parameters);
01171       while (it.current ())
01172       {
01173         localPart->setDispositionParm (it.currentKey (),
01174                                        *(it.current ()));
01175         ++it;
01176       }
01177       parameters.clear ();
01178     }
01179 
01180     // body language
01181     parseSentence (inWords);
01182 
01183   }
01184   else
01185   {
01186     // is simple part
01187     inWords.pos--;
01188     inWords.data[inWords.pos] = '('; //fake a sentence
01189     if ( localPart )
01190       inSection = inSection + ".1";
01191     localPart = parseSimplePart (inWords, inSection, localPart);
01192     inWords.pos--;
01193     inWords.data[inWords.pos] = ')'; //remove fake
01194   }
01195 
01196   // see if we have more to come
01197   while (!inWords.isEmpty () && inWords[0] != ')')
01198   {
01199     //eat the extensions to this part
01200     if (inWords[0] == '(')
01201       parseSentence (inWords);
01202     else
01203       parseLiteralC(inWords);
01204   }
01205 
01206   if (inWords[0] == ')')
01207     inWords.pos++;
01208   skipWS (inWords);
01209 
01210   return localPart;
01211 }
01212 
01213 void imapParser::parseBody (parseString & inWords)
01214 {
01215   // see if we got a part specifier
01216   if (inWords[0] == '[')
01217   {
01218     QByteArray specifier;
01219     QByteArray label;
01220     inWords.pos++;
01221 
01222     specifier = parseOneWord (inWords, TRUE);
01223 
01224     if (inWords[0] == '(')
01225     {
01226       inWords.pos++;
01227 
01228       while (!inWords.isEmpty () && inWords[0] != ')')
01229       {
01230         label = parseOneWord (inWords);
01231       }
01232 
01233       if (inWords[0] == ')')
01234         inWords.pos++;
01235     }
01236     if (inWords[0] == ']')
01237       inWords.pos++;
01238     skipWS (inWords);
01239 
01240     // parse the header
01241     if (qstrncmp(specifier, "0", specifier.size()) == 0)
01242     {
01243       mailHeader *envelope = 0;
01244       if (lastHandled)
01245         envelope = lastHandled->getHeader ();
01246 
01247       if (!envelope || seenUid.isEmpty ())
01248       {
01249         kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl;
01250         // don't know where to put it, throw it away
01251         parseLiteralC(inWords, true);
01252       }
01253       else
01254       {
01255         kdDebug(7116) << "imapParser::parseBody - reading " << envelope << " " << seenUid.ascii () << endl;
01256         // fill it up with data
01257         QString theHeader = parseLiteralC(inWords, true);
01258         mimeIOQString myIO;
01259 
01260         myIO.setString (theHeader);
01261         envelope->parseHeader (myIO);
01262 
01263       }
01264     }
01265     else if (qstrncmp(specifier, "HEADER.FIELDS", specifier.size()) == 0)
01266     {
01267       // BODY[HEADER.FIELDS (References)] {n}
01268       //kdDebug(7116) << "imapParser::parseBody - HEADER.FIELDS: "
01269       // << QCString(label.data(), label.size()+1) << endl;
01270       if (qstrncmp(label, "REFERENCES", label.size()) == 0)
01271       {
01272        mailHeader *envelope = 0;
01273        if (lastHandled)
01274          envelope = lastHandled->getHeader ();
01275 
01276        if (!envelope || seenUid.isEmpty ())
01277        {
01278          kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl;
01279          // don't know where to put it, throw it away
01280          parseLiteralC (inWords, true);
01281        }
01282        else
01283        {
01284          QCString references = parseLiteralC(inWords, true);
01285          int start = references.find ('<');
01286          int end = references.findRev ('>');
01287          if (start < end)
01288                  references = references.mid (start, end - start + 1);
01289          envelope->setReferences(references.simplifyWhiteSpace());
01290        }
01291       }
01292       else
01293       { // not a header we care about throw it away
01294         parseLiteralC(inWords, true);
01295       }
01296     }
01297     else
01298     {
01299       QCString spec(specifier.data(), specifier.size()+1);
01300       if (spec.find(".MIME") != -1)
01301       {
01302         mailHeader *envelope = new mailHeader;
01303         QString theHeader = parseLiteralC(inWords, false);
01304         mimeIOQString myIO;
01305         myIO.setString (theHeader);
01306         envelope->parseHeader (myIO);
01307         if (lastHandled)
01308           lastHandled->setHeader (envelope);
01309         return;
01310       }
01311       // throw it away
01312       kdDebug(7116) << "imapParser::parseBody - discarding " << seenUid.ascii () << endl;
01313       parseLiteralC(inWords, true);
01314     }
01315 
01316   }
01317   else // no part specifier
01318   {
01319     mailHeader *envelope = 0;
01320     if (lastHandled)
01321       envelope = lastHandled->getHeader ();
01322 
01323     if (!envelope || seenUid.isEmpty ())
01324     {
01325       kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl;
01326       // don't know where to put it, throw it away
01327       parseSentence (inWords);
01328     }
01329     else
01330     {
01331       kdDebug(7116) << "imapParser::parseBody - reading " << envelope << " " << seenUid.ascii () << endl;
01332       // fill it up with data
01333       QString section;
01334       mimeHeader *body = parseBodyStructure (inWords, section, envelope);
01335       if (body != envelope)
01336         delete body;
01337     }
01338   }
01339 }
01340 
01341 void imapParser::parseFetch (ulong /* value */, parseString & inWords)
01342 {
01343   if (inWords[0] != '(')
01344     return;
01345   inWords.pos++;
01346   skipWS (inWords);
01347 
01348   delete lastHandled;
01349   lastHandled = 0;
01350 
01351   while (!inWords.isEmpty () && inWords[0] != ')')
01352   {
01353     if (inWords[0] == '(')
01354       parseSentence (inWords);
01355     else
01356     {
01357       QCString word = parseLiteralC(inWords, false, true);
01358 
01359       switch (word[0])
01360       {
01361       case 'E':
01362         if (word == "ENVELOPE")
01363         {
01364           mailHeader *envelope = 0;
01365 
01366           if (lastHandled)
01367             envelope = lastHandled->getHeader ();
01368           else
01369             lastHandled = new imapCache();
01370 
01371           if (envelope && !envelope->getMessageId ().isEmpty ())
01372           {
01373             // we have seen this one already
01374             // or don't know where to put it
01375             parseSentence (inWords);
01376           }
01377           else
01378           {
01379             envelope = parseEnvelope (inWords);
01380             if (envelope)
01381             {
01382               envelope->setPartSpecifier (seenUid + ".0");
01383               lastHandled->setHeader (envelope);
01384               lastHandled->setUid (seenUid.toULong ());
01385             }
01386           }
01387         }
01388         break;
01389 
01390       case 'B':
01391         if (word == "BODY")
01392         {
01393           parseBody (inWords);
01394         }
01395         else if (word == "BODY[]" )
01396         {
01397           // Do the same as with "RFC822"
01398           parseLiteralC(inWords, true);
01399         }
01400         else if (word == "BODYSTRUCTURE")
01401         {
01402           mailHeader *envelope = 0;
01403 
01404           if (lastHandled)
01405             envelope = lastHandled->getHeader ();
01406 
01407           // fill it up with data
01408           QString section;
01409           mimeHeader *body =
01410             parseBodyStructure (inWords, section, envelope);
01411           QByteArray data;
01412           QDataStream stream( data, IO_WriteOnly );
01413           body->serialize(stream);
01414           parseRelay(data);
01415 
01416           delete body;
01417         }
01418         break;
01419 
01420       case 'U':
01421         if (word == "UID")
01422         {
01423           seenUid = parseOneWordC(inWords);
01424           mailHeader *envelope = 0;
01425           if (lastHandled)
01426             envelope = lastHandled->getHeader ();
01427           else
01428             lastHandled = new imapCache();
01429 
01430           if (seenUid.isEmpty ())
01431           {
01432             // unknown what to do
01433             kdDebug(7116) << "imapParser::parseFetch - UID empty" << endl;
01434           }
01435           else
01436           {
01437             lastHandled->setUid (seenUid.toULong ());
01438           }
01439           if (envelope)
01440             envelope->setPartSpecifier (seenUid);
01441         }
01442         break;
01443 
01444       case 'R':
01445         if (word == "RFC822.SIZE")
01446         {
01447           ulong size;
01448           parseOneNumber (inWords, size);
01449 
01450           if (!lastHandled) lastHandled = new imapCache();
01451           lastHandled->setSize (size);
01452         }
01453         else if (word.find ("RFC822") == 0)
01454         {
01455           // might be RFC822 RFC822.TEXT RFC822.HEADER
01456           parseLiteralC(inWords, true);
01457         }
01458         break;
01459 
01460       case 'I':
01461         if (word == "INTERNALDATE")
01462         {
01463           QCString date = parseOneWordC(inWords);
01464           if (!lastHandled) lastHandled = new imapCache();
01465           lastHandled->setDate(date);
01466         }
01467         break;
01468 
01469       case 'F':
01470         if (word == "FLAGS")
01471         {
01472       //kdDebug(7116) << "GOT FLAGS " << inWords.cstr() << endl;
01473           if (!lastHandled) lastHandled = new imapCache();
01474           lastHandled->setFlags (imapInfo::_flags (inWords.cstr()));
01475         }
01476         break;
01477 
01478       default:
01479         parseLiteralC(inWords);
01480         break;
01481       }
01482     }
01483   }
01484 
01485   // see if we have more to come
01486   while (!inWords.isEmpty () && inWords[0] != ')')
01487   {
01488     //eat the extensions to this part
01489     if (inWords[0] == '(')
01490       parseSentence (inWords);
01491     else
01492       parseLiteralC(inWords);
01493   }
01494 
01495   if (inWords[0] != ')')
01496     return;
01497   inWords.pos++;
01498   skipWS (inWords);
01499 }
01500 
01501 
01502 // default parser
01503 void imapParser::parseSentence (parseString & inWords)
01504 {
01505   bool first = true;
01506   int stack = 0;
01507 
01508   //find the first nesting parentheses
01509 
01510   while (!inWords.isEmpty () && (stack != 0 || first))
01511   {
01512     first = false;
01513     skipWS (inWords);
01514 
01515     unsigned char ch = inWords[0];
01516     switch (ch)
01517     {
01518     case '(':
01519       inWords.pos++;
01520       ++stack;
01521       break;
01522     case ')':
01523       inWords.pos++;
01524       --stack;
01525       break;
01526     case '[':
01527       inWords.pos++;
01528       ++stack;
01529       break;
01530     case ']':
01531       inWords.pos++;
01532       --stack;
01533       break;
01534     default:
01535       parseLiteralC(inWords);
01536       skipWS (inWords);
01537       break;
01538     }
01539   }
01540   skipWS (inWords);
01541 }
01542 
01543 void imapParser::parseRecent (ulong value, parseString & result)
01544 {
01545   selectInfo.setRecent (value);
01546   result.pos = result.data.size();
01547 }
01548 
01549 void imapParser::parseNamespace (parseString & result)
01550 {
01551   if ( result[0] != '(' )
01552     return;
01553 
01554   QString delimEmpty;
01555   if ( namespaceToDelimiter.contains( QString::null ) )
01556     delimEmpty = namespaceToDelimiter[QString::null];
01557 
01558   namespaceToDelimiter.clear();
01559   imapNamespaces.clear();
01560 
01561   // remember what section we're in (user, other users, shared)
01562   int ns = -1;
01563   bool personalAvailable = false;
01564   while ( !result.isEmpty() )
01565   {
01566     if ( result[0] == '(' )
01567     {
01568       result.pos++; // tie off (
01569       if ( result[0] == '(' )
01570       {
01571         // new namespace section
01572         result.pos++; // tie off (
01573         ++ns;
01574       }
01575       // namespace prefix
01576       QCString prefix = parseOneWordC( result );
01577       // delimiter
01578       QCString delim = parseOneWordC( result );
01579       kdDebug(7116) << "imapParser::parseNamespace ns='" << prefix <<
01580        "',delim='" << delim << "'" << endl;
01581       if ( ns == 0 )
01582       {
01583         // at least one personal ns
01584         personalAvailable = true;
01585       }
01586       QString nsentry = QString::number( ns ) + "=" + QString(prefix) +
01587         "=" + QString(delim);
01588       imapNamespaces.append( nsentry );
01589       if ( prefix.right( 1 ) == delim ) {
01590         // strip delimiter to get a correct entry for comparisons
01591         prefix.resize( prefix.length() );
01592       }
01593       namespaceToDelimiter[prefix] = delim;
01594 
01595       result.pos++; // tie off )
01596       skipWS( result );
01597     } else if ( result[0] == ')' )
01598     {
01599       result.pos++; // tie off )
01600       skipWS( result );
01601     } else if ( result[0] == 'N' )
01602     {
01603       // drop NIL
01604       ++ns;
01605       parseOneWord( result );
01606     } else {
01607       // drop whatever it is
01608       parseOneWord( result );
01609     }
01610   }
01611   if ( !delimEmpty.isEmpty() ) {
01612     // remember default delimiter
01613     namespaceToDelimiter[QString::null] = delimEmpty;
01614     if ( !personalAvailable )
01615     {
01616       // at least one personal ns would be nice
01617       kdDebug(7116) << "imapParser::parseNamespace - registering own personal ns" << endl;
01618       QString nsentry = "0==" + delimEmpty;
01619       imapNamespaces.append( nsentry );
01620     }
01621   }
01622 }
01623 
01624 int imapParser::parseLoop ()
01625 {
01626   parseString result;
01627 
01628   if (!parseReadLine(result.data)) return -1;
01629 
01630   //kdDebug(7116) << result.cstr(); // includes \n
01631 
01632   if (result.data.isEmpty())
01633     return 0;
01634   if (!sentQueue.count ())
01635   {
01636     // maybe greeting or BYE everything else SHOULD not happen, use NOOP or IDLE
01637     kdDebug(7116) << "imapParser::parseLoop - unhandledResponse: \n" << result.cstr() << endl;
01638     unhandled << result.cstr();
01639   }
01640   else
01641   {
01642     imapCommand *current = sentQueue.at (0);
01643     switch (result[0])
01644     {
01645     case '*':
01646       result.data.resize(result.data.size() - 2);  // tie off CRLF
01647       parseUntagged (result);
01648       break;
01649     case '+':
01650       continuation.duplicate(result.data);
01651       break;
01652     default:
01653       {
01654         QCString tag = parseLiteralC(result);
01655         if (current->id() == tag.data())
01656         {
01657           result.data.resize(result.data.size() - 2);  // tie off CRLF
01658           QByteArray resultCode = parseLiteral (result); //the result
01659           current->setResult (resultCode);
01660           current->setResultInfo(result.cstr());
01661           current->setComplete ();
01662 
01663           sentQueue.removeRef (current);
01664           completeQueue.append (current);
01665           if (result.length())
01666             parseResult (resultCode, result, current->command());
01667         }
01668         else
01669         {
01670           kdDebug(7116) << "imapParser::parseLoop - unknown tag '" << tag << "'" << endl;
01671           QCString cstr = tag + " " + result.cstr();
01672           result.data = cstr;
01673           result.pos = 0;
01674           result.data.resize(cstr.length());
01675         }
01676       }
01677       break;
01678     }
01679   }
01680 
01681   return 1;
01682 }
01683 
01684 void
01685 imapParser::parseRelay (const QByteArray & buffer)
01686 {
01687   Q_UNUSED(buffer);
01688   qWarning
01689     ("imapParser::parseRelay - virtual function not reimplemented - data lost");
01690 }
01691 
01692 void
01693 imapParser::parseRelay (ulong len)
01694 {
01695   Q_UNUSED(len);
01696   qWarning
01697     ("imapParser::parseRelay - virtual function not reimplemented - announcement lost");
01698 }
01699 
01700 bool imapParser::parseRead (QByteArray & buffer, ulong len, ulong relay)
01701 {
01702   Q_UNUSED(buffer);
01703   Q_UNUSED(len);
01704   Q_UNUSED(relay);
01705   qWarning
01706     ("imapParser::parseRead - virtual function not reimplemented - no data read");
01707   return FALSE;
01708 }
01709 
01710 bool imapParser::parseReadLine (QByteArray & buffer, ulong relay)
01711 {
01712   Q_UNUSED(buffer);
01713   Q_UNUSED(relay);
01714   qWarning
01715     ("imapParser::parseReadLine - virtual function not reimplemented - no data read");
01716   return FALSE;
01717 }
01718 
01719 void
01720 imapParser::parseWriteLine (const QString & str)
01721 {
01722   Q_UNUSED(str);
01723   qWarning
01724     ("imapParser::parseWriteLine - virtual function not reimplemented - no data written");
01725 }
01726 
01727 void
01728 imapParser::parseURL (const KURL & _url, QString & _box, QString & _section,
01729                       QString & _type, QString & _uid, QString & _validity, QString & _info)
01730 {
01731   QStringList parameters;
01732 
01733   _box = _url.path ();
01734   kdDebug(7116) << "imapParser::parseURL " << _box << endl;
01735   int paramStart = _box.find("/;");
01736   if ( paramStart > -1 )
01737   {
01738     QString paramString = _box.right( _box.length() - paramStart-2 );
01739     parameters = QStringList::split (';', paramString);  //split parameters
01740     _box.truncate( paramStart ); // strip parameters
01741   }
01742   // extract parameters
01743   for (QStringList::ConstIterator it (parameters.begin ());
01744        it != parameters.end (); ++it)
01745   {
01746     QString temp = (*it);
01747 
01748     // if we have a '/' separator we'll just nuke it
01749     int pt = temp.find ('/');
01750     if (pt > 0)
01751       temp.truncate(pt);
01752     if (temp.find ("section=", 0, false) == 0)
01753       _section = temp.right (temp.length () - 8);
01754     else if (temp.find ("type=", 0, false) == 0)
01755       _type = temp.right (temp.length () - 5);
01756     else if (temp.find ("uid=", 0, false) == 0)
01757       _uid = temp.right (temp.length () - 4);
01758     else if (temp.find ("uidvalidity=", 0, false) == 0)
01759       _validity = temp.right (temp.length () - 12);
01760     else if (temp.find ("info=", 0, false) == 0)
01761       _info = temp.right (temp.length () - 5);
01762   }
01763 //  kdDebug(7116) << "URL: section= " << _section << ", type= " << _type << ", uid= " << _uid << endl;
01764 //  kdDebug(7116) << "URL: user() " << _url.user() << endl;
01765 //  kdDebug(7116) << "URL: path() " << _url.path() << endl;
01766 //  kdDebug(7116) << "URL: encodedPathAndQuery() " << _url.encodedPathAndQuery() << endl;
01767 
01768   if (!_box.isEmpty ())
01769   {
01770     // strip /
01771     if (_box[0] == '/')
01772       _box = _box.right (_box.length () - 1);
01773     if (!_box.isEmpty () && _box[_box.length () - 1] == '/')
01774       _box.truncate(_box.length() - 1);
01775   }
01776   kdDebug(7116) << "URL: box= " << _box << ", section= " << _section << ", type= " 
01777     << _type << ", uid= " << _uid << ", validity= " << _validity << ", info= " << _info << endl;
01778 }
01779 
01780 
01781 QCString imapParser::parseLiteralC(parseString & inWords, bool relay, bool stopAtBracket, int *outlen) {
01782 
01783   if (inWords[0] == '{')
01784   {
01785     QCString retVal;
01786     ulong runLen = inWords.find ('}', 1);
01787     if (runLen > 0)
01788     {
01789       bool proper;
01790       ulong runLenSave = runLen + 1;
01791       QCString tmpstr(runLen);
01792       inWords.takeMidNoResize(tmpstr, 1, runLen - 1);
01793       runLen = tmpstr.toULong (&proper);
01794       inWords.pos += runLenSave;
01795       if (proper)
01796       {
01797         //now get the literal from the server
01798         if (relay)
01799           parseRelay (runLen);
01800         QByteArray rv;
01801         parseRead (rv, runLen, relay ? runLen : 0);
01802         rv.resize(QMAX(runLen, rv.size())); // what's the point?
01803         retVal = b2c(rv);
01804         inWords.clear();
01805         parseReadLine (inWords.data); // must get more
01806 
01807         // no duplicate data transfers
01808         relay = false;
01809       }
01810       else
01811       {
01812         kdDebug(7116) << "imapParser::parseLiteral - error parsing {} - " /*<< strLen*/ << endl;
01813       }
01814     }
01815     else
01816     {
01817       inWords.clear();
01818       kdDebug(7116) << "imapParser::parseLiteral - error parsing unmatched {" << endl;
01819     }
01820     if (outlen) {
01821       *outlen = retVal.length(); // optimize me
01822     }
01823     skipWS (inWords);
01824     return retVal;
01825   }
01826 
01827   return parseOneWordC(inWords, stopAtBracket, outlen);
01828 }
01829 
01830 // does not know about literals ( {7} literal )
01831 QCString imapParser::parseOneWordC (parseString & inWords, bool stopAtBracket, int *outLen)
01832 {
01833   uint retValSize = 0;
01834   uint len = inWords.length();
01835   if (len == 0) {
01836     return QCString();
01837   }
01838 
01839   if (len > 0 && inWords[0] == '"')
01840   {
01841     unsigned int i = 1;
01842     bool quote = FALSE;
01843     while (i < len && (inWords[i] != '"' || quote))
01844     {
01845       if (inWords[i] == '\\') quote = !quote;
01846       else quote = FALSE;
01847       i++;
01848     }
01849     if (i < len)
01850     {
01851       QCString retVal(i);
01852       inWords.pos++;
01853       inWords.takeLeftNoResize(retVal, i - 1);
01854       len = i - 1;
01855       int offset = 0;
01856       for (unsigned int j = 0; j <= len; j++) {
01857         if (retVal[j] == '\\') {
01858           offset++;
01859           j++;
01860         }
01861         retVal[j - offset] = retVal[j];
01862       }
01863       retVal[len - offset] = 0;
01864       retValSize = len - offset;
01865       inWords.pos += i;
01866       skipWS (inWords);
01867       if (outLen) {
01868         *outLen = retValSize;
01869       }
01870       return retVal;
01871     }
01872     else
01873     {
01874       kdDebug(7116) << "imapParser::parseOneWord - error parsing unmatched \"" << endl;
01875       QCString retVal = inWords.cstr();
01876       retValSize = len;
01877       inWords.clear();
01878       if (outLen) {
01879         *outLen = retValSize;
01880       }
01881       return retVal;
01882     }
01883   }
01884   else
01885   {
01886     // not quoted
01887     unsigned int i;
01888     // search for end
01889     for (i = 0; i < len; ++i) {
01890         char ch = inWords[i];
01891         if (ch <= ' ' || ch == '(' || ch == ')' ||
01892             (stopAtBracket && (ch == '[' || ch == ']')))
01893             break;
01894     }
01895 
01896     QCString retVal(i+1);
01897     inWords.takeLeftNoResize(retVal, i);
01898     retValSize = i;
01899     inWords.pos += i;
01900 
01901     if (retVal == "NIL") {
01902       retVal.truncate(0);
01903       retValSize = 0;
01904     }
01905     skipWS (inWords);
01906     if (outLen) {
01907       *outLen = retValSize;
01908     }
01909     return retVal;
01910   }
01911 }
01912 
01913 bool imapParser::parseOneNumber (parseString & inWords, ulong & num)
01914 {
01915   bool valid;
01916   num = parseOneWordC(inWords, TRUE).toULong(&valid);
01917   return valid;
01918 }
01919 
01920 bool imapParser::hasCapability (const QString & cap)
01921 {
01922   QString c = cap.lower();
01923 //  kdDebug(7116) << "imapParser::hasCapability - Looking for '" << cap << "'" << endl;
01924   for (QStringList::ConstIterator it = imapCapabilities.begin ();
01925        it != imapCapabilities.end (); ++it)
01926   {
01927 //    kdDebug(7116) << "imapParser::hasCapability - Examining '" << (*it) << "'" << endl;
01928     if ( !(kasciistricmp(c.ascii(), (*it).ascii())) )
01929     {
01930       return true;
01931     }
01932   }
01933   return false;
01934 }
01935 
01936 void imapParser::removeCapability (const QString & cap)
01937 {
01938   imapCapabilities.remove(cap.lower());
01939 }
01940 
01941 QString imapParser::namespaceForBox( const QString & box )
01942 {
01943   kdDebug(7116) << "imapParse::namespaceForBox " << box << endl;
01944   QString myNamespace;
01945   if ( !box.isEmpty() )
01946   {
01947     QValueList<QString> list = namespaceToDelimiter.keys();
01948     QString cleanPrefix;
01949     for ( QValueList<QString>::Iterator it = list.begin(); it != list.end(); ++it )
01950     {
01951       if ( !(*it).isEmpty() && box.find( *it ) != -1 )
01952         return (*it);
01953     }
01954   }
01955   return myNamespace;
01956 }
01957 
KDE Home | KDE Accessibility Home | Description of Access Keys