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]
