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