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 kdWarning(7116) << "Unable to close mailbox!" << endl;
01784 error(ERR_CANNOT_RENAME, src.path());
01785 return;
01786 }
01787 setState(ISTATE_LOGIN);
01788 }
01789 imapCommand *cmd = doCommand (imapCommand::clientRename (sBox, dBox));
01790 if (cmd->result () != "OK") {
01791 error (ERR_CANNOT_RENAME, src.path());
01792 completeQueue.removeRef (cmd);
01793 return;
01794 }
01795 completeQueue.removeRef (cmd);
01796 }
01797 break;
01798
01799 case ITYPE_MSG:
01800 case ITYPE_ATTACH:
01801 case ITYPE_UNKNOWN:
01802 error (ERR_CANNOT_RENAME, src.path());
01803 break;
01804 }
01805 }
01806 else
01807 {
01808 error (ERR_CANNOT_RENAME, src.path());
01809 return;
01810 }
01811 finished ();
01812 }
01813
01814 void
01815 IMAP4Protocol::slave_status ()
01816 {
01817 bool connected = (getState() != ISTATE_NO) && isConnectionValid();
01818 kdDebug(7116) << "IMAP4::slave_status " << connected << endl;
01819 slaveStatus ( connected ? myHost : QString::null, connected );
01820 }
01821
01822 void
01823 IMAP4Protocol::dispatch (int command, const QByteArray & data)
01824 {
01825 kdDebug(7116) << "IMAP4::dispatch - command=" << command << endl;
01826 KIO::TCPSlaveBase::dispatch (command, data);
01827 }
01828
01829 void
01830 IMAP4Protocol::stat (const KURL & _url)
01831 {
01832 kdDebug(7116) << "IMAP4::stat - " << _url.prettyURL() << endl;
01833 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01834
01835 enum IMAP_TYPE aType =
01836 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter,
01837 aInfo, true);
01838
01839 UDSEntry entry;
01840 UDSAtom atom;
01841
01842 atom.m_uds = UDS_NAME;
01843 atom.m_str = aBox;
01844 entry.append (atom);
01845
01846 if (!aSection.isEmpty())
01847 {
01848 if (getState() == ISTATE_SELECT && aBox == getCurrentBox())
01849 {
01850 imapCommand *cmd = doCommand (imapCommand::clientClose());
01851 bool ok = cmd->result() == "OK";
01852 completeQueue.removeRef(cmd);
01853 if (!ok)
01854 {
01855 error(ERR_COULD_NOT_STAT, aBox);
01856 return;
01857 }
01858 setState(ISTATE_LOGIN);
01859 }
01860 bool ok = false;
01861 QString cmdInfo;
01862 if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
01863 ok = true;
01864 else
01865 {
01866 imapCommand *cmd = doCommand(imapCommand::clientStatus(aBox, aSection));
01867 ok = cmd->result() == "OK";
01868 cmdInfo = cmd->resultInfo();
01869 completeQueue.removeRef(cmd);
01870 }
01871 if (!ok)
01872 {
01873 bool found = false;
01874 imapCommand *cmd = doCommand (imapCommand::clientList ("", aBox));
01875 if (cmd->result () == "OK")
01876 {
01877 for (QValueListIterator < imapList > it = listResponses.begin ();
01878 it != listResponses.end (); ++it)
01879 {
01880 if (aBox == (*it).name ()) found = true;
01881 }
01882 }
01883 completeQueue.removeRef (cmd);
01884 if (found)
01885 error(ERR_COULD_NOT_STAT, aBox);
01886 else
01887 error(KIO::ERR_DOES_NOT_EXIST, aBox);
01888 return;
01889 }
01890 if ((aSection == "UIDNEXT" && getStatus().uidNextAvailable())
01891 || (aSection == "UNSEEN" && getStatus().unseenAvailable()))
01892 {
01893 atom.m_uds = UDS_SIZE;
01894 atom.m_str = QString::null;
01895 atom.m_long = (aSection == "UIDNEXT") ? getStatus().uidNext()
01896 : getStatus().unseen();
01897 entry.append(atom);
01898 }
01899 } else
01900 if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX || aType == ITYPE_MSG ||
01901 aType == ITYPE_ATTACH)
01902 {
01903 ulong validity = 0;
01904
01905 if (aBox == getCurrentBox ())
01906 validity = selectInfo.uidValidity ();
01907 else
01908 {
01909
01910
01911
01912 imapCommand *cmd =
01913 doCommand (imapCommand::clientStatus (aBox, "UIDVALIDITY"));
01914 completeQueue.removeRef (cmd);
01915 validity = getStatus ().uidValidity ();
01916 }
01917 validity = 0;
01918
01919 if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX)
01920 {
01921
01922 if (validity > 0 && validity != aValidity.toULong ())
01923 {
01924
01925 KURL newUrl = _url;
01926
01927 newUrl.setPath ("/" + aBox + ";UIDVALIDITY=" +
01928 QString::number(validity));
01929 kdDebug(7116) << "IMAP4::stat - redirecting to " << newUrl.prettyURL() << endl;
01930 redirection (newUrl);
01931 }
01932 }
01933 else if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
01934 {
01935
01936
01937
01938
01939
01940 if (validity > 0 && validity != aValidity.toULong ())
01941 {
01942 aType = ITYPE_UNKNOWN;
01943 kdDebug(7116) << "IMAP4::stat - url has invalid validity [" << validity << "d] " << _url.prettyURL() << endl;
01944 }
01945 }
01946 }
01947
01948 atom.m_uds = UDS_MIME_TYPE;
01949 atom.m_str = getMimeType (aType);
01950 entry.append (atom);
01951
01952 kdDebug(7116) << "IMAP4: stat: " << atom.m_str << endl;
01953 switch (aType)
01954 {
01955 case ITYPE_DIR:
01956 atom.m_uds = UDS_FILE_TYPE;
01957 atom.m_str = QString::null;
01958 atom.m_long = S_IFDIR;
01959 entry.append (atom);
01960 break;
01961
01962 case ITYPE_BOX:
01963 case ITYPE_DIR_AND_BOX:
01964 atom.m_uds = UDS_FILE_TYPE;
01965 atom.m_str = QString::null;
01966 atom.m_long = S_IFDIR;
01967 entry.append (atom);
01968 break;
01969
01970 case ITYPE_MSG:
01971 case ITYPE_ATTACH:
01972 atom.m_uds = UDS_FILE_TYPE;
01973 atom.m_str = QString::null;
01974 atom.m_long = S_IFREG;
01975 entry.append (atom);
01976 break;
01977
01978 case ITYPE_UNKNOWN:
01979 error (ERR_DOES_NOT_EXIST, _url.prettyURL());
01980 break;
01981 }
01982
01983 statEntry (entry);
01984 kdDebug(7116) << "IMAP4::stat - Finishing stat" << endl;
01985 finished ();
01986 }
01987
01988 void IMAP4Protocol::openConnection()
01989 {
01990 if (makeLogin()) connected();
01991 }
01992
01993 void IMAP4Protocol::closeConnection()
01994 {
01995 if (getState() == ISTATE_NO) return;
01996 if (getState() == ISTATE_SELECT && metaData("expunge") == "auto")
01997 {
01998 imapCommand *cmd = doCommand (imapCommand::clientExpunge());
01999 completeQueue.removeRef (cmd);
02000 }
02001 if (getState() != ISTATE_CONNECT)
02002 {
02003 imapCommand *cmd = doCommand (imapCommand::clientLogout());
02004 completeQueue.removeRef (cmd);
02005 }
02006 closeDescriptor();
02007 setState(ISTATE_NO);
02008 completeQueue.clear();
02009 sentQueue.clear();
02010 lastHandled = 0;
02011 currentBox = QString::null;
02012 readBufferLen = 0;
02013 }
02014
02015 bool IMAP4Protocol::makeLogin ()
02016 {
02017 if (getState () == ISTATE_LOGIN || getState () == ISTATE_SELECT)
02018 return true;
02019
02020 kdDebug(7116) << "IMAP4::makeLogin - checking login" << endl;
02021 bool alreadyConnected = getState() == ISTATE_CONNECT;
02022 kdDebug(7116) << "IMAP4::makeLogin - alreadyConnected " << alreadyConnected << endl;
02023 if (alreadyConnected || connectToHost (myHost.latin1(), myPort))
02024 {
02025
02026
02027 setState(ISTATE_CONNECT);
02028
02029 myAuth = metaData("auth");
02030 myTLS = metaData("tls");
02031 kdDebug(7116) << "myAuth: " << myAuth << endl;
02032
02033 imapCommand *cmd;
02034
02035 unhandled.clear ();
02036 if (!alreadyConnected) while (!parseLoop ()) ;
02037 QString greeting;
02038 if (!unhandled.isEmpty()) greeting = unhandled.first().stripWhiteSpace();
02039 unhandled.clear ();
02040 cmd = doCommand (new imapCommand ("CAPABILITY", ""));
02041
02042 kdDebug(7116) << "IMAP4: setHost: capability" << endl;
02043 for (QStringList::Iterator it = imapCapabilities.begin ();
02044 it != imapCapabilities.end (); ++it)
02045 {
02046 kdDebug(7116) << "'" << (*it) << "'" << endl;
02047 }
02048 completeQueue.removeRef (cmd);
02049
02050 if (!hasCapability("IMAP4") && !hasCapability("IMAP4rev1"))
02051 {
02052 error(ERR_COULD_NOT_LOGIN, i18n("The server %1 supports neither "
02053 "IMAP4 nor IMAP4rev1.\nIt identified itself with: %2")
02054 .arg(myHost).arg(greeting));
02055 closeConnection();
02056 return false;
02057 }
02058
02059 if (metaData("nologin") == "on") return TRUE;
02060
02061 if (myTLS == "on" && !hasCapability(QString("STARTTLS")))
02062 {
02063 error(ERR_COULD_NOT_LOGIN, i18n("The server does not support TLS.\n"
02064 "Disable this security feature to connect unencrypted."));
02065 closeConnection();
02066 return false;
02067 }
02068 if ((myTLS == "on" || (canUseTLS() && myTLS != "off")) &&
02069 hasCapability(QString("STARTTLS")))
02070 {
02071 imapCommand *cmd = doCommand (imapCommand::clientStartTLS());
02072 if (cmd->result () == "OK")
02073 {
02074 completeQueue.removeRef(cmd);
02075 int tlsrc = startTLS();
02076 if (tlsrc == 1)
02077 {
02078 kdDebug(7116) << "TLS mode has been enabled." << endl;
02079 imapCommand *cmd2 = doCommand (new imapCommand ("CAPABILITY", ""));
02080 for (QStringList::Iterator it = imapCapabilities.begin ();
02081 it != imapCapabilities.end (); ++it)
02082 {
02083 kdDebug(7116) << "'" << (*it) << "'" << endl;
02084 }
02085 completeQueue.removeRef (cmd2);
02086 } else {
02087 kdWarning(7116) << "TLS mode setup has failed. Aborting." << endl;
02088 error (ERR_COULD_NOT_LOGIN, i18n("Starting TLS failed."));
02089 closeConnection();
02090 return false;
02091 }
02092 } else completeQueue.removeRef(cmd);
02093 }
02094
02095 if (myAuth.isEmpty () || myAuth == "*") {
02096 if (hasCapability (QString ("LOGINDISABLED"))) {
02097 error (ERR_COULD_NOT_LOGIN, i18n("LOGIN is disabled by the server."));
02098 closeConnection();
02099 return false;
02100 }
02101 }
02102 else {
02103 if (!hasCapability (QString ("AUTH=") + myAuth)) {
02104 error (ERR_COULD_NOT_LOGIN, i18n("The authentication method %1 is not "
02105 "supported by the server.").arg(myAuth));
02106 closeConnection();
02107 return false;
02108 }
02109 }
02110
02111 if ( greeting.contains( QRegExp( "Cyrus IMAP4 v2.1" ) ) ) {
02112 removeCapability( "ANNOTATEMORE" );
02113 }
02114
02115
02116 QRegExp regExp( "Cyrus\\sIMAP[4]{0,1}\\sv(\\d+)\\.(\\d+)\\.(\\d+)", false );
02117 if ( regExp.search( greeting ) >= 0 ) {
02118 const int major = regExp.cap( 1 ).toInt();
02119 const int minor = regExp.cap( 2 ).toInt();
02120 const int patch = regExp.cap( 3 ).toInt();
02121 if ( major > 2 || (major == 2 && (minor > 3 || (minor == 3 && patch > 9))) ) {
02122 kdDebug(7116) << k_funcinfo << "Cyrus IMAP >= 2.3.9 detected, enabling shared seen flag support" << endl;
02123 imapCapabilities.append( "x-kmail-sharedseen" );
02124 }
02125 }
02126
02127 kdDebug(7116) << "IMAP4::makeLogin - attempting login" << endl;
02128
02129 KIO::AuthInfo authInfo;
02130 authInfo.username = myUser;
02131 authInfo.password = myPass;
02132 authInfo.prompt = i18n ("Username and password for your IMAP account:");
02133
02134 kdDebug(7116) << "IMAP4::makeLogin - open_PassDlg said user=" << myUser << " pass=xx" << endl;
02135
02136 QString resultInfo;
02137 if (myAuth.isEmpty () || myAuth == "*")
02138 {
02139 if (myUser.isEmpty () || myPass.isEmpty ()) {
02140 if(openPassDlg (authInfo)) {
02141 myUser = authInfo.username;
02142 myPass = authInfo.password;
02143 }
02144 }
02145 if (!clientLogin (myUser, myPass, resultInfo))
02146 error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to login. Probably the "
02147 "password is wrong.\nThe server %1 replied:\n%2").arg(myHost).arg(resultInfo));
02148 }
02149 else
02150 {
02151 #ifdef HAVE_LIBSASL2
02152 if (!clientAuthenticate (this, authInfo, myHost, myAuth, mySSL, resultInfo))
02153 error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to authenticate via %1.\n"
02154 "The server %2 replied:\n%3").arg(myAuth).arg(myHost).arg(resultInfo));
02155 else {
02156 myUser = authInfo.username;
02157 myPass = authInfo.password;
02158 }
02159 #else
02160 error(KIO::ERR_COULD_NOT_LOGIN, i18n("SASL authentication is not compiled into kio_imap4."));
02161 #endif
02162 }
02163 if ( hasCapability("NAMESPACE") )
02164 {
02165
02166 cmd = doCommand( imapCommand::clientNamespace() );
02167 if (cmd->result () == "OK")
02168 {
02169 kdDebug(7116) << "makeLogin - registered namespaces" << endl;
02170 }
02171 completeQueue.removeRef (cmd);
02172 }
02173
02174 cmd = doCommand( imapCommand::clientList("", "") );
02175 if (cmd->result () == "OK")
02176 {
02177 QValueListIterator < imapList > it = listResponses.begin();
02178 if ( it == listResponses.end() )
02179 {
02180
02181
02182 completeQueue.removeRef (cmd);
02183 cmd = doCommand( imapCommand::clientList("", "%") );
02184 if (cmd->result () == "OK")
02185 {
02186 it = listResponses.begin();
02187 }
02188 }
02189 if ( it != listResponses.end() )
02190 {
02191 namespaceToDelimiter[QString::null] = (*it).hierarchyDelimiter();
02192 kdDebug(7116) << "makeLogin - delimiter for empty ns='" <<
02193 (*it).hierarchyDelimiter() << "'" << endl;
02194 if ( !hasCapability("NAMESPACE") )
02195 {
02196
02197 QString nsentry = QString::number( 0 ) + "=="
02198 + (*it).hierarchyDelimiter();
02199 imapNamespaces.append( nsentry );
02200 }
02201 }
02202 }
02203 completeQueue.removeRef (cmd);
02204 } else {
02205 kdDebug(7116) << "makeLogin - NO login" << endl;
02206 }
02207
02208 return getState() == ISTATE_LOGIN;
02209 }
02210
02211 void
02212 IMAP4Protocol::parseWriteLine (const QString & aStr)
02213 {
02214
02215 QCString writer = aStr.utf8();
02216 int len = writer.length();
02217
02218
02219 if (len == 0 || (writer[len - 1] != '\n')) {
02220 len += 2;
02221 writer += "\r\n";
02222 }
02223
02224
02225 write(writer.data(), len);
02226 }
02227
02228 QString
02229 IMAP4Protocol::getMimeType (enum IMAP_TYPE aType)
02230 {
02231 switch (aType)
02232 {
02233 case ITYPE_DIR:
02234 return "inode/directory";
02235 break;
02236
02237 case ITYPE_BOX:
02238 return "message/digest";
02239 break;
02240
02241 case ITYPE_DIR_AND_BOX:
02242 return "message/directory";
02243 break;
02244
02245 case ITYPE_MSG:
02246 return "message/rfc822";
02247 break;
02248
02249
02250 case ITYPE_ATTACH:
02251 return "application/octet-stream";
02252 break;
02253
02254 case ITYPE_UNKNOWN:
02255 default:
02256 return "unknown/unknown";
02257 }
02258 }
02259
02260
02261
02262 void
02263 IMAP4Protocol::doListEntry (const KURL & _url, int stretch, imapCache * cache,
02264 bool withFlags, bool withSubject)
02265 {
02266 KURL aURL = _url;
02267 aURL.setQuery (QString::null);
02268 const QString encodedUrl = aURL.url(0, 106);
02269 doListEntry(encodedUrl, stretch, cache, withFlags, withSubject);
02270 }
02271
02272
02273
02274 void
02275 IMAP4Protocol::doListEntry (const QString & encodedUrl, int stretch, imapCache * cache,
02276 bool withFlags, bool withSubject)
02277 {
02278 if (cache)
02279 {
02280 UDSEntry entry;
02281 UDSAtom atom;
02282
02283 entry.clear ();
02284
02285 const QString uid = QString::number(cache->getUid());
02286
02287 atom.m_uds = UDS_NAME;
02288 atom.m_str = uid;
02289 atom.m_long = 0;
02290 if (stretch > 0)
02291 {
02292 atom.m_str = "0000000000000000" + atom.m_str;
02293 atom.m_str = atom.m_str.right (stretch);
02294 }
02295 if (withSubject)
02296 {
02297 mailHeader *header = cache->getHeader();
02298 if (header)
02299 atom.m_str += " " + header->getSubject();
02300 }
02301 entry.append (atom);
02302
02303 atom.m_uds = UDS_URL;
02304 atom.m_str = encodedUrl;
02305 if (atom.m_str[atom.m_str.length () - 1] != '/')
02306 atom.m_str += '/';
02307 atom.m_str += ";UID=" + uid;
02308 atom.m_long = 0;
02309 entry.append (atom);
02310
02311 atom.m_uds = UDS_FILE_TYPE;
02312 atom.m_str = QString::null;
02313 atom.m_long = S_IFREG;
02314 entry.append (atom);
02315
02316 atom.m_uds = UDS_SIZE;
02317 atom.m_long = cache->getSize();
02318 entry.append (atom);
02319
02320 atom.m_uds = UDS_MIME_TYPE;
02321 atom.m_str = "message/rfc822";
02322 atom.m_long = 0;
02323 entry.append (atom);
02324
02325 atom.m_uds = UDS_USER;
02326 atom.m_str = myUser;
02327 entry.append (atom);
02328
02329 atom.m_uds = KIO::UDS_ACCESS;
02330 atom.m_long = (withFlags) ? cache->getFlags() : S_IRUSR | S_IXUSR | S_IWUSR;
02331 entry.append (atom);
02332
02333 listEntry (entry, false);
02334 }
02335 }
02336
02337 void
02338 IMAP4Protocol::doListEntry (const KURL & _url, const QString & myBox,
02339 const imapList & item, bool appendPath)
02340 {
02341 KURL aURL = _url;
02342 aURL.setQuery (QString::null);
02343 UDSEntry entry;
02344 UDSAtom atom;
02345 int hdLen = item.hierarchyDelimiter().length();
02346
02347 {
02348
02349 QString mailboxName = item.name ();
02350
02351
02352 if (mailboxName.find (myBox) == 0 && mailboxName.length() > myBox.length())
02353 {
02354 mailboxName =
02355 mailboxName.right (mailboxName.length () - myBox.length ());
02356 }
02357 if (mailboxName[0] == '/')
02358 mailboxName = mailboxName.right (mailboxName.length () - 1);
02359 if (mailboxName.left(hdLen) == item.hierarchyDelimiter())
02360 mailboxName = mailboxName.right(mailboxName.length () - hdLen);
02361 if (mailboxName.right(hdLen) == item.hierarchyDelimiter())
02362 mailboxName.truncate(mailboxName.length () - hdLen);
02363
02364 atom.m_uds = UDS_NAME;
02365 if (!item.hierarchyDelimiter().isEmpty() &&
02366 mailboxName.find(item.hierarchyDelimiter()) != -1)
02367 atom.m_str = mailboxName.section(item.hierarchyDelimiter(), -1);
02368 else
02369 atom.m_str = mailboxName;
02370
02371
02372 if (atom.m_str.isEmpty ())
02373 atom.m_str = "..";
02374
02375 if (!atom.m_str.isEmpty ())
02376 {
02377 atom.m_long = 0;
02378 entry.append (atom);
02379
02380 if (!item.noSelect ())
02381 {
02382 atom.m_uds = UDS_MIME_TYPE;
02383 if (!item.noInferiors ())
02384 {
02385 atom.m_str = "message/directory";
02386 } else {
02387 atom.m_str = "message/digest";
02388 }
02389 atom.m_long = 0;
02390 entry.append (atom);
02391 mailboxName += '/';
02392
02393
02394 atom.m_uds = UDS_FILE_TYPE;
02395 atom.m_str = QString::null;
02396 atom.m_long = S_IFDIR;
02397 entry.append (atom);
02398 }
02399 else if (!item.noInferiors ())
02400 {
02401 atom.m_uds = UDS_MIME_TYPE;
02402 atom.m_str = "inode/directory";
02403 atom.m_long = 0;
02404 entry.append (atom);
02405 mailboxName += '/';
02406
02407
02408 atom.m_uds = UDS_FILE_TYPE;
02409 atom.m_str = QString::null;
02410 atom.m_long = S_IFDIR;
02411 entry.append (atom);
02412 }
02413 else
02414 {
02415 atom.m_uds = UDS_MIME_TYPE;
02416 atom.m_str = "unknown/unknown";
02417 atom.m_long = 0;
02418 entry.append (atom);
02419 }
02420
02421 atom.m_uds = UDS_URL;
02422 QString path = aURL.path();
02423 atom.m_str = aURL.url (0, 106);
02424 if (appendPath)
02425 {
02426 if (path[path.length() - 1] == '/' && !path.isEmpty() && path != "/")
02427 path.truncate(path.length() - 1);
02428 if (!path.isEmpty() && path != "/"
02429 && path.right(hdLen) != item.hierarchyDelimiter()) {
02430 path += item.hierarchyDelimiter();
02431 }
02432 path += mailboxName;
02433 if (path.upper() == "/INBOX/") {
02434
02435 path = path.upper();
02436 }
02437 }
02438 aURL.setPath(path);
02439 atom.m_str = aURL.url(0, 106);
02440 atom.m_long = 0;
02441 entry.append (atom);
02442
02443 atom.m_uds = UDS_USER;
02444 atom.m_str = myUser;
02445 entry.append (atom);
02446
02447 atom.m_uds = UDS_ACCESS;
02448 atom.m_long = S_IRUSR | S_IXUSR | S_IWUSR;
02449 entry.append (atom);
02450
02451 atom.m_uds = UDS_EXTRA;
02452 atom.m_str = item.attributesAsString();
02453 atom.m_long = 0;
02454 entry.append (atom);
02455
02456 listEntry (entry, false);
02457 }
02458 }
02459 }
02460
02461 enum IMAP_TYPE
02462 IMAP4Protocol::parseURL (const KURL & _url, QString & _box,
02463 QString & _section, QString & _type, QString & _uid,
02464 QString & _validity, QString & _hierarchyDelimiter,
02465 QString & _info, bool cache)
02466 {
02467 enum IMAP_TYPE retVal;
02468 retVal = ITYPE_UNKNOWN;
02469
02470 imapParser::parseURL (_url, _box, _section, _type, _uid, _validity, _info);
02471
02472
02473
02474 QString myNamespace = namespaceForBox( _box );
02475 kdDebug(7116) << "IMAP4::parseURL - namespace=" << myNamespace << endl;
02476 if ( namespaceToDelimiter.contains(myNamespace) )
02477 {
02478 _hierarchyDelimiter = namespaceToDelimiter[myNamespace];
02479 kdDebug(7116) << "IMAP4::parseURL - delimiter=" << _hierarchyDelimiter << endl;
02480 }
02481
02482 if (!_box.isEmpty ())
02483 {
02484 kdDebug(7116) << "IMAP4::parseURL - box=" << _box << endl;
02485
02486 if (makeLogin ())
02487 {
02488 if (getCurrentBox () != _box ||
02489 _type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK")
02490 {
02491 if ( cache )
02492 {
02493
02494 retVal = ITYPE_DIR_AND_BOX;
02495 } else
02496 {
02497
02498 imapCommand *cmd;
02499
02500 cmd = doCommand (imapCommand::clientList ("", _box));
02501 if (cmd->result () == "OK")
02502 {
02503 for (QValueListIterator < imapList > it = listResponses.begin ();
02504 it != listResponses.end (); ++it)
02505 {
02506
02507 if (_box == (*it).name ())
02508 {
02509 if ( !(*it).hierarchyDelimiter().isEmpty() )
02510 _hierarchyDelimiter = (*it).hierarchyDelimiter();
02511 if ((*it).noSelect ())
02512 {
02513 retVal = ITYPE_DIR;
02514 }
02515 else if ((*it).noInferiors ())
02516 {
02517 retVal = ITYPE_BOX;
02518 }
02519 else
02520 {
02521 retVal = ITYPE_DIR_AND_BOX;
02522 }
02523 }
02524 }
02525
02526 if ( retVal == ITYPE_UNKNOWN &&
02527 namespaceToDelimiter.contains(_box) ) {
02528 retVal = ITYPE_DIR;
02529 }
02530 } else {
02531 kdDebug(7116) << "IMAP4::parseURL - got error for " << _box << endl;
02532 }
02533 completeQueue.removeRef (cmd);
02534 }
02535 }
02536 else
02537 {
02538 retVal = ITYPE_BOX;
02539 }
02540 }
02541 else
02542 kdDebug(7116) << "IMAP4::parseURL: no login!" << endl;
02543
02544 }
02545 else
02546 {
02547
02548 kdDebug(7116) << "IMAP4: parseURL: box [root]" << endl;
02549 retVal = ITYPE_DIR;
02550 }
02551
02552
02553 if (retVal == ITYPE_BOX || retVal == ITYPE_DIR_AND_BOX)
02554 {
02555 if (!_uid.isEmpty ())
02556 {
02557 if (_uid.find (':') == -1 && _uid.find (',') == -1
02558 && _uid.find ('*') == -1)
02559 retVal = ITYPE_MSG;
02560 }
02561 }
02562 if (retVal == ITYPE_MSG)
02563 {
02564 if ( (_section.find ("BODY.PEEK[", 0, false) != -1 ||
02565 _section.find ("BODY[", 0, false) != -1) &&
02566 _section.find(".MIME") == -1 &&
02567 _section.find(".HEADER") == -1 )
02568 retVal = ITYPE_ATTACH;
02569 }
02570 if ( _hierarchyDelimiter.isEmpty() &&
02571 (_type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK") )
02572 {
02573
02574
02575 if (!_box.isEmpty())
02576 {
02577 int start = _url.path().findRev(_box);
02578 if (start != -1)
02579 _hierarchyDelimiter = _url.path().mid(start-1, start);
02580 kdDebug(7116) << "IMAP4::parseURL - reconstructed delimiter:" << _hierarchyDelimiter
02581 << " from URL " << _url.path() << endl;
02582 }
02583 if (_hierarchyDelimiter.isEmpty())
02584 _hierarchyDelimiter = "/";
02585 }
02586 kdDebug(7116) << "IMAP4::parseURL - return " << retVal << endl;
02587
02588 return retVal;
02589 }
02590
02591 int
02592 IMAP4Protocol::outputLine (const QCString & _str, int len)
02593 {
02594 if (len == -1) {
02595 len = _str.length();
02596 }
02597
02598 if (cacheOutput)
02599 {
02600 if ( !outputBuffer.isOpen() ) {
02601 outputBuffer.open(IO_WriteOnly);
02602 }
02603 outputBuffer.at(outputBufferIndex);
02604 outputBuffer.writeBlock(_str.data(), len);
02605 outputBufferIndex += len;
02606 return 0;
02607 }
02608
02609 QByteArray temp;
02610 bool relay = relayEnabled;
02611
02612 relayEnabled = true;
02613 temp.setRawData (_str.data (), len);
02614 parseRelay (temp);
02615 temp.resetRawData (_str.data (), len);
02616
02617 relayEnabled = relay;
02618 return 0;
02619 }
02620
02621 void IMAP4Protocol::flushOutput(QString contentEncoding)
02622 {
02623
02624 if (outputBufferIndex == 0)
02625 return;
02626 outputBuffer.close();
02627 outputCache.resize(outputBufferIndex);
02628 if (decodeContent)
02629 {
02630
02631 QByteArray decoded;
02632 if (contentEncoding.find("quoted-printable", 0, false) == 0)
02633 decoded = KCodecs::quotedPrintableDecode(outputCache);
02634 else if (contentEncoding.find("base64", 0, false) == 0)
02635 KCodecs::base64Decode(outputCache, decoded);
02636 else
02637 decoded = outputCache;
02638
02639 QString mimetype = KMimeType::findByContent( decoded )->name();
02640 kdDebug(7116) << "IMAP4::flushOutput - mimeType " << mimetype << endl;
02641 mimeType(mimetype);
02642 decodeContent = false;
02643 data( decoded );
02644 } else {
02645 data( outputCache );
02646 }
02647 mProcessedSize += outputBufferIndex;
02648 processedSize( mProcessedSize );
02649 outputBufferIndex = 0;
02650 outputCache[0] = '\0';
02651 outputBuffer.setBuffer(outputCache);
02652 }
02653
02654 ssize_t IMAP4Protocol::myRead(void *data, ssize_t len)
02655 {
02656 if (readBufferLen)
02657 {
02658 ssize_t copyLen = (len < readBufferLen) ? len : readBufferLen;
02659 memcpy(data, readBuffer, copyLen);
02660 readBufferLen -= copyLen;
02661 if (readBufferLen) memcpy(readBuffer, &readBuffer[copyLen], readBufferLen);
02662 return copyLen;
02663 }
02664 if (!isConnectionValid()) return 0;
02665 waitForResponse( responseTimeout() );
02666 return read(data, len);
02667 }
02668
02669 bool
02670 IMAP4Protocol::assureBox (const QString & aBox, bool readonly)
02671 {
02672 if (aBox.isEmpty()) return false;
02673
02674 imapCommand *cmd = 0;
02675
02676 if (aBox != getCurrentBox () || (!getSelected().readWrite() && !readonly))
02677 {
02678
02679 kdDebug(7116) << "IMAP4Protocol::assureBox - opening box" << endl;
02680 selectInfo = imapInfo();
02681 cmd = doCommand (imapCommand::clientSelect (aBox, readonly));
02682 bool ok = cmd->result() == "OK";
02683 QString cmdInfo = cmd->resultInfo();
02684 completeQueue.removeRef (cmd);
02685
02686 if (!ok)
02687 {
02688 bool found = false;
02689 cmd = doCommand (imapCommand::clientList ("", aBox));
02690 if (cmd->result () == "OK")
02691 {
02692 for (QValueListIterator < imapList > it = listResponses.begin ();
02693 it != listResponses.end (); ++it)
02694 {
02695 if (aBox == (*it).name ()) found = true;
02696 }
02697 }
02698 completeQueue.removeRef (cmd);
02699 if (found) {
02700 if (cmdInfo.find("permission", 0, false) != -1) {
02701
02702 error(ERR_ACCESS_DENIED, cmdInfo);
02703 } else {
02704 error(ERR_SLAVE_DEFINED, i18n("Unable to open folder %1. The server replied: %2").arg(aBox).arg(cmdInfo));
02705 }
02706 } else {
02707 error(KIO::ERR_DOES_NOT_EXIST, aBox);
02708 }
02709 return false;
02710 }
02711 }
02712 else
02713 {
02714
02715
02716
02717 kdDebug(7116) << "IMAP4Protocol::assureBox - reusing box" << endl;
02718 if ( mTimeOfLastNoop.secsTo( QDateTime::currentDateTime() ) > 10 ) {
02719 cmd = doCommand (imapCommand::clientNoop ());
02720 completeQueue.removeRef (cmd);
02721 mTimeOfLastNoop = QDateTime::currentDateTime();
02722 kdDebug(7116) << "IMAP4Protocol::assureBox - noop timer fired" << endl;
02723 }
02724 }
02725
02726
02727 if (!getSelected().readWrite() && !readonly)
02728 {
02729 error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox);
02730 return false;
02731 }
02732
02733 return true;
02734 }