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

KDE's Doxygen guidelines are available online.

kioslave/imap4

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

kdepimlibs-4.8.3 API Reference

Skip menu "kdepimlibs-4.8.3 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal