Fixed parsing of empty lines in header field value.

This commit is contained in:
Vincent Richard 2014-06-01 20:46:17 +02:00
parent 17d764d65c
commit 30ea54f269
3 changed files with 65 additions and 66 deletions

View File

@ -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)

View File

@ -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;
}
};

View File

@ -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