kioslaves

imap4.cc

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