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