• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

kioslave/imap4

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

KDE's Doxygen guidelines are available online.

kioslave/imap4

Skip menu "kioslave/imap4"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal