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  * Quota commands: data = 'Q' + 'R'oot/'G'et/'S'et + URL + entry + command-dependent args
01223  */
01224 void
01225 IMAP4Protocol::special (const QByteArray & aData)
01226 {
01227   kdDebug(7116) << "IMAP4Protocol::special" << endl;
01228   if (!makeLogin()) return;
01229 
01230   QDataStream stream(aData, IO_ReadOnly);
01231 
01232   int tmp;
01233   stream >> tmp;
01234 
01235   switch (tmp) {
01236   case 'C':
01237   {
01238     // copy
01239     KURL src;
01240     KURL dest;
01241     stream >> src >> dest;
01242     copy(src, dest, 0, FALSE);
01243     break;
01244   }
01245   case 'c':
01246   {
01247     // capabilities
01248     infoMessage(imapCapabilities.join(" "));
01249     finished();
01250     break;
01251   }
01252   case 'N':
01253   {
01254     // NOOP
01255     imapCommand *cmd = doCommand(imapCommand::clientNoop());
01256     if (cmd->result () != "OK")
01257     {
01258       kdDebug(7116) << "NOOP did not succeed - connection broken" << endl;
01259       completeQueue.removeRef (cmd);
01260       error (ERR_CONNECTION_BROKEN, myHost);
01261       return;
01262     }
01263     completeQueue.removeRef (cmd);
01264     finished();
01265     break;
01266   }
01267   case 'n':
01268   {
01269     // namespace in the form "section=namespace=delimiter"
01270     // entries are separated by ,
01271     infoMessage( imapNamespaces.join(",") );
01272     finished();
01273     break;
01274   }
01275   case 'U':
01276   {
01277     // unsubscribe
01278     KURL _url;
01279     stream >> _url;
01280     QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01281     parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01282     imapCommand *cmd = doCommand(imapCommand::clientUnsubscribe(aBox));
01283     if (cmd->result () != "OK")
01284     {
01285       completeQueue.removeRef (cmd);
01286       error(ERR_SLAVE_DEFINED, i18n("Unsubscribe of folder %1 "
01287                                     "failed. The server returned: %2")
01288             .arg(_url.prettyURL())
01289             .arg(cmd->resultInfo()));
01290       return;
01291     }
01292     completeQueue.removeRef (cmd);
01293     finished();
01294     break;
01295   }
01296   case 'u':
01297   {
01298     // subscribe
01299     KURL _url;
01300     stream >> _url;
01301     QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01302     parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01303     imapCommand *cmd = doCommand(imapCommand::clientSubscribe(aBox));
01304     if (cmd->result () != "OK")
01305     {
01306       completeQueue.removeRef (cmd);
01307       error(ERR_SLAVE_DEFINED, i18n("Subscribe of folder %1 "
01308                                     "failed. The server returned: %2")
01309             .arg(_url.prettyURL())
01310             .arg(cmd->resultInfo()));
01311       return;
01312     }
01313     completeQueue.removeRef (cmd);
01314     finished();
01315     break;
01316   }
01317   case 'A':
01318   {
01319     // acl
01320     int cmd;
01321     stream >> cmd;
01322     if ( hasCapability( "ACL" ) ) {
01323       specialACLCommand( cmd, stream );
01324     } else {
01325       error( ERR_UNSUPPORTED_ACTION, "ACL" );
01326     }
01327     break;
01328   }
01329   case 'M':
01330   {
01331     // annotatemore
01332     int cmd;
01333     stream >> cmd;
01334     if ( hasCapability( "ANNOTATEMORE" ) ) {
01335       specialAnnotateMoreCommand( cmd, stream );
01336     } else {
01337       error( ERR_UNSUPPORTED_ACTION, "ANNOTATEMORE" );
01338     }
01339     break;
01340   }
01341   case 'Q':
01342   {
01343     // quota
01344     int cmd;
01345     stream >> cmd;
01346     if ( hasCapability( "QUOTA" ) ) {
01347       specialQuotaCommand( cmd, stream );
01348     } else {
01349       error( ERR_UNSUPPORTED_ACTION, "QUOTA" );
01350     }
01351     break;
01352   }
01353   case 'S':
01354   {
01355     // status
01356     KURL _url;
01357     QCString newFlags;
01358     stream >> _url >> newFlags;
01359 
01360     QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01361     parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01362     if (!assureBox(aBox, false)) return;
01363     imapCommand *cmd = doCommand (imapCommand::
01364                                   clientStore (aSequence, "-FLAGS.SILENT",
01365                                                "\\SEEN \\ANSWERED \\FLAGGED \\DRAFT"));
01366     if (cmd->result () != "OK")
01367     {
01368       completeQueue.removeRef (cmd);
01369       error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
01370                                       "failed.").arg(_url.prettyURL()));
01371       return;
01372     }
01373     completeQueue.removeRef (cmd);
01374     if (!newFlags.isEmpty())
01375     {
01376       cmd = doCommand (imapCommand::
01377                        clientStore (aSequence, "+FLAGS.SILENT", newFlags));
01378       if (cmd->result () != "OK")
01379       {
01380         completeQueue.removeRef (cmd);
01381         error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
01382                                         "failed.").arg(_url.prettyURL()));
01383         return;
01384       }
01385       completeQueue.removeRef (cmd);
01386     }
01387     finished();
01388     break;
01389   }
01390   case 'E':
01391   {
01392     // search
01393     specialSearchCommand( stream );
01394     break;
01395   }
01396   default:
01397     kdWarning(7116) << "Unknown command in special(): " << tmp << endl;
01398     error( ERR_UNSUPPORTED_ACTION, QString(QChar(tmp)) );
01399     break;
01400   }
01401 }
01402 
01403 void
01404 IMAP4Protocol::specialACLCommand( int command, QDataStream& stream )
01405 {
01406   // All commands start with the URL to the box
01407   KURL _url;
01408   stream >> _url;
01409   QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01410   parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01411 
01412   switch( command ) {
01413   case 'S': // SETACL
01414   {
01415     QString user, acl;
01416     stream >> user >> acl;
01417     kdDebug(7116) << "SETACL " << aBox << " " << user << " " << acl << endl;
01418     imapCommand *cmd = doCommand(imapCommand::clientSetACL(aBox, user, acl));
01419     if (cmd->result () != "OK")
01420     {
01421       error(ERR_SLAVE_DEFINED, i18n("Setting the Access Control List on folder %1 "
01422                                       "for user %2 failed. The server returned: %3")
01423             .arg(_url.prettyURL())
01424             .arg(user)
01425             .arg(cmd->resultInfo()));
01426       return;
01427     }
01428     completeQueue.removeRef (cmd);
01429     finished();
01430     break;
01431   }
01432   case 'D': // DELETEACL
01433   {
01434     QString user;
01435     stream >> user;
01436     kdDebug(7116) << "DELETEACL " << aBox << " " << user << endl;
01437     imapCommand *cmd = doCommand(imapCommand::clientDeleteACL(aBox, user));
01438     if (cmd->result () != "OK")
01439     {
01440       error(ERR_SLAVE_DEFINED, i18n("Deleting the Access Control List on folder %1 "
01441                                     "for user %2 failed. The server returned: %3")
01442             .arg(_url.prettyURL())
01443             .arg(user)
01444             .arg(cmd->resultInfo()));
01445       return;
01446     }
01447     completeQueue.removeRef (cmd);
01448     finished();
01449     break;
01450   }
01451   case 'G': // GETACL
01452   {
01453     kdDebug(7116) << "GETACL " << aBox << endl;
01454     imapCommand *cmd = doCommand(imapCommand::clientGetACL(aBox));
01455     if (cmd->result () != "OK")
01456     {
01457       error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
01458                                      "failed. The server returned: %2")
01459             .arg(_url.prettyURL())
01460             .arg(cmd->resultInfo()));
01461       return;
01462     }
01463     // Returning information to the application from a special() command isn't easy.
01464     // I'm reusing the infoMessage trick seen above (for capabilities), but this
01465     // limits me to a string instead of a stringlist. I'm using space as separator,
01466     // since I don't think it can be used in login names.
01467     kdDebug(7116) << getResults() << endl;
01468     infoMessage(getResults().join( " " ));
01469     finished();
01470     break;
01471   }
01472   case 'L': // LISTRIGHTS
01473   {
01474     // Do we need this one? It basically shows which rights are tied together, but that's all?
01475     error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01476     break;
01477   }
01478   case 'M': // MYRIGHTS
01479   {
01480     kdDebug(7116) << "MYRIGHTS " << aBox << endl;
01481     imapCommand *cmd = doCommand(imapCommand::clientMyRights(aBox));
01482     if (cmd->result () != "OK")
01483     {
01484       error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
01485                                     "failed. The server returned: %2")
01486             .arg(_url.prettyURL())
01487             .arg(cmd->resultInfo()));
01488       return;
01489     }
01490     QStringList lst = getResults();
01491     kdDebug(7116) << "myrights results: " << lst << endl;
01492     if ( !lst.isEmpty() ) {
01493       Q_ASSERT( lst.count() == 1 );
01494       infoMessage( lst.first() );
01495     }
01496     finished();
01497     break;
01498   }
01499   default:
01500     kdWarning(7116) << "Unknown special ACL command:" << command << endl;
01501     error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01502   }
01503 }
01504 
01505 void
01506 IMAP4Protocol::specialSearchCommand( QDataStream& stream )
01507 {
01508   kdDebug(7116) << "IMAP4Protocol::specialSearchCommand" << endl;
01509   KURL _url;
01510   stream >> _url;
01511   QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01512   parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01513   if (!assureBox(aBox, false)) return;
01514 
01515   imapCommand *cmd = doCommand (imapCommand::clientSearch( aSection ));
01516   if (cmd->result () != "OK")
01517   {
01518     error(ERR_SLAVE_DEFINED, i18n("Searching of folder %1 "
01519           "failed. The server returned: %2")
01520         .arg(aBox)
01521         .arg(cmd->resultInfo()));
01522     return;
01523   }
01524   completeQueue.removeRef(cmd);
01525   QStringList lst = getResults();
01526   kdDebug(7116) << "IMAP4Protocol::specialSearchCommand '" << aSection <<
01527     "' returns " << lst << endl;
01528   infoMessage( lst.join( " " ) );
01529 
01530   finished();
01531 }
01532 
01533 void
01534 IMAP4Protocol::specialAnnotateMoreCommand( int command, QDataStream& stream )
01535 {
01536   // All commands start with the URL to the box
01537   KURL _url;
01538   stream >> _url;
01539   QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01540   parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01541 
01542   switch( command ) {
01543   case 'S': // SETANNOTATION
01544   {
01545     // Params:
01546     //  KURL URL of the mailbox
01547     //  QString entry (should be an actual entry name, no % or *; empty for server entries)
01548     //  QMap<QString,QString> attributes (name and value)
01549     QString entry;
01550     QMap<QString, QString> attributes;
01551     stream >> entry >> attributes;
01552     kdDebug(7116) << "SETANNOTATION " << aBox << " " << entry << " " << attributes.count() << " attributes" << endl;
01553     imapCommand *cmd = doCommand(imapCommand::clientSetAnnotation(aBox, entry, attributes));
01554     if (cmd->result () != "OK")
01555     {
01556       error(ERR_SLAVE_DEFINED, i18n("Setting the annotation %1 on folder %2 "
01557                                     " failed. The server returned: %3")
01558             .arg(entry)
01559             .arg(_url.prettyURL())
01560             .arg(cmd->resultInfo()));
01561       return;
01562     }
01563     completeQueue.removeRef (cmd);
01564     finished();
01565     break;
01566   }
01567   case 'G': // GETANNOTATION.
01568   {
01569     // Params:
01570     //  KURL URL of the mailbox
01571     //  QString entry (should be an actual entry name, no % or *; empty for server entries)
01572     //  QStringList attributes (list of attributes to be retrieved, possibly with % or *)
01573     QString entry;
01574     QStringList attributeNames;
01575     stream >> entry >> attributeNames;
01576     kdDebug(7116) << "GETANNOTATION " << aBox << " " << entry << " " << attributeNames << endl;
01577     imapCommand *cmd = doCommand(imapCommand::clientGetAnnotation(aBox, entry, attributeNames));
01578     if (cmd->result () != "OK")
01579     {
01580       error(ERR_SLAVE_DEFINED, i18n("Retrieving the annotation %1 on folder %2 "
01581                                      "failed. The server returned: %3")
01582             .arg(entry)
01583             .arg(_url.prettyURL())
01584             .arg(cmd->resultInfo()));
01585       return;
01586     }
01587     // Returning information to the application from a special() command isn't easy.
01588     // I'm reusing the infoMessage trick seen above (for capabilities and acls), but this
01589     // limits me to a string instead of a stringlist. Let's use \r as separator.
01590     kdDebug(7116) << getResults() << endl;
01591     infoMessage(getResults().join( "\r" ));
01592     finished();
01593     break;
01594   }
01595   default:
01596     kdWarning(7116) << "Unknown special annotate command:" << command << endl;
01597     error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01598   }
01599 }
01600 
01601 void
01602 IMAP4Protocol::specialQuotaCommand( int command, QDataStream& stream )
01603 {
01604   // All commands start with the URL to the box
01605   KURL _url;
01606   stream >> _url;
01607   QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01608   parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
01609 
01610   switch( command ) {
01611     case 'R': // GETQUOTAROOT
01612       {
01613         kdDebug(7116) << "QUOTAROOT " << aBox << endl;
01614         imapCommand *cmd = doCommand(imapCommand::clientGetQuotaroot( aBox ) );
01615         if (cmd->result () != "OK")
01616         {
01617           error(ERR_SLAVE_DEFINED, i18n("Retrieving the quota root inormation on folder %1 "
01618                 "failed. The server returned: %2")
01619               .arg(_url.prettyURL())
01620               .arg(cmd->resultInfo()));
01621           return;
01622         }
01623         infoMessage(getResults().join( "\r" ));
01624         finished();
01625         break;
01626       }
01627     case 'G': // GETQUOTA
01628       {
01629         kdDebug(7116) << "GETQUOTA command" << endl;
01630         kdWarning(7116) << "UNIMPLEMENTED" << endl;
01631         break;
01632       }
01633     case 'S': // SETQUOTA
01634       {
01635         kdDebug(7116) << "SETQUOTA command" << endl;
01636         kdWarning(7116) << "UNIMPLEMENTED" << endl;
01637         break;
01638       }
01639     default:
01640       kdWarning(7116) << "Unknown special quota command:" << command << endl;
01641       error( ERR_UNSUPPORTED_ACTION, QString(QChar(command)) );
01642   }
01643 }
01644 
01645 void
01646 IMAP4Protocol::rename (const KURL & src, const KURL & dest, bool overwrite)
01647 {
01648   kdDebug(7116) << "IMAP4::rename - [" << (overwrite ? "Overwrite" : "NoOverwrite") << "] " << src.prettyURL() << " -> " << dest.prettyURL() << endl;
01649   QString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
01650   QString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo;
01651   enum IMAP_TYPE sType =
01652     parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo, false);
01653   enum IMAP_TYPE dType =
01654     parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo, false);
01655 
01656   if (dType == ITYPE_UNKNOWN)
01657   {
01658     switch (sType)
01659     {
01660     case ITYPE_BOX:
01661     case ITYPE_DIR:
01662     case ITYPE_DIR_AND_BOX:
01663       {
01664         if (getState() == ISTATE_SELECT && sBox == getCurrentBox())
01665         {
01666           kdDebug(7116) << "IMAP4::rename - close " << getCurrentBox() << endl;
01667           // mailbox can only be renamed if it is closed
01668           imapCommand *cmd = doCommand (imapCommand::clientClose());
01669           bool ok = cmd->result() == "OK";
01670           completeQueue.removeRef(cmd);
01671           if (!ok)
01672           {
01673             error(ERR_CANNOT_RENAME, i18n("Unable to close mailbox."));
01674             return;
01675           }
01676           setState(ISTATE_LOGIN);
01677         }
01678         imapCommand *cmd = doCommand (imapCommand::clientRename (sBox, dBox));
01679         if (cmd->result () != "OK") {
01680           error (ERR_CANNOT_RENAME, cmd->result ());
01681           completeQueue.removeRef (cmd);
01682           return;
01683         }
01684         completeQueue.removeRef (cmd);
01685       }
01686       break;
01687 
01688     case ITYPE_MSG:
01689     case ITYPE_ATTACH:
01690     case ITYPE_UNKNOWN:
01691       error (ERR_CANNOT_RENAME, src.prettyURL());
01692       break;
01693     }
01694   }
01695   else
01696   {
01697     error (ERR_CANNOT_RENAME, src.prettyURL());
01698     return;
01699   }
01700   finished ();
01701 }
01702 
01703 void
01704 IMAP4Protocol::slave_status ()
01705 {
01706   bool connected = (getState() != ISTATE_NO) && isConnectionValid();
01707   kdDebug(7116) << "IMAP4::slave_status " << connected << endl;
01708   slaveStatus ( connected ? myHost : QString::null, connected );
01709 }
01710 
01711 void
01712 IMAP4Protocol::dispatch (int command, const QByteArray & data)
01713 {
01714   kdDebug(7116) << "IMAP4::dispatch - command=" << command << endl;
01715   KIO::TCPSlaveBase::dispatch (command, data);
01716 }
01717 
01718 void
01719 IMAP4Protocol::stat (const KURL & _url)
01720 {
01721   kdDebug(7116) << "IMAP4::stat - " << _url.prettyURL() << endl;
01722   QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
01723   // parseURL with caching
01724   enum IMAP_TYPE aType =
01725     parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter,
01726         aInfo, true);
01727 
01728   UDSEntry entry;
01729   UDSAtom atom;
01730 
01731   atom.m_uds = UDS_NAME;
01732   atom.m_str = aBox;
01733   entry.append (atom);
01734 
01735   if (!aSection.isEmpty())
01736   {
01737     if (getState() == ISTATE_SELECT && aBox == getCurrentBox())
01738     {
01739       imapCommand *cmd = doCommand (imapCommand::clientClose());
01740       bool ok = cmd->result() == "OK";
01741       completeQueue.removeRef(cmd);
01742       if (!ok)
01743       {
01744         error(ERR_COULD_NOT_STAT, aBox);
01745         return;
01746       }
01747       setState(ISTATE_LOGIN);
01748     }
01749     bool ok = false;
01750     QString cmdInfo;
01751     if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
01752       ok = true;
01753     else
01754     {
01755       imapCommand *cmd = doCommand(imapCommand::clientStatus(aBox, aSection));
01756       ok = cmd->result() == "OK";
01757       cmdInfo = cmd->resultInfo();
01758       completeQueue.removeRef(cmd);
01759     }
01760     if (!ok)
01761     {
01762       bool found = false;
01763       imapCommand *cmd = doCommand (imapCommand::clientList ("", aBox));
01764       if (cmd->result () == "OK")
01765       {
01766         for (QValueListIterator < imapList > it = listResponses.begin ();
01767              it != listResponses.end (); ++it)
01768         {
01769           if (aBox == (*it).name ()) found = true;
01770         }
01771       }
01772       completeQueue.removeRef (cmd);
01773       if (found)
01774         error(ERR_COULD_NOT_STAT, aBox);
01775       else
01776         error(KIO::ERR_DOES_NOT_EXIST, aBox);
01777       return;
01778     }
01779     if ((aSection == "UIDNEXT" && getStatus().uidNextAvailable())
01780       || (aSection == "UNSEEN" && getStatus().unseenAvailable()))
01781     {
01782       atom.m_uds = UDS_SIZE;
01783       atom.m_str = QString::null;
01784       atom.m_long = (aSection == "UIDNEXT") ? getStatus().uidNext()
01785         : getStatus().unseen();
01786       entry.append(atom);
01787     }
01788   } else
01789   if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX || aType == ITYPE_MSG ||
01790       aType == ITYPE_ATTACH)
01791   {
01792     ulong validity = 0;
01793     // see if the box is already in select/examine state
01794     if (aBox == getCurrentBox ())
01795       validity = selectInfo.uidValidity ();
01796     else
01797     {
01798       // do a status lookup on the box
01799       // only do this if the box is not selected
01800       // the server might change the validity for new select/examine
01801       imapCommand *cmd =
01802         doCommand (imapCommand::clientStatus (aBox, "UIDVALIDITY"));
01803       completeQueue.removeRef (cmd);
01804       validity = getStatus ().uidValidity ();
01805     }
01806     validity = 0;               // temporary
01807 
01808     if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX)
01809     {
01810       // has no or an invalid uidvalidity
01811       if (validity > 0 && validity != aValidity.toULong ())
01812       {
01813         //redirect
01814         KURL newUrl = _url;
01815 
01816         newUrl.setPath ("/" + aBox + ";UIDVALIDITY=" +
01817                         QString::number(validity));
01818         kdDebug(7116) << "IMAP4::stat - redirecting to " << newUrl.prettyURL() << endl;
01819         redirection (newUrl);
01820       }
01821     }
01822     else if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
01823     {
01824       //must determine if this message exists
01825       //cause konqueror will check this on paste operations
01826 
01827       // has an invalid uidvalidity
01828       // or no messages in box
01829       if (validity > 0 && validity != aValidity.toULong ())
01830       {
01831         aType = ITYPE_UNKNOWN;
01832         kdDebug(7116) << "IMAP4::stat - url has invalid validity [" << validity << "d] " << _url.prettyURL() << endl;
01833       }
01834     }
01835   }
01836 
01837   atom.m_uds = UDS_MIME_TYPE;
01838   atom.m_str = getMimeType (aType);
01839   entry.append (atom);
01840 
01841   kdDebug(7116) << "IMAP4: stat: " << atom.m_str << endl;
01842   switch (aType)
01843   {
01844   case ITYPE_DIR:
01845     atom.m_uds = UDS_FILE_TYPE;
01846     atom.m_str = QString::null;
01847     atom.m_long = S_IFDIR;
01848     entry.append (atom);
01849     break;
01850 
01851   case ITYPE_BOX:
01852   case ITYPE_DIR_AND_BOX:
01853     atom.m_uds = UDS_FILE_TYPE;
01854     atom.m_str = QString::null;
01855     atom.m_long = S_IFDIR;
01856     entry.append (atom);
01857     break;
01858 
01859   case ITYPE_MSG:
01860   case ITYPE_ATTACH:
01861     atom.m_uds = UDS_FILE_TYPE;
01862     atom.m_str = QString::null;
01863     atom.m_long = S_IFREG;
01864     entry.append (atom);
01865     break;
01866 
01867   case ITYPE_UNKNOWN:
01868     error (ERR_DOES_NOT_EXIST, _url.prettyURL());
01869     break;
01870   }
01871 
01872   statEntry (entry);
01873   kdDebug(7116) << "IMAP4::stat - Finishing stat" << endl;
01874   finished ();
01875 }
01876 
01877 void IMAP4Protocol::openConnection()
01878 {
01879   if (makeLogin()) connected();
01880 }
01881 
01882 void IMAP4Protocol::closeConnection()
01883 {
01884   if (getState() == ISTATE_NO) return;
01885   if (getState() == ISTATE_SELECT && metaData("expunge") == "auto")
01886   {
01887     imapCommand *cmd = doCommand (imapCommand::clientExpunge());
01888     completeQueue.removeRef (cmd);
01889   }
01890   if (getState() != ISTATE_CONNECT)
01891   {
01892     imapCommand *cmd = doCommand (imapCommand::clientLogout());
01893     completeQueue.removeRef (cmd);
01894   }
01895   closeDescriptor();
01896   setState(ISTATE_NO);
01897   completeQueue.clear();
01898   sentQueue.clear();
01899   lastHandled = 0;
01900   currentBox = QString::null;
01901   readBufferLen = 0;
01902 }
01903 
01904 bool IMAP4Protocol::makeLogin ()
01905 {
01906   if (getState () == ISTATE_LOGIN || getState () == ISTATE_SELECT)
01907     return true;
01908 
01909   kdDebug(7116) << "IMAP4::makeLogin - checking login" << endl;
01910   bool alreadyConnected = getState() == ISTATE_CONNECT;
01911   kdDebug(7116) << "IMAP4::makeLogin - alreadyConnected " << alreadyConnected << endl;
01912   if (alreadyConnected || connectToHost (myHost.latin1(), myPort))
01913   {
01914 //      fcntl (m_iSock, F_SETFL, (fcntl (m_iSock, F_GETFL) | O_NDELAY));
01915 
01916     setState(ISTATE_CONNECT);
01917 
01918     myAuth = metaData("auth");
01919     myTLS  = metaData("tls");
01920     kdDebug(7116) << "myAuth: " << myAuth << endl;
01921 
01922     imapCommand *cmd;
01923 
01924     unhandled.clear ();
01925     if (!alreadyConnected) while (!parseLoop ());    //get greeting
01926     QString greeting;
01927     if (!unhandled.isEmpty()) greeting = unhandled.first().stripWhiteSpace();
01928     unhandled.clear ();       //get rid of it
01929     cmd = doCommand (new imapCommand ("CAPABILITY", ""));
01930 
01931     kdDebug(7116) << "IMAP4: setHost: capability" << endl;
01932     for (QStringList::Iterator it = imapCapabilities.begin ();
01933          it != imapCapabilities.end (); ++it)
01934     {
01935       kdDebug(7116) << "'" << (*it) << "'" << endl;
01936     }
01937     completeQueue.removeRef (cmd);
01938 
01939     if (!hasCapability("IMAP4") && !hasCapability("IMAP4rev1"))
01940     {
01941       error(ERR_COULD_NOT_LOGIN, i18n("The server %1 supports neither "
01942         "IMAP4 nor IMAP4rev1.\nIt identified itself with: %2")
01943         .arg(myHost).arg(greeting));
01944       closeConnection();
01945       return false;
01946     }
01947 
01948     if (metaData("nologin") == "on") return TRUE;
01949 
01950     if (myTLS == "on" && !hasCapability(QString("STARTTLS")))
01951     {
01952       error(ERR_COULD_NOT_LOGIN, i18n("The server does not support TLS.\n"
01953         "Disable this security feature to connect unencrypted."));
01954       closeConnection();
01955       return false;
01956     }
01957     if ((myTLS == "on" || (canUseTLS() && myTLS != "off")) &&
01958       hasCapability(QString("STARTTLS")))
01959     {
01960       imapCommand *cmd = doCommand (imapCommand::clientStartTLS());
01961       if (cmd->result () == "OK")
01962       {
01963         completeQueue.removeRef(cmd);
01964         int tlsrc = startTLS();
01965         if (tlsrc == 1)
01966         {
01967           kdDebug(7116) << "TLS mode has been enabled." << endl;
01968           imapCommand *cmd2 = doCommand (new imapCommand ("CAPABILITY", ""));
01969           for (QStringList::Iterator it = imapCapabilities.begin ();
01970                                      it != imapCapabilities.end (); ++it)
01971           {
01972             kdDebug(7116) << "'" << (*it) << "'" << endl;
01973           }
01974           completeQueue.removeRef (cmd2);
01975         } else {
01976           kdWarning(7116) << "TLS mode setup has failed.  Aborting." << endl;
01977           error (ERR_COULD_NOT_LOGIN, i18n("Starting TLS failed."));
01978           closeConnection();
01979           return false;
01980         }
01981       } else completeQueue.removeRef(cmd);
01982     }
01983 
01984     if (myAuth.isEmpty () || myAuth == "*") {
01985       if (hasCapability (QString ("LOGINDISABLED"))) {
01986         error (ERR_COULD_NOT_LOGIN, i18n("LOGIN is disabled by the server."));
01987         closeConnection();
01988         return false;
01989       }
01990     }
01991     else {
01992       if (!hasCapability (QString ("AUTH=") + myAuth)) {
01993         error (ERR_COULD_NOT_LOGIN, i18n("The authentication method %1 is not "
01994           "supported by the server.").arg(myAuth));
01995         closeConnection();
01996         return false;
01997       }
01998     }
01999 
02000     if ( greeting.contains(  QRegExp(  "Cyrus IMAP4 v2.1" ) ) ) {
02001       removeCapability( "ANNOTATEMORE" );
02002     }
02003 
02004     kdDebug(7116) << "IMAP4::makeLogin - attempting login" << endl;
02005 
02006     KIO::AuthInfo authInfo;
02007     authInfo.username = myUser;
02008     authInfo.password = myPass;
02009     authInfo.prompt = i18n ("Username and password for your IMAP account:");
02010 
02011     kdDebug(7116) << "IMAP4::makeLogin - open_PassDlg said user=" << myUser << " pass=xx" << endl;
02012 
02013     QString resultInfo;
02014     if (myAuth.isEmpty () || myAuth == "*")
02015     {
02016       if (myUser.isEmpty () || myPass.isEmpty ()) {
02017         if(openPassDlg (authInfo)) {
02018           myUser = authInfo.username;
02019           myPass = authInfo.password;
02020         }
02021       }
02022       if (!clientLogin (myUser, myPass, resultInfo))
02023         error(KIO::ERR_COULD_NOT_LOGIN, i18n("Unable to login. Probably the "
02024         "password is wrong.\nThe server %1 replied:\n%2").arg(myHost).arg(resultInfo));
02025     }
02026     else
02027     {
02028 #ifdef HAVE_LIBSASL2
02029       if (!clientAuthenticate (this, authInfo, myHost, myAuth, mySSL, resultInfo))
02030         error(KIO::ERR_COULD_NOT_LOGIN, i18n("Unable to authenticate via %1.\n"
02031     "The server %2 replied:\n%3").arg(myAuth).arg(myHost).arg(resultInfo));
02032       else {
02033         myUser = authInfo.username;
02034         myPass = authInfo.password;
02035       }
02036 #else
02037       error(KIO::ERR_COULD_NOT_LOGIN, i18n("SASL authentication is not compiled into kio_imap4."));
02038 #endif
02039     }
02040     if ( hasCapability("NAMESPACE") )
02041     {
02042       // get all namespaces and save the namespace - delimiter association
02043       cmd = doCommand( imapCommand::clientNamespace() );
02044       if (cmd->result () == "OK")
02045       {
02046         kdDebug(7116) << "makeLogin - registered namespaces" << endl;
02047       }
02048       completeQueue.removeRef (cmd);
02049     }
02050     // get the default delimiter (empty listing)
02051     cmd = doCommand( imapCommand::clientList("", "") );
02052     if (cmd->result () == "OK")
02053     {
02054       QValueListIterator < imapList > it = listResponses.begin();
02055       if ( it == listResponses.end() )
02056       {
02057           // empty answer - this is a buggy imap server
02058           // as a fallback we fire a normal listing and take the first answer
02059           completeQueue.removeRef (cmd);
02060           cmd = doCommand( imapCommand::clientList("", "%") );
02061           if (cmd->result () == "OK")
02062           {
02063               it = listResponses.begin();
02064           }
02065       }
02066       if ( it != listResponses.end() )
02067       {
02068         namespaceToDelimiter[QString::null] = (*it).hierarchyDelimiter();
02069         kdDebug(7116) << "makeLogin - delimiter for empty ns='" <<
02070           (*it).hierarchyDelimiter() << "'" << endl;
02071         if ( !hasCapability("NAMESPACE") )
02072         {
02073           // server does not support namespaces
02074           QString nsentry = QString::number( 0 ) + "=="
02075             + (*it).hierarchyDelimiter();
02076           imapNamespaces.append( nsentry );
02077         }
02078       }
02079     }
02080     completeQueue.removeRef (cmd);
02081   } else {
02082     kdDebug(7116) << "makeLogin - NO login" << endl;
02083   }
02084 
02085   return getState() == ISTATE_LOGIN;
02086 }
02087 
02088 void
02089 IMAP4Protocol::parseWriteLine (const QString & aStr)
02090 {
02091   //kdDebug(7116) << "Writing: " << aStr << endl;
02092   QCString writer = aStr.utf8();
02093   int len = writer.length();
02094 
02095   // append CRLF if necessary
02096   if (len == 0 || (writer[len - 1] != '\n')) {
02097     len += 2;
02098     writer += "\r\n";
02099   }
02100 
02101   // write it
02102   write(writer.data(), len);
02103 }
02104 
02105 QString
02106 IMAP4Protocol::getMimeType (enum IMAP_TYPE aType)
02107 {
02108   switch (aType)
02109   {
02110   case ITYPE_DIR:
02111     return "inode/directory";
02112     break;
02113 
02114   case ITYPE_BOX:
02115     return "message/digest";
02116     break;
02117 
02118   case ITYPE_DIR_AND_BOX:
02119     return "message/directory";
02120     break;
02121 
02122   case ITYPE_MSG:
02123     return "message/rfc822";
02124     break;
02125 
02126   // this should be handled by flushOutput
02127   case ITYPE_ATTACH:
02128     return "application/octet-stream";
02129     break;
02130 
02131   case ITYPE_UNKNOWN:
02132   default:
02133     return "unknown/unknown";
02134   }
02135 }
02136 
02137 
02138 
02139 void
02140 IMAP4Protocol::doListEntry (const KURL & _url, int stretch, imapCache * cache,
02141   bool withFlags, bool withSubject)
02142 {
02143   KURL aURL = _url;
02144   aURL.setQuery (QString::null);
02145   const QString encodedUrl = aURL.url(0, 106); // utf-8
02146   doListEntry(encodedUrl, stretch, cache, withFlags, withSubject);
02147 }
02148 
02149 
02150 
02151 void
02152 IMAP4Protocol::doListEntry (const QString & encodedUrl, int stretch, imapCache * cache,
02153   bool withFlags, bool withSubject)
02154 {
02155   if (cache)
02156   {
02157     UDSEntry entry;
02158     UDSAtom atom;
02159 
02160     entry.clear ();
02161 
02162     const QString uid = QString::number(cache->getUid());
02163 
02164     atom.m_uds = UDS_NAME;
02165     atom.m_str = uid;
02166     atom.m_long = 0;
02167     if (stretch > 0)
02168     {
02169       atom.m_str = "0000000000000000" + atom.m_str;
02170       atom.m_str = atom.m_str.right (stretch);
02171     }
02172     if (withSubject)
02173     {
02174       mailHeader *header = cache->getHeader();
02175       if (header)
02176         atom.m_str += " " + header->getSubject();
02177     }
02178     entry.append (atom);
02179 
02180     atom.m_uds = UDS_URL;
02181     atom.m_str = encodedUrl; // utf-8
02182     if (atom.m_str[atom.m_str.length () - 1] != '/')
02183       atom.m_str += '/';
02184     atom.m_str += ";UID=" + uid;
02185     atom.m_long = 0;
02186     entry.append (atom);
02187 
02188     atom.m_uds = UDS_FILE_TYPE;
02189     atom.m_str = QString::null;
02190     atom.m_long = S_IFREG;
02191     entry.append (atom);
02192 
02193     atom.m_uds = UDS_SIZE;
02194     atom.m_long = cache->getSize();
02195     entry.append (atom);
02196 
02197     atom.m_uds = UDS_MIME_TYPE;
02198     atom.m_str = "message/rfc822";
02199     atom.m_long = 0;
02200     entry.append (atom);
02201 
02202     atom.m_uds = UDS_USER;
02203     atom.m_str = myUser;
02204     entry.append (atom);
02205 
02206     atom.m_uds = KIO::UDS_ACCESS;
02207     atom.m_long = (withFlags) ? cache->getFlags() : S_IRUSR | S_IXUSR | S_IWUSR;
02208     entry.append (atom);
02209 
02210     listEntry (entry, false);
02211   }
02212 }
02213 
02214 void
02215 IMAP4Protocol::doListEntry (const KURL & _url, const QString & myBox,
02216                             const imapList & item, bool appendPath)
02217 {
02218   KURL aURL = _url;
02219   aURL.setQuery (QString::null);
02220   UDSEntry entry;
02221   UDSAtom atom;
02222   int hdLen = item.hierarchyDelimiter().length();
02223 
02224   {
02225     // mailboxName will be appended to the path if appendPath is true
02226     QString mailboxName = item.name ();
02227 
02228     // some beautification
02229     if (mailboxName.find (myBox) == 0 && mailboxName.length() > myBox.length())
02230     {
02231       mailboxName =
02232         mailboxName.right (mailboxName.length () - myBox.length ());
02233     }
02234     if (mailboxName[0] == '/')
02235         mailboxName = mailboxName.right (mailboxName.length () - 1);
02236     if (mailboxName.left(hdLen) == item.hierarchyDelimiter())
02237       mailboxName = mailboxName.right(mailboxName.length () - hdLen);
02238     if (mailboxName.right(hdLen) == item.hierarchyDelimiter())
02239       mailboxName.truncate(mailboxName.length () - hdLen);
02240 
02241     atom.m_uds = UDS_NAME;
02242     if (!item.hierarchyDelimiter().isEmpty() &&
02243         mailboxName.find(item.hierarchyDelimiter()) != -1)
02244       atom.m_str = mailboxName.section(item.hierarchyDelimiter(), -1);
02245     else
02246       atom.m_str = mailboxName;
02247 
02248     // konqueror will die with an assertion failure otherwise
02249     if (atom.m_str.isEmpty ())
02250       atom.m_str = "..";
02251 
02252     if (!atom.m_str.isEmpty ())
02253     {
02254       atom.m_long = 0;
02255       entry.append (atom);
02256 
02257       if (!item.noSelect ())
02258       {
02259         atom.m_uds = UDS_MIME_TYPE;
02260         if (!item.noInferiors ())
02261         {
02262           atom.m_str = "message/directory";
02263         } else {
02264           atom.m_str = "message/digest";
02265         }
02266         atom.m_long = 0;
02267         entry.append (atom);
02268         mailboxName += '/';
02269 
02270         // explicitly set this as a directory for KFileDialog
02271         atom.m_uds = UDS_FILE_TYPE;
02272         atom.m_str = QString::null;
02273         atom.m_long = S_IFDIR;
02274         entry.append (atom);
02275       }
02276       else if (!item.noInferiors ())
02277       {
02278         atom.m_uds = UDS_MIME_TYPE;
02279         atom.m_str = "inode/directory";
02280         atom.m_long = 0;
02281         entry.append (atom);
02282         mailboxName += '/';
02283 
02284         // explicitly set this as a directory for KFileDialog
02285         atom.m_uds = UDS_FILE_TYPE;
02286         atom.m_str = QString::null;
02287         atom.m_long = S_IFDIR;
02288         entry.append (atom);
02289       }
02290       else
02291       {
02292         atom.m_uds = UDS_MIME_TYPE;
02293         atom.m_str = "unknown/unknown";
02294         atom.m_long = 0;
02295         entry.append (atom);
02296       }
02297 
02298       atom.m_uds = UDS_URL;
02299       QString path = aURL.path();
02300       atom.m_str = aURL.url (0, 106); // utf-8
02301       if (appendPath)
02302       {
02303         if (path[path.length() - 1] == '/' && !path.isEmpty() && path != "/")
02304           path.truncate(path.length() - 1);
02305         if (!path.isEmpty() && path != "/"
02306             && path.right(hdLen) != item.hierarchyDelimiter()) {
02307           path += item.hierarchyDelimiter();
02308         }
02309         path += mailboxName;
02310         if (path.upper() == "/INBOX/") {
02311             // make sure the client can rely on INBOX
02312             path = path.upper();
02313         }
02314       }
02315       aURL.setPath(path);
02316       atom.m_str = aURL.url(0, 106); // utf-8
02317       atom.m_long = 0;
02318       entry.append (atom);
02319 
02320       atom.m_uds = UDS_USER;
02321       atom.m_str = myUser;
02322       entry.append (atom);
02323 
02324       atom.m_uds = UDS_ACCESS;
02325       atom.m_long = S_IRUSR | S_IXUSR | S_IWUSR;
02326       entry.append (atom);
02327 
02328       atom.m_uds = UDS_EXTRA;
02329       atom.m_str = item.attributesAsString();
02330       atom.m_long = 0;
02331       entry.append (atom);
02332 
02333       listEntry (entry, false);
02334     }
02335   }
02336 }
02337 
02338 enum IMAP_TYPE
02339 IMAP4Protocol::parseURL (const KURL & _url, QString & _box,
02340                          QString & _section, QString & _type, QString & _uid,
02341                          QString & _validity, QString & _hierarchyDelimiter,
02342                          QString & _info, bool cache)
02343 {
02344   enum IMAP_TYPE retVal;
02345   retVal = ITYPE_UNKNOWN;
02346 
02347   imapParser::parseURL (_url, _box, _section, _type, _uid, _validity, _info);
02348 //  kdDebug(7116) << "URL: query - '" << KURL::decode_string(_url.query()) << "'" << endl;
02349 
02350   // get the delimiter
02351   QString myNamespace = namespaceForBox( _box );
02352   kdDebug(7116) << "IMAP4::parseURL - namespace=" << myNamespace << endl;
02353   if ( namespaceToDelimiter.contains(myNamespace) )
02354   {
02355     _hierarchyDelimiter = namespaceToDelimiter[myNamespace];
02356     kdDebug(7116) << "IMAP4::parseURL - delimiter=" << _hierarchyDelimiter << endl;
02357   }
02358 
02359   if (!_box.isEmpty ())
02360   {
02361     kdDebug(7116) << "IMAP4::parseURL - box=" << _box << endl;
02362 
02363     if (makeLogin ())
02364     {
02365       if (getCurrentBox () != _box ||
02366           _type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK")
02367       {
02368         if ( cache )
02369         {
02370           // assume a normal box
02371           retVal = ITYPE_DIR_AND_BOX;
02372         } else
02373         {
02374           // start a listing for the box to get the type
02375           imapCommand *cmd;
02376 
02377           cmd = doCommand (imapCommand::clientList ("", _box));
02378           if (cmd->result () == "OK")
02379           {
02380             for (QValueListIterator < imapList > it = listResponses.begin ();
02381                 it != listResponses.end (); ++it)
02382             {
02383               //kdDebug(7116) << "IMAP4::parseURL - checking " << _box << " to " << (*it).name() << endl;
02384               if (_box == (*it).name ())
02385               {
02386                 if ( !(*it).hierarchyDelimiter().isEmpty() )
02387                   _hierarchyDelimiter = (*it).hierarchyDelimiter();
02388                 if ((*it).noSelect ())
02389                 {
02390                   retVal = ITYPE_DIR;
02391                 }
02392                 else if ((*it).noInferiors ())
02393                 {
02394                   retVal = ITYPE_BOX;
02395                 }
02396                 else
02397                 {
02398                   retVal = ITYPE_DIR_AND_BOX;
02399                 }
02400               }
02401             }
02402             // if we got no list response for the box see if it's a prefix
02403             if ( retVal == ITYPE_UNKNOWN &&
02404                  namespaceToDelimiter.contains(_box) ) {
02405               retVal = ITYPE_DIR;
02406             }
02407           } else {
02408             kdDebug(7116) << "IMAP4::parseURL - got error for " << _box << endl;
02409           }
02410           completeQueue.removeRef (cmd);
02411         } // cache
02412       }
02413       else // current == box
02414       {
02415         retVal = ITYPE_BOX;
02416       }
02417     }
02418     else
02419       kdDebug(7116) << "IMAP4::parseURL: no login!" << endl;
02420 
02421   }
02422   else // empty box
02423   {
02424     // the root is just a dir
02425     kdDebug(7116) << "IMAP4: parseURL: box [root]" << endl;
02426     retVal = ITYPE_DIR;
02427   }
02428 
02429   // see if it is a real sequence or a simple uid
02430   if (retVal == ITYPE_BOX || retVal == ITYPE_DIR_AND_BOX)
02431   {
02432     if (!_uid.isEmpty ())
02433     {
02434       if (_uid.find (':') == -1 && _uid.find (',') == -1
02435           && _uid.find ('*') == -1)
02436         retVal = ITYPE_MSG;
02437     }
02438   }
02439   if (retVal == ITYPE_MSG)
02440   {
02441     if ( (_section.find ("BODY.PEEK[", 0, false) != -1 ||
02442           _section.find ("BODY[", 0, false) != -1) &&
02443          _section.find(".MIME") == -1 &&
02444          _section.find(".HEADER") == -1 )
02445       retVal = ITYPE_ATTACH;
02446   }
02447   if ( _hierarchyDelimiter.isEmpty() &&
02448        (_type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK") )
02449   {
02450     // this shouldn't happen but when the delimiter is really empty
02451     // we try to reconstruct it from the URL
02452     if (!_box.isEmpty())
02453     {
02454       int start = _url.path().findRev(_box);
02455       if (start != -1)
02456         _hierarchyDelimiter = _url.path().mid(start-1, start);
02457       kdDebug(7116) << "IMAP4::parseURL - reconstructed delimiter:" << _hierarchyDelimiter
02458         << " from URL " << _url.path() << endl;
02459     }
02460     if (_hierarchyDelimiter.isEmpty())
02461       _hierarchyDelimiter = "/";
02462   }
02463   kdDebug(7116) << "IMAP4::parseURL - return " << retVal << endl;
02464 
02465   return retVal;
02466 }
02467 
02468 int
02469 IMAP4Protocol::outputLine (const QCString & _str, int len)
02470 {
02471   if (len == -1) {
02472     len = _str.length();
02473   }
02474 
02475   if (cacheOutput)
02476   {
02477     if ( !outputBuffer.isOpen() ) {
02478       outputBuffer.open(IO_WriteOnly);
02479     }
02480     outputBuffer.at(outputBufferIndex);
02481     outputBuffer.writeBlock(_str.data(), len);
02482     outputBufferIndex += len;
02483     return 0;
02484   }
02485 
02486   QByteArray temp;
02487   bool relay = relayEnabled;
02488 
02489   relayEnabled = true;
02490   temp.setRawData (_str.data (), len);
02491   parseRelay (temp);
02492   temp.resetRawData (_str.data (), len);
02493 
02494   relayEnabled = relay;
02495   return 0;
02496 }
02497 
02498 void IMAP4Protocol::flushOutput(QString contentEncoding)
02499 {
02500   // send out cached data to the application
02501   if (outputBufferIndex == 0)
02502     return;
02503   outputBuffer.close();
02504   outputCache.resize(outputBufferIndex);
02505   if (decodeContent)
02506   {
02507     // get the coding from the MIME header
02508     QByteArray decoded;
02509     if (contentEncoding.find("quoted-printable", 0, false) == 0)
02510       decoded = KCodecs::quotedPrintableDecode(outputCache);
02511     else if (contentEncoding.find("base64", 0, false) == 0)
02512       KCodecs::base64Decode(outputCache, decoded);
02513     else
02514       decoded = outputCache;
02515 
02516     QString mimetype = KMimeType::findByContent( decoded )->name();
02517     kdDebug(7116) << "IMAP4::flushOutput - mimeType " << mimetype << endl;
02518     mimeType(mimetype);
02519     decodeContent = false;
02520     data( decoded );
02521   } else {
02522     data( outputCache );
02523   }
02524   mProcessedSize += outputBufferIndex;
02525   processedSize( mProcessedSize );
02526   outputBufferIndex = 0;
02527   outputCache[0] = '\0';
02528   outputBuffer.setBuffer(outputCache);
02529 }
02530 
02531 ssize_t IMAP4Protocol::myRead(void *data, ssize_t len)
02532 {
02533   if (readBufferLen)
02534   {
02535     ssize_t copyLen = (len < readBufferLen) ? len : readBufferLen;
02536     memcpy(data, readBuffer, copyLen);
02537     readBufferLen -= copyLen;
02538     if (readBufferLen) memcpy(readBuffer, &readBuffer[copyLen], readBufferLen);
02539     return copyLen;
02540   }
02541   if (!isConnectionValid()) return 0;
02542   waitForResponse( responseTimeout() );
02543   return read(data, len);
02544 }
02545 
02546 bool
02547 IMAP4Protocol::assureBox (const QString & aBox, bool readonly)
02548 {
02549   if (aBox.isEmpty()) return false;
02550 
02551   imapCommand *cmd = 0;
02552 
02553   if (aBox != getCurrentBox () || (!getSelected().readWrite() && !readonly))
02554   {
02555     // open the box with the appropriate mode
02556     kdDebug(7116) << "IMAP4Protocol::assureBox - opening box" << endl;
02557     selectInfo = imapInfo();
02558     cmd = doCommand (imapCommand::clientSelect (aBox, readonly));
02559     bool ok = cmd->result() == "OK";
02560     QString cmdInfo = cmd->resultInfo();
02561     completeQueue.removeRef (cmd);
02562 
02563     if (!ok)
02564     {
02565       bool found = false;
02566       cmd = doCommand (imapCommand::clientList ("", aBox));
02567       if (cmd->result () == "OK")
02568       {
02569         for (QValueListIterator < imapList > it = listResponses.begin ();
02570              it != listResponses.end (); ++it)
02571         {
02572           if (aBox == (*it).name ()) found = true;
02573         }
02574       }
02575       completeQueue.removeRef (cmd);
02576       if (found) {
02577         if (cmdInfo.find("permission", 0, false) != -1) {
02578           // not allowed to enter this folder
02579           error(ERR_ACCESS_DENIED, cmdInfo);
02580         } else {
02581           error(ERR_SLAVE_DEFINED, i18n("Unable to open folder %1. The server replied: %2").arg(aBox).arg(cmdInfo));
02582         }
02583       } else {
02584         error(KIO::ERR_DOES_NOT_EXIST, aBox);
02585       }
02586       return false;
02587     }
02588   }
02589   else
02590   {
02591     // Give the server a chance to deliver updates every ten seconds.
02592     // Doing this means a server roundtrip and since assureBox is called
02593     // after every mail, we do it with a timeout.
02594     kdDebug(7116) << "IMAP4Protocol::assureBox - reusing box" << endl;
02595     if ( mTimeOfLastNoop.secsTo( QDateTime::currentDateTime() ) > 10 ) {
02596       cmd = doCommand (imapCommand::clientNoop ());
02597       completeQueue.removeRef (cmd);
02598       mTimeOfLastNoop = QDateTime::currentDateTime();
02599       kdDebug(7116) << "IMAP4Protocol::assureBox - noop timer fired" << endl;
02600     }
02601   }
02602 
02603   // if it is the mode we want
02604   if (!getSelected().readWrite() && !readonly)
02605   {
02606     error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox);
02607     return false;
02608   }
02609 
02610   return true;
02611 }
KDE Home | KDE Accessibility Home | Description of Access Keys