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