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