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