10#include "kmime_header_parsing.h"
12#include "kmime_headerfactory_p.h"
14#include "kmime_util.h"
15#include "kmime_util_p.h"
16#include "kmime_codecs_p.h"
18#include "kmime_debug.h"
19#include "kmime_warning_p.h"
24#include <QStringDecoder>
31using namespace KMime::Types;
39 struct KMIME_EXPORT QStringOrQPair {
40 QStringOrQPair() : qstring(), qpair(nullptr, 0) {}
42 QPair<const char *, int> qpair;
46namespace HeaderParsing
50bool parseEncodedWord(
const char *&scursor,
const char *
const send,
55 assert(*(scursor - 1) ==
'=');
72 const char *charsetStart = scursor;
73 const char *languageStart =
nullptr;
77 for (; scursor != send ; scursor++) {
78 if (*scursor ==
'?') {
80 }
else if (*scursor ==
'*' && languageStart ==
nullptr) {
81 languageStart = scursor + 1;
86 if (scursor == send || *scursor !=
'?') {
88 KMIME_WARN_PREMATURE_END_OF(EncodedWord);
94 QByteArray maybeLanguage(languageStart, scursor - languageStart);
98 (languageStart ? languageStart - 1 : scursor) - charsetStart);
107 const char *encodingStart = scursor;
110 for (; scursor != send ; scursor++) {
111 if (*scursor ==
'?') {
117 if (scursor == send || *scursor !=
'?') {
119 KMIME_WARN_PREMATURE_END_OF(EncodedWord);
124 QByteArray maybeEncoding(encodingStart, scursor - encodingStart);
137 const char *encodedTextStart = scursor;
140 for (; scursor != send ; scursor++) {
141 if (*scursor ==
'?') {
142 if (scursor + 1 != send) {
143 if (*(scursor + 1) !=
'=') {
144 KMIME_WARN <<
"Stray '?' in q-encoded word, ignoring this.";
151 KMIME_WARN_PREMATURE_END_OF(EncodedWord);
157 if (*(scursor - 2) !=
'?' || *(scursor - 1) !=
'=' ||
158 scursor < encodedTextStart + 2) {
159 KMIME_WARN_PREMATURE_END_OF(EncodedWord);
164 const char *
const encodedTextEnd = scursor - 2;
174 KMIME_WARN_UNKNOWN(Encoding, maybeEncoding);
189 usedCS = cachedCharset(defaultCS);
193 usedCS = cachedCharset(defaultCS);
196 usedCS = cachedCharset(maybeCharset);
201 KMIME_WARN_UNKNOWN(Charset, maybeCharset);
209 int encodedTextLength = encodedTextEnd - encodedTextStart;
212 char *bbegin = buffer.
data();
213 char *bend = bbegin + buffer.
length();
220 if (!
dec->decode(encodedTextStart, encodedTextEnd, bbegin, bend)) {
221 KMIME_WARN << codec->
name() <<
"codec lies about its maxDecodedSizeFor("
222 << encodedTextLength <<
")\nresult may be truncated";
230 language = maybeLanguage;
235static inline void eatWhiteSpace(
const char *&scursor,
const char *
const send)
237 while (scursor != send &&
238 (*scursor ==
' ' || *scursor ==
'\n' ||
239 *scursor ==
'\t' || *scursor ==
'\r')) {
244bool parseAtom(
const char*&scursor,
const char *
const send,
247 QPair<const char *, int> maybeResult;
249 if (parseAtom(scursor, send, maybeResult, allow8Bit)) {
250 result =
QByteArray(maybeResult.first, maybeResult.second);
257bool parseAtom(
const char*&scursor,
const char *
const send,
258 QPair<const char *, int> &result,
bool allow8Bit)
260 bool success =
false;
261 const char *
start = scursor;
263 while (scursor != send) {
264 signed char ch = *scursor++;
265 if (ch > 0 && isAText(ch)) {
268 }
else if (allow8Bit && ch < 0) {
280 result.first =
start;
281 result.second = scursor -
start;
285bool parseToken(
const char*&scursor,
const char *
const send,
288 QPair<const char *, int> maybeResult;
290 if (parseToken(scursor, send, maybeResult, flags)) {
291 result =
QByteArray(maybeResult.first, maybeResult.second);
298bool parseToken(
const char*&scursor,
const char *
const send,
299 QPair<const char *, int> &result, ParseTokenFlags flags)
301 bool success =
false;
302 const char *
start = scursor;
304 while (scursor != send) {
305 signed char ch = *scursor++;
306 if (ch > 0 && isTText(ch)) {
309 }
else if ((flags & ParseTokenAllow8Bit) && ch < 0) {
313 }
else if ((flags & ParseTokenRelaxedTText) && ch ==
'/') {
323 result.first =
start;
324 result.second = scursor -
start;
328#define READ_ch_OR_FAIL if ( scursor == send ) { \
329 KMIME_WARN_PREMATURE_END_OF( GenericQuotedString ); \
339bool parseGenericQuotedString(
const char *&scursor,
const char *
const send,
341 const char openChar,
const char closeChar)
348 assert(*(scursor - 1) == openChar || *(scursor - 1) == closeChar);
350 while (scursor != send) {
351 char ch = *scursor++;
353 if (ch == closeChar || ch == openChar) {
363 KMIME_WARN_IF_8BIT(ch);
384 if (ch ==
' ' || ch ==
'\t') {
393 KMIME_WARN_NON_FOLDING(CRLF);
411 if (!isCRLF && (ch ==
' ' || ch ==
'\t')) {
427 if (scursor == send) {
431 const char *oldscursor = scursor;
435 if (*scursor++ ==
'?') {
437 if (parseEncodedWord(scursor, send, tmp, lang, charset)) {
440 if (scursor == send) {
442 }
else if (*scursor++ ==
' ') {
443 if (scursor == send) {
446 }
else if (*scursor++ ==
'=') {
447 if (scursor == send) {
451 }
else if (*scursor++ ==
'?') {
466 scursor = oldscursor;
469 scursor = oldscursor;
475 KMIME_WARN_IF_8BIT(ch);
487bool parseComment(
const char *&scursor,
const char *
const send,
488 QString &result,
bool isCRLF,
bool reallySave)
490 int commentNestingDepth = 1;
491 const char *afterLastClosingParenPos =
nullptr;
493 const char *oldscursor = scursor;
495 assert(*(scursor - 1) ==
'(');
497 while (commentNestingDepth) {
499 if (parseGenericQuotedString(scursor, send, cmntPart, isCRLF,
'(',
')')) {
500 assert(*(scursor - 1) ==
')' || *(scursor - 1) ==
'(');
503 switch (*(scursor - 1)) {
509 if (commentNestingDepth > 1) {
515 afterLastClosingParenPos = scursor;
516 --commentNestingDepth;
522 maybeCmnt += cmntPart;
525 ++commentNestingDepth;
531 if (afterLastClosingParenPos) {
532 scursor = afterLastClosingParenPos;
534 scursor = oldscursor;
545bool parsePhrase(
const char *&scursor,
const char *
const send,
549 None, Phrase, Atom, EncodedWord, QuotedString
555 QPair<const char *, int> tmpAtom;
556 const char *successfullyParsed =
nullptr;
558 const char *oldscursor;
561 bool lastWasEncodedWord =
false;
563 while (scursor != send) {
564 char ch = *scursor++;
571 if (scursor != send && (*scursor ==
' ' || *scursor ==
'\t')) {
576 successfullyParsed = scursor;
581 if (parseGenericQuotedString(scursor, send, tmp, isCRLF,
'"',
'"')) {
582 successfullyParsed = scursor;
583 assert(*(scursor - 1) ==
'"');
586 found = QuotedString;
598 lastWasEncodedWord =
false;
616 if (parseComment(scursor, send, tmp, isCRLF,
618 successfullyParsed = scursor;
619 lastWasEncodedWord =
false;
624 scursor = successfullyParsed;
631 oldscursor = scursor;
634 if (parseEncodedWord(scursor, send, tmp, lang, charset)) {
635 successfullyParsed = scursor;
644 if (!lastWasEncodedWord) {
651 lastWasEncodedWord =
true;
656 scursor = oldscursor;
663 if (parseAtom(scursor, send, tmpAtom,
true )) {
664 successfullyParsed = scursor;
679 lastWasEncodedWord =
false;
685 scursor = successfullyParsed;
690 eatWhiteSpace(scursor, send);
693 return found !=
None;
696bool parseDotAtom(
const char *&scursor,
const char *
const send,
699 eatCFWS(scursor, send, isCRLF);
702 const char *successfullyParsed;
705 if (!parseAtom(scursor, send, tmp,
false )) {
709 successfullyParsed = scursor;
711 while (scursor != send) {
714 if (scursor == send || *scursor !=
'.') {
719 if (scursor == send || !isAText(*scursor)) {
723 scursor = successfullyParsed;
729 if (!parseAtom(scursor, send, maybeAtom,
false )) {
730 scursor = successfullyParsed;
736 successfullyParsed = scursor;
739 scursor = successfullyParsed;
743void eatCFWS(
const char *&scursor,
const char *
const send,
bool isCRLF)
747 while (scursor != send) {
748 const char *oldscursor = scursor;
750 char ch = *scursor++;
760 if (parseComment(scursor, send, dummy, isCRLF,
false )) {
763 scursor = oldscursor;
767 scursor = oldscursor;
773bool parseDomain(
const char *&scursor,
const char *
const send,
776 eatCFWS(scursor, send, isCRLF);
777 if (scursor == send) {
787 if (*scursor ==
'[') {
792 while (parseGenericQuotedString(scursor, send, maybeDomainLiteral,
794 if (scursor == send) {
796 if (*(scursor - 1) ==
']') {
798 result = maybeDomainLiteral;
807 if (*(scursor - 1) ==
'[') {
812 result = maybeDomainLiteral;
818 if (parseDotAtom(scursor, send, maybeDotAtom, isCRLF)) {
820 if (scursor != send && *scursor ==
'.') {
831bool parseObsRoute(
const char *&scursor,
const char *
const send,
834 while (scursor != send) {
835 eatCFWS(scursor, send, isCRLF);
836 if (scursor == send) {
841 if (*scursor ==
',') {
850 if (*scursor ==
':') {
859 if (*scursor !=
'@') {
866 if (!parseDomain(scursor, send, maybeDomain, isCRLF)) {
870 result.
append(maybeDomain);
874 eatCFWS(scursor, send, isCRLF);
875 if (scursor == send) {
878 if (*scursor ==
':') {
882 if (*scursor ==
',') {
890bool parseAddrSpec(
const char *&scursor,
const char *
const send,
891 AddrSpec &result,
bool isCRLF)
902 QPair<const char *, int> tmpAtom;
904 while (scursor != send) {
906 eatCFWS(scursor, send, isCRLF);
908 char ch = *scursor++;
920 if (parseGenericQuotedString(scursor, send, tmp, isCRLF,
'"',
'"')) {
921 maybeLocalPart += tmp;
929 if (parseAtom(scursor, send, tmpAtom,
false )) {
948 assert(*(scursor - 1) ==
'@');
951 if (!parseDomain(scursor, send, maybeDomain, isCRLF)) {
955 result.localPart = maybeLocalPart;
956 result.domain = maybeDomain;
961bool parseAngleAddr(
const char *&scursor,
const char *
const send,
962 AddrSpec &result,
bool isCRLF)
965 eatCFWS(scursor, send, isCRLF);
966 if (scursor == send || *scursor !=
'<') {
971 eatCFWS(scursor, send, isCRLF);
972 if (scursor == send) {
976 if (*scursor ==
'@' || *scursor ==
',') {
978 KMIME_WARN <<
"obsolete source route found! ignoring.";
980 if (!parseObsRoute(scursor, send, dummy,
985 if (scursor == send) {
991 AddrSpec maybeAddrSpec;
992 if (!parseAddrSpec(scursor, send, maybeAddrSpec, isCRLF)) {
996 eatCFWS(scursor, send, isCRLF);
997 if (scursor == send || *scursor !=
'>') {
1002 result = maybeAddrSpec;
1018bool parseMailbox(
const char *&scursor,
const char *
const send,
1021 eatCFWS(scursor, send, isCRLF);
1022 if (scursor == send) {
1026 AddrSpec maybeAddrSpec;
1030 const char *oldscursor = scursor;
1031 if (parseAddrSpec(scursor, send, maybeAddrSpec, isCRLF)) {
1034 eatWhiteSpace(scursor, send);
1035 if (scursor != send && *scursor ==
'(') {
1037 if (!parseComment(scursor, send, maybeDisplayName, isCRLF,
true )) {
1041 result.
setName(stripQuotes(maybeDisplayName));
1044 scursor = oldscursor;
1047 if (!parsePhrase(scursor, send, maybeDisplayName, isCRLF)) {
1049 maybeDisplayName.
clear();
1050 scursor = oldscursor;
1053 eatCFWS(scursor, send, isCRLF);
1054 if (scursor == send) {
1060 if (!parseAngleAddr(scursor, send, maybeAddrSpec, isCRLF)) {
1064 if (maybeDisplayName.
isNull()) {
1066 eatWhiteSpace(scursor, send);
1067 if (scursor != send && *scursor ==
'(') {
1069 if (!parseComment(scursor, send, maybeDisplayName, isCRLF,
true )) {
1075 result.
setName(stripQuotes(maybeDisplayName));
1080bool parseGroup(
const char *&scursor,
const char *
const send,
1081 Address &result,
bool isCRLF)
1088 eatCFWS(scursor, send, isCRLF);
1089 if (scursor == send) {
1095 if (!parsePhrase(scursor, send, maybeDisplayName, isCRLF)) {
1100 eatCFWS(scursor, send, isCRLF);
1101 if (scursor == send || *scursor !=
':') {
1107 result.displayName = removeBidiControlChars(maybeDisplayName);
1111 while (scursor != send) {
1112 eatCFWS(scursor, send, isCRLF);
1113 if (scursor == send) {
1118 if (*scursor ==
',') {
1124 if (*scursor ==
';') {
1130 if (!parseMailbox(scursor, send, maybeMailbox, isCRLF)) {
1133 result.mailboxList.
append(maybeMailbox);
1135 eatCFWS(scursor, send, isCRLF);
1137 if (scursor == send) {
1141 if (*scursor ==
';') {
1146 if (*scursor ==
',') {
1153bool parseAddress(
const char *&scursor,
const char *
const send,
1154 Address &result,
bool isCRLF)
1158 eatCFWS(scursor, send, isCRLF);
1159 if (scursor == send) {
1165 const char *oldscursor = scursor;
1166 if (parseMailbox(scursor, send, maybeMailbox, isCRLF)) {
1168 result.displayName.
clear();
1169 result.mailboxList.
append(maybeMailbox);
1172 scursor = oldscursor;
1174 Address maybeAddress;
1177 if (!parseGroup(scursor, send, maybeAddress, isCRLF)) {
1181 result = maybeAddress;
1185bool parseAddressList(
const char *&scursor,
const char *
const send,
1188 while (scursor != send) {
1189 eatCFWS(scursor, send, isCRLF);
1191 if (scursor == send) {
1195 if (*scursor ==
',') {
1200 if (*scursor ==
';') {
1206 Address maybeAddress;
1207 if (!parseAddress(scursor, send, maybeAddress, isCRLF)) {
1210 result.
append(maybeAddress);
1212 eatCFWS(scursor, send, isCRLF);
1214 if (scursor == send) {
1218 if (*scursor ==
',') {
1225static bool parseParameter(
const char *&scursor,
const char *
const send,
1226 QPair<QString, QStringOrQPair> &result,
bool isCRLF)
1238 eatCFWS(scursor, send, isCRLF);
1239 if (scursor == send) {
1247 if (!parseToken(scursor, send, tmpAttr, ParseTokenNoFlag)) {
1253 eatCFWS(scursor, send, isCRLF);
1255 if (scursor == send || *scursor !=
'=') {
1260 eatCFWS(scursor, send, isCRLF);
1261 if (scursor == send) {
1264 KMIME_WARN <<
"attribute ends with \"*\", but value is empty!"
1265 "Chopping away \"*\".";
1266 maybeAttribute.
chop(1);
1268 result = qMakePair(maybeAttribute.
toLower(), QStringOrQPair());
1272 const char *oldscursor = scursor;
1277 QStringOrQPair maybeValue;
1278 if (*scursor ==
'"') {
1285 KMIME_WARN <<
"attribute ends with \"*\", but value is a quoted-string!"
1286 "Chopping away \"*\".";
1287 maybeAttribute.
chop(1);
1290 if (!parseGenericQuotedString(scursor, send, maybeValue.qstring, isCRLF)) {
1291 scursor = oldscursor;
1292 result = qMakePair(maybeAttribute.
toLower(), QStringOrQPair());
1297 if (!parseToken(scursor, send, maybeValue.qpair, ParseTokenRelaxedTText)) {
1298 scursor = oldscursor;
1299 result = qMakePair(maybeAttribute.
toLower(), QStringOrQPair());
1304 result = qMakePair(maybeAttribute.
toLower(), maybeValue);
1308static bool parseRawParameterList(
const char *&scursor,
const char *
const send,
1321 while (scursor != send) {
1322 eatCFWS(scursor, send, isCRLF);
1324 if (scursor == send) {
1328 if (*scursor ==
';') {
1332 QPair<QString, QStringOrQPair> maybeParameter;
1333 if (!parseParameter(scursor, send, maybeParameter, isCRLF)) {
1341 if (maybeParameter.first.isNull()) {
1344 while (scursor != send) {
1345 if (*scursor++ ==
';') {
1356 result.
insert(maybeParameter.first, maybeParameter.second);
1358 eatCFWS(scursor, send, isCRLF);
1360 if (scursor == send) {
1364 if (*scursor ==
';') {
1373 bool isContinuation,
QString &value,
1374 QPair<const char *, int> &source,
QByteArray &charset)
1380 const char *decBegin = source.first;
1381 const char *decCursor = decBegin;
1382 const char *decEnd = decCursor + source.second;
1384 if (!isContinuation) {
1386 while (decCursor != decEnd) {
1387 if (*decCursor ==
'\'') {
1394 if (decCursor == decEnd) {
1397 KMIME_WARN <<
"No charset in extended-initial-value."
1398 "Assuming \"iso-8859-1\".";
1403 charset =
QByteArray(decBegin, decCursor - decBegin);
1405 const char *oldDecCursor = ++decCursor;
1407 while (decCursor != decEnd) {
1408 if (*decCursor ==
'\'') {
1414 if (decCursor == decEnd) {
1415 KMIME_WARN <<
"No language in extended-initial-value."
1416 "Trying to recover.";
1417 decCursor = oldDecCursor;
1431 KMIME_WARN_UNKNOWN(Charset, charset);
1435 if (!rfc2231Codec) {
1437 assert(rfc2231Codec);
1454 QByteArray::Iterator bit = buffer.
begin();
1455 QByteArray::ConstIterator bend = buffer.
end();
1457 if (!
dec->decode(decCursor, decEnd, bit, bend)) {
1458 KMIME_WARN << rfc2231Codec->
name()
1459 <<
"codec lies about its maxDecodedSizeFor()"
1461 <<
"result may be truncated";
1475bool parseParameterListWithCharset(
const char *&scursor,
1476 const char *
const send,
1482 if (!parseRawParameterList(scursor, send, rawParameterList, isCRLF)) {
1486 if (rawParameterList.
isEmpty()) {
1500 NoMode = 0x0, Continued = 0x1, Encoded = 0x2
1512 for (it = rawParameterList.
begin() ; it !=
end ; ++it) {
1513 if (attribute.
isNull() || !it.
key().startsWith(attribute)) {
1519 if (!attribute.
isNull()) {
1520 result.
insert(attribute, value);
1524 attribute = it.
key();
1526 EncodingMode encodingMode = NoEncoding;
1532 encodingMode = RFC2231;
1535 if (!(*it).qstring.isNull() &&
1538 encodingMode = RFC2047;
1548 if (mode & Encoded) {
1549 if (encodingMode == RFC2231) {
1550 decodeRFC2231Value(rfc2231Codec, textcodec,
1552 value, (*it).qpair, charset);
1553 }
else if (encodingMode == RFC2047) {
1558 if ((*it).qpair.first) {
1561 value += (*it).qstring;
1569 if (!(mode & Continued)) {
1571 result.
insert(attribute, value);
1583 decodeRFC2231Value(rfc2231Codec, textcodec,
1585 value, (*it).qpair, charset);
1588 if ((*it).qpair.first) {
1591 value += (*it).qstring;
1597 if (!attribute.
isNull()) {
1598 result.
insert(attribute, value);
1604bool parseParameterList(
const char *&scursor,
const char *
const send,
1608 return parseParameterListWithCharset(scursor, send, result, charset, isCRLF);
1611static const char stdDayNames[][4] = {
1612 "Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"
1614static const int stdDayNamesLen =
sizeof stdDayNames /
sizeof *stdDayNames;
1616static bool parseDayName(
const char *&scursor,
const char *
const send)
1619 if (send - scursor < 3) {
1623 for (
int i = 0 ; i < stdDayNamesLen ; ++i) {
1624 if (qstrnicmp(scursor, stdDayNames[i], 3) == 0) {
1634static const char stdMonthNames[][4] = {
1635 "Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
1636 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
1638static const int stdMonthNamesLen =
1639 sizeof stdMonthNames /
sizeof *stdMonthNames;
1641static bool parseMonthName(
const char *&scursor,
const char *
const send,
1645 if (send - scursor < 3) {
1649 for (result = 0 ; result < stdMonthNamesLen ; ++result) {
1650 if (qstrnicmp(scursor, stdMonthNames[result], 3) == 0) {
1660static const struct {
1661 const char tzName[5];
1662 long int secsEastOfGMT;
1667 {
"EDT", -4 * 3600 },
1668 {
"EST", -5 * 3600 },
1669 {
"MST", -5 * 3600 },
1670 {
"CST", -6 * 3600 },
1671 {
"MDT", -6 * 3600 },
1672 {
"MST", -7 * 3600 },
1673 {
"PDT", -7 * 3600 },
1674 {
"PST", -8 * 3600 },
1676 {
"CET", 1 * 3600 },
1677 {
"MET", 1 * 3600 },
1679 {
"CEST", 2 * 3600 },
1680 {
"BST", 1 * 3600 },
1693 {
"K", -10 * 3600 },
1694 {
"L", -11 * 3600 },
1695 {
"M", -12 * 3600 },
1709static const int timeZonesLen =
sizeof timeZones /
sizeof *timeZones;
1711static bool parseAlphaNumericTimeZone(
const char *&scursor,
1712 const char *
const send,
1713 long int &secsEastOfGMT,
1714 bool &timeZoneKnown)
1717 if (scursor < send && *scursor ==
'"') {
1720 if (scursor == send) {
1725 QPair<const char *, int> maybeTimeZone(
nullptr, 0);
1726 if (!parseToken(scursor, send, maybeTimeZone, ParseTokenNoFlag)) {
1729 for (
int i = 0 ; i < timeZonesLen ; ++i) {
1730 if (qstrnicmp(timeZones[i].tzName,
1731 maybeTimeZone.first, maybeTimeZone.second) == 0) {
1732 scursor += maybeTimeZone.second;
1733 secsEastOfGMT = timeZones[i].secsEastOfGMT;
1734 timeZoneKnown =
true;
1736 if (scursor < send && *scursor ==
'"') {
1745 KMIME_WARN_UNKNOWN(time zone,
1746 QByteArray(maybeTimeZone.first, maybeTimeZone.second));
1748 timeZoneKnown =
false;
1753int parseDigits(
const char *&scursor,
const char *
const send,
int &result)
1757 for (; scursor != send && isdigit(*scursor) ; scursor++, digits++) {
1759 result += int(*scursor -
'0');
1764static bool parseTimeOfDay(
const char *&scursor,
const char *
const send,
1765 int &hour,
int &min,
int &sec,
bool isCRLF =
false)
1772 if (!parseDigits(scursor, send, hour)) {
1776 eatCFWS(scursor, send, isCRLF);
1777 if (scursor == send || *scursor !=
':') {
1782 eatCFWS(scursor, send, isCRLF);
1783 if (scursor == send) {
1790 if (!parseDigits(scursor, send, min)) {
1794 eatCFWS(scursor, send, isCRLF);
1795 if (scursor == send) {
1802 if (*scursor ==
':') {
1805 eatCFWS(scursor, send, isCRLF);
1806 if (scursor == send) {
1810 if (!parseDigits(scursor, send, sec)) {
1820bool parseTime(
const char *&scursor,
const char *send,
1821 int &hour,
int &min,
int &sec,
long int &secsEastOfGMT,
1822 bool &timeZoneKnown,
bool isCRLF)
1834 eatCFWS(scursor, send, isCRLF);
1835 if (scursor == send) {
1839 if (!parseTimeOfDay(scursor, send, hour, min, sec, isCRLF)) {
1843 eatCFWS(scursor, send, isCRLF);
1845 if ((scursor == send) || isdigit(*scursor)) {
1846 timeZoneKnown =
false;
1851 timeZoneKnown =
true;
1852 if (*scursor ==
'+' || *scursor ==
'-') {
1854 const char sign = *scursor++;
1857 const int tzDigits = parseDigits(scursor, send, maybeTimeZone);
1858 if (tzDigits != 4) {
1860 if (tzDigits == 2 && scursor != send && *scursor ==
':') {
1863 if (parseDigits(scursor, send, maybeTimeZone2) != 2) {
1866 maybeTimeZone = maybeTimeZone * 100 + maybeTimeZone2;
1871 secsEastOfGMT = 60 * (maybeTimeZone / 100 * 60 + maybeTimeZone % 100);
1873 secsEastOfGMT *= -1;
1874 if (secsEastOfGMT == 0) {
1875 timeZoneKnown =
false;
1880 if (!parseAlphaNumericTimeZone(scursor, send, secsEastOfGMT, timeZoneKnown)) {
1887bool parseQDateTime(
const char *&scursor,
const char *
const send,
1890 eatCFWS(scursor, send, isCRLF);
1891 if (scursor == send) {
1902bool parseDateTime(
const char *&scursor,
const char *
const send,
1917 eatCFWS(scursor, send, isCRLF);
1918 if (scursor == send) {
1925 if (parseDayName(scursor, send)) {
1926 eatCFWS(scursor, send, isCRLF);
1927 if (scursor == send) {
1931 if (*scursor ==
',') {
1933 eatCFWS(scursor, send, isCRLF);
1937 int maybeMonth = -1;
1938 bool asctimeFormat =
false;
1941 if (!isdigit(*scursor) && parseMonthName(scursor, send, maybeMonth)) {
1942 asctimeFormat =
true;
1943 eatCFWS(scursor, send, isCRLF);
1950 if (!parseDigits(scursor, send, maybeDay)) {
1954 eatCFWS(scursor, send, isCRLF);
1955 if (scursor == send) {
1960 if (*scursor ==
',') {
1967 if (!asctimeFormat && !parseMonthName(scursor, send, maybeMonth)) {
1970 if (scursor == send) {
1973 assert(maybeMonth >= 0); assert(maybeMonth <= 11);
1976 eatCFWS(scursor, send, isCRLF);
1977 if (scursor == send) {
1982 bool timeAfterYear =
true;
1983 if ((send - scursor > 3) && ((scursor[1] ==
':') || (scursor[2] ==
':'))) {
1984 timeAfterYear =
false;
1992 if (timeAfterYear && !parseDigits(scursor, send, maybeYear)) {
1996 eatCFWS(scursor, send, isCRLF);
2000 long int secsEastOfGMT = 0;
2003 if (scursor != send) {
2007 bool timeZoneKnown =
true;
2009 if (!parseTime(scursor, send,
2010 maybeHour, maybeMinute, maybeSecond,
2011 secsEastOfGMT, timeZoneKnown, isCRLF)) {
2016 if (!timeAfterYear) {
2017 eatCFWS(scursor, send, isCRLF);
2018 if (scursor == send) {
2022 if (!parseDigits(scursor, send, maybeYear)) {
2028 if (maybeYear < 50) {
2030 }
else if (maybeYear < 1000) {
2034 if (maybeYear < 1900) {
2038 maybeDate =
QDate(maybeYear, maybeMonth, maybeDay);
2039 maybeTime =
QTime(maybeHour, maybeMinute, maybeSecond);
2045 maybeDate =
QDate(maybeYear, maybeMonth, maybeDay);
2046 maybeTime =
QTime(0, 0, 0);
2062 int startOfFieldBody = head.
indexOf(
':', headerStart);
2063 if (startOfFieldBody < 0) {
2067 const char *rawType = head.
constData() + headerStart;
2068 const size_t rawTypeLen = startOfFieldBody - headerStart;
2071 if (startOfFieldBody < head.
size() - 1 && head[startOfFieldBody] ==
' ') {
2075 bool folded =
false;
2076 endOfFieldBody = findHeaderLineEnd(head, startOfFieldBody, &folded);
2079 if (rawTypeLen > 0) {
2080 header = HeaderFactory::createHeader(rawType, rawTypeLen);
2087 const auto unfoldedBody = unfoldHeader(head.
constData() + startOfFieldBody, endOfFieldBody - startOfFieldBody);
2098std::unique_ptr<KMime::Headers::Base> parseNextHeader(
QByteArrayView &head)
2100 int endOfFieldBody = 0;
2101 std::unique_ptr<KMime::Headers::Base> header(extractHeader(head, 0, endOfFieldBody));
2103 head = head.
mid(endOfFieldBody + 1);
2122 int pos = content.
indexOf(
"\n\n", 0);
2124 header = content.
left(++pos);
2125 body = content.
mid(pos + 1);
2138 while (cursor < head.
size()) {
2139 const int headerStart = cursor;
2141 if (
auto header = extractHeader(head, headerStart, endOfFieldBody)) {
2143 cursor = endOfFieldBody + 1;
static Codec * codecForName(QByteArrayView name)
virtual Decoder * makeDecoder(NewlineType newline=NewlineLF) const=0
virtual const char * name() const=0
virtual qsizetype maxDecodedSizeFor(qsizetype insize, NewlineType newline=NewlineLF) const=0
Represents an (email address, display name) pair according RFC 2822, section 3.4.
void setName(const QString &name)
Sets the name.
void setAddress(const AddrSpec &addr)
Sets the email address.
Q_SCRIPTABLE Q_NOREPLY void start()
KCODECS_EXPORT QString decodeRFC2047String(QByteArrayView src, QByteArray *usedCS, const QByteArray &defaultCS=QByteArray(), CharsetOption option=NoOption)
const QList< QKeySequence > & end()
const char * constData() const const
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
bool isEmpty() const const
QByteArray left(qsizetype len) const const
qsizetype length() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
void resize(qsizetype newSize, char c)
QByteArray right(qsizetype len) const const
qsizetype size() const const
bool startsWith(QByteArrayView bv) const const
QByteArrayView mid(qsizetype start, qsizetype length) const const
const_pointer constData() const const
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
qsizetype size() const const
bool isValid(int year, int month, int day)
bool setDate(int year, int month, int day)
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
bool isValid() const const
void append(QList< T > &&value)
iterator insert(const Key &key, const T &value)
bool isEmpty() const const
Key key(const T &value, const Key &defaultKey) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString fromLatin1(QByteArrayView str)
bool isNull() const const
QString mid(qsizetype position, qsizetype n) const const
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QString toLower() const const
bool isValid() const const
EncodedData< QByteArrayView > decode(QByteArrayView ba)
QTextStream & dec(QTextStream &stream)
QTextStream & endl(QTextStream &stream)
bool isValid(int h, int m, int s, int ms)
QTimeZone fromSecondsAheadOfUtc(int offset)