kioslaves/imap4

imap4.cc

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  */
01210 void
01211 IMAP4Protocol::special (const QByteArray & aData)
01212 {
01213   kdDebug(7116) << "IMAP4Protocol::special" << endl;
01214   if (!makeLogin()) return;
01215 
01216   QDataStream stream(aData, IO_ReadOnly);
01217 
01218   int tmp;
01219   stream >> tmp;
01220 
01221   switch (tmp) {
01222   case 'C':
01223   {
01224     // copy
01225     KURL src;
01226     KURL dest;
01227     stream >> src >> dest;
01228     copy(src, dest, 0, FALSE);
01229     break;
01230   }
01231   case 'c':
01232   {
01233     // capabilities
01234     infoMessage(imapCapabilities.join(" "));
01235     finished();
01236     break;
01237   }
01238   case 'N':
01239   {
01240     // NOOP
01241     imapCommand *cmd = doCommand(imapCommand::clientNoop());
01242     if (cmd->result () != "OK")
01243     {
01244       kdDebug(7116) << "NOOP did not succeed - connection broken" << endl;
01245       completeQueue.removeRef (cmd);
01246       error (ERR_CONNECTION_BROKEN, myHost);
01247       return;
01248     }
01249     completeQueue.removeRef (cmd);
01250     finished();
01251     break;
01252   }
01253   case 'n':
01254   {
01255     // namespace in the form "section=namespace=delimiter"
01256     // entries are separated by ,
01257     infoMessage( imapNamespaces.join(",") );
01258     finished();
01259     break;
01260   }
01261   case 'U':
01262   {
01263     // unsubscribe
01264     KURL _url;
01265     stream >> _url;
01266     QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01267     parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01268     imapCommand *cmd = doCommand(imapCommand::clientUnsubscribe(aBox));
01269     if (cmd->result () != "OK")
01270     {
01271       completeQueue.removeRef (cmd);
01272       error(ERR_SLAVE_DEFINED, i18n("Unsubscribe of folder %1 "
01273                                     "failed. The server returned: %2")
01274             .arg(_url.prettyURL())
01275             .arg(cmd->resultInfo()));
01276       return;
01277     }
01278     completeQueue.removeRef (cmd);
01279     finished();
01280     break;
01281   }
01282   case 'u':
01283   {
01284     // subscribe
01285     KURL _url;
01286     stream >> _url;
01287     QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01288     parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01289     imapCommand *cmd = doCommand(imapCommand::clientSubscribe(aBox));
01290     if (cmd->result () != "OK")
01291     {
01292       completeQueue.removeRef (cmd);
01293       error(ERR_SLAVE_DEFINED, i18n("Subscribe of folder %1 "
01294                                     "failed. The server returned: %2")
01295             .arg(_url.prettyURL())
01296             .arg(cmd->resultInfo()));
01297       return;
01298     }
01299     completeQueue.removeRef (cmd);
01300     finished();
01301     break;
01302   }
01303   case 'A':
01304   {
01305     // acl
01306     int cmd;
01307     stream >> cmd;
01308     if ( hasCapability( "ACL" ) ) {
01309       specialACLCommand( cmd, stream );
01310     } else {
01311       error( ERR_UNSUPPORTED_ACTION, "ACL" );
01312     }
01313     break;
01314   }
01315   case 'M':
01316   {
01317     // annotatemore
01318     int cmd;
01319     stream >> cmd;
01320     if ( hasCapability( "ANNOTATEMORE" ) ) {
01321       specialAnnotateMoreCommand( cmd, stream );
01322     } else {
01323       error( ERR_UNSUPPORTED_ACTION, "ANNOTATEMORE" );
01324     }
01325     break;
01326   }
01327   case 'Q':
01328   {
01329     // quota
01330     int cmd;
01331     stream >> cmd;
01332     if ( hasCapability( "QUOTA" ) ) {
01333       specialQuotaCommand( cmd, stream );
01334     } else {
01335       error( ERR_UNSUPPORTED_ACTION, "QUOTA" );
01336     }
01337     break;
01338   }
01339   case 'S':
01340   {
01341     // status
01342     KURL _url;
01343     QCString newFlags;
01344     stream >> _url >> newFlags;
01345 
01346     QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01347     parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01348     if (!assureBox(aBox, false)) return;
01349     imapCommand *cmd = doCommand (imapCommand::
01350                                   clientStore (aSequence, "-FLAGS.SILENT",
01351                                                "\\SEEN \\ANSWERED \\FLAGGED \\DRAFT"));
01352     if (cmd->result () != "OK")
01353     {
01354       completeQueue.removeRef (cmd);
01355       error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
01356                                       "failed.").arg(_url.prettyURL()));
01357       return;
01358     }
01359     completeQueue.removeRef (cmd);
01360     if (!newFlags.isEmpty())
01361     {
01362       cmd = doCommand (imapCommand::
01363                        clientStore (aSequence, "+FLAGS.SILENT", newFlags));
01364       if (cmd->result () != "OK")
01365       {
01366         completeQueue.removeRef (cmd);
01367         error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
01368                                         "failed.").arg(_url.prettyURL()));
01369         return;
01370       }
01371       completeQueue.removeRef (cmd);
01372     }
01373     finished();
01374     break;
01375   }
01376   case 's':
01377   {
01378     // seen
01379     KURL _url;
01380     bool seen;
01381     QCString newFlags;
01382     stream >> _url >> seen;
01383 
01384     QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01385     parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01386     if ( !assureBox(aBox, true) ) // read-only because changing SEEN should be possible even then
01387       return;
01388 
01389     imapCommand *cmd;
01390     if ( seen )
01391       cmd = doCommand( imapCommand::clientStore( aSequence, "+FLAGS.SILENT", "\\SEEN" ) );
01392     else
01393       cmd = doCommand( imapCommand::clientStore( aSequence, "-FLAGS.SILENT", "\\SEEN" ) );
01394 
01395     if (cmd->result () != "OK")
01396     {
01397       completeQueue.removeRef (cmd);
01398       error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
01399                                       "failed.").arg(_url.prettyURL()));
01400       return;
01401     }
01402     completeQueue.removeRef (cmd);
01403     finished();
01404     break;
01405   }
01406 
01407   case 'E':
01408   {
01409     // search
01410     specialSearchCommand( stream );
01411     break;
01412   }
01413   default:
01414     kdWarning(7116) << "Unknown command in special(): " << tmp << endl;
01415     error( ERR_UNSUPPORTED_ACTION, QString(QChar(tmp)) );
01416     break;
01417   }
01418 }
01419 
01420 void
01421 IMAP4Protocol::specialACLCommand( int command, QDataStream& stream )
01422 {
01423   // All commands start with the URL to the box
01424   KURL _url;
01425   stream >> _url;
01426   QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01427   parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01428 
01429   switch( command ) {
01430   case 'S': // SETACL
01431   {
01432     QString user, acl;
01433     stream >> user >> acl;
01434     kdDebug(7116) << "SETACL " << aBox << " " << user << " " << acl << endl;
01435     imapCommand *cmd = doCommand(imapCommand::clientSetACL(aBox, user, acl));
01436     if (cmd->result () != "OK")
01437     {
01438       error(ERR_SLAVE_DEFINED, i18n("Setting the Access Control List on folder %1 "
01439                                       "for user %2 failed. The server returned: %3")
01440             .arg(_url.prettyURL())
01441             .arg(user)
01442             .arg(cmd->resultInfo()));
01443       return;
01444     }
01445     completeQueue.removeRef (cmd);
01446     finished();
01447     break;
01448   }
01449   case 'D': // DELETEACL
01450   {
01451     QString user;
01452     stream >> user;
01453     kdDebug(7116) << "DELETEACL " << aBox << " " << user << endl;
01454     imapCommand *cmd = doCommand(imapCommand::clientDeleteACL(aBox, user));
01455     if (cmd->result () != "OK")
01456     {
01457       error(ERR_SLAVE_DEFINED, i18n("Deleting the Access Control List on folder %1 "
01458                                     "for user %2 failed. The server returned: %3")
01459             .arg(_url.prettyURL())
01460             .arg(user)
01461             .arg(cmd->resultInfo()));
01462       return;
01463     }
01464     completeQueue.removeRef (cmd);
01465     finished();
01466     break;
01467   }
01468   case 'G': // GETACL
01469   {
01470     kdDebug(7116) << "GETACL " << aBox << endl;
01471     imapCommand *cmd = doCommand(imapCommand::clientGetACL(aBox));
01472     if (cmd->result () != "OK")
01473     {
01474       error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
01475                                      "failed. The server returned: %2")
01476             .arg(_url.prettyURL())
01477             .arg(cmd->resultInfo()));
01478       return;
01479     }
01480     // Returning information to the application from a special() command isn't easy.
01481     // I'm reusing the infoMessage trick seen above (for capabilities), but this
01482     // limits me to a string instead of a stringlist. I'm using space as separator,
01483     // since I don't think it can be used in login names.
01484     kdDebug(7116) << getResults() << endl;
01485     infoMessage(getResults().join( " " ));
01486     finished();
01487     break;
01488   }
01489   case 'L': // LISTRIGHTS
01490   {
01491     // Do we need this one? It basically shows which rights are tied together, but that's all?
01492     error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01493     break;
01494   }
01495   case 'M': // MYRIGHTS
01496   {
01497     kdDebug(7116) << "MYRIGHTS " << aBox << endl;
01498     imapCommand *cmd = doCommand(imapCommand::clientMyRights(aBox));
01499     if (cmd->result () != "OK")
01500     {
01501       error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
01502                                     "failed. The server returned: %2")
01503             .arg(_url.prettyURL())
01504             .arg(cmd->resultInfo()));
01505       return;
01506     }
01507     QStringList lst = getResults();
01508     kdDebug(7116) << "myrights results: " << lst << endl;
01509     if ( !lst.isEmpty() ) {
01510       Q_ASSERT( lst.count() == 1 );
01511       infoMessage( lst.first() );
01512     }
01513     finished();
01514     break;
01515   }
01516   default:
01517     kdWarning(7116) << "Unknown special ACL command:" << command << endl;
01518     error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01519   }
01520 }
01521 
01522 void
01523 IMAP4Protocol::specialSearchCommand( QDataStream& stream )
01524 {
01525   kdDebug(7116) << "IMAP4Protocol::specialSearchCommand" << endl;
01526   KURL _url;
01527   stream >> _url;
01528   QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01529   parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01530   if (!assureBox(aBox, false)) return;
01531 
01532   imapCommand *cmd = doCommand (imapCommand::clientSearch( aSection ));
01533   if (cmd->result () != "OK")
01534   {
01535     error(ERR_SLAVE_DEFINED, i18n("Searching of folder %1 "
01536           "failed. The server returned: %2")
01537         .arg(aBox)
01538         .arg(cmd->resultInfo()));
01539     return;
01540   }
01541   completeQueue.removeRef(cmd);
01542   QStringList lst = getResults();
01543   kdDebug(7116) << "IMAP4Protocol::specialSearchCommand '" << aSection <<
01544     "' returns " << lst << endl;
01545   infoMessage( lst.join( " " ) );
01546 
01547   finished();
01548 }
01549 
01550 void
01551 IMAP4Protocol::specialAnnotateMoreCommand( int command, QDataStream& stream )
01552 {
01553   // All commands start with the URL to the box
01554   KURL _url;
01555   stream >> _url;
01556   QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01557   parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01558 
01559   switch( command ) {
01560   case 'S': // SETANNOTATION
01561   {
01562     // Params:
01563     //  KURL URL of the mailbox
01564     //  QString entry (should be an actual entry name, no % or *; empty for server entries)
01565     //  QMap<QString,QString> attributes (name and value)
01566     QString entry;
01567     QMap<QString, QString> attributes;
01568     stream >> entry >> attributes;
01569     kdDebug(7116) << "SETANNOTATION " << aBox << " " << entry << " " << attributes.count() << " attributes" << endl;
01570     imapCommand *cmd = doCommand(imapCommand::clientSetAnnotation(aBox, entry, attributes));
01571     if (cmd->result () != "OK")
01572     {
01573       error(ERR_SLAVE_DEFINED, i18n("Setting the annotation %1 on folder %2 "
01574                                     " failed. The server returned: %3")
01575             .arg(entry)
01576             .arg(_url.prettyURL())
01577             .arg(cmd->resultInfo()));
01578       return;
01579     }
01580     completeQueue.removeRef (cmd);
01581     finished();
01582     break;
01583   }
01584   case 'G': // GETANNOTATION.
01585   {
01586     // Params:
01587     //  KURL URL of the mailbox
01588     //  QString entry (should be an actual entry name, no % or *; empty for server entries)
01589     //  QStringList attributes (list of attributes to be retrieved, possibly with % or *)
01590     QString entry;
01591     QStringList attributeNames;
01592     stream >> entry >> attributeNames;
01593     kdDebug(7116) << "GETANNOTATION " << aBox << " " << entry << " " << attributeNames << endl;
01594     imapCommand *cmd = doCommand(imapCommand::clientGetAnnotation(aBox, entry, attributeNames));
01595     if (cmd->result () != "OK")
01596     {
01597       error(ERR_SLAVE_DEFINED, i18n("Retrieving the annotation %1 on folder %2 "
01598                                      "failed. The server returned: %3")
01599             .arg(entry)
01600             .arg(_url.prettyURL())
01601             .arg(cmd->resultInfo()));
01602       return;
01603     }
01604     // Returning information to the application from a special() command isn't easy.
01605     // I'm reusing the infoMessage trick seen above (for capabilities and acls), but this
01606     // limits me to a string instead of a stringlist. Let's use \r as separator.
01607     kdDebug(7116) << getResults() << endl;
01608     infoMessage(getResults().join( "\r" ));
01609     finished();
01610     break;
01611   }
01612   default:
01613     kdWarning(7116) << "Unknown special annotate command:" << command << endl;
01614     error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01615   }
01616 }
01617 
01618 void
01619 IMAP4Protocol::specialQuotaCommand( int command, QDataStream& stream )
01620 {
01621   // All commands start with the URL to the box
01622   KURL _url;
01623   stream >> _url;
01624   QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01625   parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01626 
01627   switch( command ) {
01628     case 'R': // GETQUOTAROOT
01629       {
01630         kdDebug(7116) << "QUOTAROOT " << aBox << endl;
01631         imapCommand *cmd = doCommand(imapCommand::clientGetQuotaroot( aBox ) );
01632         if (cmd->result () != "OK")
01633         {
01634           error(ERR_SLAVE_DEFINED, i18n("Retrieving the quota root information on folder %1 "
01635                 "failed. The server returned: %2")
01636               .arg(_url.prettyURL())
01637               .arg(cmd->resultInfo()));
01638           return;
01639         }
01640         infoMessage(getResults().join( "\r" ));
01641         finished();
01642         break;
01643       }
01644     case 'G': // GETQUOTA
01645       {
01646         kdDebug(7116) << "GETQUOTA command" << endl;
01647         kdWarning(7116) << "UNIMPLEMENTED" << endl;
01648         break;
01649       }
01650     case 'S': // SETQUOTA
01651       {
01652         kdDebug(7116) << "SETQUOTA command" << endl;
01653         kdWarning(7116) << "UNIMPLEMENTED" << endl;
01654         break;
01655       }
01656     default:
01657       kdWarning(7116) << "Unknown special quota command:" << command << endl;
01658       error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01659   }
01660 }
01661 
01662 void
01663 IMAP4Protocol::rename (const KURL & src, const KURL & dest, bool overwrite)
01664 {
01665   kdDebug(7116) << "IMAP4::rename - [" << (overwrite ? "Overwrite" : "NoOverwrite") << "] " << src.prettyURL() << " -> " << dest.prettyURL() << endl;
01666   QString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
01667   QString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo;
01668   enum IMAP_TYPE sType =
01669     parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo, false);
01670   enum IMAP_TYPE dType =
01671     parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo, false);
01672 
01673   if (dType == ITYPE_UNKNOWN)
01674   {
01675     switch (sType)
01676     {
01677     case ITYPE_BOX:
01678     case ITYPE_DIR:
01679     case ITYPE_DIR_AND_BOX:
01680       {
01681         if (getState() == ISTATE_SELECT && sBox == getCurrentBox())
01682         {
01683           kdDebug(7116) << "IMAP4::rename - close " << getCurrentBox() << endl;
01684           // mailbox can only be renamed if it is closed
01685           imapCommand *cmd = doCommand (imapCommand::clientClose());
01686           bool ok = cmd->result() == "OK";
01687           completeQueue.removeRef(cmd);
01688           if (!ok)
01689           {
01690             error(ERR_CANNOT_RENAME, i18n("Unable to close mailbox."));
01691             return;
01692           }
01693           setState(ISTATE_LOGIN);
01694         }
01695         imapCommand *cmd = doCommand (imapCommand::clientRename (sBox, dBox));
01696         if (cmd->result () != "OK") {
01697           error (ERR_CANNOT_RENAME, cmd->result ());
01698           completeQueue.removeRef (cmd);
01699           return;
01700         }
01701         completeQueue.removeRef (cmd);
01702       }
01703       break;
01704 
01705     case ITYPE_MSG:
01706     case ITYPE_ATTACH:
01707     case ITYPE_UNKNOWN:
01708       error (ERR_CANNOT_RENAME, src.prettyURL());
01709       break;
01710     }
01711   }
01712   else
01713   {
01714     error (ERR_CANNOT_RENAME, src.prettyURL());
01715     return;
01716   }
01717   finished ();
01718 }
01719 
01720 void
01721 IMAP4Protocol::slave_status ()
01722 {
01723   bool connected = (getState() != ISTATE_NO) && isConnectionValid();
01724   kdDebug(7116) << "IMAP4::slave_status " << connected << endl;
01725   slaveStatus ( connected ? myHost : QString::null, connected );
01726 }
01727 
01728 void
01729 IMAP4Protocol::dispatch (int command, const QByteArray & data)
01730 {
01731   kdDebug(7116) << "IMAP4::dispatch - command=" << command << endl;
01732   KIO::TCPSlaveBase::dispatch (command, data);
01733 }
01734 
01735 void
01736 IMAP4Protocol::stat (const KURL & _url)
01737 {
01738   kdDebug(7116) << "IMAP4::stat - " << _url.prettyURL() << endl;
01739   QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01740   // parseURL with caching
01741   enum IMAP_TYPE aType =
01742     parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter,
01743         aInfo, true);
01744 
01745   UDSEntry entry;
01746   UDSAtom atom;
01747 
01748   atom.m_uds = UDS_NAME;
01749   atom.m_str = aBox;
01750   entry.append (atom);
01751 
01752   if (!aSection.isEmpty())
01753   {
01754     if (getState() == ISTATE_SELECT && aBox == getCurrentBox())
01755     {
01756       imapCommand *cmd = doCommand (imapCommand::clientClose());
01757       bool ok = cmd->result() == "OK";
01758       completeQueue.removeRef(cmd);
01759       if (!ok)
01760       {
01761         error(ERR_COULD_NOT_STAT, aBox);
01762         return;
01763       }
01764       setState(ISTATE_LOGIN);
01765     }
01766     bool ok = false;
01767     QString cmdInfo;
01768     if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
01769       ok = true;
01770     else
01771     {
01772       imapCommand *cmd = doCommand(imapCommand::clientStatus(aBox, aSection));
01773       ok = cmd->result() == "OK";
01774       cmdInfo = cmd->resultInfo();
01775       completeQueue.removeRef(cmd);
01776     }
01777     if (!ok)
01778     {
01779       bool found = false;
01780       imapCommand *cmd = doCommand (imapCommand::clientList ("", aBox));
01781       if (cmd->result () == "OK")
01782       {
01783         for (QValueListIterator < imapList > it = listResponses.begin ();
01784              it != listResponses.end (); ++it)
01785         {
01786           if (aBox == (*it).name ()) found = true;
01787         }
01788       }
01789       completeQueue.removeRef (cmd);
01790       if (found)
01791         error(ERR_COULD_NOT_STAT, aBox);
01792       else
01793         error(KIO::ERR_DOES_NOT_EXIST, aBox);
01794       return;
01795     }
01796     if ((aSection == "UIDNEXT" && getStatus().uidNextAvailable())
01797       || (aSection == "UNSEEN" && getStatus().unseenAvailable()))
01798     {
01799       atom.m_uds = UDS_SIZE;
01800       atom.m_str = QString::null;
01801       atom.m_long = (aSection == "UIDNEXT") ? getStatus().uidNext()
01802         : getStatus().unseen();
01803       entry.append(atom);
01804     }
01805   } else
01806   if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX || aType == ITYPE_MSG ||
01807       aType == ITYPE_ATTACH)
01808   {
01809     ulong validity = 0;
01810     // see if the box is already in select/examine state
01811     if (aBox == getCurrentBox ())
01812       validity = selectInfo.uidValidity ();
01813     else
01814     {
01815       // do a status lookup on the box
01816       // only do this if the box is not selected
01817       // the server might change the validity for new select/examine
01818       imapCommand *cmd =
01819         doCommand (imapCommand::clientStatus (aBox, "UIDVALIDITY"));
01820       completeQueue.removeRef (cmd);
01821       validity = getStatus ().uidValidity ();
01822     }
01823     validity = 0;               // temporary
01824 
01825     if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX)
01826     {
01827       // has no or an invalid uidvalidity
01828       if (validity > 0 && validity != aValidity.toULong ())
01829       {
01830         //redirect
01831         KURL newUrl = _url;
01832 
01833         newUrl.setPath ("/" + aBox + ";UIDVALIDITY=" +
01834                         QString::number(validity));
01835         kdDebug(7116) << "IMAP4::stat - redirecting to " << newUrl.prettyURL() << endl;
01836         redirection (newUrl);
01837       }
01838     }
01839     else if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
01840     {
01841       //must determine if this message exists
01842       //cause konqueror will check this on paste operations
01843 
01844       // has an invalid uidvalidity
01845       // or no messages in box
01846       if (validity > 0 && validity != aValidity.toULong ())
01847       {
01848         aType = ITYPE_UNKNOWN;
01849         kdDebug(7116) << "IMAP4::stat - url has invalid validity [" << validity << "d] " << _url.prettyURL() << endl;
01850       }
01851     }
01852   }
01853 
01854   atom.m_uds = UDS_MIME_TYPE;
01855   atom.m_str = getMimeType (aType);
01856   entry.append (atom);
01857 
01858   kdDebug(7116) << "IMAP4: stat: " << atom.m_str << endl;
01859   switch (aType)
01860   {
01861   case ITYPE_DIR:
01862     atom.m_uds = UDS_FILE_TYPE;
01863     atom.m_str = QString::null;
01864     atom.m_long = S_IFDIR;
01865     entry.append (atom);
01866     break;
01867 
01868   case ITYPE_BOX:
01869   case ITYPE_DIR_AND_BOX:
01870     atom.m_uds = UDS_FILE_TYPE;
01871     atom.m_str = QString::null;
01872     atom.m_long = S_IFDIR;
01873     entry.append (atom);
01874     break;
01875 
01876   case ITYPE_MSG:
01877   case ITYPE_ATTACH:
01878     atom.m_uds = UDS_FILE_TYPE;
01879     atom.m_str = QString::null;
01880     atom.m_long = S_IFREG;
01881     entry.append (atom);
01882     break;
01883 
01884   case ITYPE_UNKNOWN:
01885     error (ERR_DOES_NOT_EXIST, _url.prettyURL());
01886     break;
01887   }
01888 
01889   statEntry (entry);
01890   kdDebug(7116) << "IMAP4::stat - Finishing stat" << endl;
01891   finished ();
01892 }
01893 
01894 void IMAP4Protocol::openConnection()
01895 {
01896   if (makeLogin()) connected();
01897 }
01898 
01899 void IMAP4Protocol::closeConnection()
01900 {
01901   if (getState() == ISTATE_NO) return;
01902   if (getState() == ISTATE_SELECT && metaData("expunge") == "auto")
01903   {
01904     imapCommand *cmd = doCommand (imapCommand::clientExpunge());
01905     completeQueue.removeRef (cmd);
01906   }
01907   if (getState() != ISTATE_CONNECT)
01908   {
01909     imapCommand *cmd = doCommand (imapCommand::clientLogout());
01910     completeQueue.removeRef (cmd);
01911   }
01912   closeDescriptor();
01913   setState(ISTATE_NO);
01914   completeQueue.clear();
01915   sentQueue.clear();
01916   lastHandled = 0;
01917   currentBox = QString::null;
01918   readBufferLen = 0;
01919 }
01920 
01921 bool IMAP4Protocol::makeLogin ()
01922 {
01923   if (getState () == ISTATE_LOGIN || getState () == ISTATE_SELECT)
01924     return true;
01925 
01926   kdDebug(7116) << "IMAP4::makeLogin - checking login" << endl;
01927   bool alreadyConnected = getState() == ISTATE_CONNECT;
01928   kdDebug(7116) << "IMAP4::makeLogin - alreadyConnected " << alreadyConnected << endl;
01929   if (alreadyConnected || connectToHost (myHost.latin1(), myPort))
01930   {
01931 //      fcntl (m_iSock, F_SETFL, (fcntl (m_iSock, F_GETFL) | O_NDELAY));
01932 
01933     setState(ISTATE_CONNECT);
01934 
01935     myAuth = metaData("auth");
01936     myTLS  = metaData("tls");
01937     kdDebug(7116) << "myAuth: " << myAuth << endl;
01938 
01939     imapCommand *cmd;
01940 
01941     unhandled.clear ();
01942     if (!alreadyConnected) while (!parseLoop ());    //get greeting
01943     QString greeting;
01944     if (!unhandled.isEmpty()) greeting = unhandled.first().stripWhiteSpace();
01945     unhandled.clear ();       //get rid of it
01946     cmd = doCommand (new imapCommand ("CAPABILITY", ""));
01947 
01948     kdDebug(7116) << "IMAP4: setHost: capability" << endl;
01949     for (QStringList::Iterator it = imapCapabilities.begin ();
01950          it != imapCapabilities.end (); ++it)
01951     {
01952       kdDebug(7116) << "'" << (*it) << "'" << endl;
01953     }
01954     completeQueue.removeRef (cmd);
01955 
01956     if (!hasCapability("IMAP4") && !hasCapability("IMAP4rev1"))
01957     {
01958       error(ERR_COULD_NOT_LOGIN, i18n("The server %1 supports neither "
01959         "IMAP4 nor IMAP4rev1.\nIt identified itself with: %2")
01960         .arg(myHost).arg(greeting));
01961       closeConnection();
01962       return false;
01963     }
01964 
01965     if (metaData("nologin") == "on") return TRUE;
01966 
01967     if (myTLS == "on" && !hasCapability(QString("STARTTLS")))
01968     {
01969       error(ERR_COULD_NOT_LOGIN, i18n("The server does not support TLS.\n"
01970         "Disable this security feature to connect unencrypted."));
01971       closeConnection();
01972       return false;
01973     }
01974     if ((myTLS == "on" || (canUseTLS() && myTLS != "off")) &&
01975       hasCapability(QString("STARTTLS")))
01976     {
01977       imapCommand *cmd = doCommand (imapCommand::clientStartTLS());
01978       if (cmd->result () == "OK")
01979       {
01980         completeQueue.removeRef(cmd);
01981         int tlsrc = startTLS();
01982         if (tlsrc == 1)
01983         {
01984           kdDebug(7116) << "TLS mode has been enabled." << endl;
01985           imapCommand *cmd2 = doCommand (new imapCommand ("CAPABILITY", ""));
01986           for (QStringList::Iterator it = imapCapabilities.begin ();
01987                                      it != imapCapabilities.end (); ++it)
01988           {
01989             kdDebug(7116) << "'" << (*it) << "'" << endl;
01990           }
01991           completeQueue.removeRef (cmd2);
01992         } else {
01993           kdWarning(7116) << "TLS mode setup has failed.  Aborting." << endl;
01994           error (ERR_COULD_NOT_LOGIN, i18n("Starting TLS failed."));
01995           closeConnection();
01996           return false;
01997         }
01998       } else completeQueue.removeRef(cmd);
01999     }
02000 
02001     if (myAuth.isEmpty () || myAuth == "*") {
02002       if (hasCapability (QString ("LOGINDISABLED"))) {
02003         error (ERR_COULD_NOT_LOGIN, i18n("LOGIN is disabled by the server."));
02004         closeConnection();
02005         return false;
02006       }
02007     }
02008     else {
02009       if (!hasCapability (QString ("AUTH=") + myAuth)) {
02010         error (ERR_COULD_NOT_LOGIN, i18n("The authentication method %1 is not "
02011           "supported by the server.").arg(myAuth));
02012         closeConnection();
02013         return false;
02014       }
02015     }
02016 
02017     if ( greeting.contains(  QRegExp(  "Cyrus IMAP4 v2.1" ) ) ) {
02018       removeCapability( "ANNOTATEMORE" );
02019     }
02020 
02021     kdDebug(7116) << "IMAP4::makeLogin - attempting login" << endl;
02022 
02023     KIO::AuthInfo authInfo;
02024     authInfo.username = myUser;
02025     authInfo.password = myPass;
02026     authInfo.prompt = i18n ("Username and password for your IMAP account:");
02027 
02028     kdDebug(7116) << "IMAP4::makeLogin - open_PassDlg said user=" << myUser << " pass=xx" << endl;
02029 
02030     QString resultInfo;
02031     if (myAuth.isEmpty () || myAuth == "*")
02032     {
02033       if (myUser.isEmpty () || myPass.isEmpty ()) {
02034         if(openPassDlg (authInfo)) {
02035           myUser = authInfo.username;
02036           myPass = authInfo.password;
02037         }
02038       }
02039       if (!clientLogin (myUser, myPass, resultInfo))
02040         error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to login. Probably the "
02041         "password is wrong.\nThe server %1 replied:\n%2").arg(myHost).arg(resultInfo));
02042     }
02043     else
02044     {
02045 #ifdef HAVE_LIBSASL2
02046       if (!clientAuthenticate (this, authInfo, myHost, myAuth, mySSL, resultInfo))
02047         error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to authenticate via %1.\n"
02048     "The server %2 replied:\n%3").arg(myAuth).arg(myHost).arg(resultInfo));
02049       else {
02050         myUser = authInfo.username;
02051         myPass = authInfo.password;
02052       }
02053 #else
02054       error(KIO::ERR_COULD_NOT_LOGIN, i18n("SASL authentication is not compiled into kio_imap4."));
02055 #endif
02056     }
02057     if ( hasCapability("NAMESPACE") )
02058     {
02059       // get all namespaces and save the namespace - delimiter association
02060       cmd = doCommand( imapCommand::clientNamespace() );
02061       if (cmd->result () == "OK")
02062       {
02063         kdDebug(7116) << "makeLogin - registered namespaces" << endl;
02064       }
02065       completeQueue.removeRef (cmd);
02066     }
02067     // get the default delimiter (empty listing)
02068     cmd = doCommand( imapCommand::clientList("", "") );
02069     if (cmd->result () == "OK")
02070     {
02071       QValueListIterator < imapList > it = listResponses.begin();
02072       if ( it == listResponses.end() )
02073       {
02074           // empty answer - this is a buggy imap server
02075           // as a fallback we fire a normal listing and take the first answer
02076           completeQueue.removeRef (cmd);
02077           cmd = doCommand( imapCommand::clientList("", "%") );
02078           if (cmd->result () == "OK")
02079           {
02080               it = listResponses.begin();
02081           }
02082       }
02083       if ( it != listResponses.end() )
02084       {
02085         namespaceToDelimiter[QString::null] = (*it).hierarchyDelimiter();
02086         kdDebug(7116) << "makeLogin - delimiter for empty ns='" <<
02087           (*it).hierarchyDelimiter() << "'" << endl;
02088         if ( !hasCapability("NAMESPACE") )
02089         {
02090           // server does not support namespaces
02091           QString nsentry = QString::number( 0 ) + "=="
02092             + (*it).hierarchyDelimiter();
02093           imapNamespaces.append( nsentry );
02094         }
02095       }
02096     }
02097     completeQueue.removeRef (cmd);
02098   } else {
02099     kdDebug(7116) << "makeLogin - NO login" << endl;
02100   }
02101 
02102   return getState() == ISTATE_LOGIN;
02103 }
02104 
02105 void
02106 IMAP4Protocol::parseWriteLine (const QString & aStr)
02107 {
02108   //kdDebug(7116) << "Writing: " << aStr << endl;
02109   QCString writer = aStr.utf8();
02110   int len = writer.length();
02111 
02112   // append CRLF if necessary
02113   if (len == 0 || (writer[len - 1] != '\n')) {
02114     len += 2;
02115     writer += "\r\n";
02116   }
02117 
02118   // write it
02119   write(writer.data(), len);
02120 }
02121 
02122 QString
02123 IMAP4Protocol::getMimeType (enum IMAP_TYPE aType)
02124 {
02125   switch (aType)
02126   {
02127   case ITYPE_DIR:
02128     return "inode/directory";
02129     break;
02130 
02131   case ITYPE_BOX:
02132     return "message/digest";
02133     break;
02134 
02135   case ITYPE_DIR_AND_BOX:
02136     return "message/directory";
02137     break;
02138 
02139   case ITYPE_MSG:
02140     return "message/rfc822";
02141     break;
02142 
02143   // this should be handled by flushOutput
02144   case ITYPE_ATTACH:
02145     return "application/octet-stream";
02146     break;
02147 
02148   case ITYPE_UNKNOWN:
02149   default:
02150     return "unknown/unknown";
02151   }
02152 }
02153 
02154 
02155 
02156 void
02157 IMAP4Protocol::doListEntry (const KURL & _url, int stretch, imapCache * cache,
02158   bool withFlags, bool withSubject)
02159 {
02160   KURL aURL = _url;
02161   aURL.setQuery (QString::null);
02162   const QString encodedUrl = aURL.url(0, 106); // utf-8
02163   doListEntry(encodedUrl, stretch, cache, withFlags, withSubject);
02164 }
02165 
02166 
02167 
02168 void
02169 IMAP4Protocol::doListEntry (const QString & encodedUrl, int stretch, imapCache * cache,
02170   bool withFlags, bool withSubject)
02171 {
02172   if (cache)
02173   {
02174     UDSEntry entry;
02175     UDSAtom atom;
02176 
02177     entry.clear ();
02178 
02179     const QString uid = QString::number(cache->getUid());
02180 
02181     atom.m_uds = UDS_NAME;
02182     atom.m_str = uid;
02183     atom.m_long = 0;
02184     if (stretch > 0)
02185     {
02186       atom.m_str = "0000000000000000" + atom.m_str;
02187       atom.m_str = atom.m_str.right (stretch);
02188     }
02189     if (withSubject)
02190     {
02191       mailHeader *header = cache->getHeader();
02192       if (header)
02193         atom.m_str += " " + header->getSubject();
02194     }
02195     entry.append (atom);
02196 
02197     atom.m_uds = UDS_URL;
02198     atom.m_str = encodedUrl; // utf-8
02199     if (atom.m_str[atom.m_str.length () - 1] != '/')
02200       atom.m_str += '/';
02201     atom.m_str += ";UID=" + uid;
02202     atom.m_long = 0;
02203     entry.append (atom);
02204 
02205     atom.m_uds = UDS_FILE_TYPE;
02206     atom.m_str = QString::null;
02207     atom.m_long = S_IFREG;
02208     entry.append (atom);
02209 
02210     atom.m_uds = UDS_SIZE;
02211     atom.m_long = cache->getSize();
02212     entry.append (atom);
02213 
02214     atom.m_uds = UDS_MIME_TYPE;
02215     atom.m_str = "message/rfc822";
02216     atom.m_long = 0;
02217     entry.append (atom);
02218 
02219     atom.m_uds = UDS_USER;
02220     atom.m_str = myUser;
02221     entry.append (atom);
02222 
02223     atom.m_uds = KIO::UDS_ACCESS;
02224     atom.m_long = (withFlags) ? cache->getFlags() : S_IRUSR | S_IXUSR | S_IWUSR;
02225     entry.append (atom);
02226 
02227     listEntry (entry, false);
02228   }
02229 }
02230 
02231 void
02232 IMAP4Protocol::doListEntry (const KURL & _url, const QString & myBox,
02233                             const imapList & item, bool appendPath)
02234 {
02235   KURL aURL = _url;
02236   aURL.setQuery (QString::null);
02237   UDSEntry entry;
02238   UDSAtom atom;
02239   int hdLen = item.hierarchyDelimiter().length();
02240 
02241   {
02242     // mailboxName will be appended to the path if appendPath is true
02243     QString mailboxName = item.name ();
02244 
02245     // some beautification
02246     if (mailboxName.find (myBox) == 0 && mailboxName.length() > myBox.length())
02247     {
02248       mailboxName =
02249         mailboxName.right (mailboxName.length () - myBox.length ());
02250     }
02251     if (mailboxName[0] == '/')
02252         mailboxName = mailboxName.right (mailboxName.length () - 1);
02253     if (mailboxName.left(hdLen) == item.hierarchyDelimiter())
02254       mailboxName = mailboxName.right(mailboxName.length () - hdLen);
02255     if (mailboxName.right(hdLen) == item.hierarchyDelimiter())
02256       mailboxName.truncate(mailboxName.length () - hdLen);
02257 
02258     atom.m_uds = UDS_NAME;
02259     if (!item.hierarchyDelimiter().isEmpty() &&
02260         mailboxName.find(item.hierarchyDelimiter()) != -1)
02261       atom.m_str = mailboxName.section(item.hierarchyDelimiter(), -1);
02262     else
02263       atom.m_str = mailboxName;
02264 
02265     // konqueror will die with an assertion failure otherwise
02266     if (atom.m_str.isEmpty ())
02267       atom.m_str = "..";
02268 
02269     if (!atom.m_str.isEmpty ())
02270     {
02271       atom.m_long = 0;
02272       entry.append (atom);
02273 
02274       if (!item.noSelect ())
02275       {
02276         atom.m_uds = UDS_MIME_TYPE;
02277         if (!item.noInferiors ())
02278         {
02279           atom.m_str = "message/directory";
02280         } else {
02281           atom.m_str = "message/digest";
02282         }
02283         atom.m_long = 0;
02284         entry.append (atom);
02285         mailboxName += '/';
02286 
02287         // explicitly set this as a directory for KFileDialog
02288         atom.m_uds = UDS_FILE_TYPE;
02289         atom.m_str = QString::null;
02290         atom.m_long = S_IFDIR;
02291         entry.append (atom);
02292       }
02293       else if (!item.noInferiors ())
02294       {
02295         atom.m_uds = UDS_MIME_TYPE;
02296         atom.m_str = "inode/directory";
02297         atom.m_long = 0;
02298         entry.append (atom);
02299         mailboxName += '/';
02300 
02301         // explicitly set this as a directory for KFileDialog
02302         atom.m_uds = UDS_FILE_TYPE;
02303         atom.m_str = QString::null;
02304         atom.m_long = S_IFDIR;
02305         entry.append (atom);
02306       }
02307       else
02308       {
02309         atom.m_uds = UDS_MIME_TYPE;
02310         atom.m_str = "unknown/unknown";
02311         atom.m_long = 0;
02312         entry.append (atom);
02313       }
02314 
02315       atom.m_uds = UDS_URL;
02316       QString path = aURL.path();
02317       atom.m_str = aURL.url (0, 106); // utf-8
02318       if (appendPath)
02319       {
02320         if (path[path.length() - 1] == '/' && !path.isEmpty() && path != "/")
02321           path.truncate(path.length() - 1);
02322         if (!path.isEmpty() && path != "/"
02323             && path.right(hdLen) != item.hierarchyDelimiter()) {
02324           path += item.hierarchyDelimiter();
02325         }
02326         path += mailboxName;
02327         if (path.upper() == "/INBOX/") {
02328             // make sure the client can rely on INBOX
02329             path = path.upper();
02330         }
02331       }
02332       aURL.setPath(path);
02333       atom.m_str = aURL.url(0, 106); // utf-8
02334       atom.m_long = 0;
02335       entry.append (atom);
02336 
02337       atom.m_uds = UDS_USER;
02338       atom.m_str = myUser;
02339       entry.append (atom);
02340 
02341       atom.m_uds = UDS_ACCESS;
02342       atom.m_long = S_IRUSR | S_IXUSR | S_IWUSR;
02343       entry.append (atom);
02344 
02345       atom.m_uds = UDS_EXTRA;
02346       atom.m_str = item.attributesAsString();
02347       atom.m_long = 0;
02348       entry.append (atom);
02349 
02350       listEntry (entry, false);
02351     }
02352   }
02353 }
02354 
02355 enum IMAP_TYPE
02356 IMAP4Protocol::parseURL (const KURL & _url, QString & _box,
02357                          QString & _section, QString & _type, QString & _uid,
02358                          QString & _validity, QString & _hierarchyDelimiter,
02359                          QString & _info, bool cache)
02360 {
02361   enum IMAP_TYPE retVal;
02362   retVal = ITYPE_UNKNOWN;
02363 
02364   imapParser::parseURL (_url, _box, _section, _type, _uid, _validity, _info);
02365 //  kdDebug(7116) << "URL: query - '" << KURL::decode_string(_url.query()) << "'" << endl;
02366 
02367   // get the delimiter
02368   QString myNamespace = namespaceForBox( _box );
02369   kdDebug(7116) << "IMAP4::parseURL - namespace=" << myNamespace << endl;
02370   if ( namespaceToDelimiter.contains(myNamespace) )
02371   {
02372     _hierarchyDelimiter = namespaceToDelimiter[myNamespace];
02373     kdDebug(7116) << "IMAP4::parseURL - delimiter=" << _hierarchyDelimiter << endl;
02374   }
02375 
02376   if (!_box.isEmpty ())
02377   {
02378     kdDebug(7116) << "IMAP4::parseURL - box=" << _box << endl;
02379 
02380     if (makeLogin ())
02381     {
02382       if (getCurrentBox () != _box ||
02383           _type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK")
02384       {
02385         if ( cache )
02386         {
02387           // assume a normal box
02388           retVal = ITYPE_DIR_AND_BOX;
02389         } else
02390         {
02391           // start a listing for the box to get the type
02392           imapCommand *cmd;
02393 
02394           cmd = doCommand (imapCommand::clientList ("", _box));
02395           if (cmd->result () == "OK")
02396           {
02397             for (QValueListIterator < imapList > it = listResponses.begin ();
02398                 it != listResponses.end (); ++it)
02399             {
02400               //kdDebug(7116) << "IMAP4::parseURL - checking " << _box << " to " << (*it).name() << endl;
02401               if (_box == (*it).name ())
02402               {
02403                 if ( !(*it).hierarchyDelimiter().isEmpty() )
02404                   _hierarchyDelimiter = (*it).hierarchyDelimiter();
02405                 if ((*it).noSelect ())
02406                 {
02407                   retVal = ITYPE_DIR;
02408                 }
02409                 else if ((*it).noInferiors ())
02410                 {
02411                   retVal = ITYPE_BOX;
02412                 }
02413                 else
02414                 {
02415                   retVal = ITYPE_DIR_AND_BOX;
02416                 }
02417               }
02418             }
02419             // if we got no list response for the box see if it's a prefix
02420             if ( retVal == ITYPE_UNKNOWN &&
02421                  namespaceToDelimiter.contains(_box) ) {
02422               retVal = ITYPE_DIR;
02423             }
02424           } else {
02425             kdDebug(7116) << "IMAP4::parseURL - got error for " << _box << endl;
02426           }
02427           completeQueue.removeRef (cmd);
02428         } // cache
02429       }
02430       else // current == box
02431       {
02432         retVal = ITYPE_BOX;
02433       }
02434     }
02435     else
02436       kdDebug(7116) << "IMAP4::parseURL: no login!" << endl;
02437 
02438   }
02439   else // empty box
02440   {
02441     // the root is just a dir
02442     kdDebug(7116) << "IMAP4: parseURL: box [root]" << endl;
02443     retVal = ITYPE_DIR;
02444   }
02445 
02446   // see if it is a real sequence or a simple uid
02447   if (retVal == ITYPE_BOX || retVal == ITYPE_DIR_AND_BOX)
02448   {
02449     if (!_uid.isEmpty ())
02450     {
02451       if (_uid.find (':') == -1 && _uid.find (',') == -1
02452           && _uid.find ('*') == -1)
02453         retVal = ITYPE_MSG;
02454     }
02455   }
02456   if (retVal == ITYPE_MSG)
02457   {
02458     if ( (_section.find ("BODY.PEEK[", 0, false) != -1 ||
02459           _section.find ("BODY[", 0, false) != -1) &&
02460          _section.find(".MIME") == -1 &&
02461          _section.find(".HEADER") == -1 )
02462       retVal = ITYPE_ATTACH;
02463   }
02464   if ( _hierarchyDelimiter.isEmpty() &&
02465        (_type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK") )
02466   {
02467     // this shouldn't happen but when the delimiter is really empty
02468     // we try to reconstruct it from the URL
02469     if (!_box.isEmpty())
02470     {
02471       int start = _url.path().findRev(_box);
02472       if (start != -1)
02473         _hierarchyDelimiter = _url.path().mid(start-1, start);
02474       kdDebug(7116) << "IMAP4::parseURL - reconstructed delimiter:" << _hierarchyDelimiter
02475         << " from URL " << _url.path() << endl;
02476     }
02477     if (_hierarchyDelimiter.isEmpty())
02478       _hierarchyDelimiter = "/";
02479   }
02480   kdDebug(7116) << "IMAP4::parseURL - return " << retVal << endl;
02481 
02482   return retVal;
02483 }
02484 
02485 int
02486 IMAP4Protocol::outputLine (const QCString & _str, int len)
02487 {
02488   if (len == -1) {
02489     len = _str.length();
02490   }
02491 
02492   if (cacheOutput)
02493   {
02494     if ( !outputBuffer.isOpen() ) {
02495       outputBuffer.open(IO_WriteOnly);
02496     }
02497     outputBuffer.at(outputBufferIndex);
02498     outputBuffer.writeBlock(_str.data(), len);
02499     outputBufferIndex += len;
02500     return 0;
02501   }
02502 
02503   QByteArray temp;
02504   bool relay = relayEnabled;
02505 
02506   relayEnabled = true;
02507   temp.setRawData (_str.data (), len);
02508   parseRelay (temp);
02509   temp.resetRawData (_str.data (), len);
02510 
02511   relayEnabled = relay;
02512   return 0;
02513 }
02514 
02515 void IMAP4Protocol::flushOutput(QString contentEncoding)
02516 {
02517   // send out cached data to the application
02518   if (outputBufferIndex == 0)
02519     return;
02520   outputBuffer.close();
02521   outputCache.resize(outputBufferIndex);
02522   if (decodeContent)
02523   {
02524     // get the coding from the MIME header
02525     QByteArray decoded;
02526     if (contentEncoding.find("quoted-printable", 0, false) == 0)
02527       decoded = KCodecs::quotedPrintableDecode(outputCache);
02528     else if (contentEncoding.find("base64", 0, false) == 0)
02529       KCodecs::base64Decode(outputCache, decoded);
02530     else
02531       decoded = outputCache;
02532 
02533     QString mimetype = KMimeType::findByContent( decoded )->name();
02534     kdDebug(7116) << "IMAP4::flushOutput - mimeType " << mimetype << endl;
02535     mimeType(mimetype);
02536     decodeContent = false;
02537     data( decoded );
02538   } else {
02539     data( outputCache );
02540   }
02541   mProcessedSize += outputBufferIndex;
02542   processedSize( mProcessedSize );
02543   outputBufferIndex = 0;
02544   outputCache[0] = '\0';
02545   outputBuffer.setBuffer(outputCache);
02546 }
02547 
02548 ssize_t IMAP4Protocol::myRead(void *data, ssize_t len)
02549 {
02550   if (readBufferLen)
02551   {
02552     ssize_t copyLen = (len < readBufferLen) ? len : readBufferLen;
02553     memcpy(data, readBuffer, copyLen);
02554     readBufferLen -= copyLen;
02555     if (readBufferLen) memcpy(readBuffer, &readBuffer[copyLen], readBufferLen);
02556     return copyLen;
02557   }
02558   if (!isConnectionValid()) return 0;
02559   waitForResponse( responseTimeout() );
02560   return read(data, len);
02561 }
02562 
02563 bool
02564 IMAP4Protocol::assureBox (const QString & aBox, bool readonly)
02565 {
02566   if (aBox.isEmpty()) return false;
02567 
02568   imapCommand *cmd = 0;
02569 
02570   if (aBox != getCurrentBox () || (!getSelected().readWrite() && !readonly))
02571   {
02572     // open the box with the appropriate mode
02573     kdDebug(7116) << "IMAP4Protocol::assureBox - opening box" << endl;
02574     selectInfo = imapInfo();
02575     cmd = doCommand (imapCommand::clientSelect (aBox, readonly));
02576     bool ok = cmd->result() == "OK";
02577     QString cmdInfo = cmd->resultInfo();
02578     completeQueue.removeRef (cmd);
02579 
02580     if (!ok)
02581     {
02582       bool found = false;
02583       cmd = doCommand (imapCommand::clientList ("", aBox));
02584       if (cmd->result () == "OK")
02585       {
02586         for (QValueListIterator < imapList > it = listResponses.begin ();
02587              it != listResponses.end (); ++it)
02588         {
02589           if (aBox == (*it).name ()) found = true;
02590         }
02591       }
02592       completeQueue.removeRef (cmd);
02593       if (found) {
02594         if (cmdInfo.find("permission", 0, false) != -1) {
02595           // not allowed to enter this folder
02596           error(ERR_ACCESS_DENIED, cmdInfo);
02597         } else {
02598           error(ERR_SLAVE_DEFINED, i18n("Unable to open folder %1. The server replied: %2").arg(aBox).arg(cmdInfo));
02599         }
02600       } else {
02601         error(KIO::ERR_DOES_NOT_EXIST, aBox);
02602       }
02603       return false;
02604     }
02605   }
02606   else
02607   {
02608     // Give the server a chance to deliver updates every ten seconds.
02609     // Doing this means a server roundtrip and since assureBox is called
02610     // after every mail, we do it with a timeout.
02611     kdDebug(7116) << "IMAP4Protocol::assureBox - reusing box" << endl;
02612     if ( mTimeOfLastNoop.secsTo( QDateTime::currentDateTime() ) > 10 ) {
02613       cmd = doCommand (imapCommand::clientNoop ());
02614       completeQueue.removeRef (cmd);
02615       mTimeOfLastNoop = QDateTime::currentDateTime();
02616       kdDebug(7116) << "IMAP4Protocol::assureBox - noop timer fired" << endl;
02617     }
02618   }
02619 
02620   // if it is the mode we want
02621   if (!getSelected().readWrite() && !readonly)
02622   {
02623     error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox);
02624     return false;
02625   }
02626 
02627   return true;
02628 }
KDE Home | KDE Accessibility Home | Description of Access Keys