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

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
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.6
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