This is an automated email from the ASF dual-hosted git repository.
chibenwa pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-jspf.git
commit 7c8f012c1e547233b034a9c85afdcb4df182f9af
Author: Benoit TELLIER <[email protected]>
AuthorDate: Sun Aug 23 23:24:31 2026 +0700
[DOC] Add a page on usage
---
docs/antora.yml | 6 +-
docs/modules/ROOT/nav.adoc | 1 +
docs/modules/ROOT/pages/faq.adoc | 8 +-
docs/modules/ROOT/pages/index.adoc | 3 +
docs/modules/ROOT/pages/usage.adoc | 196 +++++++++++++++++++++++++++++++++++++
5 files changed, 212 insertions(+), 2 deletions(-)
diff --git a/docs/antora.yml b/docs/antora.yml
index 72643aa..e36362a 100644
--- a/docs/antora.yml
+++ b/docs/antora.yml
@@ -1,6 +1,10 @@
name: jspf
title: Apache James jSPF
-version: '1.0.2-SNAPSHOT'
+version: '1.0.6-SNAPSHOT'
prerelease: true
nav:
- modules/ROOT/nav.adoc
+asciidoc:
+ attributes:
+ # Latest released version, as published on Maven central. Bump on release.
+ jspf-version: '1.0.5@'
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index 2e18dd3..823c80f 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -1,4 +1,5 @@
* xref:index.adoc[Overview]
+* xref:usage.adoc[How to use jSPF in your project]
* xref:faq.adoc[jSPF FAQ]
* xref:rfclist.adoc[Useful RFCs]
* xref:code-standards.adoc[Coding standards]
diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc
index de66417..e95696c 100644
--- a/docs/modules/ROOT/pages/faq.adoc
+++ b/docs/modules/ROOT/pages/faq.adoc
@@ -2,4 +2,10 @@
== Can I run jSPF from the command line?
-Sure you can. Just enter `java -jar jspf-xxx.jar` to get the usage.
+Sure you can. The `org.apache.james.jspf.impl.SPFQuery` class is a standalone entry
+point; run it with the resolver jar and its dependencies on the classpath, and without
+arguments to get the usage. See xref:usage.adoc#_running_jspf_from_the_command_line[Running jSPF from the command line].
+
+== How do I use jSPF from my own code?
+
+See xref:usage.adoc[How to use jSPF in your project].
diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc
index dd0a022..702adb7 100644
--- a/docs/modules/ROOT/pages/index.adoc
+++ b/docs/modules/ROOT/pages/index.adoc
@@ -8,6 +8,9 @@ Sender Policy Framework. It was designed to detect email spoofing. This is the
solution if you ever were tired of getting spam from yourself. For more information
see the http://www.open-spf.org/[Open SPF website].
+To check an SPF record from your own code, see
+xref:usage.adoc[How to use jSPF in your project].
+
== Releases
Both binary and source distributions are available from the
diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc
new file mode 100644
index 0000000..917474a
--- /dev/null
+++ b/docs/modules/ROOT/pages/usage.adoc
@@ -0,0 +1,196 @@
+= How to use jSPF in your project
+
+jSPF checks whether a given IP address is allowed to send mail on behalf of a
+given envelope sender, as specified by the domain's SPF record. This page shows
+how to add the library to a project, run a check, and interpret its result.
+
+== Adding the dependency
+
+The library lives in the `apache-jspf-resolver` artifact, published on Maven Central.
+
+[source,xml,subs=attributes+]
+----
+<dependency>
+ <groupId>org.apache.james.jspf</groupId>
+ <artifactId>apache-jspf-resolver</artifactId>
+ <version>{jspf-version}</version>
+</dependency>
+----
+
+With Gradle:
+
+[source,groovy,subs=attributes+]
+----
+implementation 'org.apache.james.jspf:apache-jspf-resolver:{jspf-version}'
+----
+
+jSPF pulls in `dnsjava` (the DNS resolver it uses by default), `commons-cli` (used by
+the command line tool) and `slf4j-api`. Logging goes through SLF4J, so add the binding
+of your choice - for instance `logback-classic` - to actually see log output.
+
+== Running a check
+
+`SPF` is the entry point. `DefaultSPF` wires it with the default dnsjava based
+resolver and a synchronous executor:
+
+[source,java]
+----
+SPF spf = new DefaultSPF();
+
+SPFResult result = spf.checkSPF(
+ "192.0.2.25", // <1>
+ "[email protected]", // <2>
+ "smtp.example.com"); // <3>
+
+System.out.println(result.getResult());
+----
+<1> The IP address the connection comes from.
+<2> The envelope sender, as provided in `MAIL FROM`.
+<3> The host name provided in `HELO` / `EHLO`.
+
+`SPF` instances are meant to be created once and reused for many checks.
+
+== Interpreting the result
+
+`SPFResult.getResult()` returns one of the seven RFC 4408 result strings. Compare it
+against the constants of `SPFErrorConstants` rather than against string literals:
+
+[cols="1,1,3"]
+|===
+|Constant |Value |Meaning
+
+|`PASS_CONV` |`pass` |The IP address is authorized for that sender.
+|`FAIL_CONV` |`fail` |The IP address is explicitly not authorized.
+|`SOFTFAIL_CONV` |`softfail` |Not authorized, but the domain is still transitioning.
+|`NEUTRAL_CONV` |`neutral` |The domain makes no assertion about this IP address.
+|`NONE_CONV` |`none` |No SPF record was published for that domain.
+|`TEMP_ERROR_CONV` |`temperror` |A transient error, typically a DNS failure. Retry later.
+|`PERM_ERROR_CONV` |`permerror` |The SPF record could not be processed, e.g. it is malformed.
+|===
+
+[source,java]
+----
+SPFResult result = spf.checkSPF(ip, mailFrom, helo);
+
+if (SPFErrorConstants.FAIL_CONV.equals(result.getResult())) {
+ // reject the mail, and tell the sender why
+ System.out.println(result.getExplanation());
+} else if (SPFErrorConstants.TEMP_ERROR_CONV.equals(result.getResult())) {
+ // ask the sender to try again later
+}
+----
+
+Beyond the raw result, `SPFResult` builds a ready to use `Received-SPF` header:
+
+* `getHeaderName()` returns `Received-SPF`.
+* `getHeaderText()` returns the header value alone.
+* `getHeader()` returns the full header line, for instance:
++
+----
+Received-SPF: pass (spfCheck: domain of example.com designates 192.0.2.25 as permitted
+sender) client-ip=192.0.2.25; [email protected]; helo=example.com;
+----
+
+`getExplanation()` returns the explanation published by the domain through the `exp=`
+modifier, or the empty string when there is none.
+
+== Checking asynchronously
+
+`DefaultSPF.createAsync()` builds an `SPF` backed by an asynchronous executor, so DNS
+lookups do not block the calling thread. `checkSPF` then returns immediately, and the
+returned object is a `FutureSPFResult`:
+
+[source,java]
+----
+SPF spf = DefaultSPF.createAsync();
+
+FutureSPFResult result = (FutureSPFResult) spf.checkSPF(ip, mailFrom, helo);
+
+result.addListener(spfResult -> {
+ // called once the result is available
+ System.out.println(spfResult.getResult());
+});
+----
+
+`isReady()` tells whether the result has been computed already. Note that every getter
+of `FutureSPFResult` blocks until the result is ready, so calling `getResult()` right
+away turns the asynchronous check back into a blocking one.
+
+`DefaultSPF.createSync()` is the explicit counterpart, equivalent to `new DefaultSPF()`.
+
+== Tuning the checks
+
+`SPF` exposes a few knobs, all of them optional:
+
+[source,java]
+----
+SPF spf = new DefaultSPF();
+
+spf.setTimeOut(20); // <1>
+spf.setDefaultExplanation("Blocked by SPF, see %{d}"); // <2>
+spf.setUseBestGuess(true); // <3>
+spf.setSPFMustEqualsTXT(true); // <4>
+----
+<1> DNS timeout in seconds, before a `temperror` is returned. Defaults to 20.
+<2> Explanation used when the domain publishes no `exp=` modifier. Macros are expanded.
+<3> When no SPF record is found, evaluate `v=spf1 a/24 mx/24 ptr ?all` instead of
+returning `none`. Only `pass` or `neutral` can result from a best guess. Defaults to false.
+<4> Return a `permerror` when a domain publishes both an SPF-type and a TXT-type record
+and the two disagree. Defaults to false.
+
+Two policy objects let you supply records jSPF would not otherwise see:
+
+* `getFallbackPolicy()` provides records for domains that publish none.
+* `getOverridePolicy()` replaces the records published by given domains.
+
+== Using your own DNS resolver
+
+`DefaultSPF` uses dnsjava with its default system resolver. To point jSPF at a specific
+DNS server, pass your own `Resolver` to `DNSServiceXBillImpl` and build the `SPF`
+instance yourself:
+
+[source,java]
+----
+Resolver resolver = new SimpleResolver("192.0.2.53");
+DNSService dnsService = new DNSServiceXBillImpl(resolver);
+
+SPF syncSpf = new SPF(dnsService);
+SPF asyncSpf = new SPF(dnsService, new AsynchronousSPFExecutor(dnsService));
+----
+
+If you already have a DNS layer of your own, implement the `DNSService` interface
+instead - it is the only contract jSPF needs in order to resolve records.
+
+== Running jSPF from the command line
+
+The `SPFQuery` class is a standalone entry point. Run it with the resolver jar and its
+dependencies on the classpath:
+
+[source,bash,subs=attributes+]
+----
+java -cp "apache-jspf-resolver-{jspf-version}.jar:dnsjava.jar:commons-cli.jar:slf4j-api.jar" \
+ org.apache.james.jspf.impl.SPFQuery \
+ -i 192.0.2.25 -s [email protected] -h smtp.example.com
+----
+
+The three options `-i` / `--ip`, `-s` / `--sender` and `-h` / `--helo` are required;
+running the tool without them prints the full usage. Also available are
+`-e` / `--default-explanation`, `-b` / `--enable-best-guess`,
+`-t` / `--enable-trusted-forwarder`, `-d` / `--debug` and `-v` / `--verbose`.
+
+The tool prints the result and the `Received-SPF` header on standard output, and
+reports the outcome through its exit code:
+
+[cols="1,3"]
+|===
+|Exit code |Result
+
+|0 |`pass`
+|1 |`fail`
+|2 |`softfail`
+|3 |`neutral`
+|4 |`temperror`
+|5 |`permerror`
+|6 |`none`
+|255 |Unknown result, or wrong command line arguments
+|===
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.