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 messagesActionsmilterActions = Actions.builder()
.addHeader()
.build();
// indicates which steps you want to skipProtocolStepsmilterProtocolSteps = ProtocolSteps.builder()
.noHelo()
.noData()
.noBody()
.build();
// gateway addressInetSocketAddressaddress = 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"MilterHandlermilterHandler = newAddHeaderMilterHandler(milterActions, milterProtocolSteps);
MilterGatewayManager<InetSocketAddress> gatewayManager = newMilterGatewayManager<>(serverFactory, milterHandler);
gatewayManager.bind();MTA side
CompletableFuture<Void> call(MilterSessionFactoryfactory) {
MacrosconnectionMacros = Macros.builder()
.add("j", "mx.example.org")
.add("{daemon_name}", "mx.example.org")
.add("v", "Postfix 2.10.1")
.build();
MacrosfromMacros = Macros.builder()
.add("{mail_mailer}", "smtp")
.add("{mail_host}", "mx1.example.org")
.add("{mail_addr}", "sender@example.org")
.build();
MacrosrcptMacros = Macros.builder()
.add("{rcpt_mailer}", "smtp")
.add("{rcpt_host}", "mx1.example.com")
.add("{rcpt_addr}", "sender@example.com")
.build()
MacrosheaderMacros = Macros.builder().add("i", "C1A7C20BAF").build();
List<String> envfrom = newArrayList<>();
envfrom.add("<sender@example.org>");
envfrom.add("SIZE=1552");
envfrom.add("BODY=8BITMIME");
List<String> envrcpt = newArrayList<>();
envrcpt.add("<recipient@example.com>");
envrcpt.add("ORCPT=rfc822;recipient@example.com");
returnfactory.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", "sender@example.org", headerMacros))
.thenCompose(r -> r.session().header("To", "recipient@example.com", 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.9</version>
</dependency>Feedback is welcome. Please don't hesitate to open up a new github issue or simply drop me a line at dmitry@nightcode.org.