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 eacd72f1cbafa8b8c0529cfeb5f8307d43091cad
Author: Benoit TELLIER <[email protected]>
AuthorDate: Wed Nov 4 19:36:16 2026 +0100

    [FIX] Strict parsing: stop() in a header or before a message/rfc822 body is 
not an error
    
    After stop(), the truncated buffer ends the header, and readRawField
    reported it as HEADERS_PREMATURE_END, which strict parsing turns into a
    MimeParseEventException. And at the end of the header of a message/rfc822
    entity, the parser still created the encapsulated entity: created after
    stop(), it was not stopped and failed on its empty header. So a strict
    DefaultMessageBuilder#parseHeader failed on any message/rfc822 mail.
    
    A stopped entity no longer reports the end of its header as premature,
    and no longer recurses into an encapsulated message: its body is empty.
---
 .../org/apache/james/mime4j/stream/MimeEntity.java |  8 +++--
 .../mime4j/stream/StrictMimeTokenStreamTest.java   | 38 ++++++++++++++++++++++
 .../DefaultMessageBuilderParseHeaderTest.java      | 12 +++++++
 3 files changed, 56 insertions(+), 2 deletions(-)

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 ce311aea..d3c2426c 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
@@ -236,7 +236,10 @@ class MimeEntity implements EntityStateMachine {
                 }
                 linebuf.clear();
                 if (instream.readLine(linebuf) == -1) {
-                    monitor(Event.HEADERS_PREMATURE_END);
+                    if (!stopped) {
+                        // A header cut short by stop() is not a malformed 
message
+                        monitor(Event.HEADERS_PREMATURE_END);
+                    }
                     endOfHeader = true;
                     break;
                 }
@@ -331,7 +334,8 @@ class MimeEntity implements EntityStateMachine {
                 state = EntityState.T_START_MULTIPART;
                 clearMimePartStream();
             } else if (recursionMode != RecursionMode.M_NO_RECURSE
-                    && MimeUtil.isMessage(mimeType)) {
+                    && MimeUtil.isMessage(mimeType) && !stopped) {
+                // Once stopped, the encapsulated message is not parsed: there 
is nothing left to read
                 state = EntityState.T_BODY;
                 return nextMessage();
             } else {
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 b0c8ab68..f0f8c172 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
@@ -30,7 +30,9 @@ import java.io.ByteArrayOutputStream;
 import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 public class StrictMimeTokenStreamTest {
@@ -150,4 +152,40 @@ public class StrictMimeTokenStreamTest {
     private void checkNextIs(EntityState expected) throws Exception {
         Assert.assertEquals(MimeTokenStream.stateToString(expected), 
MimeTokenStream.stateToString(parser.next()));
     }
+
+    @Test
+    public void stopOnAFieldShouldEndTheHeaderInStrictMode() 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)));
+        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);
+    }
+
+    @Test
+    public void 
stopAtTheEndOfAMessageRfc822HeaderShouldNotParseTheEncapsulatedMessage() throws 
Exception {
+        String msg = "Subject: a\r\nContent-Type: message/rfc822\r\n\r\n"
+            + "From: [email protected]\r\nSubject: c\r\n\r\nbody\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_END_HEADER) {
+            // read the header
+        }
+        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_BODY, 
EntityState.T_END_MESSAGE,
+            EntityState.T_END_OF_STREAM), after);
+    }
 }
diff --git 
a/dom/src/test/java/org/apache/james/mime4j/message/DefaultMessageBuilderParseHeaderTest.java
 
b/dom/src/test/java/org/apache/james/mime4j/message/DefaultMessageBuilderParseHeaderTest.java
index a6098aed..95546819 100644
--- 
a/dom/src/test/java/org/apache/james/mime4j/message/DefaultMessageBuilderParseHeaderTest.java
+++ 
b/dom/src/test/java/org/apache/james/mime4j/message/DefaultMessageBuilderParseHeaderTest.java
@@ -127,4 +127,16 @@ public class DefaultMessageBuilderParseHeaderTest {
             }
         }
     }
+
+    @Test
+    public void strictParseHeaderShouldReturnTheHeaderOfAMessageRfc822Mail() 
throws Exception {
+        String msg = "Subject: a\r\nContent-Type: message/rfc822\r\n\r\n"
+            + "From: [email protected]\r\nSubject: c\r\n\r\nbody\r\n";
+        DefaultMessageBuilder builder = new DefaultMessageBuilder();
+        
builder.setMimeEntityConfig(MimeConfig.custom().setStrictParsing(true).build());
+
+        Header header = builder.parseHeader(new 
ByteArrayInputStream(msg.getBytes(StandardCharsets.US_ASCII)));
+
+        assertThat(header.getFields()).extracting(f -> 
f.getName()).containsExactly("Subject", "Content-Type");
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to