GH-3560 Parse mail FROM as comma-delimited header (#3562)

Fixes https://github.com/spring-projects/spring-integration/issues/3560

According RFC 5322 `FROM` and `REPLY-TO` received mail message can be
as an array of addresses.

* Fix `MailUtils` to present those arrays as comma-delimited strings for
Spring message headers
* Fix tests to deal already with several addresses for `FROM` mime header.

In the future we may change the logic to map those mime headers to arrays
as it states according the mentioned RFC

**Cherry-pick to `5.4.x` & `5.3.x`**
This commit is contained in:
Artem Bilan
2021-05-05 16:56:01 -04:00
committed by Gary Russell
parent d5ff74b272
commit 56474a5ef5
5 changed files with 13 additions and 21 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 the original author or authors.
* Copyright 2016-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,12 +25,14 @@ import javax.mail.Message.RecipientType;
import org.springframework.integration.mail.MailHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Utilities for handling mail messages.
*
* @author Gary Russell
* @author Artem Bilan
*
* @since 4.3
*
*/
@@ -47,29 +49,21 @@ public final class MailUtils {
* @return the map.
*/
public static Map<String, Object> extractStandardHeaders(Message source) {
Map<String, Object> headers = new HashMap<String, Object>();
Map<String, Object> headers = new HashMap<>();
try {
headers.put(MailHeaders.FROM, convertToString(source.getFrom()));
headers.put(MailHeaders.FROM, StringUtils.arrayToCommaDelimitedString(source.getFrom()));
headers.put(MailHeaders.BCC, convertToStringArray(source.getRecipients(RecipientType.BCC)));
headers.put(MailHeaders.CC, convertToStringArray(source.getRecipients(RecipientType.CC)));
headers.put(MailHeaders.TO, convertToStringArray(source.getRecipients(RecipientType.TO)));
headers.put(MailHeaders.REPLY_TO, convertToString(source.getReplyTo()));
headers.put(MailHeaders.REPLY_TO, StringUtils.arrayToCommaDelimitedString(source.getReplyTo()));
headers.put(MailHeaders.SUBJECT, source.getSubject());
return headers;
}
catch (Exception e) {
throw new MessagingException("conversion of MailMessage headers failed", e);
catch (Exception ex) {
throw new MessagingException("conversion of MailMessage headers failed", ex);
}
}
private static String convertToString(Address[] addresses) {
if (addresses == null || addresses.length == 0) {
return null;
}
Assert.state(addresses.length == 1, "expected a single value but received an Array");
return addresses[0].toString();
}
private static String[] convertToStringArray(Address[] addresses) {
if (addresses != null) {
String[] addressStrings = new String[addresses.length];

View File

@@ -72,7 +72,6 @@ import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.mail.support.DefaultMailHeaderMapper;
import org.springframework.integration.test.condition.LongRunningTest;
import org.springframework.integration.test.mail.TestMailServer;
import org.springframework.integration.test.mail.TestMailServer.ImapServer;
import org.springframework.integration.test.util.TestUtils;
@@ -97,7 +96,6 @@ import com.sun.mail.imap.IMAPFolder;
@ContextConfiguration(
"classpath:org/springframework/integration/mail/config/ImapIdleChannelAdapterParserTests-context.xml")
@DirtiesContext
@LongRunningTest
public class ImapMailReceiverTests {
private AtomicInteger failed;
@@ -707,7 +705,7 @@ public class ImapMailReceiverTests {
@Test // see INT-1801
public void testImapLifecycleForRaceCondition() throws Exception {
for (int i = 0; i < 1000; i++) {
for (int i = 0; i < 100; i++) {
final ImapMailReceiver receiver = new ImapMailReceiver("imap://foo");
Store store = mock(Store.class);
Folder folder = mock(Folder.class);

View File

@@ -67,7 +67,7 @@ public class Pop3Tests {
assertThat(headers.get(MailHeaders.TO, String[].class)[0]).isEqualTo("Foo <foo@bar>");
assertThat(Arrays.toString(headers.get(MailHeaders.CC, String[].class))).isEqualTo("[a@b, c@d]");
assertThat(Arrays.toString(headers.get(MailHeaders.BCC, String[].class))).isEqualTo("[e@f, g@h]");
assertThat(headers.get(MailHeaders.FROM)).isEqualTo("Bar <bar@baz>");
assertThat(headers.get(MailHeaders.FROM)).isEqualTo("Bar <bar@baz>,Bar2 <bar2@baz>");
assertThat(headers.get(MailHeaders.SUBJECT)).isEqualTo("Test Email");
assertThat(message.getPayload()).isEqualTo("foo\r\n\r\n");
}

View File

@@ -141,7 +141,7 @@ public class MailTests {
assertThat(message).isNotNull();
MessageHeaders headers = message.getHeaders();
assertThat(headers.get(MailHeaders.TO, String[].class)).containsExactly("Foo <foo@bar>");
assertThat(headers.get(MailHeaders.FROM)).isEqualTo("Bar <bar@baz>");
assertThat(headers.get(MailHeaders.FROM)).isEqualTo("Bar <bar@baz>,Bar2 <bar2@baz>");
assertThat(headers.get(MailHeaders.SUBJECT)).isEqualTo("Test Email");
assertThat(message.getPayload()).isEqualTo("foo\r\n\r\n");
assertThat(message.getHeaders().containsKey(IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE)).isTrue();

View File

@@ -489,7 +489,7 @@ public final class TestMailServer {
"To: Foo <foo@bar>\r\n"
+ "cc: a@b, c@d\r\n"
+ "bcc: e@f, g@h\r\n"
+ "From: Bar <bar@baz>\r\n"
+ "From: Bar <bar@baz>, Bar2 <bar2@baz>\r\n"
+ "Subject: Test Email\r\n"
+ "\r\n" + BODY;