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;
|
const size_t contentsStart = pos;
|
||||||
size_t contentsEnd = 0;
|
size_t contentsEnd = 0;
|
||||||
|
|
||||||
// Extract the field value
|
bool firstLine = true;
|
||||||
while (pos < end)
|
|
||||||
{
|
|
||||||
c = buffer[pos];
|
|
||||||
|
|
||||||
// Check for folded line
|
// Parse field value, taking care of line folding (value on multiple lines)
|
||||||
if (c == '\r' && pos + 2 < end && buffer[pos + 1] == '\n' &&
|
for (size_t eol = 0 ; parserHelpers::findEOL(buffer, pos, end, &eol) ; pos = eol)
|
||||||
(buffer[pos + 2] == ' ' || buffer[pos + 2] == '\t'))
|
|
||||||
{
|
{
|
||||||
pos += 3;
|
// If the line does not start with a folding indicator (SPACE or TAB),
|
||||||
}
|
// and this is not the first line, then stop parsing lines
|
||||||
// Check for end of contents
|
if (!firstLine && !(buffer[pos] == ' ' || buffer[pos] == '\t'))
|
||||||
if (c == '\r' && pos + 1 < end && buffer[pos + 1] == '\n')
|
|
||||||
{
|
|
||||||
contentsEnd = pos;
|
|
||||||
pos += 2;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
else if (c == '\n')
|
|
||||||
{
|
|
||||||
contentsEnd = pos;
|
|
||||||
++pos;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (pos < end)
|
contentsEnd = eol;
|
||||||
{
|
firstLine = false;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos == end && contentsEnd == 0)
|
if (pos == end && contentsEnd == 0)
|
||||||
|
@ -87,6 +87,45 @@ public:
|
|||||||
const unsigned int x = static_cast <unsigned int>(c);
|
const unsigned int x = static_cast <unsigned int>(c);
|
||||||
return (x >= 0x20 && x <= 0x7E);
|
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(testBadValueType)
|
||||||
VMIME_TEST(testValueOnNextLine)
|
VMIME_TEST(testValueOnNextLine)
|
||||||
VMIME_TEST(testStripSpacesAtEnd)
|
VMIME_TEST(testStripSpacesAtEnd)
|
||||||
|
VMIME_TEST(testValueWithEmptyLine)
|
||||||
VMIME_TEST_LIST_END
|
VMIME_TEST_LIST_END
|
||||||
|
|
||||||
|
|
||||||
@ -84,4 +85,20 @@ VMIME_TEST_SUITE_BEGIN(headerFieldTest)
|
|||||||
VASSERT_EQ("Field value", toHex("field data"), toHex(hvalue->getWholeBuffer()));
|
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
|
VMIME_TEST_SUITE_END
|
||||||
|
Loading…
Reference in New Issue
Block a user