• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

kioslave/imap4

imap4.cpp

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

kioslave/imap4

Skip menu "kioslave/imap4"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.8
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal