00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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
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
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
00171
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
00227 if (!hasCapability ("AUTH=" + aAuth))
00228 return false;
00229
00230
00231 result = sasl_client_new( "imap",
00232
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
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
00276 while (parseLoop() == 0) {
00277 ;
00278 }
00279 if ( cmd->isComplete() ) break;
00280
00281 if (!continuation.isEmpty())
00282 {
00283
00284 if ( continuation.size() > 4 ) {
00285 tmp = QByteArray::fromRawData( continuation.data() + 2, continuation.size() - 4 );
00286 challenge = QByteArray::fromBase64( tmp );
00287
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
00314 challenge = tmp.toBase64();
00315 tmp.clear();
00316
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 );
00331 #endif //HAVE_LIBSASL2
00332 return retVal;
00333 }
00334
00335 void
00336 imapParser::parseUntagged (parseString & result)
00337 {
00338
00339
00340 parseOneWord(result);
00341 QByteArray what = parseLiteral (result);
00342
00343 switch (what[0])
00344 {
00345
00346 case 'B':
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
00356 imapCommand *current = sentQueue.at (0);
00357 current->setResultInfo(result.cstr());
00358 }
00359 currentState = ISTATE_NO;
00360 }
00361 break;
00362
00363 case 'N':
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':
00375 if (what[1] == 'K' && what.size() == 2)
00376 {
00377 parseResult (what, result);
00378 } else if (qstrncmp(what, "OTHER-USER", 10) == 0) {
00379 parseOtherUser (result);
00380 } else if (qstrncmp(what, "OUT-OF-OFFICE", 13) == 0) {
00381 parseOutOfOffice (result);
00382 }
00383 break;
00384 case 'D':
00385 if (qstrncmp(what, "DELEGATE", 8) == 0) {
00386 parseDelegate (result);
00387 }
00388 break;
00389
00390 case 'P':
00391 if (qstrncmp(what, "PREAUTH", what.size()) == 0)
00392 {
00393 parseResult (what, result);
00394 currentState = ISTATE_LOGIN;
00395 }
00396 break;
00397
00398
00399 case 'C':
00400 if (qstrncmp(what, "CAPABILITY", what.size()) == 0)
00401 {
00402 parseCapability (result);
00403 }
00404 break;
00405
00406 case 'F':
00407 if (qstrncmp(what, "FLAGS", what.size()) == 0)
00408 {
00409 parseFlags (result);
00410 }
00411 break;
00412
00413 case 'L':
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':
00429 if (qstrncmp(what, "MYRIGHTS", what.size()) == 0)
00430 {
00431 parseMyRights (result);
00432 }
00433 break;
00434 case 'S':
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':
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':
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':
00466 {
00467 parseCustom( result );
00468 }
00469 break;
00470 default:
00471
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)
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 }
00522 }
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':
00540 if (option == "ALERT")
00541 {
00542 rest.pos = rest.data.indexOf(']', rest.pos) + 1;
00543
00544
00545 selectInfo.setAlert( rest.cstr() );
00546 }
00547 break;
00548
00549 case 'N':
00550 if (option == "NEWNAME")
00551 {
00552 }
00553 break;
00554
00555 case 'P':
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':
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':
00580 if (option == "TRYCREATE")
00581 {
00582 }
00583 break;
00584
00585 case 'U':
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++;
00610 skipWS (rest);
00611 }
00612
00613 if (command.isEmpty())
00614 {
00615
00616
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;
00688
00689 result.pos++;
00690
00691 this_one.parseAttributes( result );
00692
00693 result.pos++;
00694 skipWS (result);
00695
00696 this_one.setHierarchyDelimiter(parseLiteral(result));
00697 this_one.setName (KIMAP::decodeImapFolderName( parseLiteral(result)));
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);
00711 parseOneWord (result);
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);
00723
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);
00735 skipWS (result);
00736 parseOneWord (result);
00737 skipWS (result);
00738 if (result.isEmpty() || result[0] != '(')
00739 return;
00740 result.pos++;
00741 skipWS (result);
00742
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
00755
00756
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
00780
00781 parseOneWord (result);
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.isEmpty() ? "" : 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);
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);
00832 Q_ASSERT( lastResults.isEmpty() );
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);
00851 if (inWords[0] != '(')
00852 return;
00853
00854 inWords.pos++;
00855 skipWS (inWords);
00856
00857 while (!inWords.isEmpty() && inWords[0] != ')')
00858 {
00859 ulong value;
00860
00861 QByteArray label = parseOneWord(inWords);
00862 if (parseOneNumber (inWords, value))
00863 {
00864 if (label == "MESSAGES")
00865 lastStatus.setCount (value);
00866 else if (label == "RECENT")
00867 lastStatus.setRecent (value);
00868 else if (label == "UIDVALIDITY")
00869 lastStatus.setUidValidity (value);
00870 else if (label == "UNSEEN")
00871 lastStatus.setUnseen (value);
00872 else if (label == "UIDNEXT")
00873 lastStatus.setUidNext (value);
00874 }
00875 }
00876
00877 if (inWords[0] == ')')
00878 inWords.pos++;
00879 skipWS (inWords);
00880 }
00881
00882 void imapParser::parseExists (ulong value, parseString & result)
00883 {
00884 selectInfo.setCount (value);
00885 result.pos = result.data.size();
00886 }
00887
00888 void imapParser::parseExpunge (ulong value, parseString & result)
00889 {
00890 Q_UNUSED(value);
00891 Q_UNUSED(result);
00892 }
00893
00894 void imapParser::parseAddressList (parseString & inWords, QList<mailAddress *>& list)
00895 {
00896 if ( inWords.isEmpty() )
00897 return;
00898 if (inWords[0] != '(')
00899 {
00900 parseOneWord (inWords);
00901 }
00902 else
00903 {
00904 inWords.pos++;
00905 skipWS (inWords);
00906
00907 while (!inWords.isEmpty () && inWords[0] != ')')
00908 {
00909 if (inWords[0] == '(') {
00910 mailAddress *addr = new mailAddress;
00911 parseAddress(inWords, *addr);
00912 list.append(addr);
00913 } else {
00914 break;
00915 }
00916 }
00917
00918 if (!inWords.isEmpty() && inWords[0] == ')')
00919 inWords.pos++;
00920 skipWS (inWords);
00921 }
00922 }
00923
00924 const mailAddress& imapParser::parseAddress (parseString & inWords, mailAddress& retVal)
00925 {
00926 inWords.pos++;
00927 skipWS (inWords);
00928
00929 retVal.setFullName(parseLiteral(inWords));
00930 retVal.setCommentRaw(parseLiteral(inWords));
00931 retVal.setUser(parseLiteral(inWords));
00932 retVal.setHost(parseLiteral(inWords));
00933
00934 if (!inWords.isEmpty() && inWords[0] == ')')
00935 inWords.pos++;
00936 skipWS (inWords);
00937
00938 return retVal;
00939 }
00940
00941 mailHeader * imapParser::parseEnvelope (parseString & inWords)
00942 {
00943 mailHeader *envelope = 0;
00944
00945 if (inWords[0] != '(')
00946 return envelope;
00947 inWords.pos++;
00948 skipWS (inWords);
00949
00950 envelope = new mailHeader;
00951
00952
00953 envelope->setDate(parseLiteral(inWords));
00954
00955
00956 envelope->setSubject(parseLiteral(inWords));
00957
00958 QList<mailAddress *> list;
00959
00960
00961 parseAddressList(inWords, list);
00962 if (!list.isEmpty()) {
00963 envelope->setFrom(*list.last());
00964 list.clear();
00965 }
00966
00967
00968 parseAddressList(inWords, list);
00969 if (!list.isEmpty()) {
00970 envelope->setSender(*list.last());
00971 list.clear();
00972 }
00973
00974
00975 parseAddressList(inWords, list);
00976 if (!list.isEmpty()) {
00977 envelope->setReplyTo(*list.last());
00978 list.clear();
00979 }
00980
00981
00982 parseAddressList (inWords, envelope->to());
00983
00984
00985 parseAddressList (inWords, envelope->cc());
00986
00987
00988 parseAddressList (inWords, envelope->bcc());
00989
00990
00991 envelope->setInReplyTo(parseLiteral(inWords));
00992
00993
00994 envelope->setMessageId(parseLiteral(inWords));
00995
00996
00997 while (!inWords.isEmpty () && inWords[0] != ')')
00998 {
00999
01000 if (inWords[0] == '(')
01001 parseSentence (inWords);
01002 else
01003 parseLiteral (inWords);
01004 }
01005
01006 if (!inWords.isEmpty() && inWords[0] == ')')
01007 inWords.pos++;
01008 skipWS (inWords);
01009
01010 return envelope;
01011 }
01012
01013
01014
01015 QHash < QByteArray, QString > imapParser::parseDisposition (parseString & inWords)
01016 {
01017 QByteArray disposition;
01018 QHash < QByteArray, QString > retVal;
01019
01020 if (inWords[0] != '(')
01021 {
01022
01023 disposition = parseOneWord (inWords);
01024 }
01025 else
01026 {
01027 inWords.pos++;
01028 skipWS (inWords);
01029
01030
01031 disposition = parseOneWord (inWords);
01032
01033 retVal = parseParameters (inWords);
01034 if (inWords[0] != ')')
01035 return retVal;
01036 inWords.pos++;
01037 skipWS (inWords);
01038 }
01039
01040 if (!disposition.isEmpty ())
01041 {
01042 retVal.insert ("content-disposition", QString(disposition));
01043 }
01044
01045 return retVal;
01046 }
01047
01048
01049
01050 QHash < QByteArray, QString > imapParser::parseParameters (parseString & inWords)
01051 {
01052 QHash < QByteArray, QString > retVal;
01053
01054 if (inWords[0] != '(')
01055 {
01056
01057 parseOneWord (inWords);
01058 }
01059 else
01060 {
01061 inWords.pos++;
01062 skipWS (inWords);
01063
01064 while (!inWords.isEmpty () && inWords[0] != ')')
01065 {
01066 const QByteArray l1 = parseLiteral(inWords);
01067 const QByteArray l2 = parseLiteral(inWords);
01068 retVal.insert (l1.toLower(), QString(l2));
01069 }
01070
01071 if (inWords[0] != ')')
01072 return retVal;
01073 inWords.pos++;
01074 skipWS (inWords);
01075 }
01076
01077 return retVal;
01078 }
01079
01080 mimeHeader * imapParser::parseSimplePart (parseString & inWords,
01081 QString & inSection, mimeHeader * localPart)
01082 {
01083 QByteArray subtype;
01084 QByteArray typeStr;
01085 QHash < QByteArray, QString > parameters;
01086 ulong size;
01087
01088 if (inWords[0] != '(')
01089 return 0;
01090
01091 if (!localPart)
01092 localPart = new mimeHeader;
01093
01094 localPart->setPartSpecifier (inSection);
01095
01096 inWords.pos++;
01097 skipWS (inWords);
01098
01099
01100 typeStr = parseLiteral(inWords);
01101
01102
01103 subtype = parseLiteral(inWords);
01104
01105 localPart->setType (typeStr + '/' + subtype);
01106
01107
01108 parameters = parseParameters (inWords);
01109 {
01110 QHashIterator < QByteArray, QString > it (parameters);
01111
01112 while (it.hasNext ())
01113 {
01114 it.next();
01115 localPart->setTypeParm (it.key (), it.value ());
01116 }
01117 parameters.clear ();
01118 }
01119
01120
01121 localPart->setID (parseLiteral(inWords));
01122
01123
01124 localPart->setDescription (parseLiteral(inWords));
01125
01126
01127 localPart->setEncoding (parseLiteral(inWords));
01128
01129
01130 if (parseOneNumber (inWords, size))
01131 localPart->setLength (size);
01132
01133
01134 if (localPart->getType().toUpper() == "MESSAGE/RFC822")
01135 {
01136
01137 mailHeader *envelope = parseEnvelope (inWords);
01138
01139
01140 parseBodyStructure (inWords, inSection, envelope);
01141
01142 localPart->setNestedMessage (envelope);
01143
01144
01145 ulong lines;
01146 parseOneNumber (inWords, lines);
01147 }
01148 else
01149 {
01150 if (typeStr == "TEXT")
01151 {
01152
01153 ulong lines;
01154 parseOneNumber (inWords, lines);
01155 }
01156
01157
01158 parseLiteral(inWords);
01159
01160
01161 parameters = parseDisposition (inWords);
01162 {
01163 QString disposition = parameters["content-disposition"];
01164
01165 localPart->setDisposition (disposition.toAscii ());
01166 QHashIterator < QByteArray, QString > it (parameters);
01167 while (it.hasNext ())
01168 {
01169 it.next();
01170 localPart->setDispositionParm (it.key (), it.value ());
01171 }
01172 parameters.clear ();
01173 }
01174
01175
01176 parseSentence (inWords);
01177 }
01178
01179
01180 while (!inWords.isEmpty () && inWords[0] != ')')
01181 {
01182
01183 if (inWords[0] == '(')
01184 parseSentence (inWords);
01185 else
01186 parseLiteral(inWords);
01187 }
01188
01189 if (inWords[0] == ')')
01190 inWords.pos++;
01191 skipWS (inWords);
01192
01193 return localPart;
01194 }
01195
01196 mimeHeader * imapParser::parseBodyStructure (parseString & inWords,
01197 QString & inSection, mimeHeader * localPart)
01198 {
01199 bool init = false;
01200 if (inSection.isEmpty())
01201 {
01202
01203 init = true;
01204
01205 inSection = "1";
01206 }
01207 int section = 0;
01208
01209 if (inWords[0] != '(')
01210 {
01211
01212 parseOneWord (inWords);
01213 return 0;
01214 }
01215 inWords.pos++;
01216 skipWS (inWords);
01217
01218 if (inWords[0] == '(')
01219 {
01220 QByteArray subtype;
01221 QHash< QByteArray, QString > parameters;
01222 QString outSection;
01223
01224 if (!localPart)
01225 localPart = new mimeHeader;
01226 else
01227 {
01228
01229 localPart->clearNestedParts ();
01230 localPart->clearTypeParameters ();
01231 localPart->clearDispositionParameters ();
01232
01233 outSection = inSection + ".HEADER";
01234 }
01235 if (inWords[0] == '(' && init)
01236 inSection = "0";
01237
01238
01239 if ( !outSection.isEmpty() ) {
01240 localPart->setPartSpecifier(outSection);
01241 } else {
01242 localPart->setPartSpecifier(inSection);
01243 }
01244
01245
01246 while (inWords[0] == '(')
01247 {
01248 outSection = QString::number(++section);
01249 if (!init)
01250 outSection = inSection + '.' + outSection;
01251 mimeHeader *subpart = parseBodyStructure (inWords, outSection, 0);
01252 localPart->addNestedPart (subpart);
01253 }
01254
01255
01256 subtype = parseOneWord (inWords);
01257
01258 localPart->setType ("MULTIPART/" + subtype);
01259
01260
01261 parameters = parseParameters (inWords);
01262 {
01263 QHashIterator < QByteArray, QString > it (parameters);
01264
01265 while (it.hasNext ())
01266 {
01267 it.next();
01268 localPart->setTypeParm (it.key (), it.value ());
01269 }
01270 parameters.clear ();
01271 }
01272
01273
01274 parameters = parseDisposition (inWords);
01275 {
01276 QString disposition = parameters["content-disposition"];
01277
01278 localPart->setDisposition (disposition.toAscii ());
01279 QHashIterator < QByteArray, QString > it (parameters);
01280 while (it.hasNext ())
01281 {
01282 it.next();
01283 localPart->setDispositionParm (it.key (), it.value ());
01284 }
01285 parameters.clear ();
01286 }
01287
01288
01289 parseSentence (inWords);
01290
01291 }
01292 else
01293 {
01294
01295 inWords.pos--;
01296 inWords.data[inWords.pos] = '(';
01297 if ( localPart )
01298 inSection = inSection + ".1";
01299 localPart = parseSimplePart (inWords, inSection, localPart);
01300 inWords.pos--;
01301 inWords.data[inWords.pos] = ')';
01302 }
01303
01304
01305 while (!inWords.isEmpty () && inWords[0] != ')')
01306 {
01307
01308 if (inWords[0] == '(')
01309 parseSentence (inWords);
01310 else
01311 parseLiteral(inWords);
01312 }
01313
01314 if (inWords[0] == ')')
01315 inWords.pos++;
01316 skipWS (inWords);
01317
01318 return localPart;
01319 }
01320
01321 void imapParser::parseBody (parseString & inWords)
01322 {
01323
01324 if (inWords[0] == '[')
01325 {
01326 QByteArray specifier;
01327 QByteArray label;
01328 inWords.pos++;
01329
01330 specifier = parseOneWord (inWords, true);
01331
01332 if (inWords[0] == '(')
01333 {
01334 inWords.pos++;
01335
01336 while (!inWords.isEmpty () && inWords[0] != ')')
01337 {
01338 label = parseOneWord (inWords);
01339 }
01340
01341 if (inWords[0] == ')')
01342 inWords.pos++;
01343 }
01344 if (inWords[0] == ']')
01345 inWords.pos++;
01346 skipWS (inWords);
01347
01348
01349 if (qstrncmp(specifier, "0", specifier.size()) == 0)
01350 {
01351 mailHeader *envelope = 0;
01352 if (lastHandled)
01353 envelope = lastHandled->getHeader ();
01354
01355 if (!envelope || seenUid.isEmpty ())
01356 {
01357 kDebug(7116) <<"imapParser::parseBody - discarding" << envelope << seenUid.toAscii ();
01358
01359 parseLiteral(inWords, true);
01360 }
01361 else
01362 {
01363 kDebug(7116) <<"imapParser::parseBody - reading" << envelope << seenUid.toAscii ();
01364
01365 QString theHeader = parseLiteral(inWords, true);
01366 mimeIOQString myIO;
01367
01368 myIO.setString (theHeader);
01369 envelope->parseHeader (myIO);
01370
01371 }
01372 }
01373 else if (qstrncmp(specifier, "HEADER.FIELDS", specifier.size()) == 0)
01374 {
01375
01376
01377
01378 if (qstrncmp(label, "REFERENCES", label.size()) == 0)
01379 {
01380 mailHeader *envelope = 0;
01381 if (lastHandled)
01382 envelope = lastHandled->getHeader ();
01383
01384 if (!envelope || seenUid.isEmpty ())
01385 {
01386 kDebug(7116) <<"imapParser::parseBody - discarding" << envelope << seenUid.toAscii ();
01387
01388 parseLiteral (inWords, true);
01389 }
01390 else
01391 {
01392 QByteArray references = parseLiteral(inWords, true);
01393 int start = references.indexOf ('<');
01394 int end = references.lastIndexOf ('>');
01395 if (start < end)
01396 references = references.mid (start, end - start + 1);
01397 envelope->setReferences(references.simplified());
01398 }
01399 }
01400 else
01401 {
01402 parseLiteral(inWords, true);
01403 }
01404 }
01405 else
01406 {
01407 if (specifier.contains(".MIME") )
01408 {
01409 mailHeader *envelope = new mailHeader;
01410 QString theHeader = parseLiteral(inWords, false);
01411 mimeIOQString myIO;
01412 myIO.setString (theHeader);
01413 envelope->parseHeader (myIO);
01414 if (lastHandled)
01415 lastHandled->setHeader (envelope);
01416 return;
01417 }
01418
01419 kDebug(7116) <<"imapParser::parseBody - discarding" << seenUid.toAscii ();
01420 parseLiteral(inWords, true);
01421 }
01422
01423 }
01424 else
01425 {
01426 mailHeader *envelope = 0;
01427 if (lastHandled)
01428 envelope = lastHandled->getHeader ();
01429
01430 if (!envelope || seenUid.isEmpty ())
01431 {
01432 kDebug(7116) <<"imapParser::parseBody - discarding" << envelope << seenUid.toAscii ();
01433
01434 parseSentence (inWords);
01435 }
01436 else
01437 {
01438 kDebug(7116) <<"imapParser::parseBody - reading" << envelope << seenUid.toAscii ();
01439
01440 QString section;
01441 mimeHeader *body = parseBodyStructure (inWords, section, envelope);
01442 if (body != envelope)
01443 delete body;
01444 }
01445 }
01446 }
01447
01448 void imapParser::parseFetch (ulong , parseString & inWords)
01449 {
01450 if (inWords[0] != '(')
01451 return;
01452 inWords.pos++;
01453 skipWS (inWords);
01454
01455 delete lastHandled;
01456 lastHandled = 0;
01457
01458 while (!inWords.isEmpty () && inWords[0] != ')')
01459 {
01460 if (inWords[0] == '(')
01461 parseSentence (inWords);
01462 else
01463 {
01464 const QByteArray word = parseLiteral(inWords, false, true);
01465
01466 switch (word[0])
01467 {
01468 case 'E':
01469 if (word == "ENVELOPE")
01470 {
01471 mailHeader *envelope = 0;
01472
01473 if (lastHandled)
01474 envelope = lastHandled->getHeader ();
01475 else
01476 lastHandled = new imapCache();
01477
01478 if (envelope && !envelope->getMessageId ().isEmpty ())
01479 {
01480
01481
01482 parseSentence (inWords);
01483 }
01484 else
01485 {
01486 envelope = parseEnvelope (inWords);
01487 if (envelope)
01488 {
01489 envelope->setPartSpecifier (seenUid + ".0");
01490 lastHandled->setHeader (envelope);
01491 lastHandled->setUid (seenUid.toULong ());
01492 }
01493 }
01494 }
01495 break;
01496
01497 case 'B':
01498 if (word == "BODY")
01499 {
01500 parseBody (inWords);
01501 }
01502 else if (word == "BODY[]" )
01503 {
01504
01505 parseLiteral(inWords, true);
01506 }
01507 else if (word == "BODYSTRUCTURE")
01508 {
01509 mailHeader *envelope = 0;
01510
01511 if (lastHandled)
01512 envelope = lastHandled->getHeader ();
01513
01514
01515 QString section;
01516 mimeHeader *body =
01517 parseBodyStructure (inWords, section, envelope);
01518 QByteArray data;
01519 QDataStream stream( &data, QIODevice::WriteOnly );
01520 if ( body )
01521 body->serialize(stream);
01522 parseRelay(data);
01523
01524 delete body;
01525 }
01526 break;
01527
01528 case 'U':
01529 if (word == "UID")
01530 {
01531 seenUid = parseOneWord(inWords);
01532 mailHeader *envelope = 0;
01533 if (lastHandled)
01534 envelope = lastHandled->getHeader ();
01535 else
01536 lastHandled = new imapCache();
01537
01538 if (seenUid.isEmpty ())
01539 {
01540
01541 kDebug(7116) <<"imapParser::parseFetch - UID empty";
01542 }
01543 else
01544 {
01545 lastHandled->setUid (seenUid.toULong ());
01546 }
01547 if (envelope)
01548 envelope->setPartSpecifier (seenUid);
01549 }
01550 break;
01551
01552 case 'R':
01553 if (word == "RFC822.SIZE")
01554 {
01555 ulong size;
01556 parseOneNumber (inWords, size);
01557
01558 if (!lastHandled) lastHandled = new imapCache();
01559 lastHandled->setSize (size);
01560 }
01561 else if (word.startsWith("RFC822"))
01562 {
01563
01564 parseLiteral(inWords, true);
01565 }
01566 break;
01567
01568 case 'I':
01569 if (word == "INTERNALDATE")
01570 {
01571 const QByteArray date = parseOneWord(inWords);
01572 if (!lastHandled) lastHandled = new imapCache();
01573 lastHandled->setDate(date);
01574 }
01575 break;
01576
01577 case 'F':
01578 if (word == "FLAGS")
01579 {
01580
01581 if (!lastHandled) lastHandled = new imapCache();
01582 lastHandled->setFlags (imapInfo::_flags (inWords.cstr()));
01583 }
01584 break;
01585
01586 default:
01587 parseLiteral(inWords);
01588 break;
01589 }
01590 }
01591 }
01592
01593
01594 while (!inWords.isEmpty () && inWords[0] != ')')
01595 {
01596
01597 if (inWords[0] == '(')
01598 parseSentence (inWords);
01599 else
01600 parseLiteral(inWords);
01601 }
01602
01603 if (inWords.isEmpty() || inWords[0] != ')')
01604 return;
01605 inWords.pos++;
01606 skipWS (inWords);
01607 }
01608
01609
01610
01611 void imapParser::parseSentence (parseString & inWords)
01612 {
01613 bool first = true;
01614 int stack = 0;
01615
01616
01617
01618 while (!inWords.isEmpty () && (stack != 0 || first))
01619 {
01620 first = false;
01621 skipWS (inWords);
01622
01623 unsigned char ch = inWords[0];
01624 switch (ch)
01625 {
01626 case '(':
01627 inWords.pos++;
01628 ++stack;
01629 break;
01630 case ')':
01631 inWords.pos++;
01632 --stack;
01633 break;
01634 case '[':
01635 inWords.pos++;
01636 ++stack;
01637 break;
01638 case ']':
01639 inWords.pos++;
01640 --stack;
01641 break;
01642 default:
01643 parseLiteral(inWords);
01644 skipWS (inWords);
01645 break;
01646 }
01647 }
01648 skipWS (inWords);
01649 }
01650
01651 void imapParser::parseRecent (ulong value, parseString & result)
01652 {
01653 selectInfo.setRecent (value);
01654 result.pos = result.data.size();
01655 }
01656
01657 void imapParser::parseNamespace (parseString & result)
01658 {
01659 if ( result[0] != '(' )
01660 return;
01661
01662 QString delimEmpty;
01663 if ( namespaceToDelimiter.contains( QString() ) )
01664 delimEmpty = namespaceToDelimiter[QString()];
01665
01666 namespaceToDelimiter.clear();
01667 imapNamespaces.clear();
01668
01669
01670 int ns = -1;
01671 bool personalAvailable = false;
01672 while ( !result.isEmpty() )
01673 {
01674 if ( result[0] == '(' )
01675 {
01676 result.pos++;
01677 if ( result[0] == '(' )
01678 {
01679
01680 result.pos++;
01681 ++ns;
01682 }
01683
01684 QString prefix = QString::fromLatin1( parseOneWord( result ) );
01685
01686 QString delim = QString::fromLatin1( parseOneWord( result ) );
01687 kDebug(7116) <<"imapParser::parseNamespace ns='" << prefix <<"',delim='" << delim <<"'";
01688 if ( ns == 0 )
01689 {
01690
01691 personalAvailable = true;
01692 }
01693 QString nsentry = QString::number( ns ) + '=' + prefix + '=' + delim;
01694 imapNamespaces.append( nsentry );
01695 if ( prefix.right( 1 ) == delim ) {
01696
01697 prefix.resize( prefix.length() );
01698 }
01699 namespaceToDelimiter[prefix] = delim;
01700
01701 result.pos++;
01702 skipWS( result );
01703 } else if ( result[0] == ')' )
01704 {
01705 result.pos++;
01706 skipWS( result );
01707 } else if ( result[0] == 'N' )
01708 {
01709
01710 ++ns;
01711 parseOneWord( result );
01712 } else {
01713
01714 parseOneWord( result );
01715 }
01716 }
01717 if ( !delimEmpty.isEmpty() ) {
01718
01719 namespaceToDelimiter[QString()] = delimEmpty;
01720 if ( !personalAvailable )
01721 {
01722
01723 kDebug(7116) <<"imapParser::parseNamespace - registering own personal ns";
01724 QString nsentry = "0==" + delimEmpty;
01725 imapNamespaces.append( nsentry );
01726 }
01727 }
01728 }
01729
01730 int imapParser::parseLoop ()
01731 {
01732 parseString result;
01733
01734 if (!parseReadLine(result.data)) return -1;
01735
01736
01737
01738 if (result.data.isEmpty())
01739 return 0;
01740 if (!sentQueue.count ())
01741 {
01742
01743 kDebug(7116) <<"imapParser::parseLoop - unhandledResponse:" << result.cstr();
01744 unhandled << result.cstr();
01745 }
01746 else
01747 {
01748 imapCommand *current = sentQueue.at (0);
01749 switch (result[0])
01750 {
01751 case '*':
01752 result.data.resize(result.data.size() - 2);
01753 parseUntagged (result);
01754 break;
01755 case '+':
01756 continuation = result.data;
01757 break;
01758 default:
01759 {
01760 QByteArray tag = parseLiteral(result);
01761 if (current->id() == tag.data())
01762 {
01763 result.data.resize(result.data.size() - 2);
01764 QByteArray resultCode = parseLiteral (result);
01765 current->setResult (resultCode);
01766 current->setResultInfo(result.cstr());
01767 current->setComplete ();
01768
01769 sentQueue.removeAll (current);
01770 completeQueue.append (current);
01771 if (result.length())
01772 parseResult (resultCode, result, current->command());
01773 }
01774 else
01775 {
01776 kDebug(7116) <<"imapParser::parseLoop - unknown tag '" << tag <<"'";
01777 QByteArray cstr = tag + ' ' + result.cstr();
01778 result.data = cstr;
01779 result.pos = 0;
01780 result.data.resize(cstr.length());
01781 }
01782 }
01783 break;
01784 }
01785 }
01786
01787 return 1;
01788 }
01789
01790 void
01791 imapParser::parseRelay (const QByteArray & buffer)
01792 {
01793 Q_UNUSED(buffer);
01794 qWarning
01795 ("imapParser::parseRelay - virtual function not reimplemented - data lost");
01796 }
01797
01798 void
01799 imapParser::parseRelay (ulong len)
01800 {
01801 Q_UNUSED(len);
01802 qWarning
01803 ("imapParser::parseRelay - virtual function not reimplemented - announcement lost");
01804 }
01805
01806 bool imapParser::parseRead (QByteArray & buffer, long len, long relay)
01807 {
01808 Q_UNUSED(buffer);
01809 Q_UNUSED(len);
01810 Q_UNUSED(relay);
01811 qWarning
01812 ("imapParser::parseRead - virtual function not reimplemented - no data read");
01813 return false;
01814 }
01815
01816 bool imapParser::parseReadLine (QByteArray & buffer, long relay)
01817 {
01818 Q_UNUSED(buffer);
01819 Q_UNUSED(relay);
01820 qWarning
01821 ("imapParser::parseReadLine - virtual function not reimplemented - no data read");
01822 return false;
01823 }
01824
01825 void
01826 imapParser::parseWriteLine (const QString & str)
01827 {
01828 Q_UNUSED(str);
01829 qWarning
01830 ("imapParser::parseWriteLine - virtual function not reimplemented - no data written");
01831 }
01832
01833 void
01834 imapParser::parseURL (const KUrl & _url, QString & _box, QString & _section,
01835 QString & _type, QString & _uid, QString & _validity, QString & _info)
01836 {
01837 QStringList parameters;
01838
01839 _box = _url.path ();
01840 kDebug(7116) <<"imapParser::parseURL" << _box;
01841 int paramStart = _box.indexOf("/;");
01842 if ( paramStart > -1 )
01843 {
01844 QString paramString = _box.right( _box.length() - paramStart-2 );
01845 parameters = paramString.split (';', QString::SkipEmptyParts);
01846 _box.truncate( paramStart );
01847 }
01848
01849 for (QStringList::ConstIterator it (parameters.constBegin ());
01850 it != parameters.constEnd (); ++it)
01851 {
01852 QString temp = (*it);
01853
01854
01855 int pt = temp.indexOf ('/');
01856 if (pt > 0)
01857 temp.truncate(pt);
01858 if (temp.startsWith("section=", Qt::CaseInsensitive))
01859 _section = temp.right (temp.length () - 8);
01860 else if (temp.startsWith("type=", Qt::CaseInsensitive))
01861 _type = temp.right (temp.length () - 5);
01862 else if (temp.startsWith("uid=", Qt::CaseInsensitive))
01863 _uid = temp.right (temp.length () - 4);
01864 else if (temp.startsWith("uidvalidity=", Qt::CaseInsensitive))
01865 _validity = temp.right (temp.length () - 12);
01866 else if (temp.startsWith("info=", Qt::CaseInsensitive))
01867 _info = temp.right (temp.length () - 5);
01868 }
01869
01870
01871
01872
01873
01874 if (!_box.isEmpty ())
01875 {
01876
01877 if (_box[0] == '/')
01878 _box = _box.right (_box.length () - 1);
01879 if (!_box.isEmpty () && _box[_box.length () - 1] == '/')
01880 _box.truncate(_box.length() - 1);
01881 }
01882 kDebug(7116) <<"URL: box=" << _box <<", section=" << _section <<", type="
01883 << _type << ", uid=" << _uid << ", validity=" << _validity << ", info=" << _info;
01884 }
01885
01886
01887 QByteArray imapParser::parseLiteral(parseString & inWords, bool relay, bool stopAtBracket) {
01888
01889 if (!inWords.isEmpty() && inWords[0] == '{')
01890 {
01891 QByteArray retVal;
01892 int runLen = inWords.find ('}', 1);
01893 if (runLen > 0)
01894 {
01895 bool proper;
01896 long runLenSave = runLen + 1;
01897 QByteArray tmpstr(runLen, '\0');
01898 inWords.takeMidNoResize(tmpstr, 1, runLen - 1);
01899 runLen = tmpstr.toULong (&proper);
01900 inWords.pos += runLenSave;
01901 if (proper)
01902 {
01903
01904 if (relay)
01905 parseRelay (runLen);
01906 QByteArray rv;
01907 parseRead (rv, runLen, relay ? runLen : 0);
01908 rv.resize(qMax(runLen, rv.size()));
01909 retVal = rv;
01910 inWords.clear();
01911 parseReadLine (inWords.data);
01912
01913
01914 relay = false;
01915 }
01916 else
01917 {
01918 kDebug(7116) <<"imapParser::parseLiteral - error parsing {} -" ;
01919 }
01920 }
01921 else
01922 {
01923 inWords.clear();
01924 kDebug(7116) <<"imapParser::parseLiteral - error parsing unmatched {";
01925 }
01926 skipWS (inWords);
01927 return retVal;
01928 }
01929
01930 return parseOneWord(inWords, stopAtBracket);
01931 }
01932
01933
01934 QByteArray imapParser::parseOneWord (parseString & inWords, bool stopAtBracket)
01935 {
01936 uint len = inWords.length();
01937 if (len == 0) {
01938 return QByteArray();
01939 }
01940
01941 if (len > 0 && inWords[0] == '"')
01942 {
01943 unsigned int i = 1;
01944 bool quote = false;
01945 while (i < len && (inWords[i] != '"' || quote))
01946 {
01947 if (inWords[i] == '\\') quote = !quote;
01948 else quote = false;
01949 i++;
01950 }
01951 if (i < len)
01952 {
01953 QByteArray retVal;
01954 retVal.resize(i);
01955 inWords.pos++;
01956 inWords.takeLeftNoResize(retVal, i - 1);
01957 len = i - 1;
01958 int offset = 0;
01959 for (unsigned int j = 0; j < len; j++) {
01960 if (retVal[j] == '\\') {
01961 offset++;
01962 j++;
01963 }
01964 retVal[j - offset] = retVal[j];
01965 }
01966 retVal.resize( len - offset );
01967 inWords.pos += i;
01968 skipWS (inWords);
01969 return retVal;
01970 }
01971 else
01972 {
01973 kDebug(7116) <<"imapParser::parseOneWord - error parsing unmatched \"";
01974 QByteArray retVal = inWords.cstr();
01975 inWords.clear();
01976 return retVal;
01977 }
01978 }
01979 else
01980 {
01981
01982 unsigned int i;
01983
01984 for (i = 0; i < len; ++i) {
01985 char ch = inWords[i];
01986 if (ch <= ' ' || ch == '(' || ch == ')' ||
01987 (stopAtBracket && (ch == '[' || ch == ']')))
01988 break;
01989 }
01990
01991 QByteArray retVal;
01992 retVal.resize(i);
01993 inWords.takeLeftNoResize(retVal, i);
01994 inWords.pos += i;
01995
01996 if (retVal == "NIL") {
01997 retVal.truncate(0);
01998 }
01999 skipWS (inWords);
02000 return retVal;
02001 }
02002 }
02003
02004 bool imapParser::parseOneNumber (parseString & inWords, ulong & num)
02005 {
02006 bool valid;
02007 num = parseOneWord(inWords, true).toULong(&valid);
02008 return valid;
02009 }
02010
02011 bool imapParser::hasCapability (const QString & cap)
02012 {
02013 QString c = cap.toLower();
02014
02015 for (QStringList::ConstIterator it = imapCapabilities.constBegin ();
02016 it != imapCapabilities.constEnd (); ++it)
02017 {
02018
02019 if ( !(kasciistricmp(c.toAscii(), (*it).toAscii())) )
02020 {
02021 return true;
02022 }
02023 }
02024 return false;
02025 }
02026
02027 void imapParser::removeCapability (const QString & cap)
02028 {
02029 imapCapabilities.removeAll(cap.toLower());
02030 }
02031
02032 QString imapParser::namespaceForBox( const QString & box )
02033 {
02034 kDebug(7116) <<"imapParse::namespaceForBox" << box;
02035 QString myNamespace;
02036 if ( !box.isEmpty() )
02037 {
02038 const QList<QString> list = namespaceToDelimiter.keys();
02039 QString cleanPrefix;
02040 for ( QList<QString>::ConstIterator it = list.begin(); it != list.end(); ++it )
02041 {
02042 if ( !(*it).isEmpty() && box.contains( *it ) )
02043 return (*it);
02044 }
02045 }
02046 return myNamespace;
02047 }
02048