imapparser.cc
00001 /********************************************************************** 00002 * 00003 * imapparser.cc - IMAP4rev1 Parser 00004 * Copyright (C) 2001-2002 Michael Haeckel <haeckel@kde.org> 00005 * Copyright (C) 2000 s.carstens@gmx.de 00006 * 00007 * This program is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation; either version 2 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * This program is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU General Public License 00018 * along with this program; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00020 * 00021 * Send comments and bug fixes to s.carstens@gmx.de 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 <tqregexp.h> 00051 #include <tqbuffer.h> 00052 #include <tqstring.h> 00053 #include <tqstringlist.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 (TQString::number(commandCounter++)); 00107 sentQueue.append (aCmd); 00108 00109 continuation.resize(0); 00110 const TQString& command = aCmd->command(); 00111 00112 if (command == "SELECT" || command == "EXAMINE") 00113 { 00114 // we need to know which box we are selecting 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 // we no longer have a box open 00123 currentBox = TQString(); 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 == "GEQUOTAROOT" 00132 || command == "GEQUOTA" 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 TQString & aUser, const TQString & aPass, 00150 TQString & 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 //some mechanisms do not require username && pass, so it doesn't need a popup 00178 //window for getting this info 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 TQString & aFTQDN, const TQString & aAuth, bool isSSL, TQString & 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 TQByteArray tmp, challenge; 00230 00231 kdDebug(7116) << "aAuth: " << aAuth << " FTQDN: " << aFTQDN << " isSSL: " << isSSL << endl; 00232 00233 // see if server supports this authenticator 00234 if (!hasCapability ("AUTH=" + aAuth)) 00235 return false; 00236 00237 // result = sasl_client_new( isSSL ? "imaps" : "imap", 00238 result = sasl_client_new( "imap", /* FIXME: with cyrus-imapd, even imaps' digest-uri 00239 must be 'imap'. I don't know if it's good or bad. */ 00240 aFTQDN.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 = TQString::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 = TQString::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 // then lets try it 00273 TQString firstCommand = aAuth; 00274 if ( !challenge.isEmpty() ) { 00275 firstCommand += " "; 00276 firstCommand += TQString::fromLatin1( challenge.data(), challenge.size() ); 00277 } 00278 cmd = sendCommand (new imapCommand ("AUTHENTICATE", firstCommand.latin1())); 00279 00280 int pl = 0; 00281 while ( pl != -1 && !cmd->isComplete () ) 00282 { 00283 //read the next line 00284 while ((pl = parseLoop()) == 0) ; 00285 00286 if (!continuation.isEmpty()) 00287 { 00288 // kdDebug(7116) << "S: " << TQCString(continuation.data(),continuation.size()+1) << endl; 00289 if ( continuation.size() > 4 ) { 00290 tmp.setRawData( continuation.data() + 2, continuation.size() - 4 ); 00291 KCodecs::base64Decode( tmp, challenge ); 00292 // kdDebug(7116) << "S-1: " << TQCString(challenge.data(),challenge.size()+1) << endl; 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 = TQString::fromUtf8( sasl_errdetail( conn ) ); 00313 sasl_dispose( &conn ); 00314 return false; 00315 } 00316 00317 tmp.setRawData( out, outlen ); 00318 // kdDebug(7116) << "C-1: " << TQCString(tmp.data(),tmp.size()+1) << endl; 00319 KCodecs::base64Encode( tmp, challenge ); 00320 tmp.resetRawData( out, outlen ); 00321 // kdDebug(7116) << "C: " << TQCString(challenge.data(),challenge.size()+1) << endl; 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 ); //we don't use sasl_en/decode(), so it's safe to dispose the connection. 00336 #endif //HAVE_LIBSASL2 00337 return retVal; 00338 } 00339 00340 void 00341 imapParser::parseUntagged (parseString & result) 00342 { 00343 //kdDebug(7116) << "imapParser::parseUntagged - '" << result.cstr() << "'" << endl; 00344 00345 parseOneWordC(result); // * 00346 TQByteArray what = parseLiteral (result); // see whats coming next 00347 00348 if(!what.isEmpty ()) { 00349 switch (what[0]) 00350 { 00351 //the status responses 00352 case 'B': // BAD or BYE 00353 if (tqstrncmp(what, "BAD", what.size()) == 0) 00354 { 00355 parseResult (what, result); 00356 } 00357 else if (tqstrncmp(what, "BYE", what.size()) == 0) 00358 { 00359 parseResult (what, result); 00360 if ( sentQueue.count() ) { 00361 // BYE that interrupts a command -> copy the reason for it 00362 imapCommand *current = sentQueue.at (0); 00363 current->setResultInfo(result.cstr()); 00364 } 00365 currentState = ISTATE_NO; 00366 } 00367 break; 00368 00369 case 'N': // NO 00370 if (what[1] == 'O' && what.size() == 2) 00371 { 00372 parseResult (what, result); 00373 } 00374 else if (tqstrncmp(what, "NAMESPACE", what.size()) == 0) 00375 { 00376 parseNamespace (result); 00377 } 00378 break; 00379 00380 case 'O': // OK 00381 if (what[1] == 'K' && what.size() == 2) 00382 { 00383 parseResult (what, result); 00384 } else if (tqstrncmp(what, "OTHER-USER", 10) == 0) { // X-GET-OTHER-USER 00385 parseOtherUser (result); 00386 } else if (tqstrncmp(what, "OUT-OF-OFFICE", 13) == 0) { // X-GET-OUT-OF-OFFICE 00387 parseOutOfOffice (result); 00388 } 00389 break; 00390 case 'D': 00391 if (tqstrncmp(what, "DELEGATE", 8) == 0) { // X-GET-DELEGATES 00392 parseDelegate (result); 00393 } 00394 break; 00395 00396 case 'P': // PREAUTH 00397 if (tqstrncmp(what, "PREAUTH", what.size()) == 0) 00398 { 00399 parseResult (what, result); 00400 currentState = ISTATE_LOGIN; 00401 } 00402 break; 00403 00404 // parse the other responses 00405 case 'C': // CAPABILITY 00406 if (tqstrncmp(what, "CAPABILITY", what.size()) == 0) 00407 { 00408 parseCapability (result); 00409 } 00410 break; 00411 00412 case 'F': // FLAGS 00413 if (tqstrncmp(what, "FLAGS", what.size()) == 0) 00414 { 00415 parseFlags (result); 00416 } 00417 break; 00418 00419 case 'L': // LIST or LSUB or LISTRIGHTS 00420 if (tqstrncmp(what, "LIST", what.size()) == 0) 00421 { 00422 parseList (result); 00423 } 00424 else if (tqstrncmp(what, "LSUB", what.size()) == 0) 00425 { 00426 parseLsub (result); 00427 } 00428 else if (tqstrncmp(what, "LISTRIGHTS", what.size()) == 0) 00429 { 00430 parseListRights (result); 00431 } 00432 break; 00433 00434 case 'M': // MYRIGHTS 00435 if (tqstrncmp(what, "MYRIGHTS", what.size()) == 0) 00436 { 00437 parseMyRights (result); 00438 } 00439 break; 00440 case 'S': // SEARCH or STATUS 00441 if (tqstrncmp(what, "SEARCH", what.size()) == 0) 00442 { 00443 parseSearch (result); 00444 } 00445 else if (tqstrncmp(what, "STATUS", what.size()) == 0) 00446 { 00447 parsetStatus (result); 00448 } 00449 break; 00450 00451 case 'A': // ACL or ANNOTATION 00452 if (tqstrncmp(what, "ACL", what.size()) == 0) 00453 { 00454 parseAcl (result); 00455 } 00456 else if (tqstrncmp(what, "ANNOTATION", what.size()) == 0) 00457 { 00458 parseAnnotation (result); 00459 } 00460 break; 00461 case 'Q': // QUOTA or QUOTAROOT 00462 if ( what.size() > 5 && tqstrncmp(what, "QUOTAROOT", what.size()) == 0) 00463 { 00464 parseQuotaRoot( result ); 00465 } 00466 else if (tqstrncmp(what, "QUOTA", what.size()) == 0) 00467 { 00468 parseQuota( result ); 00469 } 00470 break; 00471 case 'X': // Custom command 00472 { 00473 parseCustom( result ); 00474 } 00475 break; 00476 default: 00477 //better be a number 00478 { 00479 ulong number; 00480 bool valid; 00481 00482 number = TQCString(what, what.size() + 1).toUInt(&valid); 00483 if (valid) 00484 { 00485 what = parseLiteral (result); 00486 if(!what.isEmpty ()) { 00487 switch (what[0]) 00488 { 00489 case 'E': 00490 if (tqstrncmp(what, "EXISTS", what.size()) == 0) 00491 { 00492 parseExists (number, result); 00493 } 00494 else if (tqstrncmp(what, "EXPUNGE", what.size()) == 0) 00495 { 00496 parseExpunge (number, result); 00497 } 00498 break; 00499 00500 case 'F': 00501 if (tqstrncmp(what, "FETCH", what.size()) == 0) 00502 { 00503 seenUid = TQString(); 00504 parseFetch (number, result); 00505 } 00506 break; 00507 00508 case 'S': 00509 if (tqstrncmp(what, "STORE", what.size()) == 0) // deprecated store 00510 { 00511 seenUid = TQString(); 00512 parseFetch (number, result); 00513 } 00514 break; 00515 00516 case 'R': 00517 if (tqstrncmp(what, "RECENT", what.size()) == 0) 00518 { 00519 parseRecent (number, result); 00520 } 00521 break; 00522 default: 00523 break; 00524 } 00525 } 00526 } 00527 } 00528 break; 00529 } //switch 00530 } 00531 } //func 00532 00533 00534 void 00535 imapParser::parseResult (TQByteArray & result, parseString & rest, 00536 const TQString & command) 00537 { 00538 if (command == "SELECT") 00539 selectInfo.setReadWrite(true); 00540 00541 if (rest[0] == '[') 00542 { 00543 rest.pos++; 00544 TQCString option = parseOneWordC(rest, TRUE); 00545 00546 switch (option[0]) 00547 { 00548 case 'A': // ALERT 00549 if (option == "ALERT") 00550 { 00551 rest.pos = rest.data.find(']', rest.pos) + 1; 00552 // The alert text is after [ALERT]. 00553 // Is this correct or do we need to care about litterals? 00554 selectInfo.setAlert( rest.cstr() ); 00555 } 00556 break; 00557 00558 case 'N': // NEWNAME 00559 if (option == "NEWNAME") 00560 { 00561 } 00562 break; 00563 00564 case 'P': //PARSE or PERMANENTFLAGS 00565 if (option == "PARSE") 00566 { 00567 } 00568 else if (option == "PERMANENTFLAGS") 00569 { 00570 uint end = rest.data.find(']', rest.pos); 00571 TQCString flags(rest.data.data() + rest.pos, end - rest.pos); 00572 selectInfo.setPermanentFlags (flags); 00573 rest.pos = end; 00574 } 00575 break; 00576 00577 case 'R': //READ-ONLY or READ-WRITE 00578 if (option == "READ-ONLY") 00579 { 00580 selectInfo.setReadWrite (false); 00581 } 00582 else if (option == "READ-WRITE") 00583 { 00584 selectInfo.setReadWrite (true); 00585 } 00586 break; 00587 00588 case 'T': //TRYCREATE 00589 if (option == "TRYCREATE") 00590 { 00591 } 00592 break; 00593 00594 case 'U': //UIDVALIDITY or UNSEEN 00595 if (option == "UIDVALIDITY") 00596 { 00597 ulong value; 00598 if (parseOneNumber (rest, value)) 00599 selectInfo.setUidValidity (value); 00600 } 00601 else if (option == "UNSEEN") 00602 { 00603 ulong value; 00604 if (parseOneNumber (rest, value)) 00605 selectInfo.setUnseen (value); 00606 } 00607 else if (option == "UIDNEXT") 00608 { 00609 ulong value; 00610 if (parseOneNumber (rest, value)) 00611 selectInfo.setUidNext (value); 00612 } 00613 else 00614 break; 00615 00616 } 00617 if (rest[0] == ']') 00618 rest.pos++; //tie off ] 00619 skipWS (rest); 00620 } 00621 00622 if (command.isEmpty()) 00623 { 00624 // This happens when parsing an intermediate result line (those that start with '*'). 00625 // No state change involved, so we can stop here. 00626 return; 00627 } 00628 00629 switch (command[0].latin1 ()) 00630 { 00631 case 'A': 00632 if (command == "AUTHENTICATE") 00633 if (tqstrncmp(result, "OK", result.size()) == 0) 00634 currentState = ISTATE_LOGIN; 00635 break; 00636 00637 case 'L': 00638 if (command == "LOGIN") 00639 if (tqstrncmp(result, "OK", result.size()) == 0) 00640 currentState = ISTATE_LOGIN; 00641 break; 00642 00643 case 'E': 00644 if (command == "EXAMINE") 00645 { 00646 if (tqstrncmp(result, "OK", result.size()) == 0) 00647 currentState = ISTATE_SELECT; 00648 else 00649 { 00650 if (currentState == ISTATE_SELECT) 00651 currentState = ISTATE_LOGIN; 00652 currentBox = TQString(); 00653 } 00654 kdDebug(7116) << "imapParser::parseResult - current box is now " << currentBox << endl; 00655 } 00656 break; 00657 00658 case 'S': 00659 if (command == "SELECT") 00660 { 00661 if (tqstrncmp(result, "OK", result.size()) == 0) 00662 currentState = ISTATE_SELECT; 00663 else 00664 { 00665 if (currentState == ISTATE_SELECT) 00666 currentState = ISTATE_LOGIN; 00667 currentBox = TQString(); 00668 } 00669 kdDebug(7116) << "imapParser::parseResult - current box is now " << currentBox << endl; 00670 } 00671 break; 00672 00673 default: 00674 break; 00675 } 00676 00677 } 00678 00679 void imapParser::parseCapability (parseString & result) 00680 { 00681 TQCString temp( result.cstr() ); 00682 imapCapabilities = TQStringList::split ( ' ', KPIM::kAsciiToLower( temp.data() ) ); 00683 } 00684 00685 void imapParser::parseFlags (parseString & result) 00686 { 00687 selectInfo.setFlags(result.cstr()); 00688 } 00689 00690 void imapParser::parseList (parseString & result) 00691 { 00692 imapList this_one; 00693 00694 if (result[0] != '(') 00695 return; //not proper format for us 00696 00697 result.pos++; // tie off ( 00698 00699 this_one.parseAttributes( result ); 00700 00701 result.pos++; // tie off ) 00702 skipWS (result); 00703 00704 this_one.setHierarchyDelimiter(parseLiteralC(result)); 00705 this_one.setName (rfcDecoder::fromIMAP(parseLiteralC(result))); // decode modified UTF7 00706 00707 listResponses.append (this_one); 00708 } 00709 00710 void imapParser::parseLsub (parseString & result) 00711 { 00712 imapList this_one (result.cstr(), *this); 00713 listResponses.append (this_one); 00714 } 00715 00716 void imapParser::parseListRights (parseString & result) 00717 { 00718 parseOneWordC (result); // skip mailbox name 00719 parseOneWordC (result); // skip user id 00720 int outlen = 1; 00721 while ( outlen ) { 00722 TQCString word = parseOneWordC (result, false, &outlen); 00723 lastResults.append (word); 00724 } 00725 } 00726 00727 void imapParser::parseAcl (parseString & result) 00728 { 00729 parseOneWordC (result); // skip mailbox name 00730 int outlen = 1; 00731 // The result is user1 perm1 user2 perm2 etc. The caller will sort it out. 00732 while ( outlen && !result.isEmpty() ) { 00733 TQCString word = parseLiteralC (result, false, false, &outlen); 00734 lastResults.append (word); 00735 } 00736 } 00737 00738 void imapParser::parseAnnotation (parseString & result) 00739 { 00740 parseOneWordC (result); // skip mailbox name 00741 skipWS (result); 00742 parseOneWordC (result); // skip entry name (we know it since we don't allow wildcards in it) 00743 skipWS (result); 00744 if (result.isEmpty() || result[0] != '(') 00745 return; 00746 result.pos++; 00747 skipWS (result); 00748 int outlen = 1; 00749 // The result is name1 value1 name2 value2 etc. The caller will sort it out. 00750 while ( outlen && !result.isEmpty() && result[0] != ')' ) { 00751 TQCString word = parseLiteralC (result, false, false, &outlen); 00752 lastResults.append (word); 00753 } 00754 } 00755 00756 00757 void imapParser::parseQuota (parseString & result) 00758 { 00759 // quota_response ::= "QUOTA" SP astring SP quota_list 00760 // quota_list ::= "(" #quota_resource ")" 00761 // quota_resource ::= atom SP number SP number 00762 TQCString root = parseOneWordC( result ); 00763 if ( root.isEmpty() ) { 00764 lastResults.append( "" ); 00765 } else { 00766 lastResults.append( root ); 00767 } 00768 if (result.isEmpty() || result[0] != '(') 00769 return; 00770 result.pos++; 00771 skipWS (result); 00772 TQStringList triplet; 00773 int outlen = 1; 00774 while ( outlen && !result.isEmpty() && result[0] != ')' ) { 00775 TQCString word = parseLiteralC (result, false, false, &outlen); 00776 triplet.append(word); 00777 } 00778 lastResults.append( triplet.join(" ") ); 00779 } 00780 00781 void imapParser::parseQuotaRoot (parseString & result) 00782 { 00783 // quotaroot_response 00784 // ::= "QUOTAROOT" SP astring *(SP astring) 00785 parseOneWordC (result); // skip mailbox name 00786 skipWS (result); 00787 if ( result.isEmpty() ) 00788 return; 00789 TQStringList roots; 00790 int outlen = 1; 00791 while ( outlen && !result.isEmpty() ) { 00792 TQCString word = parseLiteralC (result, false, false, &outlen); 00793 roots.append (word); 00794 } 00795 lastResults.append( roots.isEmpty()? "" : roots.join(" ") ); 00796 } 00797 00798 void imapParser::parseCustom (parseString & result) 00799 { 00800 int outlen = 1; 00801 TQCString word = parseLiteralC (result, false, false, &outlen); 00802 lastResults.append( word ); 00803 } 00804 00805 void imapParser::parseOtherUser (parseString & result) 00806 { 00807 lastResults.append( parseOneWordC( result ) ); 00808 } 00809 00810 void imapParser::parseDelegate (parseString & result) 00811 { 00812 const TQString email = parseOneWordC( result ); 00813 00814 TQStringList rights; 00815 int outlen = 1; 00816 while ( outlen && !result.isEmpty() ) { 00817 TQCString word = parseLiteralC( result, false, false, &outlen ); 00818 rights.append( word ); 00819 } 00820 00821 lastResults.append( email + ":" + rights.join( "," ) ); 00822 } 00823 00824 void imapParser::parseOutOfOffice (parseString & result) 00825 { 00826 const TQString state = parseOneWordC (result); 00827 parseOneWordC (result); // skip encoding 00828 00829 int outlen = 1; 00830 TQCString msg = parseLiteralC (result, false, false, &outlen); 00831 00832 lastResults.append( state + "^" + TQString::fromUtf8( msg ) ); 00833 } 00834 00835 void imapParser::parseMyRights (parseString & result) 00836 { 00837 parseOneWordC (result); // skip mailbox name 00838 Q_ASSERT( lastResults.isEmpty() ); // we can only be called once 00839 lastResults.append (parseOneWordC (result) ); 00840 } 00841 00842 void imapParser::parseSearch (parseString & result) 00843 { 00844 ulong value; 00845 00846 while (parseOneNumber (result, value)) 00847 { 00848 lastResults.append (TQString::number(value)); 00849 } 00850 } 00851 00852 void imapParser::parsetStatus (parseString & inWords) 00853 { 00854 lasStatus = imapInfo (); 00855 00856 parseLiteralC(inWords); // swallow the box 00857 if (inWords.isEmpty() || inWords[0] != '(') 00858 return; 00859 00860 inWords.pos++; 00861 skipWS (inWords); 00862 00863 while (!inWords.isEmpty() && inWords[0] != ')') 00864 { 00865 ulong value; 00866 00867 TQCString label = parseOneWordC(inWords); 00868 if (parseOneNumber (inWords, value)) 00869 { 00870 if (label == "MESSAGES") 00871 lasStatus.setCount (value); 00872 else if (label == "RECENT") 00873 lasStatus.setRecent (value); 00874 else if (label == "UIDVALIDITY") 00875 lasStatus.setUidValidity (value); 00876 else if (label == "UNSEEN") 00877 lasStatus.setUnseen (value); 00878 else if (label == "UIDNEXT") 00879 lasStatus.setUidNext (value); 00880 } 00881 } 00882 00883 if (inWords[0] == ')') 00884 inWords.pos++; 00885 skipWS (inWords); 00886 } 00887 00888 void imapParser::parseExists (ulong value, parseString & result) 00889 { 00890 selectInfo.setCount (value); 00891 result.pos = result.data.size(); 00892 } 00893 00894 void imapParser::parseExpunge (ulong value, parseString & result) 00895 { 00896 Q_UNUSED(value); 00897 Q_UNUSED(result); 00898 } 00899 00900 void imapParser::parseAddressList (parseString & inWords, TQPtrList<mailAddress>& list) 00901 { 00902 if (inWords.isEmpty()) 00903 return; 00904 if (inWords[0] != '(') 00905 { 00906 parseOneWordC (inWords); // parse NIL 00907 } 00908 else 00909 { 00910 inWords.pos++; 00911 skipWS (inWords); 00912 00913 while (!inWords.isEmpty () && inWords[0] != ')') 00914 { 00915 if (inWords[0] == '(') { 00916 mailAddress *addr = new mailAddress; 00917 parseAddress(inWords, *addr); 00918 list.append(addr); 00919 } else { 00920 break; 00921 } 00922 } 00923 00924 if (!inWords.isEmpty() && inWords[0] == ')') 00925 inWords.pos++; 00926 skipWS (inWords); 00927 } 00928 } 00929 00930 const mailAddress& imapParser::parseAddress (parseString & inWords, mailAddress& retVal) 00931 { 00932 inWords.pos++; 00933 skipWS (inWords); 00934 00935 retVal.setFullName(parseLiteralC(inWords)); 00936 retVal.setCommentRaw(parseLiteralC(inWords)); 00937 retVal.setUser(parseLiteralC(inWords)); 00938 retVal.setHost(parseLiteralC(inWords)); 00939 00940 if (!inWords.isEmpty() && inWords[0] == ')') 00941 inWords.pos++; 00942 skipWS (inWords); 00943 00944 return retVal; 00945 } 00946 00947 mailHeader * imapParser::parseEnvelope (parseString & inWords) 00948 { 00949 mailHeader *envelope = 0; 00950 00951 if (inWords[0] != '(') 00952 return envelope; 00953 inWords.pos++; 00954 skipWS (inWords); 00955 00956 envelope = new mailHeader; 00957 00958 //date 00959 envelope->setDate(parseLiteralC(inWords)); 00960 00961 //subject 00962 envelope->setSubject(parseLiteralC(inWords)); 00963 00964 TQPtrList<mailAddress> list; 00965 list.setAutoDelete(true); 00966 00967 //from 00968 parseAddressList(inWords, list); 00969 if (!list.isEmpty()) { 00970 envelope->setFrom(*list.last()); 00971 list.clear(); 00972 } 00973 00974 //sender 00975 parseAddressList(inWords, list); 00976 if (!list.isEmpty()) { 00977 envelope->setSender(*list.last()); 00978 list.clear(); 00979 } 00980 00981 //reply-to 00982 parseAddressList(inWords, list); 00983 if (!list.isEmpty()) { 00984 envelope->setReplyTo(*list.last()); 00985 list.clear(); 00986 } 00987 00988 //to 00989 parseAddressList (inWords, envelope->to()); 00990 00991 //cc 00992 parseAddressList (inWords, envelope->cc()); 00993 00994 //bcc 00995 parseAddressList (inWords, envelope->bcc()); 00996 00997 //in-reply-to 00998 envelope->setInReplyTo(parseLiteralC(inWords)); 00999 01000 //message-id 01001 envelope->setMessageId(parseLiteralC(inWords)); 01002 01003 // see if we have more to come 01004 while (!inWords.isEmpty () && inWords[0] != ')') 01005 { 01006 //eat the extensions to this part 01007 if (inWords[0] == '(') 01008 parseSentence (inWords); 01009 else 01010 parseLiteralC (inWords); 01011 } 01012 01013 if (!inWords.isEmpty() && inWords[0] == ')') 01014 inWords.pos++; 01015 skipWS (inWords); 01016 01017 return envelope; 01018 } 01019 01020 // parse parameter pairs into a dictionary 01021 // caller must clean up the dictionary items 01022 TQAsciiDict < TQString > imapParser::parseDisposition (parseString & inWords) 01023 { 01024 TQCString disposition; 01025 TQAsciiDict < TQString > retVal (17, false); 01026 01027 // return value is a shallow copy 01028 retVal.setAutoDelete (false); 01029 01030 if (inWords[0] != '(') 01031 { 01032 //disposition only 01033 disposition = parseOneWordC (inWords); 01034 } 01035 else 01036 { 01037 inWords.pos++; 01038 skipWS (inWords); 01039 01040 //disposition 01041 disposition = parseOneWordC (inWords); 01042 retVal = parseParameters (inWords); 01043 if (inWords[0] != ')') 01044 return retVal; 01045 inWords.pos++; 01046 skipWS (inWords); 01047 } 01048 01049 if (!disposition.isEmpty ()) 01050 { 01051 retVal.insert ("content-disposition", new TQString(disposition)); 01052 } 01053 01054 return retVal; 01055 } 01056 01057 // parse parameter pairs into a dictionary 01058 // caller must clean up the dictionary items 01059 TQAsciiDict < TQString > imapParser::parseParameters (parseString & inWords) 01060 { 01061 TQAsciiDict < TQString > retVal (17, false); 01062 01063 // return value is a shallow copy 01064 retVal.setAutoDelete (false); 01065 01066 if (inWords[0] != '(') 01067 { 01068 //better be NIL 01069 parseOneWordC (inWords); 01070 } 01071 else 01072 { 01073 inWords.pos++; 01074 skipWS (inWords); 01075 01076 while (!inWords.isEmpty () && inWords[0] != ')') 01077 { 01078 TQCString l1 = parseLiteralC(inWords); 01079 TQCString l2 = parseLiteralC(inWords); 01080 retVal.insert (l1, new TQString(l2)); 01081 } 01082 01083 if (inWords[0] != ')') 01084 return retVal; 01085 inWords.pos++; 01086 skipWS (inWords); 01087 } 01088 01089 return retVal; 01090 } 01091 01092 mimeHeader * imapParser::parseSimplePart (parseString & inWords, 01093 TQString & inSection, mimeHeader * localPart) 01094 { 01095 TQCString subtype; 01096 TQCString typeStr; 01097 TQAsciiDict < TQString > parameters (17, false); 01098 ulong size; 01099 01100 parameters.setAutoDelete (true); 01101 01102 if (inWords[0] != '(') 01103 return 0; 01104 01105 if (!localPart) 01106 localPart = new mimeHeader; 01107 01108 localPart->setPartSpecifier (inSection); 01109 01110 inWords.pos++; 01111 skipWS (inWords); 01112 01113 //body type 01114 typeStr = parseLiteralC(inWords); 01115 01116 //body subtype 01117 subtype = parseLiteralC(inWords); 01118 01119 localPart->setType (typeStr + "/" + subtype); 01120 01121 //body parameter parenthesized list 01122 parameters = parseParameters (inWords); 01123 { 01124 TQAsciiDictIterator < TQString > it (parameters); 01125 01126 while (it.current ()) 01127 { 01128 localPart->setTypeParm (it.currentKey (), *(it.current ())); 01129 ++it; 01130 } 01131 parameters.clear (); 01132 } 01133 01134 //body id 01135 localPart->setID (parseLiteralC(inWords)); 01136 01137 //body description 01138 localPart->setDescription (parseLiteralC(inWords)); 01139 01140 //body encoding 01141 localPart->setEncoding (parseLiteralC(inWords)); 01142 01143 //body size 01144 if (parseOneNumber (inWords, size)) 01145 localPart->setLength (size); 01146 01147 // type specific extensions 01148 if (localPart->getType().upper() == "MESSAGE/RFC822") 01149 { 01150 //envelope structure 01151 mailHeader *envelope = parseEnvelope (inWords); 01152 01153 //body structure 01154 parseBodyStructure (inWords, inSection, envelope); 01155 01156 localPart->setNestedMessage (envelope); 01157 01158 //text lines 01159 ulong lines; 01160 parseOneNumber (inWords, lines); 01161 } 01162 else 01163 { 01164 if (typeStr == "TEXT") 01165 { 01166 //text lines 01167 ulong lines; 01168 parseOneNumber (inWords, lines); 01169 } 01170 01171 // md5 01172 parseLiteralC(inWords); 01173 01174 // body disposition 01175 parameters = parseDisposition (inWords); 01176 { 01177 TQString *disposition = parameters["content-disposition"]; 01178 01179 if (disposition) 01180 localPart->setDisposition (disposition->ascii ()); 01181 parameters.remove ("content-disposition"); 01182 TQAsciiDictIterator < TQString > it (parameters); 01183 while (it.current ()) 01184 { 01185 localPart->setDispositionParm (it.currentKey (), 01186 *(it.current ())); 01187 ++it; 01188 } 01189 01190 parameters.clear (); 01191 } 01192 01193 // body language 01194 parseSentence (inWords); 01195 } 01196 01197 // see if we have more to come 01198 while (!inWords.isEmpty () && inWords[0] != ')') 01199 { 01200 //eat the extensions to this part 01201 if (inWords[0] == '(') 01202 parseSentence (inWords); 01203 else 01204 parseLiteralC(inWords); 01205 } 01206 if (inWords[0] == ')') 01207 inWords.pos++; 01208 skipWS (inWords); 01209 01210 return localPart; 01211 } 01212 01213 mimeHeader * imapParser::parseBodyStructure (parseString & inWords, 01214 TQString & inSection, mimeHeader * localPart) 01215 { 01216 bool init = false; 01217 if (inSection.isEmpty()) 01218 { 01219 // first run 01220 init = true; 01221 // assume one part 01222 inSection = "1"; 01223 } 01224 int section = 0; 01225 01226 if (inWords[0] != '(') 01227 { 01228 // skip "" 01229 parseOneWordC (inWords); 01230 return 0; 01231 } 01232 inWords.pos++; 01233 skipWS (inWords); 01234 01235 if (inWords[0] == '(') 01236 { 01237 TQByteArray subtype; 01238 TQAsciiDict < TQString > parameters (17, false); 01239 TQString outSection; 01240 parameters.setAutoDelete (true); 01241 if (!localPart) 01242 localPart = new mimeHeader; 01243 else 01244 { 01245 // might be filled from an earlier run 01246 localPart->clearNestedParts (); 01247 localPart->clearTypeParameters (); 01248 localPart->clearDispositionParameters (); 01249 // an envelope was passed in so this is the multipart header 01250 outSection = inSection + ".HEADER"; 01251 } 01252 if (inWords[0] == '(' && init) 01253 inSection = "0"; 01254 01255 // set the section 01256 if ( !outSection.isEmpty() ) { 01257 localPart->setPartSpecifier(outSection); 01258 } else { 01259 localPart->setPartSpecifier(inSection); 01260 } 01261 01262 // is multipart (otherwise its a simplepart and handled later) 01263 while (inWords[0] == '(') 01264 { 01265 outSection = TQString::number(++section); 01266 if (!init) 01267 outSection = inSection + "." + outSection; 01268 mimeHeader *subpart = parseBodyStructure (inWords, outSection, 0); 01269 localPart->addNestedPart (subpart); 01270 } 01271 01272 // fetch subtype 01273 subtype = parseOneWordC (inWords); 01274 01275 localPart->setType ("MULTIPART/" + b2c(subtype)); 01276 01277 // fetch parameters 01278 parameters = parseParameters (inWords); 01279 { 01280 TQAsciiDictIterator < TQString > it (parameters); 01281 01282 while (it.current ()) 01283 { 01284 localPart->setTypeParm (it.currentKey (), *(it.current ())); 01285 ++it; 01286 } 01287 parameters.clear (); 01288 } 01289 01290 // body disposition 01291 parameters = parseDisposition (inWords); 01292 { 01293 TQString *disposition = parameters["content-disposition"]; 01294 01295 if (disposition) 01296 localPart->setDisposition (disposition->ascii ()); 01297 parameters.remove ("content-disposition"); 01298 TQAsciiDictIterator < TQString > it (parameters); 01299 while (it.current ()) 01300 { 01301 localPart->setDispositionParm (it.currentKey (), 01302 *(it.current ())); 01303 ++it; 01304 } 01305 parameters.clear (); 01306 } 01307 01308 // body language 01309 parseSentence (inWords); 01310 01311 } 01312 else 01313 { 01314 // is simple part 01315 inWords.pos--; 01316 inWords.data[inWords.pos] = '('; //fake a sentence 01317 if ( localPart ) 01318 inSection = inSection + ".1"; 01319 localPart = parseSimplePart (inWords, inSection, localPart); 01320 inWords.pos--; 01321 inWords.data[inWords.pos] = ')'; //remove fake 01322 } 01323 01324 // see if we have more to come 01325 while (!inWords.isEmpty () && inWords[0] != ')') 01326 { 01327 //eat the extensions to this part 01328 if (inWords[0] == '(') 01329 parseSentence (inWords); 01330 else 01331 parseLiteralC(inWords); 01332 } 01333 01334 if (inWords[0] == ')') 01335 inWords.pos++; 01336 skipWS (inWords); 01337 01338 return localPart; 01339 } 01340 01341 void imapParser::parseBody (parseString & inWords) 01342 { 01343 // see if we got a part specifier 01344 if (inWords[0] == '[') 01345 { 01346 TQCString specifier; 01347 TQCString label; 01348 inWords.pos++; 01349 01350 specifier = parseOneWordC (inWords, TRUE); 01351 01352 if (inWords[0] == '(') 01353 { 01354 inWords.pos++; 01355 01356 while (!inWords.isEmpty () && inWords[0] != ')') 01357 { 01358 label = parseOneWordC (inWords); 01359 } 01360 01361 if (!inWords.isEmpty () && inWords[0] == ')') 01362 inWords.pos++; 01363 } 01364 if (!inWords.isEmpty () && inWords[0] == ']') 01365 inWords.pos++; 01366 skipWS (inWords); 01367 01368 // parse the header 01369 if (specifier == "0") 01370 { 01371 mailHeader *envelope = 0; 01372 if (lastHandled) 01373 envelope = lastHandled->getHeader (); 01374 01375 if (!envelope || seenUid.isEmpty ()) 01376 { 01377 kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl; 01378 // don't know where to put it, throw it away 01379 parseLiteralC(inWords, true); 01380 } 01381 else 01382 { 01383 kdDebug(7116) << "imapParser::parseBody - reading " << envelope << " " << seenUid.ascii () << endl; 01384 // fill it up with data 01385 TQString theHeader = parseLiteralC(inWords, true); 01386 mimeIOTQString myIO; 01387 01388 myIO.setString (theHeader); 01389 envelope->parseHeader (myIO); 01390 01391 } 01392 } 01393 else if (specifier == "HEADER.FIELDS") 01394 { 01395 // BODY[HEADER.FIELDS (References)] {n} 01396 //kdDebug(7116) << "imapParser::parseBody - HEADER.FIELDS: " 01397 // << TQCString(label.data(), label.size()+1) << endl; 01398 if (label == "REFERENCES") 01399 { 01400 mailHeader *envelope = 0; 01401 if (lastHandled) 01402 envelope = lastHandled->getHeader (); 01403 01404 if (!envelope || seenUid.isEmpty ()) 01405 { 01406 kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl; 01407 // don't know where to put it, throw it away 01408 parseLiteralC (inWords, true); 01409 } 01410 else 01411 { 01412 TQCString references = parseLiteralC(inWords, true); 01413 int start = references.find ('<'); 01414 int end = references.findRev ('>'); 01415 if (start < end) 01416 references = references.mid (start, end - start + 1); 01417 envelope->setReferences(references.simplifyWhiteSpace()); 01418 } 01419 } 01420 else 01421 { // not a header we care about throw it away 01422 parseLiteralC(inWords, true); 01423 } 01424 } 01425 else 01426 { 01427 if (specifier.find(".MIME") != -1) 01428 { 01429 mailHeader *envelope = new mailHeader; 01430 TQString theHeader = parseLiteralC(inWords, false); 01431 mimeIOTQString myIO; 01432 myIO.setString (theHeader); 01433 envelope->parseHeader (myIO); 01434 if (lastHandled) 01435 lastHandled->setHeader (envelope); 01436 return; 01437 } 01438 // throw it away 01439 kdDebug(7116) << "imapParser::parseBody - discarding " << seenUid.ascii () << endl; 01440 parseLiteralC(inWords, true); 01441 } 01442 01443 } 01444 else // no part specifier 01445 { 01446 mailHeader *envelope = 0; 01447 if (lastHandled) 01448 envelope = lastHandled->getHeader (); 01449 01450 if (!envelope || seenUid.isEmpty ()) 01451 { 01452 kdDebug(7116) << "imapParser::parseBody - discarding " << envelope << " " << seenUid.ascii () << endl; 01453 // don't know where to put it, throw it away 01454 parseSentence (inWords); 01455 } 01456 else 01457 { 01458 kdDebug(7116) << "imapParser::parseBody - reading " << envelope << " " << seenUid.ascii () << endl; 01459 // fill it up with data 01460 TQString section; 01461 mimeHeader *body = parseBodyStructure (inWords, section, envelope); 01462 if (body != envelope) 01463 delete body; 01464 } 01465 } 01466 } 01467 01468 void imapParser::parseFetch (ulong /* value */, parseString & inWords) 01469 { 01470 if (inWords[0] != '(') 01471 return; 01472 inWords.pos++; 01473 skipWS (inWords); 01474 01475 delete lastHandled; 01476 lastHandled = 0; 01477 01478 while (!inWords.isEmpty () && inWords[0] != ')') 01479 { 01480 if (inWords[0] == '(') 01481 parseSentence (inWords); 01482 else 01483 { 01484 TQCString word = parseLiteralC(inWords, false, true); 01485 01486 if(!word.isEmpty()) { 01487 switch (word[0]) 01488 { 01489 case 'E': 01490 if (word == "ENVELOPE") 01491 { 01492 mailHeader *envelope = 0; 01493 01494 if (lastHandled) 01495 envelope = lastHandled->getHeader (); 01496 else 01497 lastHandled = new imapCache(); 01498 01499 if (envelope && !envelope->getMessageId ().isEmpty ()) 01500 { 01501 // we have seen this one already 01502 // or don't know where to put it 01503 parseSentence (inWords); 01504 } 01505 else 01506 { 01507 envelope = parseEnvelope (inWords); 01508 if (envelope) 01509 { 01510 envelope->setPartSpecifier (seenUid + ".0"); 01511 lastHandled->setHeader (envelope); 01512 lastHandled->setUid (seenUid.toULong ()); 01513 } 01514 } 01515 } 01516 break; 01517 01518 case 'B': 01519 if (word == "BODY") 01520 { 01521 parseBody (inWords); 01522 } 01523 else if (word == "BODY[]" ) 01524 { 01525 // Do the same as with "RFC822" 01526 parseLiteralC(inWords, true); 01527 } 01528 else if (word == "BODYSTRUCTURE") 01529 { 01530 mailHeader *envelope = 0; 01531 01532 if (lastHandled) 01533 envelope = lastHandled->getHeader (); 01534 01535 // fill it up with data 01536 TQString section; 01537 mimeHeader *body = 01538 parseBodyStructure (inWords, section, envelope); 01539 TQByteArray data; 01540 TQDataStream stream( data, IO_WriteOnly ); 01541 if (body) body->serialize(stream); 01542 parseRelay(data); 01543 01544 delete body; 01545 } 01546 break; 01547 01548 case 'U': 01549 if (word == "UID") 01550 { 01551 seenUid = parseOneWordC(inWords); 01552 mailHeader *envelope = 0; 01553 if (lastHandled) 01554 envelope = lastHandled->getHeader (); 01555 else 01556 lastHandled = new imapCache(); 01557 01558 if (seenUid.isEmpty ()) 01559 { 01560 // unknown what to do 01561 kdDebug(7116) << "imapParser::parseFetch - UID empty" << endl; 01562 } 01563 else 01564 { 01565 lastHandled->setUid (seenUid.toULong ()); 01566 } 01567 if (envelope) 01568 envelope->setPartSpecifier (seenUid); 01569 } 01570 break; 01571 01572 case 'R': 01573 if (word == "RFC822.SIZE") 01574 { 01575 ulong size; 01576 parseOneNumber (inWords, size); 01577 01578 if (!lastHandled) lastHandled = new imapCache(); 01579 lastHandled->setSize (size); 01580 } 01581 else if (word.find ("RFC822") == 0) 01582 { 01583 // might be RFC822 RFC822.TEXT RFC822.HEADER 01584 parseLiteralC(inWords, true); 01585 } 01586 break; 01587 01588 case 'I': 01589 if (word == "INTERNALDATE") 01590 { 01591 TQCString date = parseOneWordC(inWords); 01592 if (!lastHandled) lastHandled = new imapCache(); 01593 lastHandled->setDate(date); 01594 } 01595 break; 01596 01597 case 'F': 01598 if (word == "FLAGS") 01599 { 01600 //kdDebug(7116) << "GOT FLAGS " << inWords.cstr() << endl; 01601 if (!lastHandled) lastHandled = new imapCache(); 01602 lastHandled->setFlags (imapInfo::_flags (inWords.cstr())); 01603 } 01604 break; 01605 01606 default: 01607 parseLiteralC(inWords); 01608 break; 01609 } 01610 } else { 01611 parseLiteralC(inWords); 01612 } 01613 } 01614 } 01615 01616 // see if we have more to come 01617 while (!inWords.isEmpty () && inWords[0] != ')') 01618 { 01619 //eat the extensions to this part 01620 if (inWords[0] == '(') 01621 parseSentence (inWords); 01622 else 01623 parseLiteralC(inWords); 01624 } 01625 01626 if (inWords.isEmpty() || inWords[0] != ')') 01627 return; 01628 inWords.pos++; 01629 skipWS (inWords); 01630 } 01631 01632 01633 // default parser 01634 void imapParser::parseSentence (parseString & inWords) 01635 { 01636 bool first = true; 01637 int stack = 0; 01638 01639 //find the first nesting parentheses 01640 01641 while (!inWords.isEmpty () && (stack != 0 || first)) 01642 { 01643 first = false; 01644 skipWS (inWords); 01645 01646 unsigned char ch = inWords[0]; 01647 switch (ch) 01648 { 01649 case '(': 01650 inWords.pos++; 01651 ++stack; 01652 break; 01653 case ')': 01654 inWords.pos++; 01655 --stack; 01656 break; 01657 case '[': 01658 inWords.pos++; 01659 ++stack; 01660 break; 01661 case ']': 01662 inWords.pos++; 01663 --stack; 01664 break; 01665 default: 01666 parseLiteralC(inWords); 01667 skipWS (inWords); 01668 break; 01669 } 01670 } 01671 skipWS (inWords); 01672 } 01673 01674 void imapParser::parseRecent (ulong value, parseString & result) 01675 { 01676 selectInfo.setRecent (value); 01677 result.pos = result.data.size(); 01678 } 01679 01680 void imapParser::parseNamespace (parseString & result) 01681 { 01682 if ( result[0] != '(' ) 01683 return; 01684 01685 TQString delimEmpty; 01686 if ( namespaceToDelimiter.contains( TQString() ) ) 01687 delimEmpty = namespaceToDelimiter[TQString()]; 01688 01689 namespaceToDelimiter.clear(); 01690 imapNamespaces.clear(); 01691 01692 // remember what section we're in (user, other users, shared) 01693 int ns = -1; 01694 bool personalAvailable = false; 01695 while ( !result.isEmpty() ) 01696 { 01697 if ( result[0] == '(' ) 01698 { 01699 result.pos++; // tie off ( 01700 if ( result[0] == '(' ) 01701 { 01702 // new namespace section 01703 result.pos++; // tie off ( 01704 ++ns; 01705 } 01706 // namespace prefix 01707 TQCString prefix = parseOneWordC( result ); 01708 // delimiter 01709 TQCString delim = parseOneWordC( result ); 01710 kdDebug(7116) << "imapParser::parseNamespace ns='" << prefix << 01711 "',delim='" << delim << "'" << endl; 01712 if ( ns == 0 ) 01713 { 01714 // at least one personal ns 01715 personalAvailable = true; 01716 } 01717 TQString nsentry = TQString::number( ns ) + "=" + TQString(prefix) + 01718 "=" + TQString(delim); 01719 imapNamespaces.append( nsentry ); 01720 if ( prefix.right( 1 ) == delim ) { 01721 // strip delimiter to get a correct entry for comparisons 01722 prefix.resize( prefix.length() ); 01723 } 01724 namespaceToDelimiter[prefix] = delim; 01725 01726 result.pos++; // tie off ) 01727 skipWS( result ); 01728 } else if ( result[0] == ')' ) 01729 { 01730 result.pos++; // tie off ) 01731 skipWS( result ); 01732 } else if ( result[0] == 'N' ) 01733 { 01734 // drop NIL 01735 ++ns; 01736 parseOneWordC( result ); 01737 } else { 01738 // drop whatever it is 01739 parseOneWordC( result ); 01740 } 01741 } 01742 if ( !delimEmpty.isEmpty() ) { 01743 // remember default delimiter 01744 namespaceToDelimiter[TQString()] = delimEmpty; 01745 if ( !personalAvailable ) 01746 { 01747 // at least one personal ns would be nice 01748 kdDebug(7116) << "imapParser::parseNamespace - registering own personal ns" << endl; 01749 TQString nsentry = "0==" + delimEmpty; 01750 imapNamespaces.append( nsentry ); 01751 } 01752 } 01753 } 01754 01755 int imapParser::parseLoop () 01756 { 01757 parseString result; 01758 01759 if (!parseReadLine(result.data)) return -1; 01760 01761 //kdDebug(7116) << result.cstr(); // includes \n 01762 01763 if (result.data.isEmpty()) 01764 return 0; 01765 if (!sentQueue.count ()) 01766 { 01767 // maybe greeting or BYE everything else SHOULD not happen, use NOOP or IDLE 01768 kdDebug(7116) << "imapParser::parseLoop - unhandledResponse: \n" << result.cstr() << endl; 01769 unhandled << result.cstr(); 01770 } 01771 else 01772 { 01773 imapCommand *current = sentQueue.at (0); 01774 switch (result[0]) 01775 { 01776 case '*': 01777 result.data.resize(result.data.size() - 2); // tie off CRLF 01778 parseUntagged (result); 01779 break; 01780 case '+': 01781 continuation.duplicate(result.data); 01782 break; 01783 default: 01784 { 01785 TQCString tag = parseLiteralC(result); 01786 if (current->id() == tag.data()) 01787 { 01788 result.data.resize(result.data.size() - 2); // tie off CRLF 01789 TQByteArray resultCode = parseLiteral (result); //the result 01790 current->setResult (resultCode); 01791 current->setResultInfo(result.cstr()); 01792 current->setComplete (); 01793 01794 sentQueue.removeRef (current); 01795 completeQueue.append (current); 01796 if (result.length()) 01797 parseResult (resultCode, result, current->command()); 01798 } 01799 else 01800 { 01801 kdDebug(7116) << "imapParser::parseLoop - unknown tag '" << tag << "'" << endl; 01802 TQCString cstr = tag + " " + result.cstr(); 01803 result.data = cstr; 01804 result.pos = 0; 01805 result.data.resize(cstr.length()); 01806 } 01807 } 01808 break; 01809 } 01810 } 01811 01812 return 1; 01813 } 01814 01815 void 01816 imapParser::parseRelay (const TQByteArray & buffer) 01817 { 01818 Q_UNUSED(buffer); 01819 qWarning 01820 ("imapParser::parseRelay - virtual function not reimplemented - data lost"); 01821 } 01822 01823 void 01824 imapParser::parseRelay (ulong len) 01825 { 01826 Q_UNUSED(len); 01827 qWarning 01828 ("imapParser::parseRelay - virtual function not reimplemented - announcement lost"); 01829 } 01830 01831 bool imapParser::parseRead (TQByteArray & buffer, ulong len, ulong relay) 01832 { 01833 Q_UNUSED(buffer); 01834 Q_UNUSED(len); 01835 Q_UNUSED(relay); 01836 qWarning 01837 ("imapParser::parseRead - virtual function not reimplemented - no data read"); 01838 return FALSE; 01839 } 01840 01841 bool imapParser::parseReadLine (TQByteArray & buffer, ulong relay) 01842 { 01843 Q_UNUSED(buffer); 01844 Q_UNUSED(relay); 01845 qWarning 01846 ("imapParser::parseReadLine - virtual function not reimplemented - no data read"); 01847 return FALSE; 01848 } 01849 01850 void 01851 imapParser::parseWriteLine (const TQString & str) 01852 { 01853 Q_UNUSED(str); 01854 qWarning 01855 ("imapParser::parseWriteLine - virtual function not reimplemented - no data written"); 01856 } 01857 01858 void 01859 imapParser::parseURL (const KURL & _url, TQString & _box, TQString & _section, 01860 TQString & _type, TQString & _uid, TQString & _validity, TQString & _info) 01861 { 01862 TQStringList parameters; 01863 01864 _box = _url.path (); 01865 kdDebug(7116) << "imapParser::parseURL " << _box << endl; 01866 int paramStart = _box.find("/;"); 01867 if ( paramStart > -1 ) 01868 { 01869 TQString paramString = _box.right( _box.length() - paramStart-2 ); 01870 parameters = TQStringList::split (';', paramString); //split parameters 01871 _box.truncate( paramStart ); // strip parameters 01872 } 01873 // extract parameters 01874 for (TQStringList::ConstIterator it (parameters.begin ()); 01875 it != parameters.end (); ++it) 01876 { 01877 TQString temp = (*it); 01878 01879 int pt = temp.find ('/'); 01880 if (pt > 0) 01881 { 01882 if (temp.findRev ('"', pt) == -1 || temp.find('"', pt) == -1) 01883 { 01884 // if we have non-quoted '/' separator we'll just nuke it 01885 temp.truncate(pt); 01886 } 01887 } 01888 if (temp.find ("section=", 0, false) == 0) 01889 _section = temp.right (temp.length () - 8); 01890 else if (temp.find ("type=", 0, false) == 0) 01891 _type = temp.right (temp.length () - 5); 01892 else if (temp.find ("uid=", 0, false) == 0) 01893 _uid = temp.right (temp.length () - 4); 01894 else if (temp.find ("uidvalidity=", 0, false) == 0) 01895 _validity = temp.right (temp.length () - 12); 01896 else if (temp.find ("info=", 0, false) == 0) 01897 _info = temp.right (temp.length () - 5); 01898 } 01899 // kdDebug(7116) << "URL: section= " << _section << ", type= " << _type << ", uid= " << _uid << endl; 01900 // kdDebug(7116) << "URL: user() " << _url.user() << endl; 01901 // kdDebug(7116) << "URL: path() " << _url.path() << endl; 01902 // kdDebug(7116) << "URL: encodedPathAndQuery() " << _url.encodedPathAndQuery() << endl; 01903 01904 if (!_box.isEmpty ()) 01905 { 01906 // strip / 01907 if (_box[0] == '/') 01908 _box = _box.right (_box.length () - 1); 01909 if (!_box.isEmpty () && _box[_box.length () - 1] == '/') 01910 _box.truncate(_box.length() - 1); 01911 } 01912 kdDebug(7116) << "URL: box= " << _box << ", section= " << _section << ", type= " 01913 << _type << ", uid= " << _uid << ", validity= " << _validity << ", info= " << _info << endl; 01914 } 01915 01916 01917 TQCString imapParser::parseLiteralC(parseString & inWords, bool relay, bool stopAtBracket, int *outlen) { 01918 01919 if (!inWords.isEmpty() && inWords[0] == '{') 01920 { 01921 TQCString retVal; 01922 long srunLen = inWords.find ('}', 1); // Can return -1, so use a signed long 01923 if (srunLen > 0) 01924 { 01925 ulong runLen = (ulong)srunLen; 01926 bool proper; 01927 ulong runLenSave = runLen + 1; 01928 TQCString tmpstr(runLen); 01929 inWords.takeMidNoResize(tmpstr, 1, runLen - 1); 01930 runLen = tmpstr.toULong (&proper); 01931 inWords.pos += runLenSave; 01932 if (proper) 01933 { 01934 //now get the literal from the server 01935 if (relay) 01936 parseRelay (runLen); 01937 TQByteArray rv; 01938 parseRead (rv, runLen, relay ? runLen : 0); 01939 rv.resize(TQMAX(runLen, rv.size())); // what's the point? 01940 retVal = b2c(rv); 01941 inWords.clear(); 01942 parseReadLine (inWords.data); // must get more 01943 01944 // no duplicate data transfers 01945 relay = false; 01946 } 01947 else 01948 { 01949 kdDebug(7116) << "imapParser::parseLiteral - error parsing {} - " /*<< strLen*/ << endl; 01950 } 01951 } 01952 else 01953 { 01954 inWords.clear(); 01955 kdDebug(7116) << "imapParser::parseLiteral - error parsing unmatched {" << endl; 01956 } 01957 if (outlen) { 01958 *outlen = retVal.length(); // optimize me 01959 } 01960 skipWS (inWords); 01961 return retVal; 01962 } 01963 01964 return parseOneWordC(inWords, stopAtBracket, outlen); 01965 } 01966 01967 // does not know about literals ( {7} literal ) 01968 TQCString imapParser::parseOneWordC (parseString & inWords, bool stopAtBracket, int *outLen) 01969 { 01970 uint retValSize = 0; 01971 uint len = inWords.length(); 01972 if (len == 0) { 01973 return TQCString(); 01974 } 01975 01976 if (len > 0 && inWords[0] == '"') 01977 { 01978 unsigned int i = 1; 01979 bool quote = FALSE; 01980 while (i < len && (inWords[i] != '"' || quote)) 01981 { 01982 if (inWords[i] == '\\') quote = !quote; 01983 else quote = FALSE; 01984 i++; 01985 } 01986 if (i < len) 01987 { 01988 TQCString retVal(i); 01989 inWords.pos++; 01990 inWords.takeLeftNoResize(retVal, i - 1); 01991 len = i - 1; 01992 int offset = 0; 01993 for (unsigned int j = 0; j <= len; j++) { 01994 if (retVal[j] == '\\') { 01995 offset++; 01996 j++; 01997 } 01998 retVal[j - offset] = retVal[j]; 01999 } 02000 retVal[len - offset] = 0; 02001 retValSize = len - offset; 02002 inWords.pos += i; 02003 skipWS (inWords); 02004 if (outLen) { 02005 *outLen = retValSize; 02006 } 02007 return retVal; 02008 } 02009 else 02010 { 02011 kdDebug(7116) << "imapParser::parseOneWord - error parsing unmatched \"" << endl; 02012 TQCString retVal = inWords.cstr(); 02013 retValSize = len; 02014 inWords.clear(); 02015 if (outLen) { 02016 *outLen = retValSize; 02017 } 02018 return retVal; 02019 } 02020 } 02021 else 02022 { 02023 // not quoted 02024 unsigned int i; 02025 // search for end 02026 for (i = 0; i < len; ++i) { 02027 char ch = inWords[i]; 02028 if (ch <= ' ' || ch == '(' || ch == ')' || 02029 (stopAtBracket && (ch == '[' || ch == ']'))) 02030 break; 02031 } 02032 02033 TQCString retVal(i+1); 02034 inWords.takeLeftNoResize(retVal, i); 02035 retValSize = i; 02036 inWords.pos += i; 02037 02038 if (retVal == "NIL") { 02039 retVal.truncate(0); 02040 retValSize = 0; 02041 } 02042 skipWS (inWords); 02043 if (outLen) { 02044 *outLen = retValSize; 02045 } 02046 return retVal; 02047 } 02048 } 02049 02050 bool imapParser::parseOneNumber (parseString & inWords, ulong & num) 02051 { 02052 bool valid; 02053 num = parseOneWordC(inWords, TRUE).toULong(&valid); 02054 return valid; 02055 } 02056 02057 bool imapParser::hasCapability (const TQString & cap) 02058 { 02059 TQString c = cap.lower(); 02060 // kdDebug(7116) << "imapParser::hasCapability - Looking for '" << cap << "'" << endl; 02061 for (TQStringList::ConstIterator it = imapCapabilities.begin (); 02062 it != imapCapabilities.end (); ++it) 02063 { 02064 // kdDebug(7116) << "imapParser::hasCapability - Examining '" << (*it) << "'" << endl; 02065 if ( !(kasciistricmp(c.ascii(), (*it).ascii())) ) 02066 { 02067 return true; 02068 } 02069 } 02070 return false; 02071 } 02072 02073 void imapParser::removeCapability (const TQString & cap) 02074 { 02075 imapCapabilities.remove(cap.lower()); 02076 } 02077 02078 TQString imapParser::namespaceForBox( const TQString & box ) 02079 { 02080 kdDebug(7116) << "imapParse::namespaceForBox " << box << endl; 02081 TQString myNamespace; 02082 if ( !box.isEmpty() ) 02083 { 02084 TQValueList<TQString> list = namespaceToDelimiter.keys(); 02085 TQString cleanPrefix; 02086 for ( TQValueList<TQString>::Iterator it = list.begin(); it != list.end(); ++it ) 02087 { 02088 if ( !(*it).isEmpty() && box.find( *it ) != -1 ) 02089 return (*it); 02090 } 02091 } 02092 return myNamespace; 02093 } 02094