kioslaves/imap4

imap4.cc

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