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