imap4.cc
00001 /********************************************************************** 00002 * 00003 * imap4.cc - IMAP4rev1 KIOSlave 00004 * Copyright (C) 2001-2002 Michael Haeckel <haeckel@kde.org> 00005 * Copyright (C) 1999 John Corey <jcorey@fruity.ath.cx> 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 jcorey@fruity.ath.cx 00022 * 00023 *********************************************************************/ 00024 00059 #ifdef HAVE_CONFIG_H 00060 #include <config.h> 00061 #endif 00062 00063 #include "imap4.h" 00064 00065 #include "rfcdecoder.h" 00066 00067 #include <sys/stat.h> 00068 00069 #include <stdio.h> 00070 #include <stdlib.h> 00071 #include <signal.h> 00072 #include <sys/types.h> 00073 #include <sys/wait.h> 00074 #include <errno.h> 00075 00076 #ifdef HAVE_LIBSASL2 00077 extern "C" { 00078 #include <sasl/sasl.h> 00079 } 00080 #endif 00081 00082 #include <tqbuffer.h> 00083 #include <tqdatetime.h> 00084 #include <tqregexp.h> 00085 #include <tdeprotocolmanager.h> 00086 #include <tdemessagebox.h> 00087 #include <kdebug.h> 00088 #include <tdeio/connection.h> 00089 #include <tdeio/slaveinterface.h> 00090 #include <tdeio/passdlg.h> 00091 #include <tdelocale.h> 00092 #include <kmimetype.h> 00093 #include <kmdcodec.h> 00094 00095 #include "tdepimmacros.h" 00096 00097 #define IMAP_PROTOCOL "imap" 00098 #define IMAP_SSL_PROTOCOL "imaps" 00099 const int ImapPort = 143; 00100 const int ImapsPort = 993; 00101 00102 using namespace TDEIO; 00103 00104 extern "C" 00105 { 00106 void sigalrm_handler (int); 00107 KDE_EXPORT int kdemain (int argc, char **argv); 00108 } 00109 00110 int 00111 kdemain (int argc, char **argv) 00112 { 00113 kdDebug(7116) << "IMAP4::kdemain" << endl; 00114 00115 TDEInstance instance ("tdeio_imap4"); 00116 if (argc != 4) 00117 { 00118 fprintf(stderr, "Usage: tdeio_imap4 protocol domain-socket1 domain-socket2\n"); 00119 ::exit (-1); 00120 } 00121 00122 #ifdef HAVE_LIBSASL2 00123 if ( sasl_client_init( NULL ) != SASL_OK ) { 00124 fprintf(stderr, "SASL library initialization failed!\n"); 00125 ::exit (-1); 00126 } 00127 #endif 00128 00129 //set debug handler 00130 00131 IMAP4Protocol *slave; 00132 if (strcasecmp (argv[1], IMAP_SSL_PROTOCOL) == 0) 00133 slave = new IMAP4Protocol (argv[2], argv[3], true); 00134 else if (strcasecmp (argv[1], IMAP_PROTOCOL) == 0) 00135 slave = new IMAP4Protocol (argv[2], argv[3], false); 00136 else 00137 abort (); 00138 slave->dispatchLoop (); 00139 delete slave; 00140 00141 #ifdef HAVE_LIBSASL2 00142 sasl_done(); 00143 #endif 00144 00145 return 0; 00146 } 00147 00148 void 00149 sigchld_handler (int signo) 00150 { 00151 // A signal handler that calls for example waitpid has to save errno 00152 // before and restore it afterwards. 00153 // (cf. https://www.securecoding.cert.org/confluence/display/cplusplus/ERR32-CPP.+Do+not+rely+on+indeterminate+values+of+errno) 00154 const int save_errno = errno; 00155 int pid, status; 00156 00157 while (signo == SIGCHLD) 00158 { 00159 pid = waitpid (-1, &status, WNOHANG); 00160 if (pid <= 0) 00161 { 00162 // Reinstall signal handler, since Linux resets to default after 00163 // the signal occurred ( BSD handles it different, but it should do 00164 // no harm ). 00165 signal (SIGCHLD, sigchld_handler); 00166 break; 00167 } 00168 } 00169 00170 errno = save_errno; 00171 } 00172 00173 IMAP4Protocol::IMAP4Protocol (const TQCString & pool, const TQCString & app, bool isSSL):TCPSlaveBase ((isSSL ? 993 : 143), 00174 (isSSL ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL), pool, 00175 app, isSSL), imapParser (), mimeIO (), outputBuffer(outputCache) 00176 { 00177 outputBufferIndex = 0; 00178 mySSL = isSSL; 00179 readBuffer[0] = 0x00; 00180 relayEnabled = false; 00181 readBufferLen = 0; 00182 cacheOutput = false; 00183 decodeContent = false; 00184 mTimeOfLastNoop = TQDateTime(); 00185 } 00186 00187 IMAP4Protocol::~IMAP4Protocol () 00188 { 00189 closeDescriptor(); 00190 kdDebug(7116) << "IMAP4: Finishing" << endl; 00191 } 00192 00193 void 00194 IMAP4Protocol::get (const KURL & _url) 00195 { 00196 if (!makeLogin()) return; 00197 kdDebug(7116) << "IMAP4::get - " << _url.prettyURL() << endl; 00198 TQString aBox, aSequence, aType, aSection, aValidity, aDelimiter, aInfo; 00199 enum IMAP_TYPE aEnum = 00200 parseURL (_url, aBox, aSection, aType, aSequence, aValidity, aDelimiter, aInfo); 00201 if (aEnum != ITYPE_ATTACH) 00202 mimeType (getMimeType(aEnum)); 00203 if (aInfo == "DECODE") 00204 decodeContent = true; 00205 00206 if (aSequence == "0:0" && getState() == ISTATE_SELECT) 00207 { 00208 imapCommand *cmd = doCommand (imapCommand::clientNoop()); 00209 completeQueue.removeRef(cmd); 00210 } 00211 00212 if (aSequence.isEmpty ()) 00213 { 00214 aSequence = "1:*"; 00215 } 00216 00217 mProcessedSize = 0; 00218 imapCommand *cmd = NULL; 00219 if (!assureBox (aBox, true)) return; 00220 00221 #ifdef USE_VALIDITY 00222 if (selectInfo.uidValidityAvailable () && !aValidity.isEmpty () 00223 && selectInfo.uidValidity () != aValidity.toULong ()) 00224 { 00225 // this url is stale 00226 error (ERR_COULD_NOT_READ, _url.prettyURL()); 00227 return; 00228 } 00229 else 00230 #endif 00231 { 00232 // The "section" specified by the application can be: 00233 // * empty (which means body, size and flags) 00234 // * a known keyword, like STRUCTURE, ENVELOPE, HEADER, BODY.PEEK[...] 00235 // (in which case the slave has some logic to add the necessary items) 00236 // * Otherwise, it specifies the exact data items to request. In this case, all 00237 // the logic is in the app. 00238 00239 TQString aUpper = aSection.upper(); 00240 if (aUpper.find ("STRUCTURE") != -1) 00241 { 00242 aSection = "BODYSTRUCTURE"; 00243 } 00244 else if (aUpper.find ("ENVELOPE") != -1) 00245 { 00246 aSection = "UID RFC822.SIZE FLAGS ENVELOPE"; 00247 if (hasCapability("IMAP4rev1")) { 00248 aSection += " BODY.PEEK[HEADER.FIELDS (REFERENCES)]"; 00249 } else { 00250 // imap4 does not know HEADER.FIELDS 00251 aSection += " RFC822.HEADER.LINES (REFERENCES)"; 00252 } 00253 } 00254 else if (aUpper == "HEADER") 00255 { 00256 aSection = "UID RFC822.HEADER RFC822.SIZE FLAGS"; 00257 } 00258 else if (aUpper.find ("BODY.PEEK[") != -1) 00259 { 00260 if (aUpper.find ("BODY.PEEK[]") != -1) 00261 { 00262 if (!hasCapability("IMAP4rev1")) // imap4 does not know BODY.PEEK[] 00263 aSection.replace("BODY.PEEK[]", "RFC822.PEEK"); 00264 } 00265 aSection.prepend("UID RFC822.SIZE FLAGS "); 00266 } 00267 else if (aSection.isEmpty()) 00268 { 00269 aSection = "UID BODY[] RFC822.SIZE FLAGS"; 00270 } 00271 if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX) 00272 { 00273 // write the digest header 00274 cacheOutput = true; 00275 outputLine 00276 ("Content-Type: multipart/digest; boundary=\"IMAPDIGEST\"\r\n", 55); 00277 if (selectInfo.recentAvailable ()) 00278 outputLineStr ("X-Recent: " + 00279 TQString::number(selectInfo.recent ()) + "\r\n"); 00280 if (selectInfo.countAvailable ()) 00281 outputLineStr ("X-Count: " + TQString::number(selectInfo.count ()) + 00282 "\r\n"); 00283 if (selectInfo.unseenAvailable ()) 00284 outputLineStr ("X-Unseen: " + 00285 TQString::number(selectInfo.unseen ()) + "\r\n"); 00286 if (selectInfo.uidValidityAvailable ()) 00287 outputLineStr ("X-uidValidity: " + 00288 TQString::number(selectInfo.uidValidity ()) + 00289 "\r\n"); 00290 if (selectInfo.uidNextAvailable ()) 00291 outputLineStr ("X-UidNext: " + 00292 TQString::number(selectInfo.uidNext ()) + "\r\n"); 00293 if (selectInfo.flagsAvailable ()) 00294 outputLineStr ("X-Flags: " + TQString::number(selectInfo.flags ()) + 00295 "\r\n"); 00296 if (selectInfo.permanentFlagsAvailable ()) 00297 outputLineStr ("X-PermanentFlags: " + 00298 TQString::number(selectInfo.permanentFlags ()) + "\r\n"); 00299 if (selectInfo.readWriteAvailable ()) { 00300 if (selectInfo.readWrite()) { 00301 outputLine ("X-Access: Read/Write\r\n", 22); 00302 } else { 00303 outputLine ("X-Access: Read only\r\n", 21); 00304 } 00305 } 00306 outputLine ("\r\n", 2); 00307 flushOutput(TQString()); 00308 cacheOutput = false; 00309 } 00310 00311 if (aEnum == ITYPE_MSG || (aEnum == ITYPE_ATTACH && !decodeContent)) 00312 relayEnabled = true; // normal mode, relay data 00313 00314 if (aSequence != "0:0") 00315 { 00316 TQString contentEncoding; 00317 if (aEnum == ITYPE_ATTACH && decodeContent) 00318 { 00319 // get the MIME header and fill getLastHandled() 00320 TQString mySection = aSection; 00321 mySection.replace("]", ".MIME]"); 00322 cmd = sendCommand (imapCommand::clientFetch (aSequence, mySection)); 00323 do 00324 { 00325 while (!parseLoop ()) ; 00326 } 00327 while (!cmd->isComplete ()); 00328 completeQueue.removeRef (cmd); 00329 // get the content encoding now because getLastHandled will be cleared 00330 if (getLastHandled() && getLastHandled()->getHeader()) 00331 contentEncoding = getLastHandled()->getHeader()->getEncoding(); 00332 00333 // from here on collect the data 00334 // it is send to the client in flushOutput in one go 00335 // needed to decode the content 00336 cacheOutput = true; 00337 } 00338 00339 cmd = sendCommand (imapCommand::clientFetch (aSequence, aSection)); 00340 int res; 00341 aUpper = aSection.upper(); 00342 do 00343 { 00344 while (!(res = parseLoop())) ; 00345 if (res == -1) break; 00346 00347 mailHeader *lastone = 0; 00348 imapCache *cache = getLastHandled (); 00349 if (cache) 00350 lastone = cache->getHeader (); 00351 00352 if (cmd && !cmd->isComplete ()) 00353 { 00354 if ((aUpper.find ("BODYSTRUCTURE") != -1) 00355 || (aUpper.find ("FLAGS") != -1) 00356 || (aUpper.find ("UID") != -1) 00357 || (aUpper.find ("ENVELOPE") != -1) 00358 || (aUpper.find ("BODY.PEEK[0]") != -1 00359 && (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX))) 00360 { 00361 if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX) 00362 { 00363 // write the mime header (default is here message/rfc822) 00364 outputLine ("--IMAPDIGEST\r\n", 14); 00365 cacheOutput = true; 00366 if (cache && cache->getUid () != 0) 00367 outputLineStr ("X-UID: " + 00368 TQString::number(cache->getUid ()) + "\r\n"); 00369 if (cache && cache->getSize () != 0) 00370 outputLineStr ("X-Length: " + 00371 TQString::number(cache->getSize ()) + "\r\n"); 00372 if (cache && !cache->getDate ().isEmpty()) 00373 outputLineStr ("X-Date: " + cache->getDate () + "\r\n"); 00374 if (cache && cache->getFlags () != 0) 00375 outputLineStr ("X-Flags: " + 00376 TQString::number(cache->getFlags ()) + "\r\n"); 00377 } else cacheOutput = true; 00378 if ( lastone && !decodeContent ) 00379 lastone->outputPart (*this); 00380 cacheOutput = false; 00381 flushOutput(contentEncoding); 00382 } 00383 } // if not complete 00384 } 00385 while (cmd && !cmd->isComplete ()); 00386 if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX) 00387 { 00388 // write the end boundary 00389 outputLine ("--IMAPDIGEST--\r\n", 16); 00390 } 00391 00392 completeQueue.removeRef (cmd); 00393 } 00394 } 00395 00396 // just to keep everybody happy when no data arrived 00397 data (TQByteArray ()); 00398 00399 finished (); 00400 relayEnabled = false; 00401 cacheOutput = false; 00402 kdDebug(7116) << "IMAP4::get - finished" << endl; 00403 } 00404 00405 void 00406 IMAP4Protocol::listDir (const KURL & _url) 00407 { 00408 kdDebug(7116) << " IMAP4::listDir - " << _url.prettyURL() << endl; 00409 00410 if (_url.path().isEmpty()) 00411 { 00412 KURL url = _url; 00413 url.setPath("/"); 00414 redirection( url ); 00415 finished(); 00416 return; 00417 } 00418 00419 TQString myBox, mySequence, myLType, mySection, myValidity, myDelimiter, myInfo; 00420 // parseURL with caching 00421 enum IMAP_TYPE myType = 00422 parseURL (_url, myBox, mySection, myLType, mySequence, myValidity, 00423 myDelimiter, myInfo, true); 00424 00425 if (!makeLogin()) return; 00426 00427 if (myType == ITYPE_DIR || myType == ITYPE_DIR_AND_BOX) 00428 { 00429 TQString listStr = myBox; 00430 imapCommand *cmd; 00431 00432 if (!listStr.isEmpty () && !listStr.endsWith(myDelimiter) && 00433 mySection != "FOLDERONLY") 00434 listStr += myDelimiter; 00435 00436 if (mySection.isEmpty()) 00437 { 00438 listStr += "%"; 00439 } else if (mySection == "COMPLETE") { 00440 listStr += "*"; 00441 } 00442 kdDebug(7116) << "IMAP4Protocol::listDir - listStr=" << listStr << endl; 00443 cmd = 00444 doCommand (imapCommand::clientList ("", listStr, 00445 (myLType == "LSUB" || myLType == "LSUBNOCHECK"))); 00446 if (cmd->result () == "OK") 00447 { 00448 TQString mailboxName; 00449 UDSEntry entry; 00450 UDSAtom atom; 00451 KURL aURL = _url; 00452 if (aURL.path().find(';') != -1) 00453 aURL.setPath(aURL.path().left(aURL.path().find(';'))); 00454 00455 kdDebug(7116) << "IMAP4Protocol::listDir - got " << listResponses.count () << endl; 00456 00457 if (myLType == "LSUB") 00458 { 00459 // fire the same command as LIST to check if the box really exists 00460 TQValueList<imapList> listResponsesSave = listResponses; 00461 doCommand (imapCommand::clientList ("", listStr, false)); 00462 for (TQValueListIterator < imapList > it = listResponsesSave.begin (); 00463 it != listResponsesSave.end (); ++it) 00464 { 00465 bool boxOk = false; 00466 for (TQValueListIterator < imapList > it2 = listResponses.begin (); 00467 it2 != listResponses.end (); ++it2) 00468 { 00469 if ((*it2).name() == (*it).name()) 00470 { 00471 boxOk = true; 00472 // copy the flags from the LIST-command 00473 (*it) = (*it2); 00474 break; 00475 } 00476 } 00477 if (boxOk) 00478 doListEntry (aURL, myBox, (*it), (mySection != "FOLDERONLY")); 00479 else // this folder is dead 00480 kdDebug(7116) << "IMAP4Protocol::listDir - suppress " << (*it).name() << endl; 00481 } 00482 listResponses = listResponsesSave; 00483 } 00484 else // LIST or LSUBNOCHECK 00485 { 00486 for (TQValueListIterator < imapList > it = listResponses.begin (); 00487 it != listResponses.end (); ++it) 00488 { 00489 doListEntry (aURL, myBox, (*it), (mySection != "FOLDERONLY")); 00490 } 00491 } 00492 entry.clear (); 00493 listEntry (entry, true); 00494 } 00495 else 00496 { 00497 error (ERR_CANNOT_ENTER_DIRECTORY, _url.prettyURL()); 00498 completeQueue.removeRef (cmd); 00499 return; 00500 } 00501 completeQueue.removeRef (cmd); 00502 } 00503 if ((myType == ITYPE_BOX || myType == ITYPE_DIR_AND_BOX) 00504 && myLType != "LIST" && myLType != "LSUB" && myLType != "LSUBNOCHECK") 00505 { 00506 KURL aURL = _url; 00507 aURL.setQuery (TQString()); 00508 const TQString encodedUrl = aURL.url(0, 106); // utf-8 00509 00510 if (!_url.query ().isEmpty ()) 00511 { 00512 TQString query = KURL::decode_string (_url.query ()); 00513 query = query.right (query.length () - 1); 00514 if (!query.isEmpty()) 00515 { 00516 imapCommand *cmd = NULL; 00517 00518 if (!assureBox (myBox, true)) return; 00519 00520 if (!selectInfo.countAvailable() || selectInfo.count()) 00521 { 00522 cmd = doCommand (imapCommand::clientSearch (query)); 00523 if (cmd->result() != "OK") 00524 { 00525 error(ERR_UNSUPPORTED_ACTION, _url.prettyURL()); 00526 completeQueue.removeRef (cmd); 00527 return; 00528 } 00529 completeQueue.removeRef (cmd); 00530 00531 TQStringList list = getResults (); 00532 int stretch = 0; 00533 00534 if (selectInfo.uidNextAvailable ()) 00535 stretch = TQString::number(selectInfo.uidNext ()).length (); 00536 UDSEntry entry; 00537 imapCache fake; 00538 00539 for (TQStringList::ConstIterator it = list.begin(); it != list.end(); 00540 ++it) 00541 { 00542 fake.setUid((*it).toULong()); 00543 doListEntry (encodedUrl, stretch, &fake); 00544 } 00545 entry.clear (); 00546 listEntry (entry, true); 00547 } 00548 } 00549 } 00550 else 00551 { 00552 if (!assureBox (myBox, true)) return; 00553 00554 kdDebug(7116) << "IMAP4: select returned:" << endl; 00555 if (selectInfo.recentAvailable ()) 00556 kdDebug(7116) << "Recent: " << selectInfo.recent () << "d" << endl; 00557 if (selectInfo.countAvailable ()) 00558 kdDebug(7116) << "Count: " << selectInfo.count () << "d" << endl; 00559 if (selectInfo.unseenAvailable ()) 00560 kdDebug(7116) << "Unseen: " << selectInfo.unseen () << "d" << endl; 00561 if (selectInfo.uidValidityAvailable ()) 00562 kdDebug(7116) << "uidValidity: " << selectInfo.uidValidity () << "d" << endl; 00563 if (selectInfo.flagsAvailable ()) 00564 kdDebug(7116) << "Flags: " << selectInfo.flags () << "d" << endl; 00565 if (selectInfo.permanentFlagsAvailable ()) 00566 kdDebug(7116) << "PermanentFlags: " << selectInfo.permanentFlags () << "d" << endl; 00567 if (selectInfo.readWriteAvailable ()) 00568 kdDebug(7116) << "Access: " << (selectInfo.readWrite ()? "Read/Write" : "Read only") << endl; 00569 00570 #ifdef USE_VALIDITY 00571 if (selectInfo.uidValidityAvailable () 00572 && selectInfo.uidValidity () != myValidity.toULong ()) 00573 { 00574 //redirect 00575 KURL newUrl = _url; 00576 00577 newUrl.setPath ("/" + myBox + ";UIDVALIDITY=" + 00578 TQString::number(selectInfo.uidValidity ())); 00579 kdDebug(7116) << "IMAP4::listDir - redirecting to " << newUrl.prettyURL() << endl; 00580 redirection (newUrl); 00581 00582 00583 } 00584 else 00585 #endif 00586 if (selectInfo.count () > 0) 00587 { 00588 int stretch = 0; 00589 00590 if (selectInfo.uidNextAvailable ()) 00591 stretch = TQString::number(selectInfo.uidNext ()).length (); 00592 // kdDebug(7116) << selectInfo.uidNext() << "d used to stretch " << stretch << endl; 00593 UDSEntry entry; 00594 00595 if (mySequence.isEmpty()) mySequence = "1:*"; 00596 00597 bool withSubject = mySection.isEmpty(); 00598 if (mySection.isEmpty()) mySection = "UID RFC822.SIZE ENVELOPE"; 00599 00600 bool withFlags = mySection.upper().find("FLAGS") != -1; 00601 imapCommand *fetch = 00602 sendCommand (imapCommand:: 00603 clientFetch (mySequence, mySection)); 00604 imapCache *cache; 00605 do 00606 { 00607 while (!parseLoop ()) ; 00608 00609 cache = getLastHandled (); 00610 00611 if (cache && !fetch->isComplete()) 00612 doListEntry (encodedUrl, stretch, cache, withFlags, withSubject); 00613 } 00614 while (!fetch->isComplete ()); 00615 entry.clear (); 00616 listEntry (entry, true); 00617 } 00618 } 00619 } 00620 if ( !selectInfo.alert().isNull() ) { 00621 if ( !myBox.isEmpty() ) { 00622 warning( i18n( "Message from %1 while processing '%2': %3" ).arg( myHost, myBox, selectInfo.alert() ) ); 00623 } else { 00624 warning( i18n( "Message from %1: %2" ).arg( myHost, TQString(selectInfo.alert()) ) ); 00625 } 00626 selectInfo.setAlert( 0 ); 00627 } 00628 00629 kdDebug(7116) << "IMAP4Protocol::listDir - Finishing listDir" << endl; 00630 finished (); 00631 } 00632 00633 void 00634 IMAP4Protocol::setHost (const TQString & _host, int _port, 00635 const TQString & _user, const TQString & _pass) 00636 { 00637 if (myHost != _host || myPort != _port || myUser != _user || myPass != _pass) 00638 { // what's the point of doing 4 string compares to avoid 4 string copies? 00639 // DF: I guess to avoid calling closeConnection() unnecessarily. 00640 if (!myHost.isEmpty ()) 00641 closeConnection (); 00642 myHost = _host; 00643 if (_port == 0) 00644 myPort = (mySSL) ? ImapsPort : ImapPort; 00645 else 00646 myPort = _port; 00647 myUser = _user; 00648 myPass = _pass; 00649 } 00650 } 00651 00652 void 00653 IMAP4Protocol::parseRelay (const TQByteArray & buffer) 00654 { 00655 if (relayEnabled) { 00656 // relay data immediately 00657 data( buffer ); 00658 mProcessedSize += buffer.size(); 00659 processedSize( mProcessedSize ); 00660 } else if (cacheOutput) 00661 { 00662 // collect data 00663 if ( !outputBuffer.isOpen() ) { 00664 outputBuffer.open(IO_WriteOnly); 00665 } 00666 outputBuffer.at(outputBufferIndex); 00667 outputBuffer.writeBlock(buffer, buffer.size()); 00668 outputBufferIndex += buffer.size(); 00669 } 00670 } 00671 00672 void 00673 IMAP4Protocol::parseRelay (ulong len) 00674 { 00675 if (relayEnabled) 00676 totalSize (len); 00677 } 00678 00679 00680 bool IMAP4Protocol::parseRead(TQByteArray & buffer, ulong len, ulong relay) 00681 { 00682 char buf[8192]; 00683 while (buffer.size() < len) 00684 { 00685 ssize_t readLen = myRead(buf, TQMIN(len - buffer.size(), sizeof(buf) - 1)); 00686 if (readLen == 0) 00687 { 00688 kdDebug(7116) << "parseRead: readLen == 0 - connection broken" << endl; 00689 error (ERR_CONNECTION_BROKEN, myHost); 00690 setState(ISTATE_CONNECT); 00691 closeConnection(); 00692 return FALSE; 00693 } 00694 if (relay > buffer.size()) 00695 { 00696 TQByteArray relayData; 00697 ssize_t relbuf = relay - buffer.size(); 00698 int currentRelay = TQMIN(relbuf, readLen); 00699 relayData.setRawData(buf, currentRelay); 00700 parseRelay(relayData); 00701 relayData.resetRawData(buf, currentRelay); 00702 } 00703 { 00704 TQBuffer stream (buffer); 00705 stream.open (IO_WriteOnly); 00706 stream.at (buffer.size ()); 00707 stream.writeBlock (buf, readLen); 00708 stream.close (); 00709 } 00710 } 00711 return (buffer.size() == len); 00712 } 00713 00714 00715 bool IMAP4Protocol::parseReadLine (TQByteArray & buffer, ulong relay) 00716 { 00717 if (myHost.isEmpty()) return FALSE; 00718 00719 while (true) { 00720 ssize_t copyLen = 0; 00721 if (readBufferLen > 0) 00722 { 00723 while (copyLen < readBufferLen && readBuffer[copyLen] != '\n') copyLen++; 00724 if (copyLen < readBufferLen) copyLen++; 00725 if (relay > 0) 00726 { 00727 TQByteArray relayData; 00728 00729 if (copyLen < (ssize_t) relay) 00730 relay = copyLen; 00731 relayData.setRawData (readBuffer, relay); 00732 parseRelay (relayData); 00733 relayData.resetRawData (readBuffer, relay); 00734 // kdDebug(7116) << "relayed : " << relay << "d" << endl; 00735 } 00736 // append to buffer 00737 { 00738 TQBuffer stream (buffer); 00739 00740 stream.open (IO_WriteOnly); 00741 stream.at (buffer.size ()); 00742 stream.writeBlock (readBuffer, copyLen); 00743 stream.close (); 00744 // kdDebug(7116) << "appended " << copyLen << "d got now " << buffer.size() << endl; 00745 } 00746 00747 readBufferLen -= copyLen; 00748 if (readBufferLen) 00749 memmove(readBuffer, &readBuffer[copyLen], readBufferLen); 00750 if (buffer[buffer.size() - 1] == '\n') return TRUE; 00751 } 00752 if (!isConnectionValid()) 00753 { 00754 kdDebug(7116) << "parseReadLine - connection broken" << endl; 00755 error (ERR_CONNECTION_BROKEN, myHost); 00756 setState(ISTATE_CONNECT); 00757 closeConnection(); 00758 return FALSE; 00759 } 00760 if (!waitForResponse( responseTimeout() )) 00761 { 00762 error(ERR_SERVER_TIMEOUT, myHost); 00763 setState(ISTATE_CONNECT); 00764 closeConnection(); 00765 return FALSE; 00766 } 00767 readBufferLen = read(readBuffer, IMAP_BUFFER - 1); 00768 if (readBufferLen == 0) 00769 { 00770 kdDebug(7116) << "parseReadLine: readBufferLen == 0 - connection broken" << endl; 00771 error (ERR_CONNECTION_BROKEN, myHost); 00772 setState(ISTATE_CONNECT); 00773 closeConnection(); 00774 return FALSE; 00775 } 00776 } 00777 } 00778 00779 void 00780 IMAP4Protocol::setSubURL (const KURL & _url) 00781 { 00782 kdDebug(7116) << "IMAP4::setSubURL - " << _url.prettyURL() << endl; 00783 TDEIO::TCPSlaveBase::setSubURL (_url); 00784 } 00785 00786 void 00787 IMAP4Protocol::put (const KURL & _url, int, bool, bool) 00788 { 00789 kdDebug(7116) << "IMAP4::put - " << _url.prettyURL() << endl; 00790 // TDEIO::TCPSlaveBase::put(_url,permissions,overwrite,resume); 00791 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; 00792 enum IMAP_TYPE aType = 00793 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); 00794 00795 // see if it is a box 00796 if (aType != ITYPE_BOX && aType != ITYPE_DIR_AND_BOX) 00797 { 00798 if (aBox[aBox.length () - 1] == '/') 00799 aBox = aBox.right (aBox.length () - 1); 00800 imapCommand *cmd = doCommand (imapCommand::clientCreate (aBox)); 00801 00802 if (cmd->result () != "OK") { 00803 error (ERR_COULD_NOT_WRITE, _url.prettyURL()); 00804 completeQueue.removeRef (cmd); 00805 return; 00806 } 00807 completeQueue.removeRef (cmd); 00808 } 00809 else 00810 { 00811 TQPtrList < TQByteArray > bufferList; 00812 int length = 0; 00813 00814 int result; 00815 // Loop until we got 'dataEnd' 00816 do 00817 { 00818 TQByteArray *buffer = new TQByteArray (); 00819 dataReq (); // Request for data 00820 result = readData (*buffer); 00821 if (result > 0) 00822 { 00823 bufferList.append (buffer); 00824 length += result; 00825 } else { 00826 delete buffer; 00827 } 00828 } 00829 while (result > 0); 00830 00831 if (result != 0) 00832 { 00833 error (ERR_ABORTED, _url.prettyURL()); 00834 return; 00835 } 00836 00837 imapCommand *cmd = 00838 sendCommand (imapCommand::clientAppend (aBox, aSection, length)); 00839 while (!parseLoop ()) ; 00840 00841 // see if server is waiting 00842 if (!cmd->isComplete () && !getContinuation ().isEmpty ()) 00843 { 00844 bool sendOk = true; 00845 ulong wrote = 0; 00846 00847 TQByteArray *buffer; 00848 // send data to server 00849 while (!bufferList.isEmpty () && sendOk) 00850 { 00851 buffer = bufferList.take (0); 00852 00853 sendOk = 00854 (write (buffer->data (), buffer->size ()) == 00855 (ssize_t) buffer->size ()); 00856 wrote += buffer->size (); 00857 processedSize(wrote); 00858 delete buffer; 00859 if (!sendOk) 00860 { 00861 error (ERR_CONNECTION_BROKEN, myHost); 00862 completeQueue.removeRef (cmd); 00863 setState(ISTATE_CONNECT); 00864 closeConnection(); 00865 return; 00866 } 00867 } 00868 parseWriteLine (""); 00869 // Wait until cmd is complete, or connection breaks. 00870 while (!cmd->isComplete () && getState() != ISTATE_NO) 00871 parseLoop (); 00872 if ( getState() == ISTATE_NO ) { 00873 // TODO KDE4: pass cmd->resultInfo() as third argument. 00874 // ERR_CONNECTION_BROKEN expects a host, no way to pass details about the problem. 00875 error( ERR_CONNECTION_BROKEN, myHost ); 00876 completeQueue.removeRef (cmd); 00877 closeConnection(); 00878 return; 00879 } 00880 else if (cmd->result () != "OK") { 00881 error( ERR_SLAVE_DEFINED, cmd->resultInfo() ); 00882 completeQueue.removeRef (cmd); 00883 return; 00884 } 00885 else 00886 { 00887 if (hasCapability("UIDPLUS")) 00888 { 00889 TQString uid = cmd->resultInfo(); 00890 if (uid.find("APPENDUID") != -1) 00891 { 00892 uid = uid.section(" ", 2, 2); 00893 uid.truncate(uid.length()-1); 00894 infoMessage("UID "+uid); 00895 } 00896 } 00897 // MUST reselect to get the new message 00898 else if (aBox == getCurrentBox ()) 00899 { 00900 cmd = 00901 doCommand (imapCommand:: 00902 clientSelect (aBox, !selectInfo.readWrite ())); 00903 completeQueue.removeRef (cmd); 00904 } 00905 } 00906 } 00907 else 00908 { 00909 //error (ERR_COULD_NOT_WRITE, myHost); 00910 // Better ship the error message, e.g. "Over Quota" 00911 error (ERR_SLAVE_DEFINED, cmd->resultInfo()); 00912 completeQueue.removeRef (cmd); 00913 return; 00914 } 00915 00916 completeQueue.removeRef (cmd); 00917 } 00918 00919 finished (); 00920 } 00921 00922 void 00923 IMAP4Protocol::mkdir (const KURL & _url, int) 00924 { 00925 kdDebug(7116) << "IMAP4::mkdir - " << _url.prettyURL() << endl; 00926 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; 00927 parseURL(_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); 00928 kdDebug(7116) << "IMAP4::mkdir - create " << aBox << endl; 00929 imapCommand *cmd = doCommand (imapCommand::clientCreate(aBox)); 00930 00931 if (cmd->result () != "OK") 00932 { 00933 kdDebug(7116) << "IMAP4::mkdir - " << cmd->resultInfo() << endl; 00934 error (ERR_COULD_NOT_MKDIR, _url.prettyURL()); 00935 completeQueue.removeRef (cmd); 00936 return; 00937 } 00938 completeQueue.removeRef (cmd); 00939 00940 // start a new listing to find the type of the folder 00941 enum IMAP_TYPE type = 00942 parseURL(_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); 00943 if (type == ITYPE_BOX) 00944 { 00945 bool ask = ( aInfo.find( "ASKUSER" ) != -1 ); 00946 if ( ask && 00947 messageBox(QuestionYesNo, 00948 i18n("The following folder will be created on the server: %1 " 00949 "What do you want to store in this folder?").arg( aBox ), 00950 i18n("Create Folder"), 00951 i18n("&Messages"), i18n("&Subfolders")) == KMessageBox::No ) 00952 { 00953 cmd = doCommand(imapCommand::clientDelete(aBox)); 00954 completeQueue.removeRef (cmd); 00955 cmd = doCommand(imapCommand::clientCreate(aBox + aDelimiter)); 00956 if (cmd->result () != "OK") 00957 { 00958 error (ERR_COULD_NOT_MKDIR, _url.prettyURL()); 00959 completeQueue.removeRef (cmd); 00960 return; 00961 } 00962 completeQueue.removeRef (cmd); 00963 } 00964 } 00965 00966 cmd = doCommand(imapCommand::clientSubscribe(aBox)); 00967 completeQueue.removeRef(cmd); 00968 00969 finished (); 00970 } 00971 00972 void 00973 IMAP4Protocol::copy (const KURL & src, const KURL & dest, int, bool overwrite) 00974 { 00975 kdDebug(7116) << "IMAP4::copy - [" << (overwrite ? "Overwrite" : "NoOverwrite") << "] " << src.prettyURL() << " -> " << dest.prettyURL() << endl; 00976 TQString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo; 00977 TQString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo; 00978 enum IMAP_TYPE sType = 00979 parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo); 00980 enum IMAP_TYPE dType = 00981 parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo); 00982 00983 // see if we have to create anything 00984 if (dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX) 00985 { 00986 // this might be konqueror 00987 int sub = dBox.find (sBox); 00988 00989 // might be moving to upper folder 00990 if (sub > 0) 00991 { 00992 KURL testDir = dest; 00993 00994 TQString subDir = dBox.right (dBox.length () - dBox.findRev ('/')); 00995 TQString topDir = dBox.left (sub); 00996 testDir.setPath ("/" + topDir); 00997 dType = 00998 parseURL (testDir, topDir, dSection, dLType, dSequence, dValidity, 00999 dDelimiter, dInfo); 01000 01001 kdDebug(7116) << "IMAP4::copy - checking this destination " << topDir << endl; 01002 // see if this is what the user wants 01003 if (dType == ITYPE_BOX || dType == ITYPE_DIR_AND_BOX) 01004 { 01005 kdDebug(7116) << "IMAP4::copy - assuming this destination " << topDir << endl; 01006 dBox = topDir; 01007 } 01008 else 01009 { 01010 01011 // maybe if we create a new mailbox 01012 topDir = "/" + topDir + subDir; 01013 testDir.setPath (topDir); 01014 kdDebug(7116) << "IMAP4::copy - checking this destination " << topDir << endl; 01015 dType = 01016 parseURL (testDir, topDir, dSection, dLType, dSequence, dValidity, 01017 dDelimiter, dInfo); 01018 if (dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX) 01019 { 01020 // ok then we'll create a mailbox 01021 imapCommand *cmd = doCommand (imapCommand::clientCreate (topDir)); 01022 01023 // on success we'll use it, else we'll just try to create the given dir 01024 if (cmd->result () == "OK") 01025 { 01026 kdDebug(7116) << "IMAP4::copy - assuming this destination " << topDir << endl; 01027 dType = ITYPE_BOX; 01028 dBox = topDir; 01029 } 01030 else 01031 { 01032 completeQueue.removeRef (cmd); 01033 cmd = doCommand (imapCommand::clientCreate (dBox)); 01034 if (cmd->result () == "OK") 01035 dType = ITYPE_BOX; 01036 else 01037 error (ERR_COULD_NOT_WRITE, dest.prettyURL()); 01038 } 01039 completeQueue.removeRef (cmd); 01040 } 01041 } 01042 01043 } 01044 } 01045 if (sType == ITYPE_MSG || sType == ITYPE_BOX || sType == ITYPE_DIR_AND_BOX) 01046 { 01047 //select the source box 01048 if (!assureBox(sBox, true)) return; 01049 kdDebug(7116) << "IMAP4::copy - " << sBox << " -> " << dBox << endl; 01050 01051 //issue copy command 01052 imapCommand *cmd = 01053 doCommand (imapCommand::clientCopy (dBox, sSequence)); 01054 if (cmd->result () != "OK") 01055 { 01056 kdError(5006) << "IMAP4::copy - " << cmd->resultInfo() << endl; 01057 error (ERR_COULD_NOT_WRITE, dest.prettyURL()); 01058 completeQueue.removeRef (cmd); 01059 return; 01060 } else { 01061 if (hasCapability("UIDPLUS")) 01062 { 01063 TQString uid = cmd->resultInfo(); 01064 if (uid.find("COPYUID") != -1) 01065 { 01066 uid = uid.section(" ", 2, 3); 01067 uid.truncate(uid.length()-1); 01068 infoMessage("UID "+uid); 01069 } 01070 } 01071 } 01072 completeQueue.removeRef (cmd); 01073 } 01074 else 01075 { 01076 error (ERR_ACCESS_DENIED, src.prettyURL()); 01077 return; 01078 } 01079 finished (); 01080 } 01081 01082 void 01083 IMAP4Protocol::del (const KURL & _url, bool isFile) 01084 { 01085 kdDebug(7116) << "IMAP4::del - [" << (isFile ? "File" : "NoFile") << "] " << _url.prettyURL() << endl; 01086 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; 01087 enum IMAP_TYPE aType = 01088 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); 01089 01090 switch (aType) 01091 { 01092 case ITYPE_BOX: 01093 case ITYPE_DIR_AND_BOX: 01094 if (!aSequence.isEmpty ()) 01095 { 01096 if (aSequence == "*") 01097 { 01098 if (!assureBox (aBox, false)) return; 01099 imapCommand *cmd = doCommand (imapCommand::clientExpunge ()); 01100 if (cmd->result () != "OK") { 01101 error (ERR_CANNOT_DELETE, _url.prettyURL()); 01102 completeQueue.removeRef (cmd); 01103 return; 01104 } 01105 completeQueue.removeRef (cmd); 01106 } 01107 else 01108 { 01109 // if open for read/write 01110 if (!assureBox (aBox, false)) return; 01111 imapCommand *cmd = 01112 doCommand (imapCommand:: 01113 clientStore (aSequence, "+FLAGS.SILENT", "\\DELETED")); 01114 if (cmd->result () != "OK") { 01115 error (ERR_CANNOT_DELETE, _url.prettyURL()); 01116 completeQueue.removeRef (cmd); 01117 return; 01118 } 01119 completeQueue.removeRef (cmd); 01120 } 01121 } 01122 else 01123 { 01124 if (getCurrentBox() == aBox) 01125 { 01126 imapCommand *cmd = doCommand(imapCommand::clientClose()); 01127 completeQueue.removeRef(cmd); 01128 setState(ISTATE_LOGIN); 01129 } 01130 // We unsubscribe, otherwise we get ghost folders on UW-IMAP 01131 imapCommand *cmd = doCommand(imapCommand::clientUnsubscribe(aBox)); 01132 completeQueue.removeRef(cmd); 01133 cmd = doCommand(imapCommand::clientDelete (aBox)); 01134 // If this doesn't work, we try to empty the mailbox first 01135 if (cmd->result () != "OK") 01136 { 01137 completeQueue.removeRef(cmd); 01138 if (!assureBox(aBox, false)) return; 01139 bool stillOk = true; 01140 if (stillOk) 01141 { 01142 imapCommand *cmd = doCommand( 01143 imapCommand::clientStore("1:*", "+FLAGS.SILENT", "\\DELETED")); 01144 if (cmd->result () != "OK") stillOk = false; 01145 completeQueue.removeRef(cmd); 01146 } 01147 if (stillOk) 01148 { 01149 imapCommand *cmd = doCommand(imapCommand::clientClose()); 01150 if (cmd->result () != "OK") stillOk = false; 01151 completeQueue.removeRef(cmd); 01152 setState(ISTATE_LOGIN); 01153 } 01154 if (stillOk) 01155 { 01156 imapCommand *cmd = doCommand (imapCommand::clientDelete(aBox)); 01157 if (cmd->result () != "OK") stillOk = false; 01158 completeQueue.removeRef(cmd); 01159 } 01160 if (!stillOk) 01161 { 01162 error (ERR_COULD_NOT_RMDIR, _url.prettyURL()); 01163 return; 01164 } 01165 } else { 01166 completeQueue.removeRef (cmd); 01167 } 01168 } 01169 break; 01170 01171 case ITYPE_DIR: 01172 { 01173 imapCommand *cmd = doCommand (imapCommand::clientDelete (aBox)); 01174 if (cmd->result () != "OK") { 01175 error (ERR_COULD_NOT_RMDIR, _url.prettyURL()); 01176 completeQueue.removeRef (cmd); 01177 return; 01178 } 01179 completeQueue.removeRef (cmd); 01180 } 01181 break; 01182 01183 case ITYPE_MSG: 01184 { 01185 // if open for read/write 01186 if (!assureBox (aBox, false)) return; 01187 imapCommand *cmd = 01188 doCommand (imapCommand:: 01189 clientStore (aSequence, "+FLAGS.SILENT", "\\DELETED")); 01190 if (cmd->result () != "OK") { 01191 error (ERR_CANNOT_DELETE, _url.prettyURL()); 01192 completeQueue.removeRef (cmd); 01193 return; 01194 } 01195 completeQueue.removeRef (cmd); 01196 } 01197 break; 01198 01199 case ITYPE_UNKNOWN: 01200 case ITYPE_ATTACH: 01201 error (ERR_CANNOT_DELETE, _url.prettyURL()); 01202 break; 01203 } 01204 finished (); 01205 } 01206 01207 /* 01208 * Copy a mail: data = 'C' + srcURL (KURL) + destURL (KURL) 01209 * Capabilities: data = 'c'. Result shipped in infoMessage() signal 01210 * No-op: data = 'N' 01211 * Namespace: data = 'n'. Result shipped in infoMessage() signal 01212 * The format is: section=namespace=delimiter 01213 * Note that the namespace can be empty 01214 * Unsubscribe: data = 'U' + URL (KURL) 01215 * Subscribe: data = 'u' + URL (KURL) 01216 * Change the status: data = 'S' + URL (KURL) + Flags (TQCString) 01217 * ACL commands: data = 'A' + command + URL (KURL) + command-dependent args 01218 * AnnotateMore commands: data = 'M' + 'G'et/'S'et + URL + entry + command-dependent args 01219 * Search: data = 'E' + URL (KURL) 01220 * Quota commands: data = 'Q' + 'R'oot/'G'et/'S'et + URL + entry + command-dependent args 01221 * Custom command: data = 'X' + 'N'ormal/'E'xtended + command + command-dependent args 01222 */ 01223 void 01224 IMAP4Protocol::special (const TQByteArray & aData) 01225 { 01226 kdDebug(7116) << "IMAP4Protocol::special" << endl; 01227 if (!makeLogin()) return; 01228 01229 TQDataStream stream(aData, IO_ReadOnly); 01230 01231 int tmp; 01232 stream >> tmp; 01233 01234 switch (tmp) { 01235 case 'C': 01236 { 01237 // copy 01238 KURL src; 01239 KURL dest; 01240 stream >> src >> dest; 01241 copy(src, dest, 0, FALSE); 01242 break; 01243 } 01244 case 'c': 01245 { 01246 // capabilities 01247 infoMessage(imapCapabilities.join(" ")); 01248 finished(); 01249 break; 01250 } 01251 case 'N': 01252 { 01253 // NOOP 01254 imapCommand *cmd = doCommand(imapCommand::clientNoop()); 01255 if (cmd->result () != "OK") 01256 { 01257 kdDebug(7116) << "NOOP did not succeed - connection broken" << endl; 01258 completeQueue.removeRef (cmd); 01259 error (ERR_CONNECTION_BROKEN, myHost); 01260 return; 01261 } 01262 completeQueue.removeRef (cmd); 01263 finished(); 01264 break; 01265 } 01266 case 'n': 01267 { 01268 // namespace in the form "section=namespace=delimiter" 01269 // entries are separated by , 01270 infoMessage( imapNamespaces.join(",") ); 01271 finished(); 01272 break; 01273 } 01274 case 'U': 01275 { 01276 // unsubscribe 01277 KURL _url; 01278 stream >> _url; 01279 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; 01280 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); 01281 imapCommand *cmd = doCommand(imapCommand::clientUnsubscribe(aBox)); 01282 if (cmd->result () != "OK") 01283 { 01284 completeQueue.removeRef (cmd); 01285 error(ERR_SLAVE_DEFINED, i18n("Unsubscribe of folder %1 " 01286 "failed. The server returned: %2") 01287 .arg(_url.prettyURL()) 01288 .arg(cmd->resultInfo())); 01289 return; 01290 } 01291 completeQueue.removeRef (cmd); 01292 finished(); 01293 break; 01294 } 01295 case 'u': 01296 { 01297 // subscribe 01298 KURL _url; 01299 stream >> _url; 01300 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; 01301 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); 01302 imapCommand *cmd = doCommand(imapCommand::clientSubscribe(aBox)); 01303 if (cmd->result () != "OK") 01304 { 01305 completeQueue.removeRef (cmd); 01306 error(ERR_SLAVE_DEFINED, i18n("Subscribe of folder %1 " 01307 "failed. The server returned: %2") 01308 .arg(_url.prettyURL()) 01309 .arg(cmd->resultInfo())); 01310 return; 01311 } 01312 completeQueue.removeRef (cmd); 01313 finished(); 01314 break; 01315 } 01316 case 'A': 01317 { 01318 // acl 01319 int cmd; 01320 stream >> cmd; 01321 if ( hasCapability( "ACL" ) ) { 01322 specialACLCommand( cmd, stream ); 01323 } else { 01324 error( ERR_UNSUPPORTED_ACTION, "ACL" ); 01325 } 01326 break; 01327 } 01328 case 'M': 01329 { 01330 // annotatemore 01331 int cmd; 01332 stream >> cmd; 01333 if ( hasCapability( "ANNOTATEMORE" ) ) { 01334 specialAnnotateMoreCommand( cmd, stream ); 01335 } else { 01336 error( ERR_UNSUPPORTED_ACTION, "ANNOTATEMORE" ); 01337 } 01338 break; 01339 } 01340 case 'Q': 01341 { 01342 // quota 01343 int cmd; 01344 stream >> cmd; 01345 if ( hasCapability( "QUOTA" ) ) { 01346 specialQuotaCommand( cmd, stream ); 01347 } else { 01348 error( ERR_UNSUPPORTED_ACTION, "QUOTA" ); 01349 } 01350 break; 01351 } 01352 case 'S': 01353 { 01354 // status 01355 KURL _url; 01356 TQCString newFlags; 01357 stream >> _url >> newFlags; 01358 01359 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; 01360 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); 01361 if (!assureBox(aBox, false)) return; 01362 01363 // make sure we only touch flags we know 01364 TQCString knownFlags = "\\SEEN \\ANSWERED \\FLAGGED \\DRAFT"; 01365 const imapInfo info = getSelected(); 01366 if ( info.permanentFlagsAvailable() && (info.permanentFlags() & imapInfo::User) ) { 01367 knownFlags += " KMAILFORWARDED KMAILTODO KMAILWATCHED KMAILIGNORED $FORWARDED $TODO $WATCHED $IGNORED"; 01368 } 01369 01370 imapCommand *cmd = doCommand (imapCommand:: 01371 clientStore (aSequence, "-FLAGS.SILENT", knownFlags)); 01372 if (cmd->result () != "OK") 01373 { 01374 completeQueue.removeRef (cmd); 01375 error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 " 01376 "failed.").arg(_url.prettyURL())); 01377 return; 01378 } 01379 completeQueue.removeRef (cmd); 01380 if (!newFlags.isEmpty()) 01381 { 01382 cmd = doCommand (imapCommand:: 01383 clientStore (aSequence, "+FLAGS.SILENT", newFlags)); 01384 if (cmd->result () != "OK") 01385 { 01386 completeQueue.removeRef (cmd); 01387 error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 " 01388 "failed.").arg(_url.prettyURL())); 01389 return; 01390 } 01391 completeQueue.removeRef (cmd); 01392 } 01393 finished(); 01394 break; 01395 } 01396 case 's': 01397 { 01398 // seen 01399 KURL _url; 01400 bool seen; 01401 TQCString newFlags; 01402 stream >> _url >> seen; 01403 01404 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; 01405 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); 01406 if ( !assureBox(aBox, true) ) // read-only because changing SEEN should be possible even then 01407 return; 01408 01409 imapCommand *cmd; 01410 if ( seen ) 01411 cmd = doCommand( imapCommand::clientStore( aSequence, "+FLAGS.SILENT", "\\SEEN" ) ); 01412 else 01413 cmd = doCommand( imapCommand::clientStore( aSequence, "-FLAGS.SILENT", "\\SEEN" ) ); 01414 01415 if (cmd->result () != "OK") 01416 { 01417 completeQueue.removeRef (cmd); 01418 error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 " 01419 "failed.").arg(_url.prettyURL())); 01420 return; 01421 } 01422 completeQueue.removeRef (cmd); 01423 finished(); 01424 break; 01425 } 01426 01427 case 'E': 01428 { 01429 // search 01430 specialSearchCommand( stream ); 01431 break; 01432 } 01433 case 'X': 01434 { 01435 // custom command 01436 specialCustomCommand( stream ); 01437 break; 01438 } 01439 default: 01440 kdWarning(7116) << "Unknown command in special(): " << tmp << endl; 01441 error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(tmp)) ); 01442 break; 01443 } 01444 } 01445 01446 void 01447 IMAP4Protocol::specialACLCommand( int command, TQDataStream& stream ) 01448 { 01449 // All commands start with the URL to the box 01450 KURL _url; 01451 stream >> _url; 01452 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; 01453 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); 01454 01455 switch( command ) { 01456 case 'S': // SETACL 01457 { 01458 TQString user, acl; 01459 stream >> user >> acl; 01460 kdDebug(7116) << "SETACL " << aBox << " " << user << " " << acl << endl; 01461 imapCommand *cmd = doCommand(imapCommand::clientSetACL(aBox, user, acl)); 01462 if (cmd->result () != "OK") 01463 { 01464 error(ERR_SLAVE_DEFINED, i18n("Setting the Access Control List on folder %1 " 01465 "for user %2 failed. The server returned: %3") 01466 .arg(_url.prettyURL()) 01467 .arg(user) 01468 .arg(cmd->resultInfo())); 01469 return; 01470 } 01471 completeQueue.removeRef (cmd); 01472 finished(); 01473 break; 01474 } 01475 case 'D': // DELETEACL 01476 { 01477 TQString user; 01478 stream >> user; 01479 kdDebug(7116) << "DELETEACL " << aBox << " " << user << endl; 01480 imapCommand *cmd = doCommand(imapCommand::clientDeleteACL(aBox, user)); 01481 if (cmd->result () != "OK") 01482 { 01483 error(ERR_SLAVE_DEFINED, i18n("Deleting the Access Control List on folder %1 " 01484 "for user %2 failed. The server returned: %3") 01485 .arg(_url.prettyURL()) 01486 .arg(user) 01487 .arg(cmd->resultInfo())); 01488 return; 01489 } 01490 completeQueue.removeRef (cmd); 01491 finished(); 01492 break; 01493 } 01494 case 'G': // GETACL 01495 { 01496 kdDebug(7116) << "GETACL " << aBox << endl; 01497 imapCommand *cmd = doCommand(imapCommand::clientGetACL(aBox)); 01498 if (cmd->result () != "OK") 01499 { 01500 error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 " 01501 "failed. The server returned: %2") 01502 .arg(_url.prettyURL()) 01503 .arg(cmd->resultInfo())); 01504 return; 01505 } 01506 // Returning information to the application from a special() command isn't easy. 01507 // I'm reusing the infoMessage trick seen above (for capabilities), but this 01508 // limits me to a string instead of a stringlist. Using DQUOTE as separator, 01509 // because it's forbidden in userids by rfc3501 01510 kdDebug(7116) << getResults() << endl; 01511 infoMessage(getResults().join( "\"" )); 01512 finished(); 01513 break; 01514 } 01515 case 'L': // LISTRIGHTS 01516 { 01517 // Do we need this one? It basically shows which rights are tied together, but that's all? 01518 error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(command)) ); 01519 break; 01520 } 01521 case 'M': // MYRIGHTS 01522 { 01523 kdDebug(7116) << "MYRIGHTS " << aBox << endl; 01524 imapCommand *cmd = doCommand(imapCommand::clientMyRights(aBox)); 01525 if (cmd->result () != "OK") 01526 { 01527 error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 " 01528 "failed. The server returned: %2") 01529 .arg(_url.prettyURL()) 01530 .arg(cmd->resultInfo())); 01531 return; 01532 } 01533 TQStringList lst = getResults(); 01534 kdDebug(7116) << "myrights results: " << lst << endl; 01535 if ( !lst.isEmpty() ) { 01536 Q_ASSERT( lst.count() == 1 ); 01537 infoMessage( lst.first() ); 01538 } 01539 finished(); 01540 break; 01541 } 01542 default: 01543 kdWarning(7116) << "Unknown special ACL command:" << command << endl; 01544 error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(command)) ); 01545 } 01546 } 01547 01548 void 01549 IMAP4Protocol::specialSearchCommand( TQDataStream& stream ) 01550 { 01551 kdDebug(7116) << "IMAP4Protocol::specialSearchCommand" << endl; 01552 KURL _url; 01553 stream >> _url; 01554 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; 01555 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); 01556 if (!assureBox(aBox, true)) return; 01557 01558 imapCommand *cmd = doCommand (imapCommand::clientSearch( aSection )); 01559 if (cmd->result () != "OK") 01560 { 01561 error(ERR_SLAVE_DEFINED, i18n("Searching of folder %1 " 01562 "failed. The server returned: %2") 01563 .arg(aBox) 01564 .arg(cmd->resultInfo())); 01565 return; 01566 } 01567 completeQueue.removeRef(cmd); 01568 TQStringList lst = getResults(); 01569 kdDebug(7116) << "IMAP4Protocol::specialSearchCommand '" << aSection << 01570 "' returns " << lst << endl; 01571 infoMessage( lst.join( " " ) ); 01572 01573 finished(); 01574 } 01575 01576 void 01577 IMAP4Protocol::specialCustomCommand( TQDataStream& stream ) 01578 { 01579 kdDebug(7116) << "IMAP4Protocol::specialCustomCommand" << endl; 01580 01581 TQString command, arguments; 01582 int type; 01583 stream >> type; 01584 stream >> command >> arguments; 01585 01590 if ( type == 'N' ) { 01591 kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: normal mode" << endl; 01592 imapCommand *cmd = doCommand (imapCommand::clientCustom( command, arguments )); 01593 if (cmd->result () != "OK") 01594 { 01595 error(ERR_SLAVE_DEFINED, i18n("Custom command %1:%2 " 01596 "failed. The server returned: %3") 01597 .arg(command) 01598 .arg(arguments) 01599 .arg(cmd->resultInfo())); 01600 return; 01601 } 01602 completeQueue.removeRef(cmd); 01603 TQStringList lst = getResults(); 01604 kdDebug(7116) << "IMAP4Protocol::specialCustomCommand '" << command << 01605 ":" << arguments << 01606 "' returns " << lst << endl; 01607 infoMessage( lst.join( " " ) ); 01608 01609 finished(); 01610 } else 01615 if ( type == 'E' ) { 01616 kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: extended mode" << endl; 01617 imapCommand *cmd = sendCommand (imapCommand::clientCustom( command, TQString() )); 01618 while ( !parseLoop () ) ; 01619 01620 // see if server is waiting 01621 if (!cmd->isComplete () && !getContinuation ().isEmpty ()) 01622 { 01623 const TQByteArray buffer = arguments.utf8(); 01624 01625 // send data to server 01626 bool sendOk = (write (buffer.data (), buffer.size ()) == (ssize_t)buffer.size ()); 01627 processedSize( buffer.size() ); 01628 01629 if ( !sendOk ) { 01630 error ( ERR_CONNECTION_BROKEN, myHost ); 01631 completeQueue.removeRef ( cmd ); 01632 setState(ISTATE_CONNECT); 01633 closeConnection(); 01634 return; 01635 } 01636 } 01637 parseWriteLine (""); 01638 01639 do 01640 { 01641 while (!parseLoop ()) ; 01642 } 01643 while (!cmd->isComplete ()); 01644 01645 completeQueue.removeRef (cmd); 01646 01647 TQStringList lst = getResults(); 01648 kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: returns " << lst << endl; 01649 infoMessage( lst.join( " " ) ); 01650 01651 finished (); 01652 } 01653 } 01654 01655 void 01656 IMAP4Protocol::specialAnnotateMoreCommand( int command, TQDataStream& stream ) 01657 { 01658 // All commands start with the URL to the box 01659 KURL _url; 01660 stream >> _url; 01661 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; 01662 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); 01663 01664 switch( command ) { 01665 case 'S': // SETANNOTATION 01666 { 01667 // Params: 01668 // KURL URL of the mailbox 01669 // TQString entry (should be an actual entry name, no % or *; empty for server entries) 01670 // TQMap<TQString,TQString> attributes (name and value) 01671 TQString entry; 01672 TQMap<TQString, TQString> attributes; 01673 stream >> entry >> attributes; 01674 kdDebug(7116) << "SETANNOTATION " << aBox << " " << entry << " " << attributes.count() << " attributes" << endl; 01675 imapCommand *cmd = doCommand(imapCommand::clientSetAnnotation(aBox, entry, attributes)); 01676 if (cmd->result () != "OK") 01677 { 01678 error(ERR_SLAVE_DEFINED, i18n("Setting the annotation %1 on folder %2 " 01679 " failed. The server returned: %3") 01680 .arg(entry) 01681 .arg(_url.prettyURL()) 01682 .arg(cmd->resultInfo())); 01683 return; 01684 } 01685 completeQueue.removeRef (cmd); 01686 finished(); 01687 break; 01688 } 01689 case 'G': // GETANNOTATION. 01690 { 01691 // Params: 01692 // KURL URL of the mailbox 01693 // TQString entry (should be an actual entry name, no % or *; empty for server entries) 01694 // TQStringList attributes (list of attributes to be retrieved, possibly with % or *) 01695 TQString entry; 01696 TQStringList attributeNames; 01697 stream >> entry >> attributeNames; 01698 kdDebug(7116) << "GETANNOTATION " << aBox << " " << entry << " " << attributeNames << endl; 01699 imapCommand *cmd = doCommand(imapCommand::clientGetAnnotation(aBox, entry, attributeNames)); 01700 if (cmd->result () != "OK") 01701 { 01702 error(ERR_SLAVE_DEFINED, i18n("Retrieving the annotation %1 on folder %2 " 01703 "failed. The server returned: %3") 01704 .arg(entry) 01705 .arg(_url.prettyURL()) 01706 .arg(cmd->resultInfo())); 01707 return; 01708 } 01709 // Returning information to the application from a special() command isn't easy. 01710 // I'm reusing the infoMessage trick seen above (for capabilities and acls), but this 01711 // limits me to a string instead of a stringlist. Let's use \r as separator. 01712 kdDebug(7116) << getResults() << endl; 01713 infoMessage(getResults().join( "\r" )); 01714 finished(); 01715 break; 01716 } 01717 default: 01718 kdWarning(7116) << "Unknown special annotate command:" << command << endl; 01719 error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(command)) ); 01720 } 01721 } 01722 01723 void 01724 IMAP4Protocol::specialQuotaCommand( int command, TQDataStream& stream ) 01725 { 01726 // All commands start with the URL to the box 01727 KURL _url; 01728 stream >> _url; 01729 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; 01730 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo); 01731 01732 switch( command ) { 01733 case 'R': // GETQUOTAROOT 01734 { 01735 kdDebug(7116) << "QUOTAROOT " << aBox << endl; 01736 imapCommand *cmd = doCommand(imapCommand::clientGetQuotaroot( aBox ) ); 01737 if (cmd->result () != "OK") 01738 { 01739 error(ERR_SLAVE_DEFINED, i18n("Retrieving the quota root information on folder %1 " 01740 "failed. The server returned: %2") 01741 .arg(_url.prettyURL()) 01742 .arg(cmd->resultInfo())); 01743 return; 01744 } 01745 infoMessage(getResults().join( "\r" )); 01746 finished(); 01747 break; 01748 } 01749 case 'G': // GETQUOTA 01750 { 01751 kdDebug(7116) << "GETQUOTA command" << endl; 01752 kdWarning(7116) << "UNIMPLEMENTED" << endl; 01753 break; 01754 } 01755 case 'S': // SETQUOTA 01756 { 01757 kdDebug(7116) << "SETQUOTA command" << endl; 01758 kdWarning(7116) << "UNIMPLEMENTED" << endl; 01759 break; 01760 } 01761 default: 01762 kdWarning(7116) << "Unknown special quota command:" << command << endl; 01763 error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(command)) ); 01764 } 01765 } 01766 01767 void 01768 IMAP4Protocol::rename (const KURL & src, const KURL & dest, bool overwrite) 01769 { 01770 kdDebug(7116) << "IMAP4::rename - [" << (overwrite ? "Overwrite" : "NoOverwrite") << "] " << src.prettyURL() << " -> " << dest.prettyURL() << endl; 01771 TQString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo; 01772 TQString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo; 01773 enum IMAP_TYPE sType = 01774 parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo, false); 01775 enum IMAP_TYPE dType = 01776 parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo, false); 01777 01778 if (dType == ITYPE_UNKNOWN) 01779 { 01780 switch (sType) 01781 { 01782 case ITYPE_BOX: 01783 case ITYPE_DIR: 01784 case ITYPE_DIR_AND_BOX: 01785 { 01786 if (getState() == ISTATE_SELECT && sBox == getCurrentBox()) 01787 { 01788 kdDebug(7116) << "IMAP4::rename - close " << getCurrentBox() << endl; 01789 // mailbox can only be renamed if it is closed 01790 imapCommand *cmd = doCommand (imapCommand::clientClose()); 01791 bool ok = cmd->result() == "OK"; 01792 completeQueue.removeRef(cmd); 01793 if (!ok) 01794 { 01795 kdWarning(7116) << "Unable to close mailbox!" << endl; 01796 error(ERR_CANNOT_RENAME, src.path()); 01797 return; 01798 } 01799 setState(ISTATE_LOGIN); 01800 } 01801 imapCommand *cmd = doCommand (imapCommand::clientRename (sBox, dBox)); 01802 if (cmd->result () != "OK") { 01803 error (ERR_CANNOT_RENAME, src.path()); 01804 completeQueue.removeRef (cmd); 01805 return; 01806 } 01807 completeQueue.removeRef (cmd); 01808 } 01809 break; 01810 01811 case ITYPE_MSG: 01812 case ITYPE_ATTACH: 01813 case ITYPE_UNKNOWN: 01814 error (ERR_CANNOT_RENAME, src.path()); 01815 break; 01816 } 01817 } 01818 else 01819 { 01820 error (ERR_CANNOT_RENAME, src.path()); 01821 return; 01822 } 01823 finished (); 01824 } 01825 01826 void 01827 IMAP4Protocol::slave_status () 01828 { 01829 bool connected = (getState() != ISTATE_NO) && isConnectionValid(); 01830 kdDebug(7116) << "IMAP4::slave_status " << connected << endl; 01831 slaveStatus ( connected ? myHost : TQString(), connected ); 01832 } 01833 01834 void 01835 IMAP4Protocol::dispatch (int command, const TQByteArray & data) 01836 { 01837 kdDebug(7116) << "IMAP4::dispatch - command=" << command << endl; 01838 TDEIO::TCPSlaveBase::dispatch (command, data); 01839 } 01840 01841 void 01842 IMAP4Protocol::stat (const KURL & _url) 01843 { 01844 kdDebug(7116) << "IMAP4::stat - " << _url.prettyURL() << endl; 01845 TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo; 01846 // parseURL with caching 01847 enum IMAP_TYPE aType = 01848 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, 01849 aInfo, true); 01850 01851 UDSEntry entry; 01852 UDSAtom atom; 01853 01854 atom.m_uds = UDS_NAME; 01855 atom.m_str = aBox; 01856 entry.append (atom); 01857 01858 if (!aSection.isEmpty()) 01859 { 01860 if (getState() == ISTATE_SELECT && aBox == getCurrentBox()) 01861 { 01862 imapCommand *cmd = doCommand (imapCommand::clientClose()); 01863 bool ok = cmd->result() == "OK"; 01864 completeQueue.removeRef(cmd); 01865 if (!ok) 01866 { 01867 error(ERR_COULD_NOT_STAT, aBox); 01868 return; 01869 } 01870 setState(ISTATE_LOGIN); 01871 } 01872 bool ok = false; 01873 TQString cmdInfo; 01874 if (aType == ITYPE_MSG || aType == ITYPE_ATTACH) 01875 ok = true; 01876 else 01877 { 01878 imapCommand *cmd = doCommand(imapCommand::clienStatus(aBox, aSection)); 01879 ok = cmd->result() == "OK"; 01880 cmdInfo = cmd->resultInfo(); 01881 completeQueue.removeRef(cmd); 01882 } 01883 if (!ok) 01884 { 01885 bool found = false; 01886 imapCommand *cmd = doCommand (imapCommand::clientList ("", aBox)); 01887 if (cmd->result () == "OK") 01888 { 01889 for (TQValueListIterator < imapList > it = listResponses.begin (); 01890 it != listResponses.end (); ++it) 01891 { 01892 if (aBox == (*it).name ()) found = true; 01893 } 01894 } 01895 completeQueue.removeRef (cmd); 01896 if (found) 01897 error(ERR_COULD_NOT_STAT, aBox); 01898 else 01899 error(TDEIO::ERR_DOES_NOT_EXIST, aBox); 01900 return; 01901 } 01902 if ((aSection == "UIDNEXT" && geStatus().uidNextAvailable()) 01903 || (aSection == "UNSEEN" && geStatus().unseenAvailable())) 01904 { 01905 atom.m_uds = UDS_SIZE; 01906 atom.m_str = TQString(); 01907 atom.m_long = (aSection == "UIDNEXT") ? geStatus().uidNext() 01908 : geStatus().unseen(); 01909 entry.append(atom); 01910 } 01911 } else 01912 if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX || aType == ITYPE_MSG || 01913 aType == ITYPE_ATTACH) 01914 { 01915 ulong validity = 0; 01916 // see if the box is already in select/examine state 01917 if (aBox == getCurrentBox ()) 01918 validity = selectInfo.uidValidity (); 01919 else 01920 { 01921 // do a status lookup on the box 01922 // only do this if the box is not selected 01923 // the server might change the validity for new select/examine 01924 imapCommand *cmd = 01925 doCommand (imapCommand::clienStatus (aBox, "UIDVALIDITY")); 01926 completeQueue.removeRef (cmd); 01927 validity = geStatus ().uidValidity (); 01928 } 01929 validity = 0; // temporary 01930 01931 if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX) 01932 { 01933 // has no or an invalid uidvalidity 01934 if (validity > 0 && validity != aValidity.toULong ()) 01935 { 01936 //redirect 01937 KURL newUrl = _url; 01938 01939 newUrl.setPath ("/" + aBox + ";UIDVALIDITY=" + 01940 TQString::number(validity)); 01941 kdDebug(7116) << "IMAP4::stat - redirecting to " << newUrl.prettyURL() << endl; 01942 redirection (newUrl); 01943 } 01944 } 01945 else if (aType == ITYPE_MSG || aType == ITYPE_ATTACH) 01946 { 01947 //must determine if this message exists 01948 //cause konqueror will check this on paste operations 01949 01950 // has an invalid uidvalidity 01951 // or no messages in box 01952 if (validity > 0 && validity != aValidity.toULong ()) 01953 { 01954 aType = ITYPE_UNKNOWN; 01955 kdDebug(7116) << "IMAP4::stat - url has invalid validity [" << validity << "d] " << _url.prettyURL() << endl; 01956 } 01957 } 01958 } 01959 01960 atom.m_uds = UDS_MIME_TYPE; 01961 atom.m_str = getMimeType (aType); 01962 entry.append (atom); 01963 01964 kdDebug(7116) << "IMAP4: stat: " << atom.m_str << endl; 01965 switch (aType) 01966 { 01967 case ITYPE_DIR: 01968 atom.m_uds = UDS_FILE_TYPE; 01969 atom.m_str = TQString(); 01970 atom.m_long = S_IFDIR; 01971 entry.append (atom); 01972 break; 01973 01974 case ITYPE_BOX: 01975 case ITYPE_DIR_AND_BOX: 01976 atom.m_uds = UDS_FILE_TYPE; 01977 atom.m_str = TQString(); 01978 atom.m_long = S_IFDIR; 01979 entry.append (atom); 01980 break; 01981 01982 case ITYPE_MSG: 01983 case ITYPE_ATTACH: 01984 atom.m_uds = UDS_FILE_TYPE; 01985 atom.m_str = TQString(); 01986 atom.m_long = S_IFREG; 01987 entry.append (atom); 01988 break; 01989 01990 case ITYPE_UNKNOWN: 01991 error (ERR_DOES_NOT_EXIST, _url.prettyURL()); 01992 break; 01993 } 01994 01995 statEntry (entry); 01996 kdDebug(7116) << "IMAP4::stat - Finishing stat" << endl; 01997 finished (); 01998 } 01999 02000 void IMAP4Protocol::openConnection() 02001 { 02002 if (makeLogin()) connected(); 02003 } 02004 02005 void IMAP4Protocol::closeConnection() 02006 { 02007 if (getState() == ISTATE_NO) return; 02008 if (getState() == ISTATE_SELECT && metaData("expunge") == "auto") 02009 { 02010 imapCommand *cmd = doCommand (imapCommand::clientExpunge()); 02011 completeQueue.removeRef (cmd); 02012 } 02013 if (getState() != ISTATE_CONNECT) 02014 { 02015 imapCommand *cmd = doCommand (imapCommand::clientLogout()); 02016 completeQueue.removeRef (cmd); 02017 } 02018 closeDescriptor(); 02019 setState(ISTATE_NO); 02020 completeQueue.clear(); 02021 sentQueue.clear(); 02022 lastHandled = 0; 02023 currentBox = TQString(); 02024 readBufferLen = 0; 02025 } 02026 02027 bool IMAP4Protocol::makeLogin () 02028 { 02029 if (getState () == ISTATE_LOGIN || getState () == ISTATE_SELECT) 02030 return true; 02031 02032 kdDebug(7116) << "IMAP4::makeLogin - checking login" << endl; 02033 bool alreadyConnected = getState() == ISTATE_CONNECT; 02034 kdDebug(7116) << "IMAP4::makeLogin - alreadyConnected " << alreadyConnected << endl; 02035 if (alreadyConnected || connectToHost (myHost.latin1(), myPort)) 02036 { 02037 // fcntl (m_iSock, F_SETFL, (fcntl (m_iSock, F_GETFL) | O_NDELAY)); 02038 02039 setState(ISTATE_CONNECT); 02040 02041 myAuth = metaData("auth"); 02042 myTLS = metaData("tls"); 02043 kdDebug(7116) << "myAuth: " << myAuth << endl; 02044 02045 imapCommand *cmd; 02046 02047 unhandled.clear (); 02048 if (!alreadyConnected) while (!parseLoop ()) ; //get greeting 02049 TQString greeting; 02050 if (!unhandled.isEmpty()) greeting = unhandled.first().stripWhiteSpace(); 02051 unhandled.clear (); //get rid of it 02052 cmd = doCommand (new imapCommand ("CAPABILITY", "")); 02053 02054 kdDebug(7116) << "IMAP4: setHost: capability" << endl; 02055 for (TQStringList::Iterator it = imapCapabilities.begin (); 02056 it != imapCapabilities.end (); ++it) 02057 { 02058 kdDebug(7116) << "'" << (*it) << "'" << endl; 02059 } 02060 completeQueue.removeRef (cmd); 02061 02062 if (!hasCapability("IMAP4") && !hasCapability("IMAP4rev1")) 02063 { 02064 error(ERR_COULD_NOT_LOGIN, i18n("The server %1 supports neither " 02065 "IMAP4 nor IMAP4rev1.\nIt identified itself with: %2") 02066 .arg(myHost).arg(greeting)); 02067 closeConnection(); 02068 return false; 02069 } 02070 02071 if (metaData("nologin") == "on") return TRUE; 02072 02073 if (myTLS == "on" && !hasCapability(TQString("STARTTLS"))) 02074 { 02075 error(ERR_COULD_NOT_LOGIN, i18n("The server does not support TLS.\n" 02076 "Disable this security feature to connect unencrypted.")); 02077 closeConnection(); 02078 return false; 02079 } 02080 if ((myTLS == "on" || (canUseTLS() && myTLS != "off")) && 02081 hasCapability(TQString("STARTTLS"))) 02082 { 02083 imapCommand *cmd = doCommand (imapCommand::clientStartTLS()); 02084 if (cmd->result () == "OK") 02085 { 02086 completeQueue.removeRef(cmd); 02087 int tlsrc = startTLS(); 02088 if (tlsrc == 1) 02089 { 02090 kdDebug(7116) << "TLS mode has been enabled." << endl; 02091 imapCommand *cmd2 = doCommand (new imapCommand ("CAPABILITY", "")); 02092 for (TQStringList::Iterator it = imapCapabilities.begin (); 02093 it != imapCapabilities.end (); ++it) 02094 { 02095 kdDebug(7116) << "'" << (*it) << "'" << endl; 02096 } 02097 completeQueue.removeRef (cmd2); 02098 } else { 02099 kdWarning(7116) << "TLS mode setup has failed. Aborting." << endl; 02100 error (ERR_COULD_NOT_LOGIN, i18n("Starting TLS failed.")); 02101 closeConnection(); 02102 return false; 02103 } 02104 } else completeQueue.removeRef(cmd); 02105 } 02106 02107 if (myAuth.isEmpty () || myAuth == "*") { 02108 if (hasCapability (TQString ("LOGINDISABLED"))) { 02109 error (ERR_COULD_NOT_LOGIN, i18n("LOGIN is disabled by the server.")); 02110 closeConnection(); 02111 return false; 02112 } 02113 } 02114 else { 02115 if (!hasCapability (TQString ("AUTH=") + myAuth)) { 02116 error (ERR_COULD_NOT_LOGIN, i18n("The authentication method %1 is not " 02117 "supported by the server.").arg(myAuth)); 02118 closeConnection(); 02119 return false; 02120 } 02121 } 02122 02123 if ( greeting.contains( TQRegExp( "Cyrus IMAP4 v2.1" ) ) ) { 02124 removeCapability( "ANNOTATEMORE" ); 02125 } 02126 02127 // starting from Cyrus IMAP 2.3.9, shared seen flags are available 02128 TQRegExp regExp( "Cyrus\\sIMAP[4]{0,1}\\sv(\\d+)\\.(\\d+)\\.(\\d+)", false ); 02129 if ( regExp.search( greeting ) >= 0 ) { 02130 const int major = regExp.cap( 1 ).toInt(); 02131 const int minor = regExp.cap( 2 ).toInt(); 02132 const int patch = regExp.cap( 3 ).toInt(); 02133 if ( major > 2 || (major == 2 && (minor > 3 || (minor == 3 && patch > 9))) ) { 02134 kdDebug(7116) << k_funcinfo << "Cyrus IMAP >= 2.3.9 detected, enabling shared seen flag support" << endl; 02135 imapCapabilities.append( "x-kmail-sharedseen" ); 02136 } 02137 } 02138 02139 kdDebug(7116) << "IMAP4::makeLogin - attempting login" << endl; 02140 02141 TDEIO::AuthInfo authInfo; 02142 authInfo.username = myUser; 02143 authInfo.password = myPass; 02144 authInfo.prompt = i18n ("Username and password for your IMAP account:"); 02145 02146 kdDebug(7116) << "IMAP4::makeLogin - open_PassDlg said user=" << myUser << " pass=xx" << endl; 02147 02148 TQString resultInfo; 02149 if (myAuth.isEmpty () || myAuth == "*") 02150 { 02151 if (myUser.isEmpty () || myPass.isEmpty ()) { 02152 if(openPassDlg (authInfo)) { 02153 myUser = authInfo.username; 02154 myPass = authInfo.password; 02155 } 02156 } 02157 if (!clientLogin (myUser, myPass, resultInfo)) 02158 error(TDEIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to login. Probably the " 02159 "password is wrong.\nThe server %1 replied:\n%2").arg(myHost).arg(resultInfo)); 02160 } 02161 else 02162 { 02163 #ifdef HAVE_LIBSASL2 02164 if (!clientAuthenticate (this, authInfo, myHost, myAuth, mySSL, resultInfo)) 02165 error(TDEIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to authenticate via %1.\n" 02166 "The server %2 replied:\n%3").arg(myAuth).arg(myHost).arg(resultInfo)); 02167 else { 02168 myUser = authInfo.username; 02169 myPass = authInfo.password; 02170 } 02171 #else 02172 error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("SASL authentication is not compiled into tdeio_imap4.")); 02173 #endif 02174 } 02175 if ( hasCapability("NAMESPACE") ) 02176 { 02177 // get all namespaces and save the namespace - delimiter association 02178 cmd = doCommand( imapCommand::clientNamespace() ); 02179 if (cmd->result () == "OK") 02180 { 02181 kdDebug(7116) << "makeLogin - registered namespaces" << endl; 02182 } 02183 completeQueue.removeRef (cmd); 02184 } 02185 // get the default delimiter (empty listing) 02186 cmd = doCommand( imapCommand::clientList("", "") ); 02187 if (cmd->result () == "OK") 02188 { 02189 TQValueListIterator < imapList > it = listResponses.begin(); 02190 if ( it == listResponses.end() ) 02191 { 02192 // empty answer - this is a buggy imap server 02193 // as a fallback we fire a normal listing and take the first answer 02194 completeQueue.removeRef (cmd); 02195 cmd = doCommand( imapCommand::clientList("", "%") ); 02196 if (cmd->result () == "OK") 02197 { 02198 it = listResponses.begin(); 02199 } 02200 } 02201 if ( it != listResponses.end() ) 02202 { 02203 namespaceToDelimiter[TQString()] = (*it).hierarchyDelimiter(); 02204 kdDebug(7116) << "makeLogin - delimiter for empty ns='" << 02205 (*it).hierarchyDelimiter() << "'" << endl; 02206 if ( !hasCapability("NAMESPACE") ) 02207 { 02208 // server does not support namespaces 02209 TQString nsentry = TQString::number( 0 ) + "==" 02210 + (*it).hierarchyDelimiter(); 02211 imapNamespaces.append( nsentry ); 02212 } 02213 } 02214 } 02215 completeQueue.removeRef (cmd); 02216 } else { 02217 kdDebug(7116) << "makeLogin - NO login" << endl; 02218 } 02219 02220 return getState() == ISTATE_LOGIN; 02221 } 02222 02223 void 02224 IMAP4Protocol::parseWriteLine (const TQString & aStr) 02225 { 02226 //kdDebug(7116) << "Writing: " << aStr << endl; 02227 TQCString writer = aStr.utf8(); 02228 int len = writer.length(); 02229 02230 // append CRLF if necessary 02231 if (len == 0 || (writer[len - 1] != '\n')) { 02232 len += 2; 02233 writer += "\r\n"; 02234 } 02235 02236 // write it 02237 write(writer.data(), len); 02238 } 02239 02240 TQString 02241 IMAP4Protocol::getMimeType (enum IMAP_TYPE aType) 02242 { 02243 switch (aType) 02244 { 02245 case ITYPE_DIR: 02246 return "inode/directory"; 02247 break; 02248 02249 case ITYPE_BOX: 02250 return "message/digest"; 02251 break; 02252 02253 case ITYPE_DIR_AND_BOX: 02254 return "message/directory"; 02255 break; 02256 02257 case ITYPE_MSG: 02258 return "message/rfc822"; 02259 break; 02260 02261 // this should be handled by flushOutput 02262 case ITYPE_ATTACH: 02263 return "application/octet-stream"; 02264 break; 02265 02266 case ITYPE_UNKNOWN: 02267 default: 02268 return "unknown/unknown"; 02269 } 02270 } 02271 02272 02273 02274 void 02275 IMAP4Protocol::doListEntry (const KURL & _url, int stretch, imapCache * cache, 02276 bool withFlags, bool withSubject) 02277 { 02278 KURL aURL = _url; 02279 aURL.setQuery (TQString()); 02280 const TQString encodedUrl = aURL.url(0, 106); // utf-8 02281 doListEntry(encodedUrl, stretch, cache, withFlags, withSubject); 02282 } 02283 02284 02285 02286 void 02287 IMAP4Protocol::doListEntry (const TQString & encodedUrl, int stretch, imapCache * cache, 02288 bool withFlags, bool withSubject) 02289 { 02290 if (cache) 02291 { 02292 UDSEntry entry; 02293 UDSAtom atom; 02294 02295 entry.clear (); 02296 02297 const TQString uid = TQString::number(cache->getUid()); 02298 02299 atom.m_uds = UDS_NAME; 02300 atom.m_str = uid; 02301 atom.m_long = 0; 02302 if (stretch > 0) 02303 { 02304 atom.m_str = "0000000000000000" + atom.m_str; 02305 atom.m_str = atom.m_str.right (stretch); 02306 } 02307 if (withSubject) 02308 { 02309 mailHeader *header = cache->getHeader(); 02310 if (header) 02311 atom.m_str += " " + header->getSubject(); 02312 } 02313 entry.append (atom); 02314 02315 atom.m_uds = UDS_URL; 02316 atom.m_str = encodedUrl; // utf-8 02317 if (atom.m_str[atom.m_str.length () - 1] != '/') 02318 atom.m_str += '/'; 02319 atom.m_str += ";UID=" + uid; 02320 atom.m_long = 0; 02321 entry.append (atom); 02322 02323 atom.m_uds = UDS_FILE_TYPE; 02324 atom.m_str = TQString(); 02325 atom.m_long = S_IFREG; 02326 entry.append (atom); 02327 02328 atom.m_uds = UDS_SIZE; 02329 atom.m_long = cache->getSize(); 02330 entry.append (atom); 02331 02332 atom.m_uds = UDS_MIME_TYPE; 02333 atom.m_str = "message/rfc822"; 02334 atom.m_long = 0; 02335 entry.append (atom); 02336 02337 atom.m_uds = UDS_USER; 02338 atom.m_str = myUser; 02339 entry.append (atom); 02340 02341 atom.m_uds = TDEIO::UDS_ACCESS; 02342 atom.m_long = (withFlags) ? cache->getFlags() : S_IRUSR | S_IXUSR | S_IWUSR; 02343 entry.append (atom); 02344 02345 listEntry (entry, false); 02346 } 02347 } 02348 02349 void 02350 IMAP4Protocol::doListEntry (const KURL & _url, const TQString & myBox, 02351 const imapList & item, bool appendPath) 02352 { 02353 KURL aURL = _url; 02354 aURL.setQuery (TQString()); 02355 UDSEntry entry; 02356 UDSAtom atom; 02357 int hdLen = item.hierarchyDelimiter().length(); 02358 02359 { 02360 // mailboxName will be appended to the path if appendPath is true 02361 TQString mailboxName = item.name (); 02362 02363 // some beautification 02364 if (mailboxName.find (myBox) == 0 && mailboxName.length() > myBox.length()) 02365 { 02366 mailboxName = 02367 mailboxName.right (mailboxName.length () - myBox.length ()); 02368 } 02369 if (mailboxName[0] == '/') 02370 mailboxName = mailboxName.right (mailboxName.length () - 1); 02371 if (mailboxName.left(hdLen) == item.hierarchyDelimiter()) 02372 mailboxName = mailboxName.right(mailboxName.length () - hdLen); 02373 if (mailboxName.right(hdLen) == item.hierarchyDelimiter()) 02374 mailboxName.truncate(mailboxName.length () - hdLen); 02375 02376 atom.m_uds = UDS_NAME; 02377 if (!item.hierarchyDelimiter().isEmpty() && 02378 mailboxName.find(item.hierarchyDelimiter()) != -1) 02379 atom.m_str = mailboxName.section(item.hierarchyDelimiter(), -1); 02380 else 02381 atom.m_str = mailboxName; 02382 02383 // konqueror will die with an assertion failure otherwise 02384 if (atom.m_str.isEmpty ()) 02385 atom.m_str = ".."; 02386 02387 if (!atom.m_str.isEmpty ()) 02388 { 02389 atom.m_long = 0; 02390 entry.append (atom); 02391 02392 if (!item.noSelect ()) 02393 { 02394 atom.m_uds = UDS_MIME_TYPE; 02395 if (!item.noInferiors ()) 02396 { 02397 atom.m_str = "message/directory"; 02398 } else { 02399 atom.m_str = "message/digest"; 02400 } 02401 atom.m_long = 0; 02402 entry.append (atom); 02403 mailboxName += '/'; 02404 02405 // explicitly set this as a directory for KFileDialog 02406 atom.m_uds = UDS_FILE_TYPE; 02407 atom.m_str = TQString(); 02408 atom.m_long = S_IFDIR; 02409 entry.append (atom); 02410 } 02411 else if (!item.noInferiors ()) 02412 { 02413 atom.m_uds = UDS_MIME_TYPE; 02414 atom.m_str = "inode/directory"; 02415 atom.m_long = 0; 02416 entry.append (atom); 02417 mailboxName += '/'; 02418 02419 // explicitly set this as a directory for KFileDialog 02420 atom.m_uds = UDS_FILE_TYPE; 02421 atom.m_str = TQString(); 02422 atom.m_long = S_IFDIR; 02423 entry.append (atom); 02424 } 02425 else 02426 { 02427 atom.m_uds = UDS_MIME_TYPE; 02428 atom.m_str = "unknown/unknown"; 02429 atom.m_long = 0; 02430 entry.append (atom); 02431 } 02432 02433 atom.m_uds = UDS_URL; 02434 TQString path = aURL.path(); 02435 atom.m_str = aURL.url (0, 106); // utf-8 02436 if (appendPath) 02437 { 02438 if (path[path.length() - 1] == '/' && !path.isEmpty() && path != "/") 02439 path.truncate(path.length() - 1); 02440 if (!path.isEmpty() && path != "/" 02441 && path.right(hdLen) != item.hierarchyDelimiter()) { 02442 path += item.hierarchyDelimiter(); 02443 } 02444 path += mailboxName; 02445 if (path.upper() == "/INBOX/") { 02446 // make sure the client can rely on INBOX 02447 path = path.upper(); 02448 } 02449 } 02450 aURL.setPath(path); 02451 atom.m_str = aURL.url(0, 106); // utf-8 02452 atom.m_long = 0; 02453 entry.append (atom); 02454 02455 atom.m_uds = UDS_USER; 02456 atom.m_str = myUser; 02457 entry.append (atom); 02458 02459 atom.m_uds = UDS_ACCESS; 02460 atom.m_long = S_IRUSR | S_IXUSR | S_IWUSR; 02461 entry.append (atom); 02462 02463 atom.m_uds = UDS_EXTRA; 02464 atom.m_str = item.attributesAsString(); 02465 atom.m_long = 0; 02466 entry.append (atom); 02467 02468 listEntry (entry, false); 02469 } 02470 } 02471 } 02472 02473 enum IMAP_TYPE 02474 IMAP4Protocol::parseURL (const KURL & _url, TQString & _box, 02475 TQString & _section, TQString & _type, TQString & _uid, 02476 TQString & _validity, TQString & _hierarchyDelimiter, 02477 TQString & _info, bool cache) 02478 { 02479 enum IMAP_TYPE retVal; 02480 retVal = ITYPE_UNKNOWN; 02481 02482 imapParser::parseURL (_url, _box, _section, _type, _uid, _validity, _info); 02483 // kdDebug(7116) << "URL: query - '" << KURL::decode_string(_url.query()) << "'" << endl; 02484 02485 // get the delimiter 02486 TQString myNamespace = namespaceForBox( _box ); 02487 kdDebug(7116) << "IMAP4::parseURL - namespace=" << myNamespace << endl; 02488 if ( namespaceToDelimiter.contains(myNamespace) ) 02489 { 02490 _hierarchyDelimiter = namespaceToDelimiter[myNamespace]; 02491 kdDebug(7116) << "IMAP4::parseURL - delimiter=" << _hierarchyDelimiter << endl; 02492 } 02493 02494 if (!_box.isEmpty ()) 02495 { 02496 kdDebug(7116) << "IMAP4::parseURL - box=" << _box << endl; 02497 02498 if (makeLogin ()) 02499 { 02500 if (getCurrentBox () != _box || 02501 _type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK") 02502 { 02503 if ( cache ) 02504 { 02505 // assume a normal box 02506 retVal = ITYPE_DIR_AND_BOX; 02507 } else 02508 { 02509 // start a listing for the box to get the type 02510 imapCommand *cmd; 02511 02512 cmd = doCommand (imapCommand::clientList ("", _box)); 02513 if (cmd->result () == "OK") 02514 { 02515 for (TQValueListIterator < imapList > it = listResponses.begin (); 02516 it != listResponses.end (); ++it) 02517 { 02518 //kdDebug(7116) << "IMAP4::parseURL - checking " << _box << " to " << (*it).name() << endl; 02519 if (_box == (*it).name ()) 02520 { 02521 if ( !(*it).hierarchyDelimiter().isEmpty() ) 02522 _hierarchyDelimiter = (*it).hierarchyDelimiter(); 02523 if ((*it).noSelect ()) 02524 { 02525 retVal = ITYPE_DIR; 02526 } 02527 else if ((*it).noInferiors ()) 02528 { 02529 retVal = ITYPE_BOX; 02530 } 02531 else 02532 { 02533 retVal = ITYPE_DIR_AND_BOX; 02534 } 02535 } 02536 } 02537 // if we got no list response for the box see if it's a prefix 02538 if ( retVal == ITYPE_UNKNOWN && 02539 namespaceToDelimiter.contains(_box) ) { 02540 retVal = ITYPE_DIR; 02541 } 02542 } else { 02543 kdDebug(7116) << "IMAP4::parseURL - got error for " << _box << endl; 02544 } 02545 completeQueue.removeRef (cmd); 02546 } // cache 02547 } 02548 else // current == box 02549 { 02550 retVal = ITYPE_BOX; 02551 } 02552 } 02553 else 02554 kdDebug(7116) << "IMAP4::parseURL: no login!" << endl; 02555 02556 } 02557 else // empty box 02558 { 02559 // the root is just a dir 02560 kdDebug(7116) << "IMAP4: parseURL: box [root]" << endl; 02561 retVal = ITYPE_DIR; 02562 } 02563 02564 // see if it is a real sequence or a simple uid 02565 if (retVal == ITYPE_BOX || retVal == ITYPE_DIR_AND_BOX) 02566 { 02567 if (!_uid.isEmpty ()) 02568 { 02569 if (_uid.find (':') == -1 && _uid.find (',') == -1 02570 && _uid.find ('*') == -1) 02571 retVal = ITYPE_MSG; 02572 } 02573 } 02574 if (retVal == ITYPE_MSG) 02575 { 02576 if ( (_section.find ("BODY.PEEK[", 0, false) != -1 || 02577 _section.find ("BODY[", 0, false) != -1) && 02578 _section.find(".MIME") == -1 && 02579 _section.find(".HEADER") == -1 ) 02580 retVal = ITYPE_ATTACH; 02581 } 02582 if ( _hierarchyDelimiter.isEmpty() && 02583 (_type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK") ) 02584 { 02585 // this shouldn't happen but when the delimiter is really empty 02586 // we try to reconstruct it from the URL 02587 if (!_box.isEmpty()) 02588 { 02589 int start = _url.path().findRev(_box); 02590 if (start != -1) 02591 _hierarchyDelimiter = _url.path().mid(start-1, start); 02592 kdDebug(7116) << "IMAP4::parseURL - reconstructed delimiter:" << _hierarchyDelimiter 02593 << " from URL " << _url.path() << endl; 02594 } 02595 if (_hierarchyDelimiter.isEmpty()) 02596 _hierarchyDelimiter = "/"; 02597 } 02598 kdDebug(7116) << "IMAP4::parseURL - return " << retVal << endl; 02599 02600 return retVal; 02601 } 02602 02603 int 02604 IMAP4Protocol::outputLine (const TQCString & _str, int len) 02605 { 02606 if (len == -1) { 02607 len = _str.length(); 02608 } 02609 02610 if (cacheOutput) 02611 { 02612 if ( !outputBuffer.isOpen() ) { 02613 outputBuffer.open(IO_WriteOnly); 02614 } 02615 outputBuffer.at(outputBufferIndex); 02616 outputBuffer.writeBlock(_str.data(), len); 02617 outputBufferIndex += len; 02618 return 0; 02619 } 02620 02621 TQByteArray temp; 02622 bool relay = relayEnabled; 02623 02624 relayEnabled = true; 02625 temp.setRawData (_str.data (), len); 02626 parseRelay (temp); 02627 temp.resetRawData (_str.data (), len); 02628 02629 relayEnabled = relay; 02630 return 0; 02631 } 02632 02633 void IMAP4Protocol::flushOutput(TQString contentEncoding) 02634 { 02635 // send out cached data to the application 02636 if (outputBufferIndex == 0) 02637 return; 02638 outputBuffer.close(); 02639 outputCache.resize(outputBufferIndex); 02640 if (decodeContent) 02641 { 02642 // get the coding from the MIME header 02643 TQByteArray decoded; 02644 if (contentEncoding.find("quoted-printable", 0, false) == 0) 02645 decoded = KCodecs::quotedPrintableDecode(outputCache); 02646 else if (contentEncoding.find("base64", 0, false) == 0) 02647 KCodecs::base64Decode(outputCache, decoded); 02648 else 02649 decoded = outputCache; 02650 02651 TQString mimetype = KMimeType::findByContent( decoded )->name(); 02652 kdDebug(7116) << "IMAP4::flushOutput - mimeType " << mimetype << endl; 02653 mimeType(mimetype); 02654 decodeContent = false; 02655 data( decoded ); 02656 } else { 02657 data( outputCache ); 02658 } 02659 mProcessedSize += outputBufferIndex; 02660 processedSize( mProcessedSize ); 02661 outputBufferIndex = 0; 02662 outputCache[0] = '\0'; 02663 outputBuffer.setBuffer(outputCache); 02664 } 02665 02666 ssize_t IMAP4Protocol::myRead(void *data, ssize_t len) 02667 { 02668 if (readBufferLen) 02669 { 02670 ssize_t copyLen = (len < readBufferLen) ? len : readBufferLen; 02671 memcpy(data, readBuffer, copyLen); 02672 readBufferLen -= copyLen; 02673 if (readBufferLen) memmove(readBuffer, &readBuffer[copyLen], readBufferLen); 02674 return copyLen; 02675 } 02676 if (!isConnectionValid()) return 0; 02677 waitForResponse( responseTimeout() ); 02678 return read(data, len); 02679 } 02680 02681 bool 02682 IMAP4Protocol::assureBox (const TQString & aBox, bool readonly) 02683 { 02684 if (aBox.isEmpty()) return false; 02685 02686 imapCommand *cmd = 0; 02687 02688 if (aBox != getCurrentBox () || (!getSelected().readWrite() && !readonly)) 02689 { 02690 // open the box with the appropriate mode 02691 kdDebug(7116) << "IMAP4Protocol::assureBox - opening box" << endl; 02692 selectInfo = imapInfo(); 02693 cmd = doCommand (imapCommand::clientSelect (aBox, readonly)); 02694 bool ok = cmd->result() == "OK"; 02695 TQString cmdInfo = cmd->resultInfo(); 02696 completeQueue.removeRef (cmd); 02697 02698 if (!ok) 02699 { 02700 bool found = false; 02701 cmd = doCommand (imapCommand::clientList ("", aBox)); 02702 if (cmd->result () == "OK") 02703 { 02704 for (TQValueListIterator < imapList > it = listResponses.begin (); 02705 it != listResponses.end (); ++it) 02706 { 02707 if (aBox == (*it).name ()) found = true; 02708 } 02709 } 02710 completeQueue.removeRef (cmd); 02711 if (found) { 02712 if (cmdInfo.find("permission", 0, false) != -1) { 02713 // not allowed to enter this folder 02714 error(ERR_ACCESS_DENIED, cmdInfo); 02715 } else { 02716 error(ERR_SLAVE_DEFINED, i18n("Unable to open folder %1. The server replied: %2").arg(aBox).arg(cmdInfo)); 02717 } 02718 } else { 02719 error(TDEIO::ERR_DOES_NOT_EXIST, aBox); 02720 } 02721 return false; 02722 } 02723 } 02724 else 02725 { 02726 // Give the server a chance to deliver updates every ten seconds. 02727 // Doing this means a server roundtrip and since assureBox is called 02728 // after every mail, we do it with a timeout. 02729 kdDebug(7116) << "IMAP4Protocol::assureBox - reusing box" << endl; 02730 if ( mTimeOfLastNoop.secsTo( TQDateTime::currentDateTime() ) > 10 ) { 02731 cmd = doCommand (imapCommand::clientNoop ()); 02732 completeQueue.removeRef (cmd); 02733 mTimeOfLastNoop = TQDateTime::currentDateTime(); 02734 kdDebug(7116) << "IMAP4Protocol::assureBox - noop timer fired" << endl; 02735 } 02736 } 02737 02738 // if it is the mode we want 02739 if (!getSelected().readWrite() && !readonly) 02740 { 02741 error(TDEIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox); 02742 return false; 02743 } 02744 02745 return true; 02746 }