kioslaves/imap4

imap4.cc

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