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