23 #include "kmime_header_parsing.h"
28 #include "kmime_util.h"
29 #include "kmime_util_p.h"
31 #include "kmime_warning.h"
34 #include <kcharsets.h>
36 #include <QtCore/QTextCodec>
37 #include <QtCore/QMap>
38 #include <QtCore/QStringList>
39 #include <QtCore/QUrl>
44 using namespace KMime;
45 using namespace KMime::Types;
53 static inline QString QUrl_fromAce_wrapper(
const QString & domain )
55 if ( domain.contains( QLatin1String(
"xn--" ) ) )
56 return QUrl::fromAce( domain.toLatin1() );
61 static QString addr_spec_as_string(
const AddrSpec & as,
bool pretty )
67 static QChar dotChar = QLatin1Char(
'.' );
68 static QChar backslashChar = QLatin1Char(
'\\' );
69 static QChar quoteChar = QLatin1Char(
'"' );
71 bool needsQuotes =
false;
73 result.reserve( as.localPart.length() + as.domain.length() + 1 );
74 for (
int i = 0 ; i < as.localPart.length() ; ++i ) {
75 const QChar ch = as.localPart.at( i );
76 if ( ch == dotChar || isAText( ch.toLatin1() ) ) {
80 if ( ch == backslashChar || ch == quoteChar ) {
81 result += backslashChar;
86 const QString dom = pretty ? QUrl_fromAce_wrapper( as.domain ) : as.domain ;
88 result = quoteChar + result + quoteChar;
93 result += QLatin1Char(
'@' );
99 QString AddrSpec::asString()
const
101 return addr_spec_as_string( *
this,
false );
104 QString AddrSpec::asPrettyString()
const
106 return addr_spec_as_string( *
this,
true );
109 bool AddrSpec::isEmpty()
const
111 return localPart.isEmpty() && domain.isEmpty();
114 QByteArray Mailbox::address()
const
117 const QString asString = addr_spec_as_string( mAddrSpec,
false );
118 if ( !asString.isEmpty() ) {
119 result = asString.toLatin1();
125 AddrSpec Mailbox::addrSpec()
const
130 QString Mailbox::name()
const
135 void Mailbox::setAddress(
const AddrSpec &addr )
140 void Mailbox::setAddress(
const QByteArray &addr )
142 const char *cursor = addr.constData();
143 if ( !HeaderParsing::parseAngleAddr( cursor,
144 cursor + addr.length(), mAddrSpec ) ) {
145 if ( !HeaderParsing::parseAddrSpec( cursor, cursor + addr.length(),
147 kWarning() <<
"Invalid address";
153 void Mailbox::setName(
const QString &name )
158 void Mailbox::setNameFrom7Bit(
const QByteArray &name,
159 const QByteArray &defaultCharset )
165 bool Mailbox::hasAddress()
const
167 return !mAddrSpec.isEmpty();
170 bool Mailbox::hasName()
const
172 return !mDisplayName.isEmpty();
175 QString Mailbox::prettyAddress()
const
177 return prettyAddress( QuoteNever );
180 QString Mailbox::prettyAddress(
Quoting quoting )
const
183 return QLatin1String( address() );
186 if ( quoting != QuoteNever ) {
190 if ( hasAddress() ) {
191 s += QLatin1String(
" <") + QLatin1String( address() ) + QLatin1Char(
'>');
196 void Mailbox::fromUnicodeString(
const QString &s )
198 from7BitString( encodeRFC2047Sentence( s,
"utf-8" ) );
201 void Mailbox::from7BitString(
const QByteArray &s )
203 const char *cursor = s.constData();
204 HeaderParsing::parseMailbox( cursor, cursor + s.length(), *this );
214 QByteArray tmp = name().toLatin1();
220 if ( hasAddress() ) {
221 rv +=
" <" + address() +
'>';
228 namespace HeaderParsing {
231 bool parseEncodedWord(
const char* &scursor,
const char *
const send,
232 QString &result, QByteArray &language,
233 QByteArray &usedCS,
const QByteArray &defaultCS,
237 assert( *(scursor-1) ==
'=' );
244 char ch = *scursor++;
254 const char * charsetStart = scursor;
255 const char * languageStart = 0;
259 for ( ; scursor != send ; scursor++ ) {
260 if ( *scursor ==
'?') {
262 }
else if ( *scursor ==
'*' && languageStart == 0 ) {
263 languageStart = scursor + 1;
268 if ( scursor == send || *scursor !=
'?' ) {
270 KMIME_WARN_PREMATURE_END_OF( EncodedWord );
276 QByteArray maybeLanguage( languageStart, scursor - languageStart );
279 QByteArray maybeCharset( charsetStart,
280 ( languageStart ? languageStart - 1 : scursor ) - charsetStart );
289 const char * encodingStart = scursor;
292 for ( ; scursor != send ; scursor++ ) {
293 if ( *scursor ==
'?' ) {
299 if ( scursor == send || *scursor !=
'?' ) {
301 KMIME_WARN_PREMATURE_END_OF( EncodedWord );
306 QByteArray maybeEncoding( encodingStart, scursor - encodingStart );
319 const char * encodedTextStart = scursor;
322 for ( ; scursor != send ; scursor++ ) {
323 if ( *scursor ==
'?' ) {
324 if ( scursor + 1 != send ) {
325 if ( *( scursor + 1 ) !=
'=' ) {
326 KMIME_WARN <<
"Stray '?' in q-encoded word, ignoring this.";
335 KMIME_WARN_PREMATURE_END_OF( EncodedWord );
341 if ( *( scursor - 2 ) !=
'?' || *( scursor - 1 ) !=
'=' ||
342 scursor < encodedTextStart + 2 ) {
343 KMIME_WARN_PREMATURE_END_OF( EncodedWord );
348 const char *
const encodedTextEnd = scursor - 2;
358 KMIME_WARN_UNKNOWN( Encoding, maybeEncoding );
367 bool matchOK =
false;
368 QTextCodec *textCodec = 0;
369 if ( forceCS || maybeCharset.isEmpty() ) {
370 textCodec = KGlobal::charsets()->codecForName( QLatin1String( defaultCS ), matchOK );
373 textCodec = KGlobal::charsets()->codecForName( QLatin1String( maybeCharset ), matchOK );
375 textCodec = KGlobal::charsets()->codecForName( QLatin1String( defaultCS ), matchOK );
382 if ( !matchOK || !textCodec ) {
383 KMIME_WARN_UNKNOWN( Charset, maybeCharset );
391 int encodedTextLength = encodedTextEnd - encodedTextStart;
394 char *bbegin = buffer.data();
395 char *bend = bbegin + buffer.length();
402 if ( !dec->
decode( encodedTextStart, encodedTextEnd, bbegin, bend ) ) {
403 KMIME_WARN << codec->
name() <<
"codec lies about its maxDecodedSizeFor("
404 << encodedTextLength <<
")\nresult may be truncated";
407 result = textCodec->toUnicode( buffer.data(), bbegin - buffer.data() );
412 language = maybeLanguage;
417 static inline void eatWhiteSpace(
const char* &scursor,
const char *
const send )
419 while ( scursor != send &&
420 ( *scursor ==
' ' || *scursor ==
'\n' ||
421 *scursor ==
'\t' || *scursor ==
'\r' ) )
425 bool parseAtom(
const char * &scursor,
const char *
const send,
426 QString &result,
bool allow8Bit )
428 QPair<const char*,int> maybeResult;
430 if ( parseAtom( scursor, send, maybeResult, allow8Bit ) ) {
431 result += QString::fromLatin1( maybeResult.first, maybeResult.second );
438 bool parseAtom(
const char * &scursor,
const char *
const send,
439 QPair<const char*,int> &result,
bool allow8Bit )
441 bool success =
false;
442 const char *start = scursor;
444 while ( scursor != send ) {
445 signed char ch = *scursor++;
446 if ( ch > 0 && isAText( ch ) ) {
449 }
else if ( allow8Bit && ch < 0 ) {
451 KMIME_WARN_8BIT( ch );
461 result.first = start;
462 result.second = scursor - start;
468 bool parseToken(
const char * &scursor,
const char *
const send,
469 QString &result,
bool allow8Bit )
471 QPair<const char*,int> maybeResult;
473 if ( parseToken( scursor, send, maybeResult, allow8Bit ) ) {
474 result += QString::fromLatin1( maybeResult.first, maybeResult.second );
481 bool parseToken(
const char * &scursor,
const char *
const send,
482 QPair<const char*,int> &result,
bool allow8Bit )
484 bool success =
false;
485 const char * start = scursor;
487 while ( scursor != send ) {
488 signed char ch = *scursor++;
489 if ( ch > 0 && isTText( ch ) ) {
492 }
else if ( allow8Bit && ch < 0 ) {
494 KMIME_WARN_8BIT( ch );
504 result.first = start;
505 result.second = scursor - start;
509 #define READ_ch_OR_FAIL if ( scursor == send ) { \
510 KMIME_WARN_PREMATURE_END_OF( GenericQuotedString ); \
522 bool parseGenericQuotedString(
const char* &scursor,
const char *
const send,
523 QString &result,
bool isCRLF,
524 const char openChar,
const char closeChar )
533 assert( *(scursor-1) == openChar || *(scursor-1) == closeChar );
535 while ( scursor != send ) {
538 if ( ch == closeChar || ch == openChar ) {
548 KMIME_WARN_IF_8BIT( ch );
549 result += QLatin1Char( ch );
562 KMIME_WARN_LONE( CR );
563 result += QLatin1Char(
'\r');
569 if ( ch ==
' ' || ch ==
'\t' ) {
573 result += QLatin1Char( ch );
578 KMIME_WARN_NON_FOLDING( CRLF );
579 result += QLatin1String(
"\r\n" );
596 if ( !isCRLF && ( ch ==
' ' || ch ==
'\t' ) ) {
599 result += QLatin1Char( ch );
602 KMIME_WARN_LONE( LF );
603 result += QLatin1Char(
'\n' );
613 if( scursor == send )
616 const char *oldscursor = scursor;
618 QByteArray lang, charset;
619 if( *scursor++ ==
'?' ) {
621 if( parseEncodedWord( scursor, send, tmp, lang, charset ) ) {
625 scursor = oldscursor;
628 scursor = oldscursor;
633 KMIME_WARN_IF_8BIT( ch );
634 result += QLatin1Char( ch );
645 bool parseComment(
const char* &scursor,
const char *
const send,
646 QString &result,
bool isCRLF,
bool reallySave )
648 int commentNestingDepth = 1;
649 const char *afterLastClosingParenPos = 0;
651 const char *oldscursor = scursor;
653 assert( *(scursor-1) ==
'(' );
655 while ( commentNestingDepth ) {
657 if ( parseGenericQuotedString( scursor, send, cmntPart, isCRLF,
'(',
')' ) ) {
658 assert( *(scursor-1) ==
')' || *(scursor-1) ==
'(' );
661 switch ( *(scursor-1) ) {
667 if ( commentNestingDepth > 1 ) {
669 result += QLatin1Char(
')' );
673 afterLastClosingParenPos = scursor;
674 --commentNestingDepth;
680 maybeCmnt += cmntPart;
681 maybeCmnt += QLatin1Char(
'(' );
683 ++commentNestingDepth;
685 default: assert( 0 );
689 if ( afterLastClosingParenPos ) {
690 scursor = afterLastClosingParenPos;
692 scursor = oldscursor;
703 bool parsePhrase(
const char* &scursor,
const char *
const send,
704 QString &result,
bool isCRLF )
707 None, Phrase, Atom, EncodedWord, QuotedString
711 QByteArray lang, charset;
712 const char *successfullyParsed = 0;
714 const char *oldscursor;
717 bool lastWasEncodedWord =
false;
719 while ( scursor != send ) {
720 char ch = *scursor++;
723 if ( found == None ) {
727 if ( scursor != send && ( *scursor ==
' ' || *scursor ==
'\t' ) ) {
728 result += QLatin1String(
". " );
730 result += QLatin1Char(
'.' );
732 successfullyParsed = scursor;
737 if ( parseGenericQuotedString( scursor, send, tmp, isCRLF,
'"',
'"' ) ) {
738 successfullyParsed = scursor;
739 assert( *(scursor-1) ==
'"' );
742 found = QuotedString;
749 result += QLatin1Char(
' ');
754 lastWasEncodedWord =
false;
760 if ( found == None ) {
763 result += QLatin1Char(
' ');
772 if ( parseComment( scursor, send, tmp, isCRLF,
774 successfullyParsed = scursor;
775 lastWasEncodedWord =
false;
777 if ( found == None ) {
780 scursor = successfullyParsed;
787 oldscursor = scursor;
790 if ( parseEncodedWord( scursor, send, tmp, lang, charset ) ) {
791 successfullyParsed = scursor;
800 if ( !lastWasEncodedWord ) {
801 result += QLatin1Char(
' ');
805 default: assert( 0 );
807 lastWasEncodedWord =
true;
812 scursor = oldscursor;
819 if ( parseAtom( scursor, send, tmp,
true ) ) {
820 successfullyParsed = scursor;
830 result += QLatin1Char(
' ');
835 lastWasEncodedWord =
false;
838 if ( found == None ) {
841 scursor = successfullyParsed;
846 eatWhiteSpace( scursor, send );
849 return found != None;
853 bool parseDotAtom(
const char* &scursor,
const char *
const send,
854 QString &result,
bool isCRLF )
856 eatCFWS( scursor, send, isCRLF );
859 const char *successfullyParsed;
862 if ( !parseAtom( scursor, send, tmp,
false ) ) {
866 successfullyParsed = scursor;
868 while ( scursor != send ) {
871 if ( scursor == send || *scursor !=
'.' ) {
876 if ( scursor == send || !isAText( *scursor ) ) {
880 scursor = successfullyParsed;
886 if ( !parseAtom( scursor, send, maybeAtom,
false ) ) {
887 scursor = successfullyParsed;
891 result += QLatin1Char(
'.');
893 successfullyParsed = scursor;
896 scursor = successfullyParsed;
900 void eatCFWS(
const char* &scursor,
const char *
const send,
bool isCRLF )
904 while ( scursor != send ) {
905 const char *oldscursor = scursor;
907 char ch = *scursor++;
917 if ( parseComment( scursor, send, dummy, isCRLF,
false ) ) {
920 scursor = oldscursor;
924 scursor = oldscursor;
930 bool parseDomain(
const char* &scursor,
const char *
const send,
931 QString &result,
bool isCRLF )
933 eatCFWS( scursor, send, isCRLF );
934 if ( scursor == send ) {
944 if ( *scursor ==
'[' ) {
946 QString maybeDomainLiteral;
949 while ( parseGenericQuotedString( scursor, send, maybeDomainLiteral,
950 isCRLF,
'[',
']' ) ) {
951 if ( scursor == send ) {
953 if ( *(scursor-1) ==
']' ) {
955 result = maybeDomainLiteral;
964 if ( *(scursor-1) ==
'[' ) {
965 maybeDomainLiteral += QLatin1Char(
'[');
969 result = maybeDomainLiteral;
974 QString maybeDotAtom;
975 if ( parseDotAtom( scursor, send, maybeDotAtom, isCRLF ) ) {
976 result = maybeDotAtom;
978 if ( scursor != send && *scursor ==
'.' ) {
979 result += QLatin1Char(
'.');
988 bool parseObsRoute(
const char* &scursor,
const char*
const send,
989 QStringList &result,
bool isCRLF,
bool save )
991 while ( scursor != send ) {
992 eatCFWS( scursor, send, isCRLF );
993 if ( scursor == send ) {
998 if ( *scursor ==
',' ) {
1001 result.append( QString() );
1007 if ( *scursor ==
':' ) {
1010 result.append( QString() );
1016 if ( *scursor !=
'@' ) {
1022 QString maybeDomain;
1023 if ( !parseDomain( scursor, send, maybeDomain, isCRLF ) ) {
1027 result.append( maybeDomain );
1031 eatCFWS( scursor, send, isCRLF );
1032 if ( scursor == send ) {
1035 if ( *scursor ==
':' ) {
1039 if ( *scursor ==
',' ) {
1047 bool parseAddrSpec(
const char* &scursor,
const char *
const send,
1048 AddrSpec &result,
bool isCRLF )
1057 QString maybeLocalPart;
1060 while ( scursor != send ) {
1062 eatCFWS( scursor, send, isCRLF );
1064 char ch = *scursor++;
1067 maybeLocalPart += QLatin1Char(
'.');
1076 if ( parseGenericQuotedString( scursor, send, tmp, isCRLF,
'"',
'"' ) ) {
1077 maybeLocalPart += tmp;
1086 if ( parseAtom( scursor, send, tmp,
false ) ) {
1087 maybeLocalPart += tmp;
1104 assert( *(scursor-1) ==
'@' );
1106 QString maybeDomain;
1107 if ( !parseDomain( scursor, send, maybeDomain, isCRLF ) ) {
1111 result.localPart = maybeLocalPart;
1112 result.domain = maybeDomain;
1117 bool parseAngleAddr(
const char* &scursor,
const char *
const send,
1118 AddrSpec &result,
bool isCRLF )
1121 eatCFWS( scursor, send, isCRLF );
1122 if ( scursor == send || *scursor !=
'<' ) {
1127 eatCFWS( scursor, send, isCRLF );
1128 if ( scursor == send ) {
1132 if ( *scursor ==
'@' || *scursor ==
',' ) {
1134 KMIME_WARN <<
"obsolete source route found! ignoring.";
1136 if ( !parseObsRoute( scursor, send, dummy,
1141 if ( scursor == send ) {
1147 AddrSpec maybeAddrSpec;
1148 if ( !parseAddrSpec( scursor, send, maybeAddrSpec, isCRLF ) ) {
1152 eatCFWS( scursor, send, isCRLF );
1153 if ( scursor == send || *scursor !=
'>' ) {
1158 result = maybeAddrSpec;
1163 static QString stripQuotes(
const QString &input )
1165 const QLatin1Char quotes(
'"' );
1166 if ( input.startsWith( quotes ) && input.endsWith( quotes ) ) {
1167 QString stripped( input.mid( 1, input.size() - 2 ) );
1173 bool parseMailbox(
const char* &scursor,
const char *
const send,
1174 Mailbox &result,
bool isCRLF )
1176 eatCFWS( scursor, send, isCRLF );
1177 if ( scursor == send ) {
1181 AddrSpec maybeAddrSpec;
1182 QString maybeDisplayName;
1185 const char * oldscursor = scursor;
1186 if ( parseAddrSpec( scursor, send, maybeAddrSpec, isCRLF ) ) {
1189 eatWhiteSpace( scursor, send );
1190 if ( scursor != send && *scursor ==
'(' ) {
1192 if ( !parseComment( scursor, send, maybeDisplayName, isCRLF,
true ) ) {
1196 result.
setName( stripQuotes( maybeDisplayName ) );
1199 scursor = oldscursor;
1202 if ( !parsePhrase( scursor, send, maybeDisplayName, isCRLF ) ) {
1204 maybeDisplayName.clear();
1205 scursor = oldscursor;
1208 eatCFWS( scursor, send, isCRLF );
1209 if ( scursor == send ) {
1215 if ( !parseAngleAddr( scursor, send, maybeAddrSpec, isCRLF ) ) {
1219 if ( maybeDisplayName.isNull() ) {
1221 eatWhiteSpace( scursor, send );
1222 if ( scursor != send && *scursor ==
'(' ) {
1224 if ( !parseComment( scursor, send, maybeDisplayName, isCRLF,
true ) ) {
1230 result.
setName( stripQuotes( maybeDisplayName ) );
1235 bool parseGroup(
const char* &scursor,
const char *
const send,
1236 Address &result,
bool isCRLF )
1243 eatCFWS( scursor, send, isCRLF );
1244 if ( scursor == send ) {
1249 QString maybeDisplayName;
1250 if ( !parsePhrase( scursor, send, maybeDisplayName, isCRLF ) ) {
1255 eatCFWS( scursor, send, isCRLF );
1256 if ( scursor == send || *scursor !=
':' ) {
1266 while ( scursor != send ) {
1267 eatCFWS( scursor, send, isCRLF );
1268 if ( scursor == send ) {
1273 if ( *scursor ==
',' ) {
1279 if ( *scursor ==
';' ) {
1285 if ( !parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) {
1288 result.mailboxList.append( maybeMailbox );
1290 eatCFWS( scursor, send, isCRLF );
1292 if ( scursor == send ) {
1296 if ( *scursor ==
';' ) {
1301 if ( *scursor ==
',' ) {
1308 bool parseAddress(
const char* &scursor,
const char *
const send,
1309 Address &result,
bool isCRLF )
1313 eatCFWS( scursor, send, isCRLF );
1314 if ( scursor == send ) {
1320 const char * oldscursor = scursor;
1321 if ( parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) {
1323 result.displayName.clear();
1324 result.mailboxList.append( maybeMailbox );
1327 scursor = oldscursor;
1329 Address maybeAddress;
1332 if ( !parseGroup( scursor, send, maybeAddress, isCRLF ) ) {
1336 result = maybeAddress;
1340 bool parseAddressList(
const char* &scursor,
const char *
const send,
1341 AddressList &result,
bool isCRLF )
1343 while ( scursor != send ) {
1344 eatCFWS( scursor, send, isCRLF );
1346 if ( scursor == send ) {
1350 if ( *scursor ==
',' ) {
1355 if ( *scursor ==
';' ) {
1361 Address maybeAddress;
1362 if ( !parseAddress( scursor, send, maybeAddress, isCRLF ) ) {
1365 result.append( maybeAddress );
1367 eatCFWS( scursor, send, isCRLF );
1369 if ( scursor == send ) {
1373 if ( *scursor ==
',' ) {
1380 static QString asterisk = QString::fromLatin1(
"*0*", 1 );
1381 static QString asteriskZero = QString::fromLatin1(
"*0*", 2 );
1387 bool parseParameter(
const char* &scursor,
const char *
const send,
1388 QPair<QString,QStringOrQPair> &result,
bool isCRLF )
1400 eatCFWS( scursor, send, isCRLF );
1401 if ( scursor == send ) {
1409 QString maybeAttribute;
1410 if ( !parseToken( scursor, send, maybeAttribute,
false ) ) {
1414 eatCFWS( scursor, send, isCRLF );
1416 if ( scursor == send || *scursor !=
'=' ) {
1421 eatCFWS( scursor, send, isCRLF );
1422 if ( scursor == send ) {
1424 if ( maybeAttribute.endsWith( asterisk ) ) {
1425 KMIME_WARN <<
"attribute ends with \"*\", but value is empty!"
1426 "Chopping away \"*\".";
1427 maybeAttribute.truncate( maybeAttribute.length() - 1 );
1429 result = qMakePair( maybeAttribute.toLower(), QStringOrQPair() );
1433 const char * oldscursor = scursor;
1438 QStringOrQPair maybeValue;
1439 if ( *scursor ==
'"' ) {
1442 if ( maybeAttribute.endsWith( asterisk ) ) {
1446 KMIME_WARN <<
"attribute ends with \"*\", but value is a quoted-string!"
1447 "Chopping away \"*\".";
1448 maybeAttribute.truncate( maybeAttribute.length() - 1 );
1451 if ( !parseGenericQuotedString( scursor, send, maybeValue.qstring, isCRLF ) ) {
1452 scursor = oldscursor;
1453 result = qMakePair( maybeAttribute.toLower(), QStringOrQPair() );
1458 if ( !parseToken( scursor, send, maybeValue.qpair,
false ) ) {
1459 scursor = oldscursor;
1460 result = qMakePair( maybeAttribute.toLower(), QStringOrQPair() );
1465 result = qMakePair( maybeAttribute.toLower(), maybeValue );
1471 bool parseRawParameterList(
const char* &scursor,
const char *
const send,
1472 QMap<QString,QStringOrQPair> &result,
1485 while ( scursor != send ) {
1486 eatCFWS( scursor, send, isCRLF );
1488 if ( scursor == send ) {
1492 if ( *scursor ==
';' ) {
1497 QPair<QString,QStringOrQPair> maybeParameter;
1498 if ( !parseParameter( scursor, send, maybeParameter, isCRLF ) ) {
1506 if ( maybeParameter.first.isNull() ) {
1509 while ( scursor != send ) {
1510 if ( *scursor++ ==
';' ) {
1521 result.insert( maybeParameter.first, maybeParameter.second );
1523 eatCFWS( scursor, send, isCRLF );
1525 if ( scursor == send ) {
1529 if ( *scursor ==
';' ) {
1536 static void decodeRFC2231Value(
Codec* &rfc2231Codec,
1537 QTextCodec* &textcodec,
1538 bool isContinuation, QString &value,
1539 QPair<const char*,int> &source, QByteArray& charset )
1545 const char * decBegin = source.first;
1546 const char * decCursor = decBegin;
1547 const char * decEnd = decCursor + source.second;
1549 if ( !isContinuation ) {
1551 while ( decCursor != decEnd ) {
1552 if ( *decCursor ==
'\'' ) {
1559 if ( decCursor == decEnd ) {
1562 KMIME_WARN <<
"No charset in extended-initial-value."
1563 "Assuming \"iso-8859-1\".";
1564 value += QString::fromLatin1( decBegin, source.second );
1568 charset = QByteArray( decBegin, decCursor - decBegin );
1570 const char * oldDecCursor = ++decCursor;
1572 while ( decCursor != decEnd ) {
1573 if ( *decCursor ==
'\'' ) {
1579 if ( decCursor == decEnd ) {
1580 KMIME_WARN <<
"No language in extended-initial-value."
1581 "Trying to recover.";
1582 decCursor = oldDecCursor;
1594 bool matchOK =
false;
1595 textcodec = KGlobal::charsets()->codecForName( QLatin1String( charset ), matchOK );
1598 KMIME_WARN_UNKNOWN( Charset, charset );
1602 if ( !rfc2231Codec ) {
1604 assert( rfc2231Codec );
1608 value += QString::fromLatin1( decCursor, decEnd - decCursor );
1621 QByteArray::Iterator bit = buffer.begin();
1622 QByteArray::ConstIterator bend = buffer.end();
1624 if ( !dec->
decode( decCursor, decEnd, bit, bend ) ) {
1625 KMIME_WARN << rfc2231Codec->
name()
1626 <<
"codec lies about its maxDecodedSizeFor()" << endl
1627 <<
"result may be truncated";
1630 value += textcodec->toUnicode( buffer.begin(), bit - buffer.begin() );
1641 bool parseParameterListWithCharset(
const char* &scursor,
1642 const char *
const send,
1643 QMap<QString,QString> &result,
1644 QByteArray& charset,
bool isCRLF )
1647 QMap<QString,QStringOrQPair> rawParameterList;
1648 if (!parseRawParameterList( scursor, send, rawParameterList, isCRLF ) ) {
1652 if ( rawParameterList.isEmpty() ) {
1661 Codec * rfc2231Codec = 0;
1662 QTextCodec * textcodec = 0;
1666 NoMode = 0x0, Continued = 0x1, Encoded = 0x2
1675 QMap<QString,QStringOrQPair>::Iterator it, end = rawParameterList.end();
1677 for ( it = rawParameterList.begin() ; it != end ; ++it ) {
1678 if ( attribute.isNull() || !it.key().startsWith( attribute ) ) {
1684 if ( !attribute.isNull() ) {
1685 result.insert( attribute, value );
1689 attribute = it.key();
1691 EncodingMode encodingMode = NoEncoding;
1694 if ( attribute.endsWith( asterisk ) ) {
1695 attribute.truncate( attribute.length() - 1 );
1697 encodingMode = RFC2231;
1700 if( !(*it).qstring.isNull() && (*it).qstring.contains( QLatin1String(
"=?" ) ) ) {
1702 encodingMode = RFC2047;
1705 if ( attribute.endsWith( asteriskZero ) ) {
1706 attribute.truncate( attribute.length() - 2 );
1712 if ( mode & Encoded ) {
1713 if ( encodingMode == RFC2231 ) {
1714 decodeRFC2231Value( rfc2231Codec, textcodec,
1716 value, (*it).qpair, charset );
1718 else if ( encodingMode == RFC2047 ) {
1723 if ( (*it).qpair.first ) {
1724 value += QString::fromLatin1( (*it).qpair.first, (*it).qpair.second );
1726 value += (*it).qstring;
1734 if ( !(mode & Continued) ) {
1736 result.insert( attribute, value );
1746 if ( it.key().endsWith( asterisk ) ) {
1748 decodeRFC2231Value( rfc2231Codec, textcodec,
1750 value, (*it).qpair, charset );
1753 if ( (*it).qpair.first ) {
1754 value += QString::fromLatin1( (*it).qpair.first, (*it).qpair.second );
1756 value += (*it).qstring;
1763 if ( !attribute.isNull() ) {
1764 result.insert( attribute, value );
1771 bool parseParameterList(
const char* &scursor,
const char *
const send,
1772 QMap<QString,QString> &result,
bool isCRLF )
1775 return parseParameterListWithCharset( scursor, send, result, charset, isCRLF );
1778 static const char *
const stdDayNames[] = {
1779 "Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"
1781 static const int stdDayNamesLen =
sizeof stdDayNames /
sizeof *stdDayNames;
1783 static bool parseDayName(
const char* &scursor,
const char *
const send )
1786 if ( send - scursor < 3 ) {
1790 for (
int i = 0 ; i < stdDayNamesLen ; ++i ) {
1791 if ( qstrnicmp( scursor, stdDayNames[i], 3 ) == 0 ) {
1801 static const char *
const stdMonthNames[] = {
1802 "Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
1803 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
1805 static const int stdMonthNamesLen =
1806 sizeof stdMonthNames /
sizeof *stdMonthNames;
1808 static bool parseMonthName(
const char* &scursor,
const char *
const send,
1812 if ( send - scursor < 3 ) {
1816 for ( result = 0 ; result < stdMonthNamesLen ; ++result ) {
1817 if ( qstrnicmp( scursor, stdMonthNames[result], 3 ) == 0 ) {
1827 static const struct {
1828 const char * tzName;
1829 long int secsEastOfGMT;
1876 static const int timeZonesLen =
sizeof timeZones /
sizeof *timeZones;
1878 static bool parseAlphaNumericTimeZone(
const char* &scursor,
1879 const char *
const send,
1880 long int &secsEastOfGMT,
1881 bool &timeZoneKnown )
1884 if ( *scursor ==
'"' ) {
1887 if ( scursor == send ) {
1892 QPair<const char*,int> maybeTimeZone( 0, 0 );
1893 if ( !parseToken( scursor, send, maybeTimeZone,
false ) ) {
1896 for (
int i = 0 ; i < timeZonesLen ; ++i ) {
1897 if ( qstrnicmp( timeZones[i].tzName,
1898 maybeTimeZone.first, maybeTimeZone.second ) == 0 ) {
1899 scursor += maybeTimeZone.second;
1900 secsEastOfGMT = timeZones[i].secsEastOfGMT;
1901 timeZoneKnown =
true;
1903 if ( *scursor ==
'"' ) {
1912 KMIME_WARN_UNKNOWN( time zone,
1913 QByteArray( maybeTimeZone.first, maybeTimeZone.second ) );
1915 timeZoneKnown =
false;
1920 int parseDigits(
const char* &scursor,
const char *
const send,
int &result )
1924 for ( ; scursor != send && isdigit( *scursor ) ; scursor++, digits++ ) {
1926 result += int( *scursor -
'0' );
1931 static bool parseTimeOfDay(
const char* &scursor,
const char *
const send,
1932 int &hour,
int &min,
int &sec,
bool isCRLF=
false )
1939 if ( !parseDigits( scursor, send, hour ) ) {
1943 eatCFWS( scursor, send, isCRLF );
1944 if ( scursor == send || *scursor !=
':' ) {
1949 eatCFWS( scursor, send, isCRLF );
1950 if ( scursor == send ) {
1957 if ( !parseDigits( scursor, send, min ) ) {
1961 eatCFWS( scursor, send, isCRLF );
1962 if ( scursor == send ) {
1969 if ( *scursor ==
':' ) {
1972 eatCFWS( scursor, send, isCRLF );
1973 if ( scursor == send ) {
1977 if ( !parseDigits( scursor, send, sec ) ) {
1987 bool parseTime(
const char* &scursor,
const char * send,
1988 int &hour,
int &min,
int &sec,
long int &secsEastOfGMT,
1989 bool &timeZoneKnown,
bool isCRLF )
2001 eatCFWS( scursor, send, isCRLF );
2002 if ( scursor == send ) {
2006 if ( !parseTimeOfDay( scursor, send, hour, min, sec, isCRLF ) ) {
2010 eatCFWS( scursor, send, isCRLF );
2012 if ( ( scursor == send ) || isdigit( *scursor ) ) {
2013 timeZoneKnown =
false;
2018 timeZoneKnown =
true;
2019 if ( *scursor ==
'+' || *scursor ==
'-' ) {
2021 const char sign = *scursor++;
2024 if ( parseDigits( scursor, send, maybeTimeZone ) != 4 ) {
2027 secsEastOfGMT = 60 * ( maybeTimeZone / 100 * 60 + maybeTimeZone % 100 );
2028 if ( sign ==
'-' ) {
2029 secsEastOfGMT *= -1;
2030 if ( secsEastOfGMT == 0 ) {
2031 timeZoneKnown =
false;
2036 if ( !parseAlphaNumericTimeZone( scursor, send, secsEastOfGMT, timeZoneKnown ) ) {
2043 bool parseDateTime(
const char* &scursor,
const char *
const send,
2044 KDateTime &result,
bool isCRLF )
2056 result = KDateTime();
2057 QDateTime maybeDateTime;
2059 eatCFWS( scursor, send, isCRLF );
2060 if ( scursor == send ) {
2067 if ( parseDayName( scursor, send ) ) {
2068 eatCFWS( scursor, send, isCRLF );
2069 if ( scursor == send ) {
2073 if ( *scursor ==
',' ) {
2075 eatCFWS( scursor, send, isCRLF );
2079 int maybeMonth = -1;
2080 bool asctimeFormat =
false;
2083 if ( !isdigit( *scursor ) && parseMonthName( scursor, send, maybeMonth ) ) {
2084 asctimeFormat =
true;
2085 eatCFWS( scursor, send, isCRLF );
2092 if ( !parseDigits( scursor, send, maybeDay ) ) {
2096 eatCFWS( scursor, send, isCRLF );
2097 if ( scursor == send ) {
2102 if ( *scursor ==
',' ) {
2109 if ( !asctimeFormat && !parseMonthName( scursor, send, maybeMonth ) ) {
2112 if ( scursor == send ) {
2115 assert( maybeMonth >= 0 ); assert( maybeMonth <= 11 );
2118 eatCFWS( scursor, send, isCRLF );
2119 if ( scursor == send ) {
2124 bool timeAfterYear =
true;
2125 if ( ( send - scursor > 3 ) && ( ( scursor[1] ==
':' ) || ( scursor[2] ==
':' ) ) ) {
2126 timeAfterYear =
false;
2134 if ( timeAfterYear && !parseDigits( scursor, send, maybeYear ) ) {
2138 eatCFWS( scursor, send, isCRLF );
2139 if ( scursor == send ) {
2146 int maybeHour, maybeMinute, maybeSecond;
2147 long int secsEastOfGMT;
2148 bool timeZoneKnown =
true;
2150 if ( !parseTime( scursor, send,
2151 maybeHour, maybeMinute, maybeSecond,
2152 secsEastOfGMT, timeZoneKnown, isCRLF ) ) {
2157 if ( !timeAfterYear ) {
2158 eatCFWS( scursor, send, isCRLF );
2159 if ( scursor == send ) {
2163 if ( !parseDigits( scursor, send, maybeYear ) ) {
2169 if ( maybeYear < 50 ) {
2171 }
else if ( maybeYear < 1000 ) {
2175 if ( maybeYear < 1900 ) {
2179 maybeDateTime.setDate( QDate( maybeYear, maybeMonth, maybeDay ) );
2180 maybeDateTime.setTime( QTime( maybeHour, maybeMinute, maybeSecond ) );
2182 if ( !maybeDateTime.isValid() )
2185 result = KDateTime( maybeDateTime, KDateTime::Spec( KDateTime::OffsetFromUTC, secsEastOfGMT ) );
2186 if ( !result.isValid() )
2193 int endOfFieldBody = 0;
2194 bool folded =
false;
2197 int startOfFieldBody = head.indexOf(
':' );
2198 const int endOfFieldHeader = startOfFieldBody;
2200 if ( startOfFieldBody > -1 ) {
2202 if ( head[startOfFieldBody] ==
' ' ) {
2205 endOfFieldBody = findHeaderLineEnd( head, startOfFieldBody, &folded );
2207 QByteArray rawType = head.left( endOfFieldHeader );
2208 QByteArray rawFieldBody = head.mid( startOfFieldBody, endOfFieldBody - startOfFieldBody );
2213 if ( !rawType.isEmpty() ) {
2214 header = HeaderFactory::self()->createHeader( rawType );
2222 head.remove( 0, endOfFieldBody + 1 );
2230 void extractHeaderAndBody(
const QByteArray &content, QByteArray &header, QByteArray &body )
2236 if ( content.startsWith(
'\n' ) ) {
2237 body = content.right( content.length() - 1 );
2241 int pos = content.indexOf(
"\n\n", 0 );
2243 header = content.left( ++pos );
2244 body = content.mid( pos + 1, content.length() - pos - 1 );
2255 QByteArray copy = head;
2256 while( ( h = extractFirstHeader( copy ) ) ) {