• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • Contact Us
 

kioslave/imap4

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

kioslave/imap4

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

KDE-PIM Libraries

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