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