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 e377ed69f6754ebb4ed298e43486736047e40de1
Author: Benoit TELLIER <[email protected]>
AuthorDate: Wed Nov 4 15:10:48 2026 +0100

    [FIX] QuotedPrintableOutputStream: encode whitespace that ends the content
    
    completeEncoding() wrote a pending space or TAB unencoded, but the end of
    the content ends an encoded line, so the decoder drops that trailing
    whitespace (RFC 2045 6.7 rule (3)) and a body ending with a blank lost it
    on the next parse. The last space or TAB is now written as =20 / =09.
    
    Fixes #110
---
 .../mime4j/codec/QuotedPrintableOutputStream.java     |  5 +++++
 .../mime4j/codec/QuotedPrintableTextEncodeTest.java   | 19 ++++++++++++++++---
 .../mime4j/message/DefaultMessageWriterTest.java      | 13 +++++++++++++
 3 files changed, 34 insertions(+), 3 deletions(-)

diff --git 
a/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableOutputStream.java
 
b/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableOutputStream.java
index 5c572a80..e07f3e3e 100644
--- 
a/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableOutputStream.java
+++ 
b/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableOutputStream.java
@@ -82,6 +82,11 @@ public class QuotedPrintableOutputStream extends 
FilterOutputStream {
     }
 
     private void completeEncoding() throws IOException {
+        if (!pendingCR && (pendingSpace || pendingTab)) {
+            // whitespace that ends the content ends an encoded line: it must 
be encoded
+            escape(pendingSpace ? SP : TB);
+            clearPending();
+        }
         writePending();
         flushOutput();
     }
diff --git 
a/core/src/test/java/org/apache/james/mime4j/codec/QuotedPrintableTextEncodeTest.java
 
b/core/src/test/java/org/apache/james/mime4j/codec/QuotedPrintableTextEncodeTest.java
index d48cbe27..5c66859e 100644
--- 
a/core/src/test/java/org/apache/james/mime4j/codec/QuotedPrintableTextEncodeTest.java
+++ 
b/core/src/test/java/org/apache/james/mime4j/codec/QuotedPrintableTextEncodeTest.java
@@ -144,17 +144,30 @@ public class QuotedPrintableTextEncodeTest {
 
     @Test
     public void testDontEscapeSpaceBeforeLineEnd() throws Exception {
-        check("      ", "      ");
+        check("      ", "     =20");
     }
 
     @Test
     public void testDontEscapeTabsBeforeLineEnd() throws Exception {
-        check("\t\t\t\t", "\t\t\t\t");
+        check("\t\t\t\t", "\t\t\t=09");
     }
 
     @Test
     public void testDontWhiteSpaceBeforeLineEnd() throws Exception {
-        check("  \t\t  \t", "  \t\t  \t");
+        check("  \t\t  \t", "  \t\t  =09");
+    }
+
+    @Test
+    public void testEscapeWhitespaceAtEndOfContent() throws Exception {
+        check("a\t", "a=09");
+        check("a ", "a=20");
+        check("a \r", "a =0D");
+    }
+
+    @Test
+    public void testWhitespaceAtEndOfContentRoundTrips() throws Exception {
+        checkRoundtrip("a\t");
+        checkRoundtrip(" ");
     }
 
     private void checkRoundtrip(String content) throws Exception {
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 6fd79e2d..369bf6fa 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
@@ -334,4 +334,17 @@ public class DefaultMessageWriterTest {
         assertThat(ContentUtil.buffer(((SingleBody) 
m2.getBody()).getInputStream()))
             
.isEqualTo("a\r\r\nb\rc\n\r\n".getBytes(StandardCharsets.US_ASCII));
     }
+
+    @Test
+    public void quotedPrintableBodyEndingWithWhitespaceSurvivesWriteAndParse() 
throws Exception {
+        byte[] raw = "Content-Transfer-Encoding: 
quoted-printable\r\n\r\na\t=\r\n".getBytes(StandardCharsets.US_ASCII);
+        DefaultMessageBuilder builder = new DefaultMessageBuilder();
+        Message m1 = builder.parseMessage(new ByteArrayInputStream(raw));
+        ByteArrayOutputStream w1 = new ByteArrayOutputStream();
+        new DefaultMessageWriter().writeMessage(m1, w1);
+        Message m2 = builder.parseMessage(new 
ByteArrayInputStream(w1.toByteArray()));
+
+        assertThat(ContentUtil.buffer(((SingleBody) 
m2.getBody()).getInputStream()))
+            .isEqualTo("a\t".getBytes(StandardCharsets.US_ASCII));
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to