[Git][java-team/apache-mime4j][upstream] New upstream version 0.8.13
Tony Mancill (@tmancill)
gitlab at salsa.debian.org
Sat Aug 16 05:09:29 BST 2025
Tony Mancill pushed to branch upstream at Debian Java Maintainers / apache-mime4j
Commits:
b8b38b36 by tony mancill at 2025-08-15T20:52:30-07:00
New upstream version 0.8.13
- - - - -
27 changed files:
- CHANGELOG.md
- assemble/pom.xml
- benchmark/pom.xml
- core/pom.xml
- core/src/main/java/org/apache/james/mime4j/io/MimeBoundaryInputStream.java
- core/src/main/java/org/apache/james/mime4j/stream/RawFieldParser.java
- core/src/main/java/org/apache/james/mime4j/util/MimeParameterMapping.java
- core/src/main/javadoc/overview.html
- + core/src/test/java/org/apache/james/mime4j/parser/ExtractPartWithDifferentLengthsTest.java
- core/src/test/java/org/apache/james/mime4j/parser/MimeStreamParserTest.java
- core/src/test/java/org/apache/james/mime4j/parser/TestHandler.java
- core/src/test/java/org/apache/james/mime4j/stream/RawFieldParserTest.java
- core/src/test/java/org/apache/james/mime4j/stream/RawFieldTest.java
- dom/pom.xml
- dom/src/main/java/org/apache/james/mime4j/dom/Disposable.java
- dom/src/main/java/org/apache/james/mime4j/field/DefaultFieldParser.java
- dom/src/main/java/org/apache/james/mime4j/field/LenientFieldParser.java
- dom/src/main/java/org/apache/james/mime4j/field/address/LenientAddressParser.java
- dom/src/main/jjtree/org/apache/james/mime4j/field/address/AddressListParser.jjt
- dom/src/test/java/org/apache/james/mime4j/field/LenientContentDispositionFieldTest.java
- dom/src/test/java/org/apache/james/mime4j/field/address/DefaultAddressBuilderTest.java
- dom/src/test/java/org/apache/james/mime4j/field/address/LenientAddressBuilderTest.java
- examples/pom.xml
- james-utils/pom.xml
- mbox/pom.xml
- pom.xml
- storage/pom.xml
Changes:
=====================================
CHANGELOG.md
=====================================
@@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
+## [0.8.13] - unreleased
+
+ - MIME4J-332 Improve handling of "sectioned" field names
+ - MIME4J-331 Add support for RFC-6532 Internationalized Email Headers, and test it. (#114)
+ - MIME4J-330 Fix MimeStreamParser bug: part body stream ends with CR
+
+## [0.8.12] - 2024-12-23
+
+New release tagged without changes.
+
## [0.8.11] - 2024-03-05
- MIME4J-326 SILENT monitor for header decoding
=====================================
assemble/pom.xml
=====================================
@@ -23,7 +23,7 @@
<parent>
<artifactId>apache-mime4j-project</artifactId>
<groupId>org.apache.james</groupId>
- <version>0.8.12</version>
+ <version>0.8.13</version>
<relativePath>../pom.xml</relativePath>
</parent>
=====================================
benchmark/pom.xml
=====================================
@@ -23,7 +23,7 @@
<parent>
<artifactId>apache-mime4j-project</artifactId>
<groupId>org.apache.james</groupId>
- <version>0.8.12</version>
+ <version>0.8.13</version>
<relativePath>../pom.xml</relativePath>
</parent>
=====================================
core/pom.xml
=====================================
@@ -23,7 +23,7 @@
<parent>
<artifactId>apache-mime4j-project</artifactId>
<groupId>org.apache.james</groupId>
- <version>0.8.12</version>
+ <version>0.8.13</version>
<relativePath>../pom.xml</relativePath>
</parent>
=====================================
core/src/main/java/org/apache/james/mime4j/io/MimeBoundaryInputStream.java
=====================================
@@ -245,14 +245,14 @@ public class MimeBoundaryInputStream extends LineReaderInputStream {
// Make sure the boundary is terminated with EOS
break;
} else {
- // or with a whitespace or '--'
+ // or with a whitespace or '--'
char ch = (char)(buffer.byteAt(pos));
if (CharsetUtil.isWhitespace(ch)) {
break;
}
if (ch == '-' && remaining > 1 && (char)(buffer.byteAt(pos+1)) == '-') {
break;
- }
+ }
}
}
off = i + boundary.length;
@@ -265,8 +265,14 @@ public class MimeBoundaryInputStream extends LineReaderInputStream {
if (eof) {
limit = buffer.limit();
} else {
- limit = buffer.limit() - (boundary.length + 2);
- // [LF] [boundary] [CR][LF] minus one char
+ int position = buffer.limit() - (boundary.length + 3); // 2nd position before boundary
+ if (position >= 0 && (char) buffer.byteAt(position) == '\r') {
+ limit = buffer.limit() - (boundary.length + 3);
+ // boundary [CR][LF] minus two chars
+ } else {
+ limit = buffer.limit() - (boundary.length + 2);
+ // boundary [LF] minus one char
+ }
}
}
return bytesRead;
=====================================
core/src/main/java/org/apache/james/mime4j/stream/RawFieldParser.java
=====================================
@@ -323,6 +323,7 @@ public class RawFieldParser {
*/
public void copyContent(final ByteSequence buf, final ParserCursor cursor, final BitSet delimiters,
final StringBuilder dst) {
+ ByteArrayBuffer dstRaw = new ByteArrayBuffer(80);
int pos = cursor.getPos();
int indexFrom = cursor.getPos();
int indexTo = cursor.getUpperBound();
@@ -333,10 +334,11 @@ public class RawFieldParser {
break;
} else {
pos++;
- dst.append(current);
+ dstRaw.append(current);
}
}
cursor.updatePos(pos);
+ dst.append(ContentUtil.decode(StandardCharsets.UTF_8, dstRaw));
}
/**
=====================================
core/src/main/java/org/apache/james/mime4j/util/MimeParameterMapping.java
=====================================
@@ -19,33 +19,160 @@
package org.apache.james.mime4j.util;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.james.mime4j.MimeException;
+
+/**
+ * This class tracks parameter mappings and tries to respect <a href="https://www.rfc-editor.org/rfc/rfc2231">rfc2231</a>
+ * and conform roughly to Thunderbird's behavior as described on <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=588781>bug 588781</a>.
+ * <p>
+ * See <a href="https://issues.apache.org/jira/browse/MIME4J-332">MIME4J-332</a>
+ * <p>
+ * These are the behaviors:
+ * <ul>
+ * <li>If there are multiple keys for standard parameters, use the first and ignore the rest</li>
+ * <li>If there are multiple keys for extended parameters, use the first and ignore the rest</li>
+ * <li>If there's an extended and standard key for a given field, prefer the extended</li>
+ * <li>If there's an extended and a continuation, prefer the extended, recognizing that a continuation field may also include charset information</li>
+ * </ul>
+ */
public class MimeParameterMapping {
+ /**
+ * What type of parameter name is it
+ */
+ enum PARAMETER_TYPE {
+ /**
+ * standard parameter name, e.g. "filename"
+ */
+ STANDARD,
+ /**
+ * extended parameter without continuation numbers, e.g. "filename*", typically a sign that charset is included
+ */
+ EXTENDED,
+ /**
+ * continuation parameter, e.g. "filename*0", used to extend field lengths beyond the initial length limit, may also
+ * include charset information like {@link PARAMETER_TYPE#EXTENDED}
+ */
+ CONTINUATION
+ }
+
+ //this could be more precise and require only a *\\d+\\Z
+ private static final Pattern STAR_AND_NUMBER = Pattern.compile("\\*\\d");
+
+ private final Set<String> parameterNames = new HashSet<>();
+ private final Map<String, String> standard = new HashMap<>();
+ private final Map<String, String> extended = new HashMap<>();
+ private final Map<String, String> continuation = new HashMap<>();
+
private final Map<String, String> parameters = new HashMap<>();
- /** Charset, taken from the first item added to {@link #parameters}. */
+ private boolean needToUpdate = true;
+
+ /** Charset, taken from the first item added via {@link #addParameter(String, String)}. */
private String charset;
+ /**
+ *
+ * @return an Unmodifiable map of the parameters as calculated
+ * by the algorithm described in the class javadoc via {@link #get(String)}
+ */
public Map<String, String> getParameters() {
- return parameters;
+ if (needToUpdate) {
+ updateParameters();
+ needToUpdate = false;
+ }
+ return Collections.unmodifiableMap(parameters);
}
+ private void updateParameters() {
+ parameters.clear();
+ for (String param : parameterNames) {
+ parameters.put(param, get(param));
+ }
+ }
+
+ /**
+ * Applies the algorithm described in the class javadoc and returns
+ * the decoded value from the best option.
+ *
+ * @param name field name (must be lowercased)
+ * @return field value or <code>null</code> if it doesn't exist
+ */
public String get(String name) {
- return parameters.get(name);
+ if (! parameterNames.contains(name)) {
+ return null;
+ }
+
+ if (extended.containsKey(name)) {
+ try {
+ return decodeParameterValue(extended.get(name));
+ } catch (DecodeException e) {
+ //ignore and try next
+ }
+ }
+ if (continuation.containsKey(name)) {
+ try {
+ return decodeParameterValue(continuation.get(name));
+ } catch (DecodeException e) {
+ //ignore and try standard
+ }
+ }
+ if (standard.containsKey(name)) {
+ try {
+ return decodeParameterValue(standard.get(name));
+ } catch (DecodeException e) {
+ //ignore and try without decoding
+ }
+ }
+ //couldn't decode anything. not clear what the "spec" says is the right behavior
+ //For now, back off in reverse order
+ if (standard.containsKey(name)) {
+ return standard.get(name);
+ }
+
+ if (continuation.containsKey(name)) {
+ return continuation.get(name);
+ }
+
+ if (extended.containsKey(name)) {
+ return extended.get(name);
+ }
+ //this should be unreachable
+ return null;
}
- public void addParameter(String name, String value) {
- String key = removeSectionFromName(name).toLowerCase();
- if (parameters.containsKey(key)) {
- parameters.put(key, decodeParameterValue(parameters.get(key) + value));
- } else {
- parameters.put(key, decodeParameterValue(value));
+ public void addParameter(final String name, String value) {
+ ParameterTypePair parameterTypePair = getParameterTypePair(name);
+ parameterNames.add(parameterTypePair.fieldName);
+ needToUpdate = true;
+ switch (parameterTypePair.fieldType) {
+ case EXTENDED:
+ extended.putIfAbsent(parameterTypePair.fieldName, value);
+ break;
+ case CONTINUATION:
+ if (continuation.containsKey(parameterTypePair.fieldName)) {
+ String newValue = continuation.get(parameterTypePair.fieldName) + value;
+ continuation.put(parameterTypePair.fieldName, newValue);
+ } else {
+ continuation.put(parameterTypePair.fieldName, value);
+ }
+ break;
+ case STANDARD:
+ standard.putIfAbsent(parameterTypePair.fieldName, value);
}
}
- private String decodeParameterValue(String value) {
+ private String decodeParameterValue(String value) throws DecodeException {
if (value == null) {
return null;
}
@@ -60,20 +187,65 @@ public class MimeParameterMapping {
}
charset = value.substring(0, charsetEnd);
String fileName = value.substring(languageEnd + 1);
+ //check that the charset is valid
+ try {
+ Charset.forName(charset);
+ } catch (IllegalArgumentException e) {
+ return fileName;
+ }
return urlDecode(fileName);
}
- private String urlDecode(String value) {
+ private String urlDecode(String value) throws DecodeException {
try {
return java.net.URLDecoder.decode(value, charset);
- }
- catch (Exception ignore) {
- return value;
+ } catch (Exception e) {
+ throw new DecodeException(e);
}
}
- private String removeSectionFromName(String parameterName) {
+ private ParameterTypePair getParameterTypePair(String parameterName) {
int position = parameterName.indexOf('*');
- return parameterName.substring(0, position < 0 ? parameterName.length() : position);
+ if (position < 0) {
+ return new ParameterTypePair(parameterName.toLowerCase(Locale.ROOT), PARAMETER_TYPE.STANDARD);
+ }
+ String fieldName = parameterName.substring(0, position);
+ fieldName = fieldName.toLowerCase(Locale.ROOT);
+
+ String starAndAfter = parameterName.substring(position);
+ if ("*".equals(starAndAfter)) {
+ return new ParameterTypePair(fieldName, PARAMETER_TYPE.EXTENDED);
+ }
+ Matcher m = STAR_AND_NUMBER.matcher(starAndAfter);
+ if (m.find()) {
+ return new ParameterTypePair(fieldName, PARAMETER_TYPE.CONTINUATION);
+ }
+ //something is weird, treat this as a STAR?
+ return new ParameterTypePair(fieldName, PARAMETER_TYPE.EXTENDED);
+ }
+
+ private static class ParameterTypePair {
+ private final String fieldName;
+ private final PARAMETER_TYPE fieldType;
+
+ public ParameterTypePair(String fieldName, PARAMETER_TYPE fieldType) {
+ this.fieldName = fieldName;
+ this.fieldType = fieldType;
+ }
+ }
+
+ private static class DecodeException extends MimeException {
+
+ public DecodeException(String message) {
+ super(message);
+ }
+
+ public DecodeException(Throwable cause) {
+ super(cause);
+ }
+
+ public DecodeException(String message, Throwable cause) {
+ super(message, cause);
+ }
}
}
=====================================
core/src/main/javadoc/overview.html
=====================================
@@ -34,7 +34,7 @@
However, mime4j does include facilities to decode bodies and fields.</p>
<p>The parser has been designed to be tolerant against messages violating the standards. It has
been tested using a large corpus (>5000) of e-mail messages. As a benchmark the widely used
- perl MIME::Tools parser has been used. mime4j and MIME:Tools rarely differ (<25 in those
+ perl MIME::Tools parser has been used. mime4j and MIME:Tools rarely differ (<25 in those
5000). When they do (which only occurs for illegally formatted spam messages) we think mime4j
does a better job.</p>
</BODY>
=====================================
core/src/test/java/org/apache/james/mime4j/parser/ExtractPartWithDifferentLengthsTest.java
=====================================
@@ -0,0 +1,73 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.mime4j.parser;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.james.mime4j.MimeException;
+import org.apache.james.mime4j.stream.BodyDescriptor;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+public class ExtractPartWithDifferentLengthsTest {
+ @Test
+ public void testExtractPartWithDifferentLengths() throws Exception {
+ StringBuilder partBuilder = new StringBuilder();
+ for (int i = 1; i <= 5000; i++) {
+ partBuilder.append(i % 80 == 0 ? "\n" : "A");
+ String part = partBuilder.toString();
+ String mimeMessage = createMimeMultipart(part);
+
+ String extracted = extractPart(mimeMessage);
+ Assert.assertEquals(part, extracted);
+ }
+ }
+
+ private String createMimeMultipart(String part) {
+ return "Content-type: multipart/mixed; boundary=QvEgqhjEnYxz\n"
+ + "\n"
+ + "--QvEgqhjEnYxz\n"
+ + "Content-Type: text/plain\n"
+ + "\n"
+ + part
+ + "\r\n"
+ + "--QvEgqhjEnYxz--\n";
+ }
+
+ private String extractPart(String mimeMessage) throws
+ MimeException, IOException {
+ String[] resultWrapper = new String[1];
+
+ MimeStreamParser parser = new MimeStreamParser();
+ parser.setContentHandler(new AbstractContentHandler() {
+ @Override
+ public void body(BodyDescriptor bd, InputStream is) throws IOException {
+ resultWrapper[0] = new String(IOUtils.toString(is,
+ StandardCharsets.UTF_8).getBytes());
+ }
+ });
+ parser.parse(new ByteArrayInputStream(mimeMessage.getBytes()));
+ return resultWrapper[0];
+ }
+}
=====================================
core/src/test/java/org/apache/james/mime4j/parser/MimeStreamParserTest.java
=====================================
@@ -21,6 +21,7 @@ package org.apache.james.mime4j.parser;
import org.apache.james.mime4j.stream.BodyDescriptor;
import org.apache.james.mime4j.stream.Field;
+import org.apache.james.mime4j.stream.MimeConfig;
import org.apache.james.mime4j.util.ByteSequence;
import org.apache.james.mime4j.util.ContentUtil;
import org.junit.Assert;
@@ -29,6 +30,7 @@ import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.Charset;
import java.util.LinkedList;
public class MimeStreamParserTest {
@@ -430,6 +432,55 @@ public class MimeStreamParserTest {
Assert.assertEquals(expected, result);
}
+ @Test
+ public void testRfc6531() throws Exception {
+ MimeStreamParser parser = new MimeStreamParser(MimeConfig.STRICT);
+ parser.setContentDecoding(true);
+ TestHandler handler = new TestHandler();
+ parser.setContentHandler(handler);
+
+ String msg = "Subject: Naïve Subject\r\n"
+ + "From: foo@ø.example\r\n"
+ + "To: ø@example.com\r\n"
+ + "Content-Type: text/plain; charset=utf-8\r\n"
+ + "\r\n"
+ + "This sentence ends with the letter x.\r\n";
+ String expected = "<message>\r\n"
+ + "<header>\r\n"
+ + "<field>\r\n"
+ + "Subject: Naïve Subject"
+ + "</field>\r\n"
+ + "<field>\r\n"
+ + "From: foo@ø.example"
+ + "</field>\r\n"
+ + "<field>\r\n"
+ + "To: ø@example.com"
+ + "</field>\r\n"
+ + "<field>\r\n"
+ + "Content-Type: text/plain; charset=utf-8"
+ + "</field>\r\n"
+ + "</header>\r\n"
+ + "<body>\r\n"
+ + "This sentence ends with the letter x.\r\n"
+ + "</body>\r\n"
+ + "</message>\r\n";
+
+ // Dot the ı's and check that the ø is present in the message
+ // as its UTF8 encoding, 0xC3 0xB8. If the test uses anything
+ // else, then passing the test doesn't imply correctness.
+ byte[] msgAsUtf8 = msg.getBytes(Charset.forName("utf8"));
+ int i = 0;
+ while(i+1 < msgAsUtf8.length &&
+ (msgAsUtf8[i] != 0xC3 || msgAsUtf8[i+1] != 0xB8))
+ i++;
+ Assert.assertTrue(i < msgAsUtf8.length);
+
+ parser.parse(new ByteArrayInputStream(msgAsUtf8));
+ String result = handler.sb.toString();
+
+ Assert.assertEquals(expected, result);
+ }
+
protected String decode(ByteSequence byteSequence) {
return ContentUtil.decode(byteSequence);
}
=====================================
core/src/test/java/org/apache/james/mime4j/parser/TestHandler.java
=====================================
@@ -22,6 +22,7 @@ package org.apache.james.mime4j.parser;
import java.io.IOException;
import java.io.InputStream;
+import org.apache.james.mime4j.Charsets;
import org.apache.james.mime4j.parser.ContentHandler;
import org.apache.james.mime4j.stream.BodyDescriptor;
import org.apache.james.mime4j.stream.Field;
@@ -94,7 +95,7 @@ class TestHandler implements ContentHandler {
sb.append("<header>\r\n");
}
public void field(Field field) {
- sb.append("<field>\r\n").append(escape(ContentUtil.decode(field.getRaw()))).append("</field>\r\n");
+ sb.append("<field>\r\n").append(escape(ContentUtil.decode(Charsets.UTF_8, field.getRaw()))).append("</field>\r\n");
}
public void endHeader() {
sb.append("</header>\r\n");
=====================================
core/src/test/java/org/apache/james/mime4j/stream/RawFieldParserTest.java
=====================================
@@ -75,6 +75,25 @@ public class RawFieldParserTest {
Assert.assertTrue(cursor.atEnd());
}
+
+ @Test
+ public void testUtf8StringParsing() throws Exception {
+ String s = "grå \"rød\"";
+ ByteSequence raw = ContentUtil.encode(s);
+ ParserCursor cursor = new ParserCursor(0, 2 + s.length());
+
+ StringBuilder strbuf1 = new StringBuilder();
+ parser.copyContent(raw, cursor, RawFieldParser.INIT_BITSET(':'), strbuf1);
+ Assert.assertFalse(cursor.atEnd());
+ Assert.assertEquals("grå", strbuf1.toString());
+
+ parser.skipWhiteSpace(raw, cursor);
+
+ StringBuilder strbuf2 = new StringBuilder();
+ parser.copyQuotedContent(raw, cursor, strbuf2);
+ Assert.assertEquals("rød", strbuf2.toString());
+ }
+
@Test
public void testTokenParsingWithQuotedPairs() throws Exception {
String s = "raw: \"\\\"some\\stuff\\\\\"";
@@ -228,6 +247,17 @@ public class RawFieldParserTest {
}
}
+ @Test
+ public void testLongString() throws Exception {
+ String body = "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
+ String s = "raw: " + body;
+ ByteSequence raw = ContentUtil.encode(s);
+
+ RawField rawField = parser.parseField(raw);
+ Assert.assertEquals("raw", rawField.getName());
+ Assert.assertEquals(body, rawField.getBody());
+ }
+
@Test
public void testParsingInvalidSyntax2() throws Exception {
String s = "raw \t \t";
=====================================
core/src/test/java/org/apache/james/mime4j/stream/RawFieldTest.java
=====================================
@@ -38,6 +38,9 @@ public class RawFieldTest {
Assert.assertEquals("raw", field.getName());
Assert.assertEquals("stuff; more stuff", field.getBody());
Assert.assertEquals(s, field.toString());
+ raw = ContentUtil.encode("To: ø@ø.example");
+ field = new RawField(raw, 3, "To", null);
+ Assert.assertEquals("ø@ø.example", field.getBody());
}
@Test
=====================================
dom/pom.xml
=====================================
@@ -23,7 +23,7 @@
<parent>
<artifactId>apache-mime4j-project</artifactId>
<groupId>org.apache.james</groupId>
- <version>0.8.12</version>
+ <version>0.8.13</version>
<relativePath>../pom.xml</relativePath>
</parent>
=====================================
dom/src/main/java/org/apache/james/mime4j/dom/Disposable.java
=====================================
@@ -20,7 +20,7 @@
package org.apache.james.mime4j.dom;
/**
- * A <tt>Disposable</tt> is an object that should be disposed of explicitly
+ * A <code>Disposable</code> is an object that should be disposed of explicitly
* when it is no longer needed.
*
* The dispose method is invoked to release resources that the object is
=====================================
dom/src/main/java/org/apache/james/mime4j/field/DefaultFieldParser.java
=====================================
@@ -83,7 +83,8 @@ public class DefaultFieldParser extends DelegatingFieldParser {
* Parses the given string and returns an instance of the {@link ParsedField} class.
* The type of the class returned depends on the field name:
* </p>
- * <table summary="Field names and corresponding classes">
+ * <table>
+ * <caption>Field names and corresponding classes</caption>
* <tr><th>Class returned</th><th>Field names</th></tr>
* <tr><td>{@link ContentTypeField}</td><td>Content-Type</td></tr>
* <tr><td>{@link ContentLengthField}</td><td>Content-Length</td></tr>
=====================================
dom/src/main/java/org/apache/james/mime4j/field/LenientFieldParser.java
=====================================
@@ -103,7 +103,8 @@ public class LenientFieldParser extends DelegatingFieldParser {
* Parses the given string and returns an instance of the {@link ParsedField} class.
* The type of the class returned depends on the field name:
* </p>
- * <table summary="Field names and corresponding classes">
+ * <table>
+ * <caption>Field names and corresponding classes</caption>
* <tr><th>Class returned</th><th>Field names</th></tr>
* <tr><td>{@link ContentTypeField}</td><td>Content-Type</td></tr>
* <tr><td>{@link ContentLengthField}</td><td>Content-Length</td></tr>
=====================================
dom/src/main/java/org/apache/james/mime4j/field/address/LenientAddressParser.java
=====================================
@@ -217,7 +217,7 @@ public class LenientAddressParser implements AddressParser {
public Mailbox parseMailbox(final CharSequence text) {
ByteSequence raw = ContentUtil.encode(text);
- ParserCursor cursor = new ParserCursor(0, text.length());
+ ParserCursor cursor = new ParserCursor(0, raw.length());
return parseMailbox(raw, cursor, null);
}
=====================================
dom/src/main/jjtree/org/apache/james/mime4j/field/address/AddressListParser.jjt
=====================================
@@ -243,7 +243,9 @@ TOKEN :
{
< #ALPHA: ["a" - "z", "A" - "Z"] >
| < #DIGIT: ["0" - "9"] >
+| < UTF8NONASCII: ["\u0080" - "\uFFFF"] >
| < #ATEXT: ( <ALPHA> | <DIGIT>
+ | <UTF8NONASCII>
| "!" | "#" | "$" | "%"
| "&" | "'" | "*" | "+"
| "-" | "/" | "=" | "?"
=====================================
dom/src/test/java/org/apache/james/mime4j/field/LenientContentDispositionFieldTest.java
=====================================
@@ -19,6 +19,8 @@
package org.apache.james.mime4j.field;
+import static org.junit.Assert.assertEquals;
+
import java.util.Date;
import org.apache.james.mime4j.MimeException;
@@ -231,10 +233,65 @@ public class LenientContentDispositionFieldTest {
public void testBadEncodingFilename() throws MimeException {
ContentDispositionField f = parse("Content-Disposition: attachment; \n" +
" filename*=utf-8''4%P001!.DOC;\n" +
- " filename=\"4%P001!.DOC\"");
+ " filename=\"4%P002!.DOC\"");
- Assert.assertEquals(f.getDispositionType(), "attachment");
- Assert.assertEquals(f.getFilename(), "4%P001!.DOC4%P001!.DOC");
+ Assert.assertEquals("attachment", f.getDispositionType());
+ Assert.assertEquals("4%P002!.DOC", f.getFilename());
}
+ @Test
+ public void testDuplicateFields() throws MimeException {
+ //test that the first is taken and that concatenation is not applied
+ ContentDispositionField f = parse("Content-Disposition: attachment; \n" +
+ "filename=\"foo\";\n" +
+ "filename=\"bar2.rtf\";\n" +
+ "filename=\"bar3.rtf\"");
+ assertEquals("foo", f.getFilename());
+ }
+
+ @Test
+ public void testDuplicateFieldsPreferExtended() throws MimeException {
+ //test that extended field names are preferred to standard field names
+ ContentDispositionField f = parse("Content-Disposition: attachment; \n" +
+ "filename*=\"foo\";\"");
+ assertEquals("foo", f.getFilename());
+
+ f = parse("Content-Disposition: attachment;\n" +
+ "filename*=\"UTF-8''%D0%BC%2E%78%6C%73%78\";\n" +
+ "filename=\"bar2.rtf\";");
+ assertEquals("м.xlsx", f.getFilename());
+
+ f = parse("Content-Disposition: attachment; \n" +
+ "filename*=\"foo\";\n" +
+ "filename=\"bar2.rtf\";\n" +
+ "filename=\"bar3.rtf\"");
+ assertEquals("foo", f.getFilename());
+
+ f = parse("Content-Disposition: attachment; \n" +
+ "filename=\"bar2.rtf\";\n" +
+ "filename*=\"foo\";\n" +
+ "filename=\"bar3.rtf\"");
+ assertEquals("foo", f.getFilename());
+ }
+
+ @Test
+ public void testStandardAndContinuationFields() throws MimeException {
+ ContentDispositionField f = parse("Content-Disposition: attachment; \n" +
+ "filename*0=\"foo\";\n" +
+ "filename=\"bar2.rtf\";\n" +
+ "filename*1=\"bar3.rtf\"");
+ assertEquals("foobar3.rtf", f.getFilename());
+
+ f = parse("Content-Disposition: attachment; \n" +
+ "filename=\"bar1.rtf\";\n" +
+ "filename*0=\"foo\";\n" +
+ "filename*1=\"bar3.rtf\"");
+ assertEquals("foobar3.rtf", f.getFilename());
+
+ f = parse("Content-Disposition: attachment; \n" +
+ "filename*0=\"foo\";\n" +
+ "filename*1=\"bar2.rtf\";\n" +
+ "filename=\"bar3.rtf\"");
+ assertEquals("foobar2.rtf", f.getFilename());
+ }
}
=====================================
dom/src/test/java/org/apache/james/mime4j/field/address/DefaultAddressBuilderTest.java
=====================================
@@ -67,6 +67,10 @@ public class DefaultAddressBuilderTest {
Assert.assertEquals("Hans M\374ller", mailbox5.getName());
Assert.assertEquals("hans.mueller at acme.org", mailbox5.getAddress());
+ // UTF8 should be allowed in atoms too now
+ Mailbox mailbox6 = parser.parseMailbox(
+ "<dr.müller at dr-müller-lüdenscheid.de>");
+ Assert.assertEquals("dr.müller at dr-müller-lüdenscheid.de", mailbox6.getAddress());
}
@Test
=====================================
dom/src/test/java/org/apache/james/mime4j/field/address/LenientAddressBuilderTest.java
=====================================
@@ -231,6 +231,11 @@ public class LenientAddressBuilderTest {
"\"Hans M\374ller\" <hans.mueller at acme.org>");
Assert.assertEquals("Hans M\374ller", mailbox5.getName());
Assert.assertEquals("hans.mueller at acme.org", mailbox5.getAddress());
+
+ // UTF8 should be allowed in atoms too now
+ Mailbox mailbox6 = parser.parseMailbox(
+ "<dr.müller at dr-müller-lüdenscheid.de>");
+ Assert.assertEquals("dr.müller at dr-müller-lüdenscheid.de", mailbox6.getAddress());
}
@Test
=====================================
examples/pom.xml
=====================================
@@ -23,7 +23,7 @@
<parent>
<artifactId>apache-mime4j-project</artifactId>
<groupId>org.apache.james</groupId>
- <version>0.8.12</version>
+ <version>0.8.13</version>
<relativePath>../pom.xml</relativePath>
</parent>
=====================================
james-utils/pom.xml
=====================================
@@ -23,7 +23,7 @@
<parent>
<groupId>org.apache.james</groupId>
<artifactId>apache-mime4j-project</artifactId>
- <version>0.8.12</version>
+ <version>0.8.13</version>
<relativePath>../pom.xml</relativePath>
</parent>
=====================================
mbox/pom.xml
=====================================
@@ -23,7 +23,7 @@
<parent>
<groupId>org.apache.james</groupId>
<artifactId>apache-mime4j-project</artifactId>
- <version>0.8.12</version>
+ <version>0.8.13</version>
<relativePath>../pom.xml</relativePath>
</parent>
=====================================
pom.xml
=====================================
@@ -23,13 +23,13 @@
<parent>
<groupId>org.apache</groupId>
<artifactId>apache</artifactId>
- <version>21</version>
+ <version>35</version>
<relativePath />
</parent>
<groupId>org.apache.james</groupId>
<artifactId>apache-mime4j-project</artifactId>
- <version>0.8.12</version>
+ <version>0.8.13</version>
<packaging>pom</packaging>
<name>Apache James :: Mime4j :: Project</name>
@@ -52,7 +52,7 @@
<connection>scm:git:http://git-wip-us.apache.org/repos/asf/james-mime4j.git</connection>
<developerConnection>scm:git:ssh://git@github.com/apache/james-mime4j.git</developerConnection>
<url>https://git-wip-us.apache.org/repos/asf/james-mime4j.git</url>
- <tag>apache-mime4j-project-0.8.12</tag>
+ <tag>apache-mime4j-project-0.8.13</tag>
</scm>
<issueManagement>
<url>http://issues.apache.org/jira/browse/MIME4J</url>
@@ -69,11 +69,11 @@
<james-skin.version>1.8</james-skin.version>
<target.jdk>1.8</target.jdk>
- <commons-logging.version>1.2</commons-logging.version>
- <log4j.version>2.19.0</log4j.version>
+ <commons-logging.version>1.3.5</commons-logging.version>
+ <log4j.version>2.25.0</log4j.version>
<junit.version>4.13.2</junit.version>
- <mockito.version>3.12.4</mockito.version>
- <commons-io.version>2.17.0</commons-io.version>
+ <mockito.version>4.11.0</mockito.version>
+ <commons-io.version>2.19.0</commons-io.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
@@ -128,7 +128,7 @@
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
- <version>3.21.0</version>
+ <version>3.27.3</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -187,7 +187,7 @@
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
- <version>3.1.2</version>
+ <version>3.4.2</version>
<executions>
<execution>
<id>jar</id>
@@ -203,13 +203,13 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>5.1.1</version>
+ <version>5.1.9</version>
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.8.1</version>
+ <version>3.14.0</version>
<configuration>
<optimize>true</optimize>
<source>${target.jdk}</source>
@@ -241,7 +241,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
- <version>3.3.0</version>
+ <version>3.11.2</version>
<reportSets>
<reportSet>
<id>aggregate</id>
=====================================
storage/pom.xml
=====================================
@@ -23,7 +23,7 @@
<parent>
<artifactId>apache-mime4j-project</artifactId>
<groupId>org.apache.james</groupId>
- <version>0.8.12</version>
+ <version>0.8.13</version>
<relativePath>../pom.xml</relativePath>
</parent>
View it on GitLab: https://salsa.debian.org/java-team/apache-mime4j/-/commit/b8b38b366658996aadc04cd9f783bbfa3da3e197
--
View it on GitLab: https://salsa.debian.org/java-team/apache-mime4j/-/commit/b8b38b366658996aadc04cd9f783bbfa3da3e197
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20250816/6355da90/attachment.htm>
More information about the pkg-java-commits
mailing list