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")
105 p.fromString(aCmd->parameter());
106 currentBox = parseOneWord(p);
107 kDebug(7116) <<
"imapParser::sendCommand - setting current box to" << currentBox;
109 else if (command ==
"CLOSE")
114 else if (command.contains(
"SEARCH")
115 || command ==
"GETACL"
116 || command ==
"LISTRIGHTS"
117 || command ==
"MYRIGHTS"
118 || command ==
"GETANNOTATION"
119 || command ==
"NAMESPACE"
120 || command ==
"GETQUOTAROOT"
121 || command ==
"GETQUOTA"
122 || command ==
"X-GET-OTHER-USERS"
123 || command ==
"X-GET-DELEGATES"
124 || command ==
"X-GET-OUT-OF-OFFICE")
126 lastResults.clear ();
128 else if (command ==
"LIST"
129 || command ==
"LSUB")
131 listResponses.clear ();
133 parseWriteLine (aCmd->getStr ());
138 imapParser::clientLogin (
const QString & aUser,
const QString & aPass,
139 QString & resultInfo)
145 doCommand ( CommandPtr(
new
146 imapCommand (
"LOGIN",
"\"" + KIMAP::quoteIMAP(aUser)
147 +
"\" \"" + KIMAP::quoteIMAP(aPass) +
"\"")) );
149 if (cmd->result () ==
"OK")
151 currentState = ISTATE_LOGIN;
154 resultInfo = cmd->resultInfo();
155 completeQueue.removeAll (cmd);
159 static bool sasl_interact( KIO::SlaveBase *slave, KIO::AuthInfo &ai,
void *in )
161 kDebug(7116) <<
"sasl_interact";
162 sasl_interact_t *interact = ( sasl_interact_t * ) in;
166 for ( ; interact->id != SASL_CB_LIST_END; interact++ ) {
167 if ( interact->id == SASL_CB_AUTHNAME ||
168 interact->id == SASL_CB_PASS ) {
170 if ( ai.username.isEmpty() || ai.password.isEmpty() ) {
171 if (!slave->openPasswordDialog(ai))
178 interact = ( sasl_interact_t * ) in;
179 while( interact->id != SASL_CB_LIST_END ) {
180 kDebug(7116) <<
"SASL_INTERACT id:" << interact->id;
181 switch( interact->id ) {
183 case SASL_CB_AUTHNAME:
184 kDebug(7116) <<
"SASL_CB_[USER|AUTHNAME]: '" << ai.username <<
"'";
185 interact->result = strdup( ai.username.toUtf8() );
186 interact->len = strlen( (
const char *) interact->result );
189 kDebug(7116) <<
"SASL_CB_PASS: [hidden]";
190 interact->result = strdup( ai.password.toUtf8() );
191 interact->len = strlen( (
const char *) interact->result );
194 interact->result = 0;
204 imapParser::clientAuthenticate ( KIO::SlaveBase *slave, KIO::AuthInfo &ai,
205 const QString & aFQDN,
const QString & aAuth,
bool isSSL, QString & resultInfo)
209 sasl_conn_t *conn = 0;
210 sasl_interact_t *client_interact = 0;
213 const char *mechusing = 0;
214 QByteArray tmp, challenge;
216 kDebug(7116) <<
"aAuth:" << aAuth <<
" FQDN:" << aFQDN <<
" isSSL:" << isSSL;
219 if (!hasCapability (
"AUTH=" + aAuth))
223 result = sasl_client_new(
"imap",
226 0, 0, callbacks, 0, &conn );
228 if ( result != SASL_OK ) {
229 kDebug(7116) <<
"sasl_client_new failed with:" << result;
230 resultInfo = QString::fromUtf8( sasl_errdetail( conn ) );
235 result = sasl_client_start(conn, aAuth.toLatin1(), &client_interact,
236 hasCapability(
"SASL-IR") ? &out : 0, &outlen, &mechusing);
238 if ( result == SASL_INTERACT ) {
239 if ( !sasl_interact( slave, ai, client_interact ) ) {
240 sasl_dispose( &conn );
244 }
while ( result == SASL_INTERACT );
246 if ( result != SASL_CONTINUE && result != SASL_OK ) {
247 kDebug(7116) <<
"sasl_client_start failed with:" << result;
248 resultInfo = QString::fromUtf8( sasl_errdetail( conn ) );
249 sasl_dispose( &conn );
254 tmp = QByteArray::fromRawData( out, outlen );
255 challenge = tmp.toBase64();
258 QString firstCommand = aAuth;
259 if ( !challenge.isEmpty() ) {
261 firstCommand += QString::fromLatin1( challenge.data(), challenge.size() );
263 cmd = sendCommand (CommandPtr(
new imapCommand (
"AUTHENTICATE", firstCommand.toLatin1())));
266 while ( pl != -1 && !cmd->isComplete () ) {
268 while ( ( pl = parseLoop() ) == 0) {
272 if (!continuation.isEmpty())
275 if ( continuation.size() > 4 ) {
276 tmp = QByteArray::fromRawData( continuation.data() + 2, continuation.size() - 4 );
277 challenge = QByteArray::fromBase64( tmp );
283 result = sasl_client_step(conn, challenge.isEmpty() ? 0 : challenge.data(),
288 if (result == SASL_INTERACT) {
289 if ( !sasl_interact( slave, ai, client_interact ) ) {
290 sasl_dispose( &conn );
294 }
while ( result == SASL_INTERACT );
296 if ( result != SASL_CONTINUE && result != SASL_OK ) {
297 kDebug(7116) <<
"sasl_client_step failed with:" << result;
298 resultInfo = QString::fromUtf8( sasl_errdetail( conn ) );
299 sasl_dispose( &conn );
303 tmp = QByteArray::fromRawData( out, outlen );
305 challenge = tmp.toBase64();
308 parseWriteLine (challenge);
309 continuation.resize(0);
313 if (cmd->result () ==
"OK")
315 currentState = ISTATE_LOGIN;
318 resultInfo = cmd->resultInfo();
319 completeQueue.removeAll (cmd);
321 sasl_dispose( &conn );
330 parseOneWord(result);
331 QByteArray what = parseLiteral (result);
337 if (qstrncmp(what,
"BAD", what.size()) == 0)
339 parseResult (what, result);
341 else if (qstrncmp(what,
"BYE", what.size()) == 0)
343 parseResult (what, result);
344 if ( sentQueue.count() ) {
346 CommandPtr current = sentQueue.at (0);
347 current->setResultInfo(result.cstr());
349 currentState = ISTATE_NO;
354 if (what[1] ==
'O' && what.size() == 2)
356 parseResult (what, result);
358 else if (qstrncmp(what,
"NAMESPACE", what.size()) == 0)
360 parseNamespace (result);
365 if (what[1] ==
'K' && what.size() == 2)
367 parseResult (what, result);
368 }
else if (qstrncmp(what,
"OTHER-USER", 10) == 0) {
369 parseOtherUser (result);
370 }
else if (qstrncmp(what,
"OUT-OF-OFFICE", 13) == 0) {
371 parseOutOfOffice (result);
375 if (qstrncmp(what,
"DELEGATE", 8) == 0) {
376 parseDelegate (result);
381 if (qstrncmp(what,
"PREAUTH", what.size()) == 0)
383 parseResult (what, result);
384 currentState = ISTATE_LOGIN;
390 if (qstrncmp(what,
"CAPABILITY", what.size()) == 0)
392 parseCapability (result);
397 if (qstrncmp(what,
"FLAGS", what.size()) == 0)
404 if (qstrncmp(what,
"LIST", what.size()) == 0)
408 else if (qstrncmp(what,
"LSUB", what.size()) == 0)
412 else if (qstrncmp(what,
"LISTRIGHTS", what.size()) == 0)
414 parseListRights (result);
419 if (qstrncmp(what,
"MYRIGHTS", what.size()) == 0)
421 parseMyRights (result);
425 if (qstrncmp(what,
"SEARCH", what.size()) == 0)
427 parseSearch (result);
429 else if (qstrncmp(what,
"STATUS", what.size()) == 0)
431 parseStatus (result);
436 if (qstrncmp(what,
"ACL", what.size()) == 0)
440 else if (qstrncmp(what,
"ANNOTATION", what.size()) == 0)
442 parseAnnotation (result);
446 if ( what.size() > 5 && qstrncmp(what,
"QUOTAROOT", what.size()) == 0)
448 parseQuotaRoot( result );
450 else if (qstrncmp(what,
"QUOTA", what.size()) == 0)
452 parseQuota( result );
457 parseCustom( result );
466 number = what.toUInt(&valid);
469 what = parseLiteral (result);
473 if (qstrncmp(what,
"EXISTS", what.size()) == 0)
475 parseExists (number, result);
477 else if (qstrncmp(what,
"EXPUNGE", what.size()) == 0)
479 parseExpunge (number, result);
484 if (qstrncmp(what,
"FETCH", what.size()) == 0)
487 parseFetch (number, result);
492 if (qstrncmp(what,
"STORE", what.size()) == 0)
495 parseFetch (number, result);
500 if (qstrncmp(what,
"RECENT", what.size()) == 0)
502 parseRecent (number, result);
516 imapParser::parseResult (QByteArray & result,
parseString & rest,
517 const QString & command)
519 if (command ==
"SELECT")
520 selectInfo.setReadWrite(
true);
525 QByteArray option = parseOneWord(rest,
true);
530 if (option ==
"ALERT")
532 rest.pos = rest.data.indexOf(
']', rest.pos) + 1;
535 selectInfo.setAlert( rest.cstr() );
540 if (option ==
"NEWNAME")
546 if (option ==
"PARSE")
549 else if (option ==
"PERMANENTFLAGS")
551 uint end = rest.data.indexOf(
']', rest.pos);
552 QByteArray flags(rest.data.data() + rest.pos, end - rest.pos);
553 selectInfo.setPermanentFlags (flags);
559 if (option ==
"READ-ONLY")
561 selectInfo.setReadWrite (
false);
563 else if (option ==
"READ-WRITE")
565 selectInfo.setReadWrite (
true);
570 if (option ==
"TRYCREATE")
576 if (option ==
"UIDVALIDITY")
579 if (parseOneNumber (rest, value))
580 selectInfo.setUidValidity (value);
582 else if (option ==
"UNSEEN")
585 if (parseOneNumber (rest, value))
586 selectInfo.setUnseen (value);
588 else if (option ==
"UIDNEXT")
591 if (parseOneNumber (rest, value))
592 selectInfo.setUidNext (value);
603 if (command.isEmpty())
610 switch (command[0].toLatin1 ())
613 if (command ==
"AUTHENTICATE")
614 if (qstrncmp(result,
"OK", result.size()) == 0)
615 currentState = ISTATE_LOGIN;
619 if (command ==
"LOGIN")
620 if (qstrncmp(result,
"OK", result.size()) == 0)
621 currentState = ISTATE_LOGIN;
625 if (command ==
"EXAMINE")
627 if (qstrncmp(result,
"OK", result.size()) == 0)
628 currentState = ISTATE_SELECT;
631 if (currentState == ISTATE_SELECT)
632 currentState = ISTATE_LOGIN;
635 kDebug(7116) <<
"imapParser::parseResult - current box is now" << currentBox;
640 if (command ==
"SELECT")
642 if (qstrncmp(result,
"OK", result.size()) == 0)
643 currentState = ISTATE_SELECT;
646 if (currentState == ISTATE_SELECT)
647 currentState = ISTATE_LOGIN;
650 kDebug(7116) <<
"imapParser::parseResult - current box is now" << currentBox;
660 void imapParser::parseCapability (
parseString & result)
662 QByteArray data = result.cstr();
663 kAsciiToLower( data.data() );
664 imapCapabilities = QString::fromLatin1(data).split (
' ', QString::SkipEmptyParts );
669 selectInfo.setFlags(result.cstr());
676 if (result[0] !=
'(')
681 this_one.parseAttributes( result );
686 this_one.setHierarchyDelimiter(parseLiteral(result));
687 this_one.setName(QString::fromUtf8(KIMAP::decodeImapFolderName( parseLiteral(result))));
689 listResponses.append (this_one);
694 imapList this_one (result.cstr(), *
this);
695 listResponses.append (this_one);
698 void imapParser::parseListRights (
parseString & result)
700 parseOneWord (result);
701 parseOneWord (result);
703 const QByteArray word = parseOneWord (result);
704 if ( word.isEmpty() )
706 lastResults.append (word);
712 parseOneWord (result);
714 while ( !result.isEmpty() ) {
715 const QByteArray word = parseLiteral(result);
716 if ( word.isEmpty() )
718 lastResults.append (word);
722 void imapParser::parseAnnotation (
parseString & result)
724 parseOneWord (result);
726 parseOneWord (result);
728 if (result.isEmpty() || result[0] !=
'(')
733 while ( !result.isEmpty() && result[0] !=
')' ) {
734 const QByteArray word = parseLiteral (result);
735 if ( word.isEmpty() )
737 lastResults.append (word);
747 QByteArray root = parseOneWord( result );
748 if ( root.isEmpty() ) {
749 lastResults.append(
"" );
751 lastResults.append( root );
753 if (result.isEmpty() || result[0] !=
'(')
758 while ( !result.isEmpty() && result[0] !=
')' ) {
759 const QByteArray word = parseLiteral(result);
760 if ( word.isEmpty() )
762 triplet.append(word);
764 lastResults.append( triplet.join(
" ") );
767 void imapParser::parseQuotaRoot (
parseString & result)
771 parseOneWord (result);
773 if ( result.isEmpty() )
776 while ( !result.isEmpty() ) {
777 const QByteArray word = parseLiteral (result);
778 if ( word.isEmpty() )
782 lastResults.append( roots.isEmpty() ?
"" : roots.join(
" " ) );
785 void imapParser::parseCustom (
parseString & result)
787 QByteArray word = parseLiteral (result,
false,
false);
788 lastResults.append( word );
791 void imapParser::parseOtherUser (
parseString & result)
793 lastResults.append( parseOneWord ( result ) );
796 void imapParser::parseDelegate (
parseString & result)
798 const QString email = parseOneWord ( result );
801 while ( !result.isEmpty() ) {
802 QByteArray word = parseLiteral ( result,
false,
false );
803 rights.append( word );
806 lastResults.append( email +
':' + rights.join(
"," ) );
809 void imapParser::parseOutOfOffice (
parseString & result)
811 const QString state = parseOneWord (result);
812 parseOneWord (result);
814 QByteArray msg = parseLiteral (result,
false,
false);
816 lastResults.append( state +
'^' + QString::fromUtf8( msg ) );
819 void imapParser::parseMyRights (
parseString & result)
821 parseOneWord (result);
822 Q_ASSERT( lastResults.isEmpty() );
823 lastResults.append (parseOneWord (result) );
826 void imapParser::parseSearch (
parseString & result)
830 while (parseOneNumber (result, value))
832 lastResults.append (QString::number(value));
836 void imapParser::parseStatus (
parseString & inWords)
838 lastStatus = imapInfo ();
840 parseLiteral(inWords);
841 if (inWords[0] !=
'(')
847 while (!inWords.isEmpty() && inWords[0] !=
')')
851 QByteArray label = parseOneWord(inWords);
852 if (parseOneNumber (inWords, value))
854 if (label ==
"MESSAGES")
855 lastStatus.setCount (value);
856 else if (label ==
"RECENT")
857 lastStatus.setRecent (value);
858 else if (label ==
"UIDVALIDITY")
859 lastStatus.setUidValidity (value);
860 else if (label ==
"UNSEEN")
861 lastStatus.setUnseen (value);
862 else if (label ==
"UIDNEXT")
863 lastStatus.setUidNext (value);
867 if (inWords[0] ==
')')
872 void imapParser::parseExists (ulong value,
parseString & result)
874 selectInfo.setCount (value);
875 result.pos = result.data.size();
878 void imapParser::parseExpunge (ulong value,
parseString & result)
884 void imapParser::parseAddressList (
parseString & inWords, QList<mailAddress *>& list)
886 if ( inWords.isEmpty() )
888 if (inWords[0] !=
'(')
890 parseOneWord (inWords);
897 while (!inWords.isEmpty () && inWords[0] !=
')')
899 if (inWords[0] ==
'(') {
900 mailAddress *addr =
new mailAddress;
901 parseAddress(inWords, *addr);
908 if (!inWords.isEmpty() && inWords[0] ==
')')
914 const mailAddress& imapParser::parseAddress (
parseString & inWords, mailAddress& retVal)
919 retVal.setFullName(parseLiteral(inWords));
920 retVal.setCommentRaw(parseLiteral(inWords));
921 retVal.setUser(parseLiteral(inWords));
922 retVal.setHost(parseLiteral(inWords));
924 if (!inWords.isEmpty() && inWords[0] ==
')')
935 if (inWords[0] !=
'(')
943 envelope->
setDate(parseLiteral(inWords));
948 QList<mailAddress *> list;
951 parseAddressList(inWords, list);
952 if (!list.isEmpty()) {
953 envelope->setFrom(*list.last());
958 parseAddressList(inWords, list);
959 if (!list.isEmpty()) {
960 envelope->setSender(*list.last());
965 parseAddressList(inWords, list);
966 if (!list.isEmpty()) {
967 envelope->setReplyTo(*list.last());
972 parseAddressList (inWords, envelope->to());
975 parseAddressList (inWords, envelope->cc());
978 parseAddressList (inWords, envelope->bcc());
981 envelope->setInReplyTo(parseLiteral(inWords));
984 envelope->setMessageId(parseLiteral(inWords));
987 while (!inWords.isEmpty () && inWords[0] !=
')')
990 if (inWords[0] ==
'(')
991 parseSentence (inWords);
993 parseLiteral (inWords);
996 if (!inWords.isEmpty() && inWords[0] ==
')')
1005 QHash < QByteArray, QString > imapParser::parseDisposition (
parseString & inWords)
1007 QByteArray disposition;
1008 QHash < QByteArray, QString > retVal;
1010 if (inWords[0] !=
'(')
1013 disposition = parseOneWord (inWords);
1021 disposition = parseOneWord (inWords);
1023 retVal = parseParameters (inWords);
1024 if (inWords[0] !=
')')
1030 if (!disposition.isEmpty ())
1032 retVal.insert (
"content-disposition", QString(disposition));
1040 QHash < QByteArray, QString > imapParser::parseParameters (
parseString & inWords)
1042 QHash < QByteArray, QString > retVal;
1044 if (inWords[0] !=
'(')
1047 parseOneWord (inWords);
1054 while (!inWords.isEmpty () && inWords[0] !=
')')
1056 const QByteArray l1 = parseLiteral(inWords);
1057 const QByteArray l2 = parseLiteral(inWords);
1058 retVal.insert (l1.toLower(), QString(l2));
1061 if (inWords[0] !=
')')
1075 QHash < QByteArray, QString > parameters;
1078 if (inWords[0] !=
'(')
1084 localPart->setPartSpecifier (inSection);
1090 typeStr = parseLiteral(inWords);
1093 subtype = parseLiteral(inWords);
1095 localPart->setType (typeStr +
'/' + subtype);
1098 parameters = parseParameters (inWords);
1100 QHashIterator < QByteArray, QString > it (parameters);
1102 while (it.hasNext ())
1105 localPart->setTypeParm (it.key (), it.value ());
1107 parameters.clear ();
1111 localPart->setID (parseLiteral(inWords));
1114 localPart->setDescription (parseLiteral(inWords));
1117 localPart->setEncoding (parseLiteral(inWords));
1120 if (parseOneNumber (inWords, size))
1121 localPart->setLength (size);
1124 if (localPart->getType().toUpper() ==
"MESSAGE/RFC822")
1127 mailHeader *envelope = parseEnvelope (inWords);
1130 parseBodyStructure (inWords, inSection, envelope);
1132 localPart->setNestedMessage (envelope);
1136 parseOneNumber (inWords, lines);
1140 if (typeStr ==
"TEXT")
1144 parseOneNumber (inWords, lines);
1148 parseLiteral(inWords);
1151 parameters = parseDisposition (inWords);
1153 QString disposition = parameters[
"content-disposition"];
1155 localPart->setDisposition (disposition.toAscii ());
1156 QHashIterator < QByteArray, QString > it (parameters);
1157 while (it.hasNext ())
1160 localPart->setDispositionParm (it.key (), it.value ());
1162 parameters.clear ();
1166 parseSentence (inWords);
1170 while (!inWords.isEmpty () && inWords[0] !=
')')
1173 if (inWords[0] ==
'(')
1174 parseSentence (inWords);
1176 parseLiteral(inWords);
1179 if (inWords[0] ==
')')
1190 if (inSection.isEmpty())
1199 if (inWords[0] !=
'(')
1202 parseOneWord (inWords);
1208 if (inWords[0] ==
'(')
1211 QHash< QByteArray, QString > parameters;
1219 localPart->clearNestedParts ();
1220 localPart->clearTypeParameters ();
1221 localPart->clearDispositionParameters ();
1223 outSection = inSection +
".HEADER";
1225 if (inWords[0] ==
'(' && init)
1229 if ( !outSection.isEmpty() ) {
1230 localPart->setPartSpecifier(outSection);
1232 localPart->setPartSpecifier(inSection);
1236 while (inWords[0] ==
'(')
1238 outSection = QString::number(++section);
1240 outSection = inSection +
'.' + outSection;
1241 mimeHeader *subpart = parseBodyStructure (inWords, outSection, 0);
1242 localPart->addNestedPart (subpart);
1246 subtype = parseOneWord (inWords);
1248 localPart->setType (
"MULTIPART/" + subtype);
1251 parameters = parseParameters (inWords);
1253 QHashIterator < QByteArray, QString > it (parameters);
1255 while (it.hasNext ())
1258 localPart->setTypeParm (it.key (), it.value ());
1260 parameters.clear ();
1264 parameters = parseDisposition (inWords);
1266 QString disposition = parameters[
"content-disposition"];
1268 localPart->setDisposition (disposition.toAscii ());
1269 QHashIterator < QByteArray, QString > it (parameters);
1270 while (it.hasNext ())
1273 localPart->setDispositionParm (it.key (), it.value ());
1275 parameters.clear ();
1279 parseSentence (inWords);
1286 inWords.data[inWords.pos] =
'(';
1288 inSection = inSection +
".1";
1289 localPart = parseSimplePart (inWords, inSection, localPart);
1291 inWords.data[inWords.pos] =
')';
1295 while (!inWords.isEmpty () && inWords[0] !=
')')
1298 if (inWords[0] ==
'(')
1299 parseSentence (inWords);
1301 parseLiteral(inWords);
1304 if (inWords[0] ==
')')
1311 void imapParser::parseBody (
parseString & inWords)
1314 if (inWords[0] ==
'[')
1316 QByteArray specifier;
1320 specifier = parseOneWord (inWords,
true);
1322 if (inWords[0] ==
'(')
1326 while (!inWords.isEmpty () && inWords[0] !=
')')
1328 label = parseOneWord (inWords);
1331 if (inWords[0] ==
')')
1334 if (inWords[0] ==
']')
1339 if (qstrncmp(specifier,
"0", specifier.size()) == 0)
1343 envelope = lastHandled->getHeader ();
1345 if (!envelope || seenUid.isEmpty ())
1347 kDebug(7116) <<
"imapParser::parseBody - discarding" << envelope << seenUid.toAscii ();
1349 parseLiteral(inWords,
true);
1353 kDebug(7116) <<
"imapParser::parseBody - reading" << envelope << seenUid.toAscii ();
1355 QString theHeader = parseLiteral(inWords,
true);
1358 myIO.setString (theHeader);
1359 envelope->parseHeader (myIO);
1363 else if (qstrncmp(specifier,
"HEADER.FIELDS", specifier.size()) == 0)
1368 if (qstrncmp(label,
"REFERENCES", label.size()) == 0)
1372 envelope = lastHandled->getHeader ();
1374 if (!envelope || seenUid.isEmpty ())
1376 kDebug(7116) <<
"imapParser::parseBody - discarding" << envelope << seenUid.toAscii ();
1378 parseLiteral (inWords,
true);
1382 QByteArray references = parseLiteral(inWords,
true);
1383 int start = references.indexOf (
'<');
1384 int end = references.lastIndexOf (
'>');
1386 references = references.mid (start, end - start + 1);
1387 envelope->setReferences(references.simplified());
1392 parseLiteral(inWords,
true);
1397 if (specifier.contains(
".MIME") )
1400 QString theHeader = parseLiteral(inWords,
false);
1402 myIO.setString (theHeader);
1403 envelope->parseHeader (myIO);
1405 lastHandled->setHeader (envelope);
1409 kDebug(7116) <<
"imapParser::parseBody - discarding" << seenUid.toAscii ();
1410 parseLiteral(inWords,
true);
1418 envelope = lastHandled->getHeader ();
1420 if (!envelope || seenUid.isEmpty ())
1422 kDebug(7116) <<
"imapParser::parseBody - discarding" << envelope << seenUid.toAscii ();
1424 parseSentence (inWords);
1428 kDebug(7116) <<
"imapParser::parseBody - reading" << envelope << seenUid.toAscii ();
1431 mimeHeader *body = parseBodyStructure (inWords, section, envelope);
1432 if (body != envelope)
1438 void imapParser::parseFetch (ulong ,
parseString & inWords)
1440 if (inWords[0] !=
'(')
1448 while (!inWords.isEmpty () && inWords[0] !=
')')
1450 if (inWords[0] ==
'(')
1451 parseSentence (inWords);
1454 const QByteArray word = parseLiteral(inWords,
false,
true);
1459 if (word ==
"ENVELOPE")
1464 envelope = lastHandled->getHeader ();
1466 lastHandled =
new imapCache();
1468 if (envelope && !envelope->getMessageId ().isEmpty ())
1472 parseSentence (inWords);
1476 envelope = parseEnvelope (inWords);
1479 envelope->setPartSpecifier (seenUid +
".0");
1480 lastHandled->setHeader (envelope);
1481 lastHandled->setUid (seenUid.toULong ());
1490 parseBody (inWords);
1492 else if (word ==
"BODY[]" )
1495 parseLiteral(inWords,
true);
1497 else if (word ==
"BODYSTRUCTURE")
1502 envelope = lastHandled->getHeader ();
1507 parseBodyStructure (inWords, section, envelope);
1509 QDataStream stream( &data, QIODevice::WriteOnly );
1511 body->serialize(stream);
1521 seenUid = parseOneWord(inWords);
1524 envelope = lastHandled->getHeader ();
1526 lastHandled =
new imapCache();
1528 if (seenUid.isEmpty ())
1531 kDebug(7116) <<
"imapParser::parseFetch - UID empty";
1535 lastHandled->setUid (seenUid.toULong ());
1538 envelope->setPartSpecifier (seenUid);
1543 if (word ==
"RFC822.SIZE")
1546 parseOneNumber (inWords, size);
1548 if (!lastHandled) lastHandled =
new imapCache();
1549 lastHandled->setSize (size);
1551 else if (word.startsWith(
"RFC822"))
1554 parseLiteral(inWords,
true);
1559 if (word ==
"INTERNALDATE")
1561 const QByteArray date = parseOneWord(inWords);
1562 if (!lastHandled) lastHandled =
new imapCache();
1563 lastHandled->setDate(date);
1568 if (word ==
"FLAGS")
1571 if (!lastHandled) lastHandled =
new imapCache();
1572 lastHandled->setFlags (imapInfo::_flags (inWords.cstr()));
1577 parseLiteral(inWords);
1584 while (!inWords.isEmpty () && inWords[0] !=
')')
1587 if (inWords[0] ==
'(')
1588 parseSentence (inWords);
1590 parseLiteral(inWords);
1593 if (inWords.isEmpty() || inWords[0] !=
')')
1601 void imapParser::parseSentence (
parseString & inWords)
1608 while (!inWords.isEmpty () && (stack != 0 || first))
1613 unsigned char ch = inWords[0];
1633 parseLiteral(inWords);
1641 void imapParser::parseRecent (ulong value,
parseString & result)
1643 selectInfo.setRecent (value);
1644 result.pos = result.data.size();
1647 void imapParser::parseNamespace (
parseString & result)
1649 if ( result[0] !=
'(' )
1653 if ( namespaceToDelimiter.contains( QString() ) )
1654 delimEmpty = namespaceToDelimiter[QString()];
1656 namespaceToDelimiter.clear();
1657 imapNamespaces.clear();
1661 bool personalAvailable =
false;
1662 while ( !result.isEmpty() )
1664 if ( result[0] ==
'(' )
1667 if ( result[0] ==
'(' )
1674 QString prefix = QString::fromLatin1( parseOneWord( result ) );
1676 QString delim = QString::fromLatin1( parseOneWord( result ) );
1677 kDebug(7116) <<
"imapParser::parseNamespace ns='" << prefix <<
"',delim='" << delim <<
"'";
1681 personalAvailable =
true;
1683 QString nsentry = QString::number( ns ) +
'=' + prefix +
'=' + delim;
1684 imapNamespaces.append( nsentry );
1685 if ( prefix.right( 1 ) == delim ) {
1687 prefix.resize( prefix.length() );
1689 namespaceToDelimiter[prefix] = delim;
1693 }
else if ( result[0] ==
')' )
1697 }
else if ( result[0] ==
'N' )
1701 parseOneWord( result );
1704 parseOneWord( result );
1707 if ( !delimEmpty.isEmpty() ) {
1709 namespaceToDelimiter[QString()] = delimEmpty;
1710 if ( !personalAvailable )
1713 kDebug(7116) <<
"imapParser::parseNamespace - registering own personal ns";
1714 QString nsentry =
"0==" + delimEmpty;
1715 imapNamespaces.append( nsentry );
1720 int imapParser::parseLoop ()
1724 if (!parseReadLine(result.data))
return -1;
1728 if (result.data.isEmpty())
1730 if (!sentQueue.count ())
1733 kDebug(7116) <<
"imapParser::parseLoop - unhandledResponse:" << result.cstr();
1734 unhandled << result.cstr();
1738 CommandPtr current = sentQueue.at (0);
1742 result.data.resize(result.data.size() - 2);
1743 parseUntagged (result);
1746 continuation = result.data;
1750 QByteArray tag = parseLiteral(result);
1751 if (current->id() == tag.data())
1753 result.data.resize(result.data.size() - 2);
1754 QByteArray resultCode = parseLiteral (result);
1755 current->setResult (resultCode);
1756 current->setResultInfo(result.cstr());
1757 current->setComplete ();
1759 sentQueue.removeAll (current);
1760 completeQueue.append (current);
1761 if (result.length())
1762 parseResult (resultCode, result, current->command());
1766 kDebug(7116) <<
"imapParser::parseLoop - unknown tag '" << tag <<
"'";
1767 QByteArray cstr = tag +
' ' + result.cstr();
1770 result.data.resize(cstr.length());
1781 imapParser::parseRelay (
const QByteArray & buffer)
1785 (
"imapParser::parseRelay - virtual function not reimplemented - data lost");
1789 imapParser::parseRelay (ulong len)
1793 (
"imapParser::parseRelay - virtual function not reimplemented - announcement lost");
1796 bool imapParser::parseRead (QByteArray & buffer,
long len,
long relay)
1802 (
"imapParser::parseRead - virtual function not reimplemented - no data read");
1806 bool imapParser::parseReadLine (QByteArray & buffer,
long relay)
1811 (
"imapParser::parseReadLine - virtual function not reimplemented - no data read");
1816 imapParser::parseWriteLine (
const QString & str)
1820 (
"imapParser::parseWriteLine - virtual function not reimplemented - no data written");
1824 imapParser::parseURL (
const KUrl & _url, QString & _box, QString & _section,
1825 QString & _type, QString & _uid, QString & _validity, QString & _info)
1827 QStringList parameters;
1829 _box = _url.path ();
1830 kDebug(7116) <<
"imapParser::parseURL" << _box;
1831 int paramStart = _box.indexOf(
"/;");
1832 if ( paramStart > -1 )
1834 QString paramString = _box.right( _box.length() - paramStart-2 );
1835 parameters = paramString.split (
';', QString::SkipEmptyParts);
1836 _box.truncate( paramStart );
1839 for (QStringList::ConstIterator it (parameters.constBegin ());
1840 it != parameters.constEnd (); ++it)
1842 QString temp = (*it);
1845 int pt = temp.indexOf (
'/');
1848 if (temp.startsWith(QLatin1String(
"section="), Qt::CaseInsensitive))
1849 _section = temp.right (temp.length () - 8);
1850 else if (temp.startsWith(QLatin1String(
"type="), Qt::CaseInsensitive))
1851 _type = temp.right (temp.length () - 5);
1852 else if (temp.startsWith(QLatin1String(
"uid="), Qt::CaseInsensitive))
1853 _uid = temp.right (temp.length () - 4);
1854 else if (temp.startsWith(QLatin1String(
"uidvalidity="), Qt::CaseInsensitive))
1855 _validity = temp.right (temp.length () - 12);
1856 else if (temp.startsWith(QLatin1String(
"info="), Qt::CaseInsensitive))
1857 _info = temp.right (temp.length () - 5);
1864 if (!_box.isEmpty ())
1868 _box = _box.right (_box.length () - 1);
1869 if (!_box.isEmpty () && _box[_box.length () - 1] ==
'/')
1870 _box.truncate(_box.length() - 1);
1872 kDebug(7116) <<
"URL: box=" << _box <<
", section=" << _section <<
", type="
1873 << _type <<
", uid=" << _uid <<
", validity=" << _validity <<
", info=" << _info;
1877 QByteArray imapParser::parseLiteral(
parseString & inWords,
bool relay,
bool stopAtBracket) {
1879 if (!inWords.isEmpty() && inWords[0] ==
'{')
1882 int runLen = inWords.find (
'}', 1);
1886 long runLenSave = runLen + 1;
1887 QByteArray tmpstr(runLen,
'\0');
1888 inWords.takeMidNoResize(tmpstr, 1, runLen - 1);
1889 runLen = tmpstr.toULong (&proper);
1890 inWords.pos += runLenSave;
1895 parseRelay (runLen);
1897 parseRead (rv, runLen, relay ? runLen : 0);
1898 rv.resize(qMax(runLen, rv.size()));
1901 parseReadLine (inWords.data);
1908 kDebug(7116) <<
"imapParser::parseLiteral - error parsing {} -" ;
1914 kDebug(7116) <<
"imapParser::parseLiteral - error parsing unmatched {";
1920 return parseOneWord(inWords, stopAtBracket);
1924 QByteArray imapParser::parseOneWord (
parseString & inWords,
bool stopAtBracket)
1926 uint len = inWords.length();
1928 return QByteArray();
1931 if (len > 0 && inWords[0] ==
'"')
1935 while (i < len && (inWords[i] !=
'"' || quote))
1937 if (inWords[i] ==
'\\') quote = !quote;
1946 inWords.takeLeftNoResize(retVal, i - 1);
1949 for (
unsigned int j = 0; j < len; j++) {
1950 if (retVal[j] ==
'\\') {
1954 retVal[j - offset] = retVal[j];
1956 retVal.resize( len - offset );
1963 kDebug(7116) <<
"imapParser::parseOneWord - error parsing unmatched \"";
1964 QByteArray retVal = inWords.cstr();
1974 for (i = 0; i < len; ++i) {
1975 char ch = inWords[i];
1976 if (ch <=
' ' || ch ==
'(' || ch ==
')' ||
1977 (stopAtBracket && (ch ==
'[' || ch ==
']')))
1983 inWords.takeLeftNoResize(retVal, i);
1986 if (retVal ==
"NIL") {
1994 bool imapParser::parseOneNumber (
parseString & inWords, ulong & num)
1997 num = parseOneWord(inWords,
true).toULong(&valid);
2001 bool imapParser::hasCapability (
const QString & cap)
2003 QString c = cap.toLower();
2005 for (QStringList::ConstIterator it = imapCapabilities.constBegin ();
2006 it != imapCapabilities.constEnd (); ++it)
2009 if ( !(kasciistricmp(c.toAscii(), (*it).toAscii())) )
2017 void imapParser::removeCapability (
const QString & cap)
2019 imapCapabilities.removeAll(cap.toLower());
2022 QString imapParser::namespaceForBox(
const QString & box )
2024 kDebug(7116) <<
"imapParse::namespaceForBox" << box;
2025 QString myNamespace;
2026 if ( !box.isEmpty() )
2028 const QList<QString> list = namespaceToDelimiter.keys();
2029 QString cleanPrefix;
2030 for ( QList<QString>::ConstIterator it = list.begin(); it != list.end(); ++it )
2032 if ( !(*it).isEmpty() && box.contains( *it ) )