kioslaves/imap4

imap4.cc
1 /**********************************************************************
2  *
3  * imap4.cc - IMAP4rev1 KIOSlave
4  * Copyright (C) 2001-2002 Michael Haeckel <haeckel@kde.org>
5  * Copyright (C) 1999 John Corey
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * Send comments and bug fixes to jcorey@fruity.ath.cx
22  *
23  *********************************************************************/
24 
59 #ifdef HAVE_CONFIG_H
60 #include <config.h>
61 #endif
62 
63 #include "imap4.h"
64 
65 #include "rfcdecoder.h"
66 
67 #include <sys/stat.h>
68 
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <signal.h>
72 #include <sys/types.h>
73 #include <sys/wait.h>
74 
75 #ifdef HAVE_LIBSASL2
76 extern "C" {
77 #include <sasl/sasl.h>
78 }
79 #endif
80 
81 #include <tqbuffer.h>
82 #include <tqdatetime.h>
83 #include <tqregexp.h>
84 #include <kprotocolmanager.h>
85 #include <kmessagebox.h>
86 #include <kdebug.h>
87 #include <kio/connection.h>
88 #include <kio/slaveinterface.h>
89 #include <kio/passdlg.h>
90 #include <klocale.h>
91 #include <kmimetype.h>
92 #include <kmdcodec.h>
93 
94 #include "kdepimmacros.h"
95 
96 #define IMAP_PROTOCOL "imap"
97 #define IMAP_SSL_PROTOCOL "imaps"
98 
99 using namespace KIO;
100 
101 extern "C"
102 {
103  void sigalrm_handler (int);
104  KDE_EXPORT int kdemain (int argc, char **argv);
105 }
106 
107 int
108 kdemain (int argc, char **argv)
109 {
110  kdDebug(7116) << "IMAP4::kdemain" << endl;
111 
112  KInstance instance ("kio_imap4");
113  if (argc != 4)
114  {
115  fprintf(stderr, "Usage: kio_imap4 protocol domain-socket1 domain-socket2\n");
116  ::exit (-1);
117  }
118 
119 #ifdef HAVE_LIBSASL2
120  if ( sasl_client_init( NULL ) != SASL_OK ) {
121  fprintf(stderr, "SASL library initialization failed!\n");
122  ::exit (-1);
123  }
124 #endif
125 
126  //set debug handler
127 
128  IMAP4Protocol *slave;
129  if (strcasecmp (argv[1], IMAP_SSL_PROTOCOL) == 0)
130  slave = new IMAP4Protocol (argv[2], argv[3], true);
131  else if (strcasecmp (argv[1], IMAP_PROTOCOL) == 0)
132  slave = new IMAP4Protocol (argv[2], argv[3], false);
133  else
134  abort ();
135  slave->dispatchLoop ();
136  delete slave;
137 
138 #ifdef HAVE_LIBSASL2
139  sasl_done();
140 #endif
141 
142  return 0;
143 }
144 
145 void
146 sigchld_handler (int signo)
147 {
148  int pid, status;
149 
150  while (true && signo == SIGCHLD)
151  {
152  pid = waitpid (-1, &status, WNOHANG);
153  if (pid <= 0)
154  {
155  // Reinstall signal handler, since Linux resets to default after
156  // the signal occurred ( BSD handles it different, but it should do
157  // no harm ).
158  signal (SIGCHLD, sigchld_handler);
159  return;
160  }
161  }
162 }
163 
164 IMAP4Protocol::IMAP4Protocol (const TQCString & pool, const TQCString & app, bool isSSL):TCPSlaveBase ((isSSL ? 993 : 143),
165  (isSSL ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL), pool,
166  app, isSSL), imapParser (), mimeIO (), outputBuffer(outputCache)
167 {
168  outputBufferIndex = 0;
169  mySSL = isSSL;
170  readBuffer[0] = 0x00;
171  relayEnabled = false;
172  readBufferLen = 0;
173  cacheOutput = false;
174  decodeContent = false;
175  mTimeOfLastNoop = TQDateTime();
176 }
177 
178 IMAP4Protocol::~IMAP4Protocol ()
179 {
180  closeDescriptor();
181  kdDebug(7116) << "IMAP4: Finishing" << endl;
182 }
183 
184 void
185 IMAP4Protocol::get (const KURL & _url)
186 {
187  if (!makeLogin()) return;
188  kdDebug(7116) << "IMAP4::get - " << _url.prettyURL() << endl;
189  TQString aBox, aSequence, aType, aSection, aValidity, aDelimiter, aInfo;
190  enum IMAP_TYPE aEnum =
191  parseURL (_url, aBox, aSection, aType, aSequence, aValidity, aDelimiter, aInfo);
192  if (aEnum != ITYPE_ATTACH)
193  mimeType (getMimeType(aEnum));
194  if (aInfo == "DECODE")
195  decodeContent = true;
196 
197  if (aSequence == "0:0" && getState() == ISTATE_SELECT)
198  {
199  imapCommand *cmd = doCommand (imapCommand::clientNoop());
200  completeQueue.removeRef(cmd);
201  }
202 
203  if (aSequence.isEmpty ())
204  {
205  aSequence = "1:*";
206  }
207 
208  mProcessedSize = 0;
209  imapCommand *cmd = NULL;
210  if (!assureBox (aBox, true)) return;
211 
212 #ifdef USE_VALIDITY
213  if (selectInfo.uidValidityAvailable () && !aValidity.isEmpty ()
214  && selectInfo.uidValidity () != aValidity.toULong ())
215  {
216  // this url is stale
217  error (ERR_COULD_NOT_READ, _url.prettyURL());
218  return;
219  }
220  else
221 #endif
222  {
223  // The "section" specified by the application can be:
224  // * empty (which means body, size and flags)
225  // * a known keyword, like STRUCTURE, ENVELOPE, HEADER, BODY.PEEK[...]
226  // (in which case the slave has some logic to add the necessary items)
227  // * Otherwise, it specifies the exact data items to request. In this case, all
228  // the logic is in the app.
229 
230  TQString aUpper = aSection.upper();
231  if (aUpper.find ("STRUCTURE") != -1)
232  {
233  aSection = "BODYSTRUCTURE";
234  }
235  else if (aUpper.find ("ENVELOPE") != -1)
236  {
237  aSection = "UID RFC822.SIZE FLAGS ENVELOPE";
238  if (hasCapability("IMAP4rev1")) {
239  aSection += " BODY.PEEK[HEADER.FIELDS (REFERENCES)]";
240  } else {
241  // imap4 does not know HEADER.FIELDS
242  aSection += " RFC822.HEADER.LINES (REFERENCES)";
243  }
244  }
245  else if (aUpper == "HEADER")
246  {
247  aSection = "UID RFC822.HEADER RFC822.SIZE FLAGS";
248  }
249  else if (aUpper.find ("BODY.PEEK[") != -1)
250  {
251  if (aUpper.find ("BODY.PEEK[]") != -1)
252  {
253  if (!hasCapability("IMAP4rev1")) // imap4 does not know BODY.PEEK[]
254  aSection.replace("BODY.PEEK[]", "RFC822.PEEK");
255  }
256  aSection.prepend("UID RFC822.SIZE FLAGS ");
257  }
258  else if (aSection.isEmpty())
259  {
260  aSection = "UID BODY[] RFC822.SIZE FLAGS";
261  }
262  if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
263  {
264  // write the digest header
265  cacheOutput = true;
266  outputLine
267  ("Content-Type: multipart/digest; boundary=\"IMAPDIGEST\"\r\n", 55);
268  if (selectInfo.recentAvailable ())
269  outputLineStr ("X-Recent: " +
270  TQString::number(selectInfo.recent ()) + "\r\n");
271  if (selectInfo.countAvailable ())
272  outputLineStr ("X-Count: " + TQString::number(selectInfo.count ()) +
273  "\r\n");
274  if (selectInfo.unseenAvailable ())
275  outputLineStr ("X-Unseen: " +
276  TQString::number(selectInfo.unseen ()) + "\r\n");
277  if (selectInfo.uidValidityAvailable ())
278  outputLineStr ("X-uidValidity: " +
279  TQString::number(selectInfo.uidValidity ()) +
280  "\r\n");
281  if (selectInfo.uidNextAvailable ())
282  outputLineStr ("X-UidNext: " +
283  TQString::number(selectInfo.uidNext ()) + "\r\n");
284  if (selectInfo.flagsAvailable ())
285  outputLineStr ("X-Flags: " + TQString::number(selectInfo.flags ()) +
286  "\r\n");
287  if (selectInfo.permanentFlagsAvailable ())
288  outputLineStr ("X-PermanentFlags: " +
289  TQString::number(selectInfo.permanentFlags ()) + "\r\n");
290  if (selectInfo.readWriteAvailable ()) {
291  if (selectInfo.readWrite()) {
292  outputLine ("X-Access: Read/Write\r\n", 22);
293  } else {
294  outputLine ("X-Access: Read only\r\n", 21);
295  }
296  }
297  outputLine ("\r\n", 2);
298  flushOutput(TQString());
299  cacheOutput = false;
300  }
301 
302  if (aEnum == ITYPE_MSG || (aEnum == ITYPE_ATTACH && !decodeContent))
303  relayEnabled = true; // normal mode, relay data
304 
305  if (aSequence != "0:0")
306  {
307  TQString contentEncoding;
308  if (aEnum == ITYPE_ATTACH && decodeContent)
309  {
310  // get the MIME header and fill getLastHandled()
311  TQString mySection = aSection;
312  mySection.replace("]", ".MIME]");
313  cmd = sendCommand (imapCommand::clientFetch (aSequence, mySection));
314  do
315  {
316  while (!parseLoop ()) ;
317  }
318  while (!cmd->isComplete ());
319  completeQueue.removeRef (cmd);
320  // get the content encoding now because getLastHandled will be cleared
321  if (getLastHandled() && getLastHandled()->getHeader())
322  contentEncoding = getLastHandled()->getHeader()->getEncoding();
323 
324  // from here on collect the data
325  // it is send to the client in flushOutput in one go
326  // needed to decode the content
327  cacheOutput = true;
328  }
329 
330  cmd = sendCommand (imapCommand::clientFetch (aSequence, aSection));
331  int res;
332  aUpper = aSection.upper();
333  do
334  {
335  while (!(res = parseLoop())) ;
336  if (res == -1) break;
337 
338  mailHeader *lastone = 0;
339  imapCache *cache = getLastHandled ();
340  if (cache)
341  lastone = cache->getHeader ();
342 
343  if (cmd && !cmd->isComplete ())
344  {
345  if ((aUpper.find ("BODYSTRUCTURE") != -1)
346  || (aUpper.find ("FLAGS") != -1)
347  || (aUpper.find ("UID") != -1)
348  || (aUpper.find ("ENVELOPE") != -1)
349  || (aUpper.find ("BODY.PEEK[0]") != -1
350  && (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)))
351  {
352  if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
353  {
354  // write the mime header (default is here message/rfc822)
355  outputLine ("--IMAPDIGEST\r\n", 14);
356  cacheOutput = true;
357  if (cache && cache->getUid () != 0)
358  outputLineStr ("X-UID: " +
359  TQString::number(cache->getUid ()) + "\r\n");
360  if (cache && cache->getSize () != 0)
361  outputLineStr ("X-Length: " +
362  TQString::number(cache->getSize ()) + "\r\n");
363  if (cache && !cache->getDate ().isEmpty())
364  outputLineStr ("X-Date: " + cache->getDate () + "\r\n");
365  if (cache && cache->getFlags () != 0)
366  outputLineStr ("X-Flags: " +
367  TQString::number(cache->getFlags ()) + "\r\n");
368  } else cacheOutput = true;
369  if ( lastone && !decodeContent )
370  lastone->outputPart (*this);
371  cacheOutput = false;
372  flushOutput(contentEncoding);
373  }
374  } // if not complete
375  }
376  while (cmd && !cmd->isComplete ());
377  if (aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX)
378  {
379  // write the end boundary
380  outputLine ("--IMAPDIGEST--\r\n", 16);
381  }
382 
383  completeQueue.removeRef (cmd);
384  }
385  }
386 
387  // just to keep everybody happy when no data arrived
388  data (TQByteArray ());
389 
390  finished ();
391  relayEnabled = false;
392  cacheOutput = false;
393  kdDebug(7116) << "IMAP4::get - finished" << endl;
394 }
395 
396 void
397 IMAP4Protocol::listDir (const KURL & _url)
398 {
399  kdDebug(7116) << " IMAP4::listDir - " << _url.prettyURL() << endl;
400 
401  if (_url.path().isEmpty())
402  {
403  KURL url = _url;
404  url.setPath("/");
405  redirection( url );
406  finished();
407  return;
408  }
409 
410  TQString myBox, mySequence, myLType, mySection, myValidity, myDelimiter, myInfo;
411  // parseURL with caching
412  enum IMAP_TYPE myType =
413  parseURL (_url, myBox, mySection, myLType, mySequence, myValidity,
414  myDelimiter, myInfo, true);
415 
416  if (!makeLogin()) return;
417 
418  if (myType == ITYPE_DIR || myType == ITYPE_DIR_AND_BOX)
419  {
420  TQString listStr = myBox;
421  imapCommand *cmd;
422 
423  if (!listStr.isEmpty () && !listStr.endsWith(myDelimiter) &&
424  mySection != "FOLDERONLY")
425  listStr += myDelimiter;
426 
427  if (mySection.isEmpty())
428  {
429  listStr += "%";
430  } else if (mySection == "COMPLETE") {
431  listStr += "*";
432  }
433  kdDebug(7116) << "IMAP4Protocol::listDir - listStr=" << listStr << endl;
434  cmd =
435  doCommand (imapCommand::clientList ("", listStr,
436  (myLType == "LSUB" || myLType == "LSUBNOCHECK")));
437  if (cmd->result () == "OK")
438  {
439  TQString mailboxName;
440  UDSEntry entry;
441  UDSAtom atom;
442  KURL aURL = _url;
443  if (aURL.path().find(';') != -1)
444  aURL.setPath(aURL.path().left(aURL.path().find(';')));
445 
446  kdDebug(7116) << "IMAP4Protocol::listDir - got " << listResponses.count () << endl;
447 
448  if (myLType == "LSUB")
449  {
450  // fire the same command as LIST to check if the box really exists
451  TQValueList<imapList> listResponsesSave = listResponses;
452  doCommand (imapCommand::clientList ("", listStr, false));
453  for (TQValueListIterator < imapList > it = listResponsesSave.begin ();
454  it != listResponsesSave.end (); ++it)
455  {
456  bool boxOk = false;
457  for (TQValueListIterator < imapList > it2 = listResponses.begin ();
458  it2 != listResponses.end (); ++it2)
459  {
460  if ((*it2).name() == (*it).name())
461  {
462  boxOk = true;
463  // copy the flags from the LIST-command
464  (*it) = (*it2);
465  break;
466  }
467  }
468  if (boxOk)
469  doListEntry (aURL, myBox, (*it), (mySection != "FOLDERONLY"));
470  else // this folder is dead
471  kdDebug(7116) << "IMAP4Protocol::listDir - suppress " << (*it).name() << endl;
472  }
473  listResponses = listResponsesSave;
474  }
475  else // LIST or LSUBNOCHECK
476  {
477  for (TQValueListIterator < imapList > it = listResponses.begin ();
478  it != listResponses.end (); ++it)
479  {
480  doListEntry (aURL, myBox, (*it), (mySection != "FOLDERONLY"));
481  }
482  }
483  entry.clear ();
484  listEntry (entry, true);
485  }
486  else
487  {
488  error (ERR_CANNOT_ENTER_DIRECTORY, _url.prettyURL());
489  completeQueue.removeRef (cmd);
490  return;
491  }
492  completeQueue.removeRef (cmd);
493  }
494  if ((myType == ITYPE_BOX || myType == ITYPE_DIR_AND_BOX)
495  && myLType != "LIST" && myLType != "LSUB" && myLType != "LSUBNOCHECK")
496  {
497  KURL aURL = _url;
498  aURL.setQuery (TQString());
499  const TQString encodedUrl = aURL.url(0, 106); // utf-8
500 
501  if (!_url.query ().isEmpty ())
502  {
503  TQString query = KURL::decode_string (_url.query ());
504  query = query.right (query.length () - 1);
505  if (!query.isEmpty())
506  {
507  imapCommand *cmd = NULL;
508 
509  if (!assureBox (myBox, true)) return;
510 
511  if (!selectInfo.countAvailable() || selectInfo.count())
512  {
513  cmd = doCommand (imapCommand::clientSearch (query));
514  if (cmd->result() != "OK")
515  {
516  error(ERR_UNSUPPORTED_ACTION, _url.prettyURL());
517  completeQueue.removeRef (cmd);
518  return;
519  }
520  completeQueue.removeRef (cmd);
521 
522  TQStringList list = getResults ();
523  int stretch = 0;
524 
525  if (selectInfo.uidNextAvailable ())
526  stretch = TQString::number(selectInfo.uidNext ()).length ();
527  UDSEntry entry;
528  imapCache fake;
529 
530  for (TQStringList::ConstIterator it = list.begin(); it != list.end();
531  ++it)
532  {
533  fake.setUid((*it).toULong());
534  doListEntry (encodedUrl, stretch, &fake);
535  }
536  entry.clear ();
537  listEntry (entry, true);
538  }
539  }
540  }
541  else
542  {
543  if (!assureBox (myBox, true)) return;
544 
545  kdDebug(7116) << "IMAP4: select returned:" << endl;
546  if (selectInfo.recentAvailable ())
547  kdDebug(7116) << "Recent: " << selectInfo.recent () << "d" << endl;
548  if (selectInfo.countAvailable ())
549  kdDebug(7116) << "Count: " << selectInfo.count () << "d" << endl;
550  if (selectInfo.unseenAvailable ())
551  kdDebug(7116) << "Unseen: " << selectInfo.unseen () << "d" << endl;
552  if (selectInfo.uidValidityAvailable ())
553  kdDebug(7116) << "uidValidity: " << selectInfo.uidValidity () << "d" << endl;
554  if (selectInfo.flagsAvailable ())
555  kdDebug(7116) << "Flags: " << selectInfo.flags () << "d" << endl;
556  if (selectInfo.permanentFlagsAvailable ())
557  kdDebug(7116) << "PermanentFlags: " << selectInfo.permanentFlags () << "d" << endl;
558  if (selectInfo.readWriteAvailable ())
559  kdDebug(7116) << "Access: " << (selectInfo.readWrite ()? "Read/Write" : "Read only") << endl;
560 
561 #ifdef USE_VALIDITY
562  if (selectInfo.uidValidityAvailable ()
563  && selectInfo.uidValidity () != myValidity.toULong ())
564  {
565  //redirect
566  KURL newUrl = _url;
567 
568  newUrl.setPath ("/" + myBox + ";UIDVALIDITY=" +
569  TQString::number(selectInfo.uidValidity ()));
570  kdDebug(7116) << "IMAP4::listDir - redirecting to " << newUrl.prettyURL() << endl;
571  redirection (newUrl);
572 
573 
574  }
575  else
576 #endif
577  if (selectInfo.count () > 0)
578  {
579  int stretch = 0;
580 
581  if (selectInfo.uidNextAvailable ())
582  stretch = TQString::number(selectInfo.uidNext ()).length ();
583  // kdDebug(7116) << selectInfo.uidNext() << "d used to stretch " << stretch << endl;
584  UDSEntry entry;
585 
586  if (mySequence.isEmpty()) mySequence = "1:*";
587 
588  bool withSubject = mySection.isEmpty();
589  if (mySection.isEmpty()) mySection = "UID RFC822.SIZE ENVELOPE";
590 
591  bool withFlags = mySection.upper().find("FLAGS") != -1;
592  imapCommand *fetch =
593  sendCommand (imapCommand::
594  clientFetch (mySequence, mySection));
595  imapCache *cache;
596  do
597  {
598  while (!parseLoop ()) ;
599 
600  cache = getLastHandled ();
601 
602  if (cache && !fetch->isComplete())
603  doListEntry (encodedUrl, stretch, cache, withFlags, withSubject);
604  }
605  while (!fetch->isComplete ());
606  entry.clear ();
607  listEntry (entry, true);
608  }
609  }
610  }
611  if ( !selectInfo.alert().isNull() ) {
612  if ( !myBox.isEmpty() ) {
613  warning( i18n( "Message from %1 while processing '%2': %3" ).arg( myHost, myBox, selectInfo.alert() ) );
614  } else {
615  warning( i18n( "Message from %1: %2" ).arg( myHost, TQString(selectInfo.alert()) ) );
616  }
617  selectInfo.setAlert( 0 );
618  }
619 
620  kdDebug(7116) << "IMAP4Protocol::listDir - Finishing listDir" << endl;
621  finished ();
622 }
623 
624 void
625 IMAP4Protocol::setHost (const TQString & _host, int _port,
626  const TQString & _user, const TQString & _pass)
627 {
628  if (myHost != _host || myPort != _port || myUser != _user || myPass != _pass)
629  { // what's the point of doing 4 string compares to avoid 4 string copies?
630  // DF: I guess to avoid calling closeConnection() unnecessarily.
631  if (!myHost.isEmpty ())
632  closeConnection ();
633  myHost = _host;
634  myPort = _port;
635  myUser = _user;
636  myPass = _pass;
637  }
638 }
639 
640 void
641 IMAP4Protocol::parseRelay (const TQByteArray & buffer)
642 {
643  if (relayEnabled) {
644  // relay data immediately
645  data( buffer );
646  mProcessedSize += buffer.size();
647  processedSize( mProcessedSize );
648  } else if (cacheOutput)
649  {
650  // collect data
651  if ( !outputBuffer.isOpen() ) {
652  outputBuffer.open(IO_WriteOnly);
653  }
654  outputBuffer.at(outputBufferIndex);
655  outputBuffer.writeBlock(buffer, buffer.size());
656  outputBufferIndex += buffer.size();
657  }
658 }
659 
660 void
662 {
663  if (relayEnabled)
664  totalSize (len);
665 }
666 
667 
668 bool IMAP4Protocol::parseRead(TQByteArray & buffer, ulong len, ulong relay)
669 {
670  char buf[8192];
671  while (buffer.size() < len)
672  {
673  ssize_t readLen = myRead(buf, TQMIN(len - buffer.size(), sizeof(buf) - 1));
674  if (readLen == 0)
675  {
676  kdDebug(7116) << "parseRead: readLen == 0 - connection broken" << endl;
677  error (ERR_CONNECTION_BROKEN, myHost);
678  setState(ISTATE_CONNECT);
679  closeConnection();
680  return FALSE;
681  }
682  if (relay > buffer.size())
683  {
684  TQByteArray relayData;
685  ssize_t relbuf = relay - buffer.size();
686  int currentRelay = TQMIN(relbuf, readLen);
687  relayData.setRawData(buf, currentRelay);
688  parseRelay(relayData);
689  relayData.resetRawData(buf, currentRelay);
690  }
691  {
692  TQBuffer stream (buffer);
693  stream.open (IO_WriteOnly);
694  stream.at (buffer.size ());
695  stream.writeBlock (buf, readLen);
696  stream.close ();
697  }
698  }
699  return (buffer.size() == len);
700 }
701 
702 
703 bool IMAP4Protocol::parseReadLine (TQByteArray & buffer, ulong relay)
704 {
705  if (myHost.isEmpty()) return FALSE;
706 
707  while (true) {
708  ssize_t copyLen = 0;
709  if (readBufferLen > 0)
710  {
711  while (copyLen < readBufferLen && readBuffer[copyLen] != '\n') copyLen++;
712  if (copyLen < readBufferLen) copyLen++;
713  if (relay > 0)
714  {
715  TQByteArray relayData;
716 
717  if (copyLen < (ssize_t) relay)
718  relay = copyLen;
719  relayData.setRawData (readBuffer, relay);
720  parseRelay (relayData);
721  relayData.resetRawData (readBuffer, relay);
722 // kdDebug(7116) << "relayed : " << relay << "d" << endl;
723  }
724  // append to buffer
725  {
726  TQBuffer stream (buffer);
727 
728  stream.open (IO_WriteOnly);
729  stream.at (buffer.size ());
730  stream.writeBlock (readBuffer, copyLen);
731  stream.close ();
732 // kdDebug(7116) << "appended " << copyLen << "d got now " << buffer.size() << endl;
733  }
734 
735  readBufferLen -= copyLen;
736  if (readBufferLen)
737  memmove(readBuffer, &readBuffer[copyLen], readBufferLen);
738  if (buffer[buffer.size() - 1] == '\n') return TRUE;
739  }
740  if (!isConnectionValid())
741  {
742  kdDebug(7116) << "parseReadLine - connection broken" << endl;
743  error (ERR_CONNECTION_BROKEN, myHost);
744  setState(ISTATE_CONNECT);
745  closeConnection();
746  return FALSE;
747  }
748  if (!waitForResponse( responseTimeout() ))
749  {
750  error(ERR_SERVER_TIMEOUT, myHost);
751  setState(ISTATE_CONNECT);
752  closeConnection();
753  return FALSE;
754  }
755  readBufferLen = read(readBuffer, IMAP_BUFFER - 1);
756  if (readBufferLen == 0)
757  {
758  kdDebug(7116) << "parseReadLine: readBufferLen == 0 - connection broken" << endl;
759  error (ERR_CONNECTION_BROKEN, myHost);
760  setState(ISTATE_CONNECT);
761  closeConnection();
762  return FALSE;
763  }
764  }
765 }
766 
767 void
768 IMAP4Protocol::setSubURL (const KURL & _url)
769 {
770  kdDebug(7116) << "IMAP4::setSubURL - " << _url.prettyURL() << endl;
771  KIO::TCPSlaveBase::setSubURL (_url);
772 }
773 
774 void
775 IMAP4Protocol::put (const KURL & _url, int, bool, bool)
776 {
777  kdDebug(7116) << "IMAP4::put - " << _url.prettyURL() << endl;
778 // KIO::TCPSlaveBase::put(_url,permissions,overwrite,resume);
779  TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
780  enum IMAP_TYPE aType =
781  parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
782 
783  // see if it is a box
784  if (aType != ITYPE_BOX && aType != ITYPE_DIR_AND_BOX)
785  {
786  if (aBox[aBox.length () - 1] == '/')
787  aBox = aBox.right (aBox.length () - 1);
788  imapCommand *cmd = doCommand (imapCommand::clientCreate (aBox));
789 
790  if (cmd->result () != "OK") {
791  error (ERR_COULD_NOT_WRITE, _url.prettyURL());
792  completeQueue.removeRef (cmd);
793  return;
794  }
795  completeQueue.removeRef (cmd);
796  }
797  else
798  {
799  TQPtrList < TQByteArray > bufferList;
800  int length = 0;
801 
802  int result;
803  // Loop until we got 'dataEnd'
804  do
805  {
806  TQByteArray *buffer = new TQByteArray ();
807  dataReq (); // Request for data
808  result = readData (*buffer);
809  if (result > 0)
810  {
811  bufferList.append (buffer);
812  length += result;
813  } else {
814  delete buffer;
815  }
816  }
817  while (result > 0);
818 
819  if (result != 0)
820  {
821  error (ERR_ABORTED, _url.prettyURL());
822  return;
823  }
824 
825  imapCommand *cmd =
826  sendCommand (imapCommand::clientAppend (aBox, aSection, length));
827  while (!parseLoop ()) ;
828 
829  // see if server is waiting
830  if (!cmd->isComplete () && !getContinuation ().isEmpty ())
831  {
832  bool sendOk = true;
833  ulong wrote = 0;
834 
835  TQByteArray *buffer;
836  // send data to server
837  while (!bufferList.isEmpty () && sendOk)
838  {
839  buffer = bufferList.take (0);
840 
841  sendOk =
842  (write (buffer->data (), buffer->size ()) ==
843  (ssize_t) buffer->size ());
844  wrote += buffer->size ();
845  processedSize(wrote);
846  delete buffer;
847  if (!sendOk)
848  {
849  error (ERR_CONNECTION_BROKEN, myHost);
850  completeQueue.removeRef (cmd);
851  setState(ISTATE_CONNECT);
852  closeConnection();
853  return;
854  }
855  }
856  parseWriteLine ("");
857  // Wait until cmd is complete, or connection breaks.
858  while (!cmd->isComplete () && getState() != ISTATE_NO)
859  parseLoop ();
860  if ( getState() == ISTATE_NO ) {
861  // TODO KDE4: pass cmd->resultInfo() as third argument.
862  // ERR_CONNECTION_BROKEN expects a host, no way to pass details about the problem.
863  error( ERR_CONNECTION_BROKEN, myHost );
864  completeQueue.removeRef (cmd);
865  closeConnection();
866  return;
867  }
868  else if (cmd->result () != "OK") {
869  error( ERR_SLAVE_DEFINED, cmd->resultInfo() );
870  completeQueue.removeRef (cmd);
871  return;
872  }
873  else
874  {
875  if (hasCapability("UIDPLUS"))
876  {
877  TQString uid = cmd->resultInfo();
878  if (uid.find("APPENDUID") != -1)
879  {
880  uid = uid.section(" ", 2, 2);
881  uid.truncate(uid.length()-1);
882  infoMessage("UID "+uid);
883  }
884  }
885  // MUST reselect to get the new message
886  else if (aBox == getCurrentBox ())
887  {
888  cmd =
889  doCommand (imapCommand::
890  clientSelect (aBox, !selectInfo.readWrite ()));
891  completeQueue.removeRef (cmd);
892  }
893  }
894  }
895  else
896  {
897  //error (ERR_COULD_NOT_WRITE, myHost);
898  // Better ship the error message, e.g. "Over Quota"
899  error (ERR_SLAVE_DEFINED, cmd->resultInfo());
900  completeQueue.removeRef (cmd);
901  return;
902  }
903 
904  completeQueue.removeRef (cmd);
905  }
906 
907  finished ();
908 }
909 
910 void
911 IMAP4Protocol::mkdir (const KURL & _url, int)
912 {
913  kdDebug(7116) << "IMAP4::mkdir - " << _url.prettyURL() << endl;
914  TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
915  parseURL(_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
916  kdDebug(7116) << "IMAP4::mkdir - create " << aBox << endl;
917  imapCommand *cmd = doCommand (imapCommand::clientCreate(aBox));
918 
919  if (cmd->result () != "OK")
920  {
921  kdDebug(7116) << "IMAP4::mkdir - " << cmd->resultInfo() << endl;
922  error (ERR_COULD_NOT_MKDIR, _url.prettyURL());
923  completeQueue.removeRef (cmd);
924  return;
925  }
926  completeQueue.removeRef (cmd);
927 
928  // start a new listing to find the type of the folder
929  enum IMAP_TYPE type =
930  parseURL(_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
931  if (type == ITYPE_BOX)
932  {
933  bool ask = ( aInfo.find( "ASKUSER" ) != -1 );
934  if ( ask &&
935  messageBox(QuestionYesNo,
936  i18n("The following folder will be created on the server: %1 "
937  "What do you want to store in this folder?").arg( aBox ),
938  i18n("Create Folder"),
939  i18n("&Messages"), i18n("&Subfolders")) == KMessageBox::No )
940  {
941  cmd = doCommand(imapCommand::clientDelete(aBox));
942  completeQueue.removeRef (cmd);
943  cmd = doCommand(imapCommand::clientCreate(aBox + aDelimiter));
944  if (cmd->result () != "OK")
945  {
946  error (ERR_COULD_NOT_MKDIR, _url.prettyURL());
947  completeQueue.removeRef (cmd);
948  return;
949  }
950  completeQueue.removeRef (cmd);
951  }
952  }
953 
954  cmd = doCommand(imapCommand::clientSubscribe(aBox));
955  completeQueue.removeRef(cmd);
956 
957  finished ();
958 }
959 
960 void
961 IMAP4Protocol::copy (const KURL & src, const KURL & dest, int, bool overwrite)
962 {
963  kdDebug(7116) << "IMAP4::copy - [" << (overwrite ? "Overwrite" : "NoOverwrite") << "] " << src.prettyURL() << " -> " << dest.prettyURL() << endl;
964  TQString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
965  TQString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo;
966  enum IMAP_TYPE sType =
967  parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo);
968  enum IMAP_TYPE dType =
969  parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo);
970 
971  // see if we have to create anything
972  if (dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX)
973  {
974  // this might be konqueror
975  int sub = dBox.find (sBox);
976 
977  // might be moving to upper folder
978  if (sub > 0)
979  {
980  KURL testDir = dest;
981 
982  TQString subDir = dBox.right (dBox.length () - dBox.findRev ('/'));
983  TQString topDir = dBox.left (sub);
984  testDir.setPath ("/" + topDir);
985  dType =
986  parseURL (testDir, topDir, dSection, dLType, dSequence, dValidity,
987  dDelimiter, dInfo);
988 
989  kdDebug(7116) << "IMAP4::copy - checking this destination " << topDir << endl;
990  // see if this is what the user wants
991  if (dType == ITYPE_BOX || dType == ITYPE_DIR_AND_BOX)
992  {
993  kdDebug(7116) << "IMAP4::copy - assuming this destination " << topDir << endl;
994  dBox = topDir;
995  }
996  else
997  {
998 
999  // maybe if we create a new mailbox
1000  topDir = "/" + topDir + subDir;
1001  testDir.setPath (topDir);
1002  kdDebug(7116) << "IMAP4::copy - checking this destination " << topDir << endl;
1003  dType =
1004  parseURL (testDir, topDir, dSection, dLType, dSequence, dValidity,
1005  dDelimiter, dInfo);
1006  if (dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX)
1007  {
1008  // ok then we'll create a mailbox
1009  imapCommand *cmd = doCommand (imapCommand::clientCreate (topDir));
1010 
1011  // on success we'll use it, else we'll just try to create the given dir
1012  if (cmd->result () == "OK")
1013  {
1014  kdDebug(7116) << "IMAP4::copy - assuming this destination " << topDir << endl;
1015  dType = ITYPE_BOX;
1016  dBox = topDir;
1017  }
1018  else
1019  {
1020  completeQueue.removeRef (cmd);
1021  cmd = doCommand (imapCommand::clientCreate (dBox));
1022  if (cmd->result () == "OK")
1023  dType = ITYPE_BOX;
1024  else
1025  error (ERR_COULD_NOT_WRITE, dest.prettyURL());
1026  }
1027  completeQueue.removeRef (cmd);
1028  }
1029  }
1030 
1031  }
1032  }
1033  if (sType == ITYPE_MSG || sType == ITYPE_BOX || sType == ITYPE_DIR_AND_BOX)
1034  {
1035  //select the source box
1036  if (!assureBox(sBox, true)) return;
1037  kdDebug(7116) << "IMAP4::copy - " << sBox << " -> " << dBox << endl;
1038 
1039  //issue copy command
1040  imapCommand *cmd =
1041  doCommand (imapCommand::clientCopy (dBox, sSequence));
1042  if (cmd->result () != "OK")
1043  {
1044  kdError(5006) << "IMAP4::copy - " << cmd->resultInfo() << endl;
1045  error (ERR_COULD_NOT_WRITE, dest.prettyURL());
1046  completeQueue.removeRef (cmd);
1047  return;
1048  } else {
1049  if (hasCapability("UIDPLUS"))
1050  {
1051  TQString uid = cmd->resultInfo();
1052  if (uid.find("COPYUID") != -1)
1053  {
1054  uid = uid.section(" ", 2, 3);
1055  uid.truncate(uid.length()-1);
1056  infoMessage("UID "+uid);
1057  }
1058  }
1059  }
1060  completeQueue.removeRef (cmd);
1061  }
1062  else
1063  {
1064  error (ERR_ACCESS_DENIED, src.prettyURL());
1065  return;
1066  }
1067  finished ();
1068 }
1069 
1070 void
1071 IMAP4Protocol::del (const KURL & _url, bool isFile)
1072 {
1073  kdDebug(7116) << "IMAP4::del - [" << (isFile ? "File" : "NoFile") << "] " << _url.prettyURL() << endl;
1074  TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1075  enum IMAP_TYPE aType =
1076  parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1077 
1078  switch (aType)
1079  {
1080  case ITYPE_BOX:
1081  case ITYPE_DIR_AND_BOX:
1082  if (!aSequence.isEmpty ())
1083  {
1084  if (aSequence == "*")
1085  {
1086  if (!assureBox (aBox, false)) return;
1087  imapCommand *cmd = doCommand (imapCommand::clientExpunge ());
1088  if (cmd->result () != "OK") {
1089  error (ERR_CANNOT_DELETE, _url.prettyURL());
1090  completeQueue.removeRef (cmd);
1091  return;
1092  }
1093  completeQueue.removeRef (cmd);
1094  }
1095  else
1096  {
1097  // if open for read/write
1098  if (!assureBox (aBox, false)) return;
1099  imapCommand *cmd =
1100  doCommand (imapCommand::
1101  clientStore (aSequence, "+FLAGS.SILENT", "\\DELETED"));
1102  if (cmd->result () != "OK") {
1103  error (ERR_CANNOT_DELETE, _url.prettyURL());
1104  completeQueue.removeRef (cmd);
1105  return;
1106  }
1107  completeQueue.removeRef (cmd);
1108  }
1109  }
1110  else
1111  {
1112  if (getCurrentBox() == aBox)
1113  {
1114  imapCommand *cmd = doCommand(imapCommand::clientClose());
1115  completeQueue.removeRef(cmd);
1116  setState(ISTATE_LOGIN);
1117  }
1118  // We unsubscribe, otherwise we get ghost folders on UW-IMAP
1119  imapCommand *cmd = doCommand(imapCommand::clientUnsubscribe(aBox));
1120  completeQueue.removeRef(cmd);
1121  cmd = doCommand(imapCommand::clientDelete (aBox));
1122  // If this doesn't work, we try to empty the mailbox first
1123  if (cmd->result () != "OK")
1124  {
1125  completeQueue.removeRef(cmd);
1126  if (!assureBox(aBox, false)) return;
1127  bool stillOk = true;
1128  if (stillOk)
1129  {
1130  imapCommand *cmd = doCommand(
1131  imapCommand::clientStore("1:*", "+FLAGS.SILENT", "\\DELETED"));
1132  if (cmd->result () != "OK") stillOk = false;
1133  completeQueue.removeRef(cmd);
1134  }
1135  if (stillOk)
1136  {
1137  imapCommand *cmd = doCommand(imapCommand::clientClose());
1138  if (cmd->result () != "OK") stillOk = false;
1139  completeQueue.removeRef(cmd);
1140  setState(ISTATE_LOGIN);
1141  }
1142  if (stillOk)
1143  {
1144  imapCommand *cmd = doCommand (imapCommand::clientDelete(aBox));
1145  if (cmd->result () != "OK") stillOk = false;
1146  completeQueue.removeRef(cmd);
1147  }
1148  if (!stillOk)
1149  {
1150  error (ERR_COULD_NOT_RMDIR, _url.prettyURL());
1151  return;
1152  }
1153  } else {
1154  completeQueue.removeRef (cmd);
1155  }
1156  }
1157  break;
1158 
1159  case ITYPE_DIR:
1160  {
1161  imapCommand *cmd = doCommand (imapCommand::clientDelete (aBox));
1162  if (cmd->result () != "OK") {
1163  error (ERR_COULD_NOT_RMDIR, _url.prettyURL());
1164  completeQueue.removeRef (cmd);
1165  return;
1166  }
1167  completeQueue.removeRef (cmd);
1168  }
1169  break;
1170 
1171  case ITYPE_MSG:
1172  {
1173  // if open for read/write
1174  if (!assureBox (aBox, false)) return;
1175  imapCommand *cmd =
1176  doCommand (imapCommand::
1177  clientStore (aSequence, "+FLAGS.SILENT", "\\DELETED"));
1178  if (cmd->result () != "OK") {
1179  error (ERR_CANNOT_DELETE, _url.prettyURL());
1180  completeQueue.removeRef (cmd);
1181  return;
1182  }
1183  completeQueue.removeRef (cmd);
1184  }
1185  break;
1186 
1187  case ITYPE_UNKNOWN:
1188  case ITYPE_ATTACH:
1189  error (ERR_CANNOT_DELETE, _url.prettyURL());
1190  break;
1191  }
1192  finished ();
1193 }
1194 
1195 /*
1196  * Copy a mail: data = 'C' + srcURL (KURL) + destURL (KURL)
1197  * Capabilities: data = 'c'. Result shipped in infoMessage() signal
1198  * No-op: data = 'N'
1199  * Namespace: data = 'n'. Result shipped in infoMessage() signal
1200  * The format is: section=namespace=delimiter
1201  * Note that the namespace can be empty
1202  * Unsubscribe: data = 'U' + URL (KURL)
1203  * Subscribe: data = 'u' + URL (KURL)
1204  * Change the status: data = 'S' + URL (KURL) + Flags (TQCString)
1205  * ACL commands: data = 'A' + command + URL (KURL) + command-dependent args
1206  * AnnotateMore commands: data = 'M' + 'G'et/'S'et + URL + entry + command-dependent args
1207  * Search: data = 'E' + URL (KURL)
1208  * Quota commands: data = 'Q' + 'R'oot/'G'et/'S'et + URL + entry + command-dependent args
1209  * Custom command: data = 'X' + 'N'ormal/'E'xtended + command + command-dependent args
1210  */
1211 void
1212 IMAP4Protocol::special (const TQByteArray & aData)
1213 {
1214  kdDebug(7116) << "IMAP4Protocol::special" << endl;
1215  if (!makeLogin()) return;
1216 
1217  TQDataStream stream(aData, IO_ReadOnly);
1218 
1219  int tmp;
1220  stream >> tmp;
1221 
1222  switch (tmp) {
1223  case 'C':
1224  {
1225  // copy
1226  KURL src;
1227  KURL dest;
1228  stream >> src >> dest;
1229  copy(src, dest, 0, FALSE);
1230  break;
1231  }
1232  case 'c':
1233  {
1234  // capabilities
1235  infoMessage(imapCapabilities.join(" "));
1236  finished();
1237  break;
1238  }
1239  case 'N':
1240  {
1241  // NOOP
1242  imapCommand *cmd = doCommand(imapCommand::clientNoop());
1243  if (cmd->result () != "OK")
1244  {
1245  kdDebug(7116) << "NOOP did not succeed - connection broken" << endl;
1246  completeQueue.removeRef (cmd);
1247  error (ERR_CONNECTION_BROKEN, myHost);
1248  return;
1249  }
1250  completeQueue.removeRef (cmd);
1251  finished();
1252  break;
1253  }
1254  case 'n':
1255  {
1256  // namespace in the form "section=namespace=delimiter"
1257  // entries are separated by ,
1258  infoMessage( imapNamespaces.join(",") );
1259  finished();
1260  break;
1261  }
1262  case 'U':
1263  {
1264  // unsubscribe
1265  KURL _url;
1266  stream >> _url;
1267  TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1268  parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1269  imapCommand *cmd = doCommand(imapCommand::clientUnsubscribe(aBox));
1270  if (cmd->result () != "OK")
1271  {
1272  completeQueue.removeRef (cmd);
1273  error(ERR_SLAVE_DEFINED, i18n("Unsubscribe of folder %1 "
1274  "failed. The server returned: %2")
1275  .arg(_url.prettyURL())
1276  .arg(cmd->resultInfo()));
1277  return;
1278  }
1279  completeQueue.removeRef (cmd);
1280  finished();
1281  break;
1282  }
1283  case 'u':
1284  {
1285  // subscribe
1286  KURL _url;
1287  stream >> _url;
1288  TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1289  parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1290  imapCommand *cmd = doCommand(imapCommand::clientSubscribe(aBox));
1291  if (cmd->result () != "OK")
1292  {
1293  completeQueue.removeRef (cmd);
1294  error(ERR_SLAVE_DEFINED, i18n("Subscribe of folder %1 "
1295  "failed. The server returned: %2")
1296  .arg(_url.prettyURL())
1297  .arg(cmd->resultInfo()));
1298  return;
1299  }
1300  completeQueue.removeRef (cmd);
1301  finished();
1302  break;
1303  }
1304  case 'A':
1305  {
1306  // acl
1307  int cmd;
1308  stream >> cmd;
1309  if ( hasCapability( "ACL" ) ) {
1310  specialACLCommand( cmd, stream );
1311  } else {
1312  error( ERR_UNSUPPORTED_ACTION, "ACL" );
1313  }
1314  break;
1315  }
1316  case 'M':
1317  {
1318  // annotatemore
1319  int cmd;
1320  stream >> cmd;
1321  if ( hasCapability( "ANNOTATEMORE" ) ) {
1322  specialAnnotateMoreCommand( cmd, stream );
1323  } else {
1324  error( ERR_UNSUPPORTED_ACTION, "ANNOTATEMORE" );
1325  }
1326  break;
1327  }
1328  case 'Q':
1329  {
1330  // quota
1331  int cmd;
1332  stream >> cmd;
1333  if ( hasCapability( "QUOTA" ) ) {
1334  specialQuotaCommand( cmd, stream );
1335  } else {
1336  error( ERR_UNSUPPORTED_ACTION, "QUOTA" );
1337  }
1338  break;
1339  }
1340  case 'S':
1341  {
1342  // status
1343  KURL _url;
1344  TQCString newFlags;
1345  stream >> _url >> newFlags;
1346 
1347  TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1348  parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1349  if (!assureBox(aBox, false)) return;
1350 
1351  // make sure we only touch flags we know
1352  TQCString knownFlags = "\\SEEN \\ANSWERED \\FLAGGED \\DRAFT";
1353  const imapInfo info = getSelected();
1354  if ( info.permanentFlagsAvailable() && (info.permanentFlags() & imapInfo::User) ) {
1355  knownFlags += " KMAILFORWARDED KMAILTODO KMAILWATCHED KMAILIGNORED $FORWARDED $TODO $WATCHED $IGNORED";
1356  }
1357 
1358  imapCommand *cmd = doCommand (imapCommand::
1359  clientStore (aSequence, "-FLAGS.SILENT", knownFlags));
1360  if (cmd->result () != "OK")
1361  {
1362  completeQueue.removeRef (cmd);
1363  error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
1364  "failed.").arg(_url.prettyURL()));
1365  return;
1366  }
1367  completeQueue.removeRef (cmd);
1368  if (!newFlags.isEmpty())
1369  {
1370  cmd = doCommand (imapCommand::
1371  clientStore (aSequence, "+FLAGS.SILENT", newFlags));
1372  if (cmd->result () != "OK")
1373  {
1374  completeQueue.removeRef (cmd);
1375  error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
1376  "failed.").arg(_url.prettyURL()));
1377  return;
1378  }
1379  completeQueue.removeRef (cmd);
1380  }
1381  finished();
1382  break;
1383  }
1384  case 's':
1385  {
1386  // seen
1387  KURL _url;
1388  bool seen;
1389  TQCString newFlags;
1390  stream >> _url >> seen;
1391 
1392  TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1393  parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1394  if ( !assureBox(aBox, true) ) // read-only because changing SEEN should be possible even then
1395  return;
1396 
1397  imapCommand *cmd;
1398  if ( seen )
1399  cmd = doCommand( imapCommand::clientStore( aSequence, "+FLAGS.SILENT", "\\SEEN" ) );
1400  else
1401  cmd = doCommand( imapCommand::clientStore( aSequence, "-FLAGS.SILENT", "\\SEEN" ) );
1402 
1403  if (cmd->result () != "OK")
1404  {
1405  completeQueue.removeRef (cmd);
1406  error(ERR_COULD_NOT_WRITE, i18n("Changing the flags of message %1 "
1407  "failed.").arg(_url.prettyURL()));
1408  return;
1409  }
1410  completeQueue.removeRef (cmd);
1411  finished();
1412  break;
1413  }
1414 
1415  case 'E':
1416  {
1417  // search
1418  specialSearchCommand( stream );
1419  break;
1420  }
1421  case 'X':
1422  {
1423  // custom command
1424  specialCustomCommand( stream );
1425  break;
1426  }
1427  default:
1428  kdWarning(7116) << "Unknown command in special(): " << tmp << endl;
1429  error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(tmp)) );
1430  break;
1431  }
1432 }
1433 
1434 void
1435 IMAP4Protocol::specialACLCommand( int command, TQDataStream& stream )
1436 {
1437  // All commands start with the URL to the box
1438  KURL _url;
1439  stream >> _url;
1440  TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1441  parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1442 
1443  switch( command ) {
1444  case 'S': // SETACL
1445  {
1446  TQString user, acl;
1447  stream >> user >> acl;
1448  kdDebug(7116) << "SETACL " << aBox << " " << user << " " << acl << endl;
1449  imapCommand *cmd = doCommand(imapCommand::clientSetACL(aBox, user, acl));
1450  if (cmd->result () != "OK")
1451  {
1452  error(ERR_SLAVE_DEFINED, i18n("Setting the Access Control List on folder %1 "
1453  "for user %2 failed. The server returned: %3")
1454  .arg(_url.prettyURL())
1455  .arg(user)
1456  .arg(cmd->resultInfo()));
1457  return;
1458  }
1459  completeQueue.removeRef (cmd);
1460  finished();
1461  break;
1462  }
1463  case 'D': // DELETEACL
1464  {
1465  TQString user;
1466  stream >> user;
1467  kdDebug(7116) << "DELETEACL " << aBox << " " << user << endl;
1468  imapCommand *cmd = doCommand(imapCommand::clientDeleteACL(aBox, user));
1469  if (cmd->result () != "OK")
1470  {
1471  error(ERR_SLAVE_DEFINED, i18n("Deleting the Access Control List on folder %1 "
1472  "for user %2 failed. The server returned: %3")
1473  .arg(_url.prettyURL())
1474  .arg(user)
1475  .arg(cmd->resultInfo()));
1476  return;
1477  }
1478  completeQueue.removeRef (cmd);
1479  finished();
1480  break;
1481  }
1482  case 'G': // GETACL
1483  {
1484  kdDebug(7116) << "GETACL " << aBox << endl;
1485  imapCommand *cmd = doCommand(imapCommand::clientGetACL(aBox));
1486  if (cmd->result () != "OK")
1487  {
1488  error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
1489  "failed. The server returned: %2")
1490  .arg(_url.prettyURL())
1491  .arg(cmd->resultInfo()));
1492  return;
1493  }
1494  // Returning information to the application from a special() command isn't easy.
1495  // I'm reusing the infoMessage trick seen above (for capabilities), but this
1496  // limits me to a string instead of a stringlist. Using DQUOTE as separator,
1497  // because it's forbidden in userids by rfc3501
1498  kdDebug(7116) << getResults() << endl;
1499  infoMessage(getResults().join( "\"" ));
1500  finished();
1501  break;
1502  }
1503  case 'L': // LISTRIGHTS
1504  {
1505  // Do we need this one? It basically shows which rights are tied together, but that's all?
1506  error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(command)) );
1507  break;
1508  }
1509  case 'M': // MYRIGHTS
1510  {
1511  kdDebug(7116) << "MYRIGHTS " << aBox << endl;
1512  imapCommand *cmd = doCommand(imapCommand::clientMyRights(aBox));
1513  if (cmd->result () != "OK")
1514  {
1515  error(ERR_SLAVE_DEFINED, i18n("Retrieving the Access Control List on folder %1 "
1516  "failed. The server returned: %2")
1517  .arg(_url.prettyURL())
1518  .arg(cmd->resultInfo()));
1519  return;
1520  }
1521  TQStringList lst = getResults();
1522  kdDebug(7116) << "myrights results: " << lst << endl;
1523  if ( !lst.isEmpty() ) {
1524  Q_ASSERT( lst.count() == 1 );
1525  infoMessage( lst.first() );
1526  }
1527  finished();
1528  break;
1529  }
1530  default:
1531  kdWarning(7116) << "Unknown special ACL command:" << command << endl;
1532  error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(command)) );
1533  }
1534 }
1535 
1536 void
1538 {
1539  kdDebug(7116) << "IMAP4Protocol::specialSearchCommand" << endl;
1540  KURL _url;
1541  stream >> _url;
1542  TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1543  parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1544  if (!assureBox(aBox, false)) return;
1545 
1546  imapCommand *cmd = doCommand (imapCommand::clientSearch( aSection ));
1547  if (cmd->result () != "OK")
1548  {
1549  error(ERR_SLAVE_DEFINED, i18n("Searching of folder %1 "
1550  "failed. The server returned: %2")
1551  .arg(aBox)
1552  .arg(cmd->resultInfo()));
1553  return;
1554  }
1555  completeQueue.removeRef(cmd);
1556  TQStringList lst = getResults();
1557  kdDebug(7116) << "IMAP4Protocol::specialSearchCommand '" << aSection <<
1558  "' returns " << lst << endl;
1559  infoMessage( lst.join( " " ) );
1560 
1561  finished();
1562 }
1563 
1564 void
1566 {
1567  kdDebug(7116) << "IMAP4Protocol::specialCustomCommand" << endl;
1568 
1569  TQString command, arguments;
1570  int type;
1571  stream >> type;
1572  stream >> command >> arguments;
1573 
1578  if ( type == 'N' ) {
1579  kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: normal mode" << endl;
1580  imapCommand *cmd = doCommand (imapCommand::clientCustom( command, arguments ));
1581  if (cmd->result () != "OK")
1582  {
1583  error(ERR_SLAVE_DEFINED, i18n("Custom command %1:%2 "
1584  "failed. The server returned: %3")
1585  .arg(command)
1586  .arg(arguments)
1587  .arg(cmd->resultInfo()));
1588  return;
1589  }
1590  completeQueue.removeRef(cmd);
1591  TQStringList lst = getResults();
1592  kdDebug(7116) << "IMAP4Protocol::specialCustomCommand '" << command <<
1593  ":" << arguments <<
1594  "' returns " << lst << endl;
1595  infoMessage( lst.join( " " ) );
1596 
1597  finished();
1598  } else
1603  if ( type == 'E' ) {
1604  kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: extended mode" << endl;
1605  imapCommand *cmd = sendCommand (imapCommand::clientCustom( command, TQString() ));
1606  while ( !parseLoop () ) ;
1607 
1608  // see if server is waiting
1609  if (!cmd->isComplete () && !getContinuation ().isEmpty ())
1610  {
1611  const TQByteArray buffer = arguments.utf8();
1612 
1613  // send data to server
1614  bool sendOk = (write (buffer.data (), buffer.size ()) == (ssize_t)buffer.size ());
1615  processedSize( buffer.size() );
1616 
1617  if ( !sendOk ) {
1618  error ( ERR_CONNECTION_BROKEN, myHost );
1619  completeQueue.removeRef ( cmd );
1620  setState(ISTATE_CONNECT);
1621  closeConnection();
1622  return;
1623  }
1624  }
1625  parseWriteLine ("");
1626 
1627  do
1628  {
1629  while (!parseLoop ()) ;
1630  }
1631  while (!cmd->isComplete ());
1632 
1633  completeQueue.removeRef (cmd);
1634 
1635  TQStringList lst = getResults();
1636  kdDebug(7116) << "IMAP4Protocol::specialCustomCommand: returns " << lst << endl;
1637  infoMessage( lst.join( " " ) );
1638 
1639  finished ();
1640  }
1641 }
1642 
1643 void
1644 IMAP4Protocol::specialAnnotateMoreCommand( int command, TQDataStream& stream )
1645 {
1646  // All commands start with the URL to the box
1647  KURL _url;
1648  stream >> _url;
1649  TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1650  parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1651 
1652  switch( command ) {
1653  case 'S': // SETANNOTATION
1654  {
1655  // Params:
1656  // KURL URL of the mailbox
1657  // TQString entry (should be an actual entry name, no % or *; empty for server entries)
1658  // TQMap<TQString,TQString> attributes (name and value)
1659  TQString entry;
1660  TQMap<TQString, TQString> attributes;
1661  stream >> entry >> attributes;
1662  kdDebug(7116) << "SETANNOTATION " << aBox << " " << entry << " " << attributes.count() << " attributes" << endl;
1663  imapCommand *cmd = doCommand(imapCommand::clientSetAnnotation(aBox, entry, attributes));
1664  if (cmd->result () != "OK")
1665  {
1666  error(ERR_SLAVE_DEFINED, i18n("Setting the annotation %1 on folder %2 "
1667  " failed. The server returned: %3")
1668  .arg(entry)
1669  .arg(_url.prettyURL())
1670  .arg(cmd->resultInfo()));
1671  return;
1672  }
1673  completeQueue.removeRef (cmd);
1674  finished();
1675  break;
1676  }
1677  case 'G': // GETANNOTATION.
1678  {
1679  // Params:
1680  // KURL URL of the mailbox
1681  // TQString entry (should be an actual entry name, no % or *; empty for server entries)
1682  // TQStringList attributes (list of attributes to be retrieved, possibly with % or *)
1683  TQString entry;
1684  TQStringList attributeNames;
1685  stream >> entry >> attributeNames;
1686  kdDebug(7116) << "GETANNOTATION " << aBox << " " << entry << " " << attributeNames << endl;
1687  imapCommand *cmd = doCommand(imapCommand::clientGetAnnotation(aBox, entry, attributeNames));
1688  if (cmd->result () != "OK")
1689  {
1690  error(ERR_SLAVE_DEFINED, i18n("Retrieving the annotation %1 on folder %2 "
1691  "failed. The server returned: %3")
1692  .arg(entry)
1693  .arg(_url.prettyURL())
1694  .arg(cmd->resultInfo()));
1695  return;
1696  }
1697  // Returning information to the application from a special() command isn't easy.
1698  // I'm reusing the infoMessage trick seen above (for capabilities and acls), but this
1699  // limits me to a string instead of a stringlist. Let's use \r as separator.
1700  kdDebug(7116) << getResults() << endl;
1701  infoMessage(getResults().join( "\r" ));
1702  finished();
1703  break;
1704  }
1705  default:
1706  kdWarning(7116) << "Unknown special annotate command:" << command << endl;
1707  error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(command)) );
1708  }
1709 }
1710 
1711 void
1712 IMAP4Protocol::specialQuotaCommand( int command, TQDataStream& stream )
1713 {
1714  // All commands start with the URL to the box
1715  KURL _url;
1716  stream >> _url;
1717  TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1718  parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo);
1719 
1720  switch( command ) {
1721  case 'R': // GEQUOTAROOT
1722  {
1723  kdDebug(7116) << "QUOTAROOT " << aBox << endl;
1724  imapCommand *cmd = doCommand(imapCommand::clientGetQuotaroot( aBox ) );
1725  if (cmd->result () != "OK")
1726  {
1727  error(ERR_SLAVE_DEFINED, i18n("Retrieving the quota root information on folder %1 "
1728  "failed. The server returned: %2")
1729  .arg(_url.prettyURL())
1730  .arg(cmd->resultInfo()));
1731  return;
1732  }
1733  infoMessage(getResults().join( "\r" ));
1734  finished();
1735  break;
1736  }
1737  case 'G': // GEQUOTA
1738  {
1739  kdDebug(7116) << "GEQUOTA command" << endl;
1740  kdWarning(7116) << "UNIMPLEMENTED" << endl;
1741  break;
1742  }
1743  case 'S': // SEQUOTA
1744  {
1745  kdDebug(7116) << "SEQUOTA command" << endl;
1746  kdWarning(7116) << "UNIMPLEMENTED" << endl;
1747  break;
1748  }
1749  default:
1750  kdWarning(7116) << "Unknown special quota command:" << command << endl;
1751  error( ERR_UNSUPPORTED_ACTION, TQString(TQChar(command)) );
1752  }
1753 }
1754 
1755 void
1756 IMAP4Protocol::rename (const KURL & src, const KURL & dest, bool overwrite)
1757 {
1758  kdDebug(7116) << "IMAP4::rename - [" << (overwrite ? "Overwrite" : "NoOverwrite") << "] " << src.prettyURL() << " -> " << dest.prettyURL() << endl;
1759  TQString sBox, sSequence, sLType, sSection, sValidity, sDelimiter, sInfo;
1760  TQString dBox, dSequence, dLType, dSection, dValidity, dDelimiter, dInfo;
1761  enum IMAP_TYPE sType =
1762  parseURL (src, sBox, sSection, sLType, sSequence, sValidity, sDelimiter, sInfo, false);
1763  enum IMAP_TYPE dType =
1764  parseURL (dest, dBox, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo, false);
1765 
1766  if (dType == ITYPE_UNKNOWN)
1767  {
1768  switch (sType)
1769  {
1770  case ITYPE_BOX:
1771  case ITYPE_DIR:
1772  case ITYPE_DIR_AND_BOX:
1773  {
1774  if (getState() == ISTATE_SELECT && sBox == getCurrentBox())
1775  {
1776  kdDebug(7116) << "IMAP4::rename - close " << getCurrentBox() << endl;
1777  // mailbox can only be renamed if it is closed
1778  imapCommand *cmd = doCommand (imapCommand::clientClose());
1779  bool ok = cmd->result() == "OK";
1780  completeQueue.removeRef(cmd);
1781  if (!ok)
1782  {
1783  kdWarning(7116) << "Unable to close mailbox!" << endl;
1784  error(ERR_CANNOT_RENAME, src.path());
1785  return;
1786  }
1787  setState(ISTATE_LOGIN);
1788  }
1789  imapCommand *cmd = doCommand (imapCommand::clientRename (sBox, dBox));
1790  if (cmd->result () != "OK") {
1791  error (ERR_CANNOT_RENAME, src.path());
1792  completeQueue.removeRef (cmd);
1793  return;
1794  }
1795  completeQueue.removeRef (cmd);
1796  }
1797  break;
1798 
1799  case ITYPE_MSG:
1800  case ITYPE_ATTACH:
1801  case ITYPE_UNKNOWN:
1802  error (ERR_CANNOT_RENAME, src.path());
1803  break;
1804  }
1805  }
1806  else
1807  {
1808  error (ERR_CANNOT_RENAME, src.path());
1809  return;
1810  }
1811  finished ();
1812 }
1813 
1814 void
1815 IMAP4Protocol::slave_status ()
1816 {
1817  bool connected = (getState() != ISTATE_NO) && isConnectionValid();
1818  kdDebug(7116) << "IMAP4::slave_status " << connected << endl;
1819  slaveStatus ( connected ? myHost : TQString(), connected );
1820 }
1821 
1822 void
1823 IMAP4Protocol::dispatch (int command, const TQByteArray & data)
1824 {
1825  kdDebug(7116) << "IMAP4::dispatch - command=" << command << endl;
1826  KIO::TCPSlaveBase::dispatch (command, data);
1827 }
1828 
1829 void
1830 IMAP4Protocol::stat (const KURL & _url)
1831 {
1832  kdDebug(7116) << "IMAP4::stat - " << _url.prettyURL() << endl;
1833  TQString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1834  // parseURL with caching
1835  enum IMAP_TYPE aType =
1836  parseURL (_url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter,
1837  aInfo, true);
1838 
1839  UDSEntry entry;
1840  UDSAtom atom;
1841 
1842  atom.m_uds = UDS_NAME;
1843  atom.m_str = aBox;
1844  entry.append (atom);
1845 
1846  if (!aSection.isEmpty())
1847  {
1848  if (getState() == ISTATE_SELECT && aBox == getCurrentBox())
1849  {
1850  imapCommand *cmd = doCommand (imapCommand::clientClose());
1851  bool ok = cmd->result() == "OK";
1852  completeQueue.removeRef(cmd);
1853  if (!ok)
1854  {
1855  error(ERR_COULD_NOT_STAT, aBox);
1856  return;
1857  }
1858  setState(ISTATE_LOGIN);
1859  }
1860  bool ok = false;
1861  TQString cmdInfo;
1862  if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
1863  ok = true;
1864  else
1865  {
1866  imapCommand *cmd = doCommand(imapCommand::clienStatus(aBox, aSection));
1867  ok = cmd->result() == "OK";
1868  cmdInfo = cmd->resultInfo();
1869  completeQueue.removeRef(cmd);
1870  }
1871  if (!ok)
1872  {
1873  bool found = false;
1874  imapCommand *cmd = doCommand (imapCommand::clientList ("", aBox));
1875  if (cmd->result () == "OK")
1876  {
1877  for (TQValueListIterator < imapList > it = listResponses.begin ();
1878  it != listResponses.end (); ++it)
1879  {
1880  if (aBox == (*it).name ()) found = true;
1881  }
1882  }
1883  completeQueue.removeRef (cmd);
1884  if (found)
1885  error(ERR_COULD_NOT_STAT, aBox);
1886  else
1887  error(KIO::ERR_DOES_NOT_EXIST, aBox);
1888  return;
1889  }
1890  if ((aSection == "UIDNEXT" && geStatus().uidNextAvailable())
1891  || (aSection == "UNSEEN" && geStatus().unseenAvailable()))
1892  {
1893  atom.m_uds = UDS_SIZE;
1894  atom.m_str = TQString();
1895  atom.m_long = (aSection == "UIDNEXT") ? geStatus().uidNext()
1896  : geStatus().unseen();
1897  entry.append(atom);
1898  }
1899  } else
1900  if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX || aType == ITYPE_MSG ||
1901  aType == ITYPE_ATTACH)
1902  {
1903  ulong validity = 0;
1904  // see if the box is already in select/examine state
1905  if (aBox == getCurrentBox ())
1906  validity = selectInfo.uidValidity ();
1907  else
1908  {
1909  // do a status lookup on the box
1910  // only do this if the box is not selected
1911  // the server might change the validity for new select/examine
1912  imapCommand *cmd =
1913  doCommand (imapCommand::clienStatus (aBox, "UIDVALIDITY"));
1914  completeQueue.removeRef (cmd);
1915  validity = geStatus ().uidValidity ();
1916  }
1917  validity = 0; // temporary
1918 
1919  if (aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX)
1920  {
1921  // has no or an invalid uidvalidity
1922  if (validity > 0 && validity != aValidity.toULong ())
1923  {
1924  //redirect
1925  KURL newUrl = _url;
1926 
1927  newUrl.setPath ("/" + aBox + ";UIDVALIDITY=" +
1928  TQString::number(validity));
1929  kdDebug(7116) << "IMAP4::stat - redirecting to " << newUrl.prettyURL() << endl;
1930  redirection (newUrl);
1931  }
1932  }
1933  else if (aType == ITYPE_MSG || aType == ITYPE_ATTACH)
1934  {
1935  //must determine if this message exists
1936  //cause konqueror will check this on paste operations
1937 
1938  // has an invalid uidvalidity
1939  // or no messages in box
1940  if (validity > 0 && validity != aValidity.toULong ())
1941  {
1942  aType = ITYPE_UNKNOWN;
1943  kdDebug(7116) << "IMAP4::stat - url has invalid validity [" << validity << "d] " << _url.prettyURL() << endl;
1944  }
1945  }
1946  }
1947 
1948  atom.m_uds = UDS_MIME_TYPE;
1949  atom.m_str = getMimeType (aType);
1950  entry.append (atom);
1951 
1952  kdDebug(7116) << "IMAP4: stat: " << atom.m_str << endl;
1953  switch (aType)
1954  {
1955  case ITYPE_DIR:
1956  atom.m_uds = UDS_FILE_TYPE;
1957  atom.m_str = TQString();
1958  atom.m_long = S_IFDIR;
1959  entry.append (atom);
1960  break;
1961 
1962  case ITYPE_BOX:
1963  case ITYPE_DIR_AND_BOX:
1964  atom.m_uds = UDS_FILE_TYPE;
1965  atom.m_str = TQString();
1966  atom.m_long = S_IFDIR;
1967  entry.append (atom);
1968  break;
1969 
1970  case ITYPE_MSG:
1971  case ITYPE_ATTACH:
1972  atom.m_uds = UDS_FILE_TYPE;
1973  atom.m_str = TQString();
1974  atom.m_long = S_IFREG;
1975  entry.append (atom);
1976  break;
1977 
1978  case ITYPE_UNKNOWN:
1979  error (ERR_DOES_NOT_EXIST, _url.prettyURL());
1980  break;
1981  }
1982 
1983  statEntry (entry);
1984  kdDebug(7116) << "IMAP4::stat - Finishing stat" << endl;
1985  finished ();
1986 }
1987 
1988 void IMAP4Protocol::openConnection()
1989 {
1990  if (makeLogin()) connected();
1991 }
1992 
1993 void IMAP4Protocol::closeConnection()
1994 {
1995  if (getState() == ISTATE_NO) return;
1996  if (getState() == ISTATE_SELECT && metaData("expunge") == "auto")
1997  {
1998  imapCommand *cmd = doCommand (imapCommand::clientExpunge());
1999  completeQueue.removeRef (cmd);
2000  }
2001  if (getState() != ISTATE_CONNECT)
2002  {
2003  imapCommand *cmd = doCommand (imapCommand::clientLogout());
2004  completeQueue.removeRef (cmd);
2005  }
2006  closeDescriptor();
2007  setState(ISTATE_NO);
2008  completeQueue.clear();
2009  sentQueue.clear();
2010  lastHandled = 0;
2011  currentBox = TQString();
2012  readBufferLen = 0;
2013 }
2014 
2015 bool IMAP4Protocol::makeLogin ()
2016 {
2017  if (getState () == ISTATE_LOGIN || getState () == ISTATE_SELECT)
2018  return true;
2019 
2020  kdDebug(7116) << "IMAP4::makeLogin - checking login" << endl;
2021  bool alreadyConnected = getState() == ISTATE_CONNECT;
2022  kdDebug(7116) << "IMAP4::makeLogin - alreadyConnected " << alreadyConnected << endl;
2023  if (alreadyConnected || connectToHost (myHost.latin1(), myPort))
2024  {
2025 // fcntl (m_iSock, F_SETFL, (fcntl (m_iSock, F_GETFL) | O_NDELAY));
2026 
2027  setState(ISTATE_CONNECT);
2028 
2029  myAuth = metaData("auth");
2030  myTLS = metaData("tls");
2031  kdDebug(7116) << "myAuth: " << myAuth << endl;
2032 
2033  imapCommand *cmd;
2034 
2035  unhandled.clear ();
2036  if (!alreadyConnected) while (!parseLoop ()) ; //get greeting
2037  TQString greeting;
2038  if (!unhandled.isEmpty()) greeting = unhandled.first().stripWhiteSpace();
2039  unhandled.clear (); //get rid of it
2040  cmd = doCommand (new imapCommand ("CAPABILITY", ""));
2041 
2042  kdDebug(7116) << "IMAP4: setHost: capability" << endl;
2043  for (TQStringList::Iterator it = imapCapabilities.begin ();
2044  it != imapCapabilities.end (); ++it)
2045  {
2046  kdDebug(7116) << "'" << (*it) << "'" << endl;
2047  }
2048  completeQueue.removeRef (cmd);
2049 
2050  if (!hasCapability("IMAP4") && !hasCapability("IMAP4rev1"))
2051  {
2052  error(ERR_COULD_NOT_LOGIN, i18n("The server %1 supports neither "
2053  "IMAP4 nor IMAP4rev1.\nIt identified itself with: %2")
2054  .arg(myHost).arg(greeting));
2055  closeConnection();
2056  return false;
2057  }
2058 
2059  if (metaData("nologin") == "on") return TRUE;
2060 
2061  if (myTLS == "on" && !hasCapability(TQString("STARTTLS")))
2062  {
2063  error(ERR_COULD_NOT_LOGIN, i18n("The server does not support TLS.\n"
2064  "Disable this security feature to connect unencrypted."));
2065  closeConnection();
2066  return false;
2067  }
2068  if ((myTLS == "on" || (canUseTLS() && myTLS != "off")) &&
2069  hasCapability(TQString("STARTTLS")))
2070  {
2071  imapCommand *cmd = doCommand (imapCommand::clientStartTLS());
2072  if (cmd->result () == "OK")
2073  {
2074  completeQueue.removeRef(cmd);
2075  int tlsrc = startTLS();
2076  if (tlsrc == 1)
2077  {
2078  kdDebug(7116) << "TLS mode has been enabled." << endl;
2079  imapCommand *cmd2 = doCommand (new imapCommand ("CAPABILITY", ""));
2080  for (TQStringList::Iterator it = imapCapabilities.begin ();
2081  it != imapCapabilities.end (); ++it)
2082  {
2083  kdDebug(7116) << "'" << (*it) << "'" << endl;
2084  }
2085  completeQueue.removeRef (cmd2);
2086  } else {
2087  kdWarning(7116) << "TLS mode setup has failed. Aborting." << endl;
2088  error (ERR_COULD_NOT_LOGIN, i18n("Starting TLS failed."));
2089  closeConnection();
2090  return false;
2091  }
2092  } else completeQueue.removeRef(cmd);
2093  }
2094 
2095  if (myAuth.isEmpty () || myAuth == "*") {
2096  if (hasCapability (TQString ("LOGINDISABLED"))) {
2097  error (ERR_COULD_NOT_LOGIN, i18n("LOGIN is disabled by the server."));
2098  closeConnection();
2099  return false;
2100  }
2101  }
2102  else {
2103  if (!hasCapability (TQString ("AUTH=") + myAuth)) {
2104  error (ERR_COULD_NOT_LOGIN, i18n("The authentication method %1 is not "
2105  "supported by the server.").arg(myAuth));
2106  closeConnection();
2107  return false;
2108  }
2109  }
2110 
2111  if ( greeting.contains( TQRegExp( "Cyrus IMAP4 v2.1" ) ) ) {
2112  removeCapability( "ANNOTATEMORE" );
2113  }
2114 
2115  // starting from Cyrus IMAP 2.3.9, shared seen flags are available
2116  TQRegExp regExp( "Cyrus\\sIMAP[4]{0,1}\\sv(\\d+)\\.(\\d+)\\.(\\d+)", false );
2117  if ( regExp.search( greeting ) >= 0 ) {
2118  const int major = regExp.cap( 1 ).toInt();
2119  const int minor = regExp.cap( 2 ).toInt();
2120  const int patch = regExp.cap( 3 ).toInt();
2121  if ( major > 2 || (major == 2 && (minor > 3 || (minor == 3 && patch > 9))) ) {
2122  kdDebug(7116) << k_funcinfo << "Cyrus IMAP >= 2.3.9 detected, enabling shared seen flag support" << endl;
2123  imapCapabilities.append( "x-kmail-sharedseen" );
2124  }
2125  }
2126 
2127  kdDebug(7116) << "IMAP4::makeLogin - attempting login" << endl;
2128 
2129  KIO::AuthInfo authInfo;
2130  authInfo.username = myUser;
2131  authInfo.password = myPass;
2132  authInfo.prompt = i18n ("Username and password for your IMAP account:");
2133 
2134  kdDebug(7116) << "IMAP4::makeLogin - open_PassDlg said user=" << myUser << " pass=xx" << endl;
2135 
2136  TQString resultInfo;
2137  if (myAuth.isEmpty () || myAuth == "*")
2138  {
2139  if (myUser.isEmpty () || myPass.isEmpty ()) {
2140  if(openPassDlg (authInfo)) {
2141  myUser = authInfo.username;
2142  myPass = authInfo.password;
2143  }
2144  }
2145  if (!clientLogin (myUser, myPass, resultInfo))
2146  error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to login. Probably the "
2147  "password is wrong.\nThe server %1 replied:\n%2").arg(myHost).arg(resultInfo));
2148  }
2149  else
2150  {
2151 #ifdef HAVE_LIBSASL2
2152  if (!clientAuthenticate (this, authInfo, myHost, myAuth, mySSL, resultInfo))
2153  error(KIO::ERR_COULD_NOT_AUTHENTICATE, i18n("Unable to authenticate via %1.\n"
2154  "The server %2 replied:\n%3").arg(myAuth).arg(myHost).arg(resultInfo));
2155  else {
2156  myUser = authInfo.username;
2157  myPass = authInfo.password;
2158  }
2159 #else
2160  error(KIO::ERR_COULD_NOT_LOGIN, i18n("SASL authentication is not compiled into kio_imap4."));
2161 #endif
2162  }
2163  if ( hasCapability("NAMESPACE") )
2164  {
2165  // get all namespaces and save the namespace - delimiter association
2166  cmd = doCommand( imapCommand::clientNamespace() );
2167  if (cmd->result () == "OK")
2168  {
2169  kdDebug(7116) << "makeLogin - registered namespaces" << endl;
2170  }
2171  completeQueue.removeRef (cmd);
2172  }
2173  // get the default delimiter (empty listing)
2174  cmd = doCommand( imapCommand::clientList("", "") );
2175  if (cmd->result () == "OK")
2176  {
2177  TQValueListIterator < imapList > it = listResponses.begin();
2178  if ( it == listResponses.end() )
2179  {
2180  // empty answer - this is a buggy imap server
2181  // as a fallback we fire a normal listing and take the first answer
2182  completeQueue.removeRef (cmd);
2183  cmd = doCommand( imapCommand::clientList("", "%") );
2184  if (cmd->result () == "OK")
2185  {
2186  it = listResponses.begin();
2187  }
2188  }
2189  if ( it != listResponses.end() )
2190  {
2191  namespaceToDelimiter[TQString()] = (*it).hierarchyDelimiter();
2192  kdDebug(7116) << "makeLogin - delimiter for empty ns='" <<
2193  (*it).hierarchyDelimiter() << "'" << endl;
2194  if ( !hasCapability("NAMESPACE") )
2195  {
2196  // server does not support namespaces
2197  TQString nsentry = TQString::number( 0 ) + "=="
2198  + (*it).hierarchyDelimiter();
2199  imapNamespaces.append( nsentry );
2200  }
2201  }
2202  }
2203  completeQueue.removeRef (cmd);
2204  } else {
2205  kdDebug(7116) << "makeLogin - NO login" << endl;
2206  }
2207 
2208  return getState() == ISTATE_LOGIN;
2209 }
2210 
2211 void
2212 IMAP4Protocol::parseWriteLine (const TQString & aStr)
2213 {
2214  //kdDebug(7116) << "Writing: " << aStr << endl;
2215  TQCString writer = aStr.utf8();
2216  int len = writer.length();
2217 
2218  // append CRLF if necessary
2219  if (len == 0 || (writer[len - 1] != '\n')) {
2220  len += 2;
2221  writer += "\r\n";
2222  }
2223 
2224  // write it
2225  write(writer.data(), len);
2226 }
2227 
2228 TQString
2229 IMAP4Protocol::getMimeType (enum IMAP_TYPE aType)
2230 {
2231  switch (aType)
2232  {
2233  case ITYPE_DIR:
2234  return "inode/directory";
2235  break;
2236 
2237  case ITYPE_BOX:
2238  return "message/digest";
2239  break;
2240 
2241  case ITYPE_DIR_AND_BOX:
2242  return "message/directory";
2243  break;
2244 
2245  case ITYPE_MSG:
2246  return "message/rfc822";
2247  break;
2248 
2249  // this should be handled by flushOutput
2250  case ITYPE_ATTACH:
2251  return "application/octet-stream";
2252  break;
2253 
2254  case ITYPE_UNKNOWN:
2255  default:
2256  return "unknown/unknown";
2257  }
2258 }
2259 
2260 
2261 
2262 void
2263 IMAP4Protocol::doListEntry (const KURL & _url, int stretch, imapCache * cache,
2264  bool withFlags, bool withSubject)
2265 {
2266  KURL aURL = _url;
2267  aURL.setQuery (TQString());
2268  const TQString encodedUrl = aURL.url(0, 106); // utf-8
2269  doListEntry(encodedUrl, stretch, cache, withFlags, withSubject);
2270 }
2271 
2272 
2273 
2274 void
2275 IMAP4Protocol::doListEntry (const TQString & encodedUrl, int stretch, imapCache * cache,
2276  bool withFlags, bool withSubject)
2277 {
2278  if (cache)
2279  {
2280  UDSEntry entry;
2281  UDSAtom atom;
2282 
2283  entry.clear ();
2284 
2285  const TQString uid = TQString::number(cache->getUid());
2286 
2287  atom.m_uds = UDS_NAME;
2288  atom.m_str = uid;
2289  atom.m_long = 0;
2290  if (stretch > 0)
2291  {
2292  atom.m_str = "0000000000000000" + atom.m_str;
2293  atom.m_str = atom.m_str.right (stretch);
2294  }
2295  if (withSubject)
2296  {
2297  mailHeader *header = cache->getHeader();
2298  if (header)
2299  atom.m_str += " " + header->getSubject();
2300  }
2301  entry.append (atom);
2302 
2303  atom.m_uds = UDS_URL;
2304  atom.m_str = encodedUrl; // utf-8
2305  if (atom.m_str[atom.m_str.length () - 1] != '/')
2306  atom.m_str += '/';
2307  atom.m_str += ";UID=" + uid;
2308  atom.m_long = 0;
2309  entry.append (atom);
2310 
2311  atom.m_uds = UDS_FILE_TYPE;
2312  atom.m_str = TQString();
2313  atom.m_long = S_IFREG;
2314  entry.append (atom);
2315 
2316  atom.m_uds = UDS_SIZE;
2317  atom.m_long = cache->getSize();
2318  entry.append (atom);
2319 
2320  atom.m_uds = UDS_MIME_TYPE;
2321  atom.m_str = "message/rfc822";
2322  atom.m_long = 0;
2323  entry.append (atom);
2324 
2325  atom.m_uds = UDS_USER;
2326  atom.m_str = myUser;
2327  entry.append (atom);
2328 
2329  atom.m_uds = KIO::UDS_ACCESS;
2330  atom.m_long = (withFlags) ? cache->getFlags() : S_IRUSR | S_IXUSR | S_IWUSR;
2331  entry.append (atom);
2332 
2333  listEntry (entry, false);
2334  }
2335 }
2336 
2337 void
2338 IMAP4Protocol::doListEntry (const KURL & _url, const TQString & myBox,
2339  const imapList & item, bool appendPath)
2340 {
2341  KURL aURL = _url;
2342  aURL.setQuery (TQString());
2343  UDSEntry entry;
2344  UDSAtom atom;
2345  int hdLen = item.hierarchyDelimiter().length();
2346 
2347  {
2348  // mailboxName will be appended to the path if appendPath is true
2349  TQString mailboxName = item.name ();
2350 
2351  // some beautification
2352  if (mailboxName.find (myBox) == 0 && mailboxName.length() > myBox.length())
2353  {
2354  mailboxName =
2355  mailboxName.right (mailboxName.length () - myBox.length ());
2356  }
2357  if (mailboxName[0] == '/')
2358  mailboxName = mailboxName.right (mailboxName.length () - 1);
2359  if (mailboxName.left(hdLen) == item.hierarchyDelimiter())
2360  mailboxName = mailboxName.right(mailboxName.length () - hdLen);
2361  if (mailboxName.right(hdLen) == item.hierarchyDelimiter())
2362  mailboxName.truncate(mailboxName.length () - hdLen);
2363 
2364  atom.m_uds = UDS_NAME;
2365  if (!item.hierarchyDelimiter().isEmpty() &&
2366  mailboxName.find(item.hierarchyDelimiter()) != -1)
2367  atom.m_str = mailboxName.section(item.hierarchyDelimiter(), -1);
2368  else
2369  atom.m_str = mailboxName;
2370 
2371  // konqueror will die with an assertion failure otherwise
2372  if (atom.m_str.isEmpty ())
2373  atom.m_str = "..";
2374 
2375  if (!atom.m_str.isEmpty ())
2376  {
2377  atom.m_long = 0;
2378  entry.append (atom);
2379 
2380  if (!item.noSelect ())
2381  {
2382  atom.m_uds = UDS_MIME_TYPE;
2383  if (!item.noInferiors ())
2384  {
2385  atom.m_str = "message/directory";
2386  } else {
2387  atom.m_str = "message/digest";
2388  }
2389  atom.m_long = 0;
2390  entry.append (atom);
2391  mailboxName += '/';
2392 
2393  // explicitly set this as a directory for KFileDialog
2394  atom.m_uds = UDS_FILE_TYPE;
2395  atom.m_str = TQString();
2396  atom.m_long = S_IFDIR;
2397  entry.append (atom);
2398  }
2399  else if (!item.noInferiors ())
2400  {
2401  atom.m_uds = UDS_MIME_TYPE;
2402  atom.m_str = "inode/directory";
2403  atom.m_long = 0;
2404  entry.append (atom);
2405  mailboxName += '/';
2406 
2407  // explicitly set this as a directory for KFileDialog
2408  atom.m_uds = UDS_FILE_TYPE;
2409  atom.m_str = TQString();
2410  atom.m_long = S_IFDIR;
2411  entry.append (atom);
2412  }
2413  else
2414  {
2415  atom.m_uds = UDS_MIME_TYPE;
2416  atom.m_str = "unknown/unknown";
2417  atom.m_long = 0;
2418  entry.append (atom);
2419  }
2420 
2421  atom.m_uds = UDS_URL;
2422  TQString path = aURL.path();
2423  atom.m_str = aURL.url (0, 106); // utf-8
2424  if (appendPath)
2425  {
2426  if (path[path.length() - 1] == '/' && !path.isEmpty() && path != "/")
2427  path.truncate(path.length() - 1);
2428  if (!path.isEmpty() && path != "/"
2429  && path.right(hdLen) != item.hierarchyDelimiter()) {
2430  path += item.hierarchyDelimiter();
2431  }
2432  path += mailboxName;
2433  if (path.upper() == "/INBOX/") {
2434  // make sure the client can rely on INBOX
2435  path = path.upper();
2436  }
2437  }
2438  aURL.setPath(path);
2439  atom.m_str = aURL.url(0, 106); // utf-8
2440  atom.m_long = 0;
2441  entry.append (atom);
2442 
2443  atom.m_uds = UDS_USER;
2444  atom.m_str = myUser;
2445  entry.append (atom);
2446 
2447  atom.m_uds = UDS_ACCESS;
2448  atom.m_long = S_IRUSR | S_IXUSR | S_IWUSR;
2449  entry.append (atom);
2450 
2451  atom.m_uds = UDS_EXTRA;
2452  atom.m_str = item.attributesAsString();
2453  atom.m_long = 0;
2454  entry.append (atom);
2455 
2456  listEntry (entry, false);
2457  }
2458  }
2459 }
2460 
2461 enum IMAP_TYPE
2462 IMAP4Protocol::parseURL (const KURL & _url, TQString & _box,
2463  TQString & _section, TQString & _type, TQString & _uid,
2464  TQString & _validity, TQString & _hierarchyDelimiter,
2465  TQString & _info, bool cache)
2466 {
2467  enum IMAP_TYPE retVal;
2468  retVal = ITYPE_UNKNOWN;
2469 
2470  imapParser::parseURL (_url, _box, _section, _type, _uid, _validity, _info);
2471 // kdDebug(7116) << "URL: query - '" << KURL::decode_string(_url.query()) << "'" << endl;
2472 
2473  // get the delimiter
2474  TQString myNamespace = namespaceForBox( _box );
2475  kdDebug(7116) << "IMAP4::parseURL - namespace=" << myNamespace << endl;
2476  if ( namespaceToDelimiter.contains(myNamespace) )
2477  {
2478  _hierarchyDelimiter = namespaceToDelimiter[myNamespace];
2479  kdDebug(7116) << "IMAP4::parseURL - delimiter=" << _hierarchyDelimiter << endl;
2480  }
2481 
2482  if (!_box.isEmpty ())
2483  {
2484  kdDebug(7116) << "IMAP4::parseURL - box=" << _box << endl;
2485 
2486  if (makeLogin ())
2487  {
2488  if (getCurrentBox () != _box ||
2489  _type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK")
2490  {
2491  if ( cache )
2492  {
2493  // assume a normal box
2494  retVal = ITYPE_DIR_AND_BOX;
2495  } else
2496  {
2497  // start a listing for the box to get the type
2498  imapCommand *cmd;
2499 
2500  cmd = doCommand (imapCommand::clientList ("", _box));
2501  if (cmd->result () == "OK")
2502  {
2503  for (TQValueListIterator < imapList > it = listResponses.begin ();
2504  it != listResponses.end (); ++it)
2505  {
2506  //kdDebug(7116) << "IMAP4::parseURL - checking " << _box << " to " << (*it).name() << endl;
2507  if (_box == (*it).name ())
2508  {
2509  if ( !(*it).hierarchyDelimiter().isEmpty() )
2510  _hierarchyDelimiter = (*it).hierarchyDelimiter();
2511  if ((*it).noSelect ())
2512  {
2513  retVal = ITYPE_DIR;
2514  }
2515  else if ((*it).noInferiors ())
2516  {
2517  retVal = ITYPE_BOX;
2518  }
2519  else
2520  {
2521  retVal = ITYPE_DIR_AND_BOX;
2522  }
2523  }
2524  }
2525  // if we got no list response for the box see if it's a prefix
2526  if ( retVal == ITYPE_UNKNOWN &&
2527  namespaceToDelimiter.contains(_box) ) {
2528  retVal = ITYPE_DIR;
2529  }
2530  } else {
2531  kdDebug(7116) << "IMAP4::parseURL - got error for " << _box << endl;
2532  }
2533  completeQueue.removeRef (cmd);
2534  } // cache
2535  }
2536  else // current == box
2537  {
2538  retVal = ITYPE_BOX;
2539  }
2540  }
2541  else
2542  kdDebug(7116) << "IMAP4::parseURL: no login!" << endl;
2543 
2544  }
2545  else // empty box
2546  {
2547  // the root is just a dir
2548  kdDebug(7116) << "IMAP4: parseURL: box [root]" << endl;
2549  retVal = ITYPE_DIR;
2550  }
2551 
2552  // see if it is a real sequence or a simple uid
2553  if (retVal == ITYPE_BOX || retVal == ITYPE_DIR_AND_BOX)
2554  {
2555  if (!_uid.isEmpty ())
2556  {
2557  if (_uid.find (':') == -1 && _uid.find (',') == -1
2558  && _uid.find ('*') == -1)
2559  retVal = ITYPE_MSG;
2560  }
2561  }
2562  if (retVal == ITYPE_MSG)
2563  {
2564  if ( (_section.find ("BODY.PEEK[", 0, false) != -1 ||
2565  _section.find ("BODY[", 0, false) != -1) &&
2566  _section.find(".MIME") == -1 &&
2567  _section.find(".HEADER") == -1 )
2568  retVal = ITYPE_ATTACH;
2569  }
2570  if ( _hierarchyDelimiter.isEmpty() &&
2571  (_type == "LIST" || _type == "LSUB" || _type == "LSUBNOCHECK") )
2572  {
2573  // this shouldn't happen but when the delimiter is really empty
2574  // we try to reconstruct it from the URL
2575  if (!_box.isEmpty())
2576  {
2577  int start = _url.path().findRev(_box);
2578  if (start != -1)
2579  _hierarchyDelimiter = _url.path().mid(start-1, start);
2580  kdDebug(7116) << "IMAP4::parseURL - reconstructed delimiter:" << _hierarchyDelimiter
2581  << " from URL " << _url.path() << endl;
2582  }
2583  if (_hierarchyDelimiter.isEmpty())
2584  _hierarchyDelimiter = "/";
2585  }
2586  kdDebug(7116) << "IMAP4::parseURL - return " << retVal << endl;
2587 
2588  return retVal;
2589 }
2590 
2591 int
2592 IMAP4Protocol::outputLine (const TQCString & _str, int len)
2593 {
2594  if (len == -1) {
2595  len = _str.length();
2596  }
2597 
2598  if (cacheOutput)
2599  {
2600  if ( !outputBuffer.isOpen() ) {
2601  outputBuffer.open(IO_WriteOnly);
2602  }
2603  outputBuffer.at(outputBufferIndex);
2604  outputBuffer.writeBlock(_str.data(), len);
2605  outputBufferIndex += len;
2606  return 0;
2607  }
2608 
2609  TQByteArray temp;
2610  bool relay = relayEnabled;
2611 
2612  relayEnabled = true;
2613  temp.setRawData (_str.data (), len);
2614  parseRelay (temp);
2615  temp.resetRawData (_str.data (), len);
2616 
2617  relayEnabled = relay;
2618  return 0;
2619 }
2620 
2621 void IMAP4Protocol::flushOutput(TQString contentEncoding)
2622 {
2623  // send out cached data to the application
2624  if (outputBufferIndex == 0)
2625  return;
2626  outputBuffer.close();
2627  outputCache.resize(outputBufferIndex);
2628  if (decodeContent)
2629  {
2630  // get the coding from the MIME header
2631  TQByteArray decoded;
2632  if (contentEncoding.find("quoted-printable", 0, false) == 0)
2633  decoded = KCodecs::quotedPrintableDecode(outputCache);
2634  else if (contentEncoding.find("base64", 0, false) == 0)
2635  KCodecs::base64Decode(outputCache, decoded);
2636  else
2637  decoded = outputCache;
2638 
2639  TQString mimetype = KMimeType::findByContent( decoded )->name();
2640  kdDebug(7116) << "IMAP4::flushOutput - mimeType " << mimetype << endl;
2641  mimeType(mimetype);
2642  decodeContent = false;
2643  data( decoded );
2644  } else {
2645  data( outputCache );
2646  }
2647  mProcessedSize += outputBufferIndex;
2648  processedSize( mProcessedSize );
2649  outputBufferIndex = 0;
2650  outputCache[0] = '\0';
2651  outputBuffer.setBuffer(outputCache);
2652 }
2653 
2654 ssize_t IMAP4Protocol::myRead(void *data, ssize_t len)
2655 {
2656  if (readBufferLen)
2657  {
2658  ssize_t copyLen = (len < readBufferLen) ? len : readBufferLen;
2659  memcpy(data, readBuffer, copyLen);
2660  readBufferLen -= copyLen;
2661  if (readBufferLen) memmove(readBuffer, &readBuffer[copyLen], readBufferLen);
2662  return copyLen;
2663  }
2664  if (!isConnectionValid()) return 0;
2665  waitForResponse( responseTimeout() );
2666  return read(data, len);
2667 }
2668 
2669 bool
2670 IMAP4Protocol::assureBox (const TQString & aBox, bool readonly)
2671 {
2672  if (aBox.isEmpty()) return false;
2673 
2674  imapCommand *cmd = 0;
2675 
2676  if (aBox != getCurrentBox () || (!getSelected().readWrite() && !readonly))
2677  {
2678  // open the box with the appropriate mode
2679  kdDebug(7116) << "IMAP4Protocol::assureBox - opening box" << endl;
2680  selectInfo = imapInfo();
2681  cmd = doCommand (imapCommand::clientSelect (aBox, readonly));
2682  bool ok = cmd->result() == "OK";
2683  TQString cmdInfo = cmd->resultInfo();
2684  completeQueue.removeRef (cmd);
2685 
2686  if (!ok)
2687  {
2688  bool found = false;
2689  cmd = doCommand (imapCommand::clientList ("", aBox));
2690  if (cmd->result () == "OK")
2691  {
2692  for (TQValueListIterator < imapList > it = listResponses.begin ();
2693  it != listResponses.end (); ++it)
2694  {
2695  if (aBox == (*it).name ()) found = true;
2696  }
2697  }
2698  completeQueue.removeRef (cmd);
2699  if (found) {
2700  if (cmdInfo.find("permission", 0, false) != -1) {
2701  // not allowed to enter this folder
2702  error(ERR_ACCESS_DENIED, cmdInfo);
2703  } else {
2704  error(ERR_SLAVE_DEFINED, i18n("Unable to open folder %1. The server replied: %2").arg(aBox).arg(cmdInfo));
2705  }
2706  } else {
2707  error(KIO::ERR_DOES_NOT_EXIST, aBox);
2708  }
2709  return false;
2710  }
2711  }
2712  else
2713  {
2714  // Give the server a chance to deliver updates every ten seconds.
2715  // Doing this means a server roundtrip and since assureBox is called
2716  // after every mail, we do it with a timeout.
2717  kdDebug(7116) << "IMAP4Protocol::assureBox - reusing box" << endl;
2718  if ( mTimeOfLastNoop.secsTo( TQDateTime::currentDateTime() ) > 10 ) {
2719  cmd = doCommand (imapCommand::clientNoop ());
2720  completeQueue.removeRef (cmd);
2721  mTimeOfLastNoop = TQDateTime::currentDateTime();
2722  kdDebug(7116) << "IMAP4Protocol::assureBox - noop timer fired" << endl;
2723  }
2724  }
2725 
2726  // if it is the mode we want
2727  if (!getSelected().readWrite() && !readonly)
2728  {
2729  error(KIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox);
2730  return false;
2731  }
2732 
2733  return true;
2734 }