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]

Reply via email to