This is an automated email from the ASF dual-hosted git repository. chibenwa pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-mime4j.git
commit 5c664e85939767c586b3eaf9c532f24adf532f36 Author: Benoit TELLIER <[email protected]> AuthorDate: Thu Nov 5 05:25:13 2026 +0100 [FIX] Strict parsing: stop() at the start of an entity is not an invalid header Since 23c513fe a header cut short by stop() no longer reports HEADERS_PREMATURE_END, but nextField went on building a field from what it had. With no field read yet the builder is empty, and the failure was reported as INVALID_HEADER, which strict parsing turns into a MimeParseEventException: stop() at T_START_MESSAGE, T_START_HEADER or T_START_BODYPART failed on valid mail. So did a stop() on a field whose read-ahead line is malformed, a line the caller had stopped before. A field that fails after stop() is not reported: the header ends there. --- .../org/apache/james/mime4j/stream/MimeEntity.java | 4 ++ .../mime4j/stream/StrictMimeTokenStreamTest.java | 50 ++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/core/src/main/java/org/apache/james/mime4j/stream/MimeEntity.java b/core/src/main/java/org/apache/james/mime4j/stream/MimeEntity.java index d3c2426c..ba9da79d 100644 --- a/core/src/main/java/org/apache/james/mime4j/stream/MimeEntity.java +++ b/core/src/main/java/org/apache/james/mime4j/stream/MimeEntity.java @@ -294,6 +294,10 @@ class MimeEntity implements EntityStateMachine { field = parsedField != null ? parsedField : rawfield; return true; } catch (MimeException e) { + if (stopped) { + // Nothing, or only the line read ahead, is left to parse: the header ends here + return false; + } monitor(Event.INVALID_HEADER); if (config.isMalformedHeaderStartsBody()) { LineReaderInputStream instream = getDataStream(); diff --git a/core/src/test/java/org/apache/james/mime4j/stream/StrictMimeTokenStreamTest.java b/core/src/test/java/org/apache/james/mime4j/stream/StrictMimeTokenStreamTest.java index f0f8c172..7d338ebb 100644 --- a/core/src/test/java/org/apache/james/mime4j/stream/StrictMimeTokenStreamTest.java +++ b/core/src/test/java/org/apache/james/mime4j/stream/StrictMimeTokenStreamTest.java @@ -188,4 +188,54 @@ public class StrictMimeTokenStreamTest { Assert.assertEquals(Arrays.asList(EntityState.T_BODY, EntityState.T_END_MESSAGE, EntityState.T_END_OF_STREAM), after); } + + @Test + public void stopAtTheStartOfTheMessageShouldEndTheHeaderInStrictMode() throws Exception { + String msg = "Subject: a\r\nFrom: [email protected]\r\n\r\nbody\r\n"; + MimeTokenStream stream = new MimeTokenStream(MimeConfig.custom().setStrictParsing(true).build()); + stream.parse(new ByteArrayInputStream(msg.getBytes(StandardCharsets.US_ASCII))); + Assert.assertEquals(EntityState.T_START_MESSAGE, stream.getState()); + stream.stop(); + + List<EntityState> after = new ArrayList<>(); + EntityState state; + do { + state = stream.next(); + after.add(state); + } while (state != EntityState.T_END_OF_STREAM); + Assert.assertEquals(Arrays.asList(EntityState.T_START_HEADER, EntityState.T_END_HEADER, EntityState.T_BODY, + EntityState.T_END_MESSAGE, EntityState.T_END_OF_STREAM), after); + } + + @Test + public void stopAtTheStartOfABodyPartShouldNotFailInStrictMode() throws Exception { + String msg = "Content-Type: multipart/mixed; boundary=b\r\n\r\n" + + "--b\r\nSubject: part\r\n\r\none\r\n--b--\r\n"; + MimeTokenStream stream = new MimeTokenStream(MimeConfig.custom().setStrictParsing(true).build()); + stream.parse(new ByteArrayInputStream(msg.getBytes(StandardCharsets.US_ASCII))); + while (stream.next() != EntityState.T_START_BODYPART) { + // up to the part + } + stream.stop(); + + EntityState state; + do { + state = stream.next(); + } while (state != EntityState.T_END_OF_STREAM); + } + + @Test + public void stopOnAFieldFollowedByAMalformedLineShouldNotFailInStrictMode() throws Exception { + String msg = "Subject: a\r\nbadline\r\n\r\nbody\r\n"; + MimeTokenStream stream = new MimeTokenStream(MimeConfig.custom().setStrictParsing(true).build()); + stream.parse(new ByteArrayInputStream(msg.getBytes(StandardCharsets.US_ASCII))); + stream.next(); // T_START_HEADER + Assert.assertEquals(EntityState.T_FIELD, stream.next()); + stream.stop(); + + EntityState state; + do { + state = stream.next(); + } while (state != EntityState.T_END_OF_STREAM); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
