Java implementation of the Sendmail Milter protocol. The details of the Milter protocol can be found here. An introduction of using milter functionality with Postfix is available in the Postfix before-queue Milter support article.
Milter side
// indicates what changes you intend to do with messages
Actions milterActions = Actions.builder()
.addHeader()
.build();
// indicates which steps you want to skip
ProtocolSteps milterProtocolSteps = ProtocolSteps.builder()
.noHelo()
.noData()
.noBody()
.build();
// gateway address
InetSocketAddress address = NetUtils.parseAddress(System.getProperty("jmilter.address", "0.0.0.0:4545"));
ServerFactory<InetSocketAddress> serverFactory = ServerFactory.tcpIpFactory(address);
// a simple milter handler that only adds header "X-Received"
MilterHandler milterHandler = new AddHeaderMilterHandler(milterActions, milterProtocolSteps);
MilterGatewayManager<InetSocketAddress> gatewayManager = new MilterGatewayManager<>(serverFactory, milterHandler);
gatewayManager.bind();
MTA side
CompletableFuture<Void> call(MilterSessionFactory factory) {
Macros connectionMacros = Macros.builder()
.add("j", "mx.example.org")
.add("{daemon_name}", "mx.example.org")
.add("v", "Postfix 2.10.1")
.build();
Macros fromMacros = Macros.builder()
.add("{mail_mailer}", "smtp")
.add("{mail_host}", "mx1.example.org")
.add("{mail_addr}", "[email protected]")
.build();
Macros rcptMacros = Macros.builder()
.add("{rcpt_mailer}", "smtp")
.add("{rcpt_host}", "mx1.example.com")
.add("{rcpt_addr}", "[email protected]")
.build()
Macros headerMacros = Macros.builder().add("i", "C1A7C20BAF").build();
List<String> envfrom = new ArrayList<>();
envfrom.add("<[email protected]>");
envfrom.add("SIZE=1552");
envfrom.add("BODY=8BITMIME");
List<String> envrcpt = new ArrayList<>();
envrcpt.add("<[email protected]>");
envrcpt.add("ORCPT=rfc822;[email protected]");
return factory.createSession()
.thenCompose(s -> s.connect("[88.88.88.88]", SMFIA_INET, 4567, "88.88.88.88", connectionMacros))
.thenCompose(r -> r.session().helo("mail.example.org"))
.thenCompose(r -> r.session().envfrom(envfrom, fromMacros))
.thenCompose(r -> r.session().envrcpt(envrcpt, rcptMacros))
.thenCompose(r -> r.session().header("Subject", "Some subject", headerMacros))
.thenCompose(r -> r.session().header("From", "[email protected]", headerMacros))
.thenCompose(r -> r.session().header("To", "[email protected]", headerMacros))
.thenCompose(r -> r.session().header("Message-Id", UUID.randomUUID() + "@example.org", headerMacros))
.thenCompose(r -> r.session().eoh())
.thenCompose(r -> r.session().body("Some text".getBytes(StandardCharsets.UTF_8)))
.thenCompose(r -> r.session().eob())
.thenCompose(r -> r.session().abort())
.thenCompose(r -> r.session().quit());
}
The test folder contains the complete example code.
Name | Possible values | Default value |
---|---|---|
jmilter.netty.loggingEnabled | true, false | false |
jmilter.netty.logLevel | TRACE, DEBUG, INFO, WARN, ERROR | DEBUG |
jmilter.netty.nThreads | [0, 65535] | 0 |
jmilter.netty.failStopMode | true, false | false |
jmilter.netty.connectTimeoutMs | [0, Long.MAX_VALUE] | 5000 |
jmilter.netty.reconnectTimeoutMs | [0, Long.MAX_VALUE] | 1000 |
jmilter.netty.autoRead | true, false | true |
jmilter.netty.keepAlive | true, false | true |
jmilter.netty.tcpNoDelay | true, false | true |
jmilter.netty.reuseAddress | true, false | true |
jmilter.netty.soBacklog | [0, 65535] | 128 |
Set an option using Command Line
$ java -Djmilter.netty.logLevel="INFO"
Set an option using Java Code
System.setProperty("jmilter.netty.logLevel", "INFO");
Download the latest release via Maven:
<dependency>
<groupId>org.nightcode</groupId>
<artifactId>jmilter</artifactId>
<version>0.8</version>
</dependency>
Feedback is welcome. Please don't hesitate to open up a new github issue or simply drop me a line at [email protected].