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