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