From 425486e799de12480940c663d807d956f2170985 Mon Sep 17 00:00:00 2001 From: Benoit Tellier Date: Fri, 30 Jul 2021 14:18:56 +0700 Subject: [PATCH 01/11] JAMES-2435 Add short READMEs for examples This allows jumping from the source code to the website. --- examples/custom-listeners/README.md | 5 +++++ examples/custom-mailets/README.md | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 examples/custom-listeners/README.md create mode 100644 examples/custom-mailets/README.md diff --git a/examples/custom-listeners/README.md b/examples/custom-listeners/README.md new file mode 100644 index 00000000000..28c5497f93c --- /dev/null +++ b/examples/custom-listeners/README.md @@ -0,0 +1,5 @@ +# Writing custom Mailbox listeners + +Mailbox listeners allow extending the behaviour of mail delivery agents. + +Read this page [on the website](http://james.apache.org/howTo/custom-listeners.html) to see how to run this example. diff --git a/examples/custom-mailets/README.md b/examples/custom-mailets/README.md new file mode 100644 index 00000000000..42ae70508d6 --- /dev/null +++ b/examples/custom-mailets/README.md @@ -0,0 +1,6 @@ +# How to customize mail processing + +Mailet and matcher allow in depth customisation of mail processing within +Apache James, even allowing writing custom components. + +Read this page [on the website](http://james.apache.org/howTo/mail-processing.html) to see how to run this example. \ No newline at end of file From ecfeef62d1bdb66e693cb11b35c779ff87841b32 Mon Sep 17 00:00:00 2001 From: Benoit Tellier Date: Fri, 30 Jul 2021 14:19:46 +0700 Subject: [PATCH 02/11] [FIX] Javadoc in SMTP hooks references bad command names This looks like a copy and paste mistake. --- .../apache/james/protocols/smtp/hook/MailParametersHook.java | 2 +- .../java/org/apache/james/protocols/smtp/hook/QuitHook.java | 2 +- .../java/org/apache/james/protocols/smtp/hook/RcptHook.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/MailParametersHook.java b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/MailParametersHook.java index 24388985155..a9c82c17518 100644 --- a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/MailParametersHook.java +++ b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/MailParametersHook.java @@ -21,7 +21,7 @@ import org.apache.james.protocols.smtp.SMTPSession; /** - * Implement this interfaces to hook in the MAIL Command + * Implement this interfaces to hook in the MAIL Command parameters * */ public interface MailParametersHook extends Hook { diff --git a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/QuitHook.java b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/QuitHook.java index a6c23350040..7615513973e 100644 --- a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/QuitHook.java +++ b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/QuitHook.java @@ -25,7 +25,7 @@ import org.apache.james.protocols.smtp.SMTPSession; /** - * Implement this interfaces to hook in the MAIL Command + * Implement this interfaces to hook in the QUIT Command * */ public interface QuitHook extends Hook { diff --git a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/RcptHook.java b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/RcptHook.java index e5fc611aaa7..42c62f00274 100644 --- a/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/RcptHook.java +++ b/protocols/smtp/src/main/java/org/apache/james/protocols/smtp/hook/RcptHook.java @@ -28,7 +28,7 @@ import com.google.common.collect.ImmutableSet; /** - * Implement this interfaces to hook in the MAIL Command + * Implement this interfaces to hook in the RCPT Command */ public interface RcptHook extends Hook { /** From 7eae80342eb5ad298aaafe7cd952353333a19dfb Mon Sep 17 00:00:00 2001 From: Benoit Tellier Date: Fri, 30 Jul 2021 14:20:25 +0700 Subject: [PATCH 03/11] JAMES-2435 Exemple for SMTP hooks --- examples/custom-smtp-hooks/README.md | 141 ++++++++++++++++++ examples/custom-smtp-hooks/pom.xml | 53 +++++++ .../james/examples/LoggingRcptHook.java | 38 +++++ .../src/main/resources/smtpserver.xml | 110 ++++++++++++++ examples/pom.xml | 1 + 5 files changed, 343 insertions(+) create mode 100644 examples/custom-smtp-hooks/README.md create mode 100644 examples/custom-smtp-hooks/pom.xml create mode 100644 examples/custom-smtp-hooks/src/main/java/org/apache/james/examples/LoggingRcptHook.java create mode 100644 examples/custom-smtp-hooks/src/main/resources/smtpserver.xml diff --git a/examples/custom-smtp-hooks/README.md b/examples/custom-smtp-hooks/README.md new file mode 100644 index 00000000000..dfe5f703eae --- /dev/null +++ b/examples/custom-smtp-hooks/README.md @@ -0,0 +1,141 @@ +# Writing custom behaviour for Apache James SMTP server + +Read this page [on the website](http://james.apache.org/howTo/custom-smtp-hooks.html). + +The current project demonstrate how to write custom behaviours for Apache James SMTP server +by the means of SMTP hooks. + +SMTP hooks allows integrating third party systems with the SMTP stack, allows writing additional SMTP extensions, for +instance. + +Start by importing the dependencies: + +``` + + org.apache.james.protocols + protocols-smtp/artifactId> + +``` + +Allows writing the following hooks: + + - `AuthHook`: hook in the AUTH Command + - `HeloHook`: hook in the HELO Command + - `MailHook`: hook in the MAIL Command + - `MailParametersHook`: hook for MAIL Command parameters + - `RcptHook`: hook in the RCPT Command + - `MessageHook`: hook in the DATA Command + - `QuitHook`: hook in the QUIT Command + - `UnknownHook`: hook for unknown commands + +``` + + org.apache.james + james-server-protocols-smtp + +``` + +Allows writing the following hooks: + + - `JamesMessageHook`: DATA command. Allows access to the message content. + +James comes bundled with [provided SMTP hooks for common features](http://james.apache.org/server/dev-provided-smtp-hooks.html). +We encourage you to review them before starting writing your own hooks. + +## Writing your own hooks + +In this example we implement a single RCPT hook: + +``` +public class LoggingRcptHook implements RcptHook { + @Override + public HookResult doRcpt(SMTPSession session, MaybeSender sender, MailAddress rcpt, Map parameters) { + System.out.println("RCPT TO " + rcpt + "with parameters " + parameters); + + // Continue the SMTP transaction + return HookResult.DECLINED; + } +} +``` + +You can compile this example project: + +``` +mvn clean install +``` + +Then embed your extension into a James server. First configure your hook: + +``` + + + + smtpserver-global + 0.0.0.0:25 + 200 + + file://conf/keystore + james72laBalle + org.bouncycastle.jce.provider.BouncyCastleProvider + SunX509 + + + + + + + + + +``` + +Create a keystore (default password being `james72laBalle`): + +``` +keytool -genkey -alias james -keyalg RSA -keystore keystore +``` + +Then start a James server with your JAR and the configuration: + +``` +docker run -d \ + -v $PWD/smtpserver.xml:/root/conf/smtpserver.xml \ + -v $PWD/exts:/root/extensions-jars \ + -v $PWD/keystore:/root/conf/keystore \ + -p 25:25 \ + apache/james:memory-latest +``` + +You can play with `telnet` utility with the resulting server: + +``` +$ telnet 127.0.0.1 25 +Trying 127.0.0.1... +Connected to 127.0.0.1. +Escape character is '^]'. +220 Apache JAMES awesome SMTP Server +EHLO toto.com +250-177b73020637 Hello toto.com [172.17.0.1]) +250-PIPELINING +250-ENHANCEDSTATUSCODES +250 8BITMIME +RCPT TO: +503 5.5.0 Need MAIL before RCPT +MAIL FROM: +250 2.1.0 Sender OK +RCPT TO: +550 5.7.1 Requested action not taken: relaying denied +quit +``` + +Now looking at the logs we can verify that our RCPT have well been executed: + +``` +06:49:15.734 [INFO ] o.a.j.GuiceJamesServer - JAMES server started +06:49:30.275 [INFO ] o.a.j.p.n.BasicChannelUpstreamHandler - Connection established from 172.17.0.1 +06:50:18.892 [INFO ] o.a.j.i.n.ImapChannelUpstreamHandler - Connection established from 172.17.0.1 +06:50:19.152 [INFO ] o.a.j.i.n.ImapChannelUpstreamHandler - Connection closed for 172.17.0.1 +RCPT TO c@d.comwith parameters {TO:=, =} +``` + +Note that hooks can also be written for the LMTP protocol. \ No newline at end of file diff --git a/examples/custom-smtp-hooks/pom.xml b/examples/custom-smtp-hooks/pom.xml new file mode 100644 index 00000000000..3648c660429 --- /dev/null +++ b/examples/custom-smtp-hooks/pom.xml @@ -0,0 +1,53 @@ + + + + 4.0.0 + + org.apache.james.examples + examples + 3.7.0-SNAPSHOT + + + custom-smtp-hooks + + Apache James :: Examples :: Custom SMTP Hooks + Example of how to extend James existing SMTP implementation + + + + ${james.groupId} + james-server-protocols-smtp + ${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + \ No newline at end of file diff --git a/examples/custom-smtp-hooks/src/main/java/org/apache/james/examples/LoggingRcptHook.java b/examples/custom-smtp-hooks/src/main/java/org/apache/james/examples/LoggingRcptHook.java new file mode 100644 index 00000000000..017b25f9585 --- /dev/null +++ b/examples/custom-smtp-hooks/src/main/java/org/apache/james/examples/LoggingRcptHook.java @@ -0,0 +1,38 @@ +/**************************************************************** + * 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.examples; + +import java.util.Map; + +import org.apache.james.core.MailAddress; +import org.apache.james.core.MaybeSender; +import org.apache.james.protocols.smtp.SMTPSession; +import org.apache.james.protocols.smtp.hook.HookResult; +import org.apache.james.protocols.smtp.hook.RcptHook; + +public class LoggingRcptHook implements RcptHook { + @Override + public HookResult doRcpt(SMTPSession session, MaybeSender sender, MailAddress rcpt, Map parameters) { + System.out.println("RCPT TO " + rcpt + "with parameters " + parameters); + + // Continue the SMTP transaction + return HookResult.DECLINED; + } +} diff --git a/examples/custom-smtp-hooks/src/main/resources/smtpserver.xml b/examples/custom-smtp-hooks/src/main/resources/smtpserver.xml new file mode 100644 index 00000000000..0ddc0705c1c --- /dev/null +++ b/examples/custom-smtp-hooks/src/main/resources/smtpserver.xml @@ -0,0 +1,110 @@ + + + + + + + + + smtpserver-global + 0.0.0.0:25 + 200 + + file://conf/keystore + james72laBalle + org.bouncycastle.jce.provider.BouncyCastleProvider + SunX509 + + 360 + 0 + 0 + false + 127.0.0.0/8 + false + 0 + true + Apache JAMES awesome SMTP Server + + + + + + + + smtpserver-TLS + 0.0.0.0:465 + 200 + + file://conf/keystore + james72laBalle + org.bouncycastle.jce.provider.BouncyCastleProvider + SunX509 + + 360 + 0 + 0 + + true + 127.0.0.0/8 + + false + 0 + true + Apache JAMES awesome SMTP Server + + + + + + + + smtpserver-authenticated + 0.0.0.0:587 + 200 + + file://conf/keystore + james72laBalle + org.bouncycastle.jce.provider.BouncyCastleProvider + SunX509 + + 360 + 0 + 0 + + true + 127.0.0.0/8 + + false + 0 + true + Apache JAMES awesome SMTP Server + + + + + + + + + diff --git a/examples/pom.xml b/examples/pom.xml index ca2e0deaedf..955a661790e 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -33,6 +33,7 @@ custom-listeners custom-mailets + custom-smtp-hooks org.apache.james From c560969f2ef8ecc2c1eb6dbcba023f2721b93c9e Mon Sep 17 00:00:00 2001 From: Benoit Tellier Date: Fri, 30 Jul 2021 14:36:23 +0700 Subject: [PATCH 04/11] JAMES-2435 Website `how to` for SMTP hooks --- src/homepage/howTo/custom-smtp-hooks.html | 184 ++++++++++++++++++++++ src/homepage/howTo/index.html | 7 + 2 files changed, 191 insertions(+) create mode 100644 src/homepage/howTo/custom-smtp-hooks.html diff --git a/src/homepage/howTo/custom-smtp-hooks.html b/src/homepage/howTo/custom-smtp-hooks.html new file mode 100644 index 00000000000..7f42e2fb430 --- /dev/null +++ b/src/homepage/howTo/custom-smtp-hooks.html @@ -0,0 +1,184 @@ +--- +layout: howTo +--- + + + +
+ + +
+
+
+
+

Configure Custom SMTP hooks

+
+ +

+ The current project demonstrates how to write custom behaviors for Apache James SMTP server + by the means of SMTP hooks. +

+ +

+ SMTP hooks allow integrating third party systems with the SMTP stack and writing additional SMTP extensions, for + instance. +

+ +

+ Start by importing the dependencies: +

+ +
<dependency>
+    <groupId>org.apache.james.protocols</groupId>
+    <artifactId>protocols-smtp/artifactId>
+</dependency>
+                
+ +
    Allows writing the following hooks: +
  • AuthHook: hook in the AUTH Command
  • +
  • HeloHook: hook in the HELO Command
  • +
  • MailHook: hook in the MAIL Command
  • +
  • MailParametersHook: hook in the MAIL Command parameters
  • +
  • RcptHook: hook in the RCPT Command
  • +
  • MessageHook: hook in the DATA Command
  • +
  • QuitHook: hook in the QUIT Command
  • +
  • UnknownHook: hook for unknown commands
  • +
+ +
<dependency>
+    <groupId>org.apache.james</groupId>
+    <artifactId>james-server-protocols-smtp</artifactId>
+</dependency>
+                
+ +

James comes bundled with provided SMTP hooks for common features. + We encourage you to review them before starting writing your own hooks.

+ +
    Allows writing the following hooks: +
  • JamesMessageHook: DATA command. Allows access to the message content.
  • +
+ +
+

Writing your own hooks

+
+ +

+ Find this example on GitHub. +

+ +

+ In this example we implement a single RCPT hook: +

+ +
public class LoggingRcptHook implements RcptHook {
+    @Override
+    public HookResult doRcpt(SMTPSession session, MaybeSender sender, MailAddress rcpt, Map<String, String> parameters) {
+        System.out.println("RCPT TO " + rcpt + "with parameters " + parameters);
+
+        // Continue the SMTP transaction
+        return HookResult.DECLINED;
+    }
+}
+                
+ +

You can compile this example project:

+ +
mvn clean install
+ +

Then embed your extension into a James server. First configure your hook:

+ +
<?xml version="1.0"?>
+<smtpservers>
+    <smtpserver enabled="true">
+        <jmxName>smtpserver-global</jmxName>
+        <bind>0.0.0.0:25</bind>
+        <connectionBacklog>200</connectionBacklog>
+        <tls socketTLS="false" startTLS="false">
+            <keystore>file://conf/keystore</keystore>
+            <secret>james72laBalle</secret>
+            <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider>
+            <algorithm>SunX509</algorithm>
+        </tls>
+        <!-- ... -->
+        <handlerchain>
+            <handler class="org.apache.james.smtpserver.fastfail.ValidRcptHandler"/>
+            <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/>
+            <handler class="org.apache.james.examples.LoggingRcptHook"/>
+        </handlerchain>
+    </smtpserver>
+</smtpservers>
+                
+ +

Create a keystore (default password being james72laBalle):

+ +
keytool -genkey -alias james -keyalg RSA -keystore keystore
+ +

Then start a James server with your JAR and the configuration:

+ +
docker run -d \
+  -v $PWD/smtpserver.xml:/root/conf/smtpserver.xml \
+  -v $PWD/exts:/root/extensions-jars \
+  -v $PWD/keystore:/root/conf/keystore \
+  -p 25:25 \
+  apache/james:memory-latest
+                
+ +

You can play with telnet utility with the resulting server:

+ +
$ telnet 127.0.0.1 25
+Trying 127.0.0.1...
+Connected to 127.0.0.1.
+Escape character is '^]'.
+220 Apache JAMES awesome SMTP Server
+EHLO toto.com
+250-177b73020637 Hello toto.com [172.17.0.1])
+250-PIPELINING
+250-ENHANCEDSTATUSCODES
+250 8BITMIME
+RCPT TO: <b@c.com>
+503 5.5.0 Need MAIL before RCPT
+MAIL FROM: <b@c.com>
+250 2.1.0 Sender <b@c.com> OK
+RCPT TO: <c@d.com>
+550 5.7.1 Requested action not taken: relaying denied
+quit
+                
+ +

+ Now looking at the logs we can verify that our RCPT have well been executed: +

+ +
06:49:15.734 [INFO ] o.a.j.GuiceJamesServer - JAMES server started
+06:49:30.275 [INFO ] o.a.j.p.n.BasicChannelUpstreamHandler - Connection established from 172.17.0.1
+06:50:18.892 [INFO ] o.a.j.i.n.ImapChannelUpstreamHandler - Connection established from 172.17.0.1
+06:50:19.152 [INFO ] o.a.j.i.n.ImapChannelUpstreamHandler - Connection closed for 172.17.0.1
+RCPT TO c@d.comwith parameters {TO:=, <c@d.com>=}
+ +

Note that hooks can also be written for the LMTP protocol.

+
+ +
+
+ +
diff --git a/src/homepage/howTo/index.html b/src/homepage/howTo/index.html index b30ebc87f1b..0cff52bbf01 100644 --- a/src/homepage/howTo/index.html +++ b/src/homepage/howTo/index.html @@ -73,6 +73,13 @@

Customize James

class="james-schema" > Configure Custom Listeners + + Configure Custom SMTP hooks +

From 78d3695efe323a8fb37f9fe752a3425dd09cfcf7 Mon Sep 17 00:00:00 2001 From: Benoit Tellier Date: Fri, 30 Jul 2021 14:54:26 +0700 Subject: [PATCH 05/11] JAMES-2435 Example: creating custom SMTP commands --- examples/custom-smtp-command/README.md | 151 ++++++++++++++++++ examples/custom-smtp-command/pom.xml | 53 ++++++ .../james/examples/MyCmdHandlerLoader.java | 101 ++++++++++++ .../james/examples/MyNoopCmdHandler.java | 53 ++++++ .../src/main/resources/smtpserver.xml | 50 ++++++ examples/pom.xml | 1 + 6 files changed, 409 insertions(+) create mode 100644 examples/custom-smtp-command/README.md create mode 100644 examples/custom-smtp-command/pom.xml create mode 100644 examples/custom-smtp-command/src/main/java/org/apache/james/examples/MyCmdHandlerLoader.java create mode 100644 examples/custom-smtp-command/src/main/java/org/apache/james/examples/MyNoopCmdHandler.java create mode 100644 examples/custom-smtp-command/src/main/resources/smtpserver.xml diff --git a/examples/custom-smtp-command/README.md b/examples/custom-smtp-command/README.md new file mode 100644 index 00000000000..6bdbe2e18f3 --- /dev/null +++ b/examples/custom-smtp-command/README.md @@ -0,0 +1,151 @@ +# Creating your own SMTP commands + +Read this page [on the website](http://james.apache.org/howTo/custom-smtp-commands.html). + +The current project demonstrates how to write custom commands for Apache James SMTP server. + +Start by importing the dependencies: + +``` + + org.apache.james.protocols + protocols-smtp/artifactId> + +``` + +You can write your commands by extending the `CommandHandler` class. For instance: + +``` +/** + * Copy of NoopCmdHandler + */ +public class MyNoopCmdHandler implements CommandHandler { + private static final Collection COMMANDS = ImmutableSet.of("MYNOOP"); + + private static final Response NOOP = new SMTPResponse(SMTPRetCode.MAIL_OK, + DSNStatus.getStatus(DSNStatus.SUCCESS, DSNStatus.UNDEFINED_STATUS) + " OK") + .immutable(); + + @Override + public Response onCommand(SMTPSession session, Request request) { + return NOOP; + } + + @Override + public Collection getImplCommands() { + return COMMANDS; + } +} +``` + +You then need to list the exposed SMTP commands with a `HandlersPackage`. For instance: + +``` +/** + * This class copies CoreCmdHandlerLoader adding support for MYNOOP command + */ +public class MyCmdHandlerLoader implements HandlersPackage { + + private final List commands = new LinkedList<>(); + + public MyCmdHandlerLoader() { + Stream.of( + JamesWelcomeMessageHandler.class, + CommandDispatcher.class, + AuthCmdHandler.class, + JamesDataCmdHandler.class, + EhloCmdHandler.class, + ExpnCmdHandler.class, + HeloCmdHandler.class, + HelpCmdHandler.class, + JamesMailCmdHandler.class, + NoopCmdHandler.class, + QuitCmdHandler.class, + JamesRcptCmdHandler.class, + RsetCmdHandler.class, + VrfyCmdHandler.class, + MailSizeEsmtpExtension.class, + UsersRepositoryAuthHook.class, + AuthRequiredToRelayRcptHook.class, + SenderAuthIdentifyVerificationRcptHook.class, + PostmasterAbuseRcptHook.class, + ReceivedDataLineFilter.class, + DataLineJamesMessageHookHandler.class, + StartTlsCmdHandler.class, + AddDefaultAttributesMessageHook.class, + SendMailHandler.class, + UnknownCmdHandler.class, + CommandHandlerResultLogger.class, + HookResultLogger.class, + // Support MYNOOP + MyNoopCmdHandler.class) + .map(Class::getName) + .forEachOrdered(commands::add); + } + + @Override + public List getHandlers() { + return commands; + } +} +``` + +Then compile this little project: + +``` +mvn clean install +``` + +Write a configuration file telling James to use your `HandlerPackage`: + +``` + + + smtpserver-global + 0.0.0.0:25 + 200 + + file://conf/keystore + james72laBalle + org.bouncycastle.jce.provider.BouncyCastleProvider + SunX509 + + + + + + + +``` + +Create a keystore (default password being `james72laBalle`): + +``` +keytool -genkey -alias james -keyalg RSA -keystore keystore +``` + +Then start a James server with your JAR and the configuration: + +``` +docker run -d \ + -v $PWD/smtpserver.xml:/root/conf/smtpserver.xml \ + -v $PWD/exts:/root/extensions-jars \ + -v $PWD/keystore:/root/conf/keystore \ + -p 25:25 \ + apache/james:memory-latest +``` + +You can play with `telnet` utility with the resulting server: + +``` +$ telnet 127.0.0.1 25 +Trying 127.0.0.1... +Connected to 127.0.0.1. +Escape character is '^]'. +220 Apache JAMES awesome SMTP Server +MYNOOP +250 2.0.0 OK +quit +221 2.0.0 1f0274082fc6 Service closing transmission channel +Connection closed by foreign host. +``` diff --git a/examples/custom-smtp-command/pom.xml b/examples/custom-smtp-command/pom.xml new file mode 100644 index 00000000000..3152b0c2c9a --- /dev/null +++ b/examples/custom-smtp-command/pom.xml @@ -0,0 +1,53 @@ + + + + 4.0.0 + + org.apache.james.examples + examples + 3.7.0-SNAPSHOT + + + custom-smtp-command + + Apache James :: Examples :: Custom SMTP Command + Example of how to extend James existing SMTP implementation by adding your own commands + + + + ${james.groupId} + james-server-protocols-smtp + ${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + \ No newline at end of file diff --git a/examples/custom-smtp-command/src/main/java/org/apache/james/examples/MyCmdHandlerLoader.java b/examples/custom-smtp-command/src/main/java/org/apache/james/examples/MyCmdHandlerLoader.java new file mode 100644 index 00000000000..d0ddcc71518 --- /dev/null +++ b/examples/custom-smtp-command/src/main/java/org/apache/james/examples/MyCmdHandlerLoader.java @@ -0,0 +1,101 @@ +/**************************************************************** + * 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.examples; + +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.smtp.core.ExpnCmdHandler; +import org.apache.james.protocols.smtp.core.HeloCmdHandler; +import org.apache.james.protocols.smtp.core.HelpCmdHandler; +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.UnknownCmdHandler; +import org.apache.james.protocols.smtp.core.VrfyCmdHandler; +import org.apache.james.protocols.smtp.core.esmtp.AuthCmdHandler; +import org.apache.james.protocols.smtp.core.esmtp.EhloCmdHandler; +import org.apache.james.protocols.smtp.core.esmtp.MailSizeEsmtpExtension; +import org.apache.james.protocols.smtp.core.esmtp.StartTlsCmdHandler; +import org.apache.james.protocols.smtp.core.log.HookResultLogger; +import org.apache.james.smtpserver.AddDefaultAttributesMessageHook; +import org.apache.james.smtpserver.AuthRequiredToRelayRcptHook; +import org.apache.james.smtpserver.DataLineJamesMessageHookHandler; +import org.apache.james.smtpserver.JamesDataCmdHandler; +import org.apache.james.smtpserver.JamesMailCmdHandler; +import org.apache.james.smtpserver.JamesRcptCmdHandler; +import org.apache.james.smtpserver.JamesWelcomeMessageHandler; +import org.apache.james.smtpserver.SendMailHandler; +import org.apache.james.smtpserver.SenderAuthIdentifyVerificationRcptHook; +import org.apache.james.smtpserver.UsersRepositoryAuthHook; + +/** + * This class copies CoreCmdHandlerLoader adding support for MYNOOP command + */ +public class MyCmdHandlerLoader implements HandlersPackage { + + private final List commands = new LinkedList<>(); + + public MyCmdHandlerLoader() { + Stream.of( + JamesWelcomeMessageHandler.class, + CommandDispatcher.class, + AuthCmdHandler.class, + JamesDataCmdHandler.class, + EhloCmdHandler.class, + ExpnCmdHandler.class, + HeloCmdHandler.class, + HelpCmdHandler.class, + JamesMailCmdHandler.class, + NoopCmdHandler.class, + QuitCmdHandler.class, + JamesRcptCmdHandler.class, + RsetCmdHandler.class, + VrfyCmdHandler.class, + MailSizeEsmtpExtension.class, + UsersRepositoryAuthHook.class, + AuthRequiredToRelayRcptHook.class, + SenderAuthIdentifyVerificationRcptHook.class, + PostmasterAbuseRcptHook.class, + ReceivedDataLineFilter.class, + DataLineJamesMessageHookHandler.class, + StartTlsCmdHandler.class, + AddDefaultAttributesMessageHook.class, + SendMailHandler.class, + UnknownCmdHandler.class, + CommandHandlerResultLogger.class, + HookResultLogger.class, + // Support MYNOOP + MyNoopCmdHandler.class) + .map(Class::getName) + .forEachOrdered(commands::add); + } + + @Override + public List getHandlers() { + return commands; + } +} diff --git a/examples/custom-smtp-command/src/main/java/org/apache/james/examples/MyNoopCmdHandler.java b/examples/custom-smtp-command/src/main/java/org/apache/james/examples/MyNoopCmdHandler.java new file mode 100644 index 00000000000..7d2cdf41069 --- /dev/null +++ b/examples/custom-smtp-command/src/main/java/org/apache/james/examples/MyNoopCmdHandler.java @@ -0,0 +1,53 @@ +/**************************************************************** + * 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.examples; + +import java.util.Collection; + +import org.apache.james.protocols.api.Request; +import org.apache.james.protocols.api.Response; +import org.apache.james.protocols.api.handler.CommandHandler; +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.dsn.DSNStatus; + +import com.google.common.collect.ImmutableSet; + +/** + * Copy of NoopCmdHandler + */ +public class MyNoopCmdHandler implements CommandHandler { + private static final Collection COMMANDS = ImmutableSet.of("MYNOOP"); + + private static final Response NOOP = new SMTPResponse(SMTPRetCode.MAIL_OK, + DSNStatus.getStatus(DSNStatus.SUCCESS, DSNStatus.UNDEFINED_STATUS) + " OK") + .immutable(); + + @Override + public Response onCommand(SMTPSession session, Request request) { + return NOOP; + } + + @Override + public Collection getImplCommands() { + return COMMANDS; + } +} diff --git a/examples/custom-smtp-command/src/main/resources/smtpserver.xml b/examples/custom-smtp-command/src/main/resources/smtpserver.xml new file mode 100644 index 00000000000..504440a1a64 --- /dev/null +++ b/examples/custom-smtp-command/src/main/resources/smtpserver.xml @@ -0,0 +1,50 @@ + + + + + + + + + smtpserver-global + 0.0.0.0:25 + 200 + + file://conf/keystore + james72laBalle + org.bouncycastle.jce.provider.BouncyCastleProvider + SunX509 + + 360 + 0 + 0 + false + 127.0.0.0/8 + false + 0 + true + Apache JAMES awesome SMTP Server + + + + + + + diff --git a/examples/pom.xml b/examples/pom.xml index 955a661790e..94c7c877e19 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -33,6 +33,7 @@ custom-listeners custom-mailets + custom-smtp-command custom-smtp-hooks From b24b27000442a62d922fd2dee919d59ec3ad4ef7 Mon Sep 17 00:00:00 2001 From: Benoit Tellier Date: Fri, 30 Jul 2021 15:03:45 +0700 Subject: [PATCH 06/11] JAMES-2435 Website how to: creating custom SMTP commands --- src/homepage/howTo/custom-smtp-commands.html | 184 +++++++++++++++++++ src/homepage/howTo/index.html | 10 +- 2 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 src/homepage/howTo/custom-smtp-commands.html diff --git a/src/homepage/howTo/custom-smtp-commands.html b/src/homepage/howTo/custom-smtp-commands.html new file mode 100644 index 00000000000..b557ce2ffd1 --- /dev/null +++ b/src/homepage/howTo/custom-smtp-commands.html @@ -0,0 +1,184 @@ +--- +layout: howTo +--- + + + +
+ + +
+
+
+
+

Configure Custom SMTP commands

+
+ +

+ The current project demonstrates how to write custom commands for Apache James SMTP server. +

+ +

+ Find this example on GitHub. +

+ +

+ Start by importing the dependencies: +

+ +
<dependency>
+    <groupId>org.apache.james</groupId>
+    <artifactId>james-server-protocols-smtp</artifactId>
+</dependency>
+                
+ +

You can write your commands by extending the CommandHandler<SMTPSession> class. For instance:

+ +
/**
+  * Copy of NoopCmdHandler
+  */
+public class MyNoopCmdHandler implements CommandHandler<SMTPSession> {
+    private static final Collection<String> COMMANDS = ImmutableSet.of("MYNOOP");
+
+    private static final Response NOOP = new SMTPResponse(SMTPRetCode.MAIL_OK,
+        DSNStatus.getStatus(DSNStatus.SUCCESS, DSNStatus.UNDEFINED_STATUS) + " OK")
+        .immutable();
+
+    @Override
+    public Response onCommand(SMTPSession session, Request request) {
+        return NOOP;
+    }
+
+    @Override
+    public Collection<String> getImplCommands() {
+        return COMMANDS;
+    }
+}
+ +

You then need to list the exposed SMTP commands with a HandlersPackage. For instance:

+ +
/**
+ * This class copies CoreCmdHandlerLoader adding support for MYNOOP command
+ */
+public class MyCmdHandlerLoader implements HandlersPackage {
+
+    private final List<String> commands = new LinkedList<>();
+
+    public MyCmdHandlerLoader() {
+        Stream.of(
+            JamesWelcomeMessageHandler.class,
+            CommandDispatcher.class,
+            AuthCmdHandler.class,
+            JamesDataCmdHandler.class,
+            EhloCmdHandler.class,
+            ExpnCmdHandler.class,
+            HeloCmdHandler.class,
+            HelpCmdHandler.class,
+            JamesMailCmdHandler.class,
+            NoopCmdHandler.class,
+            QuitCmdHandler.class,
+            JamesRcptCmdHandler.class,
+            RsetCmdHandler.class,
+            VrfyCmdHandler.class,
+            MailSizeEsmtpExtension.class,
+            UsersRepositoryAuthHook.class,
+            AuthRequiredToRelayRcptHook.class,
+            SenderAuthIdentifyVerificationRcptHook.class,
+            PostmasterAbuseRcptHook.class,
+            ReceivedDataLineFilter.class,
+            DataLineJamesMessageHookHandler.class,
+            StartTlsCmdHandler.class,
+            AddDefaultAttributesMessageHook.class,
+            SendMailHandler.class,
+            UnknownCmdHandler.class,
+            CommandHandlerResultLogger.class,
+            HookResultLogger.class,
+            // Support MYNOOP
+            MyNoopCmdHandler.class)
+        .map(Class::getName)
+        .forEachOrdered(commands::add);
+    }
+
+    @Override
+    public List<String> getHandlers() {
+        return commands;
+    }
+}
+ +

You can compile this example project:

+ +
mvn clean install
+ +

Write a configuration file telling James to use your HandlerPackage:

+ +
<smtpservers>
+    <smtpserver enabled="true">
+        <jmxName>smtpserver-global</jmxName>
+        <bind>0.0.0.0:25</bind>
+        <connectionBacklog>200</connectionBacklog>
+        <tls socketTLS="false" startTLS="false">
+            <keystore>file://conf/keystore</keystore>
+            <secret>james72laBalle</secret>
+            <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider>
+            <algorithm>SunX509</algorithm>
+        </tls>
+        <!-- ... -->
+        <handlerchain coreHandlersPackage="org.apache.james.examples.MyCmdHandlerLoader">
+            <handler class="org.apache.james.smtpserver.fastfail.ValidRcptHandler"/>
+        </handlerchain>
+    </smtpserver>
+</smtpservers>
+ +

Create a keystore (default password being james72laBalle):

+ +
keytool -genkey -alias james -keyalg RSA -keystore keystore
+ +

Then start a James server with your JAR and the configuration:

+ +
docker run -d \
+  -v $PWD/smtpserver.xml:/root/conf/smtpserver.xml \
+  -v $PWD/exts:/root/extensions-jars \
+  -v $PWD/keystore:/root/conf/keystore \
+  -p 25:25 \
+  apache/james:memory-latest
+                
+ +

You can play with telnet utility with the resulting server and use the MYNOOP command:

+ +
$ $ telnet 127.0.0.1 25
+Trying 127.0.0.1...
+Connected to 127.0.0.1.
+Escape character is '^]'.
+220 Apache JAMES awesome SMTP Server
+MYNOOP
+250 2.0.0 OK
+quit
+221 2.0.0 1f0274082fc6 Service closing transmission channel
+Connection closed by foreign host.
+
+ +
+
+ +
diff --git a/src/homepage/howTo/index.html b/src/homepage/howTo/index.html index 0cff52bbf01..3b00283bf01 100644 --- a/src/homepage/howTo/index.html +++ b/src/homepage/howTo/index.html @@ -58,7 +58,7 @@

James how to's...

Customize James

-

This section will show you how to modify James to use it in your purpose

+

This section will show you how to modify James to use it in your purpose, and embed your own code.

Customize James class="james-schema" > Configure Custom SMTP hooks +
+ + Configure Custom SMTP commands +

From d98a15888d3d07694501f8e2de8ce1d8a7a8b8b7 Mon Sep 17 00:00:00 2001 From: Benoit Tellier Date: Fri, 30 Jul 2021 15:32:48 +0700 Subject: [PATCH 07/11] JAMES-2435 Example: creating custom WebAdmin routes --- examples/custom-webadmin-route/README.md | 79 +++++++++++++++++++ examples/custom-webadmin-route/pom.xml | 53 +++++++++++++ .../org/apache/james/examples/RouteA.java | 37 +++++++++ .../src/main/resources/webadmin.properties | 25 ++++++ examples/pom.xml | 16 ++-- 5 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 examples/custom-webadmin-route/README.md create mode 100644 examples/custom-webadmin-route/pom.xml create mode 100644 examples/custom-webadmin-route/src/main/java/org/apache/james/examples/RouteA.java create mode 100644 examples/custom-webadmin-route/src/main/resources/webadmin.properties diff --git a/examples/custom-webadmin-route/README.md b/examples/custom-webadmin-route/README.md new file mode 100644 index 00000000000..eba5a849d62 --- /dev/null +++ b/examples/custom-webadmin-route/README.md @@ -0,0 +1,79 @@ +# Writing custom webadmin routes + +Read this page [on the website](http://james.apache.org/howTo/custom-webadmin-routes.html). + +The current project demonstrates how to write custom webadmin routes for Apache James. This enables writing new +administrative features exposed over a REST API. This can allow you to write some additional features, make James +interact with third party systems, do advance reporting... + +Start by importing the dependencies: + +``` + + org.apache.james + james-server-webadmin-core + +``` + +You can then write your first route using the [Spark Java](https://sparkjava.com/) framework: + +``` +public class RouteA implements Routes { + @Override + public String getBasePath() { + return "/hello/a"; + } + + @Override + public void define(Service service) { + service.get(getBasePath(), (req, res) -> "RouteA\n"); + } +} +``` + +Knowing that: + - entending `Routes` will ensure that authentication is requested if configured. + - extending `PublicRoutes` will not request authentication. + +You can compile this example project: + +``` +mvn clean install +``` + +Then embed your route into a James server. First configure your route into `webadmin.properties`: + +``` +enabled=true +port=8000 +host=localhost + +# List of fully qualified class names that should be exposed over webadmin +# in addition to your product default routes. Routes needs to be located +# within the classpath or in the ./extensions-jars folder. +extensions.routes=org.apache.james.examples.RouteA +``` + +Create a keystore (default password being `james72laBalle`): + +``` +keytool -genkey -alias james -keyalg RSA -keystore keystore +``` + +Then start a James server with your JAR and the configuration: + +``` +$ docker run -d \ + -v $PWD/webadmin.properties:/root/conf/webadmin.properties \ + -v $PWD/exts:/root/extensions-jars \ + -v $PWD/keystore:/root/conf/keystore \ + -p 25:25 \ + apache/james:memory-latest +``` + +You can play with `curl` utility with the resulting server: + +``` +$ curl -XGET http://172.17.0.2:8000/hello/a +RouteA +``` \ No newline at end of file diff --git a/examples/custom-webadmin-route/pom.xml b/examples/custom-webadmin-route/pom.xml new file mode 100644 index 00000000000..7ed2260bc22 --- /dev/null +++ b/examples/custom-webadmin-route/pom.xml @@ -0,0 +1,53 @@ + + + + 4.0.0 + + org.apache.james.examples + examples + 3.7.0-SNAPSHOT + + + custom-webadmin-route + + Apache James :: Examples :: Custom WebAdmin route + Example of how to extend James by adding your own webadmin routes + + + + ${james.groupId} + james-server-webadmin-core + ${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + \ No newline at end of file diff --git a/examples/custom-webadmin-route/src/main/java/org/apache/james/examples/RouteA.java b/examples/custom-webadmin-route/src/main/java/org/apache/james/examples/RouteA.java new file mode 100644 index 00000000000..e23e7d4d5d2 --- /dev/null +++ b/examples/custom-webadmin-route/src/main/java/org/apache/james/examples/RouteA.java @@ -0,0 +1,37 @@ +/**************************************************************** + * 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.examples; + + +import org.apache.james.webadmin.Routes; + +import spark.Service; + +public class RouteA implements Routes { + @Override + public String getBasePath() { + return "/hello/a"; + } + + @Override + public void define(Service service) { + service.get(getBasePath(), (req, res) -> "RouteA\n"); + } +} diff --git a/examples/custom-webadmin-route/src/main/resources/webadmin.properties b/examples/custom-webadmin-route/src/main/resources/webadmin.properties new file mode 100644 index 00000000000..d19b4e5f2a2 --- /dev/null +++ b/examples/custom-webadmin-route/src/main/resources/webadmin.properties @@ -0,0 +1,25 @@ +# 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. + +enabled=true +port=8000 +host=localhost + +# List of fully qualified class names that should be exposed over webadmin +# in addition to your product default routes. Routes needs to be located +# within the classpath or in the ./extensions-jars folder. +extensions.routes=org.apache.james.examples.RouteA \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml index 94c7c877e19..d906580b39e 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -25,21 +25,25 @@ 23 + org.apache.james.examples examples 3.7.0-SNAPSHOT pom Apache James :: Examples - - custom-listeners - custom-mailets - custom-smtp-command - custom-smtp-hooks - + org.apache.james ${project.version} 1.11 1.11 + + + custom-listeners + custom-mailets + custom-smtp-command + custom-smtp-hooks + custom-webadmin-route + From e7e4cd32964ab5319701498bce2b52baa7523994 Mon Sep 17 00:00:00 2001 From: Benoit Tellier Date: Fri, 30 Jul 2021 15:50:30 +0700 Subject: [PATCH 08/11] JAMES-2435 WebSite how to: creating custom WebAdmin routes --- .../howTo/custom-webadmin-routes.html | 114 ++++++++++++++++++ src/homepage/howTo/index.html | 7 ++ 2 files changed, 121 insertions(+) create mode 100644 src/homepage/howTo/custom-webadmin-routes.html diff --git a/src/homepage/howTo/custom-webadmin-routes.html b/src/homepage/howTo/custom-webadmin-routes.html new file mode 100644 index 00000000000..7f48c71a1e6 --- /dev/null +++ b/src/homepage/howTo/custom-webadmin-routes.html @@ -0,0 +1,114 @@ +--- +layout: howTo +--- + + + +
+ + +
+
+
+
+

Configure Custom WebAdmin routes

+
+ +

+ The current project demonstrates how to write custom webadmin routes for Apache James. This enables writing new + administrative features exposed over a REST API. This can allow you to write some additional features, make James + interact with third party systems, do advance reporting... +

+ +

+ Find this example on GitHub. +

+ +

+ Start by importing the dependencies: +

+ +
<dependency>
+    <groupId>org.apache.james</groupId>
+    <artifactId>james-server-webadmin-core</artifactId>
+</dependency>
+                
+ +

You can then write your first route using the Spark Java framework:

+ +
public class RouteA implements Routes {
+    @Override
+    public String getBasePath() {
+        return "/hello/a";
+    }
+
+    @Override
+    public void define(Service service) {
+        service.get(getBasePath(), (req, res) -> "RouteA\n");
+    }
+}
+ +
    Knowing that: +
  • entending Routes: will ensure that authentication is requested if configured.
  • +
  • entending PublicRoutes: will not request authentication.
  • +
+ +

You can compile this example project:

+ +
mvn clean install
+ +

Then embed your route into a James server. First configure your route into webadmin.properties:

+ +
enabled=true
+port=8000
+host=localhost
+
+# List of fully qualified class names that should be exposed over webadmin
+# in addition to your product default routes. Routes needs to be located
+# within the classpath or in the ./extensions-jars folder.
+extensions.routes=org.apache.james.examples.RouteA
+ +

Create a keystore (default password being james72laBalle):

+ +
keytool -genkey -alias james -keyalg RSA -keystore keystore
+ +

Then start a James server with your JAR and the configuration:

+ +
docker run -d \
+   -v $PWD/webadmin.properties:/root/conf/webadmin.properties \
+   -v $PWD/exts:/root/extensions-jars \
+   -v $PWD/keystore:/root/conf/keystore \
+   -p 25:25 \
+   apache/james:memory-latest
+ +

You can play with curl utility with the resulting server:

+ +
$ curl -XGET http://172.17.0.2:8000/hello/a
+RouteA
+
+ +
+
+ +
diff --git a/src/homepage/howTo/index.html b/src/homepage/howTo/index.html index 3b00283bf01..04250dcbf45 100644 --- a/src/homepage/howTo/index.html +++ b/src/homepage/howTo/index.html @@ -88,6 +88,13 @@

Customize James

class="james-schema" > Configure Custom SMTP commands + + Configure Custom WebAdmin routes +

From 572de96a567399121788db20e10aa722f3b68765 Mon Sep 17 00:00:00 2001 From: Benoit Tellier Date: Fri, 30 Jul 2021 16:41:40 +0700 Subject: [PATCH 09/11] JAMES-2435 Example: Custom James server assembly --- examples/custom-james-assembly/README.md | 154 ++++++++++++++++++ examples/custom-james-assembly/pom.xml | 98 +++++++++++ .../sample-configuration/imapserver.xml | 44 +++++ .../sample-configuration/logback.xml | 58 +++++++ .../sample-configuration/mailetcontainer.xml | 153 +++++++++++++++++ .../mailrepositorystore.xml | 33 ++++ .../sample-configuration/smtpserver.xml | 51 ++++++ .../james/examples/CustomJamesServerMain.java | 74 +++++++++ examples/pom.xml | 1 + 9 files changed, 666 insertions(+) create mode 100644 examples/custom-james-assembly/README.md create mode 100644 examples/custom-james-assembly/pom.xml create mode 100644 examples/custom-james-assembly/sample-configuration/imapserver.xml create mode 100644 examples/custom-james-assembly/sample-configuration/logback.xml create mode 100644 examples/custom-james-assembly/sample-configuration/mailetcontainer.xml create mode 100644 examples/custom-james-assembly/sample-configuration/mailrepositorystore.xml create mode 100644 examples/custom-james-assembly/sample-configuration/smtpserver.xml create mode 100644 examples/custom-james-assembly/src/main/java/org/apache/james/examples/CustomJamesServerMain.java diff --git a/examples/custom-james-assembly/README.md b/examples/custom-james-assembly/README.md new file mode 100644 index 00000000000..c26525e053b --- /dev/null +++ b/examples/custom-james-assembly/README.md @@ -0,0 +1,154 @@ +# Assemble a James server tailored to your needs + +Read this page [on the website](http://james.apache.org/howTo/custom-james-assembly.html). + +The current project demonstrates how to write a custom assembly in order to write your +own tailor-made server. + +This enables: + + - Arbitrary composition of technologies (example JPA mailbox with Cassandra user management) + - Write any additional components + - Drop any unneeded component + - You have control on the dependencies and can reduce the classpath size + +## Example: Write an IMAP+SMTP only memory server + +In order to do this select the modules you wished to assemble [in the Guice building blocks](https://github.com/apache/james-project/tree/master/server/container/guice). We encourage you to have +a fine grain control of your dependencies but for the sake of simplicity this example will reuse the dependencies of an +existing James application: + +``` + + ${james.groupId} + james-server-memory-app + ${project.version} + +``` + +Once done assemble the guice modules together in a class implementing `JamesServerMain`: + +``` +public class CustomJamesServerMain implements JamesServerMain { + public static final Module PROTOCOLS = Modules.combine( + new IMAPServerModule(), + new ProtocolHandlerModule(), + new MailRepositoryTaskSerializationModule(), + new SMTPServerModule()); + + public static final Module CUSTOM_SERVER_MODULE = Modules.combine( + new MailetProcessingModule(), + new MailboxModule(), + new MemoryDataModule(), + new MemoryEventStoreModule(), + new MemoryMailboxModule(), + new MemoryMailQueueModule(), + new TaskManagerModule(), + new RawPostDequeueDecoratorModule(), + binder -> binder.bind(MailetContainerModule.DefaultProcessorsConfigurationSupplier.class) + .toInstance(BaseHierarchicalConfiguration::new)); + + public static final Module CUSTOM_SERVER_AGGREGATE_MODULE = Modules.combine( + CUSTOM_SERVER_MODULE, + PROTOCOLS); + + public static void main(String[] args) throws Exception { + Configuration configuration = Configuration.builder() + .useWorkingDirectoryEnvProperty() + .build(); + + JamesServerMain.main(GuiceJamesServer.forConfiguration(configuration) + .combineWith(CUSTOM_SERVER_AGGREGATE_MODULE)); + } + } +``` + +You need to write a minimal main method launching your guice module composition. + +We do provide in this example [JIB](https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin) to package this custom James assembly into docker: + +``` + + com.google.cloud.tools + jib-maven-plugin + 2.7.1 + + + adoptopenjdk:11-jdk-hotspot + + + apache/james + + custom-latest + + + + org.apache.james.examples.CustomJamesServerMain + + 25 + 143 + + /root + + -Dlogback.configurationFile=/root/conf/logback.xml + -Dworking.directory=/root/ + + USE_CURRENT_TIMESTAMP + + + + + sample-configuration + /root/conf + + + + + + + + buildTar + + package + + + +``` + +We provide a minimal [sample configuration](https://github.com/apache/james-project/tree/master/examples/custom-james-assembly/sample-configuration). + +You can compile this example project: + +``` +mvn clean install +``` + +Create a keystore (default password being `james72laBalle`): + +``` +keytool -genkey -alias james -keyalg RSA -keystore keystore +``` + +Import the build result: + +``` +$ docker load -i target/jib-image.tar +``` + +Then launch your custom server with docker: + +``` +docker run \ + -v $PWD/keystore:/root/conf/keystore \ + -p 25:25 \ + -p 143:143 \ + -ti \ + apache/james:custom-latest +``` + +You will see that your custom James server starts smoothly: + +``` +... +09:40:25.884 [INFO ] o.a.j.GuiceJamesServer - JAMES server started +``` diff --git a/examples/custom-james-assembly/pom.xml b/examples/custom-james-assembly/pom.xml new file mode 100644 index 00000000000..7e1059c3665 --- /dev/null +++ b/examples/custom-james-assembly/pom.xml @@ -0,0 +1,98 @@ + + + + 4.0.0 + + org.apache.james.examples + examples + 3.7.0-SNAPSHOT + + + custom-james-assembly + + Apache James :: Examples :: Custom James server assembly + Assemble your own James server tailored to your needs. + + + + ${james.groupId} + james-server-memory-app + ${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + com.google.cloud.tools + jib-maven-plugin + 2.7.1 + + + adoptopenjdk:11-jdk-hotspot + + + apache/james + + custom-latest + + + + org.apache.james.examples.CustomJamesServerMain + + 25 + 143 + + /root + + -Dlogback.configurationFile=/root/conf/logback.xml + -Dworking.directory=/root/ + + USE_CURRENT_TIMESTAMP + + + + + sample-configuration + /root/conf + + + + + + + + buildTar + + package + + + + + + \ No newline at end of file diff --git a/examples/custom-james-assembly/sample-configuration/imapserver.xml b/examples/custom-james-assembly/sample-configuration/imapserver.xml new file mode 100644 index 00000000000..552431106a6 --- /dev/null +++ b/examples/custom-james-assembly/sample-configuration/imapserver.xml @@ -0,0 +1,44 @@ + + + + + + + + + + imapserver + 0.0.0.0:143 + 200 + + + file://conf/keystore + james72laBalle + org.bouncycastle.jce.provider.BouncyCastleProvider + + 0 + 0 + 120 + SECONDS + true + + diff --git a/examples/custom-james-assembly/sample-configuration/logback.xml b/examples/custom-james-assembly/sample-configuration/logback.xml new file mode 100644 index 00000000000..5af37af521b --- /dev/null +++ b/examples/custom-james-assembly/sample-configuration/logback.xml @@ -0,0 +1,58 @@ + + + + + + + true + + + + + %d{HH:mm:ss.SSS} %highlight([%-5level]) %logger{15} - %msg%n%rEx + false + + + + + /logs/james.log + + /logs/james.%i.log.tar.gz + 1 + 3 + + + + 100MB + + + + %d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx + false + + + + + + + + + + diff --git a/examples/custom-james-assembly/sample-configuration/mailetcontainer.xml b/examples/custom-james-assembly/sample-configuration/mailetcontainer.xml new file mode 100644 index 00000000000..5d247dcbb58 --- /dev/null +++ b/examples/custom-james-assembly/sample-configuration/mailetcontainer.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + postmaster + + + + 20 + memory://var/mail/error/ + + + + + + + + transport + + + + + + mailetContainerErrors + + + ignore + + + memory://var/mail/error/ + propagate + + + + + + + + + + + + bcc + ignore + + + rrt-error + + + local-delivery + + + local-address-error + 550 - Requested action not taken: no such user here + + + relay + + + relay-denied + + + + + + + + + + + + outgoing + 5000, 100000, 500000 + 3 + 0 + 10 + true + bounces + + + + + + mailetContainerLocalAddressError + + + none + + + memory://var/mail/address-error/ + + + + + + mailetContainerRelayDenied + + + none + + + memory://var/mail/relay-denied/ + Warning: You are sending an e-mail to a remote server. You must be authenticated to perform such an operation + + + + + + bounces + + + false + + + + + + memory://var/mail/rrt-error/ + true + + + + + + + + + + diff --git a/examples/custom-james-assembly/sample-configuration/mailrepositorystore.xml b/examples/custom-james-assembly/sample-configuration/mailrepositorystore.xml new file mode 100644 index 00000000000..c7f22b97c77 --- /dev/null +++ b/examples/custom-james-assembly/sample-configuration/mailrepositorystore.xml @@ -0,0 +1,33 @@ + + + + + + + + memory + + + + memory + + + + diff --git a/examples/custom-james-assembly/sample-configuration/smtpserver.xml b/examples/custom-james-assembly/sample-configuration/smtpserver.xml new file mode 100644 index 00000000000..558593098bf --- /dev/null +++ b/examples/custom-james-assembly/sample-configuration/smtpserver.xml @@ -0,0 +1,51 @@ + + + + + + + + + smtpserver-global + 0.0.0.0:25 + 200 + + file://conf/keystore + james72laBalle + org.bouncycastle.jce.provider.BouncyCastleProvider + SunX509 + + 360 + 0 + 0 + false + 127.0.0.0/8 + false + 0 + true + Apache JAMES awesome SMTP Server + + + + + + + + diff --git a/examples/custom-james-assembly/src/main/java/org/apache/james/examples/CustomJamesServerMain.java b/examples/custom-james-assembly/src/main/java/org/apache/james/examples/CustomJamesServerMain.java new file mode 100644 index 00000000000..08f537f4e07 --- /dev/null +++ b/examples/custom-james-assembly/src/main/java/org/apache/james/examples/CustomJamesServerMain.java @@ -0,0 +1,74 @@ +/**************************************************************** + * 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.examples; + +import org.apache.commons.configuration2.BaseHierarchicalConfiguration; +import org.apache.james.GuiceJamesServer; +import org.apache.james.JamesServerMain; +import org.apache.james.modules.MailboxModule; +import org.apache.james.modules.MailetProcessingModule; +import org.apache.james.modules.data.MemoryDataModule; +import org.apache.james.modules.eventstore.MemoryEventStoreModule; +import org.apache.james.modules.mailbox.MemoryMailboxModule; +import org.apache.james.modules.protocols.IMAPServerModule; +import org.apache.james.modules.protocols.ProtocolHandlerModule; +import org.apache.james.modules.protocols.SMTPServerModule; +import org.apache.james.modules.queue.memory.MemoryMailQueueModule; +import org.apache.james.modules.server.MailRepositoryTaskSerializationModule; +import org.apache.james.modules.server.MailetContainerModule; +import org.apache.james.modules.server.RawPostDequeueDecoratorModule; +import org.apache.james.modules.server.TaskManagerModule; +import org.apache.james.server.core.configuration.Configuration; + +import com.google.inject.Module; +import com.google.inject.util.Modules; + +public class CustomJamesServerMain implements JamesServerMain { + public static final Module PROTOCOLS = Modules.combine( + new IMAPServerModule(), + new ProtocolHandlerModule(), + new MailRepositoryTaskSerializationModule(), + new SMTPServerModule()); + + public static final Module CUSTOM_SERVER_MODULE = Modules.combine( + new MailetProcessingModule(), + new MailboxModule(), + new MemoryDataModule(), + new MemoryEventStoreModule(), + new MemoryMailboxModule(), + new MemoryMailQueueModule(), + new TaskManagerModule(), + new RawPostDequeueDecoratorModule(), + binder -> binder.bind(MailetContainerModule.DefaultProcessorsConfigurationSupplier.class) + .toInstance(BaseHierarchicalConfiguration::new)); + + public static final Module CUSTOM_SERVER_AGGREGATE_MODULE = Modules.combine( + CUSTOM_SERVER_MODULE, + PROTOCOLS); + + public static void main(String[] args) throws Exception { + Configuration configuration = Configuration.builder() + .useWorkingDirectoryEnvProperty() + .build(); + + JamesServerMain.main(GuiceJamesServer.forConfiguration(configuration) + .combineWith(CUSTOM_SERVER_AGGREGATE_MODULE)); + } +} diff --git a/examples/pom.xml b/examples/pom.xml index d906580b39e..3da8ea48f8c 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -40,6 +40,7 @@
+ custom-james-assembly custom-listeners custom-mailets custom-smtp-command From 22d8f70582d6f334fe29c2f011fbf18aae067c70 Mon Sep 17 00:00:00 2001 From: Benoit Tellier Date: Fri, 30 Jul 2021 16:55:43 +0700 Subject: [PATCH 10/11] JAMES-2435 Website how to: Custom James server assembly --- src/homepage/howTo/custom-james-assembly.html | 190 ++++++++++++++++++ src/homepage/howTo/index.html | 7 + 2 files changed, 197 insertions(+) create mode 100644 src/homepage/howTo/custom-james-assembly.html diff --git a/src/homepage/howTo/custom-james-assembly.html b/src/homepage/howTo/custom-james-assembly.html new file mode 100644 index 00000000000..a278fb8fad1 --- /dev/null +++ b/src/homepage/howTo/custom-james-assembly.html @@ -0,0 +1,190 @@ +--- +layout: howTo +--- + + + +
+ + +
+
+
+
+

Write Custom James server assembly

+
+

+ Find this example on GitHub. +

+ +

+ The current project demonstrates how to write a custom assembly in order to write your + own tailor-made server. +

+ +
    This enables: +
  • Arbitrary composition of technologies (example JPA mailbox with Cassandra user management)
  • +
  • Write any additional components
  • +
  • Drop any unneeded component
  • +
  • You have control on the dependencies and can reduce the classpath size
  • +
+ +
+

Example: Write an IMAP+SMTP only memory server

+
+ +

+ In order to do this select the modules you wished to assemble + in the Guice building blocks. + We encourage you to have a fine grain control of your dependencies but for the sake of simplicity + this example will reuse the dependencies of an existing James application: +

+ +
<dependency>
+    <groupId>${james.groupId}</groupId>
+    <artifactId>james-server-memory-app</artifactId>
+    <version>${project.version}</version>
+</dependency>
+ +

Once done assemble the guice modules together in a class implementing JamesServerMain:

+ +
public class CustomJamesServerMain implements JamesServerMain {
+       public static final Module PROTOCOLS = Modules.combine(
+           new IMAPServerModule(),
+           new ProtocolHandlerModule(),
+           new MailRepositoryTaskSerializationModule(),
+           new SMTPServerModule());
+
+       public static final Module CUSTOM_SERVER_MODULE = Modules.combine(
+           new MailetProcessingModule(),
+           new MailboxModule(),
+           new MemoryDataModule(),
+           new MemoryEventStoreModule(),
+           new MemoryMailboxModule(),
+           new MemoryMailQueueModule(),
+           new TaskManagerModule(),
+           new RawPostDequeueDecoratorModule(),
+           binder -> binder.bind(MailetContainerModule.DefaultProcessorsConfigurationSupplier.class)
+               .toInstance(BaseHierarchicalConfiguration::new));
+
+       public static final Module CUSTOM_SERVER_AGGREGATE_MODULE = Modules.combine(
+           CUSTOM_SERVER_MODULE,
+           PROTOCOLS);
+
+       public static void main(String[] args) throws Exception {
+           Configuration configuration = Configuration.builder()
+               .useWorkingDirectoryEnvProperty()
+               .build();
+
+           JamesServerMain.main(GuiceJamesServer.forConfiguration(configuration)
+               .combineWith(CUSTOM_SERVER_AGGREGATE_MODULE));
+       }
+   }
+ +

You need to write a minimal main method launching your guice module composition.

+ +

We do provide in this example JIB + to package this custom James assembly into docker:

+ +
<plugin>
+    <groupId>com.google.cloud.tools</groupId>
+    <artifactId>jib-maven-plugin</artifactId>
+    <version>2.7.1</version>
+    <configuration>
+        <from>
+            <image>adoptopenjdk:11-jdk-hotspot</image>
+        </from>
+        <to>
+            <image>apache/james</image>
+            <tags>
+                <tag>custom-latest</tag>
+            </tags>
+        </to>
+        <container>
+            <mainClass>org.apache.james.examples.CustomJamesServerMain</mainClass>
+            <ports>
+                <port>25</port> <!-- JMAP -->
+                <port>143</port> <!-- IMAP -->
+            </ports>
+            <appRoot>/root</appRoot>
+            <jvmFlags>
+                <jvmFlag>-Dlogback.configurationFile=/root/conf/logback.xml</jvmFlag>
+                <jvmFlag>-Dworking.directory=/root/</jvmFlag>
+            </jvmFlags>
+            <creationTime>USE_CURRENT_TIMESTAMP</creationTime>
+        </container>
+        <extraDirectories>
+            <paths>
+                <path>
+                    <from>sample-configuration</from>
+                    <into>/root/conf</into>
+                </path>
+            </paths>
+        </extraDirectories>
+    </configuration>
+    <executions>
+        <execution>
+            <goals>
+                <goal>buildTar</goal>
+            </goals>
+            <phase>package</phase>
+        </execution>
+    </executions>
+</plugin>}
+ +

We provide a minimal sample configuration. +

+ +

You can compile this example project:

+ +
mvn clean install
+ +

Create a keystore (default password being james72laBalle):

+ +
keytool -genkey -alias james -keyalg RSA -keystore keystore
+ +

Import the build result:

+ +
$ docker load -i target/jib-image.tar
+ +

Then launch your custom server with docker:

+ +
docker run \
+    -v $PWD/keystore:/root/conf/keystore \
+    -p 25:25 \
+    -p 143:143 \
+    -ti  \
+    apache/james:custom-latest
+                
+ +

You will see that your custom James server starts smoothly:

+ +
...
+09:40:25.884 [INFO ] o.a.j.GuiceJamesServer - JAMES server started
+
+ +
+
+ +
diff --git a/src/homepage/howTo/index.html b/src/homepage/howTo/index.html index 04250dcbf45..e45d1a5966e 100644 --- a/src/homepage/howTo/index.html +++ b/src/homepage/howTo/index.html @@ -95,6 +95,13 @@

Customize James

class="james-schema" > Configure Custom WebAdmin routes + + Write Custom James server assembly +

From f4e82e26da18fc36293cad14a674b275b1338a70 Mon Sep 17 00:00:00 2001 From: Benoit Tellier Date: Fri, 30 Jul 2021 17:05:27 +0700 Subject: [PATCH 11/11] JAMES-2435 Examples: add a comprehensive README.md --- examples/README.md | 61 ++++++++++++++++++++++++++++ examples/custom-smtp-hooks/README.md | 6 +-- 2 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 examples/README.md diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000000..48017a4ca23 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,61 @@ +# Examples + +This submodule shows how one can work with James. + +Each subprojects illustrate a specific concept. + +## How to customize mail processing + +At the heart of James lies the Mailet container, which allows mail processing. This is splitted into smaller units, with specific responsibilities: + + - `Mailets`: Are operations performed with the mail: modifying it, performing a side-effect, etc... + - `Matchers`: Are per-recipient conditions for mailet executions + - `Processors`: Are matcher/mailet pair execution threads + +Once we define the mailet container content through the mailetcontailer.xml file. Hence, we can arrange James standard +components to achieve basic logic. But what if our goals are more complex? What if we need our own processing components? + +[This example](custom-mailets) shows how to write such components! + +## Configure Custom Mailbox Listeners + +Mailbox Listener is a component in James Mailbox System. Each time an action is applied on a mailbox(adding, deleting), + or on an email(adding, deleting, updating flags...), then an event representing that action is generated and delivered + to all the Listeners that had been registered before. After receiving events, listeners retrieve information from the + events then execute their business (Indexing emails in ElasticSearch, updating quota of users, detecting spam emails...) + +**Mailbox Listeners** allow customizing the behaviour of James used as a Mail Delivery Agent (MDA). + +[This example](custom-listeners) shows how to write such components! + +## Configure Custom SMTP hooks + +SMTP hooks allow integrating third party systems with the SMTP stack, allows writing additional SMTP extensions, for +instance. + +[This example](custom-smtp-hooks) shows how to write such components! + +## Configure Custom SMTP commands + +This subproject demonstrates how to write custom commands for Apache James SMTP server. + +[This example](custom-smtp-command) shows how to write additional SMTP commands! + +## Configure Custom WebAdmin routes + +The current project demonstrates how to write custom webadmin routes for Apache James. This enables writing new +administrative features exposed over a REST API. This can allow you to write some additional features, make James +interact with third party systems, do advance reporting... + +[This example](custom-webadmin-route) shows how to write additional Webadmin routes! + +## Write Custom James server assembly + +[This example](custom-james-assembly) demonstrates how to write a custom assembly in order to write your own tailor-made server. + +This enables: + + - Arbitrary composition of technologies (example JPA mailbox with Cassandra user management) + - Write any additional components + - Drop any unneeded component + - You have control on the dependencies and can reduce the classpath size diff --git a/examples/custom-smtp-hooks/README.md b/examples/custom-smtp-hooks/README.md index dfe5f703eae..dc2ff097091 100644 --- a/examples/custom-smtp-hooks/README.md +++ b/examples/custom-smtp-hooks/README.md @@ -2,10 +2,10 @@ Read this page [on the website](http://james.apache.org/howTo/custom-smtp-hooks.html). -The current project demonstrate how to write custom behaviours for Apache James SMTP server +The current project demonstrates how to write custom behaviours for Apache James SMTP server by the means of SMTP hooks. -SMTP hooks allows integrating third party systems with the SMTP stack, allows writing additional SMTP extensions, for +SMTP hooks allow integrating third party systems with the SMTP stack and writing additional SMTP extensions, for instance. Start by importing the dependencies: @@ -138,4 +138,4 @@ Now looking at the logs we can verify that our RCPT have well been executed: RCPT TO c@d.comwith parameters {TO:=, =} ``` -Note that hooks can also be written for the LMTP protocol. \ No newline at end of file +Note that hooks can also be written for the LMTP protocol.