This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch 3.9.x in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 5ed5a146d40f6f38f0f0b3c3dd72368901bad416 Author: Benoit TELLIER <[email protected]> AuthorDate: Wed Nov 12 17:48:15 2025 +0100 JAMES-4086 FoldLongLines: folded content exceeding line length (#2851) --- .../james/transport/mailets/FoldLongLines.java | 6 +- .../apache/james/transport/mailets/MimeUtil.java | 80 ---------------------- .../james/transport/mailets/FoldLongLinesTest.java | 44 +++++++++++- 3 files changed, 46 insertions(+), 84 deletions(-) diff --git a/mailet/standard/src/main/java/org/apache/james/transport/mailets/FoldLongLines.java b/mailet/standard/src/main/java/org/apache/james/transport/mailets/FoldLongLines.java index 01608c9454..3ebf19979e 100644 --- a/mailet/standard/src/main/java/org/apache/james/transport/mailets/FoldLongLines.java +++ b/mailet/standard/src/main/java/org/apache/james/transport/mailets/FoldLongLines.java @@ -26,6 +26,7 @@ import jakarta.mail.Header; import jakarta.mail.MessagingException; import org.apache.commons.lang3.stream.Streams; +import org.apache.james.mime4j.util.MimeUtil; import org.apache.mailet.Mail; import org.apache.mailet.base.GenericMailet; @@ -94,8 +95,9 @@ public class FoldLongLines extends GenericMailet { private String fold(Header header) { int headerNameLength = header.getName().length() + HEADER_SEPARATOR.length(); - // TODO After new release of mime4j with commit https://github.com/apache/james-mime4j/commit/66a09219457854c7a26e5b7c0e4c9dd59b4b0c32, update to use MimeUtil of mime4j and remove MimeUtil class file - return MimeUtil.fold(header.getValue(), headerNameLength, maxCharacters); + String unfold = MimeUtil.unfold(header.getValue()); + String res = MimeUtil.fold(unfold, headerNameLength, maxCharacters); + return res; } private boolean exceedLineLimit(Header header) { diff --git a/mailet/standard/src/main/java/org/apache/james/transport/mailets/MimeUtil.java b/mailet/standard/src/main/java/org/apache/james/transport/mailets/MimeUtil.java deleted file mode 100644 index ef6213a57d..0000000000 --- a/mailet/standard/src/main/java/org/apache/james/transport/mailets/MimeUtil.java +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************** - * 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.transport.mailets; - -// TODO After new release of mime4j with commit https://github.com/apache/james-mime4j/commit/66a09219457854c7a26e5b7c0e4c9dd59b4b0c32, remove this class -public class MimeUtil { - /** - * Splits the specified string into a multiple-line representation with - * lines no longer than the maximum number of characters (because the line might contain - * encoded words; see <a href='http://www.faqs.org/rfcs/rfc2047.html'>RFC - * 2047</a> section 2). If the string contains non-whitespace sequences - * longer than the maximum number of characters a line break is inserted at the whitespace - * character following the sequence resulting in a line longer than the maximum number of - * characters. - * - * @param s - * string to split. - * @param usedCharacters - * number of characters already used up. Usually the number of - * characters for header field name plus colon and one space. - * @param maxCharacters - * maximum number of characters - * @return a multiple-line representation of the given string. - */ - public static String fold(String s, int usedCharacters, int maxCharacters) { - final int length = s.length(); - if (usedCharacters + length <= maxCharacters) { - return s; - } - - StringBuilder sb = new StringBuilder(); - - int lastLineBreak = -usedCharacters; - int wspIdx = indexOfWsp(s, 0); - while (true) { - if (wspIdx == length) { - sb.append(s.substring(Math.max(0, lastLineBreak))); - return sb.toString(); - } - - int nextWspIdx = indexOfWsp(s, wspIdx + 1); - - if (nextWspIdx - lastLineBreak > maxCharacters) { - sb.append(s, Math.max(0, lastLineBreak), wspIdx); - sb.append("\r\n"); - lastLineBreak = wspIdx; - } - - wspIdx = nextWspIdx; - } - } - - private static int indexOfWsp(String s, int fromIndex) { - final int len = s.length(); - for (int index = fromIndex; index < len; index++) { - char c = s.charAt(index); - if (c == ' ' || c == '\t') { - return index; - } - } - return len; - } -} diff --git a/mailet/standard/src/test/java/org/apache/james/transport/mailets/FoldLongLinesTest.java b/mailet/standard/src/test/java/org/apache/james/transport/mailets/FoldLongLinesTest.java index 37a15f59b6..77be5ccced 100644 --- a/mailet/standard/src/test/java/org/apache/james/transport/mailets/FoldLongLinesTest.java +++ b/mailet/standard/src/test/java/org/apache/james/transport/mailets/FoldLongLinesTest.java @@ -90,8 +90,7 @@ public class FoldLongLinesTest { List<Header> headers = Streams.of(mail.getMessage().getAllHeaders()).filter(header -> header.getName().equals(HEADER_NAME)).toList(); assertThat(headers).hasSize(1); - assertThat(headers.getFirst().getValue()).isEqualTo("<a1@gmailcom>\n" + - "<a2@gmailcom>\r\n" + + assertThat(headers.getFirst().getValue()).isEqualTo("<a1@gmailcom><a2@gmailcom>\r\n" + " <a3@gmailcom> <a4@gmailcom>"); } @@ -159,6 +158,47 @@ public class FoldLongLinesTest { }); } + @Test + void shouldBeIdempotent() throws Exception { + FakeMailetConfig mailetConfig = FakeMailetConfig.builder() + .mailetName("Test") + .mailetContext(mailetContext) + .setProperty(MAX_CHARACTERS_PARAMETER_NAME, "40") + .build(); + foldMailet.init(mailetConfig); + + Mail mail = FakeMail.builder() + .name("mail").mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .addHeader(HEADER_NAME, "a".repeat(40) + "\r\n a") + .build()) + .build(); + foldMailet.service(mail); + + assertThat(mail.getMessage().getHeader(HEADER_NAME)[0]) + .isEqualTo("a".repeat(40) + "\r\n a"); + } + + @Test + void shouldTolerateLongerFoldedLines() throws Exception { + FakeMailetConfig mailetConfig = FakeMailetConfig.builder() + .mailetName("Test") + .mailetContext(mailetContext) + .setProperty(MAX_CHARACTERS_PARAMETER_NAME, "40") + .build(); + foldMailet.init(mailetConfig); + + Mail mail = FakeMail.builder() + .name("mail").mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .addHeader(HEADER_NAME, "a a".repeat(20) + "\r\n a") + .build()) + .build(); + foldMailet.service(mail); + + assertThat(mail.getMessage().getHeader(HEADER_NAME)[0]) + .isEqualTo("a aa aa aa aa aa aa aa aa aa\r\n" + + " aa aa aa aa aa aa aa aa aa aa a a"); + } + @Test void serviceShouldNotChangeTheRelativePositionOfTheHeaderThatHasTheSameNameAsHeadersWithLongLine() throws Exception { FakeMailetConfig mailetConfig = FakeMailetConfig.builder() --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
