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