Fixed parsing of empty lines in header field value.
This commit is contained in:
parent
17d764d65c
commit
30ea54f269
@ -144,75 +144,18 @@ shared_ptr <headerField> headerField::parseNext
|
||||
const size_t contentsStart = pos;
|
||||
size_t contentsEnd = 0;
|
||||
|
||||
// Extract the field value
|
||||
while (pos < end)
|
||||
bool firstLine = true;
|
||||
|
||||
// Parse field value, taking care of line folding (value on multiple lines)
|
||||
for (size_t eol = 0 ; parserHelpers::findEOL(buffer, pos, end, &eol) ; pos = eol)
|
||||
{
|
||||
c = buffer[pos];
|
||||
|
||||
// Check for folded line
|
||||
if (c == '\r' && pos + 2 < end && buffer[pos + 1] == '\n' &&
|
||||
(buffer[pos + 2] == ' ' || buffer[pos + 2] == '\t'))
|
||||
{
|
||||
pos += 3;
|
||||
}
|
||||
// Check for end of contents
|
||||
if (c == '\r' && pos + 1 < end && buffer[pos + 1] == '\n')
|
||||
{
|
||||
contentsEnd = pos;
|
||||
pos += 2;
|
||||
// If the line does not start with a folding indicator (SPACE or TAB),
|
||||
// and this is not the first line, then stop parsing lines
|
||||
if (!firstLine && !(buffer[pos] == ' ' || buffer[pos] == '\t'))
|
||||
break;
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
contentsEnd = pos;
|
||||
++pos;
|
||||
break;
|
||||
}
|
||||
|
||||
while (pos < end)
|
||||
{
|
||||
c = buffer[pos];
|
||||
|
||||
// Check for end of line
|
||||
if (c == '\r' && pos + 1 < end && buffer[pos + 1] == '\n')
|
||||
{
|
||||
contentsEnd = pos;
|
||||
pos += 2;
|
||||
break;
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
contentsEnd = pos;
|
||||
++pos;
|
||||
break;
|
||||
}
|
||||
|
||||
++pos;
|
||||
}
|
||||
|
||||
// Handle the case of folded lines
|
||||
if (buffer[pos] == ' ' || buffer[pos] == '\t')
|
||||
{
|
||||
// This is a folding white-space: we keep it as is and
|
||||
// we continue with contents parsing...
|
||||
|
||||
// If the line contains only space characters, we assume it is
|
||||
// the end of the headers. This is not strictly standard-compliant
|
||||
// but, hey, we can't fail when parsing some malformed mails...
|
||||
while (pos < end && (buffer[pos] == ' ' || buffer[pos] == '\t'))
|
||||
++pos;
|
||||
|
||||
if ((pos < end && buffer[pos] == '\n') ||
|
||||
(pos + 1 < end && buffer[pos] == '\r' && buffer[pos + 1] == '\n'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// End of this field
|
||||
break;
|
||||
}
|
||||
contentsEnd = eol;
|
||||
firstLine = false;
|
||||
}
|
||||
|
||||
if (pos == end && contentsEnd == 0)
|
||||
|
@ -87,6 +87,45 @@ public:
|
||||
const unsigned int x = static_cast <unsigned int>(c);
|
||||
return (x >= 0x20 && x <= 0x7E);
|
||||
}
|
||||
|
||||
|
||||
/** Finds the next EOL sequence in the specified buffer.
|
||||
* An EOL sequence may be a CR+LF sequence, or a LF sequence.
|
||||
*
|
||||
* @param buffer search buffer
|
||||
* @param currentPos start searching from this position
|
||||
* @param end stop searching at this position
|
||||
* @param eol will receive the position after the EOL sequence
|
||||
* @return true if an EOL sequence has been found, or false if
|
||||
* no EOL sequence was found before the end of the buffer
|
||||
*/
|
||||
static bool findEOL(const string& buffer, const size_t currentPos, const size_t end, size_t* eol)
|
||||
{
|
||||
size_t pos = currentPos;
|
||||
|
||||
if (pos == end)
|
||||
return false;
|
||||
|
||||
while (pos < end)
|
||||
{
|
||||
if (buffer[pos] == '\r' && pos + 1 < end && buffer[pos + 1] == '\n')
|
||||
{
|
||||
*eol = pos + 2;
|
||||
return true;
|
||||
}
|
||||
else if (buffer[pos] == '\n')
|
||||
{
|
||||
*eol = pos + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
++pos;
|
||||
}
|
||||
|
||||
*eol = end;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -30,6 +30,7 @@ VMIME_TEST_SUITE_BEGIN(headerFieldTest)
|
||||
VMIME_TEST(testBadValueType)
|
||||
VMIME_TEST(testValueOnNextLine)
|
||||
VMIME_TEST(testStripSpacesAtEnd)
|
||||
VMIME_TEST(testValueWithEmptyLine)
|
||||
VMIME_TEST_LIST_END
|
||||
|
||||
|
||||
@ -84,4 +85,20 @@ VMIME_TEST_SUITE_BEGIN(headerFieldTest)
|
||||
VASSERT_EQ("Field value", toHex("field data"), toHex(hvalue->getWholeBuffer()));
|
||||
}
|
||||
|
||||
void testValueWithEmptyLine()
|
||||
{
|
||||
vmime::parsingContext ctx;
|
||||
|
||||
const vmime::string buffer = "Field: \r\n\tdata1\r\n\tdata2\r\n\t\r\n\tdata3";
|
||||
|
||||
vmime::shared_ptr <vmime::headerField> hfield =
|
||||
vmime::headerField::parseNext(ctx, buffer, 0, buffer.size());
|
||||
|
||||
vmime::shared_ptr <vmime::text> hvalue =
|
||||
hfield->getValue <vmime::text>();
|
||||
|
||||
VASSERT_EQ("Field name", "Field", hfield->getName());
|
||||
VASSERT_EQ("Field value", "data1 data2 data3", hvalue->getWholeBuffer());
|
||||
}
|
||||
|
||||
VMIME_TEST_SUITE_END
|
||||
|
Loading…
Reference in New Issue
Block a user