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