68 #include <sys/types.h>
73 #include <sasl/sasl.h>
77 #include <qdatetime.h>
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>
86 #include <kmimetype.h>
91 #include "kdemacros.h"
93 #define IMAP_PROTOCOL "imap"
94 #define IMAP_SSL_PROTOCOL "imaps"
95 const int ImapPort = 143;
96 const int ImapsPort = 993;
102 void sigalrm_handler(
int );
103 KDE_EXPORT
int kdemain(
int argc,
char **argv );
107 kdemain (
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();
140 sigchld_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 );
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),
176 readBuffer[0] = 0x00;
179 IMAP4Protocol::~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 );
213 if ( !assureBox( aBox,
true ) ) {
218 if ( selectInfo.uidValidityAvailable() && !aValidity.
isEmpty() &&
219 selectInfo.uidValidity() != aValidity.
toULong() ) {
221 error( ERR_COULD_NOT_READ, _url.prettyUrl() );
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: " +
264 if ( selectInfo.countAvailable() ) {
265 outputLineStr(
"X-Count: " +
268 if ( selectInfo.unseenAvailable() ) {
269 outputLineStr(
"X-Unseen: " +
272 if ( selectInfo.uidValidityAvailable() ) {
273 outputLineStr(
"X-uidValidity: " +
276 if ( selectInfo.uidNextAvailable() ) {
277 outputLineStr(
"X-UidNext: " +
280 if ( selectInfo.flagsAvailable() ) {
281 outputLineStr(
"X-Flags: " +
284 if ( selectInfo.permanentFlagsAvailable() ) {
285 outputLineStr(
"X-PermanentFlags: " +
288 if ( selectInfo.readWriteAvailable() ) {
289 if ( selectInfo.readWrite() ) {
300 if ( aEnum == ITYPE_MSG || ( aEnum == ITYPE_ATTACH && !decodeContent ) ) {
304 if ( aSequence !=
"0:0" ) {
306 if ( aEnum == ITYPE_ATTACH && decodeContent ) {
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();
331 while ( !( res = parseLoop() ) ) {
338 imapCache *cache = getLastHandled();
340 lastone = cache->getHeader();
343 if ( cmd && !cmd->isComplete() ) {
344 if ( aUpper.
contains(
"BODYSTRUCTURE" ) ||
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: " +
358 if ( cache->getSize() != 0 ) {
359 outputLineStr(
"X-Length: " +
362 if ( !cache->getDate().isEmpty() ) {
366 if ( cache->getFlags() != 0 ) {
367 outputLineStr(
"X-Flags: " +
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 );
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 ) {
426 mySection !=
"FOLDERONLY" ) {
427 listStr += myDelimiter;
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" ) {
454 it != listResponsesSave.
end(); ++it ) {
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;
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" ) {
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 );
515 if ( selectInfo.uidNextAvailable() ) {
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=" +
566 kDebug( 7116 ) <<
"IMAP4::listDir - redirecting to" << newUrl.prettyUrl();
567 redirection( newUrl );
571 if ( selectInfo.count() > 0 ) {
574 if ( selectInfo.uidNextAvailable() ) {
584 bool withSubject = mySection.
isEmpty();
586 mySection =
"UID RFC822.SIZE ENVELOPE";
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() ) {
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";
622 IMAP4Protocol::setHost (
const QString & _host, quint16 _port,
625 if ( myHost != _host || myPort != _port || myUser != _user || myPass != _pass ) {
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() ) {
685 ssize_t relbuf = relay - buffer.
size();
686 int currentRelay = qMin( relbuf, readLen );
693 stream.
open( QIODevice::WriteOnly );
695 stream.
write( buf, readLen );
699 return ( buffer.
size() == len );
710 if ( readBufferLen > 0 ) {
711 while ( copyLen < readBufferLen && readBuffer[copyLen] !=
'\n' ) {
714 if ( copyLen < readBufferLen ) {
720 if ( copyLen < (ssize_t) 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 );
769 IMAP4Protocol::setSubURL (
const KUrl & _url)
771 kDebug( 7116 ) <<
"IMAP4::setSubURL -" << _url.prettyUrl();
772 KIO::TCPSlaveBase::setSubUrl( _url );
776 IMAP4Protocol::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] ==
'/' ) {
791 if ( cmd->result() !=
"OK" ) {
792 error( ERR_COULD_NOT_WRITE, _url.prettyUrl() );
793 completeQueue.removeAll( cmd );
796 completeQueue.removeAll( cmd );
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() ) {
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 );
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 );
942 IMAP4Protocol::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 );
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();
1023 uid = uid.
section(
' ', 2, 3 );
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:
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() ) {
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" ) ) {
1283 if ( hasCapability(
"ANNOTATEMORE" ) ) {
1295 if ( hasCapability(
"QUOTA" ) ) {
1296 specialQuotaCommand( cmd, stream );
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 );
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() ) );
1474 kDebug( 7116 ) <<
"myrights results:" << lst;
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 );
1509 kDebug( 7116 ) <<
"IMAP4Protocol::specialSearchCommand '" << aSection
1510 <<
"' returns" << lst;
1511 infoMessage( lst.join(
" " ) );
1519 kDebug( 7116 ) <<
"IMAP4Protocol::specialCustomCommand" << endl;
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 );
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 );
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 ) {
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 );
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 ) ) );
1653 IMAP4Protocol::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 ) ) );
1695 IMAP4Protocol::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() );
1747 IMAP4Protocol::slave_status()
1749 bool connected = (getState() != ISTATE_NO) && isConnected();
1750 kDebug( 7116 ) <<
"IMAP4::slave_status" << connected;
1751 slaveStatus( connected ? myHost :
QString(), connected );
1755 IMAP4Protocol::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 );
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" ) {
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=" +
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";
1893 void IMAP4Protocol::openConnection()
1895 if ( makeLogin() ) {
1900 void 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();
1922 bool 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";
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",
"" ) ) );
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 ) );
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 ==
"*" ) {
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" ) {
2072 if ( it != listResponses.
end() ) {
2073 namespaceToDelimiter[
QString()] = ( *it ).hierarchyDelimiter();
2074 kDebug( 7116 ) <<
"makeLogin - delimiter for empty ns='" << ( *it ).hierarchyDelimiter() <<
"'";
2075 if ( !hasCapability(
"NAMESPACE" ) ) {
2078 imapNamespaces.
append( nsentry );
2082 completeQueue.removeAll( cmd );
2084 kDebug( 7116 ) <<
"makeLogin - NO login";
2087 return getState() == ISTATE_LOGIN;
2095 int len = writer.
length();
2098 if ( len == 0 || ( writer[len - 1] !=
'\n' ) ) {
2104 write( writer.
data(), len );
2108 IMAP4Protocol::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";
2139 IMAP4Protocol::doListEntry (
const KUrl & _url,
int stretch, imapCache * cache,
2140 bool withFlags,
bool withSubject)
2144 const QString encodedUrl = aURL.url( KUrl::LeaveTrailingSlash );
2145 doListEntry( encodedUrl, stretch, cache, withFlags, withSubject );
2149 IMAP4Protocol::doListEntry (
const QString & encodedUrl,
int stretch, imapCache * cache,
2150 bool withFlags,
bool withSubject)
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() );
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 );
2193 IMAP4Protocol::doListEntry (
const KUrl & _url,
const QString & myBox,
2194 const imapList & item,
bool appendPath)
2199 int hdLen = item.hierarchyDelimiter().length();
2203 QString mailboxName = item.name();
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() ) {
2221 if ( !item.hierarchyDelimiter().isEmpty() &&
2222 mailboxName.
contains( item.hierarchyDelimiter() ) ) {
2223 tmp = mailboxName.
section( item.hierarchyDelimiter(), -1 );
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() ) {
2253 entry.
insert( UDSEntry::UDS_FILE_TYPE, S_IFDIR );
2260 if ( path[path.
length() - 1] ==
'/' && !path.
isEmpty() && path !=
"/" ) {
2263 if ( !path.
isEmpty() && path !=
"/" &&
2264 path.
right( hdLen ) != item.hierarchyDelimiter() ) {
2265 path += item.hierarchyDelimiter();
2267 path += mailboxName;
2268 if ( path.
toUpper() ==
"/INBOX/" ) {
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 );
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;
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" ) {
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 ) {
2372 if ( retVal == ITYPE_MSG ) {
2373 if ( ( _section.
contains(
"BODY.PEEK[", Qt::CaseInsensitive ) ||
2374 _section.
contains(
"BODY[", Qt::CaseInsensitive ) ) &&
2377 retVal = ITYPE_ATTACH;
2379 if ( _hierarchyDelimiter.
isEmpty() &&
2380 ( _type ==
"LIST" || _type ==
"LSUB" || _type ==
"LSUBNOCHECK" ) ) {
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;
2407 if ( cacheOutput ) {
2408 if ( !outputBuffer.
isOpen() ) {
2409 outputBuffer.
open( QIODevice::WriteOnly );
2411 outputBuffer.
seek( outputBufferIndex );
2413 outputBufferIndex += len;
2418 bool relay = relayEnabled;
2420 relayEnabled =
true;
2425 relayEnabled = relay;
2432 if ( outputBufferIndex == 0 ) {
2435 outputBuffer.
close();
2436 outputCache.
resize( outputBufferIndex );
2437 if ( decodeContent ) {
2441 decoded = KCodecs::quotedPrintableDecode( 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';
2463 ssize_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 );
2482 IMAP4Protocol::assureBox (
const QString & aBox,
bool readonly)
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" ) {
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";
2531 completeQueue.removeAll( cmd );
2533 kDebug( 7116 ) <<
"IMAP4Protocol::assureBox - noop timer fired";
2538 if ( !getSelected().readWrite() && !readonly ) {
2539 error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, aBox );
ulong toULong(bool *ok, int base) const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
QString & append(QChar ch)
void truncate(int position)
virtual void listDir(const KUrl &_url)
list a directory/mailbox
QString & prepend(QChar ch)
static CommandPtr clientGetQuotaroot(const QString &box)
Create a GETQUOTAROOT command.
static CommandPtr clientUnsubscribe(const QString &path)
Create a UNSUBSCRIBE command.
virtual void stat(const KUrl &_url)
stat a mailbox, message, attachment
QByteArray fromRawData(const char *data, int size)
static CommandPtr clientList(const QString &reference, const QString &path, bool lsub=false)
Create a LIST command.
static CommandPtr clientRename(const QString &src, const QString &dest)
Create a RENAME command.
QString join(const QString &separator) const
virtual void flushOutput(const QString &contentEncoding=QString())
send out cached data to the application
virtual void del(const KUrl &_url, bool isFile)
delete a mailbox
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.
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
static CommandPtr clientNamespace()
Create a NAMESPACE command.
static CommandPtr clientStore(const QString &set, const QString &item, const QString &data, bool nouid=false)
Create a STORE command.
QString number(int n, int base)
int count(const T &value) const
void append(const T &value)
static CommandPtr clientLogout()
Create a LOGOUT command.
QString & insert(int position, QChar ch)
void specialSearchCommand(QDataStream &)
Search current folder, the search string is passed as SECTION.
static CommandPtr clientCreate(const QString &path)
Create a CREATE command.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
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...
static CommandPtr clientSubscribe(const QString &path)
Create a SUBSCRIBE command.
virtual bool parseRead(QByteArray &buffer, long len, long relay=0)
reimplement the parser read at least len bytes
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
virtual bool open(QFlags< QIODevice::OpenModeFlag > flags)
static CommandPtr clientMyRights(const QString &box)
Create a MYRIGHTS command.
static CommandPtr clientDeleteACL(const QString &box, const QString &user)
Create a DELETEACL command.
QString right(int n) const
static CommandPtr clientGetACL(const QString &box)
Create a GETACL command.
static CommandPtr clientFetch(ulong uid, const QString &fields, bool nouid=false)
Create a FETCH command.
bool contains(QChar ch, Qt::CaseSensitivity cs) const
virtual int outputLine(const QByteArray &_str, int len=-1)
reimplement the mimeIO
static CommandPtr clientClose()
Create a CLOSE command.
QString & replace(int position, int n, QChar after)
static CommandPtr clientSearch(const QString &search, bool nouid=false)
Create a SEARCH command.
static CommandPtr clientExpunge()
Create a EXPUNGE command.
QDateTime currentDateTime()
QString mid(int position, int n) const
void specialCustomCommand(QDataStream &)
Send a custom command to the server.
QByteArray fromBase64(const QByteArray &base64)
int secsTo(const QDateTime &other) const
static CommandPtr clientStartTLS()
Create a STARTTLS command.
static CommandPtr clientCustom(const QString &command, const QString &arguments)
Create a custom command.
virtual void special(const QByteArray &data)
Capabilities, NOOP, (Un)subscribe, Change status, Change ACL.
void specialACLCommand(int command, QDataStream &stream)
Send an ACL command which is identified by command.
void setBuffer(QByteArray *byteArray)
virtual void mkdir(const KUrl &url, int permissions)
create a mailbox
QString section(QChar sep, int start, int end, QFlags< QString::SectionFlag > flags) const
QString left(int n) const
void specialAnnotateMoreCommand(int command, QDataStream &stream)
Send an annotation command which is identified by command.
qint64 write(const char *data, qint64 maxSize)
QString fromLatin1(const char *str, int size)
encapulate a IMAP command
static CommandPtr clientNoop()
Create a NOOP command.
static CommandPtr clientSelect(const QString &path, bool examine=false)
Create a SELECT command.
const_iterator constEnd() const
const_iterator constBegin() const
static CommandPtr clientAppend(const QString &box, const QString &flags, ulong size)
Create a APPEND command.
virtual bool seek(qint64 pos)
virtual void parseWriteLine(const QString &)
reimplement the parser
static CommandPtr clientCopy(const QString &box, const QString &sequence, bool nouid=false)
Create a COPY command.
static CommandPtr clientGetAnnotation(const QString &box, const QString &entry, const QStringList &attributeNames)
Create a GETANNOTATION command.
static CommandPtr clientStatus(const QString &path, const QString ¶meters)
Create a STATUS command.
virtual bool parseReadLine(QByteArray &buffer, long relay=0)
reimplement the parser
static CommandPtr clientSetAnnotation(const QString &box, const QString &entry, const QMap< QString, QString > &attributes)
Create a SETANNOTATION command.
virtual void parseRelay(const QByteArray &buffer)
reimplement the parser relay hook to send the fetched data directly to an upper level ...
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...
QByteArray toUtf8() const