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

kioslave/imap4

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

KDE-PIM Libraries

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