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