Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,58 @@ public void testTextMailWithDataSourceAttachment() throws Exception {
assertThat(attachmentFileName).isEqualTo(fileName);
}

@Test
@Deployment(resources = "org/flowable/engine/test/bpmn/mail/EmailServiceTaskTest.testTextMailWithDataSourceAttachment.bpmn20.xml")
public void testTextMailWithDataSourceAttachmentLongNonAsciiFileName() throws Exception {
String fileName = "Précis of Evidence VA23-5-1034 Lorge Chocolatier PN 1136882 Retail Henry Street Kenmare Co.pdf";
String fileContent = "This is the file content";
HashMap<String, Object> vars = new HashMap<>();
vars.put("attachmentsBean", new AttachmentsBean());
vars.put("fileContent", fileContent);
vars.put("fileName", fileName);
runtimeService.startProcessInstanceByKey("textMailWithDataSourceAttachment", vars);

List<WiserMessage> messages = wiser.getMessages();
assertThat(messages).hasSize(1);
WiserMessage message = messages.get(0);
MimeMultipart mm = (MimeMultipart) message.getMimeMessage().getContent();
assertThat(mm.getCount()).isEqualTo(2);
String attachmentFileName = mm.getBodyPart(1).getDataHandler().getName();
assertThat(attachmentFileName).isEqualTo(fileName);
}

@Test
@Deployment(resources = "org/flowable/engine/test/bpmn/mail/EmailServiceTaskTest.testTextMailWithDataSourceAttachment.bpmn20.xml")
public void testTextMailWithDataSourceAttachmentUsesConfiguredCharset() throws Exception {
Charset originalCharset = processEngineConfiguration.getMailServerDefaultCharset();

try {
processEngineConfiguration.setMailServerDefaultCharset(StandardCharsets.ISO_8859_1);
reinitilizeMailClients();

String fileName = "café.pdf";
String fileContent = "This is the file content";
HashMap<String, Object> vars = new HashMap<>();
vars.put("attachmentsBean", new AttachmentsBean());
vars.put("fileContent", fileContent);
vars.put("fileName", fileName);
runtimeService.startProcessInstanceByKey("textMailWithDataSourceAttachment", vars);

List<WiserMessage> messages = wiser.getMessages();
assertThat(messages).hasSize(1);
WiserMessage message = messages.get(0);
MimeMultipart mm = (MimeMultipart) message.getMimeMessage().getContent();
String contentDisposition = mm.getBodyPart(1).getHeader("Content-Disposition")[0];

// 'é' is a single byte (0xE9) in ISO-8859-1 but two bytes (0xC3 0xA9) in UTF-8: this string can only
// appear if the configured ISO-8859-1 charset was actually used to encode the attachment file name.
assertThat(contentDisposition).contains("ISO-8859-1''caf%E9.pdf");
assertThat(mm.getBodyPart(1).getDataHandler().getName()).isEqualTo(fileName);
} finally {
processEngineConfiguration.setMailServerDefaultCharset(originalCharset);
}
}

@Test
@Deployment
public void testTextMailWithNotExistingFileAttachment() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@
import jakarta.mail.Session;
import jakarta.mail.Transport;
import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.ContentDisposition;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeBodyPart;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;
import jakarta.mail.internet.MimeUtility;
import jakarta.mail.internet.ParameterList;

import org.apache.commons.lang3.StringUtils;
import org.flowable.common.engine.api.FlowableException;
Expand Down Expand Up @@ -323,12 +325,7 @@ protected MimeMultipart createMultiPartContent(String text, String html, String
if (attachmentsExists) {
for (DataSource attachment : attachments) {
BodyPart bodyPart = new MimeBodyPart();
bodyPart.setDisposition(Part.ATTACHMENT);
try {
bodyPart.setFileName(MimeUtility.encodeText(attachment.getName(), charset, null));
} catch (UnsupportedEncodingException e) {
throw new FlowableMailException("Could not encode attachment file name", e);
}
setAttachmentFileName(bodyPart, attachment.getName(), charset);
bodyPart.setDataHandler(new DataHandler(attachment));
rootContainer.addBodyPart(bodyPart);
}
Expand All @@ -337,6 +334,21 @@ protected MimeMultipart createMultiPartContent(String text, String html, String
return rootContainer;
}

protected void setAttachmentFileName(BodyPart bodyPart, String fileName, String charset) throws MessagingException {
// Build the Content-Disposition header ourselves instead of using BodyPart.setFileName(): that method
// ignores the charset argument entirely and RFC 2231 encodes with MimeUtility.getDefaultMIMECharset()
// (the JVM's file.encoding, unless mail.mime.charset is set), so it silently diverges from the charset
// used for the rest of the message. ParameterList.set(name, value, charset) does the same RFC 2231
// encoding and continuation folding, but with the charset we actually pass it, and leaves ASCII names
// untouched. When no charset is configured, fall back to the same default MimeBodyPart.setText() falls
// back to for the message body, so the file name and body stay consistent with each other.
String mimeCharset = charset != null ? charset : MimeUtility.mimeCharset(MimeUtility.getDefaultJavaCharset());
ParameterList parameters = new ParameterList();
parameters.set("filename", fileName, mimeCharset);
ContentDisposition disposition = new ContentDisposition(Part.ATTACHMENT, parameters);
bodyPart.setHeader("Content-Disposition", disposition.toString());
}

protected Session createSession() {
if (serverConfiguration instanceof MailJndiServerConfiguration jndiServerConfiguration) {
return createSession(jndiServerConfiguration);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/* Licensed 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.flowable.mail.common.impl.jakarta.mail;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;

import jakarta.mail.BodyPart;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMultipart;
import jakarta.mail.util.ByteArrayDataSource;

import org.junit.jupiter.api.Test;

class JakartaMailFlowableMailClientTest {

protected final JakartaMailFlowableMailClient client = new JakartaMailFlowableMailClient(null, null);

@Test
void attachmentFileNameThatIsLongAndNonAsciiIsNotCorrupted() throws MessagingException {
String fileName = "Précis of Evidence VA23-5-1034 Lorge Chocolatier PN 1136882 Retail Henry Street Kenmare Co.pdf";

ByteArrayDataSource attachment = new ByteArrayDataSource("attachment content".getBytes(), "application/pdf");
attachment.setName(fileName);

MimeMultipart multipart = client.createMultiPartContent(null, null, "UTF-8", List.of(attachment));

BodyPart attachmentPart = multipart.getBodyPart(0);
assertThat(attachmentPart.getFileName()).isEqualTo(fileName);
}

@Test
void attachmentFileNameThatIsShortAndAsciiIsUnaffected() throws MessagingException {
String fileName = "invoice.pdf";
ByteArrayDataSource attachment = new ByteArrayDataSource("attachment content".getBytes(), "application/pdf");
attachment.setName(fileName);

MimeMultipart multipart = client.createMultiPartContent(null, null, "UTF-8", List.of(attachment));

BodyPart attachmentPart = multipart.getBodyPart(0);
assertThat(attachmentPart.getFileName()).isEqualTo(fileName);
}

@Test
void attachmentFileNameIsEncodedWithTheGivenCharsetRatherThanThePlatformDefault() throws MessagingException {
String fileName = "café.pdf";
ByteArrayDataSource attachment = new ByteArrayDataSource("attachment content".getBytes(), "application/pdf");
attachment.setName(fileName);

MimeMultipart multipart = client.createMultiPartContent(null, null, "ISO-8859-1", List.of(attachment));

BodyPart attachmentPart = multipart.getBodyPart(0);
String contentDisposition = attachmentPart.getHeader("Content-Disposition")[0];

// 'é' is a single byte (0xE9) in ISO-8859-1 but two bytes (0xC3 0xA9) in UTF-8: this string can only
// appear if the ISO-8859-1 charset passed to createMultiPartContent() was actually used to encode it,
// rather than UTF-8 or the JVM's platform default charset.
assertThat(contentDisposition).contains("ISO-8859-1''caf%E9.pdf");
assertThat(attachmentPart.getFileName()).isEqualTo(fileName);
}

}