tdeioslave/imap4

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