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 CommandPtr cmd = doCommand (imapCommand::clientNoop());
00210 completeQueue.removeAll(cmd);
00211 }
00212
00213 if (aSequence.isEmpty ())
00214 {
00215 aSequence = "1:*";
00216 }
00217
00218 mProcessedSize = 0;
00219 CommandPtr cmd;
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 CommandPtr 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 CommandPtr cmd;
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 CommandPtr 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 CommandPtr 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 CommandPtr 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 CommandPtr 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 CommandPtr 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 CommandPtr 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 CommandPtr 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 CommandPtr 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 CommandPtr cmd = doCommand(imapCommand::clientClose());
01127 completeQueue.removeAll(cmd);
01128 setState(ISTATE_LOGIN);
01129 }
01130
01131 CommandPtr 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 CommandPtr 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 CommandPtr 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 CommandPtr 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 CommandPtr 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 CommandPtr 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 CommandPtr 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 finished();
01264 break;
01265 }
01266 case 'n':
01267 {
01268
01269
01270 infoMessage( imapNamespaces.join(",") );
01271 finished();
01272 break;
01273 }
01274 case 'U':
01275 {
01276
01277 KUrl _url;
01278 stream >> _url;
01279 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01280 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01281 CommandPtr cmd = doCommand(imapCommand::clientUnsubscribe(aBox));
01282 if (cmd->result () != "OK")
01283 {
01284 completeQueue.removeAll (cmd);
01285 error(ERR_SLAVE_DEFINED, i18n("Unsubscribe of folder %1 "
01286 "failed. The server returned: %2",
01287 _url.prettyUrl(),
01288 cmd->resultInfo()));
01289 return;
01290 }
01291 completeQueue.removeAll (cmd);
01292 finished();
01293 break;
01294 }
01295 case 'u':
01296 {
01297
01298 KUrl _url;
01299 stream >> _url;
01300 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01301 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01302 CommandPtr cmd = doCommand(imapCommand::clientSubscribe(aBox));
01303 if (cmd->result () != "OK")
01304 {
01305 completeQueue.removeAll (cmd);
01306 error(ERR_SLAVE_DEFINED, i18n("Subscribe of folder %1 "
01307 "failed. The server returned: %2",
01308 _url.prettyUrl(),
01309 cmd->resultInfo()));
01310 return;
01311 }
01312 completeQueue.removeAll (cmd);
01313 finished();
01314 break;
01315 }
01316 case 'A':
01317 {
01318
01319 int cmd;
01320 stream >> cmd;
01321 if ( hasCapability( "ACL" ) ) {
01322 specialACLCommand( cmd, stream );
01323 } else {
01324 error( ERR_UNSUPPORTED_ACTION, "ACL" );
01325 }
01326 break;
01327 }
01328 case 'M':
01329 {
01330
01331 int cmd;
01332 stream >> cmd;
01333 if ( hasCapability( "ANNOTATEMORE" ) ) {
01334 specialAnnotateMoreCommand( cmd, stream );
01335 } else {
01336 error( ERR_UNSUPPORTED_ACTION, "ANNOTATEMORE" );
01337 }
01338 break;
01339 }
01340 case 'Q':
01341 {
01342
01343 int cmd;
01344 stream >> cmd;
01345 if ( hasCapability( "QUOTA" ) ) {
01346 specialQuotaCommand( cmd, stream );
01347 } else {
01348 error( ERR_UNSUPPORTED_ACTION, "QUOTA" );
01349 }
01350 break;
01351 }
01352 case 'S':
01353 {
01354
01355 KUrl _url;
01356 QByteArray newFlags;
01357 stream >> _url >> newFlags;
01358
01359 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01360 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01361 if (!assureBox(aBox, false)) return;
01362
01363
01364 QByteArray knownFlags = "\\SEEN \\ANSWERED \\FLAGGED \\DRAFT";
01365 const imapInfo info = getSelected();
01366 if ( info.permanentFlagsAvailable() && (info.permanentFlags() & imapInfo::User) ) {
01367 knownFlags += " KMAILFORWARDED KMAILTODO KMAILWATCHED KMAILIGNORED $FORWARDED $TODO $WATCHED $IGNORED";
01368 }
01369
01370 CommandPtr cmd = doCommand (imapCommand::
01371 clientStore (aSequence, "-FLAGS.SILENT", knownFlags));
01372 if (cmd->result () != "OK")
01373 {
01374 completeQueue.removeAll (cmd);
01375 error(ERR_SLAVE_DEFINED, i18n("Changing the flags of message %1 "
01376 "failed with %2.", _url.prettyUrl(), cmd->result()));
01377 return;
01378 }
01379 completeQueue.removeAll (cmd);
01380 if (!newFlags.isEmpty())
01381 {
01382 cmd = doCommand (imapCommand::
01383 clientStore (aSequence, "+FLAGS.SILENT", newFlags));
01384 if (cmd->result () != "OK")
01385 {
01386 completeQueue.removeAll (cmd);
01387 error(ERR_SLAVE_DEFINED, i18n("Silent Changing the flags of message %1 "
01388 "failed with %2.", _url.prettyUrl(), cmd->result()));
01389 return;
01390 }
01391 completeQueue.removeAll (cmd);
01392 }
01393 finished();
01394 break;
01395 }
01396 case 's':
01397 {
01398
01399 KUrl _url;
01400 bool seen;
01401 QByteArray newFlags;
01402 stream >> _url >> seen;
01403
01404 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01405 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01406 if ( !assureBox(aBox, true) )
01407 return;
01408
01409 CommandPtr cmd;
01410 if ( seen )
01411 cmd = doCommand( imapCommand::clientStore( aSequence, "+FLAGS.SILENT", "\\SEEN" ) );
01412 else
01413 cmd = doCommand( imapCommand::clientStore( aSequence, "-FLAGS.SILENT", "\\SEEN" ) );
01414
01415 if (cmd->result () != "OK")
01416 {
01417 completeQueue.removeAll (cmd);
01418 error(ERR_COULD_NOT_WRITE,
01419 i18n( "Changing the flags of message %1 failed.", _url.prettyUrl() ) );
01420 return;
01421 }
01422 completeQueue.removeAll (cmd);
01423 finished();
01424 break;
01425 }
01426
01427 case 'E':
01428 {
01429
01430 specialSearchCommand( stream );
01431 break;
01432 }
01433 case 'X':
01434 {
01435
01436 specialCustomCommand( stream );
01437 break;
01438 }
01439 default:
01440 kWarning(7116) <<"Unknown command in special():" << tmp;
01441 error( ERR_UNSUPPORTED_ACTION, QString(QChar(tmp)) );
01442 break;
01443 }
01444 }
01445
01446 void
01447 IMAP4Protocol::specialACLCommand( int command, QDataStream& stream )
01448 {
01449
01450 KUrl _url;
01451 stream >> _url;
01452 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01453 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01454
01455 switch( command ) {
01456 case 'S':
01457 {
01458 QString user, acl;
01459 stream >> user >> acl;
01460 kDebug(7116) <<"SETACL" << aBox << user << acl;
01461 CommandPtr cmd = doCommand(imapCommand::clientSetACL(aBox, user, acl));
01462 if (cmd->result () != "OK")
01463 {
01464 error(ERR_SLAVE_DEFINED, i18n("Setting the Access Control List on folder %1 "
01465 "for user %2 failed. The server returned: %3",
01466 _url.prettyUrl(),
01467 user,
01468 cmd->resultInfo()));
01469 return;
01470 }
01471 completeQueue.removeAll (cmd);
01472 finished();
01473 break;
01474 }
01475 case 'D':
01476 {
01477 QString user;
01478 stream >> user;
01479 kDebug(7116) <<"DELETEACL" << aBox << user;
01480 CommandPtr cmd = doCommand(imapCommand::clientDeleteACL(aBox, user));
01481 if (cmd->result () != "OK")
01482 {
01483 error(ERR_SLAVE_DEFINED, i18n("Deleting the Access Control List on folder %1 "
01484 "for user %2 failed. The server returned: %3",
01485 _url.prettyUrl(),
01486 user,
01487 cmd->resultInfo()));
01488 return;
01489 }
01490 completeQueue.removeAll (cmd);
01491 finished();
01492 break;
01493 }
01494 case 'G':
01495 {
01496 kDebug(7116) <<"GETACL" << aBox;
01497 CommandPtr cmd = doCommand(imapCommand::clientGetACL(aBox));
01498 if (cmd->result () != "OK")
01499 {
01500 error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
01501 "failed. The server returned: %2",
01502 _url.prettyUrl(),
01503 cmd->resultInfo()));
01504 return;
01505 }
01506
01507
01508
01509
01510 kDebug(7116) << getResults();
01511 infoMessage(getResults().join( "\"" ));
01512 finished();
01513 break;
01514 }
01515 case 'L':
01516 {
01517
01518 error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01519 break;
01520 }
01521 case 'M':
01522 {
01523 kDebug(7116) <<"MYRIGHTS" << aBox;
01524 CommandPtr cmd = doCommand(imapCommand::clientMyRights(aBox));
01525 if (cmd->result () != "OK")
01526 {
01527 error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
01528 "failed. The server returned: %2",
01529 _url.prettyUrl(),
01530 cmd->resultInfo()));
01531 return;
01532 }
01533 QStringList lst = getResults();
01534 kDebug(7116) <<"myrights results:" << lst;
01535 if ( !lst.isEmpty() ) {
01536 Q_ASSERT( lst.count() == 1 );
01537 infoMessage( lst.first() );
01538 }
01539 finished();
01540 break;
01541 }
01542 default:
01543 kWarning(7116) <<"Unknown special ACL command:" << command;
01544 error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01545 }
01546 }
01547
01548 void
01549 IMAP4Protocol::specialSearchCommand( QDataStream& stream )
01550 {
01551 kDebug(7116) <<"IMAP4Protocol::specialSearchCommand";
01552 KUrl _url;
01553 stream >> _url;
01554 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01555 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01556 if (!assureBox(aBox, false)) return;
01557
01558 CommandPtr cmd = doCommand (imapCommand::clientSearch( aSection ));
01559 if (cmd->result () != "OK")
01560 {
01561 error(ERR_SLAVE_DEFINED, i18n("Searching of folder %1 "
01562 "failed. The server returned: %2",
01563 aBox,
01564 cmd->resultInfo()));
01565 return;
01566 }
01567 completeQueue.removeAll(cmd);
01568 QStringList lst = getResults();
01569 kDebug(7116) <<"IMAP4Protocol::specialSearchCommand '" << aSection <<
01570 "' returns" << lst;
01571 infoMessage( lst.join( " " ) );
01572
01573 finished();
01574 }
01575
01576 void
01577 IMAP4Protocol::specialCustomCommand( QDataStream& stream )
01578 {
01579 kDebug(7116) << "IMAP4Protocol::specialCustomCommand" << endl;
01580
01581 QString command, arguments;
01582 int type;
01583 stream >> type;
01584 stream >> command >> arguments;
01585
01590 if ( type == 'N' ) {
01591 kDebug(7116) << "IMAP4Protocol::specialCustomCommand: normal mode" << endl;
01592 CommandPtr cmd = doCommand (imapCommand::clientCustom( command, arguments ));
01593 if (cmd->result () != "OK")
01594 {
01595 error( ERR_SLAVE_DEFINED,
01596 i18n( "Custom command %1:%2 failed. The server returned: %3",
01597 command, arguments, cmd->resultInfo() ) );
01598 return;
01599 }
01600 completeQueue.removeAll(cmd);
01601 QStringList lst = getResults();
01602 kDebug(7116) << "IMAP4Protocol::specialCustomCommand '" << command <<
01603 ":" << arguments <<
01604 "' returns " << lst << endl;
01605 infoMessage( lst.join( " " ) );
01606
01607 finished();
01608 } else
01613 if ( type == 'E' ) {
01614 kDebug(7116) << "IMAP4Protocol::specialCustomCommand: extended mode" << endl;
01615 CommandPtr cmd = sendCommand (imapCommand::clientCustom( command, QString() ));
01616 while ( !parseLoop () ) {};
01617
01618
01619 if (!cmd->isComplete () && !getContinuation ().isEmpty ())
01620 {
01621 const QByteArray buffer = arguments.toUtf8();
01622
01623
01624 bool sendOk = (write (buffer.data (), buffer.size ()) == (ssize_t)buffer.size ());
01625 processedSize( buffer.size() );
01626
01627 if ( !sendOk ) {
01628 error ( ERR_CONNECTION_BROKEN, myHost );
01629 completeQueue.removeAll ( cmd );
01630 setState(ISTATE_CONNECT);
01631 closeConnection();
01632 return;
01633 }
01634 }
01635 parseWriteLine ("");
01636
01637 do
01638 {
01639 while (!parseLoop ()) {};
01640 }
01641 while (!cmd->isComplete ());
01642
01643 completeQueue.removeAll (cmd);
01644
01645 QStringList lst = getResults();
01646 kDebug(7116) << "IMAP4Protocol::specialCustomCommand: returns " << lst << endl;
01647 infoMessage( lst.join( " " ) );
01648
01649 finished ();
01650 }
01651 }
01652
01653 void
01654 IMAP4Protocol::specialAnnotateMoreCommand( int command, QDataStream& stream )
01655 {
01656
01657 KUrl _url;
01658 stream >> _url;
01659 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01660 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01661
01662 switch( command ) {
01663 case 'S':
01664 {
01665
01666
01667
01668
01669 QString entry;
01670 QMap<QString, QString> attributes;
01671 stream >> entry >> attributes;
01672 kDebug(7116) <<"SETANNOTATION" << aBox << entry << attributes.count() <<" attributes";
01673 CommandPtr cmd = doCommand(imapCommand::clientSetAnnotation(aBox, entry, attributes));
01674 if (cmd->result () != "OK")
01675 {
01676 error(ERR_SLAVE_DEFINED, i18n("Setting the annotation %1 on folder %2 "
01677 " failed. The server returned: %3",
01678 entry,
01679 _url.prettyUrl(),
01680 cmd->resultInfo()));
01681 return;
01682 }
01683 completeQueue.removeAll (cmd);
01684 finished();
01685 break;
01686 }
01687 case 'G':
01688 {
01689
01690
01691
01692
01693 QString entry;
01694 QStringList attributeNames;
01695 stream >> entry >> attributeNames;
01696 kDebug(7116) <<"GETANNOTATION" << aBox << entry << attributeNames;
01697 CommandPtr cmd = doCommand(imapCommand::clientGetAnnotation(aBox, entry, attributeNames));
01698 if (cmd->result () != "OK")
01699 {
01700 error(ERR_SLAVE_DEFINED, i18n("Retrieving the annotation %1 on folder %2 "
01701 "failed. The server returned: %3",
01702 entry,
01703 _url.prettyUrl(),
01704 cmd->resultInfo()));
01705 return;
01706 }
01707
01708
01709
01710 kDebug(7116) << getResults();
01711 infoMessage(getResults().join( "\r" ));
01712 finished();
01713 break;
01714 }
01715 default:
01716 kWarning(7116) <<"Unknown special annotate command:" << command;
01717 error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01718 }
01719 }
01720
01721 void
01722 IMAP4Protocol::specialQuotaCommand( int command, QDataStream& stream )
01723 {
01724
01725 KUrl _url;
01726 stream >> _url;
01727 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01728 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01729
01730 switch( command ) {
01731 case 'R':
01732 {
01733 kDebug(7116) <<"QUOTAROOT" << aBox;
01734 CommandPtr cmd = doCommand(imapCommand::clientGetQuotaroot( aBox ) );
01735 if (cmd->result () != "OK")
01736 {
01737 error(ERR_SLAVE_DEFINED, i18n("Retrieving the quota root information on folder %1 "
01738 "failed. The server returned: %2",
01739 _url.prettyUrl(), cmd->resultInfo()));
01740 return;
01741 }
01742 infoMessage(getResults().join( "\r" ));
01743 finished();
01744 break;
01745 }
01746 case 'G':
01747 {
01748 kDebug(7116) <<"GETQUOTA command";
01749 kWarning(7116) <<"UNIMPLEMENTED";
01750 break;
01751 }
01752 case 'S':
01753 {
01754 kDebug(7116) <<"SETQUOTA command";
01755 kWarning(7116) <<"UNIMPLEMENTED";
01756 break;
01757 }
01758 default:
01759 kWarning(7116) <<"Unknown special quota command:" << command;
01760 error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01761 }
01762 }
01763
01764
01765 void
01766 IMAP4Protocol::rename (const KUrl & src, const KUrl & dest, KIO::JobFlags flags)
01767 {
01768 kDebug(7116) <<"IMAP4::rename - [" << ((flags & KIO::Overwrite) ?"Overwrite" :"NoOverwrite") <<"]" << src <<" ->" << dest;
01769 QString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
01770 QString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo;
01771 enum IMAP_TYPE sType =
01772 parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo, false);
01773 enum IMAP_TYPE dType =
01774 parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo, false);
01775
01776 if (dType == ITYPE_UNKNOWN)
01777 {
01778 switch (sType)
01779 {
01780 case ITYPE_BOX:
01781 case ITYPE_DIR:
01782 case ITYPE_DIR_AND_BOX:
01783 {
01784 if (getState() == ISTATE_SELECT && sBox == getCurrentBox())
01785 {
01786 kDebug(7116) <<"IMAP4::rename - close" << getCurrentBox();
01787
01788 CommandPtr cmd = doCommand (imapCommand::clientClose());
01789 bool ok = cmd->result() == "OK";
01790 completeQueue.removeAll(cmd);
01791 if (!ok)
01792 {
01793 error(ERR_CANNOT_RENAME, i18n("Unable to close mailbox."));
01794 return;
01795 }
01796 setState(ISTATE_LOGIN);
01797 }
01798 CommandPtr cmd = doCommand (imapCommand::clientRename (sBox, dBox));
01799 if (cmd->result () != "OK") {
01800 error (ERR_CANNOT_RENAME, cmd->result ());
01801 completeQueue.removeAll (cmd);
01802 return;
01803 }
01804 completeQueue.removeAll (cmd);
01805 }
01806 break;
01807
01808 case ITYPE_MSG:
01809 case ITYPE_ATTACH:
01810 case ITYPE_UNKNOWN:
01811 error (ERR_CANNOT_RENAME, src.prettyUrl());
01812 break;
01813 }
01814 }
01815 else
01816 {
01817 error (ERR_CANNOT_RENAME, src.prettyUrl());
01818 return;
01819 }
01820 finished ();
01821 }
01822
01823 void
01824 IMAP4Protocol::slave_status ()
01825 {
01826 bool connected = (getState() != ISTATE_NO) && isConnected();
01827 kDebug(7116) <<"IMAP4::slave_status" << connected;
01828 slaveStatus ( connected ? myHost : QString(), connected );
01829 }
01830
01831 void
01832 IMAP4Protocol::dispatch (int command, const QByteArray & data)
01833 {
01834 kDebug(7116) <<"IMAP4::dispatch - command=" << command;
01835 KIO::TCPSlaveBase::dispatch (command, data);
01836 }
01837
01838 void
01839 IMAP4Protocol::stat (const KUrl & _url)
01840 {
01841 kDebug(7116) <<"IMAP4::stat -" << _url.prettyUrl();
01842 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01843
01844 enum IMAP_TYPE aType =
01845 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter,
01846 aInfo, true);
01847
01848 UDSEntry entry;
01849
01850 entry.insert( UDSEntry::UDS_NAME, aBox);
01851
01852 if (!aSection.isEmpty())
01853 {
01854 if (getState() == ISTATE_SELECT && aBox == getCurrentBox())
01855 {
01856 CommandPtr cmd = doCommand (imapCommand::clientClose());
01857 bool ok = cmd->result() == "OK";
01858 completeQueue.removeAll(cmd);
01859 if (!ok)
01860 {
01861 error(ERR_COULD_NOT_STAT, i18n("Unable to close mailbox."));
01862 return;
01863 }
01864 setState(ISTATE_LOGIN);
01865 }
01866 bool ok = false;
01867 QString cmdInfo;
01868 if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
01869 ok = true;
01870 else
01871 {
01872 CommandPtr cmd = doCommand(imapCommand::clientStatus(aBox, aSection));
01873 ok = cmd->result() == "OK";
01874 cmdInfo = cmd->resultInfo();
01875 completeQueue.removeAll(cmd);
01876 }
01877 if (!ok)
01878 {
01879 bool found = false;
01880 CommandPtr cmd = doCommand (imapCommand::clientList ("", aBox));
01881 if (cmd->result () == "OK")
01882 {
01883 for (QList< imapList >::Iterator it = listResponses.begin ();
01884 it != listResponses.end (); ++it)
01885 {
01886 if (aBox == (*it).name ()) found = true;
01887 }
01888 }
01889 completeQueue.removeAll (cmd);
01890 if (found)
01891 error(ERR_COULD_NOT_STAT, i18n("Unable to get information about folder %1. The server replied: %2", aBox, cmdInfo));
01892 else
01893 error(KIO::ERR_DOES_NOT_EXIST, aBox);
01894 return;
01895 }
01896 if ((aSection == "UIDNEXT" && getStatus().uidNextAvailable())
01897 || (aSection == "UNSEEN" && getStatus().unseenAvailable()))
01898 {
01899 entry.insert( UDSEntry::UDS_SIZE, (aSection == "UIDNEXT") ? getStatus().uidNext()
01900 : getStatus().unseen());
01901 }
01902 } else
01903 if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX || aType == ITYPE_MSG ||
01904 aType == ITYPE_ATTACH)
01905 {
01906 ulong validity = 0;
01907
01908 if (aBox == getCurrentBox ())
01909 validity = selectInfo.uidValidity ();
01910 else
01911 {
01912
01913
01914
01915 CommandPtr cmd =
01916 doCommand (imapCommand::clientStatus (aBox, "UIDVALIDITY"));
01917 completeQueue.removeAll (cmd);
01918 validity = getStatus ().uidValidity ();
01919 }
01920 #ifdef __GNUC__
01921 #warning This is temporary since Dec 2000 and makes most of the below code invalid
01922 #endif
01923 validity = 0;
01924
01925 if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX)
01926 {
01927
01928 if (validity > 0 && validity != aValidity.toULong ())
01929 {
01930
01931 KUrl newUrl = _url;
01932
01933 newUrl.setPath ('/' + aBox + ";UIDVALIDITY=" +
01934 QString::number(validity));
01935 kDebug(7116) <<"IMAP4::stat - redirecting to" << newUrl.prettyUrl();
01936 redirection (newUrl);
01937 }
01938 }
01939 else if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
01940 {
01941
01942
01943
01944
01945
01946 if (validity > 0 && validity != aValidity.toULong ())
01947 {
01948 aType = ITYPE_UNKNOWN;
01949 kDebug(7116) <<"IMAP4::stat - url has invalid validity [" << validity <<"d]" << _url.prettyUrl();
01950 }
01951 }
01952 }
01953
01954 entry.insert( UDSEntry::UDS_MIME_TYPE,getMimeType (aType));
01955
01956
01957 switch (aType)
01958 {
01959 case ITYPE_DIR:
01960 entry.insert( UDSEntry::UDS_FILE_TYPE, S_IFDIR);
01961 break;
01962
01963 case ITYPE_BOX:
01964 case ITYPE_DIR_AND_BOX:
01965 entry.insert(UDSEntry::UDS_FILE_TYPE, S_IFDIR);
01966 break;
01967
01968 case ITYPE_MSG:
01969 case ITYPE_ATTACH:
01970 entry.insert(UDSEntry::UDS_FILE_TYPE, S_IFREG);
01971 break;
01972
01973 case ITYPE_UNKNOWN:
01974 error (ERR_DOES_NOT_EXIST, _url.prettyUrl());
01975 break;
01976 }
01977
01978 statEntry (entry);
01979 kDebug(7116) <<"IMAP4::stat - Finishing stat";
01980 finished ();
01981 }
01982
01983 void IMAP4Protocol::openConnection()
01984 {
01985 if (makeLogin()) connected();
01986 }
01987
01988 void IMAP4Protocol::closeConnection()
01989 {
01990 if (getState() == ISTATE_NO) return;
01991 if (getState() == ISTATE_SELECT && metaData("expunge") == "auto")
01992 {
01993 CommandPtr cmd = doCommand (imapCommand::clientExpunge());
01994 completeQueue.removeAll (cmd);
01995 }
01996 if (getState() != ISTATE_CONNECT)
01997 {
01998 CommandPtr cmd = doCommand (imapCommand::clientLogout());
01999 completeQueue.removeAll (cmd);
02000 }
02001 disconnectFromHost();
02002 setState(ISTATE_NO);
02003 completeQueue.clear();
02004 sentQueue.clear();
02005 lastHandled = 0;
02006 currentBox.clear();
02007 readBufferLen = 0;
02008 }
02009
02010 bool IMAP4Protocol::makeLogin ()
02011 {
02012 if (getState () == ISTATE_LOGIN || getState () == ISTATE_SELECT)
02013 return true;
02014
02015 kDebug(7116) <<"IMAP4::makeLogin - checking login";
02016 bool alreadyConnected = getState() == ISTATE_CONNECT;
02017 kDebug(7116) <<"IMAP4::makeLogin - alreadyConnected" << alreadyConnected;
02018 if (alreadyConnected || connectToHost (( mySSL ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL ), myHost,
02019 myPort))
02020 {
02021
02022
02023 setState(ISTATE_CONNECT);
02024
02025 myAuth = metaData("auth");
02026 myTLS = metaData("tls");
02027 kDebug(7116) <<"myAuth:" << myAuth;
02028
02029 CommandPtr cmd;
02030
02031 unhandled.clear ();
02032 if (!alreadyConnected) while (!parseLoop ()) {}
02033 QString greeting;
02034 if (!unhandled.isEmpty()) greeting = unhandled.first().trimmed();
02035 unhandled.clear ();
02036 cmd = doCommand (CommandPtr(new imapCommand ("CAPABILITY", "")));
02037
02038 kDebug(7116) <<"IMAP4: setHost: capability";
02039 for (QStringList::const_iterator it = imapCapabilities.constBegin ();
02040 it != imapCapabilities.constEnd (); ++it)
02041 {
02042 kDebug(7116) <<"'" << (*it) <<"'";
02043 }
02044 completeQueue.removeAll (cmd);
02045
02046 if (!hasCapability("IMAP4") && !hasCapability("IMAP4rev1"))
02047 {
02048 error(ERR_COULD_NOT_LOGIN, i18n("The server %1 supports neither "
02049 "IMAP4 nor IMAP4rev1.\nIt identified itself with: %2",
02050 myHost, greeting));
02051 closeConnection();
02052 return false;
02053 }
02054
02055 if (metaData("nologin") == "on") return true;
02056
02057 if (myTLS == "on" && !hasCapability(QString("STARTTLS")))
02058 {
02059 error(ERR_COULD_NOT_LOGIN, i18n("The server does not support TLS.\n"
02060 "Disable this security feature to connect unencrypted."));
02061 closeConnection();
02062 return false;
02063 }
02064 if ((myTLS == "on" ) &&
02065 hasCapability(QString("STARTTLS")))
02066 {
02067 CommandPtr cmd = doCommand (imapCommand::clientStartTLS());
02068 if (cmd->result () == "OK")
02069 {
02070 completeQueue.removeAll(cmd);
02071 if (startSsl())
02072 {
02073 kDebug(7116) <<"TLS mode has been enabled.";
02074 CommandPtr cmd2 = doCommand (CommandPtr(new imapCommand ("CAPABILITY", "")));
02075 for (QStringList::const_iterator it = imapCapabilities.constBegin ();
02076 it != imapCapabilities.constEnd (); ++it)
02077 {
02078 kDebug(7116) <<"'" << (*it) <<"'";
02079 }
02080 completeQueue.removeAll (cmd2);
02081 } else {
02082 kWarning(7116) <<"TLS mode setup has failed. Aborting.";
02083 error (ERR_COULD_NOT_LOGIN, i18n("Starting TLS failed."));
02084 closeConnection();
02085 return false;
02086 }
02087 } else completeQueue.removeAll(cmd);
02088 }
02089
02090 if (!myAuth.isEmpty () && myAuth != "*"
02091 && !hasCapability (QString ("AUTH=") + myAuth))
02092 {
02093 error (ERR_COULD_NOT_LOGIN, i18n("The authentication method %1 is not "
02094 "supported by the server.", myAuth));
02095 closeConnection();
02096 return false;
02097 }
02098
02099 if ( greeting.contains( QRegExp( "Cyrus IMAP4 v2.1" ) ) ) {
02100 removeCapability( "ANNOTATEMORE" );
02101 }
02102
02103 kDebug(7116) <<"IMAP4::makeLogin - attempting login";
02104
02105 KIO::AuthInfo authInfo;
02106 authInfo.username = myUser;
02107 authInfo.password = myPass;
02108 authInfo.prompt = i18n ("Username and password for your IMAP account:");
02109
02110 kDebug(7116) <<"IMAP4::makeLogin - open_PassDlg said user=" << myUser <<" pass=xx";
02111
02112 QString resultInfo;
02113 if (myAuth.isEmpty () || myAuth == "*")
02114 {
02115 if (myUser.isEmpty () || myPass.isEmpty ()) {
02116 if(openPasswordDialog (authInfo)) {
02117 myUser = authInfo.username;
02118 myPass = authInfo.password;
02119 }
02120 }
02121 if (!clientLogin (myUser, myPass, resultInfo))
02122 error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to login. Probably the "
02123 "password is wrong.\nThe server %1 replied:\n%2", myHost, resultInfo));
02124 }
02125 else
02126 {
02127 #ifdef HAVE_LIBSASL2
02128 if (!clientAuthenticate (this, authInfo, myHost, myAuth, mySSL, resultInfo))
02129 error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to authenticate via %1.\n" "The server %2 replied:\n%3", myAuth, myHost, resultInfo));
02130 else {
02131 myUser = authInfo.username;
02132 myPass = authInfo.password;
02133 }
02134 #else
02135 error(KIO::ERR_COULD_NOT_LOGIN, i18n("SASL authentication is not compiled into kio_imap4."));
02136 #endif
02137 }
02138 if ( hasCapability("NAMESPACE") )
02139 {
02140
02141 cmd = doCommand( imapCommand::clientNamespace() );
02142 if (cmd->result () == "OK")
02143 {
02144 kDebug(7116) <<"makeLogin - registered namespaces";
02145 }
02146 completeQueue.removeAll (cmd);
02147 }
02148
02149 cmd = doCommand( imapCommand::clientList("", "") );
02150 if (cmd->result () == "OK")
02151 {
02152 QList< imapList >::Iterator it = listResponses.begin();
02153 if ( it != listResponses.end() )
02154 {
02155 namespaceToDelimiter[QString()] = (*it).hierarchyDelimiter();
02156 kDebug(7116) <<"makeLogin - delimiter for empty ns='" << (*it).hierarchyDelimiter() <<"'";
02157 if ( !hasCapability("NAMESPACE") )
02158 {
02159
02160 QString nsentry = QString::number( 0 ) + "==" + (*it).hierarchyDelimiter();
02161 imapNamespaces.append( nsentry );
02162 }
02163 }
02164 }
02165 completeQueue.removeAll (cmd);
02166 } else {
02167 kDebug(7116) <<"makeLogin - NO login";
02168 }
02169
02170 return getState() == ISTATE_LOGIN;
02171 }
02172
02173 void
02174 IMAP4Protocol::parseWriteLine (const QString & aStr)
02175 {
02176
02177 QByteArray writer = aStr.toUtf8();
02178 int len = writer.length();
02179
02180
02181 if (len == 0 || (writer[len - 1] != '\n')) {
02182 len += 2;
02183 writer += "\r\n";
02184 }
02185
02186
02187 write(writer.data(), len);
02188 }
02189
02190 QString
02191 IMAP4Protocol::getMimeType (enum IMAP_TYPE aType)
02192 {
02193 switch (aType)
02194 {
02195 case ITYPE_DIR:
02196 return "inode/directory";
02197 break;
02198
02199 case ITYPE_BOX:
02200 return "message/digest";
02201 break;
02202
02203 case ITYPE_DIR_AND_BOX:
02204 return "message/directory";
02205 break;
02206
02207 case ITYPE_MSG:
02208 return "message/rfc822";
02209 break;
02210
02211
02212 case ITYPE_ATTACH:
02213 return "application/octet-stream";
02214 break;
02215
02216 case ITYPE_UNKNOWN:
02217 default:
02218 return "unknown/unknown";
02219 }
02220 }
02221
02222
02223
02224 void
02225 IMAP4Protocol::doListEntry (const KUrl & _url, int stretch, imapCache * cache,
02226 bool withFlags, bool withSubject)
02227 {
02228 KUrl aURL = _url;
02229 aURL.setQuery (QString());
02230 const QString encodedUrl = aURL.url(KUrl::LeaveTrailingSlash);
02231 doListEntry(encodedUrl, stretch, cache, withFlags, withSubject);
02232 }
02233
02234
02235
02236 void
02237 IMAP4Protocol::doListEntry (const QString & encodedUrl, int stretch, imapCache * cache,
02238 bool withFlags, bool withSubject)
02239 {
02240 if (cache)
02241 {
02242 UDSEntry entry;
02243
02244 entry.clear ();
02245
02246 const QString uid = QString::number(cache->getUid());
02247 QString tmp = uid;
02248 if (stretch > 0)
02249 {
02250 tmp = "0000000000000000" + uid;
02251 tmp = tmp.right (stretch);
02252 }
02253 if (withSubject)
02254 {
02255 mailHeader *header = cache->getHeader();
02256 if (header)
02257 tmp += ' ' + header->getSubject();
02258 }
02259 entry.insert (UDSEntry::UDS_NAME,tmp);
02260
02261 tmp = encodedUrl;
02262 if (tmp[tmp.length () - 1] != '/')
02263 tmp += '/';
02264 tmp += ";UID=" + uid;
02265 entry.insert( UDSEntry::UDS_URL, tmp);
02266
02267 entry.insert(UDSEntry::UDS_FILE_TYPE,S_IFREG);
02268
02269 entry.insert(UDSEntry::UDS_SIZE, cache->getSize());
02270
02271 entry.insert( UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("message/rfc822"));
02272
02273 entry.insert(UDSEntry::UDS_USER,myUser);
02274
02275 entry.insert( KIO::UDSEntry::UDS_ACCESS, (withFlags) ? cache->getFlags() : S_IRUSR | S_IXUSR | S_IWUSR);
02276
02277 listEntry (entry, false);
02278 }
02279 }
02280
02281 void
02282 IMAP4Protocol::doListEntry (const KUrl & _url, const QString & myBox,
02283 const imapList & item, bool appendPath)
02284 {
02285 KUrl aURL = _url;
02286 aURL.setQuery (QString());
02287 UDSEntry entry;
02288 int hdLen = item.hierarchyDelimiter().length();
02289
02290 {
02291
02292 QString mailboxName = item.name ();
02293
02294
02295 if ( mailboxName.startsWith(myBox) && mailboxName.length() > myBox.length())
02296 {
02297 mailboxName =
02298 mailboxName.right (mailboxName.length () - myBox.length ());
02299 }
02300 if (mailboxName[0] == '/')
02301 mailboxName = mailboxName.right (mailboxName.length () - 1);
02302 if (mailboxName.left(hdLen) == item.hierarchyDelimiter())
02303 mailboxName = mailboxName.right(mailboxName.length () - hdLen);
02304 if (mailboxName.right(hdLen) == item.hierarchyDelimiter())
02305 mailboxName.truncate(mailboxName.length () - hdLen);
02306
02307 QString tmp;
02308 if (!item.hierarchyDelimiter().isEmpty() &&
02309 mailboxName.contains(item.hierarchyDelimiter()) )
02310 tmp = mailboxName.section(item.hierarchyDelimiter(), -1);
02311 else
02312 tmp = mailboxName;
02313
02314
02315 if (tmp.isEmpty ())
02316 tmp = "..";
02317
02318 if (!tmp.isEmpty ())
02319 {
02320 entry.insert(UDSEntry::UDS_NAME,tmp);
02321
02322 if (!item.noSelect ())
02323 {
02324 if (!item.noInferiors ())
02325 {
02326 tmp = "message/directory";
02327 } else {
02328 tmp = "message/digest";
02329 }
02330 entry.insert(UDSEntry::UDS_MIME_TYPE,tmp);
02331
02332 mailboxName += '/';
02333
02334
02335 entry.insert(UDSEntry::UDS_FILE_TYPE,S_IFDIR);
02336 }
02337 else if (!item.noInferiors ())
02338 {
02339 entry.insert(UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/directory"));
02340 mailboxName += '/';
02341
02342
02343 entry.insert(UDSEntry::UDS_FILE_TYPE,S_IFDIR);
02344 }
02345 else
02346 {
02347 entry.insert(UDSEntry::UDS_MIME_TYPE,QString::fromLatin1("unknown/unknown"));
02348 }
02349
02350 QString path = aURL.path();
02351 if (appendPath)
02352 {
02353 if (path[path.length() - 1] == '/' && !path.isEmpty() && path != "/")
02354 path.truncate(path.length() - 1);
02355 if (!path.isEmpty() && path != "/"
02356 && path.right(hdLen) != item.hierarchyDelimiter()) {
02357 path += item.hierarchyDelimiter();
02358 }
02359 path += mailboxName;
02360 if (path.toUpper() == "/INBOX/") {
02361
02362 path = path.toUpper();
02363 }
02364 }
02365 aURL.setPath(path);
02366 tmp = aURL.url(KUrl::LeaveTrailingSlash);
02367 entry.insert(UDSEntry::UDS_URL, tmp);
02368
02369 entry.insert( UDSEntry::UDS_USER, myUser);
02370
02371 entry.insert( UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IWUSR);
02372
02373 entry.insert( UDSEntry::UDS_EXTRA,item.attributesAsString());
02374
02375 listEntry (entry, false);
02376 }
02377 }
02378 }
02379
02380 enum IMAP_TYPE
02381 IMAP4Protocol::parseURL (const KUrl & _url, QString & _box,
02382 QString & _section, QString & _type, QString & _uid,
02383 QString & _validity, QString & _hierarchyDelimiter,
02384 QString & _info, bool cache)
02385 {
02386 enum IMAP_TYPE retVal;
02387 retVal = ITYPE_UNKNOWN;
02388
02389 imapParser::parseURL (_url, _box, _section, _type, _uid, _validity, _info);
02390
02391
02392
02393 QString myNamespace = namespaceForBox( _box );
02394 kDebug(7116) <<"IMAP4::parseURL - namespace=" << myNamespace;
02395 if ( namespaceToDelimiter.contains(myNamespace) )
02396 {
02397 _hierarchyDelimiter = namespaceToDelimiter[myNamespace];
02398 kDebug(7116) <<"IMAP4::parseURL - delimiter=" << _hierarchyDelimiter;
02399 }
02400
02401 if (!_box.isEmpty ())
02402 {
02403 kDebug(7116) <<"IMAP4::parseURL - box=" << _box;
02404
02405 if (makeLogin ())
02406 {
02407 if (getCurrentBox () != _box ||
02408 _type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK")
02409 {
02410 if ( cache )
02411 {
02412
02413 retVal = ITYPE_DIR_AND_BOX;
02414 } else
02415 {
02416
02417 CommandPtr cmd;
02418
02419 cmd = doCommand (imapCommand::clientList ("", _box));
02420 if (cmd->result () == "OK")
02421 {
02422 for (QList< imapList >::Iterator it = listResponses.begin ();
02423 it != listResponses.end (); ++it)
02424 {
02425
02426 if (_box == (*it).name ())
02427 {
02428 if ( !(*it).hierarchyDelimiter().isEmpty() )
02429 _hierarchyDelimiter = (*it).hierarchyDelimiter();
02430 if ((*it).noSelect ())
02431 {
02432 retVal = ITYPE_DIR;
02433 }
02434 else if ((*it).noInferiors ())
02435 {
02436 retVal = ITYPE_BOX;
02437 }
02438 else
02439 {
02440 retVal = ITYPE_DIR_AND_BOX;
02441 }
02442 }
02443 }
02444
02445 if ( retVal == ITYPE_UNKNOWN &&
02446 namespaceToDelimiter.contains(_box) ) {
02447 retVal = ITYPE_DIR;
02448 }
02449 } else {
02450 kDebug(7116) <<"IMAP4::parseURL - got error for" << _box;
02451 }
02452 completeQueue.removeAll (cmd);
02453 }
02454 }
02455 else
02456 {
02457 retVal = ITYPE_BOX;
02458 }
02459 }
02460 else
02461 kDebug(7116) <<"IMAP4::parseURL: no login!";
02462
02463 }
02464 else
02465 {
02466
02467 kDebug(7116) <<"IMAP4: parseURL: box [root]";
02468 retVal = ITYPE_DIR;
02469 }
02470
02471
02472 if (retVal == ITYPE_BOX || retVal == ITYPE_DIR_AND_BOX)
02473 {
02474 if (!_uid.isEmpty ())
02475 {
02476 if ( !_uid.contains(':') && !_uid.contains(',') && !_uid.contains('*') )
02477 retVal = ITYPE_MSG;
02478 }
02479 }
02480 if (retVal == ITYPE_MSG)
02481 {
02482 if ( ( _section.contains("BODY.PEEK[", Qt::CaseInsensitive) ||
02483 _section.contains("BODY[", Qt::CaseInsensitive) ) &&
02484 !_section.contains(".MIME") &&
02485 !_section.contains(".HEADER") )
02486 retVal = ITYPE_ATTACH;
02487 }
02488 if ( _hierarchyDelimiter.isEmpty() &&
02489 (_type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK") )
02490 {
02491
02492
02493 if (!_box.isEmpty())
02494 {
02495 int start = _url.path().lastIndexOf(_box);
02496 if (start != -1)
02497 _hierarchyDelimiter = _url.path().mid(start-1, start);
02498 kDebug(7116) <<"IMAP4::parseURL - reconstructed delimiter:" << _hierarchyDelimiter
02499 << "from URL" << _url.path();
02500 }
02501 if (_hierarchyDelimiter.isEmpty())
02502 _hierarchyDelimiter = "/";
02503 }
02504 kDebug(7116) <<"IMAP4::parseURL - return" << retVal;
02505
02506 return retVal;
02507 }
02508
02509 int
02510 IMAP4Protocol::outputLine (const QByteArray & _str, int len)
02511 {
02512 if (len == -1) {
02513 len = _str.length();
02514 }
02515
02516 if (cacheOutput)
02517 {
02518 if ( !outputBuffer.isOpen() ) {
02519 outputBuffer.open(QIODevice::WriteOnly);
02520 }
02521 outputBuffer.seek( outputBufferIndex );
02522 outputBuffer.write(_str.data(), len);
02523 outputBufferIndex += len;
02524 return 0;
02525 }
02526
02527 QByteArray temp;
02528 bool relay = relayEnabled;
02529
02530 relayEnabled = true;
02531 temp = QByteArray::fromRawData (_str.data (), len);
02532 parseRelay (temp);
02533 temp.clear();
02534
02535 relayEnabled = relay;
02536 return 0;
02537 }
02538
02539 void IMAP4Protocol::flushOutput(const QString &contentEncoding)
02540 {
02541
02542 if (outputBufferIndex == 0)
02543 return;
02544 outputBuffer.close();
02545 outputCache.resize(outputBufferIndex);
02546 if (decodeContent)
02547 {
02548
02549 QByteArray decoded;
02550 if ( contentEncoding.startsWith("quoted-printable", Qt::CaseInsensitive) )
02551 decoded = KCodecs::quotedPrintableDecode(outputCache);
02552 else if ( contentEncoding.startsWith("base64", Qt::CaseInsensitive) )
02553 decoded = QByteArray::fromBase64( outputCache );
02554 else
02555 decoded = outputCache;
02556
02557 QString mimetype = KMimeType::findByContent( decoded )->name();
02558 kDebug(7116) <<"IMAP4::flushOutput - mimeType" << mimetype;
02559 mimeType(mimetype);
02560 decodeContent = false;
02561 data( decoded );
02562 } else {
02563 data( outputCache );
02564 }
02565 mProcessedSize += outputBufferIndex;
02566 processedSize( mProcessedSize );
02567 outputBufferIndex = 0;
02568 outputCache[0] = '\0';
02569 outputBuffer.setBuffer(&outputCache);
02570 }
02571
02572 ssize_t IMAP4Protocol::myRead(void *data, ssize_t len)
02573 {
02574 if (readBufferLen)
02575 {
02576 ssize_t copyLen = (len < readBufferLen) ? len : readBufferLen;
02577 memcpy(data, readBuffer, copyLen);
02578 readBufferLen -= copyLen;
02579 if (readBufferLen) memcpy(readBuffer, &readBuffer[copyLen], readBufferLen);
02580 return copyLen;
02581 }
02582 if (!isConnected()) return 0;
02583 waitForResponse( responseTimeout() );
02584 return read((char*)data, len);
02585 }
02586
02587 bool
02588 IMAP4Protocol::assureBox (const QString & aBox, bool readonly)
02589 {
02590 if (aBox.isEmpty()) return false;
02591
02592 CommandPtr cmd;
02593
02594 if (aBox != getCurrentBox () || (!getSelected().readWrite() && !readonly))
02595 {
02596
02597 kDebug(7116) <<"IMAP4Protocol::assureBox - opening box";
02598 selectInfo = imapInfo();
02599 cmd = doCommand (imapCommand::clientSelect (aBox, readonly));
02600 bool ok = cmd->result() == "OK";
02601 QString cmdInfo = cmd->resultInfo();
02602 completeQueue.removeAll (cmd);
02603
02604 if (!ok)
02605 {
02606 bool found = false;
02607 cmd = doCommand (imapCommand::clientList ("", aBox));
02608 if (cmd->result () == "OK")
02609 {
02610 for (QList< imapList >::Iterator it = listResponses.begin ();
02611 it != listResponses.end (); ++it)
02612 {
02613 if (aBox == (*it).name ()) found = true;
02614 }
02615 }
02616 completeQueue.removeAll (cmd);
02617 if (found) {
02618 if ( cmdInfo.contains("permission", Qt::CaseInsensitive) ) {
02619
02620 error(ERR_ACCESS_DENIED, cmdInfo);
02621 } else {
02622 error(ERR_SLAVE_DEFINED, i18n("Unable to open folder %1. The server replied: %2", aBox, cmdInfo));
02623 }
02624 } else {
02625 error(KIO::ERR_DOES_NOT_EXIST, aBox);
02626 }
02627 return false;
02628 }
02629 }
02630 else
02631 {
02632
02633
02634
02635 kDebug(7116) <<"IMAP4Protocol::assureBox - reusing box";
02636 if ( mTimeOfLastNoop.secsTo( QDateTime::currentDateTime() ) > 10 ) {
02637 cmd = doCommand (imapCommand::clientNoop ());
02638 completeQueue.removeAll (cmd);
02639 mTimeOfLastNoop = QDateTime::currentDateTime();
02640 kDebug(7116) <<"IMAP4Protocol::assureBox - noop timer fired";
02641 }
02642 }
02643
02644
02645 if (!getSelected().readWrite() && !readonly)
02646 {
02647 error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox);
02648 return false;
02649 }
02650
02651 return true;
02652 }