JAMES-2529 JMAP filtering should not fail when one invalid address MimeMessage helpers for getting mailAdress is doing some aggressive validation.
The fallback method was not attempting to split the underlying header, leading to an error downstream when trying to parse several addresses as if it was a signle one. Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/155ddd70 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/155ddd70 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/155ddd70 Branch: refs/heads/master Commit: 155ddd7028be9cfa64902fe2f801846b2e178bc8 Parents: f4e4204 Author: Benoit Tellier <[email protected]> Authored: Tue Oct 23 12:07:06 2018 +0700 Committer: Benoit Tellier <[email protected]> Committed: Wed Oct 31 08:48:30 2018 +0700 ---------------------------------------------------------------------- .../jmap/mailet/filter/HeaderExtractor.java | 27 ++++++---- .../jmap/mailet/filter/JMAPFilteringTest.java | 55 ++++++++++++++++++++ 2 files changed, 71 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/155ddd70/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java index 65d1629..b2bea2c 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java @@ -25,8 +25,8 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Stream; -import javax.mail.Address; import javax.mail.Message; +import javax.mail.internet.InternetAddress; import org.apache.james.javax.AddressHelper; import org.apache.james.jmap.api.filtering.Rule; @@ -36,6 +36,7 @@ import org.apache.mailet.Mail; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.github.fge.lambdas.Throwing; import com.github.fge.lambdas.functions.ThrowingFunction; import com.google.common.collect.ImmutableMap; @@ -47,7 +48,7 @@ public interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> HeaderExtractor CC_EXTRACTOR = recipientExtractor(Message.RecipientType.CC); HeaderExtractor TO_EXTRACTOR = recipientExtractor(Message.RecipientType.TO); HeaderExtractor RECIPIENT_EXTRACTOR = and(TO_EXTRACTOR, CC_EXTRACTOR); - HeaderExtractor FROM_EXTRACTOR = addressExtractor(mail -> mail.getMessage().getFrom(), FROM); + HeaderExtractor FROM_EXTRACTOR = addressExtractor(mail -> mail.getMessage().getHeader(FROM), FROM); Map<Rule.Condition.Field, HeaderExtractor> HEADER_EXTRACTOR_REGISTRY = ImmutableMap.<Rule.Condition.Field, HeaderExtractor>builder() .put(Rule.Condition.Field.SUBJECT, SUBJECT_EXTRACTOR) @@ -57,21 +58,23 @@ public interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> .put(Rule.Condition.Field.TO, TO_EXTRACTOR) .build(); + boolean STRICT_PARSING = true; + static HeaderExtractor and(HeaderExtractor headerExtractor1, HeaderExtractor headerExtractor2) { return (Mail mail) -> StreamUtils.flatten(headerExtractor1.apply(mail), headerExtractor2.apply(mail)); } static HeaderExtractor recipientExtractor(Message.RecipientType type) { - ThrowingFunction<Mail, Address[]> addressGetter = mail -> mail.getMessage().getRecipients(type); - String fallbackHeaderName = type.toString(); + String headerName = type.toString(); + ThrowingFunction<Mail, String[]> addressGetter = mail -> mail.getMessage().getHeader(headerName); - return addressExtractor(addressGetter, fallbackHeaderName); + return addressExtractor(addressGetter, headerName); } - static HeaderExtractor addressExtractor(ThrowingFunction<Mail, Address[]> addressGetter, String fallbackHeaderName) { + static HeaderExtractor addressExtractor(ThrowingFunction<Mail, String[]> addressGetter, String fallbackHeaderName) { return mail -> { try { - return toContent(addressGetter.apply(mail)); + return toAddressContents(addressGetter.apply(mail)); } catch (Exception e) { LOGGER.info("Failed parsing header. Falling back to unparsed header value matching", e); return Stream.of(mail.getMessage().getHeader(fallbackHeaderName)) @@ -80,10 +83,12 @@ public interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> }; } - static Stream<String> toContent(Address[] addresses) { - return Optional.ofNullable(addresses) - .map(AddressHelper::asStringStream) - .orElse(Stream.empty()); + static Stream<String> toAddressContents(String[] headers) { + return Stream.concat( + StreamUtils.ofNullable(headers), + StreamUtils.ofNullable(headers) + .map(Throwing.function(string -> InternetAddress.parseHeader(string, !STRICT_PARSING))) + .flatMap(AddressHelper::asStringStream)); } static Optional<HeaderExtractor> asHeaderExtractor(Rule.Condition.Field field) { http://git-wip-us.apache.org/repos/asf/james-project/blob/155ddd70/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java index 9ab1649..f534d3b 100644 --- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java @@ -237,6 +237,56 @@ class JMAPFilteringTest { .valueToMatch(USER_1_ADDRESS), argumentBuilder(fieldAndHeader.field) + .description("Address exact match in a failing " + fieldAndHeader.headerName + " header") + .header(fieldAndHeader.headerName, "invalid@ white.space.in.domain.tld") + .valueToMatch("invalid@ white.space.in.domain.tld"), + + argumentBuilder(fieldAndHeader.field) + .description("Address exact match in a coma quoted " + fieldAndHeader.headerName + " header") + .header(fieldAndHeader.headerName, "Toto <\"a, b\"@quoted.com>") + .valueToMatch("\"a, b\"@quoted.com"), + + argumentBuilder(fieldAndHeader.field) + .description("Username exact match in a coma quoted " + fieldAndHeader.headerName + " header") + .header(fieldAndHeader.headerName, "Toto <\"a, b\"@quoted.com>") + .valueToMatch("Toto"), + + argumentBuilder(fieldAndHeader.field) + .description("Full exact match in a coma quoted " + fieldAndHeader.headerName + " header") + .header(fieldAndHeader.headerName, "Toto <\"a, b\"@quoted.com>") + .valueToMatch("Toto <\"a, b\"@quoted.com>"), + + argumentBuilder(fieldAndHeader.field) + .description("Address exact match in a failing + coma quoted" + fieldAndHeader.headerName + " header") + .header(fieldAndHeader.headerName, "invalid@ space.org, Toto <\"a, b\"@quoted.com>") + .valueToMatch("\"a, b\"@quoted.com"), + + argumentBuilder(fieldAndHeader.field) + .description("Username exact match in a failing + coma quoted " + fieldAndHeader.headerName + " header") + .header(fieldAndHeader.headerName, "invalid@ space.org, Toto <\"a, b\"@quoted.com>") + .valueToMatch("Toto"), + + argumentBuilder(fieldAndHeader.field) + .description("Full exact match in a failing + coma quoted " + fieldAndHeader.headerName + " header") + .header(fieldAndHeader.headerName, "invalid@ space.org, Toto <\"a, b\"@quoted.com>") + .valueToMatch("Toto <\"a, b\"@quoted.com>"), + + argumentBuilder(fieldAndHeader.field) + .description("Address exact match in a failing " + fieldAndHeader.headerName + " header with multiple values") + .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS + ", invalid@ white.space.in.domain.tld") + .valueToMatch(USER_1_FULL_ADDRESS), + + argumentBuilder(fieldAndHeader.field) + .description("Address exact match in a failing " + fieldAndHeader.headerName + " header with multiple values") + .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS + ", invalid@ white.space.in.domain.tld") + .valueToMatch(USER_1_ADDRESS), + + argumentBuilder(fieldAndHeader.field) + .description("Address exact match in a failing " + fieldAndHeader.headerName + " header with multiple values") + .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS + ", invalid@ white.space.in.domain.tld") + .valueToMatch(USER_1_USERNAME), + + argumentBuilder(fieldAndHeader.field) .description("Full header exact match in a full " + fieldAndHeader.headerName + " header") .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS) .valueToMatch(USER_1_FULL_ADDRESS), @@ -317,6 +367,11 @@ class JMAPFilteringTest { .valueToMatch("ser1 <"), argumentBuilder(fieldAndHeader.field) + .description("Address exact match in a full " + fieldAndHeader.headerName + " header with multiple addresses") + .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS + ", Invalid <invalid@ white.space.in.domain.tld>") + .valueToMatch("invalid@ white.space.in.domain.tld"), + + argumentBuilder(fieldAndHeader.field) .description("Address partial match in a full " + fieldAndHeader.headerName + " header") .header(fieldAndHeader.headerName, USER_1_FULL_ADDRESS) .valueToMatch("ser1@jam"), --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
