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