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