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
+|===


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to