The Apache Software Foundation

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,
            SenderAuthIdentifyVerificationHook.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>

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 \
  -p 25:25 \
  apache/james:memory-latest --generate-keystore
                

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.