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.