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

kioslave/imap4

imap4.cpp

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

kioslave/imap4

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.9
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