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]

Reply via email to