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

kioslave/imap4

  • 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
72extern "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"
95const int ImapPort = 143;
96const int ImapsPort = 993;
97
98using namespace KIO;
99
100extern "C"
101{
102 void sigalrm_handler( int );
103 KDE_EXPORT int kdemain( int argc, char **argv );
104}
105
106int
107kdemain (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
139void
140sigchld_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
162IMAP4Protocol::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
179IMAP4Protocol::~IMAP4Protocol ()
180{
181 disconnectFromHost();
182 kDebug( 7116 ) << "IMAP4: Finishing";
183}
184
185void
186IMAP4Protocol::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
399void
400IMAP4Protocol::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
621void
622IMAP4Protocol::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
642void
643IMAP4Protocol::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
661void
662IMAP4Protocol::parseRelay (ulong len)
663{
664 if ( relayEnabled ) {
665 totalSize( len );
666 }
667}
668
669bool 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
702bool 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
768void
769IMAP4Protocol::setSubURL (const KUrl & _url)
770{
771 kDebug( 7116 ) << "IMAP4::setSubURL -" << _url.prettyUrl();
772 KIO::TCPSlaveBase::setSubUrl( _url );
773}
774
775void
776IMAP4Protocol::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
895void
896IMAP4Protocol::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
941void
942IMAP4Protocol::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
1037void
1038IMAP4Protocol::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 */
1176void
1177IMAP4Protocol::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
1396void
1397IMAP4Protocol::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
1488void
1489IMAP4Protocol::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
1516void
1517IMAP4Protocol::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
1590void
1591IMAP4Protocol::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
1652void
1653IMAP4Protocol::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
1694void
1695IMAP4Protocol::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
1746void
1747IMAP4Protocol::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
1754void
1755IMAP4Protocol::dispatch (int command, const QByteArray & data)
1756{
1757 kDebug( 7116 ) << "IMAP4::dispatch - command=" << command;
1758 KIO::TCPSlaveBase::dispatch( command, data );
1759}
1760
1761void
1762IMAP4Protocol::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
1893void IMAP4Protocol::openConnection()
1894{
1895 if ( makeLogin() ) {
1896 connected();
1897 }
1898}
1899
1900void 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
1922bool 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
2090void
2091IMAP4Protocol::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
2107QString
2108IMAP4Protocol::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
2138void
2139IMAP4Protocol::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
2148void
2149IMAP4Protocol::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
2192void
2193IMAP4Protocol::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
2288enum IMAP_TYPE
2289IMAP4Protocol::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
2400int
2401IMAP4Protocol::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
2429void 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
2463ssize_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
2481bool
2482IMAP4Protocol::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}
IMAP4Protocol
IOSlave derived class.
Definition imap4.h:55
IMAP4Protocol::special
virtual void special(const QByteArray &data)
Capabilities, NOOP, (Un)subscribe, Change status, Change ACL.
Definition imap4.cpp:1177
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
IMAP4Protocol::del
virtual void del(const KUrl &_url, bool isFile)
delete a mailbox
Definition imap4.cpp:1038
IMAP4Protocol::mkdir
virtual void mkdir(const KUrl &url, int permissions)
create a mailbox
Definition imap4.cpp:896
IMAP4Protocol::parseReadLine
virtual bool parseReadLine(QByteArray &buffer, long relay=0)
reimplement the parser
Definition imap4.cpp:702
IMAP4Protocol::specialSearchCommand
void specialSearchCommand(QDataStream &)
Search current folder, the search string is passed as SECTION.
Definition imap4.cpp:1489
IMAP4Protocol::stat
virtual void stat(const KUrl &_url)
stat a mailbox, message, attachment
Definition imap4.cpp:1762
IMAP4Protocol::specialAnnotateMoreCommand
void specialAnnotateMoreCommand(int command, QDataStream &stream)
Send an annotation command which is identified by command.
Definition imap4.cpp:1591
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::parseWriteLine
virtual void parseWriteLine(const QString &)
reimplement the parser
Definition imap4.cpp:2091
IMAP4Protocol::specialACLCommand
void specialACLCommand(int command, QDataStream &stream)
Send an ACL command which is identified by command.
Definition imap4.cpp:1397
IMAP4Protocol::listDir
virtual void listDir(const KUrl &_url)
list a directory/mailbox
Definition imap4.cpp:400
IMAP4Protocol::flushOutput
virtual void flushOutput(const QString &contentEncoding=QString())
send out cached data to the application
Definition imap4.cpp:2429
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
IMAP4Protocol::outputLine
virtual int outputLine(const QByteArray &_str, int len=-1)
reimplement the mimeIO
Definition imap4.cpp:2401
IMAP4Protocol::specialCustomCommand
void specialCustomCommand(QDataStream &)
Send a custom command to the server.
Definition imap4.cpp:1517
IMAP4Protocol::parseRead
virtual bool parseRead(QByteArray &buffer, long len, long relay=0)
reimplement the parser read at least len bytes
Definition imap4.cpp:669
imapCommand
encapulate a IMAP command
Definition imapcommand.h:43
imapCommand::clientGetAnnotation
static CommandPtr clientGetAnnotation(const QString &box, const QString &entry, const QStringList &attributeNames)
Create a GETANNOTATION command.
Definition imapcommand.cpp:380
imapCommand::clientLogout
static CommandPtr clientLogout()
Create a LOGOUT command.
Definition imapcommand.cpp:311
imapCommand::clientSearch
static CommandPtr clientSearch(const QString &search, bool nouid=false)
Create a SEARCH command.
Definition imapcommand.cpp:297
imapCommand::clientSelect
static CommandPtr clientSelect(const QString &path, bool examine=false)
Create a SELECT command.
Definition imapcommand.cpp:208
imapCommand::clientNoop
static CommandPtr clientNoop()
Create a NOOP command.
Definition imapcommand.cpp:162
imapCommand::clientAppend
static CommandPtr clientAppend(const QString &box, const QString &flags, ulong size)
Create a APPEND command.
Definition imapcommand.cpp:233
imapCommand::clientMyRights
static CommandPtr clientMyRights(const QString &box)
Create a MYRIGHTS command.
Definition imapcommand.cpp:354
imapCommand::clientSetAnnotation
static CommandPtr clientSetAnnotation(const QString &box, const QString &entry, const QMap< QString, QString > &attributes)
Create a SETANNOTATION command.
Definition imapcommand.cpp:361
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
imapCommand::clientSubscribe
static CommandPtr clientSubscribe(const QString &path)
Create a SUBSCRIBE command.
Definition imapcommand.cpp:269
imapCommand::clientList
static CommandPtr clientList(const QString &reference, const QString &path, bool lsub=false)
Create a LIST command.
Definition imapcommand.cpp:199
imapCommand::clientCustom
static CommandPtr clientCustom(const QString &command, const QString &arguments)
Create a custom command.
Definition imapcommand.cpp:412
imapCommand::clientFetch
static CommandPtr clientFetch(ulong uid, const QString &fields, bool nouid=false)
Create a FETCH command.
Definition imapcommand.cpp:168
imapCommand::clientNamespace
static CommandPtr clientNamespace()
Create a NAMESPACE command.
Definition imapcommand.cpp:399
imapCommand::clientStartTLS
static CommandPtr clientStartTLS()
Create a STARTTLS command.
Definition imapcommand.cpp:317
imapCommand::clientDeleteACL
static CommandPtr clientDeleteACL(const QString &box, const QString &user)
Create a DELETEACL command.
Definition imapcommand.cpp:331
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
imapCommand::clientStatus
static CommandPtr clientStatus(const QString &path, const QString &parameters)
Create a STATUS command.
Definition imapcommand.cpp:247
imapCommand::clientCreate
static CommandPtr clientCreate(const QString &path)
Create a CREATE command.
Definition imapcommand.cpp:255
imapCommand::clientClose
static CommandPtr clientClose()
Create a CLOSE command.
Definition imapcommand.cpp:219
imapCommand::clientGetACL
static CommandPtr clientGetACL(const QString &box)
Create a GETACL command.
Definition imapcommand.cpp:339
imapCommand::clientRename
static CommandPtr clientRename(const QString &src, const QString &dest)
Create a RENAME command.
Definition imapcommand.cpp:289
imapCommand::clientExpunge
static CommandPtr clientExpunge()
Create a EXPUNGE command.
Definition imapcommand.cpp:283
imapCommand::clientCopy
static CommandPtr clientCopy(const QString &box, const QString &sequence, bool nouid=false)
Create a COPY command.
Definition imapcommand.cpp:225
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
mailHeader
Definition mailheader.h:34
mailHeader::getSubject
const QString getSubject()
get the unicode subject
Definition mailheader.h:117
mimeIO
Definition mimeio.h:29
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Wed Jan 24 2024 00:00:00 by doxygen 1.10.0 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-4.14.10 API Reference

Skip menu "kdepimlibs-4.14.10 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
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