25 #include "imapparser.h"
27 #include "mailheader.h"
28 #include "mimeheader.h"
29 #include "mailaddress.h"
31 #include <sys/types.h>
38 #include <sasl/sasl.h>
44 #include <QStringList>
52 #include <kimap/rfccodecs.h>
53 using namespace KIMAP;
55 static sasl_callback_t callbacks[] = {
56 { SASL_CB_ECHOPROMPT, NULL, NULL },
57 { SASL_CB_NOECHOPROMPT, NULL, NULL },
58 { SASL_CB_GETREALM, NULL, NULL },
59 { SASL_CB_USER, NULL, NULL },
60 { SASL_CB_AUTHNAME, NULL, NULL },
61 { SASL_CB_PASS, NULL, NULL },
62 { SASL_CB_CANON_USER, NULL, NULL },
63 { SASL_CB_LIST_END, NULL, NULL }
66 imapParser::imapParser ()
68 currentState = ISTATE_NO;
73 imapParser::~imapParser ()
80 imapParser::doCommand (CommandPtr aCmd)
84 while ( pl != -1 && !aCmd->isComplete() ) {
85 while ( ( pl = parseLoop() ) == 0 ) {
93 imapParser::sendCommand (CommandPtr aCmd)
95 aCmd->setId( QString::number( commandCounter++ ) );
96 sentQueue.append( aCmd );
98 continuation.resize( 0 );
99 const QString& command = aCmd->command();
101 if ( command ==
"SELECT" || command ==
"EXAMINE" ) {
104 p.fromString( aCmd->parameter() );
105 currentBox = parseOneWord( p );
106 kDebug( 7116 ) <<
"imapParser::sendCommand - setting current box to" << currentBox;
107 }
else if ( command ==
"CLOSE" ) {
110 }
else if ( command.contains(
"SEARCH" ) ||
111 command ==
"GETACL" ||
112 command ==
"LISTRIGHTS" ||
113 command ==
"MYRIGHTS" ||
114 command ==
"GETANNOTATION" ||
115 command ==
"NAMESPACE" ||
116 command ==
"GETQUOTAROOT" ||
117 command ==
"GETQUOTA" ||
118 command ==
"X-GET-OTHER-USERS" ||
119 command ==
"X-GET-DELEGATES" ||
120 command ==
"X-GET-OUT-OF-OFFICE" ) {
122 }
else if ( command ==
"LIST" ||
123 command ==
"LSUB" ) {
124 listResponses.clear();
126 parseWriteLine( aCmd->getStr() );
131 imapParser::clientLogin (
const QString & aUser,
const QString & aPass,
132 QString & resultInfo)
137 cmd = doCommand( CommandPtr(
new imapCommand(
"LOGIN",
"\"" + KIMAP::quoteIMAP( aUser ) +
138 "\" \"" + KIMAP::quoteIMAP( aPass ) +
"\"" ) ) );
140 if ( cmd->result() ==
"OK" ) {
141 currentState = ISTATE_LOGIN;
144 resultInfo = cmd->resultInfo();
145 completeQueue.removeAll( cmd );
149 static bool sasl_interact( KIO::SlaveBase *slave, KIO::AuthInfo &ai,
void *in )
151 kDebug( 7116 ) <<
"sasl_interact";
152 sasl_interact_t *interact = ( sasl_interact_t * ) in;
156 for ( ; interact->id != SASL_CB_LIST_END; interact++ ) {
157 if ( interact->id == SASL_CB_AUTHNAME ||
158 interact->id == SASL_CB_PASS ) {
160 if ( ai.username.isEmpty() || ai.password.isEmpty() ) {
161 if ( !slave->openPasswordDialog( ai ) ) {
169 interact = ( sasl_interact_t * ) in;
170 while ( interact->id != SASL_CB_LIST_END ) {
171 kDebug( 7116 ) <<
"SASL_INTERACT id:" << interact->id;
172 switch ( interact->id ) {
174 case SASL_CB_AUTHNAME:
175 kDebug( 7116 ) <<
"SASL_CB_[USER|AUTHNAME]: '" << ai.username <<
"'";
176 interact->result = strdup( ai.username.toUtf8() );
177 interact->len = strlen( (
const char *) interact->result );
180 kDebug( 7116 ) <<
"SASL_CB_PASS: [hidden]";
181 interact->result = strdup( ai.password.toUtf8() );
182 interact->len = strlen( (
const char *) interact->result );
185 interact->result = 0;
195 imapParser::clientAuthenticate ( KIO::SlaveBase *slave, KIO::AuthInfo &ai,
196 const QString & aFQDN,
const QString & aAuth,
bool isSSL, QString & resultInfo)
200 sasl_conn_t *conn = 0;
201 sasl_interact_t *client_interact = 0;
204 const char *mechusing = 0;
205 QByteArray tmp, challenge;
207 kDebug( 7116 ) <<
"aAuth:" << aAuth <<
" FQDN:" << aFQDN <<
" isSSL:" << isSSL;
210 if ( !hasCapability(
"AUTH=" + aAuth ) ) {
215 result = sasl_client_new(
"imap",
218 0, 0, callbacks, 0, &conn );
220 if ( result != SASL_OK ) {
221 kDebug( 7116 ) <<
"sasl_client_new failed with:" << result;
222 resultInfo = QString::fromUtf8( sasl_errdetail( conn ) );
227 result = sasl_client_start( conn, aAuth.toLatin1(), &client_interact,
228 hasCapability(
"SASL-IR" ) ? &out : 0, &outlen, &mechusing );
230 if ( result == SASL_INTERACT ) {
231 if ( !sasl_interact( slave, ai, client_interact ) ) {
232 sasl_dispose( &conn );
236 }
while ( result == SASL_INTERACT );
238 if ( result != SASL_CONTINUE && result != SASL_OK ) {
239 kDebug( 7116 ) <<
"sasl_client_start failed with:" << result;
240 resultInfo = QString::fromUtf8( sasl_errdetail( conn ) );
241 sasl_dispose( &conn );
246 tmp = QByteArray::fromRawData( out, outlen );
247 challenge = tmp.toBase64();
250 QString firstCommand = aAuth;
251 if ( !challenge.isEmpty() ) {
253 firstCommand += QString::fromLatin1( challenge.data(), challenge.size() );
255 cmd = sendCommand( CommandPtr(
new imapCommand(
"AUTHENTICATE", firstCommand.toLatin1() ) ) );
258 while ( pl != -1 && !cmd->isComplete() ) {
260 while ( ( pl = parseLoop() ) == 0 ) {
264 if ( !continuation.isEmpty() ) {
266 if ( continuation.size() > 4 ) {
267 tmp = QByteArray::fromRawData( continuation.data() + 2, continuation.size() - 4 );
268 challenge = QByteArray::fromBase64( tmp );
274 result = sasl_client_step( conn, challenge.isEmpty() ? 0 : challenge.data(),
279 if ( result == SASL_INTERACT ) {
280 if ( !sasl_interact( slave, ai, client_interact ) ) {
281 sasl_dispose( &conn );
285 }
while ( result == SASL_INTERACT );
287 if ( result != SASL_CONTINUE && result != SASL_OK ) {
288 kDebug( 7116 ) <<
"sasl_client_step failed with:" << result;
289 resultInfo = QString::fromUtf8( sasl_errdetail( conn ) );
290 sasl_dispose( &conn );
294 tmp = QByteArray::fromRawData( out, outlen );
296 challenge = tmp.toBase64();
299 parseWriteLine( challenge );
300 continuation.resize( 0 );
304 if ( cmd->result() ==
"OK" ) {
305 currentState = ISTATE_LOGIN;
308 resultInfo = cmd->resultInfo();
309 completeQueue.removeAll( cmd );
311 sasl_dispose( &conn );
320 parseOneWord( result );
321 QByteArray what = parseLiteral( result );
326 if ( qstrncmp( what,
"BAD", what.size() ) == 0 ) {
327 parseResult( what, result );
328 }
else if ( qstrncmp( what,
"BYE", what.size() ) == 0 ) {
329 parseResult( what, result );
330 if ( sentQueue.count() ) {
332 CommandPtr current = sentQueue.at( 0 );
333 current->setResultInfo( result.cstr() );
335 currentState = ISTATE_NO;
340 if ( what[1] ==
'O' && what.size() == 2 ) {
341 parseResult( what, result );
342 }
else if ( qstrncmp( what,
"NAMESPACE", what.size() ) == 0 ) {
343 parseNamespace( result );
348 if ( what[1] ==
'K' && what.size() == 2 ) {
349 parseResult( what, result );
350 }
else if ( qstrncmp( what,
"OTHER-USER", 10 ) == 0 ) {
351 parseOtherUser( result );
352 }
else if ( qstrncmp( what,
"OUT-OF-OFFICE", 13 ) == 0 ) {
353 parseOutOfOffice( result );
357 if ( qstrncmp( what,
"DELEGATE", 8 ) == 0 ) {
358 parseDelegate( result );
363 if ( qstrncmp( what,
"PREAUTH", what.size() ) == 0 ) {
364 parseResult( what, result );
365 currentState = ISTATE_LOGIN;
371 if ( qstrncmp( what,
"CAPABILITY", what.size() ) == 0 ) {
372 parseCapability( result );
377 if ( qstrncmp( what,
"FLAGS", what.size() ) == 0 ) {
378 parseFlags( result );
383 if ( qstrncmp( what,
"LIST", what.size() ) == 0 ) {
385 }
else if ( qstrncmp( what,
"LSUB", what.size() ) == 0 ) {
387 }
else if ( qstrncmp( what,
"LISTRIGHTS", what.size() ) == 0 ) {
388 parseListRights( result );
393 if ( qstrncmp( what,
"MYRIGHTS", what.size() ) == 0 ) {
394 parseMyRights( result );
398 if ( qstrncmp( what,
"SEARCH", what.size() ) == 0 ) {
399 parseSearch( result );
400 }
else if ( qstrncmp( what,
"STATUS", what.size() ) == 0 ) {
401 parseStatus( result );
406 if ( qstrncmp( what,
"ACL", what.size() ) == 0 ) {
408 }
else if ( qstrncmp( what,
"ANNOTATION", what.size() ) == 0 ) {
409 parseAnnotation( result );
413 if ( what.size() > 5 && qstrncmp( what,
"QUOTAROOT", what.size() ) == 0 ) {
414 parseQuotaRoot( result );
415 }
else if ( qstrncmp( what,
"QUOTA", what.size() ) == 0 ) {
416 parseQuota( result );
421 parseCustom( result );
430 number = what.toUInt( &valid );
432 what = parseLiteral( result );
435 if ( qstrncmp( what,
"EXISTS", what.size() ) == 0 ) {
436 parseExists( number, result );
437 }
else if ( qstrncmp( what,
"EXPUNGE", what.size() ) == 0 ) {
438 parseExpunge( number, result );
443 if ( qstrncmp( what,
"FETCH", what.size() ) == 0 ) {
445 parseFetch( number, result );
450 if ( qstrncmp( what,
"STORE", what.size() ) == 0 ) {
452 parseFetch( number, result );
457 if ( qstrncmp( what,
"RECENT", what.size() ) == 0 ) {
458 parseRecent( number, result );
471 imapParser::parseResult (QByteArray & result,
parseString & rest,
472 const QString & command)
474 if ( command ==
"SELECT" ) {
475 selectInfo.setReadWrite(
true );
478 if ( rest[0] ==
'[' ) {
480 QByteArray option = parseOneWord( rest,
true );
482 switch ( option[0] ) {
484 if ( option ==
"ALERT" ) {
485 rest.pos = rest.data.indexOf(
']', rest.pos ) + 1;
488 selectInfo.setAlert( rest.cstr() );
493 if ( option ==
"NEWNAME" ) {
498 if ( option ==
"PARSE" ) {
499 }
else if ( option ==
"PERMANENTFLAGS" ) {
500 uint end = rest.data.indexOf(
']', rest.pos );
501 QByteArray flags( rest.data.data() + rest.pos, end - rest.pos );
502 selectInfo.setPermanentFlags( flags );
508 if ( option ==
"READ-ONLY" ) {
509 selectInfo.setReadWrite(
false );
510 }
else if ( option ==
"READ-WRITE" ) {
511 selectInfo.setReadWrite(
true );
516 if ( option ==
"TRYCREATE" ) {
521 if ( option ==
"UIDVALIDITY" ) {
523 if ( parseOneNumber( rest, value ) ) {
524 selectInfo.setUidValidity( value );
526 }
else if ( option ==
"UNSEEN" ) {
528 if ( parseOneNumber( rest, value ) ) {
529 selectInfo.setUnseen( value );
531 }
else if ( option ==
"UIDNEXT" ) {
533 if ( parseOneNumber( rest, value ) ) {
534 selectInfo.setUidNext( value );
540 if ( rest[0] ==
']' ) {
546 if ( command.isEmpty() ) {
552 switch ( command[0].toLatin1() ) {
554 if ( command ==
"AUTHENTICATE" ) {
555 if ( qstrncmp( result,
"OK", result.size() ) == 0 ) {
556 currentState = ISTATE_LOGIN;
562 if ( command ==
"LOGIN" ) {
563 if ( qstrncmp( result,
"OK", result.size() ) == 0 ) {
564 currentState = ISTATE_LOGIN;
570 if ( command ==
"EXAMINE" ) {
571 if ( qstrncmp( result,
"OK", result.size() ) == 0 ) {
572 currentState = ISTATE_SELECT;
574 if ( currentState == ISTATE_SELECT ) {
575 currentState = ISTATE_LOGIN;
579 kDebug( 7116 ) <<
"imapParser::parseResult - current box is now" << currentBox;
584 if ( command ==
"SELECT" ) {
585 if ( qstrncmp( result,
"OK", result.size() ) == 0 ) {
586 currentState = ISTATE_SELECT;
588 if ( currentState == ISTATE_SELECT ) {
589 currentState = ISTATE_LOGIN;
593 kDebug( 7116 ) <<
"imapParser::parseResult - current box is now" << currentBox;
602 void imapParser::parseCapability (
parseString & result)
604 QByteArray data = result.cstr();
605 kAsciiToLower( data.data() );
606 imapCapabilities = QString::fromLatin1( data ).split(
' ', QString::SkipEmptyParts );
611 selectInfo.setFlags( result.cstr() );
618 if ( result[0] !=
'(' ) {
624 this_one.parseAttributes( result );
629 this_one.setHierarchyDelimiter( parseLiteral( result ) );
630 this_one.setName( QString::fromUtf8( KIMAP::decodeImapFolderName( parseLiteral( result ) ) ) );
632 listResponses.append( this_one );
637 imapList this_one( result.cstr(), *this );
638 listResponses.append( this_one );
641 void imapParser::parseListRights (
parseString & result)
643 parseOneWord( result );
644 parseOneWord( result );
646 const QByteArray word = parseOneWord( result );
647 if ( word.isEmpty() ) {
650 lastResults.append( word );
656 parseOneWord( result );
658 while ( !result.isEmpty() ) {
659 const QByteArray word = parseLiteral( result );
660 if ( word.isEmpty() ) {
663 lastResults.append( word );
667 void imapParser::parseAnnotation (
parseString & result)
669 parseOneWord( result );
671 parseOneWord( result );
673 if ( result.isEmpty() || result[0] !=
'(' ) {
679 while ( !result.isEmpty() && result[0] !=
')' ) {
680 const QByteArray word = parseLiteral( result );
681 if ( word.isEmpty() ) {
684 lastResults.append( word );
693 QByteArray root = parseOneWord( result );
694 if ( root.isEmpty() ) {
695 lastResults.append(
"" );
697 lastResults.append( root );
699 if ( result.isEmpty() || result[0] !=
'(' ) {
705 while ( !result.isEmpty() && result[0] !=
')' ) {
706 const QByteArray word = parseLiteral( result );
707 if ( word.isEmpty() ) {
710 triplet.append( word );
712 lastResults.append( triplet.join(
" " ) );
715 void imapParser::parseQuotaRoot (
parseString & result)
719 parseOneWord( result );
721 if ( result.isEmpty() ) {
725 while ( !result.isEmpty() ) {
726 const QByteArray word = parseLiteral( result );
727 if ( word.isEmpty() ) {
730 roots.append( word );
732 lastResults.append( roots.isEmpty() ?
"" : roots.join(
" " ) );
735 void imapParser::parseCustom (
parseString & result)
737 QByteArray word = parseLiteral( result,
false,
false );
738 lastResults.append( word );
741 void imapParser::parseOtherUser (
parseString & result)
743 lastResults.append( parseOneWord( result ) );
746 void imapParser::parseDelegate (
parseString & result)
748 const QString email = parseOneWord( result );
751 while ( !result.isEmpty() ) {
752 QByteArray word = parseLiteral( result,
false,
false );
753 rights.append( word );
756 lastResults.append( email +
':' + rights.join(
"," ) );
759 void imapParser::parseOutOfOffice (
parseString & result)
761 const QString state = parseOneWord( result );
762 parseOneWord( result );
764 QByteArray msg = parseLiteral( result,
false,
false );
766 lastResults.append( state +
'^' + QString::fromUtf8( msg ) );
769 void imapParser::parseMyRights (
parseString & result)
771 parseOneWord( result );
772 Q_ASSERT( lastResults.isEmpty() );
773 lastResults.append( parseOneWord( result ) );
776 void imapParser::parseSearch (
parseString & result)
780 while ( parseOneNumber( result, value ) ) {
781 lastResults.append( QString::number( value ) );
785 void imapParser::parseStatus (
parseString & inWords)
787 lastStatus = imapInfo();
789 parseLiteral( inWords );
790 if ( inWords[0] !=
'(' ) {
797 while ( !inWords.isEmpty() && inWords[0] !=
')' ) {
800 QByteArray label = parseOneWord( inWords );
801 if ( parseOneNumber( inWords, value ) ) {
802 if ( label ==
"MESSAGES" ) {
803 lastStatus.setCount( value );
804 }
else if ( label ==
"RECENT" ) {
805 lastStatus.setRecent( value );
806 }
else if ( label ==
"UIDVALIDITY" ) {
807 lastStatus.setUidValidity( value );
808 }
else if ( label ==
"UNSEEN" ) {
809 lastStatus.setUnseen( value );
810 }
else if ( label ==
"UIDNEXT" ) {
811 lastStatus.setUidNext( value );
816 if ( inWords[0] ==
')' ) {
822 void imapParser::parseExists (ulong value,
parseString & result)
824 selectInfo.setCount( value );
825 result.pos = result.data.size();
828 void imapParser::parseExpunge (ulong value,
parseString & result)
834 void imapParser::parseAddressList (
parseString & inWords, QList<mailAddress *>& list)
836 if ( inWords.isEmpty() ) {
839 if ( inWords[0] !=
'(' ) {
840 parseOneWord( inWords );
845 while ( !inWords.isEmpty() && inWords[0] !=
')' ) {
846 if ( inWords[0] ==
'(' ) {
847 mailAddress *addr =
new mailAddress;
848 parseAddress( inWords, *addr );
855 if ( !inWords.isEmpty() && inWords[0] ==
')' ) {
862 const mailAddress& imapParser::parseAddress (
parseString & inWords, mailAddress& retVal)
867 retVal.setFullName( parseLiteral( inWords ) );
868 retVal.setCommentRaw( parseLiteral( inWords ) );
869 retVal.setUser( parseLiteral( inWords ) );
870 retVal.setHost( parseLiteral( inWords ) );
872 if ( !inWords.isEmpty() && inWords[0] ==
')' ) {
883 if ( inWords[0] !=
'(' ) {
892 envelope->
setDate( parseLiteral( inWords ) );
895 envelope->
setSubject( parseLiteral( inWords ) );
897 QList<mailAddress *> list;
900 parseAddressList( inWords, list );
901 if ( !list.isEmpty() ) {
902 envelope->setFrom( *list.last() );
907 parseAddressList(inWords, list);
908 if ( !list.isEmpty() ) {
909 envelope->setSender( *list.last() );
914 parseAddressList( inWords, list );
915 if ( !list.isEmpty() ) {
916 envelope->setReplyTo( *list.last() );
921 parseAddressList( inWords, envelope->to() );
924 parseAddressList( inWords, envelope->cc() );
927 parseAddressList( inWords, envelope->bcc() );
930 envelope->setInReplyTo( parseLiteral( inWords ) );
933 envelope->setMessageId( parseLiteral( inWords ) );
936 while ( !inWords.isEmpty() && inWords[0] !=
')' ) {
938 if ( inWords[0] ==
'(' ) {
939 parseSentence( inWords );
941 parseLiteral( inWords );
945 if ( !inWords.isEmpty() && inWords[0] ==
')' ) {
954 QHash < QByteArray, QString > imapParser::parseDisposition (
parseString & inWords)
956 QByteArray disposition;
957 QHash < QByteArray, QString > retVal;
959 if ( inWords[0] !=
'(' ) {
961 disposition = parseOneWord( inWords );
967 disposition = parseOneWord( inWords );
969 retVal = parseParameters( inWords );
970 if ( inWords[0] !=
')' ) {
977 if ( !disposition.isEmpty() ) {
978 retVal.insert(
"content-disposition", QString( disposition ) );
985 QHash < QByteArray, QString > imapParser::parseParameters (
parseString & inWords)
987 QHash < QByteArray, QString > retVal;
989 if ( inWords[0] !=
'(' ) {
991 parseOneWord( inWords );
996 while ( !inWords.isEmpty() && inWords[0] !=
')' ) {
997 const QByteArray l1 = parseLiteral( inWords );
998 const QByteArray l2 = parseLiteral( inWords );
999 retVal.insert( l1.toLower(), QString( l2 ) );
1002 if ( inWords[0] !=
')' ) {
1016 QHash < QByteArray, QString > parameters;
1019 if ( inWords[0] !=
'(' ) {
1027 localPart->setPartSpecifier( inSection );
1033 typeStr = parseLiteral( inWords );
1036 subtype = parseLiteral( inWords );
1038 localPart->setType( typeStr +
'/' + subtype );
1041 parameters = parseParameters( inWords );
1043 QHashIterator < QByteArray, QString > it( parameters );
1045 while ( it.hasNext() ) {
1047 localPart->setTypeParm( it.key(), it.value() );
1053 localPart->setID( parseLiteral( inWords ) );
1056 localPart->setDescription( parseLiteral( inWords ) );
1059 localPart->setEncoding( parseLiteral( inWords ) );
1062 if ( parseOneNumber( inWords, size ) ) {
1063 localPart->setLength( size );
1067 if ( localPart->getType().toUpper() ==
"MESSAGE/RFC822" ) {
1069 mailHeader *envelope = parseEnvelope( inWords );
1072 parseBodyStructure( inWords, inSection, envelope );
1074 localPart->setNestedMessage( envelope );
1078 parseOneNumber( inWords, lines );
1080 if ( typeStr ==
"TEXT" ) {
1083 parseOneNumber( inWords, lines );
1087 parseLiteral( inWords );
1090 parameters = parseDisposition( inWords );
1092 QString disposition = parameters[
"content-disposition"];
1094 localPart->setDisposition( disposition.toLatin1() );
1095 QHashIterator < QByteArray, QString > it( parameters );
1096 while ( it.hasNext() ) {
1098 localPart->setDispositionParm( it.key(), it.value() );
1104 parseSentence( inWords );
1108 while ( !inWords.isEmpty() && inWords[0] !=
')' ) {
1110 if ( inWords[0] ==
'(' ) {
1111 parseSentence( inWords );
1113 parseLiteral( inWords );
1117 if ( inWords[0] ==
')' ) {
1128 if ( inSection.isEmpty() ) {
1136 if ( inWords[0] !=
'(' ) {
1138 parseOneWord( inWords );
1144 if ( inWords[0] ==
'(' ) {
1146 QHash< QByteArray, QString > parameters;
1153 localPart->clearNestedParts();
1154 localPart->clearTypeParameters();
1155 localPart->clearDispositionParameters();
1157 outSection = inSection +
".HEADER";
1159 if ( inWords[0] ==
'(' && init ) {
1164 if ( !outSection.isEmpty() ) {
1165 localPart->setPartSpecifier( outSection );
1167 localPart->setPartSpecifier( inSection );
1171 while ( inWords[0] ==
'(' ) {
1172 outSection = QString::number( ++section );
1174 outSection = inSection +
'.' + outSection;
1176 mimeHeader *subpart = parseBodyStructure( inWords, outSection, 0 );
1177 localPart->addNestedPart( subpart );
1181 subtype = parseOneWord( inWords );
1183 localPart->setType(
"MULTIPART/" + subtype );
1186 parameters = parseParameters( inWords );
1188 QHashIterator < QByteArray, QString > it( parameters );
1190 while ( it.hasNext() ) {
1192 localPart->setTypeParm( it.key(), it.value() );
1198 parameters = parseDisposition( inWords );
1200 QString disposition = parameters[
"content-disposition"];
1202 localPart->setDisposition( disposition.toLatin1() );
1203 QHashIterator < QByteArray, QString > it( parameters );
1204 while ( it.hasNext() ) {
1206 localPart->setDispositionParm( it.key(), it.value() );
1212 parseSentence( inWords );
1217 inWords.data[inWords.pos] =
'(';
1219 inSection = inSection +
".1";
1221 localPart = parseSimplePart( inWords, inSection, localPart );
1223 inWords.data[inWords.pos] =
')';
1227 while ( !inWords.isEmpty() && inWords[0] !=
')' ) {
1229 if ( inWords[0] ==
'(' ) {
1230 parseSentence( inWords );
1232 parseLiteral( inWords );
1236 if ( inWords[0] ==
')' ) {
1243 void imapParser::parseBody (
parseString & inWords)
1246 if ( inWords[0] ==
'[' ) {
1247 QByteArray specifier;
1251 specifier = parseOneWord( inWords,
true );
1253 if ( inWords[0] ==
'(' ) {
1256 while ( !inWords.isEmpty() && inWords[0] !=
')' ) {
1257 label = parseOneWord( inWords );
1260 if ( inWords[0] ==
')' ) {
1264 if ( inWords[0] ==
']' ) {
1270 if ( qstrncmp( specifier,
"0", specifier.size() ) == 0 ) {
1272 if ( lastHandled ) {
1273 envelope = lastHandled->getHeader();
1276 if ( !envelope || seenUid.isEmpty() ) {
1277 kDebug( 7116 ) <<
"imapParser::parseBody - discarding" << envelope << seenUid.toLatin1();
1279 parseLiteral( inWords,
true );
1281 kDebug( 7116 ) <<
"imapParser::parseBody - reading" << envelope << seenUid.toLatin1();
1283 QString theHeader = parseLiteral( inWords,
true );
1286 myIO.setString( theHeader );
1287 envelope->parseHeader( myIO );
1289 }
else if ( qstrncmp( specifier,
"HEADER.FIELDS", specifier.size() ) == 0 ) {
1293 if ( qstrncmp( label,
"REFERENCES", label.size() ) == 0 ) {
1295 if ( lastHandled ) {
1296 envelope = lastHandled->getHeader();
1299 if ( !envelope || seenUid.isEmpty() ) {
1300 kDebug( 7116 ) <<
"imapParser::parseBody - discarding" << envelope << seenUid.toLatin1();
1302 parseLiteral( inWords,
true );
1304 QByteArray references = parseLiteral( inWords,
true );
1305 int start = references.indexOf(
'<' );
1306 int end = references.lastIndexOf(
'>' );
1307 if ( start < end ) {
1308 references = references.mid( start, end - start + 1 );
1310 envelope->setReferences( references.simplified() );
1313 parseLiteral( inWords,
true );
1316 if ( specifier.contains(
".MIME" ) ) {
1318 QString theHeader = parseLiteral( inWords,
false );
1320 myIO.setString( theHeader );
1321 envelope->parseHeader( myIO );
1322 if ( lastHandled ) {
1323 lastHandled->setHeader( envelope );
1328 kDebug( 7116 ) <<
"imapParser::parseBody - discarding" << seenUid.toLatin1();
1329 parseLiteral( inWords,
true );
1333 if ( lastHandled ) {
1334 envelope = lastHandled->getHeader();
1337 if ( !envelope || seenUid.isEmpty() ) {
1338 kDebug( 7116 ) <<
"imapParser::parseBody - discarding" << envelope << seenUid.toLatin1();
1340 parseSentence( inWords );
1342 kDebug( 7116 ) <<
"imapParser::parseBody - reading" << envelope << seenUid.toLatin1();
1345 mimeHeader *body = parseBodyStructure( inWords, section, envelope );
1346 if ( body != envelope ) {
1353 void imapParser::parseFetch (ulong ,
parseString & inWords)
1355 if ( inWords[0] !=
'(' ) {
1364 while ( !inWords.isEmpty() && inWords[0] !=
')' ) {
1365 if ( inWords[0] ==
'(' ) {
1366 parseSentence( inWords );
1368 const QByteArray word = parseLiteral( inWords,
false,
true );
1370 switch ( word[0] ) {
1372 if ( word ==
"ENVELOPE" ) {
1375 if ( lastHandled ) {
1376 envelope = lastHandled->getHeader();
1378 lastHandled =
new imapCache();
1381 if ( envelope && !envelope->getMessageId().isEmpty() ) {
1384 parseSentence( inWords );
1386 envelope = parseEnvelope( inWords );
1388 envelope->setPartSpecifier( seenUid +
".0" );
1389 lastHandled->setHeader( envelope );
1390 lastHandled->setUid( seenUid.toULong() );
1397 if ( word ==
"BODY" ) {
1398 parseBody( inWords );
1399 }
else if ( word ==
"BODY[]" ) {
1401 parseLiteral( inWords,
true );
1402 }
else if ( word ==
"BODYSTRUCTURE" ) {
1405 if ( lastHandled ) {
1406 envelope = lastHandled->getHeader();
1411 mimeHeader *body = parseBodyStructure( inWords, section, envelope );
1413 QDataStream stream( &data, QIODevice::WriteOnly );
1415 body->serialize( stream );
1423 if ( word ==
"UID" ) {
1424 seenUid = parseOneWord( inWords );
1426 if ( lastHandled ) {
1427 envelope = lastHandled->getHeader();
1429 lastHandled =
new imapCache();
1432 if ( seenUid.isEmpty() ) {
1434 kDebug( 7116 ) <<
"imapParser::parseFetch - UID empty";
1436 lastHandled->setUid( seenUid.toULong() );
1439 envelope->setPartSpecifier( seenUid );
1445 if ( word ==
"RFC822.SIZE" ) {
1447 parseOneNumber( inWords, size );
1449 if ( !lastHandled ) {
1450 lastHandled =
new imapCache();
1452 lastHandled->setSize( size );
1453 }
else if ( word.startsWith(
"RFC822" ) ) {
1455 parseLiteral( inWords,
true );
1460 if ( word ==
"INTERNALDATE" ) {
1461 const QByteArray date = parseOneWord( inWords );
1462 if ( !lastHandled ) {
1463 lastHandled =
new imapCache();
1465 lastHandled->setDate( date );
1470 if ( word ==
"FLAGS" ) {
1472 if ( !lastHandled ) {
1473 lastHandled =
new imapCache();
1475 lastHandled->setFlags( imapInfo::_flags( inWords.cstr() ) );
1480 parseLiteral( inWords );
1487 while ( !inWords.isEmpty() && inWords[0] !=
')' ) {
1489 if ( inWords[0] ==
'(' ) {
1490 parseSentence( inWords );
1492 parseLiteral( inWords );
1496 if ( inWords.isEmpty() || inWords[0] !=
')' ) {
1504 void imapParser::parseSentence (
parseString & inWords)
1511 while ( !inWords.isEmpty() && ( stack != 0 || first ) ) {
1515 unsigned char ch = inWords[0];
1534 parseLiteral( inWords );
1542 void imapParser::parseRecent (ulong value,
parseString & result)
1544 selectInfo.setRecent( value );
1545 result.pos = result.data.size();
1548 void imapParser::parseNamespace (
parseString & result)
1550 if ( result[0] !=
'(' ) {
1555 if ( namespaceToDelimiter.contains( QString() ) ) {
1556 delimEmpty = namespaceToDelimiter[QString()];
1559 namespaceToDelimiter.clear();
1560 imapNamespaces.clear();
1564 bool personalAvailable =
false;
1565 while ( !result.isEmpty() ) {
1566 if ( result[0] ==
'(' ) {
1568 if ( result[0] ==
'(' ) {
1574 QString prefix = QString::fromLatin1( parseOneWord( result ) );
1576 QString delim = QString::fromLatin1( parseOneWord( result ) );
1577 kDebug( 7116 ) <<
"imapParser::parseNamespace ns='" << prefix <<
"',delim='" << delim <<
"'";
1580 personalAvailable =
true;
1582 QString nsentry = QString::number( ns ) +
'=' + prefix +
'=' + delim;
1583 imapNamespaces.append( nsentry );
1584 if ( prefix.right( 1 ) == delim ) {
1586 prefix.resize( prefix.length() );
1588 namespaceToDelimiter[prefix] = delim;
1592 }
else if ( result[0] ==
')' ) {
1595 }
else if ( result[0] ==
'N' ) {
1598 parseOneWord( result );
1601 parseOneWord( result );
1604 if ( !delimEmpty.isEmpty() ) {
1606 namespaceToDelimiter[QString()] = delimEmpty;
1607 if ( !personalAvailable ) {
1609 kDebug( 7116 ) <<
"imapParser::parseNamespace - registering own personal ns";
1610 QString nsentry =
"0==" + delimEmpty;
1611 imapNamespaces.append( nsentry );
1616 int imapParser::parseLoop ()
1620 if ( !parseReadLine( result.data ) ) {
1626 if ( result.data.isEmpty() ) {
1629 if ( !sentQueue.count() ) {
1631 kDebug( 7116 ) <<
"imapParser::parseLoop - unhandledResponse:" << result.cstr();
1632 unhandled << result.cstr();
1634 CommandPtr current = sentQueue.at( 0 );
1635 switch ( result[0] ) {
1637 result.data.resize( result.data.size() - 2 );
1638 parseUntagged( result );
1641 continuation = result.data;
1645 QByteArray tag = parseLiteral( result );
1646 if ( current->id() == tag.data() ) {
1647 result.data.resize( result.data.size() - 2 );
1648 QByteArray resultCode = parseLiteral( result );
1649 current->setResult( resultCode );
1650 current->setResultInfo( result.cstr() );
1651 current->setComplete();
1653 sentQueue.removeAll( current );
1654 completeQueue.append( current );
1655 if ( result.length() ) {
1656 parseResult( resultCode, result, current->command() );
1659 kDebug( 7116 ) <<
"imapParser::parseLoop - unknown tag '" << tag <<
"'";
1660 QByteArray cstr = tag +
' ' + result.cstr();
1663 result.data.resize( cstr.length() );
1673 imapParser::parseRelay (
const QByteArray & buffer)
1676 qWarning(
"imapParser::parseRelay - virtual function not reimplemented - data lost" );
1680 imapParser::parseRelay (ulong len)
1683 qWarning(
"imapParser::parseRelay - virtual function not reimplemented - announcement lost" );
1686 bool imapParser::parseRead (QByteArray & buffer,
long len,
long relay)
1691 qWarning(
"imapParser::parseRead - virtual function not reimplemented - no data read" );
1695 bool imapParser::parseReadLine (QByteArray & buffer,
long relay)
1699 qWarning(
"imapParser::parseReadLine - virtual function not reimplemented - no data read" );
1704 imapParser::parseWriteLine (
const QString & str)
1707 qWarning(
"imapParser::parseWriteLine - virtual function not reimplemented - no data written" );
1711 imapParser::parseURL (
const KUrl & _url, QString & _box, QString & _section,
1712 QString & _type, QString & _uid, QString & _validity, QString & _info)
1714 QStringList parameters;
1717 kDebug( 7116 ) <<
"imapParser::parseURL" << _box;
1718 int paramStart = _box.indexOf(
"/;" );
1719 if ( paramStart > -1 ) {
1720 QString paramString = _box.right( _box.length() - paramStart - 2 );
1721 parameters = paramString.split(
';', QString::SkipEmptyParts );
1722 _box.truncate( paramStart );
1725 for ( QStringList::ConstIterator it( parameters.constBegin() );
1726 it != parameters.constEnd(); ++it ) {
1727 QString temp = ( *it );
1730 int pt = temp.indexOf(
'/' );
1732 temp.truncate( pt );
1734 if ( temp.startsWith( QLatin1String(
"section=" ), Qt::CaseInsensitive ) ) {
1735 _section = temp.right( temp.length() - 8 );
1736 }
else if ( temp.startsWith( QLatin1String(
"type=" ), Qt::CaseInsensitive ) ) {
1737 _type = temp.right( temp.length() - 5 );
1738 }
else if ( temp.startsWith( QLatin1String(
"uid=" ), Qt::CaseInsensitive ) ) {
1739 _uid = temp.right( temp.length() - 4 );
1740 }
else if ( temp.startsWith( QLatin1String(
"uidvalidity=" ), Qt::CaseInsensitive ) ) {
1741 _validity = temp.right( temp.length() - 12 );
1742 }
else if ( temp.startsWith( QLatin1String(
"info=" ), Qt::CaseInsensitive ) ) {
1743 _info = temp.right( temp.length() - 5 );
1751 if ( !_box.isEmpty() ) {
1753 if ( _box[0] ==
'/' ) {
1754 _box = _box.right( _box.length() - 1 );
1756 if ( !_box.isEmpty() && _box[_box.length() - 1] ==
'/' ) {
1757 _box.truncate( _box.length() - 1 );
1760 kDebug( 7116 ) <<
"URL: box=" << _box <<
", section=" << _section <<
", type="
1761 << _type <<
", uid=" << _uid <<
", validity=" << _validity
1762 <<
", info=" << _info;
1766 QByteArray imapParser::parseLiteral(
parseString & inWords,
bool relay,
bool stopAtBracket) {
1768 if ( !inWords.isEmpty() && inWords[0] ==
'{' ) {
1770 int runLen = inWords.find(
'}', 1);
1773 long runLenSave = runLen + 1;
1774 QByteArray tmpstr( runLen,
'\0' );
1775 inWords.takeMidNoResize( tmpstr, 1, runLen - 1 );
1776 runLen = tmpstr.toULong( &proper );
1777 inWords.pos += runLenSave;
1781 parseRelay( runLen );
1784 parseRead( rv, runLen, relay ? runLen : 0 );
1785 rv.resize( qMax( runLen, rv.size() ) );
1788 parseReadLine( inWords.data );
1793 kDebug( 7116 ) <<
"imapParser::parseLiteral - error parsing {} -" ;
1797 kDebug( 7116 ) <<
"imapParser::parseLiteral - error parsing unmatched {";
1802 return parseOneWord( inWords, stopAtBracket );
1806 QByteArray imapParser::parseOneWord (
parseString & inWords,
bool stopAtBracket)
1808 uint len = inWords.length();
1810 return QByteArray();
1813 if ( len > 0 && inWords[0] ==
'"' ) {
1816 while ( i < len && ( inWords[i] !=
'"' || quote ) ) {
1817 if ( inWords[i] ==
'\\' ) {
1828 inWords.takeLeftNoResize( retVal, i - 1 );
1831 for (
unsigned int j = 0; j < len; j++ ) {
1832 if ( retVal[j] ==
'\\' ) {
1836 retVal[j - offset] = retVal[j];
1838 retVal.resize( len - offset );
1843 kDebug( 7116 ) <<
"imapParser::parseOneWord - error parsing unmatched \"";
1844 QByteArray retVal = inWords.cstr();
1852 for ( i = 0; i < len; ++i ) {
1853 char ch = inWords[i];
1854 if ( ch <=
' ' || ch ==
'(' || ch ==
')' ||
1855 ( stopAtBracket && ( ch ==
'[' || ch ==
']' ) ) ) {
1862 inWords.takeLeftNoResize( retVal, i );
1865 if ( retVal ==
"NIL" ) {
1866 retVal.truncate( 0 );
1873 bool imapParser::parseOneNumber (
parseString & inWords, ulong & num)
1876 num = parseOneWord( inWords,
true ).toULong( &valid );
1880 bool imapParser::hasCapability (
const QString & cap)
1882 QString c = cap.toLower();
1884 for ( QStringList::ConstIterator it = imapCapabilities.constBegin();
1885 it != imapCapabilities.constEnd(); ++it ) {
1887 if ( !( kasciistricmp( c.toLatin1(), ( *it ).toAscii() ) ) ) {
1894 void imapParser::removeCapability (
const QString & cap)
1896 imapCapabilities.removeAll( cap.toLower() );
1899 QString imapParser::namespaceForBox(
const QString & box )
1901 kDebug( 7116 ) <<
"imapParse::namespaceForBox" << box;
1902 QString myNamespace;
1903 if ( !box.isEmpty() ) {
1904 const QList<QString> list = namespaceToDelimiter.keys();
1905 QString cleanPrefix;
1906 for ( QList<QString>::ConstIterator it = list.begin(); it != list.end(); ++it ) {
1907 if ( !( *it ).isEmpty() && box.contains( *it ) ) {