10#include "headerparsing.h"
11#include "headerparsing_p.h"
13#include "headerfactory_p.h"
19#include "kmime_debug.h"
24#include <QStringDecoder>
31using namespace KMime::Types;
39 struct KMIME_EXPORT QStringOrQPair {
45namespace HeaderParsing
49bool parseEncodedWord(
const char *&scursor,
const char *
const send,
54 assert(*(scursor - 1) ==
'=');
71 const char *charsetStart = scursor;
72 const char *languageStart =
nullptr;
76 for (; scursor != send ; scursor++) {
77 if (*scursor ==
'?') {
79 }
else if (*scursor ==
'*' && languageStart ==
nullptr) {
80 languageStart = scursor + 1;
85 if (scursor == send || *scursor !=
'?') {
87 KMIME_WARN_PREMATURE_END_OF(EncodedWord);
93 QByteArray maybeLanguage(languageStart, scursor - languageStart);
97 (languageStart ? languageStart - 1 : scursor) - charsetStart);
106 const char *encodingStart = scursor;
109 for (; scursor != send ; scursor++) {
110 if (*scursor ==
'?') {
116 if (scursor == send || *scursor !=
'?') {
118 KMIME_WARN_PREMATURE_END_OF(EncodedWord);
123 QByteArray maybeEncoding(encodingStart, scursor - encodingStart);
136 const char *encodedTextStart = scursor;
139 for (; scursor != send ; scursor++) {
140 if (*scursor ==
'?') {
141 if (scursor + 1 != send) {
142 if (*(scursor + 1) !=
'=') {
143 KMIME_WARN <<
"Stray '?' in q-encoded word, ignoring this.";
150 KMIME_WARN_PREMATURE_END_OF(EncodedWord);
156 if (*(scursor - 2) !=
'?' || *(scursor - 1) !=
'=' ||
157 scursor < encodedTextStart + 2) {
158 KMIME_WARN_PREMATURE_END_OF(EncodedWord);
163 const char *
const encodedTextEnd = scursor - 2;
173 KMIME_WARN_UNKNOWN(Encoding, maybeEncoding);
188 usedCS = cachedCharset(defaultCS);
192 usedCS = cachedCharset(defaultCS);
195 usedCS = cachedCharset(maybeCharset);
200 KMIME_WARN_UNKNOWN(Charset, maybeCharset);
208 const auto encodedTextLength = encodedTextEnd - encodedTextStart;
211 char *bbegin = buffer.
data();
212 char *bend = bbegin + buffer.
length();
219 if (!
dec->decode(encodedTextStart, encodedTextEnd, bbegin, bend)) {
220 KMIME_WARN << codec->
name() <<
"codec lies about its maxDecodedSizeFor("
221 << encodedTextLength <<
")\nresult may be truncated";
229 language = maybeLanguage;
234static inline void eatWhiteSpace(
const char *&scursor,
const char *
const send)
236 while (scursor != send &&
237 (*scursor ==
' ' || *scursor ==
'\n' ||
238 *scursor ==
'\t' || *scursor ==
'\r')) {
243bool parseAtom(
const char*&scursor,
const char *
const send,
246 bool success =
false;
247 const char *
start = scursor;
249 while (scursor != send) {
250 signed char ch = *scursor++;
251 if (ch > 0 && isAText(ch)) {
254 }
else if (allow8Bit && ch < 0) {
270bool parseToken(
const char*&scursor,
const char *
const send,
273 bool success =
false;
274 const char *
start = scursor;
276 while (scursor != send) {
277 signed char ch = *scursor++;
278 if (ch > 0 && isTText(ch)) {
281 }
else if ((flags & ParseTokenAllow8Bit) && ch < 0) {
285 }
else if ((flags & ParseTokenRelaxedTText) && ch ==
'/') {
299#define READ_ch_OR_FAIL if ( scursor == send ) { \
300 KMIME_WARN_PREMATURE_END_OF( GenericQuotedString ); \
310bool parseGenericQuotedString(
const char *&scursor,
const char *
const send,
312 const char openChar,
const char closeChar)
319 assert(*(scursor - 1) == openChar || *(scursor - 1) == closeChar);
321 while (scursor != send) {
322 char ch = *scursor++;
324 if (ch == closeChar || ch == openChar) {
334 KMIME_WARN_IF_8BIT(ch);
355 if (ch ==
' ' || ch ==
'\t') {
364 KMIME_WARN_NON_FOLDING(CRLF);
382 if (!isCRLF && (ch ==
' ' || ch ==
'\t')) {
398 if (scursor == send) {
402 const char *oldscursor = scursor;
406 if (*scursor++ ==
'?') {
408 if (parseEncodedWord(scursor, send, tmp, lang, charset)) {
411 if (scursor == send) {
413 }
else if (*scursor++ ==
' ') {
414 if (scursor == send) {
417 }
else if (*scursor++ ==
'=') {
418 if (scursor == send) {
422 }
else if (*scursor++ ==
'?') {
437 scursor = oldscursor;
440 scursor = oldscursor;
446 KMIME_WARN_IF_8BIT(ch);
458bool parseComment(
const char *&scursor,
const char *
const send,
459 QString &result,
bool isCRLF,
bool reallySave)
461 int commentNestingDepth = 1;
462 const char *afterLastClosingParenPos =
nullptr;
464 const char *oldscursor = scursor;
466 assert(*(scursor - 1) ==
'(');
468 while (commentNestingDepth) {
470 if (parseGenericQuotedString(scursor, send, cmntPart, isCRLF,
'(',
')')) {
471 assert(*(scursor - 1) ==
')' || *(scursor - 1) ==
'(');
474 switch (*(scursor - 1)) {
480 if (commentNestingDepth > 1) {
486 afterLastClosingParenPos = scursor;
487 --commentNestingDepth;
493 maybeCmnt += cmntPart;
496 ++commentNestingDepth;
502 if (afterLastClosingParenPos) {
503 scursor = afterLastClosingParenPos;
505 scursor = oldscursor;
516bool parsePhrase(
const char *&scursor,
const char *
const send,
520 None, Phrase, Atom, EncodedWord, QuotedString
527 const char *successfullyParsed =
nullptr;
529 const char *oldscursor;
532 bool lastWasEncodedWord =
false;
534 while (scursor != send) {
535 char ch = *scursor++;
542 if (scursor != send && (*scursor ==
' ' || *scursor ==
'\t')) {
547 successfullyParsed = scursor;
552 if (parseGenericQuotedString(scursor, send, tmp, isCRLF,
'"',
'"')) {
553 successfullyParsed = scursor;
554 assert(*(scursor - 1) ==
'"');
557 found = QuotedString;
569 lastWasEncodedWord =
false;
587 if (parseComment(scursor, send, tmp, isCRLF,
589 successfullyParsed = scursor;
590 lastWasEncodedWord =
false;
595 scursor = successfullyParsed;
602 oldscursor = scursor;
605 if (parseEncodedWord(scursor, send, tmp, lang, charset)) {
606 successfullyParsed = scursor;
615 if (!lastWasEncodedWord) {
622 lastWasEncodedWord =
true;
627 scursor = oldscursor;
634 if (parseAtom(scursor, send, tmpAtom,
true )) {
635 successfullyParsed = scursor;
650 lastWasEncodedWord =
false;
656 scursor = successfullyParsed;
661 eatWhiteSpace(scursor, send);
664 return found !=
None;
667bool parseDotAtom(
const char *&scursor,
const char *
const send,
670 eatCFWS(scursor, send, isCRLF);
673 const char *successfullyParsed;
676 if (!parseAtom(scursor, send, maybeAtom,
false )) {
680 successfullyParsed = scursor;
682 while (scursor != send) {
685 if (scursor == send || *scursor !=
'.') {
690 if (scursor == send || !isAText(*scursor)) {
694 scursor = successfullyParsed;
700 if (!parseAtom(scursor, send, maybeAtom,
false )) {
701 scursor = successfullyParsed;
707 successfullyParsed = scursor;
710 scursor = successfullyParsed;
714void eatCFWS(
const char *&scursor,
const char *
const send,
bool isCRLF)
718 while (scursor != send) {
719 const char *oldscursor = scursor;
721 char ch = *scursor++;
731 if (parseComment(scursor, send, dummy, isCRLF,
false )) {
734 scursor = oldscursor;
738 scursor = oldscursor;
744bool parseDomain(
const char *&scursor,
const char *
const send,
747 eatCFWS(scursor, send, isCRLF);
748 if (scursor == send) {
758 if (*scursor ==
'[') {
763 while (parseGenericQuotedString(scursor, send, maybeDomainLiteral,
765 if (scursor == send) {
767 if (*(scursor - 1) ==
']') {
769 result = maybeDomainLiteral;
778 if (*(scursor - 1) ==
'[') {
783 result = maybeDomainLiteral;
789 if (parseDotAtom(scursor, send, maybeDotAtom, isCRLF)) {
791 if (scursor != send && *scursor ==
'.') {
802bool parseObsRoute(
const char *&scursor,
const char *
const send,
805 while (scursor != send) {
806 eatCFWS(scursor, send, isCRLF);
807 if (scursor == send) {
812 if (*scursor ==
',') {
821 if (*scursor ==
':') {
830 if (*scursor !=
'@') {
837 if (!parseDomain(scursor, send, maybeDomain, isCRLF)) {
841 result.
append(maybeDomain);
845 eatCFWS(scursor, send, isCRLF);
846 if (scursor == send) {
849 if (*scursor ==
':') {
853 if (*scursor ==
',') {
861bool parseAddrSpec(
const char *&scursor,
const char *
const send,
862 AddrSpec &result,
bool isCRLF)
875 while (scursor != send) {
877 eatCFWS(scursor, send, isCRLF);
879 char ch = *scursor++;
891 if (parseGenericQuotedString(scursor, send, tmp, isCRLF,
'"',
'"')) {
892 maybeLocalPart += tmp;
900 if (parseAtom(scursor, send, tmpAtom,
false )) {
918 assert(*(scursor - 1) ==
'@');
921 if (!parseDomain(scursor, send, maybeDomain, isCRLF)) {
925 result.localPart = maybeLocalPart;
926 result.domain = maybeDomain;
931bool parseAngleAddr(
const char *&scursor,
const char *
const send,
932 AddrSpec &result,
bool isCRLF)
935 eatCFWS(scursor, send, isCRLF);
936 if (scursor == send || *scursor !=
'<') {
941 eatCFWS(scursor, send, isCRLF);
942 if (scursor == send) {
946 if (*scursor ==
'@' || *scursor ==
',') {
948 KMIME_WARN <<
"obsolete source route found! ignoring.";
950 if (!parseObsRoute(scursor, send, dummy,
955 if (scursor == send) {
961 AddrSpec maybeAddrSpec;
962 if (!parseAddrSpec(scursor, send, maybeAddrSpec, isCRLF)) {
966 eatCFWS(scursor, send, isCRLF);
967 if (scursor == send || *scursor !=
'>') {
972 result = maybeAddrSpec;
988bool parseMailbox(
const char *&scursor,
const char *
const send,
991 eatCFWS(scursor, send, isCRLF);
992 if (scursor == send) {
996 AddrSpec maybeAddrSpec;
1000 const char *oldscursor = scursor;
1001 if (parseAddrSpec(scursor, send, maybeAddrSpec, isCRLF)) {
1004 eatWhiteSpace(scursor, send);
1005 if (scursor != send && *scursor ==
'(') {
1007 if (!parseComment(scursor, send, maybeDisplayName, isCRLF,
true )) {
1011 result.
setName(stripQuotes(maybeDisplayName));
1014 scursor = oldscursor;
1017 if (!parsePhrase(scursor, send, maybeDisplayName, isCRLF)) {
1019 maybeDisplayName.
clear();
1020 scursor = oldscursor;
1023 eatCFWS(scursor, send, isCRLF);
1024 if (scursor == send) {
1030 if (!parseAngleAddr(scursor, send, maybeAddrSpec, isCRLF)) {
1034 if (maybeDisplayName.
isNull()) {
1036 eatWhiteSpace(scursor, send);
1037 if (scursor != send && *scursor ==
'(') {
1039 if (!parseComment(scursor, send, maybeDisplayName, isCRLF,
true )) {
1045 result.
setName(stripQuotes(maybeDisplayName));
1050bool parseGroup(
const char *&scursor,
const char *
const send,
1051 Address &result,
bool isCRLF)
1058 eatCFWS(scursor, send, isCRLF);
1059 if (scursor == send) {
1065 if (!parsePhrase(scursor, send, maybeDisplayName, isCRLF)) {
1070 eatCFWS(scursor, send, isCRLF);
1071 if (scursor == send || *scursor !=
':') {
1077 result.displayName = removeBidiControlChars(maybeDisplayName);
1081 while (scursor != send) {
1082 eatCFWS(scursor, send, isCRLF);
1083 if (scursor == send) {
1088 if (*scursor ==
',') {
1094 if (*scursor ==
';') {
1100 if (!parseMailbox(scursor, send, maybeMailbox, isCRLF)) {
1103 result.mailboxList.
append(maybeMailbox);
1105 eatCFWS(scursor, send, isCRLF);
1107 if (scursor == send) {
1111 if (*scursor ==
';') {
1116 if (*scursor ==
',') {
1123bool parseAddress(
const char *&scursor,
const char *
const send,
1124 Address &result,
bool isCRLF)
1128 eatCFWS(scursor, send, isCRLF);
1129 if (scursor == send) {
1135 const char *oldscursor = scursor;
1136 if (parseMailbox(scursor, send, maybeMailbox, isCRLF)) {
1138 result.displayName.
clear();
1139 result.mailboxList.
append(maybeMailbox);
1142 scursor = oldscursor;
1144 Address maybeAddress;
1147 if (!parseGroup(scursor, send, maybeAddress, isCRLF)) {
1151 result = maybeAddress;
1155bool parseAddressList(
const char *&scursor,
const char *
const send,
1158 while (scursor != send) {
1159 eatCFWS(scursor, send, isCRLF);
1161 if (scursor == send) {
1165 if (*scursor ==
',') {
1170 if (*scursor ==
';') {
1176 Address maybeAddress;
1177 if (!parseAddress(scursor, send, maybeAddress, isCRLF)) {
1180 result.
append(maybeAddress);
1182 eatCFWS(scursor, send, isCRLF);
1184 if (scursor == send) {
1188 if (*scursor ==
',') {
1195static bool parseParameter(
const char *&scursor,
const char *
const send,
1196 QPair<QByteArray, QStringOrQPair> &result,
bool isCRLF)
1208 eatCFWS(scursor, send, isCRLF);
1209 if (scursor == send) {
1217 if (!parseToken(scursor, send, maybeAttribute, ParseTokenNoFlag)) {
1221 eatCFWS(scursor, send, isCRLF);
1223 if (scursor == send || *scursor !=
'=') {
1228 eatCFWS(scursor, send, isCRLF);
1229 if (scursor == send) {
1231 if (maybeAttribute.
endsWith(
'*')) {
1232 KMIME_WARN <<
"attribute ends with \"*\", but value is empty!"
1233 "Chopping away \"*\".";
1234 maybeAttribute.
chop(1);
1240 const char *oldscursor = scursor;
1245 QStringOrQPair maybeValue;
1246 if (*scursor ==
'"') {
1249 if (maybeAttribute.
endsWith(
'*')) {
1253 KMIME_WARN <<
"attribute ends with \"*\", but value is a quoted-string!"
1254 "Chopping away \"*\".";
1255 maybeAttribute.
chop(1);
1258 if (!parseGenericQuotedString(scursor, send, maybeValue.qstring, isCRLF)) {
1259 scursor = oldscursor;
1265 if (!parseToken(scursor, send, maybeValue.view, ParseTokenRelaxedTText)) {
1266 scursor = oldscursor;
1276static bool parseRawParameterList(
const char *&scursor,
const char *
const send,
1277 std::map<QByteArray, QStringOrQPair> &result,
1289 while (scursor != send) {
1290 eatCFWS(scursor, send, isCRLF);
1292 if (scursor == send) {
1296 if (*scursor ==
';') {
1300 QPair<QByteArray, QStringOrQPair> maybeParameter;
1301 if (!parseParameter(scursor, send, maybeParameter, isCRLF)) {
1309 if (maybeParameter.first.isNull()) {
1312 while (scursor != send) {
1313 if (*scursor++ ==
';') {
1324 result[maybeParameter.first] = maybeParameter.second;
1326 eatCFWS(scursor, send, isCRLF);
1328 if (scursor == send) {
1332 if (*scursor ==
';') {
1341 bool isContinuation,
QString &value,
1348 const char *decBegin = source.
data();
1349 const char *decCursor = decBegin;
1350 const char *decEnd = decCursor + source.
size();
1352 if (!isContinuation) {
1354 while (decCursor != decEnd) {
1355 if (*decCursor ==
'\'') {
1362 if (decCursor == decEnd) {
1365 KMIME_WARN <<
"No charset in extended-initial-value."
1366 "Assuming \"iso-8859-1\".";
1371 charset =
QByteArray(decBegin, decCursor - decBegin);
1373 const char *oldDecCursor = ++decCursor;
1375 while (decCursor != decEnd) {
1376 if (*decCursor ==
'\'') {
1382 if (decCursor == decEnd) {
1383 KMIME_WARN <<
"No language in extended-initial-value."
1384 "Trying to recover.";
1385 decCursor = oldDecCursor;
1399 KMIME_WARN_UNKNOWN(Charset, charset);
1403 if (!rfc2231Codec) {
1405 assert(rfc2231Codec);
1422 QByteArray::Iterator bit = buffer.
begin();
1423 QByteArray::ConstIterator bend = buffer.
end();
1425 if (!
dec->decode(decCursor, decEnd, bit, bend)) {
1426 KMIME_WARN << rfc2231Codec->
name()
1427 <<
"codec lies about its maxDecodedSizeFor()"
1429 <<
"result may be truncated";
1443bool parseParameterListWithCharset(
const char *&scursor,
1444 const char *
const send,
1445 Headers::ParameterMap &result,
1449 std::map<QByteArray, QStringOrQPair> rawParameterList;
1450 if (!parseRawParameterList(scursor, send, rawParameterList, isCRLF)) {
1454 if (rawParameterList.empty()) {
1468 NoMode = 0x0, Continued = 0x1, Encoded = 0x2
1477 for (
auto &it : rawParameterList) {
1478 if (attribute.
isNull() || !it.first.startsWith(attribute)) {
1484 if (!attribute.
isNull()) {
1485 result[attribute] = value;
1489 attribute = it.
first;
1491 EncodingMode encodingMode = NoEncoding;
1497 encodingMode = RFC2231;
1500 if (!it.second.qstring.isNull() &&
1503 encodingMode = RFC2047;
1513 if (mode & Encoded) {
1514 if (encodingMode == RFC2231) {
1515 decodeRFC2231Value(rfc2231Codec, textcodec,
1517 value, it.second.view, charset);
1518 }
else if (encodingMode == RFC2047) {
1523 if (!it.second.view.isNull()) {
1526 value += it.second.qstring;
1534 if (!(mode & Continued)) {
1536 result[attribute] = value;
1546 if (it.first.endsWith(
'*')) {
1548 decodeRFC2231Value(rfc2231Codec, textcodec,
1550 value, it.second.view, charset);
1553 if (!it.second.view.isNull()) {
1556 value += it.second.qstring;
1562 if (!attribute.
isNull()) {
1563 result[attribute] = value;
1569static const char stdDayNames[][4] = {
1570 "Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"
1572static const int stdDayNamesLen =
sizeof stdDayNames /
sizeof *stdDayNames;
1574static bool parseDayName(
const char *&scursor,
const char *
const send)
1577 if (send - scursor < 3) {
1581 for (
int i = 0 ; i < stdDayNamesLen ; ++i) {
1582 if (qstrnicmp(scursor, stdDayNames[i], 3) == 0) {
1592static const char stdMonthNames[][4] = {
1593 "Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
1594 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
1596static const int stdMonthNamesLen =
1597 sizeof stdMonthNames /
sizeof *stdMonthNames;
1599static bool parseMonthName(
const char *&scursor,
const char *
const send,
1603 if (send - scursor < 3) {
1607 for (result = 0 ; result < stdMonthNamesLen ; ++result) {
1608 if (qstrnicmp(scursor, stdMonthNames[result], 3) == 0) {
1618static const struct {
1619 const char tzName[5];
1625 {
"EDT", -4 * 3600 },
1626 {
"EST", -5 * 3600 },
1627 {
"MST", -5 * 3600 },
1628 {
"CST", -6 * 3600 },
1629 {
"MDT", -6 * 3600 },
1630 {
"MST", -7 * 3600 },
1631 {
"PDT", -7 * 3600 },
1632 {
"PST", -8 * 3600 },
1634 {
"CET", 1 * 3600 },
1635 {
"MET", 1 * 3600 },
1637 {
"CEST", 2 * 3600 },
1638 {
"BST", 1 * 3600 },
1651 {
"K", -10 * 3600 },
1652 {
"L", -11 * 3600 },
1653 {
"M", -12 * 3600 },
1667static const int timeZonesLen =
sizeof timeZones /
sizeof *timeZones;
1669static bool parseAlphaNumericTimeZone(
const char *&scursor,
1670 const char *
const send,
1671 long int &secsEastOfGMT,
1672 bool &timeZoneKnown)
1675 if (scursor < send && *scursor ==
'"') {
1678 if (scursor == send) {
1684 if (!parseToken(scursor, send, maybeTimeZone, ParseTokenNoFlag)) {
1687 for (
int i = 0 ; i < timeZonesLen ; ++i) {
1689 scursor += maybeTimeZone.
size();
1690 secsEastOfGMT = timeZones[i].secsEastOfGMT;
1691 timeZoneKnown =
true;
1693 if (scursor < send && *scursor ==
'"') {
1702 KMIME_WARN_UNKNOWN(time zone, maybeTimeZone);
1704 timeZoneKnown =
false;
1709int parseDigits(
const char *&scursor,
const char *
const send,
int &result)
1713 for (; scursor != send && isdigit(*scursor) ; scursor++, digits++) {
1715 result += int(*scursor -
'0');
1720static bool parseTimeOfDay(
const char *&scursor,
const char *
const send,
1721 int &hour,
int &min,
int &sec,
bool isCRLF =
false)
1728 if (!parseDigits(scursor, send, hour)) {
1732 eatCFWS(scursor, send, isCRLF);
1733 if (scursor == send || *scursor !=
':') {
1738 eatCFWS(scursor, send, isCRLF);
1739 if (scursor == send) {
1746 if (!parseDigits(scursor, send, min)) {
1750 eatCFWS(scursor, send, isCRLF);
1751 if (scursor == send) {
1758 if (*scursor ==
':') {
1761 eatCFWS(scursor, send, isCRLF);
1762 if (scursor == send) {
1766 if (!parseDigits(scursor, send, sec)) {
1776bool parseTime(
const char *&scursor,
const char *send,
1777 int &hour,
int &min,
int &sec,
long int &secsEastOfGMT,
1778 bool &timeZoneKnown,
bool isCRLF)
1790 eatCFWS(scursor, send, isCRLF);
1791 if (scursor == send) {
1795 if (!parseTimeOfDay(scursor, send, hour, min, sec, isCRLF)) {
1799 eatCFWS(scursor, send, isCRLF);
1801 if ((scursor == send) || isdigit(*scursor)) {
1802 timeZoneKnown =
false;
1807 timeZoneKnown =
true;
1808 if (*scursor ==
'+' || *scursor ==
'-') {
1810 const char sign = *scursor++;
1813 const int tzDigits = parseDigits(scursor, send, maybeTimeZone);
1814 if (tzDigits != 4) {
1816 if (tzDigits == 2 && scursor != send && *scursor ==
':') {
1819 if (parseDigits(scursor, send, maybeTimeZone2) != 2) {
1822 maybeTimeZone = maybeTimeZone * 100 + maybeTimeZone2;
1827 secsEastOfGMT = 60 * (maybeTimeZone / 100 * 60 + maybeTimeZone % 100);
1829 secsEastOfGMT *= -1;
1830 if (secsEastOfGMT == 0) {
1831 timeZoneKnown =
false;
1836 if (!parseAlphaNumericTimeZone(scursor, send, secsEastOfGMT, timeZoneKnown)) {
1843bool parseQDateTime(
const char *&scursor,
const char *
const send,
1846 eatCFWS(scursor, send, isCRLF);
1847 if (scursor == send) {
1858bool parseDateTime(
const char *&scursor,
const char *
const send,
1873 eatCFWS(scursor, send, isCRLF);
1874 if (scursor == send) {
1881 if (parseDayName(scursor, send)) {
1882 eatCFWS(scursor, send, isCRLF);
1883 if (scursor == send) {
1887 if (*scursor ==
',') {
1889 eatCFWS(scursor, send, isCRLF);
1893 int maybeMonth = -1;
1894 bool asctimeFormat =
false;
1897 if (!isdigit(*scursor) && parseMonthName(scursor, send, maybeMonth)) {
1898 asctimeFormat =
true;
1899 eatCFWS(scursor, send, isCRLF);
1906 if (!parseDigits(scursor, send, maybeDay)) {
1910 eatCFWS(scursor, send, isCRLF);
1911 if (scursor == send) {
1916 if (*scursor ==
',') {
1923 if (!asctimeFormat && !parseMonthName(scursor, send, maybeMonth)) {
1926 if (scursor == send) {
1929 assert(maybeMonth >= 0); assert(maybeMonth <= 11);
1932 eatCFWS(scursor, send, isCRLF);
1933 if (scursor == send) {
1938 bool timeAfterYear =
true;
1939 if ((send - scursor > 3) && ((scursor[1] ==
':') || (scursor[2] ==
':'))) {
1940 timeAfterYear =
false;
1948 if (timeAfterYear && !parseDigits(scursor, send, maybeYear)) {
1952 eatCFWS(scursor, send, isCRLF);
1956 long int secsEastOfGMT = 0;
1959 if (scursor != send) {
1963 bool timeZoneKnown =
true;
1965 if (!parseTime(scursor, send,
1966 maybeHour, maybeMinute, maybeSecond,
1967 secsEastOfGMT, timeZoneKnown, isCRLF)) {
1972 if (!timeAfterYear) {
1973 eatCFWS(scursor, send, isCRLF);
1974 if (scursor == send) {
1978 if (!parseDigits(scursor, send, maybeYear)) {
1984 if (maybeYear < 50) {
1986 }
else if (maybeYear < 1000) {
1990 if (maybeYear < 1900) {
1994 maybeDate =
QDate(maybeYear, maybeMonth, maybeDay);
1995 maybeTime =
QTime(maybeHour, maybeMinute, maybeSecond);
2001 maybeDate =
QDate(maybeYear, maybeMonth, maybeDay);
2002 maybeTime =
QTime(0, 0, 0);
2018 auto startOfFieldBody = head.
indexOf(
':', headerStart);
2019 if (startOfFieldBody < 0) {
2023 const char *rawType = head.
constData() + headerStart;
2024 const size_t rawTypeLen = startOfFieldBody - headerStart;
2027 if (startOfFieldBody < head.
size() - 1 && head[startOfFieldBody] ==
' ') {
2031 bool folded =
false;
2032 endOfFieldBody = findHeaderLineEnd(head, startOfFieldBody, &folded);
2035 if (rawTypeLen > 0) {
2036 header = HeaderFactory::createHeader(rawType, rawTypeLen);
2043 const auto unfoldedBody = unfoldHeader(head.
constData() + startOfFieldBody, endOfFieldBody - startOfFieldBody);
2054std::unique_ptr<KMime::Headers::Base> parseNextHeader(
QByteArrayView &head)
2056 qsizetype endOfFieldBody = 0;
2057 std::unique_ptr<KMime::Headers::Base> header(extractHeader(head, 0, endOfFieldBody));
2059 head = head.
mid(endOfFieldBody + 1);
2078 auto pos = content.
indexOf(
"\n\n", 0);
2080 header = content.
left(++pos);
2081 body = content.
mid(pos + 1);
2093 qsizetype cursor = 0;
2094 while (cursor < head.
size()) {
2095 const auto headerStart = cursor;
2096 qsizetype endOfFieldBody;
2097 if (
auto header = extractHeader(head, headerStart, endOfFieldBody)) {
2099 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 char * constData() const const
bool endsWith(QByteArrayView bv) const const
QByteArray first(qsizetype n) const const
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
bool isEmpty() const const
bool isNull() 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
QByteArray toLower() const const
QByteArrayView mid(qsizetype start, qsizetype length) const const
void chop(qsizetype length)
int compare(QByteArrayView bv, Qt::CaseSensitivity cs) const const
const_pointer constData() const const
const_pointer data() const const
bool endsWith(QByteArrayView bv) const const
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
qsizetype size() const const
QByteArray toByteArray() 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)
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
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)