00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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 <qbuffer.h>
00082 #include <qdatetime.h>
00083 #include <qregexp.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
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
00156
00157
00158 signal (SIGCHLD, sigchld_handler);
00159 return;
00160 }
00161 }
00162 }
00163
00164 IMAP4Protocol::IMAP4Protocol (const QCString & pool, const QCString & 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 = QDateTime();
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 QString 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
00217 error (ERR_COULD_NOT_READ, _url.prettyURL());
00218 return;
00219 }
00220 else
00221 #endif
00222 {
00223
00224
00225
00226
00227
00228
00229
00230 QString 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
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"))
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
00265 cacheOutput = true;
00266 outputLine
00267 ("Content-Type: multipart/digest; boundary=\"IMAPDIGEST\"\r\n", 55);
00268 if (selectInfo.recentAvailable ())
00269 outputLineStr ("X-Recent: " +
00270 QString::number(selectInfo.recent ()) + "\r\n");
00271 if (selectInfo.countAvailable ())
00272 outputLineStr ("X-Count: " + QString::number(selectInfo.count ()) +
00273 "\r\n");
00274 if (selectInfo.unseenAvailable ())
00275 outputLineStr ("X-Unseen: " +
00276 QString::number(selectInfo.unseen ()) + "\r\n");
00277 if (selectInfo.uidValidityAvailable ())
00278 outputLineStr ("X-uidValidity: " +
00279 QString::number(selectInfo.uidValidity ()) +
00280 "\r\n");
00281 if (selectInfo.uidNextAvailable ())
00282 outputLineStr ("X-UidNext: " +
00283 QString::number(selectInfo.uidNext ()) + "\r\n");
00284 if (selectInfo.flagsAvailable ())
00285 outputLineStr ("X-Flags: " + QString::number(selectInfo.flags ()) +
00286 "\r\n");
00287 if (selectInfo.permanentFlagsAvailable ())
00288 outputLineStr ("X-PermanentFlags: " +
00289 QString::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(QString::null);
00299 cacheOutput = false;
00300 }
00301
00302 if (aEnum == ITYPE_MSG || (aEnum == ITYPE_ATTACH && !decodeContent))
00303 relayEnabled = true;
00304
00305 if (aSequence != "0:0")
00306 {
00307 QString contentEncoding;
00308 if (aEnum == ITYPE_ATTACH && decodeContent)
00309 {
00310
00311 QString 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
00321 if (getLastHandled() && getLastHandled()->getHeader())
00322 contentEncoding = getLastHandled()->getHeader()->getEncoding();
00323
00324
00325
00326
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
00355 outputLine ("--IMAPDIGEST\r\n", 14);
00356 cacheOutput = true;
00357 if (cache && cache->getUid () != 0)
00358 outputLineStr ("X-UID: " +
00359 QString::number(cache->getUid ()) + "\r\n");
00360 if (cache && cache->getSize () != 0)
00361 outputLineStr ("X-Length: " +
00362 QString::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 QString::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 }
00375 }
00376 while (cmd && !cmd->isComplete ());
00377 if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
00378 {
00379
00380 outputLine ("--IMAPDIGEST--\r\n", 16);
00381 }
00382
00383 completeQueue.removeRef (cmd);
00384 }
00385 }
00386
00387
00388 data (QByteArray ());
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 QString myBox, mySequence, myLType, mySection, myValidity, myDelimiter, myInfo;
00411
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 QString 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 QString 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
00451 QValueList<imapList> listResponsesSave = listResponses;
00452 doCommand (imapCommand::clientList ("", listStr, false));
00453 for (QValueListIterator < imapList > it = listResponsesSave.begin ();
00454 it != listResponsesSave.end (); ++it)
00455 {
00456 bool boxOk = false;
00457 for (QValueListIterator < imapList > it2 = listResponses.begin ();
00458 it2 != listResponses.end (); ++it2)
00459 {
00460 if ((*it2).name() == (*it).name())
00461 {
00462 boxOk = true;
00463
00464 (*it) = (*it2);
00465 break;
00466 }
00467 }
00468 if (boxOk)
00469 doListEntry (aURL, myBox, (*it), (mySection != "FOLDERONLY"));
00470 else
00471 kdDebug(7116) << "IMAP4Protocol::listDir - suppress " << (*it).name() << endl;
00472 }
00473 listResponses = listResponsesSave;
00474 }
00475 else
00476 {
00477 for (QValueListIterator < 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 (QString::null);
00499 const QString encodedUrl = aURL.url(0, 106);
00500
00501 if (!_url.query ().isEmpty ())
00502 {
00503 QString 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 QStringList list = getResults ();
00523 int stretch = 0;
00524
00525 if (selectInfo.uidNextAvailable ())
00526 stretch = QString::number(selectInfo.uidNext ()).length ();
00527 UDSEntry entry;
00528 imapCache fake;
00529
00530 for (QStringList::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
00566 KURL newUrl = _url;
00567
00568 newUrl.setPath ("/" + myBox + ";UIDVALIDITY=" +
00569 QString::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 = QString::number(selectInfo.uidNext ()).length ();
00583
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, 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 QString & _host, int _port,
00626 const QString & _user, const QString & _pass)
00627 {
00628 if (myHost != _host || myPort != _port || myUser != _user || myPass != _pass)
00629 {
00630
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 QByteArray & buffer)
00642 {
00643 if (relayEnabled) {
00644
00645 data( buffer );
00646 mProcessedSize += buffer.size();
00647 processedSize( mProcessedSize );
00648 } else if (cacheOutput)
00649 {
00650
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(QByteArray & buffer, ulong len, ulong relay)
00669 {
00670 char buf[8192];
00671 while (buffer.size() < len)
00672 {
00673 ssize_t readLen = myRead(buf, QMIN(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 QByteArray relayData;
00685 ssize_t relbuf = relay - buffer.size();
00686 int currentRelay = QMIN(relbuf, readLen);
00687 relayData.setRawData(buf, currentRelay);
00688 parseRelay(relayData);
00689 relayData.resetRawData(buf, currentRelay);
00690 }
00691 {
00692 QBuffer 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 (QByteArray & 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 QByteArray 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
00723 }
00724
00725 {
00726 QBuffer stream (buffer);
00727
00728 stream.open (IO_WriteOnly);
00729 stream.at (buffer.size ());
00730 stream.writeBlock (readBuffer, copyLen);
00731 stream.close ();
00732
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
00779 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
00780 enum IMAP_TYPE aType =
00781 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
00782
00783
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 QPtrList < QByteArray > bufferList;
00800 int length = 0;
00801
00802 int result;
00803
00804 do
00805 {
00806 QByteArray *buffer = new QByteArray ();
00807 dataReq ();
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
00830 if (!cmd->isComplete () && !getContinuation ().isEmpty ())
00831 {
00832 bool sendOk = true;
00833 ulong wrote = 0;
00834
00835 QByteArray *buffer;
00836
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
00858 while (!cmd->isComplete () && getState() != ISTATE_NO)
00859 parseLoop ();
00860 if ( getState() == ISTATE_NO ) {
00861
00862
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 QString 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
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
00898
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 QString 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
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 QString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
00965 QString 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
00972 if (dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX)
00973 {
00974
00975 int sub = dBox.find (sBox);
00976
00977
00978 if (sub > 0)
00979 {
00980 KURL testDir = dest;
00981
00982 QString subDir = dBox.right (dBox.length () - dBox.findRev ('/'));
00983 QString 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
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
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
01009 imapCommand *cmd = doCommand (imapCommand::clientCreate (topDir));
01010
01011
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
01036 if (!assureBox(sBox, true)) return;
01037 kdDebug(7116) << "IMAP4::copy - " << sBox << " -> " << dBox << endl;
01038
01039
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 QString 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 QString 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
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
01119 imapCommand *cmd = doCommand(imapCommand::clientUnsubscribe(aBox));
01120 completeQueue.removeRef(cmd);
01121 cmd = doCommand(imapCommand::clientDelete (aBox));
01122
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
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
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206
01207
01208
01209
01210
01211 void
01212 IMAP4Protocol::special (const QByteArray & aData)
01213 {
01214 kdDebug(7116) << "IMAP4Protocol::special" << endl;
01215 if (!makeLogin()) return;
01216
01217 QDataStream stream(aData, IO_ReadOnly);
01218
01219 int tmp;
01220 stream >> tmp;
01221
01222 switch (tmp) {
01223 case 'C':
01224 {
01225
01226 KURL src;
01227 KURL dest;
01228 stream >> src >> dest;
01229 copy(src, dest, 0, FALSE);
01230 break;
01231 }
01232 case 'c':
01233 {
01234
01235 infoMessage(imapCapabilities.join(" "));
01236 finished();
01237 break;
01238 }
01239 case 'N':
01240 {
01241
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
01257
01258 infoMessage( imapNamespaces.join(",") );
01259 finished();
01260 break;
01261 }
01262 case 'U':
01263 {
01264
01265 KURL _url;
01266 stream >> _url;
01267 QString 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
01286 KURL _url;
01287 stream >> _url;
01288 QString 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
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
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
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
01343 KURL _url;
01344 QCString newFlags;
01345 stream >> _url >> newFlags;
01346
01347 QString 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
01352 QCString 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
01387 KURL _url;
01388 bool seen;
01389 QCString newFlags;
01390 stream >> _url >> seen;
01391
01392 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01393 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01394 if ( !assureBox(aBox, true) )
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
01418 specialSearchCommand( stream );
01419 break;
01420 }
01421 case 'X':
01422 {
01423
01424 specialCustomCommand( stream );
01425 break;
01426 }
01427 default:
01428 kdWarning(7116) << "Unknown command in special(): " << tmp << endl;
01429 error( ERR_UNSUPPORTED_ACTION, QString(QChar(tmp)) );
01430 break;
01431 }
01432 }
01433
01434 void
01435 IMAP4Protocol::specialACLCommand( int command, QDataStream& stream )
01436 {
01437
01438 KURL _url;
01439 stream >> _url;
01440 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01441 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01442
01443 switch( command ) {
01444 case 'S':
01445 {
01446 QString 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':
01464 {
01465 QString 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':
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
01495
01496
01497
01498 kdDebug(7116) << getResults() << endl;
01499 infoMessage(getResults().join( "\"" ));
01500 finished();
01501 break;
01502 }
01503 case 'L':
01504 {
01505
01506 error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01507 break;
01508 }
01509 case 'M':
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 QStringList 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, QString(QChar(command)) );
01533 }
01534 }
01535
01536 void
01537 IMAP4Protocol::specialSearchCommand( QDataStream& stream )
01538 {
01539 kdDebug(7116) << "IMAP4Protocol::specialSearchCommand" << endl;
01540 KURL _url;
01541 stream >> _url;
01542 QString 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 QStringList 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( QDataStream& stream )
01566 {
01567 kdDebug(7116) << "IMAP4Protocol::specialCustomCommand" << endl;
01568
01569 QString 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 QStringList 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, QString() ));
01606 while ( !parseLoop () ) ;
01607
01608
01609 if (!cmd->isComplete () && !getContinuation ().isEmpty ())
01610 {
01611 const QByteArray buffer = arguments.utf8();
01612
01613
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 QStringList 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, QDataStream& stream )
01645 {
01646
01647 KURL _url;
01648 stream >> _url;
01649 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01650 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01651
01652 switch( command ) {
01653 case 'S':
01654 {
01655
01656
01657
01658
01659 QString entry;
01660 QMap<QString, QString> 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':
01678 {
01679
01680
01681
01682
01683 QString entry;
01684 QStringList 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
01698
01699
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, QString(QChar(command)) );
01708 }
01709 }
01710
01711 void
01712 IMAP4Protocol::specialQuotaCommand( int command, QDataStream& stream )
01713 {
01714
01715 KURL _url;
01716 stream >> _url;
01717 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01718 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01719
01720 switch( command ) {
01721 case 'R':
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':
01738 {
01739 kdDebug(7116) << "GETQUOTA command" << endl;
01740 kdWarning(7116) << "UNIMPLEMENTED" << endl;
01741 break;
01742 }
01743 case 'S':
01744 {
01745 kdDebug(7116) << "SETQUOTA 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, QString(QChar(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 QString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
01760 QString 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
01778 imapCommand *cmd = doCommand (imapCommand::clientClose());
01779 bool ok = cmd->result() == "OK";
01780 completeQueue.removeRef(cmd);
01781 if (!ok)
01782 {
01783 error(ERR_CANNOT_RENAME, i18n("Unable to close mailbox."));
01784 return;
01785 }
01786 setState(ISTATE_LOGIN);
01787 }
01788 imapCommand *cmd = doCommand (imapCommand::clientRename (sBox, dBox));
01789 if (cmd->result () != "OK") {
01790 error (ERR_CANNOT_RENAME, cmd->result ());
01791 completeQueue.removeRef (cmd);
01792 return;
01793 }
01794 completeQueue.removeRef (cmd);
01795 }
01796 break;
01797
01798 case ITYPE_MSG:
01799 case ITYPE_ATTACH:
01800 case ITYPE_UNKNOWN:
01801 error (ERR_CANNOT_RENAME, src.prettyURL());
01802 break;
01803 }
01804 }
01805 else
01806 {
01807 error (ERR_CANNOT_RENAME, src.prettyURL());
01808 return;
01809 }
01810 finished ();
01811 }
01812
01813 void
01814 IMAP4Protocol::slave_status ()
01815 {
01816 bool connected = (getState() != ISTATE_NO) && isConnectionValid();
01817 kdDebug(7116) << "IMAP4::slave_status " << connected << endl;
01818 slaveStatus ( connected ? myHost : QString::null, connected );
01819 }
01820
01821 void
01822 IMAP4Protocol::dispatch (int command, const QByteArray & data)
01823 {
01824 kdDebug(7116) << "IMAP4::dispatch - command=" << command << endl;
01825 KIO::TCPSlaveBase::dispatch (command, data);
01826 }
01827
01828 void
01829 IMAP4Protocol::stat (const KURL & _url)
01830 {
01831 kdDebug(7116) << "IMAP4::stat - " << _url.prettyURL() << endl;
01832 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01833
01834 enum IMAP_TYPE aType =
01835 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter,
01836 aInfo, true);
01837
01838 UDSEntry entry;
01839 UDSAtom atom;
01840
01841 atom.m_uds = UDS_NAME;
01842 atom.m_str = aBox;
01843 entry.append (atom);
01844
01845 if (!aSection.isEmpty())
01846 {
01847 if (getState() == ISTATE_SELECT && aBox == getCurrentBox())
01848 {
01849 imapCommand *cmd = doCommand (imapCommand::clientClose());
01850 bool ok = cmd->result() == "OK";
01851 completeQueue.removeRef(cmd);
01852 if (!ok)
01853 {
01854 error(ERR_COULD_NOT_STAT, aBox);
01855 return;
01856 }
01857 setState(ISTATE_LOGIN);
01858 }
01859 bool ok = false;
01860 QString cmdInfo;
01861 if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
01862 ok = true;
01863 else
01864 {
01865 imapCommand *cmd = doCommand(imapCommand::clientStatus(aBox, aSection));
01866 ok = cmd->result() == "OK";
01867 cmdInfo = cmd->resultInfo();
01868 completeQueue.removeRef(cmd);
01869 }
01870 if (!ok)
01871 {
01872 bool found = false;
01873 imapCommand *cmd = doCommand (imapCommand::clientList ("", aBox));
01874 if (cmd->result () == "OK")
01875 {
01876 for (QValueListIterator < imapList > it = listResponses.begin ();
01877 it != listResponses.end (); ++it)
01878 {
01879 if (aBox == (*it).name ()) found = true;
01880 }
01881 }
01882 completeQueue.removeRef (cmd);
01883 if (found)
01884 error(ERR_COULD_NOT_STAT, aBox);
01885 else
01886 error(KIO::ERR_DOES_NOT_EXIST, aBox);
01887 return;
01888 }
01889 if ((aSection == "UIDNEXT" && getStatus().uidNextAvailable())
01890 || (aSection == "UNSEEN" && getStatus().unseenAvailable()))
01891 {
01892 atom.m_uds = UDS_SIZE;
01893 atom.m_str = QString::null;
01894 atom.m_long = (aSection == "UIDNEXT") ? getStatus().uidNext()
01895 : getStatus().unseen();
01896 entry.append(atom);
01897 }
01898 } else
01899 if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX || aType == ITYPE_MSG ||
01900 aType == ITYPE_ATTACH)
01901 {
01902 ulong validity = 0;
01903
01904 if (aBox == getCurrentBox ())
01905 validity = selectInfo.uidValidity ();
01906 else
01907 {
01908
01909
01910
01911 imapCommand *cmd =
01912 doCommand (imapCommand::clientStatus (aBox, "UIDVALIDITY"));
01913 completeQueue.removeRef (cmd);
01914 validity = getStatus ().uidValidity ();
01915 }
01916 validity = 0;
01917
01918 if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX)
01919 {
01920
01921 if (validity > 0 && validity != aValidity.toULong ())
01922 {
01923
01924 KURL newUrl = _url;
01925
01926 newUrl.setPath ("/" + aBox + ";UIDVALIDITY=" +
01927 QString::number(validity));
01928 kdDebug(7116) << "IMAP4::stat - redirecting to " << newUrl.prettyURL() << endl;
01929 redirection (newUrl);
01930 }
01931 }
01932 else if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
01933 {
01934
01935
01936
01937
01938
01939 if (validity > 0 && validity != aValidity.toULong ())
01940 {
01941 aType = ITYPE_UNKNOWN;
01942 kdDebug(7116) << "IMAP4::stat - url has invalid validity [" << validity << "d] " << _url.prettyURL() << endl;
01943 }
01944 }
01945 }
01946
01947 atom.m_uds = UDS_MIME_TYPE;
01948 atom.m_str = getMimeType (aType);
01949 entry.append (atom);
01950
01951 kdDebug(7116) << "IMAP4: stat: " << atom.m_str << endl;
01952 switch (aType)
01953 {
01954 case ITYPE_DIR:
01955 atom.m_uds = UDS_FILE_TYPE;
01956 atom.m_str = QString::null;
01957 atom.m_long = S_IFDIR;
01958 entry.append (atom);
01959 break;
01960
01961 case ITYPE_BOX:
01962 case ITYPE_DIR_AND_BOX:
01963 atom.m_uds = UDS_FILE_TYPE;
01964 atom.m_str = QString::null;
01965 atom.m_long = S_IFDIR;
01966 entry.append (atom);
01967 break;
01968
01969 case ITYPE_MSG:
01970 case ITYPE_ATTACH:
01971 atom.m_uds = UDS_FILE_TYPE;
01972 atom.m_str = QString::null;
01973 atom.m_long = S_IFREG;
01974 entry.append (atom);
01975 break;
01976
01977 case ITYPE_UNKNOWN:
01978 error (ERR_DOES_NOT_EXIST, _url.prettyURL());
01979 break;
01980 }
01981
01982 statEntry (entry);
01983 kdDebug(7116) << "IMAP4::stat - Finishing stat" << endl;
01984 finished ();
01985 }
01986
01987 void IMAP4Protocol::openConnection()
01988 {
01989 if (makeLogin()) connected();
01990 }
01991
01992 void IMAP4Protocol::closeConnection()
01993 {
01994 if (getState() == ISTATE_NO) return;
01995 if (getState() == ISTATE_SELECT && metaData("expunge") == "auto")
01996 {
01997 imapCommand *cmd = doCommand (imapCommand::clientExpunge());
01998 completeQueue.removeRef (cmd);
01999 }
02000 if (getState() != ISTATE_CONNECT)
02001 {
02002 imapCommand *cmd = doCommand (imapCommand::clientLogout());
02003 completeQueue.removeRef (cmd);
02004 }
02005 closeDescriptor();
02006 setState(ISTATE_NO);
02007 completeQueue.clear();
02008 sentQueue.clear();
02009 lastHandled = 0;
02010 currentBox = QString::null;
02011 readBufferLen = 0;
02012 }
02013
02014 bool IMAP4Protocol::makeLogin ()
02015 {
02016 if (getState () == ISTATE_LOGIN || getState () == ISTATE_SELECT)
02017 return true;
02018
02019 kdDebug(7116) << "IMAP4::makeLogin - checking login" << endl;
02020 bool alreadyConnected = getState() == ISTATE_CONNECT;
02021 kdDebug(7116) << "IMAP4::makeLogin - alreadyConnected " << alreadyConnected << endl;
02022 if (alreadyConnected || connectToHost (myHost.latin1(), myPort))
02023 {
02024
02025
02026 setState(ISTATE_CONNECT);
02027
02028 myAuth = metaData("auth");
02029 myTLS = metaData("tls");
02030 kdDebug(7116) << "myAuth: " << myAuth << endl;
02031
02032 imapCommand *cmd;
02033
02034 unhandled.clear ();
02035 if (!alreadyConnected) while (!parseLoop ()) ;
02036 QString greeting;
02037 if (!unhandled.isEmpty()) greeting = unhandled.first().stripWhiteSpace();
02038 unhandled.clear ();
02039 cmd = doCommand (new imapCommand ("CAPABILITY", ""));
02040
02041 kdDebug(7116) << "IMAP4: setHost: capability" << endl;
02042 for (QStringList::Iterator it = imapCapabilities.begin ();
02043 it != imapCapabilities.end (); ++it)
02044 {
02045 kdDebug(7116) << "'" << (*it) << "'" << endl;
02046 }
02047 completeQueue.removeRef (cmd);
02048
02049 if (!hasCapability("IMAP4") && !hasCapability("IMAP4rev1"))
02050 {
02051 error(ERR_COULD_NOT_LOGIN, i18n("The server %1 supports neither "
02052 "IMAP4 nor IMAP4rev1.\nIt identified itself with: %2")
02053 .arg(myHost).arg(greeting));
02054 closeConnection();
02055 return false;
02056 }
02057
02058 if (metaData("nologin") == "on") return TRUE;
02059
02060 if (myTLS == "on" && !hasCapability(QString("STARTTLS")))
02061 {
02062 error(ERR_COULD_NOT_LOGIN, i18n("The server does not support TLS.\n"
02063 "Disable this security feature to connect unencrypted."));
02064 closeConnection();
02065 return false;
02066 }
02067 if ((myTLS == "on" || (canUseTLS() && myTLS != "off")) &&
02068 hasCapability(QString("STARTTLS")))
02069 {
02070 imapCommand *cmd = doCommand (imapCommand::clientStartTLS());
02071 if (cmd->result () == "OK")
02072 {
02073 completeQueue.removeRef(cmd);
02074 int tlsrc = startTLS();
02075 if (tlsrc == 1)
02076 {
02077 kdDebug(7116) << "TLS mode has been enabled." << endl;
02078 imapCommand *cmd2 = doCommand (new imapCommand ("CAPABILITY", ""));
02079 for (QStringList::Iterator it = imapCapabilities.begin ();
02080 it != imapCapabilities.end (); ++it)
02081 {
02082 kdDebug(7116) << "'" << (*it) << "'" << endl;
02083 }
02084 completeQueue.removeRef (cmd2);
02085 } else {
02086 kdWarning(7116) << "TLS mode setup has failed. Aborting." << endl;
02087 error (ERR_COULD_NOT_LOGIN, i18n("Starting TLS failed."));
02088 closeConnection();
02089 return false;
02090 }
02091 } else completeQueue.removeRef(cmd);
02092 }
02093
02094 if (myAuth.isEmpty () || myAuth == "*") {
02095 if (hasCapability (QString ("LOGINDISABLED"))) {
02096 error (ERR_COULD_NOT_LOGIN, i18n("LOGIN is disabled by the server."));
02097 closeConnection();
02098 return false;
02099 }
02100 }
02101 else {
02102 if (!hasCapability (QString ("AUTH=") + myAuth)) {
02103 error (ERR_COULD_NOT_LOGIN, i18n("The authentication method %1 is not "
02104 "supported by the server.").arg(myAuth));
02105 closeConnection();
02106 return false;
02107 }
02108 }
02109
02110 if ( greeting.contains( QRegExp( "Cyrus IMAP4 v2.1" ) ) ) {
02111 removeCapability( "ANNOTATEMORE" );
02112 }
02113
02114 kdDebug(7116) << "IMAP4::makeLogin - attempting login" << endl;
02115
02116 KIO::AuthInfo authInfo;
02117 authInfo.username = myUser;
02118 authInfo.password = myPass;
02119 authInfo.prompt = i18n ("Username and password for your IMAP account:");
02120
02121 kdDebug(7116) << "IMAP4::makeLogin - open_PassDlg said user=" << myUser << " pass=xx" << endl;
02122
02123 QString resultInfo;
02124 if (myAuth.isEmpty () || myAuth == "*")
02125 {
02126 if (myUser.isEmpty () || myPass.isEmpty ()) {
02127 if(openPassDlg (authInfo)) {
02128 myUser = authInfo.username;
02129 myPass = authInfo.password;
02130 }
02131 }
02132 if (!clientLogin (myUser, myPass, resultInfo))
02133 error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to login. Probably the "
02134 "password is wrong.\nThe server %1 replied:\n%2").arg(myHost).arg(resultInfo));
02135 }
02136 else
02137 {
02138 #ifdef HAVE_LIBSASL2
02139 if (!clientAuthenticate (this, authInfo, myHost, myAuth, mySSL, resultInfo))
02140 error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to authenticate via %1.\n"
02141 "The server %2 replied:\n%3").arg(myAuth).arg(myHost).arg(resultInfo));
02142 else {
02143 myUser = authInfo.username;
02144 myPass = authInfo.password;
02145 }
02146 #else
02147 error(KIO::ERR_COULD_NOT_LOGIN, i18n("SASL authentication is not compiled into kio_imap4."));
02148 #endif
02149 }
02150 if ( hasCapability("NAMESPACE") )
02151 {
02152
02153 cmd = doCommand( imapCommand::clientNamespace() );
02154 if (cmd->result () == "OK")
02155 {
02156 kdDebug(7116) << "makeLogin - registered namespaces" << endl;
02157 }
02158 completeQueue.removeRef (cmd);
02159 }
02160
02161 cmd = doCommand( imapCommand::clientList("", "") );
02162 if (cmd->result () == "OK")
02163 {
02164 QValueListIterator < imapList > it = listResponses.begin();
02165 if ( it == listResponses.end() )
02166 {
02167
02168
02169 completeQueue.removeRef (cmd);
02170 cmd = doCommand( imapCommand::clientList("", "%") );
02171 if (cmd->result () == "OK")
02172 {
02173 it = listResponses.begin();
02174 }
02175 }
02176 if ( it != listResponses.end() )
02177 {
02178 namespaceToDelimiter[QString::null] = (*it).hierarchyDelimiter();
02179 kdDebug(7116) << "makeLogin - delimiter for empty ns='" <<
02180 (*it).hierarchyDelimiter() << "'" << endl;
02181 if ( !hasCapability("NAMESPACE") )
02182 {
02183
02184 QString nsentry = QString::number( 0 ) + "=="
02185 + (*it).hierarchyDelimiter();
02186 imapNamespaces.append( nsentry );
02187 }
02188 }
02189 }
02190 completeQueue.removeRef (cmd);
02191 } else {
02192 kdDebug(7116) << "makeLogin - NO login" << endl;
02193 }
02194
02195 return getState() == ISTATE_LOGIN;
02196 }
02197
02198 void
02199 IMAP4Protocol::parseWriteLine (const QString & aStr)
02200 {
02201
02202 QCString writer = aStr.utf8();
02203 int len = writer.length();
02204
02205
02206 if (len == 0 || (writer[len - 1] != '\n')) {
02207 len += 2;
02208 writer += "\r\n";
02209 }
02210
02211
02212 write(writer.data(), len);
02213 }
02214
02215 QString
02216 IMAP4Protocol::getMimeType (enum IMAP_TYPE aType)
02217 {
02218 switch (aType)
02219 {
02220 case ITYPE_DIR:
02221 return "inode/directory";
02222 break;
02223
02224 case ITYPE_BOX:
02225 return "message/digest";
02226 break;
02227
02228 case ITYPE_DIR_AND_BOX:
02229 return "message/directory";
02230 break;
02231
02232 case ITYPE_MSG:
02233 return "message/rfc822";
02234 break;
02235
02236
02237 case ITYPE_ATTACH:
02238 return "application/octet-stream";
02239 break;
02240
02241 case ITYPE_UNKNOWN:
02242 default:
02243 return "unknown/unknown";
02244 }
02245 }
02246
02247
02248
02249 void
02250 IMAP4Protocol::doListEntry (const KURL & _url, int stretch, imapCache * cache,
02251 bool withFlags, bool withSubject)
02252 {
02253 KURL aURL = _url;
02254 aURL.setQuery (QString::null);
02255 const QString encodedUrl = aURL.url(0, 106);
02256 doListEntry(encodedUrl, stretch, cache, withFlags, withSubject);
02257 }
02258
02259
02260
02261 void
02262 IMAP4Protocol::doListEntry (const QString & encodedUrl, int stretch, imapCache * cache,
02263 bool withFlags, bool withSubject)
02264 {
02265 if (cache)
02266 {
02267 UDSEntry entry;
02268 UDSAtom atom;
02269
02270 entry.clear ();
02271
02272 const QString uid = QString::number(cache->getUid());
02273
02274 atom.m_uds = UDS_NAME;
02275 atom.m_str = uid;
02276 atom.m_long = 0;
02277 if (stretch > 0)
02278 {
02279 atom.m_str = "0000000000000000" + atom.m_str;
02280 atom.m_str = atom.m_str.right (stretch);
02281 }
02282 if (withSubject)
02283 {
02284 mailHeader *header = cache->getHeader();
02285 if (header)
02286 atom.m_str += " " + header->getSubject();
02287 }
02288 entry.append (atom);
02289
02290 atom.m_uds = UDS_URL;
02291 atom.m_str = encodedUrl;
02292 if (atom.m_str[atom.m_str.length () - 1] != '/')
02293 atom.m_str += '/';
02294 atom.m_str += ";UID=" + uid;
02295 atom.m_long = 0;
02296 entry.append (atom);
02297
02298 atom.m_uds = UDS_FILE_TYPE;
02299 atom.m_str = QString::null;
02300 atom.m_long = S_IFREG;
02301 entry.append (atom);
02302
02303 atom.m_uds = UDS_SIZE;
02304 atom.m_long = cache->getSize();
02305 entry.append (atom);
02306
02307 atom.m_uds = UDS_MIME_TYPE;
02308 atom.m_str = "message/rfc822";
02309 atom.m_long = 0;
02310 entry.append (atom);
02311
02312 atom.m_uds = UDS_USER;
02313 atom.m_str = myUser;
02314 entry.append (atom);
02315
02316 atom.m_uds = KIO::UDS_ACCESS;
02317 atom.m_long = (withFlags) ? cache->getFlags() : S_IRUSR | S_IXUSR | S_IWUSR;
02318 entry.append (atom);
02319
02320 listEntry (entry, false);
02321 }
02322 }
02323
02324 void
02325 IMAP4Protocol::doListEntry (const KURL & _url, const QString & myBox,
02326 const imapList & item, bool appendPath)
02327 {
02328 KURL aURL = _url;
02329 aURL.setQuery (QString::null);
02330 UDSEntry entry;
02331 UDSAtom atom;
02332 int hdLen = item.hierarchyDelimiter().length();
02333
02334 {
02335
02336 QString mailboxName = item.name ();
02337
02338
02339 if (mailboxName.find (myBox) == 0 && mailboxName.length() > myBox.length())
02340 {
02341 mailboxName =
02342 mailboxName.right (mailboxName.length () - myBox.length ());
02343 }
02344 if (mailboxName[0] == '/')
02345 mailboxName = mailboxName.right (mailboxName.length () - 1);
02346 if (mailboxName.left(hdLen) == item.hierarchyDelimiter())
02347 mailboxName = mailboxName.right(mailboxName.length () - hdLen);
02348 if (mailboxName.right(hdLen) == item.hierarchyDelimiter())
02349 mailboxName.truncate(mailboxName.length () - hdLen);
02350
02351 atom.m_uds = UDS_NAME;
02352 if (!item.hierarchyDelimiter().isEmpty() &&
02353 mailboxName.find(item.hierarchyDelimiter()) != -1)
02354 atom.m_str = mailboxName.section(item.hierarchyDelimiter(), -1);
02355 else
02356 atom.m_str = mailboxName;
02357
02358
02359 if (atom.m_str.isEmpty ())
02360 atom.m_str = "..";
02361
02362 if (!atom.m_str.isEmpty ())
02363 {
02364 atom.m_long = 0;
02365 entry.append (atom);
02366
02367 if (!item.noSelect ())
02368 {
02369 atom.m_uds = UDS_MIME_TYPE;
02370 if (!item.noInferiors ())
02371 {
02372 atom.m_str = "message/directory";
02373 } else {
02374 atom.m_str = "message/digest";
02375 }
02376 atom.m_long = 0;
02377 entry.append (atom);
02378 mailboxName += '/';
02379
02380
02381 atom.m_uds = UDS_FILE_TYPE;
02382 atom.m_str = QString::null;
02383 atom.m_long = S_IFDIR;
02384 entry.append (atom);
02385 }
02386 else if (!item.noInferiors ())
02387 {
02388 atom.m_uds = UDS_MIME_TYPE;
02389 atom.m_str = "inode/directory";
02390 atom.m_long = 0;
02391 entry.append (atom);
02392 mailboxName += '/';
02393
02394
02395 atom.m_uds = UDS_FILE_TYPE;
02396 atom.m_str = QString::null;
02397 atom.m_long = S_IFDIR;
02398 entry.append (atom);
02399 }
02400 else
02401 {
02402 atom.m_uds = UDS_MIME_TYPE;
02403 atom.m_str = "unknown/unknown";
02404 atom.m_long = 0;
02405 entry.append (atom);
02406 }
02407
02408 atom.m_uds = UDS_URL;
02409 QString path = aURL.path();
02410 atom.m_str = aURL.url (0, 106);
02411 if (appendPath)
02412 {
02413 if (path[path.length() - 1] == '/' && !path.isEmpty() && path != "/")
02414 path.truncate(path.length() - 1);
02415 if (!path.isEmpty() && path != "/"
02416 && path.right(hdLen) != item.hierarchyDelimiter()) {
02417 path += item.hierarchyDelimiter();
02418 }
02419 path += mailboxName;
02420 if (path.upper() == "/INBOX/") {
02421
02422 path = path.upper();
02423 }
02424 }
02425 aURL.setPath(path);
02426 atom.m_str = aURL.url(0, 106);
02427 atom.m_long = 0;
02428 entry.append (atom);
02429
02430 atom.m_uds = UDS_USER;
02431 atom.m_str = myUser;
02432 entry.append (atom);
02433
02434 atom.m_uds = UDS_ACCESS;
02435 atom.m_long = S_IRUSR | S_IXUSR | S_IWUSR;
02436 entry.append (atom);
02437
02438 atom.m_uds = UDS_EXTRA;
02439 atom.m_str = item.attributesAsString();
02440 atom.m_long = 0;
02441 entry.append (atom);
02442
02443 listEntry (entry, false);
02444 }
02445 }
02446 }
02447
02448 enum IMAP_TYPE
02449 IMAP4Protocol::parseURL (const KURL & _url, QString & _box,
02450 QString & _section, QString & _type, QString & _uid,
02451 QString & _validity, QString & _hierarchyDelimiter,
02452 QString & _info, bool cache)
02453 {
02454 enum IMAP_TYPE retVal;
02455 retVal = ITYPE_UNKNOWN;
02456
02457 imapParser::parseURL (_url, _box, _section, _type, _uid, _validity, _info);
02458
02459
02460
02461 QString myNamespace = namespaceForBox( _box );
02462 kdDebug(7116) << "IMAP4::parseURL - namespace=" << myNamespace << endl;
02463 if ( namespaceToDelimiter.contains(myNamespace) )
02464 {
02465 _hierarchyDelimiter = namespaceToDelimiter[myNamespace];
02466 kdDebug(7116) << "IMAP4::parseURL - delimiter=" << _hierarchyDelimiter << endl;
02467 }
02468
02469 if (!_box.isEmpty ())
02470 {
02471 kdDebug(7116) << "IMAP4::parseURL - box=" << _box << endl;
02472
02473 if (makeLogin ())
02474 {
02475 if (getCurrentBox () != _box ||
02476 _type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK")
02477 {
02478 if ( cache )
02479 {
02480
02481 retVal = ITYPE_DIR_AND_BOX;
02482 } else
02483 {
02484
02485 imapCommand *cmd;
02486
02487 cmd = doCommand (imapCommand::clientList ("", _box));
02488 if (cmd->result () == "OK")
02489 {
02490 for (QValueListIterator < imapList > it = listResponses.begin ();
02491 it != listResponses.end (); ++it)
02492 {
02493
02494 if (_box == (*it).name ())
02495 {
02496 if ( !(*it).hierarchyDelimiter().isEmpty() )
02497 _hierarchyDelimiter = (*it).hierarchyDelimiter();
02498 if ((*it).noSelect ())
02499 {
02500 retVal = ITYPE_DIR;
02501 }
02502 else if ((*it).noInferiors ())
02503 {
02504 retVal = ITYPE_BOX;
02505 }
02506 else
02507 {
02508 retVal = ITYPE_DIR_AND_BOX;
02509 }
02510 }
02511 }
02512
02513 if ( retVal == ITYPE_UNKNOWN &&
02514 namespaceToDelimiter.contains(_box) ) {
02515 retVal = ITYPE_DIR;
02516 }
02517 } else {
02518 kdDebug(7116) << "IMAP4::parseURL - got error for " << _box << endl;
02519 }
02520 completeQueue.removeRef (cmd);
02521 }
02522 }
02523 else
02524 {
02525 retVal = ITYPE_BOX;
02526 }
02527 }
02528 else
02529 kdDebug(7116) << "IMAP4::parseURL: no login!" << endl;
02530
02531 }
02532 else
02533 {
02534
02535 kdDebug(7116) << "IMAP4: parseURL: box [root]" << endl;
02536 retVal = ITYPE_DIR;
02537 }
02538
02539
02540 if (retVal == ITYPE_BOX || retVal == ITYPE_DIR_AND_BOX)
02541 {
02542 if (!_uid.isEmpty ())
02543 {
02544 if (_uid.find (':') == -1 && _uid.find (',') == -1
02545 && _uid.find ('*') == -1)
02546 retVal = ITYPE_MSG;
02547 }
02548 }
02549 if (retVal == ITYPE_MSG)
02550 {
02551 if ( (_section.find ("BODY.PEEK[", 0, false) != -1 ||
02552 _section.find ("BODY[", 0, false) != -1) &&
02553 _section.find(".MIME") == -1 &&
02554 _section.find(".HEADER") == -1 )
02555 retVal = ITYPE_ATTACH;
02556 }
02557 if ( _hierarchyDelimiter.isEmpty() &&
02558 (_type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK") )
02559 {
02560
02561
02562 if (!_box.isEmpty())
02563 {
02564 int start = _url.path().findRev(_box);
02565 if (start != -1)
02566 _hierarchyDelimiter = _url.path().mid(start-1, start);
02567 kdDebug(7116) << "IMAP4::parseURL - reconstructed delimiter:" << _hierarchyDelimiter
02568 << " from URL " << _url.path() << endl;
02569 }
02570 if (_hierarchyDelimiter.isEmpty())
02571 _hierarchyDelimiter = "/";
02572 }
02573 kdDebug(7116) << "IMAP4::parseURL - return " << retVal << endl;
02574
02575 return retVal;
02576 }
02577
02578 int
02579 IMAP4Protocol::outputLine (const QCString & _str, int len)
02580 {
02581 if (len == -1) {
02582 len = _str.length();
02583 }
02584
02585 if (cacheOutput)
02586 {
02587 if ( !outputBuffer.isOpen() ) {
02588 outputBuffer.open(IO_WriteOnly);
02589 }
02590 outputBuffer.at(outputBufferIndex);
02591 outputBuffer.writeBlock(_str.data(), len);
02592 outputBufferIndex += len;
02593 return 0;
02594 }
02595
02596 QByteArray temp;
02597 bool relay = relayEnabled;
02598
02599 relayEnabled = true;
02600 temp.setRawData (_str.data (), len);
02601 parseRelay (temp);
02602 temp.resetRawData (_str.data (), len);
02603
02604 relayEnabled = relay;
02605 return 0;
02606 }
02607
02608 void IMAP4Protocol::flushOutput(QString contentEncoding)
02609 {
02610
02611 if (outputBufferIndex == 0)
02612 return;
02613 outputBuffer.close();
02614 outputCache.resize(outputBufferIndex);
02615 if (decodeContent)
02616 {
02617
02618 QByteArray decoded;
02619 if (contentEncoding.find("quoted-printable", 0, false) == 0)
02620 decoded = KCodecs::quotedPrintableDecode(outputCache);
02621 else if (contentEncoding.find("base64", 0, false) == 0)
02622 KCodecs::base64Decode(outputCache, decoded);
02623 else
02624 decoded = outputCache;
02625
02626 QString mimetype = KMimeType::findByContent( decoded )->name();
02627 kdDebug(7116) << "IMAP4::flushOutput - mimeType " << mimetype << endl;
02628 mimeType(mimetype);
02629 decodeContent = false;
02630 data( decoded );
02631 } else {
02632 data( outputCache );
02633 }
02634 mProcessedSize += outputBufferIndex;
02635 processedSize( mProcessedSize );
02636 outputBufferIndex = 0;
02637 outputCache[0] = '\0';
02638 outputBuffer.setBuffer(outputCache);
02639 }
02640
02641 ssize_t IMAP4Protocol::myRead(void *data, ssize_t len)
02642 {
02643 if (readBufferLen)
02644 {
02645 ssize_t copyLen = (len < readBufferLen) ? len : readBufferLen;
02646 memcpy(data, readBuffer, copyLen);
02647 readBufferLen -= copyLen;
02648 if (readBufferLen) memcpy(readBuffer, &readBuffer[copyLen], readBufferLen);
02649 return copyLen;
02650 }
02651 if (!isConnectionValid()) return 0;
02652 waitForResponse( responseTimeout() );
02653 return read(data, len);
02654 }
02655
02656 bool
02657 IMAP4Protocol::assureBox (const QString & aBox, bool readonly)
02658 {
02659 if (aBox.isEmpty()) return false;
02660
02661 imapCommand *cmd = 0;
02662
02663 if (aBox != getCurrentBox () || (!getSelected().readWrite() && !readonly))
02664 {
02665
02666 kdDebug(7116) << "IMAP4Protocol::assureBox - opening box" << endl;
02667 selectInfo = imapInfo();
02668 cmd = doCommand (imapCommand::clientSelect (aBox, readonly));
02669 bool ok = cmd->result() == "OK";
02670 QString cmdInfo = cmd->resultInfo();
02671 completeQueue.removeRef (cmd);
02672
02673 if (!ok)
02674 {
02675 bool found = false;
02676 cmd = doCommand (imapCommand::clientList ("", aBox));
02677 if (cmd->result () == "OK")
02678 {
02679 for (QValueListIterator < imapList > it = listResponses.begin ();
02680 it != listResponses.end (); ++it)
02681 {
02682 if (aBox == (*it).name ()) found = true;
02683 }
02684 }
02685 completeQueue.removeRef (cmd);
02686 if (found) {
02687 if (cmdInfo.find("permission", 0, false) != -1) {
02688
02689 error(ERR_ACCESS_DENIED, cmdInfo);
02690 } else {
02691 error(ERR_SLAVE_DEFINED, i18n("Unable to open folder %1. The server replied: %2").arg(aBox).arg(cmdInfo));
02692 }
02693 } else {
02694 error(KIO::ERR_DOES_NOT_EXIST, aBox);
02695 }
02696 return false;
02697 }
02698 }
02699 else
02700 {
02701
02702
02703
02704 kdDebug(7116) << "IMAP4Protocol::assureBox - reusing box" << endl;
02705 if ( mTimeOfLastNoop.secsTo( QDateTime::currentDateTime() ) > 10 ) {
02706 cmd = doCommand (imapCommand::clientNoop ());
02707 completeQueue.removeRef (cmd);
02708 mTimeOfLastNoop = QDateTime::currentDateTime();
02709 kdDebug(7116) << "IMAP4Protocol::assureBox - noop timer fired" << endl;
02710 }
02711 }
02712
02713
02714 if (!getSelected().readWrite() && !readonly)
02715 {
02716 error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox);
02717 return false;
02718 }
02719
02720 return true;
02721 }