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 c9847207a5aa3ce2526bfee78edf3dbae9c22e35 Author: Rene Cordier <[email protected]> AuthorDate: Tue Nov 3 14:31:45 2026 +0700 fix: ignore a malformed leading duplicate Content-Type / Content-Transfer-Encoding header so core and DOM agree (PR #137) --- .../james/mime4j/message/AbstractEntity.java | 20 +++++++++++++++ .../message/DefaultBodyDescriptorBuilder.java | 30 ++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 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 1875ada7..3c688905 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 @@ -28,6 +28,7 @@ import org.apache.james.mime4j.dom.field.ContentTransferEncodingField; import org.apache.james.mime4j.dom.field.ContentTypeField; import org.apache.james.mime4j.dom.field.FieldName; import org.apache.james.mime4j.dom.field.ParsedField; +import org.apache.james.mime4j.stream.Field; /** * Abstract MIME entity. @@ -135,6 +136,15 @@ 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)) { + ContentTypeField f = (ContentTypeField) field; + if (f.getMimeType() != null && !(f.isMultipart() && f.getBoundary() == null)) { + return f; + } + } return (ContentTypeField) getHeader().getField(FieldName.CONTENT_TYPE_LOWERCASE); } @@ -153,6 +163,16 @@ public abstract class AbstractEntity implements Entity { * @return the transfer encoding. */ public String getContentTransferEncoding() { + // a first Content-Transfer-Encoding header with an empty value does not + // describe the body: use the next usable one, like the core + // FallbackBodyDescriptorBuilder does + for (Field field : getHeader().getFields(FieldName.CONTENT_TRANSFER_ENCODING)) { + ContentTransferEncodingField f = (ContentTransferEncodingField) field; + String encoding = f.getEncoding(); + if (encoding != null && encoding.trim().length() > 0) { + return calcTransferEncoding(f); + } + } ContentTransferEncodingField f = (ContentTransferEncodingField) getHeader().getField(FieldName.CONTENT_TRANSFER_ENCODING); diff --git a/dom/src/main/java/org/apache/james/mime4j/message/DefaultBodyDescriptorBuilder.java b/dom/src/main/java/org/apache/james/mime4j/message/DefaultBodyDescriptorBuilder.java index af3a6b2e..c4b3f84b 100644 --- a/dom/src/main/java/org/apache/james/mime4j/message/DefaultBodyDescriptorBuilder.java +++ b/dom/src/main/java/org/apache/james/mime4j/message/DefaultBodyDescriptorBuilder.java @@ -28,6 +28,7 @@ import java.util.Map; import org.apache.james.mime4j.MimeException; import org.apache.james.mime4j.codec.DecodeMonitor; import org.apache.james.mime4j.dom.FieldParser; +import org.apache.james.mime4j.dom.field.ContentTransferEncodingField; import org.apache.james.mime4j.dom.field.ContentTypeField; import org.apache.james.mime4j.dom.field.FieldName; import org.apache.james.mime4j.dom.field.ParsedField; @@ -38,6 +39,8 @@ import org.apache.james.mime4j.stream.Field; import org.apache.james.mime4j.stream.RawField; import org.apache.james.mime4j.util.MimeUtil; +import static org.apache.james.mime4j.dom.field.ContentTypeField.TYPE_MULTIPART_PREFIX; + /** * Default {@link BodyDescriptorBuilder} implementation. */ @@ -96,12 +99,35 @@ public class DefaultBodyDescriptorBuilder implements BodyDescriptorBuilder { public Field addField(final RawField rawfield) throws MimeException { ParsedField field = fieldParser.parse(rawfield, monitor); String name = field.getNameLowerCase(); - if (field.bodyDescriptionField() && !fields.containsKey(name)) { - fields.put(name, field); + if (field.bodyDescriptionField()) { + ParsedField previous = fields.get(name); + if (previous == null || !describesBody(previous) && describesBody(field)) { + fields.put(name, field); + } } return field; } + /** + * Whether the field actually describes the body: a Content-Type with no + * mime type (empty or invalid, or a multipart type without boundary) or a + * Content-Transfer-Encoding with an empty value does not, and a later + * field may then be used instead. + */ + private static boolean describesBody(ParsedField field) { + if (field instanceof ContentTypeField) { + ContentTypeField contentType = (ContentTypeField) field; + return contentType.getMimeType() != null + && !(contentType.getMimeType().startsWith(TYPE_MULTIPART_PREFIX) + && contentType.getBoundary() == null); + } + if (field instanceof ContentTransferEncodingField) { + String encoding = ((ContentTransferEncodingField) field).getEncoding(); + return encoding != null && encoding.trim().length() > 0; + } + return true; + } + public BodyDescriptor build() { String actualMimeType = null; String actualMediaType = null; --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
