This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit d47f0e9d543793bdf9708d05e4c8b86ff15ea397 Author: Benoit Tellier <[email protected]> AuthorDate: Fri Apr 30 21:52:24 2021 +0700 JAMES-3574 Create a LMTP handler chain for executing the mailet container --- .../MailetContainerCmdHandlerLoader.java | 84 +++++++++++ .../james/lmtpserver/MailetContainerHandler.java | 59 ++++++++ .../james/lmtpserver/NoopJamesMessageHook.java | 32 +++++ .../lmtpserver/MailetContainerHandlerTest.java | 156 +++++++++++++++++++++ .../src/test/resources/lmtpmailet.xml | 34 +++++ 5 files changed, 365 insertions(+) diff --git a/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/MailetContainerCmdHandlerLoader.java b/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/MailetContainerCmdHandlerLoader.java new file mode 100644 index 0000000..f86289b --- /dev/null +++ b/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/MailetContainerCmdHandlerLoader.java @@ -0,0 +1,84 @@ +/**************************************************************** + * 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.lmtpserver; + +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Stream; + +import org.apache.james.protocols.api.handler.CommandDispatcher; +import org.apache.james.protocols.api.handler.CommandHandlerResultLogger; +import org.apache.james.protocols.lib.handler.HandlersPackage; +import org.apache.james.protocols.lmtp.core.LhloCmdHandler; +import org.apache.james.protocols.lmtp.core.WelcomeMessageHandler; +import org.apache.james.protocols.smtp.core.ExpnCmdHandler; +import org.apache.james.protocols.smtp.core.NoopCmdHandler; +import org.apache.james.protocols.smtp.core.PostmasterAbuseRcptHook; +import org.apache.james.protocols.smtp.core.QuitCmdHandler; +import org.apache.james.protocols.smtp.core.ReceivedDataLineFilter; +import org.apache.james.protocols.smtp.core.RsetCmdHandler; +import org.apache.james.protocols.smtp.core.VrfyCmdHandler; +import org.apache.james.protocols.smtp.core.esmtp.MailSizeEsmtpExtension; +import org.apache.james.protocols.smtp.core.log.HookResultLogger; +import org.apache.james.smtpserver.AuthRequiredToRelayRcptHook; +import org.apache.james.smtpserver.JamesDataCmdHandler; +import org.apache.james.smtpserver.JamesMailCmdHandler; +import org.apache.james.smtpserver.JamesRcptCmdHandler; +import org.apache.james.smtpserver.fastfail.ValidRcptHandler; + +/** + * This class allows creating a LMTP server executing the mailet container + */ +public class MailetContainerCmdHandlerLoader implements HandlersPackage { + + private final List<String> commands = new LinkedList<>(); + + + public MailetContainerCmdHandlerLoader() { + Stream.of( + WelcomeMessageHandler.class, + CommandDispatcher.class, + JamesDataCmdHandler.class, + ExpnCmdHandler.class, + LhloCmdHandler.class, + JamesMailCmdHandler.class, + NoopCmdHandler.class, + QuitCmdHandler.class, + JamesRcptCmdHandler.class, + ValidRcptHandler.class, + RsetCmdHandler.class, + VrfyCmdHandler.class, + MailSizeEsmtpExtension.class, + AuthRequiredToRelayRcptHook.class, + PostmasterAbuseRcptHook.class, + ReceivedDataLineFilter.class, + MailetContainerHandler.class, + CommandHandlerResultLogger.class, + NoopJamesMessageHook.class, + HookResultLogger.class) + .map(Class::getName) + .forEachOrdered(commands::add); + } + + @Override + public List<String> getHandlers() { + return commands; + } +} diff --git a/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/MailetContainerHandler.java b/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/MailetContainerHandler.java new file mode 100644 index 0000000..fcb58c0 --- /dev/null +++ b/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/MailetContainerHandler.java @@ -0,0 +1,59 @@ +/**************************************************************** + * 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.lmtpserver; + +import javax.inject.Inject; +import javax.mail.MessagingException; + +import org.apache.james.mailetcontainer.api.MailProcessor; +import org.apache.james.protocols.api.Response; +import org.apache.james.protocols.smtp.SMTPResponse; +import org.apache.james.protocols.smtp.SMTPRetCode; +import org.apache.james.protocols.smtp.SMTPSession; +import org.apache.james.protocols.smtp.core.AbstractHookableCmdHandler; +import org.apache.james.protocols.smtp.dsn.DSNStatus; +import org.apache.james.protocols.smtp.hook.HookResult; +import org.apache.james.protocols.smtp.hook.HookReturnCode; +import org.apache.james.smtpserver.DataLineJamesMessageHookHandler; +import org.apache.mailet.Mail; + +public class MailetContainerHandler extends DataLineJamesMessageHookHandler { + private final MailProcessor mailProcessor; + + @Inject + public MailetContainerHandler(MailProcessor mailProcessor) { + this.mailProcessor = mailProcessor; + } + + @Override + protected Response processExtensions(SMTPSession session, Mail mail) { + try { + mailProcessor.service(mail); + + return AbstractHookableCmdHandler.calcDefaultSMTPResponse(HookResult.builder() + .hookReturnCode(HookReturnCode.ok()) + .smtpReturnCode(SMTPRetCode.MAIL_OK) + .smtpDescription(DSNStatus.getStatus(DSNStatus.SUCCESS, DSNStatus.CONTENT_OTHER) + " Message received") + .build()); + } catch (MessagingException e) { + return new SMTPResponse(SMTPRetCode.LOCAL_ERROR, DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.UNDEFINED_STATUS) + "Temporary error deliver message"); + } + } +} diff --git a/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/NoopJamesMessageHook.java b/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/NoopJamesMessageHook.java new file mode 100644 index 0000000..d6719a7 --- /dev/null +++ b/server/protocols/protocols-lmtp/src/main/java/org/apache/james/lmtpserver/NoopJamesMessageHook.java @@ -0,0 +1,32 @@ +/**************************************************************** + * 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.lmtpserver; + +import org.apache.james.protocols.smtp.SMTPSession; +import org.apache.james.protocols.smtp.hook.HookResult; +import org.apache.james.smtpserver.JamesMessageHook; +import org.apache.mailet.Mail; + +public class NoopJamesMessageHook implements JamesMessageHook { + @Override + public HookResult onMessage(SMTPSession session, Mail mail) { + return HookResult.DECLINED; + } +} diff --git a/server/protocols/protocols-lmtp/src/test/java/org/apache/james/lmtpserver/MailetContainerHandlerTest.java b/server/protocols/protocols-lmtp/src/test/java/org/apache/james/lmtpserver/MailetContainerHandlerTest.java new file mode 100644 index 0000000..98ecfb9 --- /dev/null +++ b/server/protocols/protocols-lmtp/src/test/java/org/apache/james/lmtpserver/MailetContainerHandlerTest.java @@ -0,0 +1,156 @@ +/**************************************************************** + * 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.lmtpserver; + +import static org.apache.james.jmap.JMAPTestingConstants.DOMAIN; +import static org.apache.james.jmap.JMAPTestingConstants.LOCALHOST_IP; +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import org.apache.james.core.Domain; +import org.apache.james.core.Username; +import org.apache.james.dnsservice.api.DNSService; +import org.apache.james.dnsservice.api.InMemoryDNSService; +import org.apache.james.domainlist.api.DomainList; +import org.apache.james.domainlist.lib.DomainListConfiguration; +import org.apache.james.domainlist.memory.MemoryDomainList; +import org.apache.james.filesystem.api.FileSystem; +import org.apache.james.lmtpserver.netty.LMTPServerFactory; +import org.apache.james.mailetcontainer.api.MailProcessor; +import org.apache.james.metrics.api.MetricFactory; +import org.apache.james.metrics.tests.RecordingMetricFactory; +import org.apache.james.protocols.lib.mock.ConfigLoader; +import org.apache.james.protocols.lib.mock.MockProtocolHandlerLoader; +import org.apache.james.rrt.api.AliasReverseResolver; +import org.apache.james.rrt.api.CanSendFrom; +import org.apache.james.rrt.api.RecipientRewriteTable; +import org.apache.james.rrt.api.RecipientRewriteTableConfiguration; +import org.apache.james.rrt.lib.AliasReverseResolverImpl; +import org.apache.james.rrt.lib.CanSendFromImpl; +import org.apache.james.rrt.memory.MemoryRecipientRewriteTable; +import org.apache.james.server.core.configuration.Configuration; +import org.apache.james.server.core.filesystem.FileSystemImpl; +import org.apache.james.user.api.UsersRepository; +import org.apache.james.user.memory.MemoryUsersRepository; +import org.apache.mailet.Mail; +import org.awaitility.Awaitility; +import org.jboss.netty.util.HashedWheelTimer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class MailetContainerHandlerTest { + static class RecordingMailProcessor implements MailProcessor { + private final ArrayList<Mail> mails = new ArrayList<>(); + + @Override + public void service(Mail mail) { + mails.add(mail); + } + + public List<Mail> getMails() { + return mails; + } + } + + private RecordingMailProcessor recordingMailProcessor; + private LMTPServerFactory lmtpServerFactory; + + @BeforeEach + void setUp() throws Exception { + InMemoryDNSService dnsService = new InMemoryDNSService() + .registerMxRecord(Domain.LOCALHOST.asString(), "127.0.0.1") + .registerMxRecord("examplebis.local", "127.0.0.1") + .registerMxRecord("127.0.0.1", "127.0.0.1"); + MemoryDomainList domainList = new MemoryDomainList(dnsService); + domainList.configure(DomainListConfiguration.builder() + .autoDetect(false) + .autoDetectIp(false) + .build()); + recordingMailProcessor = new RecordingMailProcessor(); + + domainList.addDomain(Domain.of("examplebis.local")); + MemoryUsersRepository usersRepository = MemoryUsersRepository.withVirtualHosting(domainList); + + usersRepository.addUser(Username.of("[email protected]"), "pwd"); + + FileSystem fileSystem = new FileSystemImpl(Configuration.builder() + .workingDirectory("../") + .configurationFromClasspath() + .build().directories()); + MemoryRecipientRewriteTable rewriteTable = new MemoryRecipientRewriteTable(); + rewriteTable.setConfiguration(RecipientRewriteTableConfiguration.DEFAULT_ENABLED); + AliasReverseResolver aliasReverseResolver = new AliasReverseResolverImpl(rewriteTable); + CanSendFrom canSendFrom = new CanSendFromImpl(rewriteTable, aliasReverseResolver); + MockProtocolHandlerLoader loader = MockProtocolHandlerLoader.builder() + .put(binder -> binder.bind(DomainList.class).toInstance(domainList)) + .put(binder -> binder.bind(RecipientRewriteTable.class).toInstance(rewriteTable)) + .put(binder -> binder.bind(CanSendFrom.class).toInstance(canSendFrom)) + .put(binder -> binder.bind(MailProcessor.class).toInstance(recordingMailProcessor)) + .put(binder -> binder.bind(FileSystem.class).toInstance(fileSystem)) + .put(binder -> binder.bind(DNSService.class).toInstance(dnsService)) + .put(binder -> binder.bind(UsersRepository.class).toInstance(usersRepository)) + .put(binder -> binder.bind(MetricFactory.class).to(RecordingMetricFactory.class)) + .build(); + lmtpServerFactory = new LMTPServerFactory(loader, fileSystem, new RecordingMetricFactory(), new HashedWheelTimer()); + + lmtpServerFactory.configure(ConfigLoader.getConfig(ClassLoader.getSystemResourceAsStream("lmtpmailet.xml"))); + lmtpServerFactory.init(); + } + + @AfterEach + void tearDown() { + lmtpServerFactory.destroy(); + } + + @Test + void emailShouldTriggerTheMailProcessing() throws Exception { + SocketChannel server = SocketChannel.open(); + server.connect(new InetSocketAddress(LOCALHOST_IP, getLmtpPort())); + + server.write(ByteBuffer.wrap(("LHLO <" + DOMAIN + ">\r\n").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap(("MAIL FROM: <bob@" + DOMAIN + ">\r\n").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap(("RCPT TO: <[email protected]>\r\n").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap(("DATA\r\n").getBytes(StandardCharsets.UTF_8))); + server.read(ByteBuffer.allocate(1024)); // needed to synchronize + server.write(ByteBuffer.wrap(("header:value\r\n\r\nbody").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap(("\r\n").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap((".").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap(("\r\n").getBytes(StandardCharsets.UTF_8))); + server.write(ByteBuffer.wrap(("QUIT\r\n").getBytes(StandardCharsets.UTF_8))); + + Awaitility.await() + .untilAsserted(() -> assertThat(recordingMailProcessor.getMails()).hasSize(1)); + } + + public int getLmtpPort() { + return lmtpServerFactory.getServers().stream() + .findFirst() + .flatMap(server -> server.getListenAddresses().stream().findFirst()) + .map(InetSocketAddress::getPort) + .orElseThrow(() -> new IllegalStateException("LMTP server not defined")); + } +} \ No newline at end of file diff --git a/server/protocols/protocols-lmtp/src/test/resources/lmtpmailet.xml b/server/protocols/protocols-lmtp/src/test/resources/lmtpmailet.xml new file mode 100644 index 0000000..3b1d4e1 --- /dev/null +++ b/server/protocols/protocols-lmtp/src/test/resources/lmtpmailet.xml @@ -0,0 +1,34 @@ +<?xml version="1.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. + --> + +<lmtpservers> + <lmtpserver enabled="true"> + <jmxName>lmtpserver</jmxName> + <bind>0.0.0.0:0</bind> + <connectionBacklog>200</connectionBacklog> + <connectiontimeout>1200</connectiontimeout> + <connectionLimit>0</connectionLimit> + <connectionLimitPerIP>0</connectionLimitPerIP> + <maxmessagesize>0</maxmessagesize> + <handlerchain coreHandlersPackage="org.apache.james.lmtpserver.MailetContainerCmdHandlerLoader"> + <handler class="org.apache.james.lmtpserver.MailetContainerCmdHandlerLoader"/> + </handlerchain> + </lmtpserver> +</lmtpservers> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
