79#include <kprotocolmanager.h>
80#include <kcomponentdata.h>
81#include <kmessagebox.h>
83#include <kio/connection.h>
84#include <kio/slaveinterface.h>
85#include <klocalizedstring.h>
93#define IMAP_PROTOCOL "imap"
94#define IMAP_SSL_PROTOCOL "imaps"
95const int ImapPort = 143;
96const int ImapsPort = 993;
102 void sigalrm_handler(
int );
103 KDE_EXPORT
int kdemain(
int argc,
char **argv );
107kdemain (
int argc,
char **argv)
109 kDebug( 7116 ) <<
"IMAP4::kdemain";
111 KComponentData instance(
"kio_imap4" );
113 fprintf( stderr,
"Usage: kio_imap4 protocol domain-socket1 domain-socket2\n" );
124 if ( strcasecmp( argv[1], IMAP_SSL_PROTOCOL ) == 0 ) {
126 }
else if ( strcasecmp( argv[1], IMAP_PROTOCOL ) == 0 ) {
131 slave->dispatchLoop();
140sigchld_handler (
int signo)
145 const int save_errno = errno;
148 while ( signo == SIGCHLD ) {
149 pid = waitpid( -1, &status, WNOHANG );
154 KDE_signal( SIGCHLD, sigchld_handler );
162IMAP4Protocol::IMAP4Protocol (
const QByteArray & pool,
const QByteArray & app,
bool isSSL)
163 :TCPSlaveBase ((isSSL ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL), pool, app, isSSL),
167 relayEnabled( false ),
168 cacheOutput( false ),
169 decodeContent( false ),
170 outputBuffer(&outputCache),
171 outputBufferIndex(0),
174 mTimeOfLastNoop( QDateTime() )
176 readBuffer[0] = 0x00;
179IMAP4Protocol::~IMAP4Protocol ()
181 disconnectFromHost();
182 kDebug( 7116 ) <<
"IMAP4: Finishing";
188 if ( !makeLogin() ) {
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 ) );
198 if ( aInfo ==
"DECODE" ) {
199 decodeContent =
true;
202 if ( aSequence ==
"0:0" && getState() == ISTATE_SELECT ) {
204 completeQueue.removeAll( cmd );
207 if ( aSequence.isEmpty() ) {
213 if ( !assureBox( aBox,
true ) ) {
218 if ( selectInfo.uidValidityAvailable() && !aValidity.isEmpty() &&
219 selectInfo.uidValidity() != aValidity.toULong() ) {
221 error( ERR_COULD_NOT_READ, _url.prettyUrl() );
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)]";
242 aSection +=
" RFC822.HEADER.LINES (REFERENCES)";
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" ) ) {
249 aSection.replace(
"BODY.PEEK[]",
"RFC822.PEEK" );
252 aSection.prepend(
"UID RFC822.SIZE FLAGS " );
253 }
else if ( aSection.isEmpty() ) {
254 aSection =
"UID BODY[] RFC822.SIZE FLAGS";
256 if ( aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX ) {
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" );
264 if ( selectInfo.countAvailable() ) {
265 outputLineStr(
"X-Count: " +
266 QString::number( selectInfo.count() ) +
"\r\n" );
268 if ( selectInfo.unseenAvailable() ) {
269 outputLineStr(
"X-Unseen: " +
270 QString::number( selectInfo.unseen() ) +
"\r\n" );
272 if ( selectInfo.uidValidityAvailable() ) {
273 outputLineStr(
"X-uidValidity: " +
274 QString::number( selectInfo.uidValidity() ) +
"\r\n" );
276 if ( selectInfo.uidNextAvailable() ) {
277 outputLineStr(
"X-UidNext: " +
278 QString::number( selectInfo.uidNext() ) +
"\r\n" );
280 if ( selectInfo.flagsAvailable() ) {
281 outputLineStr(
"X-Flags: " +
282 QString::number( selectInfo.flags() ) +
"\r\n" );
284 if ( selectInfo.permanentFlagsAvailable() ) {
285 outputLineStr(
"X-PermanentFlags: " +
286 QString::number( selectInfo.permanentFlags() ) +
"\r\n" );
288 if ( selectInfo.readWriteAvailable() ) {
289 if ( selectInfo.readWrite() ) {
300 if ( aEnum == ITYPE_MSG || ( aEnum == ITYPE_ATTACH && !decodeContent ) ) {
304 if ( aSequence !=
"0:0" ) {
305 QString contentEncoding;
306 if ( aEnum == ITYPE_ATTACH && decodeContent ) {
308 QString mySection = aSection;
309 mySection.replace(
']',
".MIME]" );
312 while ( !parseLoop() ) {
314 }
while ( !cmd->isComplete() );
315 completeQueue.removeAll( cmd );
317 if ( getLastHandled() && getLastHandled()->getHeader() ) {
318 contentEncoding = getLastHandled()->getHeader()->getEncoding();
329 aUpper = aSection.toUpper();
331 while ( !( res = parseLoop() ) ) {
338 imapCache *cache = getLastHandled();
340 lastone = cache->getHeader();
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 ) {
354 if ( cache->getUid() != 0 ) {
355 outputLineStr(
"X-UID: " +
356 QString::number( cache->getUid() ) +
"\r\n" );
358 if ( cache->getSize() != 0 ) {
359 outputLineStr(
"X-Length: " +
360 QString::number( cache->getSize() ) +
"\r\n" );
362 if ( !cache->getDate().isEmpty() ) {
363 outputLineStr( QByteArray(QByteArray(
"X-Date: ") +
364 cache->getDate() + QByteArray(
"\r\n")) );
366 if ( cache->getFlags() != 0 ) {
367 outputLineStr(
"X-Flags: " +
368 QString::number( cache->getFlags() ) +
"\r\n" );
373 if ( lastone && !decodeContent ) {
374 lastone->outputPart( *
this );
380 }
while ( cmd && !cmd->isComplete() );
381 if ( aEnum == ITYPE_BOX || aEnum == ITYPE_DIR_AND_BOX ) {
386 completeQueue.removeAll( cmd );
391 data( QByteArray() );
394 relayEnabled =
false;
396 kDebug( 7116 ) <<
"IMAP4::get - finished";
402 kDebug( 7116 ) <<
"IMAP4::listDir -" << _url.prettyUrl();
404 if ( _url.path().isEmpty() ) {
412 QString myBox, mySequence, myLType, mySection, myValidity, myDelimiter, myInfo;
414 enum IMAP_TYPE myType =
415 parseURL( _url, myBox, mySection, myLType, mySequence, myValidity, myDelimiter, myInfo,
true );
417 if ( !makeLogin() ) {
421 if ( myType == ITYPE_DIR || myType == ITYPE_DIR_AND_BOX ) {
422 QString listStr = myBox;
425 if ( !listStr.isEmpty() && !listStr.endsWith( myDelimiter ) &&
426 mySection !=
"FOLDERONLY" ) {
427 listStr += myDelimiter;
430 if ( mySection.isEmpty() ) {
432 }
else if ( mySection ==
"COMPLETE" ) {
435 kDebug( 7116 ) <<
"IMAP4Protocol::listDir - listStr=" << listStr;
438 ( myLType ==
"LSUB" || myLType ==
"LSUBNOCHECK" ) ) );
439 if ( cmd->result() ==
"OK" ) {
443 if ( aURL.path().contains(
';' ) ) {
444 aURL.setPath( aURL.path().left( aURL.path().indexOf(
';' ) ) );
447 kDebug( 7116 ) <<
"IMAP4Protocol::listDir - got" << listResponses.count();
449 if ( myLType ==
"LSUB" ) {
451 QList<imapList> listResponsesSave = listResponses;
453 for ( QList< imapList >::Iterator it = listResponsesSave.begin();
454 it != listResponsesSave.end(); ++it ) {
456 for ( QList< imapList >::Iterator it2 = listResponses.begin();
457 it2 != listResponses.end(); ++it2 ) {
458 if ( ( *it2 ).name() == ( *it ).name() ) {
466 doListEntry( aURL, myBox, ( *it ), ( mySection !=
"FOLDERONLY" ) );
468 kDebug( 7116 ) <<
"IMAP4Protocol::listDir - suppress" << ( *it ).name();
471 listResponses = listResponsesSave;
473 for ( QList< imapList >::Iterator it = listResponses.begin();
474 it != listResponses.end(); ++it ) {
475 doListEntry( aURL, myBox, ( *it ), ( mySection !=
"FOLDERONLY" ) );
479 listEntry( entry,
true );
481 error( ERR_CANNOT_ENTER_DIRECTORY, _url.prettyUrl() );
482 completeQueue.removeAll( cmd );
485 completeQueue.removeAll( cmd );
487 if ( ( myType == ITYPE_BOX || myType == ITYPE_DIR_AND_BOX ) &&
488 myLType !=
"LIST" && myLType !=
"LSUB" && myLType !=
"LSUBNOCHECK" ) {
490 aURL.setQuery( QString() );
491 const QString encodedUrl = aURL.url( KUrl::LeaveTrailingSlash );
493 if ( !_url.query().isEmpty() ) {
494 QString query = KUrl::fromPercentEncoding( _url.query().toLatin1() );
495 query = query.right( query.length() - 1 );
496 if ( !query.isEmpty() ) {
499 if ( !assureBox( myBox,
true ) ) {
503 if ( !selectInfo.countAvailable() || selectInfo.count() ) {
505 if ( cmd->result() !=
"OK" ) {
506 error( ERR_UNSUPPORTED_ACTION, _url.prettyUrl() );
507 completeQueue.removeAll( cmd );
510 completeQueue.removeAll( cmd );
512 QStringList list = getResults();
515 if ( selectInfo.uidNextAvailable() ) {
516 stretch = QString::number( selectInfo.uidNext() ).length();
521 for ( QStringList::ConstIterator it = list.constBegin(); it != list.constEnd();
523 fake.setUid( ( *it ).toULong() );
524 doListEntry( encodedUrl, stretch, &fake );
527 listEntry( entry,
true );
531 if ( !assureBox( myBox,
true ) ) {
535 kDebug( 7116 ) <<
"IMAP4: select returned:";
536 if ( selectInfo.recentAvailable() ) {
537 kDebug( 7116 ) <<
"Recent:" << selectInfo.recent() <<
"d";
539 if ( selectInfo.countAvailable() ) {
540 kDebug( 7116 ) <<
"Count:" << selectInfo.count() <<
"d";
542 if ( selectInfo.unseenAvailable() ) {
543 kDebug( 7116 ) <<
"Unseen:" << selectInfo.unseen() <<
"d";
545 if ( selectInfo.uidValidityAvailable() ) {
546 kDebug( 7116 ) <<
"uidValidity:" << selectInfo.uidValidity() <<
"d";
548 if ( selectInfo.flagsAvailable() ) {
549 kDebug( 7116 ) <<
"Flags:" << selectInfo.flags() <<
"d";
551 if ( selectInfo.permanentFlagsAvailable() ) {
552 kDebug( 7116 ) <<
"PermanentFlags:" << selectInfo.permanentFlags() <<
"d";
554 if ( selectInfo.readWriteAvailable() ) {
555 kDebug( 7116 ) <<
"Access:" << ( selectInfo.readWrite() ?
"Read/Write" :
"Read only" );
559 if ( selectInfo.uidValidityAvailable() &&
560 selectInfo.uidValidity() != myValidity.toULong() ) {
564 newUrl.setPath(
'/' + myBox +
";UIDVALIDITY=" +
565 QString::number( selectInfo.uidValidity() ) );
566 kDebug( 7116 ) <<
"IMAP4::listDir - redirecting to" << newUrl.prettyUrl();
567 redirection( newUrl );
571 if ( selectInfo.count() > 0 ) {
574 if ( selectInfo.uidNextAvailable() ) {
575 stretch = QString::number( selectInfo.uidNext() ).length();
580 if ( mySequence.isEmpty() ) {
584 bool withSubject = mySection.isEmpty();
585 if ( mySection.isEmpty() ) {
586 mySection =
"UID RFC822.SIZE ENVELOPE";
589 bool withFlags = mySection.toUpper().contains(
"FLAGS") ;
594 while ( !parseLoop() ) {
597 cache = getLastHandled();
599 if ( cache && !fetch->isComplete() ) {
600 doListEntry( encodedUrl, stretch, cache, withFlags, withSubject );
602 }
while ( !fetch->isComplete() );
604 listEntry( entry,
true );
608 if ( !selectInfo.alert().isNull() ) {
609 if ( !myBox.isEmpty() ) {
610 warning( i18n(
"Message from %1 while processing '%2': %3", myHost, myBox, selectInfo.alert() ) );
612 warning( i18n(
"Message from %1: %2", myHost, selectInfo.alert() ) );
614 selectInfo.setAlert( 0 );
617 kDebug( 7116 ) <<
"IMAP4Protocol::listDir - Finishing listDir";
622IMAP4Protocol::setHost (
const QString & _host, quint16 _port,
623 const QString & _user,
const QString & _pass)
625 if ( myHost != _host || myPort != _port || myUser != _user || myPass != _pass ) {
628 if ( !myHost.isEmpty() ) {
633 myPort = ( mySSL ) ? ImapsPort : ImapPort;
645 if ( relayEnabled ) {
648 mProcessedSize += buffer.size();
649 processedSize( mProcessedSize );
650 }
else if ( cacheOutput ) {
652 if ( !outputBuffer.isOpen() ) {
653 outputBuffer.open( QIODevice::WriteOnly );
655 outputBuffer.seek( outputBufferIndex );
656 outputBuffer.write( buffer, buffer.size() );
657 outputBufferIndex += buffer.size();
664 if ( relayEnabled ) {
671 const long int bufLen = 8192;
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 );
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 );
692 QBuffer stream( &buffer );
693 stream.open( QIODevice::WriteOnly );
694 stream.seek( buffer.size() );
695 stream.write( buf, readLen );
699 return ( buffer.size() == len );
704 if ( myHost.isEmpty() ) {
710 if ( readBufferLen > 0 ) {
711 while ( copyLen < readBufferLen && readBuffer[copyLen] !=
'\n' ) {
714 if ( copyLen < readBufferLen ) {
718 QByteArray relayData;
720 if ( copyLen < (ssize_t) relay ) {
723 relayData = QByteArray::fromRawData( readBuffer, relay );
730 int oldsize = buffer.size();
731 buffer.resize( oldsize + copyLen );
732 memcpy( buffer.data() + oldsize, readBuffer, copyLen );
736 readBufferLen -= copyLen;
737 if ( readBufferLen ) {
738 memmove( readBuffer, &readBuffer[copyLen], readBufferLen );
740 if ( buffer[buffer.size() - 1] ==
'\n' ) {
744 if ( !isConnected() ) {
745 kDebug( 7116 ) <<
"parseReadLine - connection broken";
746 error( ERR_CONNECTION_BROKEN, myHost );
747 setState( ISTATE_CONNECT );
751 if ( !waitForResponse( responseTimeout() ) ) {
752 error( ERR_SERVER_TIMEOUT, myHost );
753 setState( ISTATE_CONNECT );
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 );
769IMAP4Protocol::setSubURL (
const KUrl & _url)
771 kDebug( 7116 ) <<
"IMAP4::setSubURL -" << _url.prettyUrl();
772 KIO::TCPSlaveBase::setSubUrl( _url );
776IMAP4Protocol::put (
const KUrl & _url,
int, KIO::JobFlags)
778 kDebug( 7116 ) <<
"IMAP4::put -" << _url.prettyUrl();
780 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
781 enum IMAP_TYPE aType =
782 parseURL( _url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo );
785 if ( aType != ITYPE_BOX && aType != ITYPE_DIR_AND_BOX ) {
786 if ( aBox[aBox.length() - 1] ==
'/' ) {
787 aBox = aBox.right( aBox.length() - 1 );
791 if ( cmd->result() !=
"OK" ) {
792 error( ERR_COULD_NOT_WRITE, _url.prettyUrl() );
793 completeQueue.removeAll( cmd );
796 completeQueue.removeAll( cmd );
798 QList < QByteArray* > bufferList;
804 QByteArray *buffer =
new QByteArray();
806 result = readData( *buffer );
808 bufferList.append( buffer );
813 }
while ( result > 0 );
816 error( ERR_ABORTED, _url.prettyUrl() );
821 while ( !parseLoop() ) {
825 if ( !cmd->isComplete() && !getContinuation().isEmpty() ) {
830 QListIterator<QByteArray *> it( bufferList );
832 while ( it.hasNext() && sendOk ) {
836 ( write( buffer->data(), buffer->size() ) ==
837 (ssize_t) buffer->size() );
838 wrote += buffer->size();
839 processedSize( wrote );
842 error( ERR_CONNECTION_BROKEN, myHost );
843 completeQueue.removeAll( cmd );
844 setState( ISTATE_CONNECT );
851 while ( !cmd->isComplete() && getState() != ISTATE_NO ) {
854 if ( getState() == ISTATE_NO ) {
857 error( ERR_CONNECTION_BROKEN, myHost );
858 completeQueue.removeAll( cmd );
861 }
else if ( cmd->result() !=
"OK" ) {
862 error( ERR_SLAVE_DEFINED, cmd->resultInfo() );
863 completeQueue.removeAll( cmd );
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 );
875 else if ( aBox == getCurrentBox() ) {
878 completeQueue.removeAll( cmd );
884 error( ERR_SLAVE_DEFINED, cmd->resultInfo() );
885 completeQueue.removeAll( cmd );
889 completeQueue.removeAll( cmd );
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;
904 if ( cmd->result() !=
"OK" ) {
905 kDebug( 7116 ) <<
"IMAP4::mkdir -" << cmd->resultInfo();
906 error( ERR_COULD_NOT_MKDIR, _url.prettyUrl() );
907 completeQueue.removeAll( cmd );
910 completeQueue.removeAll( cmd );
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" ) );
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 ) {
924 completeQueue.removeAll( cmd );
926 if ( cmd->result() !=
"OK" ) {
927 error( ERR_COULD_NOT_MKDIR, _url.prettyUrl() );
928 completeQueue.removeAll( cmd );
931 completeQueue.removeAll( cmd );
936 completeQueue.removeAll( cmd );
942IMAP4Protocol::copy (
const KUrl & src,
const KUrl & dest,
int, KIO::JobFlags flags)
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 );
953 if ( dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX ) {
955 int sub = dBox.indexOf( sBox );
961 QString subDir = dBox.right( dBox.length() - dBox.lastIndexOf(
'/' ) );
962 QString topDir = dBox.left( sub );
963 testDir.setPath(
'/' + topDir );
965 parseURL( testDir, topDir, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo );
967 kDebug( 7116 ) <<
"IMAP4::copy - checking this destination" << topDir;
969 if ( dType == ITYPE_BOX || dType == ITYPE_DIR_AND_BOX ) {
970 kDebug( 7116 ) <<
"IMAP4::copy - assuming this destination" << topDir;
975 topDir =
'/' + topDir + subDir;
976 testDir.setPath( topDir );
977 kDebug( 7116 ) <<
"IMAP4::copy - checking this destination" << topDir;
979 parseURL( testDir, topDir, dSection, dLType, dSequence, dValidity, dDelimiter, dInfo );
980 if ( dType != ITYPE_BOX && dType != ITYPE_DIR_AND_BOX ) {
985 if ( cmd->result() ==
"OK" ) {
986 kDebug( 7116 ) <<
"IMAP4::copy - assuming this destination" << topDir;
990 completeQueue.removeAll( cmd );
992 if ( cmd->result() ==
"OK" ) {
995 error( ERR_COULD_NOT_WRITE, dest.prettyUrl() );
998 completeQueue.removeAll( cmd );
1004 if ( sType == ITYPE_MSG || sType == ITYPE_BOX || sType == ITYPE_DIR_AND_BOX ) {
1006 if ( !assureBox( sBox,
true ) ) {
1009 kDebug( 7116 ) <<
"IMAP4::copy -" << sBox <<
" ->" << dBox;
1014 if ( cmd->result() !=
"OK" ) {
1015 kError( 5006 ) <<
"IMAP4::copy -" << cmd->resultInfo();
1016 error( ERR_COULD_NOT_WRITE, dest.prettyUrl() );
1017 completeQueue.removeAll( cmd );
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 );
1029 completeQueue.removeAll( cmd );
1031 error( ERR_ACCESS_DENIED, src.prettyUrl() );
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 );
1047 case ITYPE_DIR_AND_BOX:
1048 if ( !aSequence.isEmpty() ) {
1049 if ( aSequence ==
"*" ) {
1050 if ( !assureBox( aBox,
false ) ) {
1054 if ( cmd->result() !=
"OK" ) {
1055 error( ERR_CANNOT_DELETE, _url.prettyUrl() );
1056 completeQueue.removeAll( cmd );
1059 completeQueue.removeAll( cmd );
1062 if ( !assureBox( aBox,
false ) ) {
1067 if ( cmd->result() !=
"OK" ) {
1068 error( ERR_CANNOT_DELETE, _url.prettyUrl() );
1069 completeQueue.removeAll( cmd );
1072 completeQueue.removeAll( cmd );
1075 if ( getCurrentBox() == aBox ) {
1077 completeQueue.removeAll( cmd );
1078 setState( ISTATE_LOGIN );
1082 completeQueue.removeAll( cmd );
1085 if ( cmd->result() !=
"OK" ) {
1086 completeQueue.removeAll( cmd );
1087 if ( !assureBox( aBox,
false ) ) {
1090 bool stillOk =
true;
1093 if ( cmd->result() !=
"OK" ) {
1096 completeQueue.removeAll( cmd );
1100 if ( cmd->result() !=
"OK" ) {
1103 completeQueue.removeAll( cmd );
1104 setState( ISTATE_LOGIN );
1108 if ( cmd->result() !=
"OK" ) {
1111 completeQueue.removeAll( cmd );
1114 error( ERR_COULD_NOT_RMDIR, _url.prettyUrl() );
1118 completeQueue.removeAll( cmd );
1126 if ( cmd->result() !=
"OK" ) {
1127 error( ERR_COULD_NOT_RMDIR, _url.prettyUrl() );
1128 completeQueue.removeAll( cmd );
1131 completeQueue.removeAll( cmd );
1138 if ( !assureBox ( aBox,
false ) ) {
1143 if ( cmd->result() !=
"OK" ) {
1144 error( ERR_CANNOT_DELETE, _url.prettyUrl() );
1145 completeQueue.removeAll( cmd );
1148 completeQueue.removeAll( cmd );
1154 error( ERR_CANNOT_DELETE, _url.prettyUrl() );
1179 kDebug( 7116 ) <<
"IMAP4Protocol::special";
1180 if ( !makeLogin() ) {
1184 QDataStream stream( aData );
1195 stream >> src >> dest;
1196 copy( src, dest, 0, KIO::DefaultFlags );
1202 infoMessage( imapCapabilities.join(
" " ) );
1210 if ( cmd->result() !=
"OK" ) {
1211 kDebug( 7116 ) <<
"NOOP did not succeed - connection broken";
1212 completeQueue.removeAll( cmd );
1213 error( ERR_CONNECTION_BROKEN, myHost );
1216 completeQueue.removeAll( cmd );
1224 infoMessage( imapNamespaces.join(
"," ) );
1233 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1234 parseURL( _url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo );
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() ) );
1243 completeQueue.removeAll( cmd );
1252 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1253 parseURL( _url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo );
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() ) );
1262 completeQueue.removeAll( cmd );
1271 if ( hasCapability(
"ACL" ) ) {
1274 error( ERR_UNSUPPORTED_ACTION, QString::fromLatin1(
"ACL" ) );
1283 if ( hasCapability(
"ANNOTATEMORE" ) ) {
1286 error( ERR_UNSUPPORTED_ACTION, QString::fromLatin1(
"ANNOTATEMORE" ) );
1295 if ( hasCapability(
"QUOTA" ) ) {
1296 specialQuotaCommand( cmd, stream );
1298 error( ERR_UNSUPPORTED_ACTION, QString::fromLatin1(
"QUOTA" ) );
1306 QByteArray newFlags;
1307 stream >> _url >> newFlags;
1309 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1310 parseURL( _url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo );
1311 if ( !assureBox( aBox,
false ) ) {
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";
1323 if ( cmd->result() !=
"OK" ) {
1324 completeQueue.removeAll( cmd );
1325 error( ERR_SLAVE_DEFINED, i18n(
"Changing the flags of message %1 "
1327 _url.prettyUrl(), cmd->result() ) );
1330 completeQueue.removeAll( cmd );
1331 if ( !newFlags.isEmpty() ) {
1333 if ( cmd->result() !=
"OK" ) {
1334 completeQueue.removeAll( cmd );
1335 error( ERR_SLAVE_DEFINED, i18n(
"Silent Changing the flags of message %1 "
1337 _url.prettyUrl(), cmd->result() ) );
1340 completeQueue.removeAll( cmd );
1350 QByteArray newFlags;
1351 stream >> _url >> seen;
1353 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1354 parseURL( _url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo );
1355 if ( !assureBox( aBox,
true ) ) {
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() ) );
1372 completeQueue.removeAll( cmd );
1390 kWarning( 7116 ) <<
"Unknown command in special():" << tmp;
1391 error( ERR_UNSUPPORTED_ACTION, QString( QChar( tmp ) ) );
1402 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1403 parseURL( _url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo );
1405 switch ( command ) {
1409 stream >> user >> acl;
1410 kDebug( 7116 ) <<
"SETACL" << 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() ) );
1418 completeQueue.removeAll( cmd );
1426 kDebug( 7116 ) <<
"DELETEACL" << 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() ) );
1434 completeQueue.removeAll( cmd );
1440 kDebug( 7116 ) <<
"GETACL" << 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() ) );
1452 kDebug( 7116 ) << getResults();
1453 infoMessage( getResults().join(
"\"" ) );
1460 error( ERR_UNSUPPORTED_ACTION, QString( QChar( command ) ) );
1465 kDebug( 7116 ) <<
"MYRIGHTS" << 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() ) );
1473 QStringList lst = getResults();
1474 kDebug( 7116 ) <<
"myrights results:" << lst;
1475 if ( !lst.isEmpty() ) {
1476 Q_ASSERT( lst.count() == 1 );
1477 infoMessage( lst.first() );
1483 kWarning( 7116 ) <<
"Unknown special ACL command:" << command;
1484 error( ERR_UNSUPPORTED_ACTION, QString( QChar( command ) ) );
1491 kDebug( 7116 ) <<
"IMAP4Protocol::specialSearchCommand";
1494 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1495 parseURL( _url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo );
1496 if ( !assureBox( aBox,
true ) ) {
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() ) );
1507 completeQueue.removeAll( cmd );
1508 QStringList lst = getResults();
1509 kDebug( 7116 ) <<
"IMAP4Protocol::specialSearchCommand '" << aSection
1510 <<
"' returns" << lst;
1511 infoMessage( lst.join(
" " ) );
1519 kDebug( 7116 ) <<
"IMAP4Protocol::specialCustomCommand" << endl;
1521 QString command, arguments;
1524 stream >> command >> arguments;
1530 if ( type ==
'N' ) {
1531 kDebug( 7116 ) <<
"IMAP4Protocol::specialCustomCommand: normal mode" << endl;
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() ) );
1538 completeQueue.removeAll( cmd );
1539 QStringList lst = getResults();
1540 kDebug( 7116 ) <<
"IMAP4Protocol::specialCustomCommand '" << command
1542 <<
"' returns " << lst << endl;
1543 infoMessage( lst.join(
" " ) );
1551 if ( type ==
'E' ) {
1552 kDebug( 7116 ) <<
"IMAP4Protocol::specialCustomCommand: extended mode" << endl;
1554 while ( !parseLoop() ) {
1558 if ( !cmd->isComplete() && !getContinuation().isEmpty() ) {
1559 const QByteArray buffer = arguments.toUtf8();
1562 bool sendOk = ( write( buffer.data(), buffer.size() ) == (ssize_t)buffer.size() );
1563 processedSize( buffer.size() );
1566 error( ERR_CONNECTION_BROKEN, myHost );
1567 completeQueue.removeAll( cmd );
1568 setState( ISTATE_CONNECT );
1576 while ( !parseLoop() ) {
1578 }
while ( !cmd->isComplete() );
1580 completeQueue.removeAll( cmd );
1582 QStringList lst = getResults();
1583 kDebug( 7116 ) <<
"IMAP4Protocol::specialCustomCommand: returns " << lst << endl;
1584 infoMessage( lst.join(
" " ) );
1596 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1597 parseURL( _url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo );
1599 switch ( command ) {
1607 QMap<QString, QString> attributes;
1608 stream >> entry >> attributes;
1609 kDebug( 7116 ) <<
"SETANNOTATION" << aBox << entry << attributes.count() <<
" 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() ) );
1617 completeQueue.removeAll( cmd );
1628 QStringList attributeNames;
1629 stream >> entry >> attributeNames;
1630 kDebug( 7116 ) <<
"GETANNOTATION" << 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() ) );
1641 kDebug( 7116 ) << getResults();
1642 infoMessage( getResults().join(
"\r" ) );
1647 kWarning( 7116 ) <<
"Unknown special annotate command:" << command;
1648 error( ERR_UNSUPPORTED_ACTION, QString( QChar( command ) ) );
1653IMAP4Protocol::specialQuotaCommand(
int command, QDataStream& stream )
1658 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1659 parseURL( _url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo );
1661 switch ( command ) {
1664 kDebug( 7116 ) <<
"QUOTAROOT" << 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() ) );
1672 infoMessage( getResults().join(
"\r" ) );
1678 kDebug( 7116 ) <<
"GETQUOTA command";
1679 kWarning( 7116 ) <<
"UNIMPLEMENTED";
1684 kDebug( 7116 ) <<
"SETQUOTA command";
1685 kWarning( 7116 ) <<
"UNIMPLEMENTED";
1689 kWarning( 7116 ) <<
"Unknown special quota command:" << command;
1690 error( ERR_UNSUPPORTED_ACTION, QString( QChar( command ) ) );
1695IMAP4Protocol::rename (
const KUrl & src,
const KUrl & dest, KIO::JobFlags flags)
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 );
1705 if ( dType == ITYPE_UNKNOWN ) {
1709 case ITYPE_DIR_AND_BOX:
1711 if ( getState() == ISTATE_SELECT && sBox == getCurrentBox() ) {
1712 kDebug( 7116 ) <<
"IMAP4::rename - close" << getCurrentBox();
1715 bool ok = cmd->result() ==
"OK";
1716 completeQueue.removeAll( cmd );
1718 error( ERR_SLAVE_DEFINED, i18n(
"Unable to close mailbox." ) );
1721 setState( ISTATE_LOGIN );
1724 if ( cmd->result() !=
"OK" ) {
1725 error( ERR_CANNOT_RENAME, cmd->result() );
1726 completeQueue.removeAll( cmd );
1729 completeQueue.removeAll( cmd );
1736 error( ERR_CANNOT_RENAME, src.prettyUrl() );
1740 error( ERR_CANNOT_RENAME, src.prettyUrl() );
1747IMAP4Protocol::slave_status()
1749 bool connected = (getState() != ISTATE_NO) && isConnected();
1750 kDebug( 7116 ) <<
"IMAP4::slave_status" << connected;
1751 slaveStatus( connected ? myHost : QString(), connected );
1755IMAP4Protocol::dispatch (
int command,
const QByteArray & data)
1757 kDebug( 7116 ) <<
"IMAP4::dispatch - command=" << command;
1758 KIO::TCPSlaveBase::dispatch( command, data );
1764 kDebug( 7116 ) <<
"IMAP4::stat -" << _url.prettyUrl();
1765 QString aBox, aSequence, aLType, aSection, aValidity, aDelimiter, aInfo;
1767 enum IMAP_TYPE aType =
1768 parseURL( _url, aBox, aSection, aLType, aSequence, aValidity, aDelimiter, aInfo,
true );
1772 entry.insert( UDSEntry::UDS_NAME, aBox );
1774 if ( !aSection.isEmpty() ) {
1775 if ( getState() == ISTATE_SELECT && aBox == getCurrentBox() ) {
1777 bool ok = cmd->result() ==
"OK";
1778 completeQueue.removeAll( cmd );
1780 error( ERR_SLAVE_DEFINED, i18n(
"Unable to close mailbox." ) );
1783 setState( ISTATE_LOGIN );
1787 if ( aType == ITYPE_MSG || aType == ITYPE_ATTACH ) {
1791 ok = cmd->result() ==
"OK";
1792 cmdInfo = cmd->resultInfo();
1793 completeQueue.removeAll( cmd );
1798 if ( cmd->result() ==
"OK" ) {
1799 for ( QList< imapList >::Iterator it = listResponses.begin();
1800 it != listResponses.end(); ++it ) {
1801 if ( aBox == ( *it ).name() ) {
1806 completeQueue.removeAll( cmd );
1808 error( ERR_SLAVE_DEFINED, i18n(
"Unable to get information about folder %1. The server replied: %2",
1811 error( KIO::ERR_DOES_NOT_EXIST, aBox );
1815 if ( ( aSection ==
"UIDNEXT" && getStatus().uidNextAvailable() ) ||
1816 ( aSection ==
"UNSEEN" && getStatus().unseenAvailable() ) ) {
1817 entry.insert( UDSEntry::UDS_SIZE, ( aSection ==
"UIDNEXT" ) ? getStatus().uidNext()
1818 : getStatus().unseen() );
1820 }
else if ( aType == ITYPE_BOX ||
1821 aType == ITYPE_DIR_AND_BOX ||
1822 aType == ITYPE_MSG ||
1823 aType == ITYPE_ATTACH ) {
1826 if ( aBox == getCurrentBox() ) {
1827 validity = selectInfo.uidValidity();
1833 completeQueue.removeAll( cmd );
1834 validity = getStatus().uidValidity();
1837#warning This is temporary since Dec 2000 and makes most of the below code invalid
1841 if ( aType == ITYPE_BOX || aType == ITYPE_DIR_AND_BOX ) {
1843 if ( validity > 0 && validity != aValidity.toULong() ) {
1847 newUrl.setPath(
'/' + aBox +
";UIDVALIDITY=" +
1848 QString::number( validity ) );
1849 kDebug( 7116 ) <<
"IMAP4::stat - redirecting to" << newUrl.prettyUrl();
1850 redirection( newUrl );
1852 }
else if ( aType == ITYPE_MSG || aType == ITYPE_ATTACH ) {
1858 if ( validity > 0 && validity != aValidity.toULong() ) {
1859 aType = ITYPE_UNKNOWN;
1860 kDebug( 7116 ) <<
"IMAP4::stat - url has invalid validity [" << validity <<
"d]" << _url.prettyUrl();
1865 entry.insert( UDSEntry::UDS_MIME_TYPE, getMimeType( aType ) );
1870 entry.insert( UDSEntry::UDS_FILE_TYPE, S_IFDIR );
1874 case ITYPE_DIR_AND_BOX:
1875 entry.insert( UDSEntry::UDS_FILE_TYPE, S_IFDIR );
1880 entry.insert( UDSEntry::UDS_FILE_TYPE, S_IFREG );
1884 error( ERR_DOES_NOT_EXIST, _url.prettyUrl() );
1889 kDebug( 7116 ) <<
"IMAP4::stat - Finishing stat";
1893void IMAP4Protocol::openConnection()
1895 if ( makeLogin() ) {
1900void IMAP4Protocol::closeConnection()
1902 if ( getState() == ISTATE_NO ) {
1905 if ( getState() == ISTATE_SELECT && metaData(
"expunge" ) ==
"auto" ) {
1907 completeQueue.removeAll( cmd );
1909 if ( getState() != ISTATE_CONNECT ) {
1911 completeQueue.removeAll( cmd );
1913 disconnectFromHost();
1914 setState( ISTATE_NO );
1915 completeQueue.clear();
1922bool IMAP4Protocol::makeLogin()
1924 if ( getState() == ISTATE_LOGIN || getState() == ISTATE_SELECT ) {
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 ) ) {
1935 setState( ISTATE_CONNECT );
1937 myAuth = metaData(
"auth" );
1938 myTLS = metaData(
"tls" );
1939 kDebug( 7116 ) <<
"myAuth:" << myAuth;
1944 if ( !alreadyConnected ) {
1945 while ( !parseLoop() ) {
1949 if ( !unhandled.isEmpty() ) {
1950 greeting = unhandled.first().trimmed();
1953 cmd = doCommand( CommandPtr(
new imapCommand(
"CAPABILITY",
"" ) ) );
1955 kDebug( 7116 ) <<
"IMAP4: setHost: capability";
1956 for ( QStringList::const_iterator it = imapCapabilities.constBegin();
1957 it != imapCapabilities.constEnd(); ++it ) {
1958 kDebug( 7116 ) <<
"'" << ( *it ) <<
"'";
1960 completeQueue.removeAll( cmd );
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 ) );
1970 if ( metaData(
"nologin" ) ==
"on" ) {
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." ) );
1980 if ( ( myTLS ==
"on" ) &&
1981 hasCapability( QString(
"STARTTLS" ) ) ) {
1983 if ( cmd->result() ==
"OK" ) {
1984 completeQueue.removeAll( cmd );
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 ) <<
"'";
1992 completeQueue.removeAll( cmd2 );
1994 kWarning( 7116 ) <<
"TLS mode setup has failed. Aborting.";
1995 error( ERR_COULD_NOT_LOGIN, i18n(
"Starting TLS failed." ) );
2000 completeQueue.removeAll( cmd );
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 ) );
2012 if ( greeting.contains( QRegExp(
"Cyrus IMAP4 v2.1" ) ) ) {
2013 removeCapability(
"ANNOTATEMORE" );
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" );
2028 kDebug( 7116 ) <<
"IMAP4::makeLogin - attempting login";
2030 KIO::AuthInfo authInfo;
2031 authInfo.username = myUser;
2032 authInfo.password = myPass;
2033 authInfo.prompt = i18n(
"Username and password for your IMAP account:" );
2035 kDebug( 7116 ) <<
"IMAP4::makeLogin - open_PassDlg said user=" << myUser <<
" pass=xx";
2038 if ( myAuth.isEmpty() || myAuth ==
"*" ) {
2039 if ( myUser.isEmpty() || myPass.isEmpty() ) {
2040 if ( openPasswordDialog( authInfo ) ) {
2041 myUser = authInfo.username;
2042 myPass = authInfo.password;
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 ) );
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 ) );
2056 myUser = authInfo.username;
2057 myPass = authInfo.password;
2060 if ( hasCapability(
"NAMESPACE" ) ) {
2063 if ( cmd->result() ==
"OK" ) {
2064 kDebug( 7116 ) <<
"makeLogin - registered namespaces";
2066 completeQueue.removeAll( cmd );
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" ) ) {
2077 QString nsentry = QString::number( 0 ) +
"==" + ( *it ).hierarchyDelimiter();
2078 imapNamespaces.append( nsentry );
2082 completeQueue.removeAll( cmd );
2084 kDebug( 7116 ) <<
"makeLogin - NO login";
2087 return getState() == ISTATE_LOGIN;
2094 QByteArray writer = aStr.toUtf8();
2095 int len = writer.length();
2098 if ( len == 0 || ( writer[len - 1] !=
'\n' ) ) {
2104 write( writer.data(), len );
2108IMAP4Protocol::getMimeType (
enum IMAP_TYPE aType)
2112 return "inode/directory";
2116 return "message/digest";
2119 case ITYPE_DIR_AND_BOX:
2120 return "message/directory";
2124 return "message/rfc822";
2129 return "application/octet-stream";
2134 return "unknown/unknown";
2139IMAP4Protocol::doListEntry (
const KUrl & _url,
int stretch, imapCache * cache,
2140 bool withFlags,
bool withSubject)
2143 aURL.setQuery( QString() );
2144 const QString encodedUrl = aURL.url( KUrl::LeaveTrailingSlash );
2145 doListEntry( encodedUrl, stretch, cache, withFlags, withSubject );
2149IMAP4Protocol::doListEntry (
const QString & encodedUrl,
int stretch, imapCache * cache,
2150 bool withFlags,
bool withSubject)
2157 const QString uid = QString::number( cache->getUid() );
2159 if ( stretch > 0 ) {
2160 tmp =
"0000000000000000" + uid;
2161 tmp = tmp.right( stretch );
2163 if ( withSubject ) {
2169 entry.insert( UDSEntry::UDS_NAME, tmp );
2172 if ( tmp[tmp.length() - 1] !=
'/' ) {
2175 tmp +=
";UID=" + uid;
2176 entry.insert( UDSEntry::UDS_URL, tmp );
2178 entry.insert( UDSEntry::UDS_FILE_TYPE, S_IFREG );
2180 entry.insert( UDSEntry::UDS_SIZE, cache->getSize() );
2182 entry.insert( UDSEntry::UDS_MIME_TYPE, QString::fromLatin1(
"message/rfc822" ) );
2184 entry.insert( UDSEntry::UDS_USER, myUser );
2186 entry.insert( KIO::UDSEntry::UDS_ACCESS, ( withFlags ) ? cache->getFlags() : S_IRUSR | S_IXUSR | S_IWUSR );
2188 listEntry( entry,
false );
2193IMAP4Protocol::doListEntry (
const KUrl & _url,
const QString & myBox,
2194 const imapList & item,
bool appendPath)
2197 aURL.setQuery( QString() );
2199 int hdLen = item.hierarchyDelimiter().length();
2203 QString mailboxName = item.name();
2206 if ( mailboxName.startsWith( myBox ) && mailboxName.length() > myBox.length() ) {
2208 mailboxName.right( mailboxName.length() - myBox.length() );
2210 if ( mailboxName[0] ==
'/' ) {
2211 mailboxName = mailboxName.right( mailboxName.length() - 1 );
2213 if ( mailboxName.left( hdLen ) == item.hierarchyDelimiter() ) {
2214 mailboxName = mailboxName.right( mailboxName.length() - hdLen );
2216 if ( mailboxName.right( hdLen ) == item.hierarchyDelimiter() ) {
2217 mailboxName.truncate( mailboxName.length() - hdLen );
2221 if ( !item.hierarchyDelimiter().isEmpty() &&
2222 mailboxName.contains( item.hierarchyDelimiter() ) ) {
2223 tmp = mailboxName.section( item.hierarchyDelimiter(), -1 );
2229 if ( tmp.isEmpty() ) {
2233 if ( !tmp.isEmpty() ) {
2234 entry.insert( UDSEntry::UDS_NAME, tmp );
2236 if ( !item.noSelect() ) {
2237 if ( !item.noInferiors() ) {
2238 tmp =
"message/directory";
2240 tmp =
"message/digest";
2242 entry.insert( UDSEntry::UDS_MIME_TYPE, tmp );
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" ) );
2253 entry.insert( UDSEntry::UDS_FILE_TYPE, S_IFDIR );
2255 entry.insert( UDSEntry::UDS_MIME_TYPE, QString::fromLatin1(
"unknown/unknown" ) );
2258 QString path = aURL.path();
2260 if ( path[path.length() - 1] ==
'/' && !path.isEmpty() && path !=
"/" ) {
2261 path.truncate( path.length() - 1 );
2263 if ( !path.isEmpty() && path !=
"/" &&
2264 path.right( hdLen ) != item.hierarchyDelimiter() ) {
2265 path += item.hierarchyDelimiter();
2267 path += mailboxName;
2268 if ( path.toUpper() ==
"/INBOX/" ) {
2270 path = path.toUpper();
2273 aURL.setPath( path );
2274 tmp = aURL.url( KUrl::LeaveTrailingSlash );
2275 entry.insert( UDSEntry::UDS_URL, tmp );
2277 entry.insert( UDSEntry::UDS_USER, myUser );
2279 entry.insert( UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IWUSR );
2281 entry.insert( UDSEntry::UDS_EXTRA, item.attributesAsString() );
2283 listEntry( entry,
false );
2290 QString & _section, QString & _type, QString & _uid,
2291 QString & _validity, QString & _hierarchyDelimiter,
2292 QString & _info,
bool cache)
2294 enum IMAP_TYPE retVal;
2295 retVal = ITYPE_UNKNOWN;
2297 imapParser::parseURL( _url, _box, _section, _type, _uid, _validity, _info );
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;
2308 if ( !_box.isEmpty() ) {
2309 kDebug( 7116 ) <<
"IMAP4::parseURL - box=" << _box;
2311 if ( makeLogin() ) {
2312 if ( getCurrentBox() != _box ||
2315 _type ==
"LSUBNOCHECK" ) {
2318 retVal = ITYPE_DIR_AND_BOX;
2324 if ( cmd->result() ==
"OK" ) {
2325 for ( QList< imapList >::Iterator it = listResponses.begin();
2326 it != listResponses.end(); ++it ) {
2328 if ( _box == ( *it ).name() ) {
2329 if ( !( *it ).hierarchyDelimiter().isEmpty() ) {
2330 _hierarchyDelimiter = ( *it ).hierarchyDelimiter();
2332 if ( ( *it ).noSelect() ) {
2334 }
else if ( ( *it ).noInferiors() ) {
2337 retVal = ITYPE_DIR_AND_BOX;
2342 if ( retVal == ITYPE_UNKNOWN &&
2343 namespaceToDelimiter.contains( _box ) ) {
2347 kDebug( 7116 ) <<
"IMAP4::parseURL - got error for" << _box;
2349 completeQueue.removeAll( cmd );
2355 kDebug( 7116 ) <<
"IMAP4::parseURL: no login!";
2360 kDebug( 7116 ) <<
"IMAP4::parseURL: box [root]";
2365 if ( retVal == ITYPE_BOX || retVal == ITYPE_DIR_AND_BOX ) {
2366 if ( !_uid.isEmpty() ) {
2367 if ( !_uid.contains(
':' ) && !_uid.contains(
',' ) && !_uid.contains(
'*' ) ) {
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;
2379 if ( _hierarchyDelimiter.isEmpty() &&
2380 ( _type ==
"LIST" || _type ==
"LSUB" || _type ==
"LSUBNOCHECK" ) ) {
2383 if ( !_box.isEmpty() ) {
2384 int start = _url.path().lastIndexOf( _box );
2385 if ( start != -1 ) {
2386 _hierarchyDelimiter = _url.path().mid( start - 1, start );
2388 kDebug( 7116 ) <<
"IMAP4::parseURL - reconstructed delimiter:" << _hierarchyDelimiter
2389 <<
"from URL" << _url.path();
2391 if ( _hierarchyDelimiter.isEmpty() ) {
2392 _hierarchyDelimiter =
'/';
2395 kDebug( 7116 ) <<
"IMAP4::parseURL - return" << retVal;
2404 len = _str.length();
2407 if ( cacheOutput ) {
2408 if ( !outputBuffer.isOpen() ) {
2409 outputBuffer.open( QIODevice::WriteOnly );
2411 outputBuffer.seek( outputBufferIndex );
2412 outputBuffer.write( _str.data(), len );
2413 outputBufferIndex += len;
2418 bool relay = relayEnabled;
2420 relayEnabled =
true;
2421 temp = QByteArray::fromRawData( _str.data(), len );
2425 relayEnabled = relay;
2432 if ( outputBufferIndex == 0 ) {
2435 outputBuffer.close();
2436 outputCache.resize( outputBufferIndex );
2437 if ( decodeContent ) {
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 );
2445 decoded = outputCache;
2448 QString mimetype = KMimeType::findByContent( decoded )->name();
2449 kDebug( 7116 ) <<
"IMAP4::flushOutput - mimeType" << mimetype;
2450 mimeType( mimetype );
2451 decodeContent =
false;
2454 data( outputCache );
2456 mProcessedSize += outputBufferIndex;
2457 processedSize( mProcessedSize );
2458 outputBufferIndex = 0;
2459 outputCache[0] =
'\0';
2460 outputBuffer.setBuffer( &outputCache );
2463ssize_t IMAP4Protocol::myRead(
void *data, ssize_t len)
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 );
2474 if ( !isConnected() ) {
2477 waitForResponse( responseTimeout() );
2478 return read( (
char*)data, len );
2482IMAP4Protocol::assureBox (
const QString & aBox,
bool readonly)
2484 if ( aBox.isEmpty() ) {
2490 if ( aBox != getCurrentBox() || ( !getSelected().readWrite() && !readonly ) ) {
2492 kDebug( 7116 ) <<
"IMAP4Protocol::assureBox - opening box";
2493 selectInfo = imapInfo();
2495 bool ok = cmd->result() ==
"OK";
2496 QString cmdInfo = cmd->resultInfo();
2497 completeQueue.removeAll( cmd );
2502 if ( cmd->result() ==
"OK" ) {
2503 for ( QList< imapList >::Iterator it = listResponses.begin();
2504 it != listResponses.end(); ++it ) {
2505 if ( aBox == ( *it ).name() ) {
2510 completeQueue.removeAll( cmd );
2512 if ( cmdInfo.contains(
"permission", Qt::CaseInsensitive ) ) {
2514 error( ERR_ACCESS_DENIED, cmdInfo );
2516 error( ERR_SLAVE_DEFINED, i18n(
"Unable to open folder %1. The server replied: %2",
2520 error( KIO::ERR_DOES_NOT_EXIST, aBox );
2528 kDebug( 7116 ) <<
"IMAP4Protocol::assureBox - reusing box";
2529 if ( mTimeOfLastNoop.secsTo( QDateTime::currentDateTime() ) > 10 ) {
2531 completeQueue.removeAll( cmd );
2532 mTimeOfLastNoop = QDateTime::currentDateTime();
2533 kDebug( 7116 ) <<
"IMAP4Protocol::assureBox - noop timer fired";
2538 if ( !getSelected().readWrite() && !readonly ) {
2539 error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox );
virtual void special(const QByteArray &data)
Capabilities, NOOP, (Un)subscribe, Change status, Change ACL.
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.
virtual void del(const KUrl &_url, bool isFile)
delete a mailbox
virtual void mkdir(const KUrl &url, int permissions)
create a mailbox
virtual bool parseReadLine(QByteArray &buffer, long relay=0)
reimplement the parser
void specialSearchCommand(QDataStream &)
Search current folder, the search string is passed as SECTION.
virtual void stat(const KUrl &_url)
stat a mailbox, message, attachment
void specialAnnotateMoreCommand(int command, QDataStream &stream)
Send an annotation command which is identified by command.
virtual void parseRelay(const QByteArray &buffer)
reimplement the parser relay hook to send the fetched data directly to an upper level
virtual void parseWriteLine(const QString &)
reimplement the parser
void specialACLCommand(int command, QDataStream &stream)
Send an ACL command which is identified by command.
virtual void listDir(const KUrl &_url)
list a directory/mailbox
virtual void flushOutput(const QString &contentEncoding=QString())
send out cached data to the application
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...
virtual int outputLine(const QByteArray &_str, int len=-1)
reimplement the mimeIO
void specialCustomCommand(QDataStream &)
Send a custom command to the server.
virtual bool parseRead(QByteArray &buffer, long len, long relay=0)
reimplement the parser read at least len bytes
encapulate a IMAP command
static CommandPtr clientGetAnnotation(const QString &box, const QString &entry, const QStringList &attributeNames)
Create a GETANNOTATION command.
static CommandPtr clientLogout()
Create a LOGOUT command.
static CommandPtr clientSearch(const QString &search, bool nouid=false)
Create a SEARCH command.
static CommandPtr clientSelect(const QString &path, bool examine=false)
Create a SELECT command.
static CommandPtr clientNoop()
Create a NOOP command.
static CommandPtr clientAppend(const QString &box, const QString &flags, ulong size)
Create a APPEND command.
static CommandPtr clientMyRights(const QString &box)
Create a MYRIGHTS command.
static CommandPtr clientSetAnnotation(const QString &box, const QString &entry, const QMap< QString, QString > &attributes)
Create a SETANNOTATION command.
static CommandPtr clientStore(const QString &set, const QString &item, const QString &data, bool nouid=false)
Create a STORE command.
static CommandPtr clientSubscribe(const QString &path)
Create a SUBSCRIBE command.
static CommandPtr clientList(const QString &reference, const QString &path, bool lsub=false)
Create a LIST command.
static CommandPtr clientCustom(const QString &command, const QString &arguments)
Create a custom command.
static CommandPtr clientFetch(ulong uid, const QString &fields, bool nouid=false)
Create a FETCH command.
static CommandPtr clientNamespace()
Create a NAMESPACE command.
static CommandPtr clientStartTLS()
Create a STARTTLS command.
static CommandPtr clientDeleteACL(const QString &box, const QString &user)
Create a DELETEACL command.
static CommandPtr clientGetQuotaroot(const QString &box)
Create a GETQUOTAROOT command.
static CommandPtr clientUnsubscribe(const QString &path)
Create a UNSUBSCRIBE command.
static CommandPtr clientStatus(const QString &path, const QString ¶meters)
Create a STATUS command.
static CommandPtr clientCreate(const QString &path)
Create a CREATE command.
static CommandPtr clientClose()
Create a CLOSE command.
static CommandPtr clientGetACL(const QString &box)
Create a GETACL command.
static CommandPtr clientRename(const QString &src, const QString &dest)
Create a RENAME command.
static CommandPtr clientExpunge()
Create a EXPUNGE command.
static CommandPtr clientCopy(const QString &box, const QString &sequence, bool nouid=false)
Create a COPY command.
static CommandPtr clientDelete(const QString &path)
Create a DELETE command.
static CommandPtr clientSetACL(const QString &box, const QString &user, const QString &acl)
Create a SETACL command.