• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

kioslave/imap4

imapparser.cpp

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

kioslave/imap4

Skip menu "kioslave/imap4"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.7.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal