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 72e2ddd7643832fff3e1e136e8fa5f0c9af9a65a
Author: Benoit TELLIER <[email protected]>
AuthorDate: Wed Nov 4 15:04:24 2026 +0100

    [FIX] DefaultMessageWriter: delimit a multipart with the Content-Type that 
describes the body
    
    Since c9847207 the parser and AbstractEntity skip a leading Content-Type
    that gives no mime type or is multipart without boundary, and use the
    next usable one. DefaultMessageWriter still read the first Content-Type
    to find the boundary, so writing such a parsed Multipart threw
    "Multipart boundary not specified". The writer now picks the field with
    the same rule, through a helper shared with AbstractEntity.
    
    Fixes #104
---
 .../apache/james/mime4j/message/AbstractEntity.java | 17 ++++++++++++-----
 .../james/mime4j/message/DefaultMessageWriter.java  |  4 +---
 .../mime4j/message/DefaultMessageWriterTest.java    | 21 +++++++++++++++++++++
 3 files changed, 34 insertions(+), 8 deletions(-)

diff --git 
a/dom/src/main/java/org/apache/james/mime4j/message/AbstractEntity.java 
b/dom/src/main/java/org/apache/james/mime4j/message/AbstractEntity.java
index 3c688905..4089d783 100644
--- a/dom/src/main/java/org/apache/james/mime4j/message/AbstractEntity.java
+++ b/dom/src/main/java/org/apache/james/mime4j/message/AbstractEntity.java
@@ -136,16 +136,23 @@ public abstract class AbstractEntity implements Entity {
     }
 
     private ContentTypeField getContentTypeField() {
-        // a first Content-Type header that yields no mime type (empty, 
invalid,
-        // or multipart without boundary) does not describe the body: use the 
next
-        // usable one, like the core FallbackBodyDescriptorBuilder does
-        for (Field field : 
getHeader().getFields(FieldName.CONTENT_TYPE_LOWERCASE)) {
+        return getContentTypeField(getHeader());
+    }
+
+    /**
+     * Returns the Content-Type field of the header that describes the body.
+     * A first Content-Type header that yields no mime type (empty, invalid,
+     * or multipart without boundary) does not describe the body: the next
+     * usable one is used, like the core FallbackBodyDescriptorBuilder does.
+     */
+    static ContentTypeField getContentTypeField(Header header) {
+        for (Field field : header.getFields(FieldName.CONTENT_TYPE_LOWERCASE)) 
{
             ContentTypeField f = (ContentTypeField) field;
             if (f.getMimeType() != null && !(f.isMultipart() && 
f.getBoundary() == null)) {
                 return f;
             }
         }
-        return (ContentTypeField) 
getHeader().getField(FieldName.CONTENT_TYPE_LOWERCASE);
+        return (ContentTypeField) 
header.getField(FieldName.CONTENT_TYPE_LOWERCASE);
     }
 
     /**
diff --git 
a/dom/src/main/java/org/apache/james/mime4j/message/DefaultMessageWriter.java 
b/dom/src/main/java/org/apache/james/mime4j/message/DefaultMessageWriter.java
index c8920f17..9a9eda62 100644
--- 
a/dom/src/main/java/org/apache/james/mime4j/message/DefaultMessageWriter.java
+++ 
b/dom/src/main/java/org/apache/james/mime4j/message/DefaultMessageWriter.java
@@ -34,7 +34,6 @@ import org.apache.james.mime4j.dom.MessageWriter;
 import org.apache.james.mime4j.dom.Multipart;
 import org.apache.james.mime4j.dom.SingleBody;
 import org.apache.james.mime4j.dom.field.ContentTypeField;
-import org.apache.james.mime4j.dom.field.FieldName;
 import org.apache.james.mime4j.stream.Field;
 import org.apache.james.mime4j.util.ByteArrayBuffer;
 import org.apache.james.mime4j.util.ByteSequence;
@@ -242,8 +241,7 @@ public class DefaultMessageWriter implements MessageWriter {
             throw new IllegalArgumentException(
                     "Missing header in parent entity");
 
-        ContentTypeField contentType = (ContentTypeField) header
-                .getField(FieldName.CONTENT_TYPE_LOWERCASE);
+        ContentTypeField contentType = 
AbstractEntity.getContentTypeField(header);
         if (contentType == null)
             throw new IllegalArgumentException(
                     "Content-Type field not specified");
diff --git 
a/dom/src/test/java/org/apache/james/mime4j/message/DefaultMessageWriterTest.java
 
b/dom/src/test/java/org/apache/james/mime4j/message/DefaultMessageWriterTest.java
index 369bf6fa..b280c281 100644
--- 
a/dom/src/test/java/org/apache/james/mime4j/message/DefaultMessageWriterTest.java
+++ 
b/dom/src/test/java/org/apache/james/mime4j/message/DefaultMessageWriterTest.java
@@ -347,4 +347,25 @@ public class DefaultMessageWriterTest {
         assertThat(ContentUtil.buffer(((SingleBody) 
m2.getBody()).getInputStream()))
             .isEqualTo("a\t".getBytes(StandardCharsets.US_ASCII));
     }
+
+    @Test
+    public void writeMessageShouldUseTheContentTypeThatDescribesTheBody() 
throws Exception {
+        String raw = "Content-Type: multipart/mixed\r\n"
+            + "Content-Type: multipart/mixed; boundary=b\r\n"
+            + "\r\n"
+            + "--b\r\n"
+            + "\r\n"
+            + "x\r\n"
+            + "--b--\r\n";
+        Message message = new DefaultMessageBuilder()
+            .parseMessage(new 
ByteArrayInputStream(raw.getBytes(StandardCharsets.US_ASCII)));
+        assertThat(message.getBody()).isInstanceOf(Multipart.class);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        new DefaultMessageWriter().writeMessage(message, out);
+
+        Message readBack = new DefaultMessageBuilder()
+            .parseMessage(new ByteArrayInputStream(out.toByteArray()));
+        assertThat(((Multipart) readBack.getBody()).getCount()).isEqualTo(1);
+    }
 }


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

Reply via email to