aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/vmime/headerField.cpp77
-rw-r--r--src/vmime/parserHelpers.hpp39
-rw-r--r--tests/parser/headerFieldTest.cpp17
3 files changed, 66 insertions, 67 deletions
diff --git a/src/vmime/headerField.cpp b/src/vmime/headerField.cpp
index f4c6187e..b88a5cd2 100644
--- a/src/vmime/headerField.cpp
+++ b/src/vmime/headerField.cpp
@@ -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;
- break;
- }
- else if (c == '\n')
- {
- contentsEnd = pos;
- ++pos;
+ // 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;
- }
-
- 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)
diff --git a/src/vmime/parserHelpers.hpp b/src/vmime/parserHelpers.hpp
index ce3f422a..430487b4 100644
--- a/src/vmime/parserHelpers.hpp
+++ b/src/vmime/parserHelpers.hpp
@@ -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;
+ }
};
diff --git a/tests/parser/headerFieldTest.cpp b/tests/parser/headerFieldTest.cpp
index 5281a3a2..0642dae7 100644
--- a/tests/parser/headerFieldTest.cpp
+++ b/tests/parser/headerFieldTest.cpp
@@ -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