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 2eadff76e645cf1c275adcfc65cf1a2c77bf8513 Author: Rene Cordier <[email protected]> AuthorDate: Fri Sep 19 16:54:10 2025 +0700 JAMES-4148 Refactor HeaderExtractor to be able to extract headers on MessageResult and Mail --- .../james/jmap/mailet/filter/FilteringHeaders.java | 92 ++++++++++++++++++++++ .../james/jmap/mailet/filter/HeaderExtractor.java | 23 +++--- .../james/jmap/mailet/filter/MailMatcher.java | 7 +- .../james/jmap/mailet/filter/RuleMatcher.java | 13 ++- 4 files changed, 118 insertions(+), 17 deletions(-) diff --git a/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/FilteringHeaders.java b/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/FilteringHeaders.java new file mode 100644 index 0000000000..264847e74b --- /dev/null +++ b/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/FilteringHeaders.java @@ -0,0 +1,92 @@ +/**************************************************************** + * 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.jmap.mailet.filter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import jakarta.mail.MessagingException; + +import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.model.Header; +import org.apache.james.mailbox.model.Headers; +import org.apache.james.mailbox.model.MessageResult; +import org.apache.mailet.Mail; + +public interface FilteringHeaders { + class MailFilteringHeaders implements FilteringHeaders { + private final Mail mail; + + public MailFilteringHeaders(Mail mail) { + this.mail = mail; + } + + @Override + public String[] getHeader(String name) throws MessagingException { + return mail.getMessage().getHeader(name); + } + + @Override + public String getSubject() throws MessagingException { + return mail.getMessage().getSubject(); + } + } + + class MessageResultFilteringHeaders implements FilteringHeaders { + private final Headers headers; + + public MessageResultFilteringHeaders(MessageResult messageResult) throws MailboxException { + this.headers = messageResult.getHeaders(); + } + + @Override + public String[] getHeader(String name) throws MailboxException { + return getMatchingHeaders(name); + } + + @Override + public String getSubject() throws MailboxException { + return Arrays.stream(getMatchingHeaders("Subject")) + .findFirst() + .orElse(null); + } + + private String[] getMatchingHeaders(String name) throws MailboxException { + final List<String> results = new ArrayList<>(); + if (name != null) { + Iterator<Header> iterator = headers.headers(); + while (iterator.hasNext()) { + Header header = iterator.next(); + final String headerName = header.getName(); + if (name.equalsIgnoreCase(headerName)) { + results.add(header.getValue()); + } + } + } + return results.toArray(new String[0]); + } + } + + String[] getHeader(String name) throws Exception; + + String getSubject() throws Exception; +} diff --git a/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java b/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java index 72430ad960..c5a57ffa11 100644 --- a/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java +++ b/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/HeaderExtractor.java @@ -32,7 +32,6 @@ import org.apache.james.javax.AddressHelper; import org.apache.james.jmap.api.filtering.Rule; import org.apache.james.mime4j.util.MimeUtil; import org.apache.james.util.StreamUtils; -import org.apache.mailet.Mail; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,15 +40,15 @@ import com.github.fge.lambdas.functions.ThrowingFunction; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; -public interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> { +public interface HeaderExtractor extends ThrowingFunction<FilteringHeaders, Stream<String>> { Logger LOGGER = LoggerFactory.getLogger(HeaderExtractor.class); - HeaderExtractor SUBJECT_EXTRACTOR = mail -> - StreamUtils.ofNullables(mail.getMessage().getSubject()); + HeaderExtractor SUBJECT_EXTRACTOR = filteringHeaders -> + StreamUtils.ofNullables(filteringHeaders.getSubject()); 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().getHeader(FROM), FROM); + HeaderExtractor FROM_EXTRACTOR = addressExtractor(filteringHeaders -> filteringHeaders.getHeader(FROM), FROM); Map<Rule.Condition.Field, HeaderExtractor> HEADER_EXTRACTOR_REGISTRY = ImmutableMap.<Rule.Condition.Field, HeaderExtractor>builder() .put(Rule.Condition.FixedField.SUBJECT, SUBJECT_EXTRACTOR) @@ -62,23 +61,23 @@ public interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> boolean STRICT_PARSING = true; static HeaderExtractor and(HeaderExtractor headerExtractor1, HeaderExtractor headerExtractor2) { - return (Mail mail) -> StreamUtils.flatten(headerExtractor1.apply(mail), headerExtractor2.apply(mail)); + return (FilteringHeaders filteringHeaders) -> StreamUtils.flatten(headerExtractor1.apply(filteringHeaders), headerExtractor2.apply(filteringHeaders)); } static HeaderExtractor recipientExtractor(Message.RecipientType type) { String headerName = type.toString(); - ThrowingFunction<Mail, String[]> addressGetter = mail -> mail.getMessage().getHeader(headerName); + ThrowingFunction<FilteringHeaders, String[]> addressGetter = filteringHeaders -> filteringHeaders.getHeader(headerName); return addressExtractor(addressGetter, headerName); } - static HeaderExtractor addressExtractor(ThrowingFunction<Mail, String[]> addressGetter, String fallbackHeaderName) { - return mail -> { + static HeaderExtractor addressExtractor(ThrowingFunction<FilteringHeaders, String[]> addressGetter, String fallbackHeaderName) { + return filteringHeaders -> { try { - return toAddressContents(addressGetter.apply(mail)); + return toAddressContents(addressGetter.apply(filteringHeaders)); } catch (Exception e) { LOGGER.info("Failed parsing header. Falling back to unparsed header value matching", e); - return Stream.of(mail.getMessage().getHeader(fallbackHeaderName)) + return Stream.of(filteringHeaders.getHeader(fallbackHeaderName)) .map(MimeUtil::unscrambleHeaderValue); } }; @@ -96,7 +95,7 @@ public interface HeaderExtractor extends ThrowingFunction<Mail, Stream<String>> Preconditions.checkArgument(field instanceof Rule.Condition.CustomHeaderField); Rule.Condition.CustomHeaderField customHeaderField = (Rule.Condition.CustomHeaderField) field; - return Optional.of(mail -> StreamUtils.ofNullables(mail.getMessage().getHeader(customHeaderField.headerName()))); + return Optional.of(filteringHeaders -> StreamUtils.ofNullables(filteringHeaders.getHeader(customHeaderField.headerName()))); }); } } diff --git a/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java b/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java index f583561cad..dc433411e8 100644 --- a/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java +++ b/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/MailMatcher.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.stream.Stream; import org.apache.james.jmap.api.filtering.Rule; -import org.apache.mailet.Mail; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,10 +45,10 @@ public interface MailMatcher { } @Override - public boolean match(Mail mail) { + public boolean match(FilteringHeaders filteringHeaders) { try { Predicate<MailMatchingCondition> predicate = (MailMatchingCondition mailMatchingCondition) -> { - Stream<String> headerLines = mailMatchingCondition.getHeaderExtractor().apply(mail); + Stream<String> headerLines = mailMatchingCondition.getHeaderExtractor().apply(filteringHeaders); return mailMatchingCondition.getContentMatcher().match(headerLines, mailMatchingCondition.getRuleValue()); }; @@ -107,5 +106,5 @@ public interface MailMatcher { ).collect(ImmutableList.toImmutableList()), rule.getConditionGroup().getConditionCombiner()); } - boolean match(Mail mail); + boolean match(FilteringHeaders filteringHeaders); } diff --git a/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java b/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java index 07f0371182..4e59fb4611 100644 --- a/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java +++ b/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/RuleMatcher.java @@ -23,6 +23,8 @@ import java.util.List; import java.util.stream.Stream; import org.apache.james.jmap.api.filtering.Rule; +import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.model.MessageResult; import org.apache.mailet.Mail; import com.google.common.base.Preconditions; @@ -37,7 +39,16 @@ class RuleMatcher { } Stream<Rule> findApplicableRules(Mail mail) { + FilteringHeaders filteringHeaders = new FilteringHeaders.MailFilteringHeaders(mail); + + return filteringRules.stream() + .filter(rule -> MailMatcher.from(rule).match(filteringHeaders)); + } + + Stream<Rule> findApplicableRules(MessageResult messageResult) throws MailboxException { + FilteringHeaders filteringHeaders = new FilteringHeaders.MessageResultFilteringHeaders(messageResult); + return filteringRules.stream() - .filter(rule -> MailMatcher.from(rule).match(mail)); + .filter(rule -> MailMatcher.from(rule).match(filteringHeaders)); } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
