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 extern "C" {
00074 #include <sasl/sasl.h>
00075 }
00076
00077 #include <qbuffer.h>
00078 #include <qdatetime.h>
00079 #include <QRegExp>
00080 #include <kprotocolmanager.h>
00081 #include <kcomponentdata.h>
00082 #include <kmessagebox.h>
00083 #include <kdebug.h>
00084 #include <kio/connection.h>
00085 #include <kio/slaveinterface.h>
00086 #include <kio/passworddialog.h>
00087 #include <klocale.h>
00088 #include <kmimetype.h>
00089 #include <kcodecs.h>
00090 #include <kde_file.h>
00091
00092 #include "common.h"
00093 #include "kdemacros.h"
00094
00095 #define IMAP_PROTOCOL "imap"
00096 #define IMAP_SSL_PROTOCOL "imaps"
00097 const int ImapPort = 143;
00098 const int ImapsPort = 993;
00099
00100 using namespace KIO;
00101
00102 extern "C"
00103 {
00104 void sigalrm_handler (int);
00105 KDE_EXPORT int kdemain (int argc, char **argv);
00106 }
00107
00108 int
00109 kdemain (int argc, char **argv)
00110 {
00111 kDebug(7116) <<"IMAP4::kdemain";
00112
00113 KComponentData instance ("kio_imap4");
00114 if (argc != 4)
00115 {
00116 fprintf(stderr, "Usage: kio_imap4 protocol domain-socket1 domain-socket2\n");
00117 ::exit (-1);
00118 }
00119
00120 if (!initSASL())
00121 ::exit(-1);
00122
00123
00124
00125 IMAP4Protocol *slave;
00126 if (strcasecmp (argv[1], IMAP_SSL_PROTOCOL) == 0)
00127 slave = new IMAP4Protocol (argv[2], argv[3], true);
00128 else if (strcasecmp (argv[1], IMAP_PROTOCOL) == 0)
00129 slave = new IMAP4Protocol (argv[2], argv[3], false);
00130 else
00131 abort ();
00132 slave->dispatchLoop ();
00133 delete slave;
00134
00135 sasl_done();
00136
00137 return 0;
00138 }
00139
00140 void
00141 sigchld_handler (int signo)
00142 {
00143
00144
00145
00146 const int save_errno = errno;
00147 int pid, status;
00148
00149 while (signo == SIGCHLD)
00150 {
00151 pid = waitpid (-1, &status, WNOHANG);
00152 if (pid <= 0)
00153 {
00154
00155
00156
00157 KDE_signal (SIGCHLD, sigchld_handler);
00158 break;
00159 }
00160 }
00161
00162 errno = save_errno;
00163 }
00164
00165 IMAP4Protocol::IMAP4Protocol (const QByteArray & pool, const QByteArray & app, bool isSSL)
00166 :TCPSlaveBase ((isSSL ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL), pool, app, isSSL),
00167 imapParser (),
00168 mimeIO (),
00169 mySSL( isSSL ),
00170 relayEnabled( false ),
00171 cacheOutput( false ),
00172 decodeContent( false ),
00173 outputBuffer(&outputCache),
00174 outputBufferIndex(0),
00175 mProcessedSize( 0 ),
00176 readBufferLen( 0 ),
00177 mTimeOfLastNoop( QDateTime() )
00178 {
00179 readBuffer[0] = 0x00;
00180 }
00181
00182 IMAP4Protocol::~IMAP4Protocol ()
00183 {
00184 disconnectFromHost();
00185 kDebug(7116) <<"IMAP4: Finishing";
00186 }
00187
00188 void
00189 IMAP4Protocol::get (const KUrl & _url)
00190 {
00191 if (!makeLogin()) return;
00192 kDebug(7116) <<"IMAP4::get -" << _url.prettyUrl();
00193 QString aBox, aSequence, aType, aSection, aValidity, aDelimiter, aInfo;
00194 enum IMAP_TYPE aEnum =
00195 parseURL (_url, aBox, aSection, aType, aSequence, aValidity, aDelimiter, aInfo);
00196 if (aEnum != ITYPE_ATTACH)
00197 mimeType (getMimeType(aEnum));
00198 if (aInfo == "DECODE")
00199 decodeContent = true;
00200
00201 if (aSequence == "0:0" && getState() == ISTATE_SELECT)
00202 {
00203 CommandPtr cmd = doCommand (imapCommand::clientNoop());
00204 completeQueue.removeAll(cmd);
00205 }
00206
00207 if (aSequence.isEmpty ())
00208 {
00209 aSequence = "1:*";
00210 }
00211
00212 mProcessedSize = 0;
00213 CommandPtr cmd;
00214 if (!assureBox (aBox, true)) return;
00215
00216 #ifdef USE_VALIDITY
00217 if (selectInfo.uidValidityAvailable () && !aValidity.isEmpty ()
00218 && selectInfo.uidValidity () != aValidity.toULong ())
00219 {
00220
00221 error (ERR_COULD_NOT_READ, _url.prettyUrl());
00222 return;
00223 }
00224 else
00225 #endif
00226 {
00227
00228
00229
00230
00231
00232
00233
00234 QString aUpper = aSection.toUpper();
00235 if (aUpper.contains("STRUCTURE"))
00236 {
00237 aSection = "BODYSTRUCTURE";
00238 }
00239 else if (aUpper.contains("ENVELOPE"))
00240 {
00241 aSection = "UID RFC822.SIZE FLAGS ENVELOPE";
00242 if (hasCapability("IMAP4rev1")) {
00243 aSection += " BODY.PEEK[HEADER.FIELDS (REFERENCES)]";
00244 } else {
00245
00246 aSection += " RFC822.HEADER.LINES (REFERENCES)";
00247 }
00248 }
00249 else if (aUpper == "HEADER")
00250 {
00251 aSection = "UID RFC822.HEADER RFC822.SIZE FLAGS";
00252 }
00253 else if (aUpper.contains("BODY.PEEK["))
00254 {
00255 if (aUpper.contains("BODY.PEEK[]"))
00256 {
00257 if (!hasCapability("IMAP4rev1"))
00258 aSection.replace("BODY.PEEK[]", "RFC822.PEEK");
00259 }
00260 aSection.prepend("UID RFC822.SIZE FLAGS ");
00261 }
00262 else if (aSection.isEmpty())
00263 {
00264 aSection = "UID BODY[] RFC822.SIZE FLAGS";
00265 }
00266 if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
00267 {
00268
00269 cacheOutput = true;
00270 outputLine
00271 ("Content-Type: multipart/digest; boundary=\"IMAPDIGEST\"\r\n", 55);
00272 if (selectInfo.recentAvailable ())
00273 outputLineStr ("X-Recent: " +
00274 QString::number(selectInfo.recent ()) + "\r\n");
00275 if (selectInfo.countAvailable ())
00276 outputLineStr ("X-Count: " + QString::number(selectInfo.count ()) +
00277 "\r\n");
00278 if (selectInfo.unseenAvailable ())
00279 outputLineStr ("X-Unseen: " +
00280 QString::number(selectInfo.unseen ()) + "\r\n");
00281 if (selectInfo.uidValidityAvailable ())
00282 outputLineStr ("X-uidValidity: " +
00283 QString::number(selectInfo.uidValidity ()) +
00284 "\r\n");
00285 if (selectInfo.uidNextAvailable ())
00286 outputLineStr ("X-UidNext: " +
00287 QString::number(selectInfo.uidNext ()) + "\r\n");
00288 if (selectInfo.flagsAvailable ())
00289 outputLineStr ("X-Flags: " + QString::number(selectInfo.flags ()) +
00290 "\r\n");
00291 if (selectInfo.permanentFlagsAvailable ())
00292 outputLineStr ("X-PermanentFlags: " +
00293 QString::number(selectInfo.permanentFlags ()) + "\r\n");
00294 if (selectInfo.readWriteAvailable ()) {
00295 if (selectInfo.readWrite()) {
00296 outputLine ("X-Access: Read/Write\r\n", 22);
00297 } else {
00298 outputLine ("X-Access: Read only\r\n", 21);
00299 }
00300 }
00301 outputLine ("\r\n", 2);
00302 flushOutput(QString());
00303 cacheOutput = false;
00304 }
00305
00306 if (aEnum == ITYPE_MSG || (aEnum == ITYPE_ATTACH && !decodeContent))
00307 relayEnabled = true;
00308
00309 if (aSequence != "0:0")
00310 {
00311 QString contentEncoding;
00312 if (aEnum == ITYPE_ATTACH && decodeContent)
00313 {
00314
00315 QString mySection = aSection;
00316 mySection.replace(']', ".MIME]");
00317 cmd = sendCommand (imapCommand::clientFetch (aSequence, mySection));
00318 do
00319 {
00320 while (!parseLoop ()) {}
00321 }
00322 while (!cmd->isComplete ());
00323 completeQueue.removeAll (cmd);
00324
00325 if (getLastHandled() && getLastHandled()->getHeader())
00326 contentEncoding = getLastHandled()->getHeader()->getEncoding();
00327
00328
00329
00330
00331 cacheOutput = true;
00332 }
00333
00334 cmd = sendCommand (imapCommand::clientFetch (aSequence, aSection));
00335 int res;
00336 aUpper = aSection.toUpper();
00337 do
00338 {
00339 while (!(res = parseLoop())) {}
00340 if (res == -1) break;
00341
00342 mailHeader *lastone = 0;
00343 imapCache *cache = getLastHandled ();
00344 if (cache)
00345 lastone = cache->getHeader ();
00346
00347 if (cmd && !cmd->isComplete ())
00348 {
00349 if ( aUpper.contains("BODYSTRUCTURE")
00350 || aUpper.contains("FLAGS")
00351 || aUpper.contains("UID")
00352 || aUpper.contains("ENVELOPE")
00353 || (aUpper.contains("BODY.PEEK[0]")
00354 && (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)))
00355 {
00356 if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
00357 {
00358
00359 outputLine ("--IMAPDIGEST\r\n", 14);
00360 cacheOutput = true;
00361 if (cache->getUid () != 0)
00362 outputLineStr ("X-UID: " +
00363 QString::number(cache->getUid ()) + "\r\n");
00364 if (cache->getSize () != 0)
00365 outputLineStr ("X-Length: " +
00366 QString::number(cache->getSize ()) + "\r\n");
00367 if (!cache->getDate ().isEmpty())
00368 outputLineStr ("X-Date: " + cache->getDate () + "\r\n");
00369 if (cache->getFlags () != 0)
00370 outputLineStr ("X-Flags: " +
00371 QString::number(cache->getFlags ()) + "\r\n");
00372 } else cacheOutput = true;
00373 if ( lastone && !decodeContent )
00374 lastone->outputPart (*this);
00375 cacheOutput = false;
00376 flushOutput(contentEncoding);
00377 }
00378 }
00379 }
00380 while (cmd && !cmd->isComplete ());
00381 if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
00382 {
00383
00384 outputLine ("--IMAPDIGEST--\r\n", 16);
00385 }
00386
00387 completeQueue.removeAll (cmd);
00388 }
00389 }
00390
00391
00392 data (QByteArray ());
00393
00394 finished ();
00395 relayEnabled = false;
00396 cacheOutput = false;
00397 kDebug(7116) <<"IMAP4::get - finished";
00398 }
00399
00400 void
00401 IMAP4Protocol::listDir (const KUrl & _url)
00402 {
00403 kDebug(7116) <<" IMAP4::listDir -" << _url.prettyUrl();
00404
00405 if (_url.path().isEmpty())
00406 {
00407 KUrl url = _url;
00408 url.setPath("/");
00409 redirection( url );
00410 finished();
00411 return;
00412 }
00413
00414 QString myBox, mySequence, myLType, mySection, myValidity, myDelimiter, myInfo;
00415
00416 enum IMAP_TYPE myType =
00417 parseURL (_url, myBox, mySection, myLType, mySequence, myValidity,
00418 myDelimiter, myInfo, true);
00419
00420 if (!makeLogin()) return;
00421
00422 if (myType == ITYPE_DIR || myType == ITYPE_DIR_AND_BOX)
00423 {
00424 QString listStr = myBox;
00425 CommandPtr cmd;
00426
00427 if (!listStr.isEmpty () && !listStr.endsWith(myDelimiter) &&
00428 mySection != "FOLDERONLY")
00429 listStr += myDelimiter;
00430
00431 if (mySection.isEmpty())
00432 {
00433 listStr += '%';
00434 } else if (mySection == "COMPLETE") {
00435 listStr += '*';
00436 }
00437 kDebug(7116) <<"IMAP4Protocol::listDir - listStr=" << listStr;
00438 cmd =
00439 doCommand (imapCommand::clientList ("", listStr,
00440 (myLType == "LSUB" || myLType == "LSUBNOCHECK")));
00441 if (cmd->result () == "OK")
00442 {
00443 QString mailboxName;
00444 UDSEntry entry;
00445 KUrl aURL = _url;
00446 if ( aURL.path().contains(';') )
00447 aURL.setPath(aURL.path().left(aURL.path().indexOf(';')));
00448
00449 kDebug(7116) <<"IMAP4Protocol::listDir - got" << listResponses.count ();
00450
00451 if (myLType == "LSUB")
00452 {
00453
00454 QList<imapList> listResponsesSave = listResponses;
00455 doCommand (imapCommand::clientList ("", listStr, false));
00456 for (QList< imapList >::Iterator it = listResponsesSave.begin ();
00457 it != listResponsesSave.end (); ++it)
00458 {
00459 bool boxOk = false;
00460 for (QList< imapList >::Iterator it2 = listResponses.begin ();
00461 it2 != listResponses.end (); ++it2)
00462 {
00463 if ((*it2).name() == (*it).name())
00464 {
00465 boxOk = true;
00466
00467 (*it) = (*it2);
00468 break;
00469 }
00470 }
00471 if (boxOk)
00472 doListEntry (aURL, myBox, (*it), (mySection != "FOLDERONLY"));
00473 else
00474 kDebug(7116) <<"IMAP4Protocol::listDir - suppress" << (*it).name();
00475 }
00476 listResponses = listResponsesSave;
00477 }
00478 else
00479 {
00480 for (QList< imapList >::Iterator it = listResponses.begin ();
00481 it != listResponses.end (); ++it)
00482 {
00483 doListEntry (aURL, myBox, (*it), (mySection != "FOLDERONLY"));
00484 }
00485 }
00486 entry.clear ();
00487 listEntry (entry, true);
00488 }
00489 else
00490 {
00491 error (ERR_CANNOT_ENTER_DIRECTORY, _url.prettyUrl());
00492 completeQueue.removeAll (cmd);
00493 return;
00494 }
00495 completeQueue.removeAll (cmd);
00496 }
00497 if ((myType == ITYPE_BOX || myType == ITYPE_DIR_AND_BOX)
00498 && myLType != "LIST" && myLType != "LSUB" && myLType != "LSUBNOCHECK")
00499 {
00500 KUrl aURL = _url;
00501 aURL.setQuery (QString());
00502 const QString encodedUrl = aURL.url(KUrl::LeaveTrailingSlash);
00503
00504 if (!_url.query ().isEmpty ())
00505 {
00506 QString query = KUrl::fromPercentEncoding (_url.query().toLatin1());
00507 query = query.right (query.length () - 1);
00508 if (!query.isEmpty())
00509 {
00510 CommandPtr cmd;
00511
00512 if (!assureBox (myBox, true)) return;
00513
00514 if (!selectInfo.countAvailable() || selectInfo.count())
00515 {
00516 cmd = doCommand (imapCommand::clientSearch (query));
00517 if (cmd->result() != "OK")
00518 {
00519 error(ERR_UNSUPPORTED_ACTION, _url.prettyUrl());
00520 completeQueue.removeAll (cmd);
00521 return;
00522 }
00523 completeQueue.removeAll (cmd);
00524
00525 QStringList list = getResults ();
00526 int stretch = 0;
00527
00528 if (selectInfo.uidNextAvailable ())
00529 stretch = QString::number(selectInfo.uidNext ()).length ();
00530 UDSEntry entry;
00531 imapCache fake;
00532
00533 for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd();
00534 ++it)
00535 {
00536 fake.setUid((*it).toULong());
00537 doListEntry (encodedUrl, stretch, &fake);
00538 }
00539 entry.clear ();
00540 listEntry (entry, true);
00541 }
00542 }
00543 }
00544 else
00545 {
00546 if (!assureBox (myBox, true)) return;
00547
00548 kDebug(7116) <<"IMAP4: select returned:";
00549 if (selectInfo.recentAvailable ())
00550 kDebug(7116) <<"Recent:" << selectInfo.recent () <<"d";
00551 if (selectInfo.countAvailable ())
00552 kDebug(7116) <<"Count:" << selectInfo.count () <<"d";
00553 if (selectInfo.unseenAvailable ())
00554 kDebug(7116) <<"Unseen:" << selectInfo.unseen () <<"d";
00555 if (selectInfo.uidValidityAvailable ())
00556 kDebug(7116) <<"uidValidity:" << selectInfo.uidValidity () <<"d";
00557 if (selectInfo.flagsAvailable ())
00558 kDebug(7116) <<"Flags:" << selectInfo.flags () <<"d";
00559 if (selectInfo.permanentFlagsAvailable ())
00560 kDebug(7116) <<"PermanentFlags:" << selectInfo.permanentFlags () <<"d";
00561 if (selectInfo.readWriteAvailable ())
00562 kDebug(7116) <<"Access:" << (selectInfo.readWrite ()?"Read/Write" :"Read only");
00563
00564 #ifdef USE_VALIDITY
00565 if (selectInfo.uidValidityAvailable ()
00566 && selectInfo.uidValidity () != myValidity.toULong ())
00567 {
00568
00569 KUrl newUrl = _url;
00570
00571 newUrl.setPath ('/' + myBox + ";UIDVALIDITY=" +
00572 QString::number(selectInfo.uidValidity ()));
00573 kDebug(7116) <<"IMAP4::listDir - redirecting to" << newUrl.prettyUrl();
00574 redirection (newUrl);
00575
00576
00577 }
00578 else
00579 #endif
00580 if (selectInfo.count () > 0)
00581 {
00582 int stretch = 0;
00583
00584 if (selectInfo.uidNextAvailable ())
00585 stretch = QString::number(selectInfo.uidNext ()).length ();
00586
00587 UDSEntry entry;
00588
00589 if (mySequence.isEmpty()) mySequence = "1:*";
00590
00591 bool withSubject = mySection.isEmpty();
00592 if (mySection.isEmpty()) mySection = "UID RFC822.SIZE ENVELOPE";
00593
00594 bool withFlags = mySection.toUpper().contains("FLAGS") ;
00595 CommandPtr fetch =
00596 sendCommand (imapCommand::
00597 clientFetch (mySequence, mySection));
00598 imapCache *cache;
00599 do
00600 {
00601 while (!parseLoop ()) {}
00602
00603 cache = getLastHandled ();
00604
00605 if (cache && !fetch->isComplete())
00606 doListEntry (encodedUrl, stretch, cache, withFlags, withSubject);
00607 }
00608 while (!fetch->isComplete ());
00609 entry.clear ();
00610 listEntry (entry, true);
00611 }
00612 }
00613 }
00614 if ( !selectInfo.alert().isNull() ) {
00615 if ( !myBox.isEmpty() ) {
00616 warning( i18n( "Message from %1 while processing '%2': %3", myHost, myBox, selectInfo.alert() ) );
00617 } else {
00618 warning( i18n( "Message from %1: %2", myHost, selectInfo.alert() ) );
00619 }
00620 selectInfo.setAlert( 0 );
00621 }
00622
00623 kDebug(7116) <<"IMAP4Protocol::listDir - Finishing listDir";
00624 finished ();
00625 }
00626
00627 void
00628 IMAP4Protocol::setHost (const QString & _host, quint16 _port,
00629 const QString & _user, const QString & _pass)
00630 {
00631 if (myHost != _host || myPort != _port || myUser != _user || myPass != _pass)
00632 {
00633
00634 if (!myHost.isEmpty ())
00635 closeConnection ();
00636 myHost = _host;
00637 if (_port == 0)
00638 myPort = (mySSL) ? ImapsPort : ImapPort;
00639 else
00640 myPort = _port;
00641 myUser = _user;
00642 myPass = _pass;
00643 }
00644 }
00645
00646 void
00647 IMAP4Protocol::parseRelay (const QByteArray & buffer)
00648 {
00649 if (relayEnabled) {
00650
00651 data( buffer );
00652 mProcessedSize += buffer.size();
00653 processedSize( mProcessedSize );
00654 } else if (cacheOutput)
00655 {
00656
00657 if ( !outputBuffer.isOpen() ) {
00658 outputBuffer.open(QIODevice::WriteOnly);
00659 }
00660 outputBuffer.seek( outputBufferIndex );
00661 outputBuffer.write(buffer, buffer.size());
00662 outputBufferIndex += buffer.size();
00663 }
00664 }
00665
00666 void
00667 IMAP4Protocol::parseRelay (ulong len)
00668 {
00669 if (relayEnabled)
00670 totalSize (len);
00671 }
00672
00673
00674 bool IMAP4Protocol::parseRead(QByteArray & buffer, long len, long relay)
00675 {
00676 const long int bufLen = 8192;
00677 char buf[bufLen];
00678
00679 while (buffer.size() < len )
00680 {
00681 ssize_t readLen = myRead(buf, qMin(len - buffer.size(), bufLen - 1));
00682 if (readLen == 0)
00683 {
00684 kDebug(7116) <<"parseRead: readLen == 0 - connection broken";
00685 error (ERR_CONNECTION_BROKEN, myHost);
00686 setState(ISTATE_CONNECT);
00687 closeConnection();
00688 return false;
00689 }
00690 if (relay > buffer.size())
00691 {
00692 QByteArray relayData;
00693 ssize_t relbuf = relay - buffer.size();
00694 int currentRelay = qMin(relbuf, readLen);
00695 relayData = QByteArray::fromRawData(buf, currentRelay);
00696 parseRelay(relayData);
00697 relayData.clear();
00698 }
00699 {
00700 QBuffer stream( &buffer );
00701 stream.open (QIODevice::WriteOnly);
00702 stream.seek (buffer.size ());
00703 stream.write (buf, readLen);
00704 stream.close ();
00705 }
00706 }
00707 return (buffer.size() == len);
00708 }
00709
00710
00711 bool IMAP4Protocol::parseReadLine (QByteArray & buffer, long relay)
00712 {
00713 if (myHost.isEmpty()) return false;
00714
00715 while (true) {
00716 ssize_t copyLen = 0;
00717 if (readBufferLen > 0)
00718 {
00719 while (copyLen < readBufferLen && readBuffer[copyLen] != '\n') copyLen++;
00720 if (copyLen < readBufferLen) copyLen++;
00721 if (relay > 0)
00722 {
00723 QByteArray relayData;
00724
00725 if (copyLen < (ssize_t) relay)
00726 relay = copyLen;
00727 relayData = QByteArray::fromRawData (readBuffer, relay);
00728 parseRelay (relayData);
00729 relayData.clear();
00730
00731 }
00732
00733 {
00734 int oldsize = buffer.size();
00735 buffer.resize(oldsize + copyLen);
00736 memcpy(buffer.data() + oldsize, readBuffer, copyLen);
00737
00738 }
00739
00740 readBufferLen -= copyLen;
00741 if (readBufferLen)
00742 memmove(readBuffer, &readBuffer[copyLen], readBufferLen);
00743 if (buffer[buffer.size() - 1] == '\n') return true;
00744 }
00745 if (!isConnected())
00746 {
00747 kDebug(7116) <<"parseReadLine - connection broken";
00748 error (ERR_CONNECTION_BROKEN, myHost);
00749 setState(ISTATE_CONNECT);
00750 closeConnection();
00751 return false;
00752 }
00753 if (!waitForResponse( responseTimeout() ))
00754 {
00755 error(ERR_SERVER_TIMEOUT, myHost);
00756 setState(ISTATE_CONNECT);
00757 closeConnection();
00758 return false;
00759 }
00760 readBufferLen = read(readBuffer, IMAP_BUFFER - 1);
00761 if (readBufferLen == 0)
00762 {
00763 kDebug(7116) <<"parseReadLine: readBufferLen == 0 - connection broken";
00764 error (ERR_CONNECTION_BROKEN, myHost);
00765 setState(ISTATE_CONNECT);
00766 closeConnection();
00767 return false;
00768 }
00769 }
00770 }
00771
00772 void
00773 IMAP4Protocol::setSubURL (const KUrl & _url)
00774 {
00775 kDebug(7116) <<"IMAP4::setSubURL -" << _url.prettyUrl();
00776 KIO::TCPSlaveBase::setSubUrl (_url);
00777 }
00778
00779 void
00780 IMAP4Protocol::put (const KUrl & _url, int, KIO::JobFlags)
00781 {
00782 kDebug(7116) <<"IMAP4::put -" << _url.prettyUrl();
00783
00784 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
00785 enum IMAP_TYPE aType =
00786 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
00787
00788
00789 if (aType != ITYPE_BOX && aType != ITYPE_DIR_AND_BOX)
00790 {
00791 if (aBox[aBox.length () - 1] == '/')
00792 aBox = aBox.right (aBox.length () - 1);
00793 CommandPtr cmd = doCommand (imapCommand::clientCreate (aBox));
00794
00795 if (cmd->result () != "OK") {
00796 error (ERR_COULD_NOT_WRITE, _url.prettyUrl());
00797 completeQueue.removeAll (cmd);
00798 return;
00799 }
00800 completeQueue.removeAll (cmd);
00801 }
00802 else
00803 {
00804 QList < QByteArray* > bufferList;
00805 int length = 0;
00806
00807 int result;
00808
00809 do
00810 {
00811 QByteArray *buffer = new QByteArray ();
00812 dataReq ();
00813 result = readData (*buffer);
00814 if (result > 0)
00815 {
00816 bufferList.append (buffer);
00817 length += result;
00818 } else {
00819 delete buffer;
00820 }
00821 }
00822 while (result > 0);
00823
00824 if (result != 0)
00825 {
00826 error (ERR_ABORTED, _url.prettyUrl());
00827 return;
00828 }
00829
00830 CommandPtr cmd =
00831 sendCommand (imapCommand::clientAppend (aBox, aSection, length));
00832 while (!parseLoop ()) {}
00833
00834
00835 if (!cmd->isComplete () && !getContinuation ().isEmpty ())
00836 {
00837 bool sendOk = true;
00838 ulong wrote = 0;
00839
00840 QByteArray *buffer;
00841 QListIterator<QByteArray *> it(bufferList);
00842
00843 while (it.hasNext() && sendOk)
00844 {
00845 buffer = it.next();
00846
00847 sendOk =
00848 (write (buffer->data (), buffer->size ()) ==
00849 (ssize_t) buffer->size ());
00850 wrote += buffer->size ();
00851 processedSize(wrote);
00852 delete buffer;
00853 if (!sendOk)
00854 {
00855 error (ERR_CONNECTION_BROKEN, myHost);
00856 completeQueue.removeAll (cmd);
00857 setState(ISTATE_CONNECT);
00858 closeConnection();
00859 return;
00860 }
00861 }
00862 parseWriteLine ("");
00863
00864 while (!cmd->isComplete () && getState() != ISTATE_NO)
00865 parseLoop ();
00866 if ( getState() == ISTATE_NO ) {
00867
00868
00869 error( ERR_CONNECTION_BROKEN, myHost );
00870 completeQueue.removeAll (cmd);
00871 closeConnection();
00872 return;
00873 }
00874 else if (cmd->result () != "OK") {
00875 error( ERR_SLAVE_DEFINED, cmd->resultInfo() );
00876 completeQueue.removeAll (cmd);
00877 return;
00878 }
00879 else
00880 {
00881 if (hasCapability("UIDPLUS"))
00882 {
00883 QString uid = cmd->resultInfo();
00884 if ( uid.contains("APPENDUID") )
00885 {
00886 uid = uid.section(" ", 2, 2);
00887 uid.truncate(uid.length()-1);
00888 infoMessage("UID "+uid);
00889 }
00890 }
00891
00892 else if (aBox == getCurrentBox ())
00893 {
00894 cmd =
00895 doCommand (imapCommand::
00896 clientSelect (aBox, !selectInfo.readWrite ()));
00897 completeQueue.removeAll (cmd);
00898 }
00899 }
00900 }
00901 else
00902 {
00903
00904
00905 error (ERR_SLAVE_DEFINED, cmd->resultInfo());
00906 completeQueue.removeAll (cmd);
00907 return;
00908 }
00909
00910 completeQueue.removeAll (cmd);
00911 }
00912
00913 finished ();
00914 }
00915
00916 void
00917 IMAP4Protocol::mkdir (const KUrl & _url, int)
00918 {
00919 kDebug(7116) <<"IMAP4::mkdir -" << _url.prettyUrl();
00920 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
00921 parseURL(_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
00922 kDebug(7116) <<"IMAP4::mkdir - create" << aBox;
00923 CommandPtr cmd = doCommand (imapCommand::clientCreate(aBox));
00924
00925 if (cmd->result () != "OK")
00926 {
00927 kDebug(7116) <<"IMAP4::mkdir -" << cmd->resultInfo();
00928 error (ERR_COULD_NOT_MKDIR, _url.prettyUrl());
00929 completeQueue.removeAll (cmd);
00930 return;
00931 }
00932 completeQueue.removeAll (cmd);
00933
00934
00935 enum IMAP_TYPE type =
00936 parseURL(_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
00937 if (type == ITYPE_BOX)
00938 {
00939 bool ask = ( aInfo.contains( "ASKUSER" ) );
00940 if ( ask &&
00941 messageBox(QuestionYesNo,
00942 i18n("The following folder will be created on the server: %1 "
00943 "What do you want to store in this folder?", aBox ),
00944 i18n("Create Folder"),
00945 i18n("&Messages"), i18n("&Subfolders")) == KMessageBox::No )
00946 {
00947 cmd = doCommand(imapCommand::clientDelete(aBox));
00948 completeQueue.removeAll (cmd);
00949 cmd = doCommand(imapCommand::clientCreate(aBox + aDelimiter));
00950 if (cmd->result () != "OK")
00951 {
00952 error (ERR_COULD_NOT_MKDIR, _url.prettyUrl());
00953 completeQueue.removeAll (cmd);
00954 return;
00955 }
00956 completeQueue.removeAll (cmd);
00957 }
00958 }
00959
00960 cmd = doCommand(imapCommand::clientSubscribe(aBox));
00961 completeQueue.removeAll(cmd);
00962
00963 finished ();
00964 }
00965
00966 void
00967 IMAP4Protocol::copy (const KUrl & src, const KUrl & dest, int, KIO::JobFlags flags)
00968 {
00969 kDebug(7116) <<"IMAP4::copy - [" << ((flags & KIO::Overwrite) ?"Overwrite" :"NoOverwrite") <<"]" << src.prettyUrl() <<" ->" << dest.prettyUrl();
00970 QString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
00971 QString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo;
00972 enum IMAP_TYPE sType =
00973 parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo);
00974 enum IMAP_TYPE dType =
00975 parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo);
00976
00977
00978 if (dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX)
00979 {
00980
00981 int sub = dBox.indexOf (sBox);
00982
00983
00984 if (sub > 0)
00985 {
00986 KUrl testDir = dest;
00987
00988 QString subDir = dBox.right (dBox.length () - dBox.lastIndexOf ('/'));
00989 QString topDir = dBox.left (sub);
00990 testDir.setPath ('/' + topDir);
00991 dType =
00992 parseURL (testDir, topDir, dSection, dLType, dSequence, dValidity,
00993 dDelimiter, dInfo);
00994
00995 kDebug(7116) <<"IMAP4::copy - checking this destination" << topDir;
00996
00997 if (dType == ITYPE_BOX || dType == ITYPE_DIR_AND_BOX)
00998 {
00999 kDebug(7116) <<"IMAP4::copy - assuming this destination" << topDir;
01000 dBox = topDir;
01001 }
01002 else
01003 {
01004
01005
01006 topDir = '/' + topDir + subDir;
01007 testDir.setPath (topDir);
01008 kDebug(7116) <<"IMAP4::copy - checking this destination" << topDir;
01009 dType =
01010 parseURL (testDir, topDir, dSection, dLType, dSequence, dValidity,
01011 dDelimiter, dInfo);
01012 if (dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX)
01013 {
01014
01015 CommandPtr cmd = doCommand (imapCommand::clientCreate (topDir));
01016
01017
01018 if (cmd->result () == "OK")
01019 {
01020 kDebug(7116) <<"IMAP4::copy - assuming this destination" << topDir;
01021 dType = ITYPE_BOX;
01022 dBox = topDir;
01023 }
01024 else
01025 {
01026 completeQueue.removeAll (cmd);
01027 cmd = doCommand (imapCommand::clientCreate (dBox));
01028 if (cmd->result () == "OK")
01029 dType = ITYPE_BOX;
01030 else
01031 error (ERR_COULD_NOT_WRITE, dest.prettyUrl());
01032 }
01033 completeQueue.removeAll (cmd);
01034 }
01035 }
01036
01037 }
01038 }
01039 if (sType == ITYPE_MSG || sType == ITYPE_BOX || sType == ITYPE_DIR_AND_BOX)
01040 {
01041
01042 if (!assureBox(sBox, true)) return;
01043 kDebug(7116) <<"IMAP4::copy -" << sBox <<" ->" << dBox;
01044
01045
01046 CommandPtr cmd =
01047 doCommand (imapCommand::clientCopy (dBox, sSequence));
01048 if (cmd->result () != "OK")
01049 {
01050 kError(5006) <<"IMAP4::copy -" << cmd->resultInfo();
01051 error (ERR_COULD_NOT_WRITE, dest.prettyUrl());
01052 completeQueue.removeAll (cmd);
01053 return;
01054 } else {
01055 if (hasCapability("UIDPLUS"))
01056 {
01057 QString uid = cmd->resultInfo();
01058 if ( uid.contains("COPYUID") )
01059 {
01060 uid = uid.section(" ", 2, 3);
01061 uid.truncate(uid.length()-1);
01062 infoMessage("UID "+uid);
01063 }
01064 }
01065 }
01066 completeQueue.removeAll (cmd);
01067 }
01068 else
01069 {
01070 error (ERR_ACCESS_DENIED, src.prettyUrl());
01071 return;
01072 }
01073 finished ();
01074 }
01075
01076 void
01077 IMAP4Protocol::del (const KUrl & _url, bool isFile)
01078 {
01079 kDebug(7116) <<"IMAP4::del - [" << (isFile ?"File" :"NoFile") <<"]" << _url.prettyUrl();
01080 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01081 enum IMAP_TYPE aType =
01082 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01083
01084 switch (aType)
01085 {
01086 case ITYPE_BOX:
01087 case ITYPE_DIR_AND_BOX:
01088 if (!aSequence.isEmpty ())
01089 {
01090 if (aSequence == "*")
01091 {
01092 if (!assureBox (aBox, false)) return;
01093 CommandPtr cmd = doCommand (imapCommand::clientExpunge ());
01094 if (cmd->result () != "OK") {
01095 error (ERR_CANNOT_DELETE, _url.prettyUrl());
01096 completeQueue.removeAll (cmd);
01097 return;
01098 }
01099 completeQueue.removeAll (cmd);
01100 }
01101 else
01102 {
01103
01104 if (!assureBox (aBox, false)) return;
01105 CommandPtr cmd =
01106 doCommand (imapCommand::
01107 clientStore (aSequence, "+FLAGS.SILENT", "\\DELETED"));
01108 if (cmd->result () != "OK") {
01109 error (ERR_CANNOT_DELETE, _url.prettyUrl());
01110 completeQueue.removeAll (cmd);
01111 return;
01112 }
01113 completeQueue.removeAll (cmd);
01114 }
01115 }
01116 else
01117 {
01118 if (getCurrentBox() == aBox)
01119 {
01120 CommandPtr cmd = doCommand(imapCommand::clientClose());
01121 completeQueue.removeAll(cmd);
01122 setState(ISTATE_LOGIN);
01123 }
01124
01125 CommandPtr cmd = doCommand(imapCommand::clientUnsubscribe(aBox));
01126 completeQueue.removeAll(cmd);
01127 cmd = doCommand(imapCommand::clientDelete (aBox));
01128
01129 if (cmd->result () != "OK")
01130 {
01131 completeQueue.removeAll(cmd);
01132 if (!assureBox(aBox, false)) return;
01133 bool stillOk = true;
01134 if (stillOk)
01135 {
01136 CommandPtr cmd = doCommand(
01137 imapCommand::clientStore("1:*", "+FLAGS.SILENT", "\\DELETED"));
01138 if (cmd->result () != "OK") stillOk = false;
01139 completeQueue.removeAll(cmd);
01140 }
01141 if (stillOk)
01142 {
01143 CommandPtr cmd = doCommand(imapCommand::clientClose());
01144 if (cmd->result () != "OK") stillOk = false;
01145 completeQueue.removeAll(cmd);
01146 setState(ISTATE_LOGIN);
01147 }
01148 if (stillOk)
01149 {
01150 CommandPtr cmd = doCommand (imapCommand::clientDelete(aBox));
01151 if (cmd->result () != "OK") stillOk = false;
01152 completeQueue.removeAll(cmd);
01153 }
01154 if (!stillOk)
01155 {
01156 error (ERR_COULD_NOT_RMDIR, _url.prettyUrl());
01157 return;
01158 }
01159 } else {
01160 completeQueue.removeAll (cmd);
01161 }
01162 }
01163 break;
01164
01165 case ITYPE_DIR:
01166 {
01167 CommandPtr cmd = doCommand (imapCommand::clientDelete (aBox));
01168 if (cmd->result () != "OK") {
01169 error (ERR_COULD_NOT_RMDIR, _url.prettyUrl());
01170 completeQueue.removeAll (cmd);
01171 return;
01172 }
01173 completeQueue.removeAll (cmd);
01174 }
01175 break;
01176
01177 case ITYPE_MSG:
01178 {
01179
01180 if (!assureBox (aBox, false)) return;
01181 CommandPtr cmd =
01182 doCommand (imapCommand::
01183 clientStore (aSequence, "+FLAGS.SILENT", "\\DELETED"));
01184 if (cmd->result () != "OK") {
01185 error (ERR_CANNOT_DELETE, _url.prettyUrl());
01186 completeQueue.removeAll (cmd);
01187 return;
01188 }
01189 completeQueue.removeAll (cmd);
01190 }
01191 break;
01192
01193 case ITYPE_UNKNOWN:
01194 case ITYPE_ATTACH:
01195 error (ERR_CANNOT_DELETE, _url.prettyUrl());
01196 break;
01197 }
01198 finished ();
01199 }
01200
01201
01202
01203
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217 void
01218 IMAP4Protocol::special (const QByteArray & aData)
01219 {
01220 kDebug(7116) <<"IMAP4Protocol::special";
01221 if (!makeLogin()) return;
01222
01223 QDataStream stream( aData );
01224
01225 int tmp;
01226 stream >> tmp;
01227
01228 switch (tmp) {
01229 case 'C':
01230 {
01231
01232 KUrl src;
01233 KUrl dest;
01234 stream >> src >> dest;
01235 copy(src, dest, 0, false);
01236 break;
01237 }
01238 case 'c':
01239 {
01240
01241 infoMessage(imapCapabilities.join(" "));
01242 finished();
01243 break;
01244 }
01245 case 'N':
01246 {
01247
01248 CommandPtr cmd = doCommand(imapCommand::clientNoop());
01249 if (cmd->result () != "OK")
01250 {
01251 kDebug(7116) <<"NOOP did not succeed - connection broken";
01252 completeQueue.removeAll (cmd);
01253 error (ERR_CONNECTION_BROKEN, myHost);
01254 return;
01255 }
01256 completeQueue.removeAll (cmd);
01257 finished();
01258 break;
01259 }
01260 case 'n':
01261 {
01262
01263
01264 infoMessage( imapNamespaces.join(",") );
01265 finished();
01266 break;
01267 }
01268 case 'U':
01269 {
01270
01271 KUrl _url;
01272 stream >> _url;
01273 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01274 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01275 CommandPtr cmd = doCommand(imapCommand::clientUnsubscribe(aBox));
01276 if (cmd->result () != "OK")
01277 {
01278 completeQueue.removeAll (cmd);
01279 error(ERR_SLAVE_DEFINED, i18n("Unsubscribe of folder %1 "
01280 "failed. The server returned: %2",
01281 _url.prettyUrl(),
01282 cmd->resultInfo()));
01283 return;
01284 }
01285 completeQueue.removeAll (cmd);
01286 finished();
01287 break;
01288 }
01289 case 'u':
01290 {
01291
01292 KUrl _url;
01293 stream >> _url;
01294 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01295 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01296 CommandPtr cmd = doCommand(imapCommand::clientSubscribe(aBox));
01297 if (cmd->result () != "OK")
01298 {
01299 completeQueue.removeAll (cmd);
01300 error(ERR_SLAVE_DEFINED, i18n("Subscribe of folder %1 "
01301 "failed. The server returned: %2",
01302 _url.prettyUrl(),
01303 cmd->resultInfo()));
01304 return;
01305 }
01306 completeQueue.removeAll (cmd);
01307 finished();
01308 break;
01309 }
01310 case 'A':
01311 {
01312
01313 int cmd;
01314 stream >> cmd;
01315 if ( hasCapability( "ACL" ) ) {
01316 specialACLCommand( cmd, stream );
01317 } else {
01318 error( ERR_UNSUPPORTED_ACTION, QString::fromLatin1("ACL") );
01319 }
01320 break;
01321 }
01322 case 'M':
01323 {
01324
01325 int cmd;
01326 stream >> cmd;
01327 if ( hasCapability( "ANNOTATEMORE" ) ) {
01328 specialAnnotateMoreCommand( cmd, stream );
01329 } else {
01330 error( ERR_UNSUPPORTED_ACTION, QString::fromLatin1("ANNOTATEMORE") );
01331 }
01332 break;
01333 }
01334 case 'Q':
01335 {
01336
01337 int cmd;
01338 stream >> cmd;
01339 if ( hasCapability( "QUOTA" ) ) {
01340 specialQuotaCommand( cmd, stream );
01341 } else {
01342 error( ERR_UNSUPPORTED_ACTION, QString::fromLatin1("QUOTA") );
01343 }
01344 break;
01345 }
01346 case 'S':
01347 {
01348
01349 KUrl _url;
01350 QByteArray newFlags;
01351 stream >> _url >> newFlags;
01352
01353 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01354 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01355 if (!assureBox(aBox, false)) return;
01356
01357
01358 QByteArray knownFlags = "\\SEEN \\ANSWERED \\FLAGGED \\DRAFT";
01359 const imapInfo info = getSelected();
01360 if ( info.permanentFlagsAvailable() && (info.permanentFlags() & imapInfo::User) ) {
01361 knownFlags += " KMAILFORWARDED KMAILTODO KMAILWATCHED KMAILIGNORED $FORWARDED $TODO $WATCHED $IGNORED";
01362 }
01363
01364 CommandPtr cmd = doCommand (imapCommand::
01365 clientStore (aSequence, "-FLAGS.SILENT", knownFlags));
01366 if (cmd->result () != "OK")
01367 {
01368 completeQueue.removeAll (cmd);
01369 error(ERR_SLAVE_DEFINED, i18n("Changing the flags of message %1 "
01370 "failed with %2.", _url.prettyUrl(), cmd->result()));
01371 return;
01372 }
01373 completeQueue.removeAll (cmd);
01374 if (!newFlags.isEmpty())
01375 {
01376 cmd = doCommand (imapCommand::
01377 clientStore (aSequence, "+FLAGS.SILENT", newFlags));
01378 if (cmd->result () != "OK")
01379 {
01380 completeQueue.removeAll (cmd);
01381 error(ERR_SLAVE_DEFINED, i18n("Silent Changing the flags of message %1 "
01382 "failed with %2.", _url.prettyUrl(), cmd->result()));
01383 return;
01384 }
01385 completeQueue.removeAll (cmd);
01386 }
01387 finished();
01388 break;
01389 }
01390 case 's':
01391 {
01392
01393 KUrl _url;
01394 bool seen;
01395 QByteArray newFlags;
01396 stream >> _url >> seen;
01397
01398 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01399 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01400 if ( !assureBox(aBox, true) )
01401 return;
01402
01403 CommandPtr cmd;
01404 if ( seen )
01405 cmd = doCommand( imapCommand::clientStore( aSequence, "+FLAGS.SILENT", "\\SEEN" ) );
01406 else
01407 cmd = doCommand( imapCommand::clientStore( aSequence, "-FLAGS.SILENT", "\\SEEN" ) );
01408
01409 if (cmd->result () != "OK")
01410 {
01411 completeQueue.removeAll (cmd);
01412 error(ERR_COULD_NOT_WRITE,
01413 i18n( "Changing the flags of message %1 failed.", _url.prettyUrl() ) );
01414 return;
01415 }
01416 completeQueue.removeAll (cmd);
01417 finished();
01418 break;
01419 }
01420
01421 case 'E':
01422 {
01423
01424 specialSearchCommand( stream );
01425 break;
01426 }
01427 case 'X':
01428 {
01429
01430 specialCustomCommand( stream );
01431 break;
01432 }
01433 default:
01434 kWarning(7116) <<"Unknown command in special():" << tmp;
01435 error( ERR_UNSUPPORTED_ACTION, QString(QChar(tmp)) );
01436 break;
01437 }
01438 }
01439
01440 void
01441 IMAP4Protocol::specialACLCommand( int command, QDataStream& stream )
01442 {
01443
01444 KUrl _url;
01445 stream >> _url;
01446 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01447 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01448
01449 switch( command ) {
01450 case 'S':
01451 {
01452 QString user, acl;
01453 stream >> user >> acl;
01454 kDebug(7116) <<"SETACL" << aBox << user << acl;
01455 CommandPtr cmd = doCommand(imapCommand::clientSetACL(aBox, user, acl));
01456 if (cmd->result () != "OK")
01457 {
01458 error(ERR_SLAVE_DEFINED, i18n("Setting the Access Control List on folder %1 "
01459 "for user %2 failed. The server returned: %3",
01460 _url.prettyUrl(),
01461 user,
01462 cmd->resultInfo()));
01463 return;
01464 }
01465 completeQueue.removeAll (cmd);
01466 finished();
01467 break;
01468 }
01469 case 'D':
01470 {
01471 QString user;
01472 stream >> user;
01473 kDebug(7116) <<"DELETEACL" << aBox << user;
01474 CommandPtr cmd = doCommand(imapCommand::clientDeleteACL(aBox, user));
01475 if (cmd->result () != "OK")
01476 {
01477 error(ERR_SLAVE_DEFINED, i18n("Deleting the Access Control List on folder %1 "
01478 "for user %2 failed. The server returned: %3",
01479 _url.prettyUrl(),
01480 user,
01481 cmd->resultInfo()));
01482 return;
01483 }
01484 completeQueue.removeAll (cmd);
01485 finished();
01486 break;
01487 }
01488 case 'G':
01489 {
01490 kDebug(7116) <<"GETACL" << aBox;
01491 CommandPtr cmd = doCommand(imapCommand::clientGetACL(aBox));
01492 if (cmd->result () != "OK")
01493 {
01494 error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
01495 "failed. The server returned: %2",
01496 _url.prettyUrl(),
01497 cmd->resultInfo()));
01498 return;
01499 }
01500
01501
01502
01503
01504 kDebug(7116) << getResults();
01505 infoMessage(getResults().join( "\"" ));
01506 finished();
01507 break;
01508 }
01509 case 'L':
01510 {
01511
01512 error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01513 break;
01514 }
01515 case 'M':
01516 {
01517 kDebug(7116) <<"MYRIGHTS" << aBox;
01518 CommandPtr cmd = doCommand(imapCommand::clientMyRights(aBox));
01519 if (cmd->result () != "OK")
01520 {
01521 error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
01522 "failed. The server returned: %2",
01523 _url.prettyUrl(),
01524 cmd->resultInfo()));
01525 return;
01526 }
01527 QStringList lst = getResults();
01528 kDebug(7116) <<"myrights results:" << lst;
01529 if ( !lst.isEmpty() ) {
01530 Q_ASSERT( lst.count() == 1 );
01531 infoMessage( lst.first() );
01532 }
01533 finished();
01534 break;
01535 }
01536 default:
01537 kWarning(7116) <<"Unknown special ACL command:" << command;
01538 error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01539 }
01540 }
01541
01542 void
01543 IMAP4Protocol::specialSearchCommand( QDataStream& stream )
01544 {
01545 kDebug(7116) <<"IMAP4Protocol::specialSearchCommand";
01546 KUrl _url;
01547 stream >> _url;
01548 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01549 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01550 if (!assureBox(aBox, true)) return;
01551
01552 CommandPtr cmd = doCommand (imapCommand::clientSearch( aSection ));
01553 if (cmd->result () != "OK")
01554 {
01555 error(ERR_SLAVE_DEFINED, i18n("Searching of folder %1 "
01556 "failed. The server returned: %2",
01557 aBox,
01558 cmd->resultInfo()));
01559 return;
01560 }
01561 completeQueue.removeAll(cmd);
01562 QStringList lst = getResults();
01563 kDebug(7116) <<"IMAP4Protocol::specialSearchCommand '" << aSection <<
01564 "' returns" << lst;
01565 infoMessage( lst.join( " " ) );
01566
01567 finished();
01568 }
01569
01570 void
01571 IMAP4Protocol::specialCustomCommand( QDataStream& stream )
01572 {
01573 kDebug(7116) << "IMAP4Protocol::specialCustomCommand" << endl;
01574
01575 QString command, arguments;
01576 int type;
01577 stream >> type;
01578 stream >> command >> arguments;
01579
01584 if ( type == 'N' ) {
01585 kDebug(7116) << "IMAP4Protocol::specialCustomCommand: normal mode" << endl;
01586 CommandPtr cmd = doCommand (imapCommand::clientCustom( command, arguments ));
01587 if (cmd->result () != "OK")
01588 {
01589 error( ERR_SLAVE_DEFINED,
01590 i18n( "Custom command %1:%2 failed. The server returned: %3",
01591 command, arguments, cmd->resultInfo() ) );
01592 return;
01593 }
01594 completeQueue.removeAll(cmd);
01595 QStringList lst = getResults();
01596 kDebug(7116) << "IMAP4Protocol::specialCustomCommand '" << command <<
01597 ":" << arguments <<
01598 "' returns " << lst << endl;
01599 infoMessage( lst.join( " " ) );
01600
01601 finished();
01602 } else
01607 if ( type == 'E' ) {
01608 kDebug(7116) << "IMAP4Protocol::specialCustomCommand: extended mode" << endl;
01609 CommandPtr cmd = sendCommand (imapCommand::clientCustom( command, QString() ));
01610 while ( !parseLoop () ) {};
01611
01612
01613 if (!cmd->isComplete () && !getContinuation ().isEmpty ())
01614 {
01615 const QByteArray buffer = arguments.toUtf8();
01616
01617
01618 bool sendOk = (write (buffer.data (), buffer.size ()) == (ssize_t)buffer.size ());
01619 processedSize( buffer.size() );
01620
01621 if ( !sendOk ) {
01622 error ( ERR_CONNECTION_BROKEN, myHost );
01623 completeQueue.removeAll ( cmd );
01624 setState(ISTATE_CONNECT);
01625 closeConnection();
01626 return;
01627 }
01628 }
01629 parseWriteLine ("");
01630
01631 do
01632 {
01633 while (!parseLoop ()) {};
01634 }
01635 while (!cmd->isComplete ());
01636
01637 completeQueue.removeAll (cmd);
01638
01639 QStringList lst = getResults();
01640 kDebug(7116) << "IMAP4Protocol::specialCustomCommand: returns " << lst << endl;
01641 infoMessage( lst.join( " " ) );
01642
01643 finished ();
01644 }
01645 }
01646
01647 void
01648 IMAP4Protocol::specialAnnotateMoreCommand( int command, QDataStream& stream )
01649 {
01650
01651 KUrl _url;
01652 stream >> _url;
01653 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01654 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01655
01656 switch( command ) {
01657 case 'S':
01658 {
01659
01660
01661
01662
01663 QString entry;
01664 QMap<QString, QString> attributes;
01665 stream >> entry >> attributes;
01666 kDebug(7116) <<"SETANNOTATION" << aBox << entry << attributes.count() <<" attributes";
01667 CommandPtr cmd = doCommand(imapCommand::clientSetAnnotation(aBox, entry, attributes));
01668 if (cmd->result () != "OK")
01669 {
01670 error(ERR_SLAVE_DEFINED, i18n("Setting the annotation %1 on folder %2 "
01671 " failed. The server returned: %3",
01672 entry,
01673 _url.prettyUrl(),
01674 cmd->resultInfo()));
01675 return;
01676 }
01677 completeQueue.removeAll (cmd);
01678 finished();
01679 break;
01680 }
01681 case 'G':
01682 {
01683
01684
01685
01686
01687 QString entry;
01688 QStringList attributeNames;
01689 stream >> entry >> attributeNames;
01690 kDebug(7116) <<"GETANNOTATION" << aBox << entry << attributeNames;
01691 CommandPtr cmd = doCommand(imapCommand::clientGetAnnotation(aBox, entry, attributeNames));
01692 if (cmd->result () != "OK")
01693 {
01694 error(ERR_SLAVE_DEFINED, i18n("Retrieving the annotation %1 on folder %2 "
01695 "failed. The server returned: %3",
01696 entry,
01697 _url.prettyUrl(),
01698 cmd->resultInfo()));
01699 return;
01700 }
01701
01702
01703
01704 kDebug(7116) << getResults();
01705 infoMessage(getResults().join( "\r" ));
01706 finished();
01707 break;
01708 }
01709 default:
01710 kWarning(7116) <<"Unknown special annotate command:" << command;
01711 error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01712 }
01713 }
01714
01715 void
01716 IMAP4Protocol::specialQuotaCommand( int command, QDataStream& stream )
01717 {
01718
01719 KUrl _url;
01720 stream >> _url;
01721 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01722 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01723
01724 switch( command ) {
01725 case 'R':
01726 {
01727 kDebug(7116) <<"QUOTAROOT" << aBox;
01728 CommandPtr cmd = doCommand(imapCommand::clientGetQuotaroot( aBox ) );
01729 if (cmd->result () != "OK")
01730 {
01731 error(ERR_SLAVE_DEFINED, i18n("Retrieving the quota root information on folder %1 "
01732 "failed. The server returned: %2",
01733 _url.prettyUrl(), cmd->resultInfo()));
01734 return;
01735 }
01736 infoMessage(getResults().join( "\r" ));
01737 finished();
01738 break;
01739 }
01740 case 'G':
01741 {
01742 kDebug(7116) <<"GETQUOTA command";
01743 kWarning(7116) <<"UNIMPLEMENTED";
01744 break;
01745 }
01746 case 'S':
01747 {
01748 kDebug(7116) <<"SETQUOTA command";
01749 kWarning(7116) <<"UNIMPLEMENTED";
01750 break;
01751 }
01752 default:
01753 kWarning(7116) <<"Unknown special quota command:" << command;
01754 error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01755 }
01756 }
01757
01758
01759 void
01760 IMAP4Protocol::rename (const KUrl & src, const KUrl & dest, KIO::JobFlags flags)
01761 {
01762 kDebug(7116) <<"IMAP4::rename - [" << ((flags & KIO::Overwrite) ?"Overwrite" :"NoOverwrite") <<"]" << src <<" ->" << dest;
01763 QString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
01764 QString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo;
01765 enum IMAP_TYPE sType =
01766 parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo, false);
01767 enum IMAP_TYPE dType =
01768 parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo, false);
01769
01770 if (dType == ITYPE_UNKNOWN)
01771 {
01772 switch (sType)
01773 {
01774 case ITYPE_BOX:
01775 case ITYPE_DIR:
01776 case ITYPE_DIR_AND_BOX:
01777 {
01778 if (getState() == ISTATE_SELECT && sBox == getCurrentBox())
01779 {
01780 kDebug(7116) <<"IMAP4::rename - close" << getCurrentBox();
01781
01782 CommandPtr cmd = doCommand (imapCommand::clientClose());
01783 bool ok = cmd->result() == "OK";
01784 completeQueue.removeAll(cmd);
01785 if (!ok)
01786 {
01787 error(ERR_CANNOT_RENAME, i18n("Unable to close mailbox."));
01788 return;
01789 }
01790 setState(ISTATE_LOGIN);
01791 }
01792 CommandPtr cmd = doCommand (imapCommand::clientRename (sBox, dBox));
01793 if (cmd->result () != "OK") {
01794 error (ERR_CANNOT_RENAME, cmd->result ());
01795 completeQueue.removeAll (cmd);
01796 return;
01797 }
01798 completeQueue.removeAll (cmd);
01799 }
01800 break;
01801
01802 case ITYPE_MSG:
01803 case ITYPE_ATTACH:
01804 case ITYPE_UNKNOWN:
01805 error (ERR_CANNOT_RENAME, src.prettyUrl());
01806 break;
01807 }
01808 }
01809 else
01810 {
01811 error (ERR_CANNOT_RENAME, src.prettyUrl());
01812 return;
01813 }
01814 finished ();
01815 }
01816
01817 void
01818 IMAP4Protocol::slave_status ()
01819 {
01820 bool connected = (getState() != ISTATE_NO) && isConnected();
01821 kDebug(7116) <<"IMAP4::slave_status" << connected;
01822 slaveStatus ( connected ? myHost : QString(), connected );
01823 }
01824
01825 void
01826 IMAP4Protocol::dispatch (int command, const QByteArray & data)
01827 {
01828 kDebug(7116) <<"IMAP4::dispatch - command=" << command;
01829 KIO::TCPSlaveBase::dispatch (command, data);
01830 }
01831
01832 void
01833 IMAP4Protocol::stat (const KUrl & _url)
01834 {
01835 kDebug(7116) <<"IMAP4::stat -" << _url.prettyUrl();
01836 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01837
01838 enum IMAP_TYPE aType =
01839 parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter,
01840 aInfo, true);
01841
01842 UDSEntry entry;
01843
01844 entry.insert( UDSEntry::UDS_NAME, aBox);
01845
01846 if (!aSection.isEmpty())
01847 {
01848 if (getState() == ISTATE_SELECT && aBox == getCurrentBox())
01849 {
01850 CommandPtr cmd = doCommand (imapCommand::clientClose());
01851 bool ok = cmd->result() == "OK";
01852 completeQueue.removeAll(cmd);
01853 if (!ok)
01854 {
01855 error(ERR_COULD_NOT_STAT, i18n("Unable to close mailbox."));
01856 return;
01857 }
01858 setState(ISTATE_LOGIN);
01859 }
01860 bool ok = false;
01861 QString cmdInfo;
01862 if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
01863 ok = true;
01864 else
01865 {
01866 CommandPtr cmd = doCommand(imapCommand::clientStatus(aBox, aSection));
01867 ok = cmd->result() == "OK";
01868 cmdInfo = cmd->resultInfo();
01869 completeQueue.removeAll(cmd);
01870 }
01871 if (!ok)
01872 {
01873 bool found = false;
01874 CommandPtr cmd = doCommand (imapCommand::clientList ("", aBox));
01875 if (cmd->result () == "OK")
01876 {
01877 for (QList< imapList >::Iterator it = listResponses.begin ();
01878 it != listResponses.end (); ++it)
01879 {
01880 if (aBox == (*it).name ()) found = true;
01881 }
01882 }
01883 completeQueue.removeAll (cmd);
01884 if (found)
01885 error(ERR_COULD_NOT_STAT, i18n("Unable to get information about folder %1. The server replied: %2", aBox, cmdInfo));
01886 else
01887 error(KIO::ERR_DOES_NOT_EXIST, aBox);
01888 return;
01889 }
01890 if ((aSection == "UIDNEXT" && getStatus().uidNextAvailable())
01891 || (aSection == "UNSEEN" && getStatus().unseenAvailable()))
01892 {
01893 entry.insert( UDSEntry::UDS_SIZE, (aSection == "UIDNEXT") ? getStatus().uidNext()
01894 : getStatus().unseen());
01895 }
01896 } else
01897 if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX || aType == ITYPE_MSG ||
01898 aType == ITYPE_ATTACH)
01899 {
01900 ulong validity = 0;
01901
01902 if (aBox == getCurrentBox ())
01903 validity = selectInfo.uidValidity ();
01904 else
01905 {
01906
01907
01908
01909 CommandPtr cmd =
01910 doCommand (imapCommand::clientStatus (aBox, "UIDVALIDITY"));
01911 completeQueue.removeAll (cmd);
01912 validity = getStatus ().uidValidity ();
01913 }
01914 #ifdef __GNUC__
01915 #warning This is temporary since Dec 2000 and makes most of the below code invalid
01916 #endif
01917 validity = 0;
01918
01919 if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX)
01920 {
01921
01922 if (validity > 0 && validity != aValidity.toULong ())
01923 {
01924
01925 KUrl newUrl = _url;
01926
01927 newUrl.setPath ('/' + aBox + ";UIDVALIDITY=" +
01928 QString::number(validity));
01929 kDebug(7116) <<"IMAP4::stat - redirecting to" << newUrl.prettyUrl();
01930 redirection (newUrl);
01931 }
01932 }
01933 else if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
01934 {
01935
01936
01937
01938
01939
01940 if (validity > 0 && validity != aValidity.toULong ())
01941 {
01942 aType = ITYPE_UNKNOWN;
01943 kDebug(7116) <<"IMAP4::stat - url has invalid validity [" << validity <<"d]" << _url.prettyUrl();
01944 }
01945 }
01946 }
01947
01948 entry.insert( UDSEntry::UDS_MIME_TYPE,getMimeType (aType));
01949
01950
01951 switch (aType)
01952 {
01953 case ITYPE_DIR:
01954 entry.insert( UDSEntry::UDS_FILE_TYPE, S_IFDIR);
01955 break;
01956
01957 case ITYPE_BOX:
01958 case ITYPE_DIR_AND_BOX:
01959 entry.insert(UDSEntry::UDS_FILE_TYPE, S_IFDIR);
01960 break;
01961
01962 case ITYPE_MSG:
01963 case ITYPE_ATTACH:
01964 entry.insert(UDSEntry::UDS_FILE_TYPE, S_IFREG);
01965 break;
01966
01967 case ITYPE_UNKNOWN:
01968 error (ERR_DOES_NOT_EXIST, _url.prettyUrl());
01969 break;
01970 }
01971
01972 statEntry (entry);
01973 kDebug(7116) <<"IMAP4::stat - Finishing stat";
01974 finished ();
01975 }
01976
01977 void IMAP4Protocol::openConnection()
01978 {
01979 if (makeLogin()) connected();
01980 }
01981
01982 void IMAP4Protocol::closeConnection()
01983 {
01984 if (getState() == ISTATE_NO) return;
01985 if (getState() == ISTATE_SELECT && metaData("expunge") == "auto")
01986 {
01987 CommandPtr cmd = doCommand (imapCommand::clientExpunge());
01988 completeQueue.removeAll (cmd);
01989 }
01990 if (getState() != ISTATE_CONNECT)
01991 {
01992 CommandPtr cmd = doCommand (imapCommand::clientLogout());
01993 completeQueue.removeAll (cmd);
01994 }
01995 disconnectFromHost();
01996 setState(ISTATE_NO);
01997 completeQueue.clear();
01998 sentQueue.clear();
01999 lastHandled = 0;
02000 currentBox.clear();
02001 readBufferLen = 0;
02002 }
02003
02004 bool IMAP4Protocol::makeLogin ()
02005 {
02006 if (getState () == ISTATE_LOGIN || getState () == ISTATE_SELECT)
02007 return true;
02008
02009 kDebug(7116) <<"IMAP4::makeLogin - checking login";
02010 bool alreadyConnected = getState() == ISTATE_CONNECT;
02011 kDebug(7116) <<"IMAP4::makeLogin - alreadyConnected" << alreadyConnected;
02012 if (alreadyConnected || connectToHost (( mySSL ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL ), myHost,
02013 myPort))
02014 {
02015
02016
02017 setState(ISTATE_CONNECT);
02018
02019 myAuth = metaData("auth");
02020 myTLS = metaData("tls");
02021 kDebug(7116) <<"myAuth:" << myAuth;
02022
02023 CommandPtr cmd;
02024
02025 unhandled.clear ();
02026 if (!alreadyConnected) while (!parseLoop ()) {}
02027 QString greeting;
02028 if (!unhandled.isEmpty()) greeting = unhandled.first().trimmed();
02029 unhandled.clear ();
02030 cmd = doCommand (CommandPtr(new imapCommand ("CAPABILITY", "")));
02031
02032 kDebug(7116) <<"IMAP4: setHost: capability";
02033 for (QStringList::const_iterator it = imapCapabilities.constBegin ();
02034 it != imapCapabilities.constEnd (); ++it)
02035 {
02036 kDebug(7116) <<"'" << (*it) <<"'";
02037 }
02038 completeQueue.removeAll (cmd);
02039
02040 if (!hasCapability("IMAP4") && !hasCapability("IMAP4rev1"))
02041 {
02042 error(ERR_COULD_NOT_LOGIN, i18n("The server %1 supports neither "
02043 "IMAP4 nor IMAP4rev1.\nIt identified itself with: %2",
02044 myHost, greeting));
02045 closeConnection();
02046 return false;
02047 }
02048
02049 if (metaData("nologin") == "on") return true;
02050
02051 if (myTLS == "on" && !hasCapability(QString("STARTTLS")))
02052 {
02053 error(ERR_COULD_NOT_LOGIN, i18n("The server does not support TLS.\n"
02054 "Disable this security feature to connect unencrypted."));
02055 closeConnection();
02056 return false;
02057 }
02058 if ((myTLS == "on" ) &&
02059 hasCapability(QString("STARTTLS")))
02060 {
02061 CommandPtr cmd = doCommand (imapCommand::clientStartTLS());
02062 if (cmd->result () == "OK")
02063 {
02064 completeQueue.removeAll(cmd);
02065 if (startSsl())
02066 {
02067 kDebug(7116) <<"TLS mode has been enabled.";
02068 CommandPtr cmd2 = doCommand (CommandPtr(new imapCommand ("CAPABILITY", "")));
02069 for (QStringList::const_iterator it = imapCapabilities.constBegin ();
02070 it != imapCapabilities.constEnd (); ++it)
02071 {
02072 kDebug(7116) <<"'" << (*it) <<"'";
02073 }
02074 completeQueue.removeAll (cmd2);
02075 } else {
02076 kWarning(7116) <<"TLS mode setup has failed. Aborting.";
02077 error (ERR_COULD_NOT_LOGIN, i18n("Starting TLS failed."));
02078 closeConnection();
02079 return false;
02080 }
02081 } else completeQueue.removeAll(cmd);
02082 }
02083
02084 if (!myAuth.isEmpty () && myAuth != "*"
02085 && !hasCapability (QString ("AUTH=") + myAuth))
02086 {
02087 error (ERR_COULD_NOT_LOGIN, i18n("The authentication method %1 is not "
02088 "supported by the server.", myAuth));
02089 closeConnection();
02090 return false;
02091 }
02092
02093 if ( greeting.contains( QRegExp( "Cyrus IMAP4 v2.1" ) ) ) {
02094 removeCapability( "ANNOTATEMORE" );
02095 }
02096
02097
02098 QRegExp regExp( "Cyrus\\sIMAP[4]{0,1}\\sv(\\d+)\\.(\\d+)\\.(\\d+)", Qt::CaseInsensitive );
02099 if ( regExp.indexIn( greeting ) >= 0 ) {
02100 const int major = regExp.cap( 1 ).toInt();
02101 const int minor = regExp.cap( 2 ).toInt();
02102 const int patch = regExp.cap( 3 ).toInt();
02103 if ( major > 2 || (major == 2 && (minor > 3 || (minor == 3 && patch > 9))) ) {
02104 kDebug(7116) << "Cyrus IMAP >= 2.3.9 detected, enabling shared seen flag support";
02105 imapCapabilities.append( "x-kmail-sharedseen" );
02106 }
02107 }
02108
02109 kDebug(7116) <<"IMAP4::makeLogin - attempting login";
02110
02111 KIO::AuthInfo authInfo;
02112 authInfo.username = myUser;
02113 authInfo.password = myPass;
02114 authInfo.prompt = i18n ("Username and password for your IMAP account:");
02115
02116 kDebug(7116) <<"IMAP4::makeLogin - open_PassDlg said user=" << myUser <<" pass=xx";
02117
02118 QString resultInfo;
02119 if (myAuth.isEmpty () || myAuth == "*")
02120 {
02121 if (myUser.isEmpty () || myPass.isEmpty ()) {
02122 if(openPasswordDialog (authInfo)) {
02123 myUser = authInfo.username;
02124 myPass = authInfo.password;
02125 }
02126 }
02127 if (!clientLogin (myUser, myPass, resultInfo))
02128 error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to login. Probably the "
02129 "password is wrong.\nThe server %1 replied:\n%2", myHost, resultInfo));
02130 }
02131 else
02132 {
02133 if (!clientAuthenticate (this, authInfo, myHost, myAuth, mySSL, resultInfo))
02134 error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to authenticate via %1.\n" "The server %2 replied:\n%3", myAuth, myHost, resultInfo));
02135 else {
02136 myUser = authInfo.username;
02137 myPass = authInfo.password;
02138 }
02139 }
02140 if ( hasCapability("NAMESPACE") )
02141 {
02142
02143 cmd = doCommand( imapCommand::clientNamespace() );
02144 if (cmd->result () == "OK")
02145 {
02146 kDebug(7116) <<"makeLogin - registered namespaces";
02147 }
02148 completeQueue.removeAll (cmd);
02149 }
02150
02151 cmd = doCommand( imapCommand::clientList("", "") );
02152 if (cmd->result () == "OK")
02153 {
02154 QList< imapList >::Iterator it = listResponses.begin();
02155 if ( it != listResponses.end() )
02156 {
02157 namespaceToDelimiter[QString()] = (*it).hierarchyDelimiter();
02158 kDebug(7116) <<"makeLogin - delimiter for empty ns='" << (*it).hierarchyDelimiter() <<"'";
02159 if ( !hasCapability("NAMESPACE") )
02160 {
02161
02162 QString nsentry = QString::number( 0 ) + "==" + (*it).hierarchyDelimiter();
02163 imapNamespaces.append( nsentry );
02164 }
02165 }
02166 }
02167 completeQueue.removeAll (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 CommandPtr 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 CommandPtr cmd;
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 }