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 bf7a8fb48f23542548719e324d5cd9a912d34bc3 Author: Benoit TELLIER <[email protected]> AuthorDate: Thu Nov 5 05:27:40 2026 +0100 [FIX] RawFieldParser: a quoted pair in a quoted charset stands for the escaped character The backslash was dropped for the boundary only, so charset="iso-8859\-1" gave an invalid charset name to the lenient parsers: the default DOM fell back on the JVM charset and MimeTokenStream#getReader threw, whereas the strict parser honoured iso-8859-1. A charset name cannot hold a backslash (RFC 2978), so its quoted pairs are now unescaped too. Encoded-word decoding and the other parameters do not change. --- .../apache/james/mime4j/stream/RawFieldParser.java | 19 +++++++++++-------- .../james/mime4j/stream/RawFieldParserTest.java | 9 +++++++++ .../mime4j/field/LenientContentTypeFieldTest.java | 10 ++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/apache/james/mime4j/stream/RawFieldParser.java b/core/src/main/java/org/apache/james/mime4j/stream/RawFieldParser.java index c0b87673..9e696fdf 100644 --- a/core/src/main/java/org/apache/james/mime4j/stream/RawFieldParser.java +++ b/core/src/main/java/org/apache/james/mime4j/stream/RawFieldParser.java @@ -168,7 +168,10 @@ public class RawFieldParser { } // The boundary is a byte exact delimiter: never decode it as an encoded word, // and a quoted pair always stands for the escaped character alone - String value = parseValue(buf, cursor, SEMICOLON, "boundary".equalsIgnoreCase(name)); + boolean boundary = "boundary".equalsIgnoreCase(name); + // Nor can a charset name hold a backslash (RFC 2978 mime-charset) + boolean unescape = boundary || "charset".equalsIgnoreCase(name); + String value = parseValue(buf, cursor, SEMICOLON, boundary, unescape); if (!cursor.atEnd()) { cursor.updatePos(cursor.getPos() + 1); } @@ -222,11 +225,11 @@ public class RawFieldParser { * is not delimited by any character. */ public String parseValue(final ByteSequence buf, final ParserCursor cursor, final BitSet delimiters) { - return parseValue(buf, cursor, delimiters, false); + return parseValue(buf, cursor, delimiters, false, false); } private String parseValue(final ByteSequence buf, final ParserCursor cursor, final BitSet delimiters, - final boolean boundary) { + final boolean boundary, final boolean unescape) { StringBuilder dst = new StringBuilder(); boolean whitespace = false; while (!cursor.atEnd()) { @@ -246,7 +249,7 @@ public class RawFieldParser { if (dst.length() > 0 && whitespace) { dst.append(' '); } - copyQuotedContent(buf, cursor, dst, boundary); + copyQuotedContent(buf, cursor, dst, boundary, unescape); whitespace = false; } else { if (dst.length() > 0 && whitespace) { @@ -480,7 +483,7 @@ public class RawFieldParser { */ public void copyQuotedContent(final ByteSequence buf, final ParserCursor cursor, final StringBuilder dst) { - copyQuotedContent(buf, cursor, dst, false); + copyQuotedContent(buf, cursor, dst, false, false); } private static boolean isLineBreak(final char c) { @@ -488,7 +491,7 @@ public class RawFieldParser { } private void copyQuotedContent(final ByteSequence buf, final ParserCursor cursor, - final StringBuilder dst, final boolean boundary) { + final StringBuilder dst, final boolean boundary, final boolean unescape) { if (cursor.atEnd()) { return; } @@ -515,8 +518,8 @@ public class RawFieldParser { // applies to the character after it continue; } - // A boundary cannot hold a backslash (RFC 2046 bchars): the quoted pair is unescaped - if (!boundary && current != '\"' && current != '\\') { + // A boundary or a charset cannot hold a backslash: the quoted pair is unescaped + if (!unescape && current != '\"' && current != '\\') { dstRaw.append('\\'); } dstRaw.append(currentByte); diff --git a/core/src/test/java/org/apache/james/mime4j/stream/RawFieldParserTest.java b/core/src/test/java/org/apache/james/mime4j/stream/RawFieldParserTest.java index 596f6a43..e4239046 100644 --- a/core/src/test/java/org/apache/james/mime4j/stream/RawFieldParserTest.java +++ b/core/src/test/java/org/apache/james/mime4j/stream/RawFieldParserTest.java @@ -583,4 +583,13 @@ public class RawFieldParserTest { org.junit.Assert.assertEquals("c", params.get(1).getName()); org.junit.Assert.assertEquals("d e", params.get(1).getValue()); } + + @Test + public void quotedPairInCharsetShouldBeUnescaped() { + ByteSequence buf = ContentUtil.encode("text/plain; charset=\"iso-8859\\-1\"; name=\"a\\bc\""); + RawBody body = parser.parseRawBody(buf, new ParserCursor(0, buf.length())); + org.junit.Assert.assertEquals("iso-8859-1", body.getParams().get(0).getValue()); + // other parameters keep the backslash before an ordinary character, as before + org.junit.Assert.assertEquals("a\\bc", body.getParams().get(1).getValue()); + } } diff --git a/dom/src/test/java/org/apache/james/mime4j/field/LenientContentTypeFieldTest.java b/dom/src/test/java/org/apache/james/mime4j/field/LenientContentTypeFieldTest.java index 0a62e2fb..3f4b534f 100644 --- a/dom/src/test/java/org/apache/james/mime4j/field/LenientContentTypeFieldTest.java +++ b/dom/src/test/java/org/apache/james/mime4j/field/LenientContentTypeFieldTest.java @@ -120,4 +120,14 @@ public class LenientContentTypeFieldTest { // other parameters keep the backslash before an ordinary character, as before Assert.assertEquals("a\\bc", lenient.getParameter("name")); } + + @Test + public void quotedPairInCharsetShouldBeUnescapedLikeTheStrictParser() throws Exception { + String raw = "Content-Type: text/plain; charset=\"iso-8859\\-1\""; + ContentTypeField lenient = parse(raw); + ContentTypeField strict = (ContentTypeField) DefaultFieldParser.parse(raw); + + Assert.assertEquals("iso-8859-1", strict.getCharset()); + Assert.assertEquals("iso-8859-1", lenient.getCharset()); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
