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

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"
  • 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.5
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