diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 000000000..d2c344878
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,71 @@
+version: 2.1
+
+orbs:
+ github-maven-deploy: github-maven-deploy/github-maven-deploy@1.3.2
+
+executors:
+ java21-executor:
+ docker:
+ - image: cimg/openjdk:21.0
+
+mvn-build-test-command: &mvn-build-test-command
+ mvn-build-test-command: mvn verify -Ppublish-cli -DexcludeLiveServerTests=true -Dmaven.javadoc.skip=true -Djacoco.skip=true -Dlicense.skip=true
+
+mvn-collect-artifacts-command: &mvn-collect-artifacts-command
+ mvn-collect-artifacts-command: |
+ mkdir -p artifacts/cli
+ cp modules/cli-module/target/*standalone* artifacts/cli/
+
+mvn-deploy-command: &mvn-deploy-command
+ mvn-deploy-command: |
+ mvn -s .circleci/maven-central-settings.xml clean deploy -Ppublish-cli -DdeployAtEnd=true -DperformRelease=true -DskipTests -Dspotbugs.skip=true -Denforcer.skip=true -Djacoco.skip=true -Dossindex.skip=true
+ mvn com.mycila:license-maven-plugin:3.0:remove
+ context: RELEASE_PROFILE_BBOTTEMA
+
+workflows:
+ workflow:
+ jobs:
+ - github-maven-deploy/build-and-test:
+ <<: *mvn-build-test-command
+ <<: *mvn-collect-artifacts-command
+ filters:
+ branches:
+ only: master
+
+ - github-maven-deploy/approve-deploy-patch-version:
+ type: approval
+ requires:
+ - github-maven-deploy/build-and-test
+ - github-maven-deploy/approve-deploy-minor-version:
+ type: approval
+ requires:
+ - github-maven-deploy/build-and-test
+ - github-maven-deploy/approve-deploy-major-version:
+ type: approval
+ requires:
+ - github-maven-deploy/build-and-test
+ - github-maven-deploy/approve-deploy-as-is-version:
+ type: approval
+ requires:
+ - github-maven-deploy/build-and-test
+
+ - github-maven-deploy/deploy-patch-version:
+ executor: java21-executor
+ requires:
+ - github-maven-deploy/approve-deploy-patch-version
+ <<: *mvn-deploy-command
+ - github-maven-deploy/deploy-minor-version:
+ executor: java21-executor
+ requires:
+ - github-maven-deploy/approve-deploy-minor-version
+ <<: *mvn-deploy-command
+ - github-maven-deploy/deploy-major-version:
+ executor: java21-executor
+ requires:
+ - github-maven-deploy/approve-deploy-major-version
+ <<: *mvn-deploy-command
+ - github-maven-deploy/deploy-as-is-version:
+ executor: java21-executor
+ requires:
+ - github-maven-deploy/approve-deploy-as-is-version
+ <<: *mvn-deploy-command
diff --git a/.circleci/maven-central-settings.xml b/.circleci/maven-central-settings.xml
new file mode 100644
index 000000000..7d84bb07a
--- /dev/null
+++ b/.circleci/maven-central-settings.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ central
+ ${env.CENTRAL_USERNAME}
+ ${env.CENTRAL_PASSWORD}
+
+
+
+
+
+ gpg
+
+ gpg
+ ${env.GPG_PASSPHRASE}
+
+
+
+
+
+ gpg
+
+
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..f69503aba
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+custom: ["https://paypal.me/bennybottema", "https://www.buymeacoffee.com/bbottema"]
\ No newline at end of file
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..13cf1bb5b
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,48 @@
+# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
+
+version: 2
+updates:
+ - package-ecosystem: "maven"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ target-branch: "develop"
+ ignore:
+ - dependency-name: "org.mnode.ical4j:ical4j-vcard"
+ update-types:
+ - "version-update:semver-patch"
+ - "version-update:semver-minor"
+ - "version-update:semver-major"
+ - dependency-name: "info.picocli:picocli"
+ update-types:
+ - "version-update:semver-minor"
+ - "version-update:semver-major"
+ - dependency-name: "org.springframework:spring-framework-bom"
+ update-types:
+ - "version-update:semver-minor"
+ - "version-update:semver-major"
+ - dependency-name: "org.springframework.boot:*"
+ update-types:
+ - "version-update:semver-minor"
+ - "version-update:semver-major"
+ - dependency-name: "org.junit.jupiter:*"
+ versions:
+ - "[6,)"
+ - dependency-name: "org.junit.platform:*"
+ versions:
+ - "[6,)"
+ - dependency-name: "org.mockito:*"
+ versions:
+ - "[5,)"
+ - dependency-name: "org.junit-pioneer:junit-pioneer"
+ versions:
+ - "[2,)"
+ - dependency-name: "com.mycila:license-maven-plugin"
+ versions:
+ - "[5,)"
+ - dependency-name: "org.apache.felix:maven-bundle-plugin"
+ versions:
+ - "[6,)"
+ - dependency-name: "org.apache.karaf.tooling:karaf-maven-plugin"
+ versions:
+ - "[4.4.8,)"
diff --git a/.gitignore b/.gitignore
index 28ef4fcf2..02e5f5fd0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,56 +1,11 @@
-# Created by https://www.gitignore.io
-
-### Intellij ###
-# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
-
-*.iml
-
-## Directory-based project format:
-.idea/
-# if you remove the above rule, at least ignore the following:
-
-# User-specific stuff:
-# .idea/workspace.xml
-# .idea/tasks.xml
-# .idea/dictionaries
-
-# Sensitive or high-churn files:
-# .idea/dataSources.ids
-# .idea/dataSources.xml
-# .idea/sqlDataSources.xml
-# .idea/dynamic.xml
-# .idea/uiDesigner.xml
-
-# Gradle:
-# .idea/gradle.xml
-# .idea/libraries
-
-# Mongo Explorer plugin:
-# .idea/mongoSettings.xml
-
-## File-based project format:
-*.ipr
-*.iws
-
-## Plugin-specific files:
-
-# IntelliJ
-/out/
-
-# mpeltonen/sbt-idea plugin
-.idea_modules/
-
-# JIRA plugin
-atlassian-ide-plugin.xml
-
-# Crashlytics plugin (for Android Studio and IntelliJ)
-com_crashlytics_export_strings.xml
-crashlytics.properties
-crashlytics-build.properties
-
+/.idea
+/maven-master-project.iml
/target
-
-# Eclipse
-/.settings
-.classpath
-.project
+/modules/jacoco-aggregator-module/target
+/modules/jacoco-aggregator-module/*.iml
+*.iml
+*.log
+/modules/simple-java-mail/src/test/resources/secure-testdata/secure-testdata/
+.atlasarc/
+/build-coverage.sh
+/.maintainer-env.ps1
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000..db14f5156
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "angular-app"]
+ path = simplejavamail.org
+ url = https://github.com/simple-java-mail/simplejavamail.org.git
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 45fbf38b8..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-language: java
-sudo: false
-install: mvn install -DskipFindBugs=true -DskipTests=true -Dmaven.javadoc.skip=true -B -V
-script: mvn clean verify -Dmaven.javadoc.skip=true
-cache:
- directories:
- - $HOME/.m2
-# whitelist
-branches:
- only:
- - master
- - stable
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/01-authenticated-socks-loopback.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/01-authenticated-socks-loopback.md
new file mode 100644
index 000000000..c68c8995f
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/01-authenticated-socks-loopback.md
@@ -0,0 +1,34 @@
+# Bind the authenticated SOCKS bridge to loopback
+
+- Status: Done
+- Priority: Medium
+- Work: Code, tests, documentation
+
+GitHub issue: #676
+
+## Problem
+
+The configuration guide says the bridge port is “always localhost,” but `AnonymousSocks5ServerImpl` binds `new InetSocketAddress(proxyBridgePort)`, which listens on wildcard interfaces. While the bridge is active it accepts unauthenticated SOCKS CONNECT requests and relays them through the authenticated upstream proxy.
+
+## Plan
+
+1. Bind explicitly to a loopback `InetAddress`.
+2. Configure Jakarta Mail with that exact loopback literal rather than the ambiguous `localhost` name, so the client and listener use the same IP family.
+3. Add a test asserting the listening address is loopback-only.
+4. Update the documentation to describe the tested loopback-only binding.
+5. Add a concise explanation of why the local bridge exists and when it runs.
+
+## Acceptance criteria
+
+- [x] The bridge cannot be reached through a non-loopback interface.
+- [x] The Jakarta Mail SOCKS host matches the address on which the bridge listens.
+- [x] Authenticated proxy sends still work.
+- [x] Configuration and proxy documentation match the tested binding behavior.
+- [x] The change is called out in release notes if it affects unusual custom deployments.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/configuration.hbs:211-214`
+- Implementation: `modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/AnonymousSocks5ServerImpl.java:45-50`
+- Protocol handler: `modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/Socks5Handler.java`
+- Tests: `AnonymousSocks5ServerImplTest` proves the bind address is loopback; `MailerSOCKSLiveTest` sends mail through the authenticated-proxy bridge path.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/02-tls-trust-model.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/02-tls-trust-model.md
new file mode 100644
index 000000000..c148637ae
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/02-tls-trust-model.md
@@ -0,0 +1,31 @@
+# Document the TLS trust model accurately
+
+- Status: Done
+- Priority: High
+- Work: Implementation, documentation, tests
+
+## Problem
+
+The SMTPS and SMTP_TLS sections promised strict trust-store and hostname validation, but the default set `mail.*.ssl.trust=*`. Hostname verification was enabled separately, and the security page contradicted its earlier description by later saying that all hosts were trusted.
+
+## Plan
+
+1. Explain issuer trust and hostname verification as two independent controls.
+2. Change the default to JVM trust-store validation and document the compatibility impact.
+3. Show the secure defaults: `trustingAllHosts(false)` plus `verifyingServerIdentity(true)`.
+4. Explain that `trustingSSLHosts(...)` accepts the named hosts outside normal CA trust.
+5. Add prominent v9.2.0 migration and release guidance for private and self-signed PKI.
+
+## Acceptance criteria
+
+- [x] Security, configuration, and Javadocs describe the same defaults.
+- [x] The recommended production example enables both issuer and hostname verification.
+- [x] No text claims that either control alone is sufficient against MITM.
+- [x] Tests anchor `ssl.trust` and `checkserveridentity` defaults.
+
+## Evidence
+
+- Issue: [#677](https://github.com/bbottema/simple-java-mail/issues/677)
+- Documentation: `simplejavamail.org/src/pages/security.hbs`, `simplejavamail.org/src/pages/configuration.hbs`, and `simplejavamail.org/src/pages/migration-notes-9.2.0.hbs`
+- Defaults: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerGenericBuilder.java`
+- Tests: `modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/MailerTest.java`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/03-embedded-image-containment.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/03-embedded-image-containment.md
new file mode 100644
index 000000000..00a45840a
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/03-embedded-image-containment.md
@@ -0,0 +1,33 @@
+# Make embedded-image resolution a real containment boundary
+
+- Status: Done
+- Priority: High
+- Work: Code, security tests, documentation
+
+## Problem
+
+Auto-resolution is recommended for freely entered HTML, but `allowingEmbeddedImageOutsideBase*` is not a reliable sandbox. File paths are joined without canonical containment, and URL containment does not adequately constrain scheme and authority.
+
+## Plan
+
+1. Define the intended containment contract for file, classpath, and URL resources.
+2. Resolve and normalize paths before checking they remain under the configured root.
+3. Compare URL scheme, host, effective port, and normalized path, and recheck every redirect.
+4. Resolve filesystem paths to their real location so symlinks cannot escape the base.
+5. Add traversal, sibling-prefix, alternate-origin, redirect, encoded-path, symlink, and absolute-resource tests.
+6. Document the unrestricted behavior when no base is configured or an explicit `allow outside` option is enabled.
+
+## Acceptance criteria
+
+- [x] `../` and encoded traversal cannot leave the configured base.
+- [x] A URL on another authority cannot pass a same-path check.
+- [x] Explicit “allow outside” options retain their documented behavior.
+- [x] The guide clearly distinguishes convenience from a security boundary.
+
+## Evidence
+
+- Issue: [#678](https://github.com/bbottema/simple-java-mail/issues/678), with historical context from [#260](https://github.com/bbottema/simple-java-mail/issues/260) and [#617](https://github.com/bbottema/simple-java-mail/issues/617)
+- Documentation: `simplejavamail.org/src/pages/features.hbs` and `simplejavamail.org/src/pages/migration-notes-9.2.0.hbs`
+- Javadocs: `modules/core-module/src/main/java/org/simplejavamail/api/email/EmailPopulatingBuilder.java`
+- Resolution code: `modules/core-module/src/main/java/org/simplejavamail/internal/util/MiscUtil.java`
+- Tests: `modules/simple-java-mail/src/test/java/org/simplejavamail/internal/util/MiscUtilTest.java` and the existing `EmailPopulatingBuilderImpl1Test` compatibility suite
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/04-dkim-safe-defaults.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/04-dkim-safe-defaults.md
new file mode 100644
index 000000000..aeb0189b7
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/04-dkim-safe-defaults.md
@@ -0,0 +1,32 @@
+# Replace unsafe DKIM examples
+
+- Status: Done
+- Priority: High
+- Work: Configuration validation, tests, documentation
+
+## Problem
+
+Primary DKIM examples enable the body-length parameter and exclude `From` and `Subject`. The API warns that `l=` permits unsigned appended content; excluding `From` is incompatible with a valid DKIM signature and excluding `Subject` weakens integrity.
+
+## Plan
+
+1. Use safe defaults in the first complete example.
+2. Remove `From` and `Subject` from header-exclusion examples.
+3. Put `useLengthParam(true)` in an advanced warning block, if retained at all.
+4. Reject case-insensitive attempts to exclude the mandatory `From` header.
+5. Explain what header exclusions are for and which headers should remain signed.
+
+## Acceptance criteria
+
+- [x] The normal example keeps `useLengthParam(false)`.
+- [x] `From` is always signed in examples and rejected as an exclusion, case-insensitively.
+- [x] Security consequences precede the advanced `useLengthParam(true)` opt-in example.
+- [x] Property and programmatic examples agree on safe defaults and relay-specific exclusions.
+- [x] The Java builder, convenience APIs, and property-driven defaults all enforce the configuration boundary.
+
+## Evidence
+
+- Issue: [#679](https://github.com/bbottema/simple-java-mail/issues/679), with historical context from [#344](https://github.com/bbottema/simple-java-mail/issues/344) and [#499](https://github.com/bbottema/simple-java-mail/issues/499).
+- Configuration guard: `modules/core-module/src/main/java/org/simplejavamail/api/email/config/DkimConfig.java`
+- API and property coverage: `modules/simple-java-mail/src/test/java/org/simplejavamail/api/email/config/DkimConfigTest.java` and `modules/simple-java-mail/src/test/java/org/simplejavamail/email/internal/EmailPopulatingBuilderImpl2Test.java`
+- Documentation and migration material: `simplejavamail.org/src/pages/security.hbs`, `simplejavamail.org/src/pages/configuration.hbs`, and `simplejavamail.org/src/pages/migration-notes-9.2.0.hbs`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/05-smime-verification-semantics.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/05-smime-verification-semantics.md
new file mode 100644
index 000000000..069536733
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/05-smime-verification-semantics.md
@@ -0,0 +1,37 @@
+# Define S/MIME signature verification precisely
+
+- Status: Done
+- Priority: High
+- Work: Code and documentation
+
+## Problem
+
+`getSmimeSignatureValid()` is presented as if it authenticates the sender. It verifies cryptographic consistency using the certificate included with the message, but does not establish certificate trust, identity, validity period, revocation status, or a PKIX chain.
+
+Three implementation paths also overstate the result: Outlook conversion can infer `true` from successfully extracted content without recording a verification result, a message not classified as definitely signed can return `true` without running the cryptographic check, and combined results currently use OR semantics so one successful signature can hide another failure.
+
+## Plan
+
+1. Make `true` mean that every applicable signature represented by the metadata was actually checked and passed cryptographic verification.
+2. Make `false` win when verification results are combined, and leave the value `null` when no check was applicable or performed.
+3. List the trust checks applications must perform separately.
+4. Preserve lenient parsing from issue #571: invalid signed content remains available when it can be extracted.
+5. Align website text and API Javadocs.
+
+## Acceptance criteria
+
+- [x] “Valid” is never equated with “trusted sender.”
+- [x] Outlook conversion never infers a valid signature from content extraction alone.
+- [x] A `true` result is impossible unless a signer and signed state were found and cryptographic verification ran.
+- [x] Combined verification results fail closed.
+- [x] Missing trust, identity, validity, and revocation checks are explicit.
+- [x] Reading and security sections use the same terminology.
+- [x] The documentation does not imply that certificate trust can be evaluated through an API that does not currently expose the signer certificate or chain.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/security.hbs:651`
+- Verification implementation: `modules/smime-module/src/main/java/org/simplejavamail/internal/smimesupport/SMIMESupport.java:441`
+- Regression coverage: `modules/simple-java-mail/src/test/java/org/simplejavamail/internal/smimesupport/ReadSmimeSelfSignedTest.java`
+- Result aggregation coverage: `modules/simple-java-mail/src/test/java/org/simplejavamail/internal/smimesupport/OriginalSmimeDetailsImplTest.java`
+- Implementation record: [GitHub issue #680](https://github.com/bbottema/simple-java-mail/issues/680)
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/06-cluster-failover-claims.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/06-cluster-failover-claims.md
new file mode 100644
index 000000000..2365c08ec
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/01-security-and-correctness/06-cluster-failover-claims.md
@@ -0,0 +1,30 @@
+# Remove unsupported cluster failover claims
+
+- Status: Done
+- Priority: High
+- Work: Documentation
+
+## Problem
+
+The configuration page says a failed server is removed and remaining mailers continue transparently. Current behavior selects one pool, invalidates a failed transport, and rethrows. It does not retry, quarantine the server, or remove its pool.
+
+## Plan
+
+1. Rename the documented capability to clustered load balancing.
+2. Describe failure behavior precisely.
+3. Remove automatic removal and failover examples.
+4. Explain that retries, circuit breaking, and health management are application-owned.
+5. Treat true failover as a separate future feature if desired.
+
+## Acceptance criteria
+
+- [x] No page promises transparent failover or automatic server removal.
+- [x] The failure example shows the exception reaching caller code.
+- [x] Load-balancing behavior remains clearly documented.
+- [x] Configuration and homepage cluster language agree.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/configuration.hbs:863`
+- Pool selection: `modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/BatchSupport.java:111`
+- Failure path: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/util/TransportRunner.java:97`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/01-validator-reset-example.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/01-validator-reset-example.md
new file mode 100644
index 000000000..eb0c5c975
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/01-validator-reset-example.md
@@ -0,0 +1,26 @@
+# Correct the validator reset example
+
+- Status: Done
+- Priority: High
+- Work: Documentation, source Javadocs and regression tests
+
+## Problem
+
+The example has `clearEmailValidator()` and `resetEmailValidator()` backwards. Reset restores `JMail.strictValidator()`; clear sets the configurable address validator to `null`.
+
+## Plan
+
+Swap the calls and comments, then distinguish clearing the address validator from disabling all client-side validation.
+
+## Acceptance criteria
+
+- [x] `resetEmailValidator()` is shown restoring the strict default.
+- [x] `clearEmailValidator()` is shown removing address validation only.
+- [x] The example agrees with API Javadocs and implementation tests.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/configuration.hbs:205`
+- Contract: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerGenericBuilder.java:648` and `:769`
+- Implementation: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerGenericBuilderImpl.java:773` and `:883`
+- Regression coverage: `modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/MailerBuilderTest.java:12`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/02-configloader-overloads.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/02-configloader-overloads.md
new file mode 100644
index 000000000..fab36f2e8
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/02-configloader-overloads.md
@@ -0,0 +1,26 @@
+# Replace removed ConfigLoader overloads
+
+- Status: Done
+- Priority: High
+- Work: Documentation
+
+## Problem
+
+Examples call `loadProperties(File, boolean)` and `loadProperties(File)`, neither of which exists in 9.1.5.
+
+## Plan
+
+Use `Files.newInputStream(Path.of(...))` with an explicit `addProperties` boolean, or build a `Properties` object. Mention that `ConfigLoader` closes a supplied stream.
+
+## Acceptance criteria
+
+- [x] Every ConfigLoader example uses a current public overload.
+- [x] Filesystem and classpath examples are clearly distinguished.
+- [x] Stream ownership is documented.
+- [x] Snippets compile against 9.1.5.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/configuration.hbs` now uses the current `String`, `InputStream`, and `Properties` overloads, with `Path.of(...)` for filesystem input.
+- API: `modules/core-module/src/main/java/org/simplejavamail/config/ConfigLoader.java:342-386` confirms the published overloads and stream-closing behavior.
+- Verification: `npm run check`, `npm run build`, and `npm run verifyLinks:internal` pass; the internal-link check covers 1,280 links across 20 generated pages.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/03-delivery-receipt-example.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/03-delivery-receipt-example.md
new file mode 100644
index 000000000..4d5143791
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/03-delivery-receipt-example.md
@@ -0,0 +1,26 @@
+# Repair the delivery-receipt example
+
+- Status: Done
+- Priority: High
+- Work: Documentation
+
+## Problem
+
+The receipt example has broken fluent chaining and uses a removed two-argument `Recipient` constructor.
+
+## Plan
+
+Use the current string overloads or `RecipientBuilder`, and present one complete compilable chain for disposition notification and return receipt addresses.
+
+## Acceptance criteria
+
+- [x] The example compiles against 9.1.5.
+- [x] It uses current recipient construction APIs.
+- [x] The text distinguishes request headers from guaranteed receipt delivery.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/features.hbs` now contains one complete chain using `RecipientBuilder` and the current receipt-address overloads.
+- Model: `modules/core-module/src/main/java/org/simplejavamail/api/email/Recipient.java:18-41` confirms the removed two-argument constructor is no longer used.
+- Builder: `modules/core-module/src/main/java/org/simplejavamail/api/email/EmailPopulatingBuilder.java` documents MDN and return-receipt request semantics and the Reply-To/From fallback.
+- Verification: `npm run check`, `npm run build`, `npm run verifyLinks:internal`, and core `javadoc:javadoc` pass.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/04-reply-content-alternatives.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/04-reply-content-alternatives.md
new file mode 100644
index 000000000..df18d87ed
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/04-reply-content-alternatives.md
@@ -0,0 +1,30 @@
+# Update both reply content alternatives
+
+- Status: Done
+- Priority: High
+- Work: Documentation
+
+## Problem
+
+`replyingTo(...)` creates plain-text and HTML alternatives, but the example prepends the new reply only to plain text. HTML-capable clients can therefore show only the quoted original.
+
+## Plan
+
+Add `prependTextHTML(...)`, or explicitly clear the HTML alternative when demonstrating a plain-only reply. Explain why both representations must be updated.
+
+## Acceptance criteria
+
+- [x] The reply is present in both body alternatives.
+- [x] The example renders correctly for plain-text and HTML clients.
+- [x] The guidance links to the broader reply API coverage item.
+
+## Related follow-up
+
+- [Cover reply and body-editing APIs](../04-missing-coverage/02-reply-and-body-api.md)
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/features.hbs` now prepends the reply to both plain-text and HTML alternatives and explains why they stay paired.
+- Contract: `modules/core-module/src/main/java/org/simplejavamail/api/email/EmailStartingBuilder.java:139-145` documents that replies start with both alternatives.
+- Implementation: `modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailStartingBuilderImpl.java:151-157` initializes both quoted bodies.
+- Verification: `npm run check`, `npm run build`, and `npm run verifyLinks:internal` pass; generated HTML preserves the escaped HTML reply string.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/05-outlook-msg-conversion.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/05-outlook-msg-conversion.md
new file mode 100644
index 000000000..5bf395853
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/05-outlook-msg-conversion.md
@@ -0,0 +1,26 @@
+# Correct Outlook MSG conversion calls
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation and source Javadocs
+
+## Problem
+
+The examples call a nonexistent `readToString(...)` helper, implying that binary `.msg` data should be decoded into a Java `String`, and then pass that
+value to overloads whose `String` parameter means a filename.
+
+## Plan
+
+Pass the path string directly, or use `File` or `InputStream`. Keep binary data out of text helpers.
+
+## Acceptance criteria
+
+- [x] All three Outlook conversion examples use the correct overload semantics.
+- [x] No example converts MSG bytes to text.
+- [x] The path, file, and stream alternatives are labeled clearly.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/features.hbs` now labels path-string, `File`, and fresh binary `InputStream` alternatives and uses only real APIs.
+- API: `modules/simple-java-mail/src/main/java/org/simplejavamail/converter/EmailConverter.java` consistently names and documents filename-based `String` parameters.
+- Verification: `npm run check`, `npm run build`, `npm run verifyLinks:internal`, targeted `EmailConverterTest`, and generated Javadocs pass.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/06-cli-recipient-flags.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/06-cli-recipient-flags.md
new file mode 100644
index 000000000..2ac488da3
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/06-cli-recipient-flags.md
@@ -0,0 +1,32 @@
+# Restore dedicated CLI recipient flags
+
+- Status: Done
+- Priority: Medium
+- Work: CLI compatibility fix, generated CLI data, regression tests
+
+## Problem
+
+The primary CLI example uses `--email:to`, `--email:cc`, and `--email:bcc`, but these options were accidentally removed along with the old Java recipient overloads in 9.0.0.
+
+The retained `--email:withRecipients` option cannot replace them in a command containing more than one recipient type. Picocli combines repeated occurrences into one invocation, so three occurrences are presented as twelve arguments to a method that accepts at most four.
+
+## Plan
+
+Restore `--email:to`, `--email:cc`, and `--email:bcc` through an internal CLI-only builder facade. Move the retained string-parsing `withRecipients(...)` bridge to that facade as well, while keeping the object-based recipient methods on the public Java builder API. Regenerate the packaged CLI metadata and execute the published form in a subprocess smoke test.
+
+## Acceptance criteria
+
+- [x] The command works with the current `sjm send` CLI.
+- [x] TO, CC, and BCC are all demonstrated correctly.
+- [x] A smoke test guards the published command.
+- [x] The dedicated methods remain outside `EmailPopulatingBuilder`.
+- [x] The CLI/property string parser remains available internally but is absent from `EmailPopulatingBuilder`.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/cli.hbs:56-69`
+- Issue: https://github.com/bbottema/simple-java-mail/issues/682
+- Regression source: commit `66345568b2d12b3ce505c9aaade58b0ad0af401f`
+- CLI-only facade: `modules/core-module/src/main/java/org/simplejavamail/api/internal/clisupport/CliEmailRecipientBuilder.java`
+- Mapper guard: `modules/cli-module/src/test/java/org/simplejavamail/internal/clisupport/BuilderApiToPicocliCommandsMapperTest.java`
+- Process smoke test: `modules/cli-module/src/test/java/org/simplejavamail/internal/clisupport/CliProcessSmokeTest.java`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/07-cli-argument-file-subcommand.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/07-cli-argument-file-subcommand.md
new file mode 100644
index 000000000..74e526f02
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/07-cli-argument-file-subcommand.md
@@ -0,0 +1,25 @@
+# Add the CLI argument-file subcommand
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation, CLI smoke test
+
+## Problem
+
+The argument file contained only options and was invoked as `sjm @file`, but the CLI requires `send`, `connect`, or `validate`.
+
+## Plan
+
+Keep the self-contained `sjm @file` form and put `send` first in the argument file. Explain that Picocli expands the file after `sjm`, then exercise that form through the CLI entry point used by both generated launchers.
+
+## Acceptance criteria
+
+- [x] The argument-file example parses successfully.
+- [x] The relationship between the root command and subcommands is clear.
+- [x] The chosen form works through the CLI entry point used by the supported launch scripts.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/cli.hbs:115-131`
+- Command model: `modules/cli-module/src/main/java/org/simplejavamail/internal/clisupport/CliCommandLineProducer.java:43-54`
+- Process smoke test: `modules/cli-module/src/test/java/org/simplejavamail/internal/clisupport/CliProcessSmokeTest.java`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/08-test-connection-overload.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/08-test-connection-overload.md
new file mode 100644
index 000000000..481585d3f
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/08-test-connection-overload.md
@@ -0,0 +1,24 @@
+# Correct the testConnection overload
+
+- Status: Done
+- Priority: Low
+- Work: Documentation
+
+## Problem
+
+The async example referenced nonexistent `mailer.testConnection(email, true)`.
+
+## Plan
+
+Use `mailer.testConnection(true)` and handle its returned `CompletableFuture`. Explain that `true` selects asynchronous execution, while synchronous failures are thrown directly.
+
+## Acceptance criteria
+
+- [x] The example compiles.
+- [x] The connection test is not shown taking an Email.
+- [x] Its future and error behavior agree with the Mailer contract.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/features.hbs:489-520`
+- API: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java:48-57`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/09-dkim-private-key-overloads.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/09-dkim-private-key-overloads.md
new file mode 100644
index 000000000..d9c1cab80
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/09-dkim-private-key-overloads.md
@@ -0,0 +1,24 @@
+# Correct DKIM private-key overloads
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation
+
+## Problem
+
+The guide showed `dkimPrivateKeyData(File)`, but files are accepted by `dkimPrivateKeyPath(File)`. Data overloads accept `InputStream`, `String`, or `byte[]`.
+
+## Plan
+
+Use a real `byte[]` variable in the in-memory example. Separate path/file inputs from raw-data inputs and document when data is copied, how strings are encoded, and who closes an input stream.
+
+## Acceptance criteria
+
+- [x] Every shown overload exists in 9.1.5.
+- [x] File-path and in-memory key examples are not conflated.
+- [x] Examples compile in the snippet fixture.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/security.hbs:353-393`
+- API: `modules/core-module/src/main/java/org/simplejavamail/api/email/config/DkimConfig.java:169-218`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/10-dkim-property-key-format.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/10-dkim-property-key-format.md
new file mode 100644
index 000000000..ab3f84f30
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/10-dkim-property-key-format.md
@@ -0,0 +1,28 @@
+# Define the DKIM property key format
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation or code enhancement
+
+## Problem
+
+The property guide said inline Base64 DKIM key data was accepted, but a non-file property value was converted directly to UTF-8 bytes without Base64 decoding.
+
+## Plan
+
+Version 9.2.0 adds two explicit formats: `file:` reads key bytes from a deliberate path and `base64:` decodes inline Base64 key bytes. Existing unprefixed values retain the pre-9.2 path-or-UTF-8-data behavior for compatibility. Explicit malformed values fail without repeating inline key material in the error.
+
+## Acceptance criteria
+
+- [x] Property documentation matches implementation exactly.
+- [x] A test covers the chosen inline format.
+- [x] Invalid values fail with a useful message.
+- [x] Security and configuration pages show the same contract.
+
+## Evidence
+
+- Resolver: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/DkimPrivateKeyPropertyResolver.java`
+- Integration: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/EmailGovernanceImpl.java`
+- Tests: `modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/internal/DkimPrivateKeyPropertyResolverTest.java`
+- Documentation: `simplejavamail.org/src/pages/security.hbs` and `simplejavamail.org/src/pages/configuration.hbs`
+- Migration and release notes: `simplejavamail.org/src/pages/migration-notes-9.2.0.hbs`, `README.md`, `RELEASE.txt`, and `RELEASE_HISTORY.md`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/11-smime-content-cipher.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/11-smime-content-cipher.md
new file mode 100644
index 000000000..9720ac5f2
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/11-smime-content-cipher.md
@@ -0,0 +1,28 @@
+# Use a valid S/MIME content cipher
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation
+
+## Problem
+
+The configuration example used `DES_EDE3_WRAP`, a key-wrap OID, where the API expects a content-encryption cipher.
+
+## Plan
+
+The configuration and security examples now use the recommended `AES256_CBC`. `DES_EDE3_CBC` is mentioned only as a legacy compatibility choice. The property-default test carries the exact algorithm names into `SmimeEncryptionConfig`, and the existing end-to-end alternative-algorithm test now encrypts and decrypts with `AES256_CBC`.
+
+## Acceptance criteria
+
+- [x] The example creates a usable content encryptor.
+- [x] The preferred algorithm matches the API recommendation.
+- [x] The example is covered by an S/MIME test or fixture.
+
+## Evidence
+
+- Issue: `https://github.com/bbottema/simple-java-mail/issues/684`
+- Documentation: `simplejavamail.org/src/pages/configuration.hbs` and `simplejavamail.org/src/pages/security.hbs`
+- API: `modules/core-module/src/main/java/org/simplejavamail/api/email/config/SmimeEncryptionConfig.java:73-92`
+- Implementation: `modules/smime-module/src/main/java/org/simplejavamail/internal/smimesupport/SMIMESupport.java:542-551`
+- Property mapping: `modules/simple-java-mail/src/test/java/org/simplejavamail/email/internal/EmailPopulatingBuilderImpl2Test.java`
+- End-to-end encryption: `modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/MailerLiveTest.java`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/12-java-snippet-syntax.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/12-java-snippet-syntax.md
new file mode 100644
index 000000000..79621d892
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/02-copyable-api-examples/12-java-snippet-syntax.md
@@ -0,0 +1,31 @@
+# Repair remaining Java snippet syntax
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation
+
+## Problem
+
+Several examples contain syntax errors: ellipses attached to recipient variable names, an extra closing parenthesis, a missing semicolon, and `printStrackTrace()`. Some transport examples also configure several alternatives on one builder, even though each call replaces the previous choice.
+
+## Plan
+
+1. Repair each known syntax defect.
+2. Present transport strategies as alternatives instead of consecutive configuration calls.
+3. Verify the previously reported TLS example and leave it alone if the earlier TLS work already repaired it.
+4. Retain `.(..)` as the established shorthand for omitted builder configuration.
+
+## Acceptance criteria
+
+- [x] Known syntax defects are removed.
+- [x] The transport-strategy overview presents alternatives, not one overwriting chain.
+- [x] Nearby examples have been checked for the same concrete mistakes.
+
+## Evidence
+
+- `simplejavamail.org/src/pages/features.hbs`: repaired recipient, content-transfer-encoding, attachment, calendar, and exception-handling examples; split mutually exclusive encoding, SMTP, and transport choices into explicit alternatives.
+- `simplejavamail.org/src/pages/debugging.hbs`: repaired the override-recipient example.
+- `simplejavamail.org/src/pages/security.hbs`: presents transport strategies as alternatives and separates the Java and properties forms of opportunistic TLS configuration.
+- The certificate-trust example reported by the audit was already valid after the earlier TLS documentation work and was left unchanged.
+- The established `.(..)` notation remains in place where it deliberately means omitted builder configuration.
+- Verification: `npm run check` and `npm run verifyLinks` passed on 6 August 2026.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/01-configloader-replacement-semantics.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/01-configloader-replacement-semantics.md
new file mode 100644
index 000000000..dc1e0643f
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/01-configloader-replacement-semantics.md
@@ -0,0 +1,26 @@
+# Clarify ConfigLoader replacement semantics
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation
+
+## Problem
+
+The guide says loading with `addProperties=false` “clears everything.” The implementation clears cached file/programmatic values and then reapplies system properties and environment variables.
+
+## Plan
+
+Describe replacement as replacing loaded defaults while retaining higher-precedence runtime sources. Update every “ditch all defaults” example.
+
+## Acceptance criteria
+
+- [x] No example claims system or environment values are cleared.
+- [x] Replacement and additive loading are contrasted precisely.
+- [x] A test or source anchor supports the stated precedence.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/configuration.hbs` now distinguishes merging from replacement, explains that runtime sources are reapplied, and uses the correct term “system properties.”
+- Implementation: `modules/core-module/src/main/java/org/simplejavamail/config/ConfigLoader.java:360-365,375-396,400-429` clears resolved values only in replacement mode and resolves system properties before environment variables before the supplied source.
+- Tests: `modules/simple-java-mail/src/test/java/org/simplejavamail/config/ConfigLoaderTest.java:131-139,237-258,397-410,413-451` anchors system-property retention, additive loading, environment loading, and runtime-source precedence.
+- Verification: `npm run check` and `npm run verifyLinks` passed on 6 August 2026.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/02-extra-properties-precedence.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/02-extra-properties-precedence.md
new file mode 100644
index 000000000..3ec114752
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/02-extra-properties-precedence.md
@@ -0,0 +1,27 @@
+# Resolve extra-property precedence
+
+- Status: Done
+- Priority: Medium
+- Work: Code decision, tests, documentation
+
+## Problem
+
+The general precedence contract says system properties override environment and file values. For `simplejavamail.extraproperties.*`, current insertion order lets file values overwrite both.
+
+## Plan
+
+Decide whether the general precedence contract also applies to the wildcard namespace. Prefer fixing implementation ordering; otherwise document the exception prominently.
+
+## Acceptance criteria
+
+- [x] Precedence is intentional and covered by tests.
+- [x] Fixed and wildcard property behavior is documented without contradiction.
+- [x] Migration impact is noted if implementation ordering changes.
+
+## Evidence
+
+- Issue: [#685](https://github.com/bbottema/simple-java-mail/issues/685), linked to the original extra-property work in #279 and assigned to v9.2.0.
+- Implementation: `modules/core-module/src/main/java/org/simplejavamail/config/ConfigLoader.java` now applies file, environment, then system values so later entries have the documented higher priority.
+- Tests: `ConfigLoaderTest.loadPropertiesExtraPropertiesFollowStandardPrecedence` covers system-over-environment and environment-over-file collisions; all 20 `ConfigLoaderTest` tests pass on JDK 8.
+- Documentation: `simplejavamail.org/src/pages/configuration.hbs` states the shared precedence contract, and `migration-notes-9.2.0.hbs` explains the duplicate-key impact.
+- Release notes: `README.md`, `RELEASE.txt`, and `RELEASE_HISTORY.md` record the v9.2.0 behavior change.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/03-custom-session-proxy.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/03-custom-session-proxy.md
new file mode 100644
index 000000000..d7eec5bf6
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/03-custom-session-proxy.md
@@ -0,0 +1,36 @@
+# Restore proxying with a custom Session
+
+- Status: Done
+- Priority: Low
+- Work: Bug fix, Javadoc, regression tests
+
+## Problem
+
+`usingSession(session).withProxy(...)` was presented as self-contained, but stopped configuring the Session's SOCKS route when the 5.0 builder overhaul removed the transport-strategy marker from caller-supplied Sessions. Anonymous proxy settings were retained only in `ProxyConfig`; authenticated proxying started a local bridge without pointing Jakarta Mail at it.
+
+## Resolution
+
+Restore the original proxy contract without taking ownership of the caller's Session configuration. When a supplied Session has no Simple Java Mail transport-strategy marker, `withProxy(...)` overwrites only the supported `mail.smtp.socks.host` and `mail.smtp.socks.port` route:
+
+- anonymous proxying points Jakarta Mail directly at the upstream proxy;
+- authenticated proxying points Jakarta Mail at Simple Java Mail's local authentication bridge;
+- SMTP server, TLS, authentication, transport and custom properties remain untouched.
+
+The builder Javadoc now calls out proxy routing as the deliberate exception to the otherwise preconfigured-Session contract.
+
+## Acceptance criteria
+
+- [x] Anonymous custom-Session proxying configures the upstream SOCKS route.
+- [x] Authenticated custom-Session proxying configures the local bridge route.
+- [x] Unrelated caller-supplied Session properties remain unchanged.
+- [x] The custom-Session ownership boundary is explicit in the public Javadoc.
+- [x] Regression tests cover both proxy modes on Java 8.
+
+## Evidence
+
+- Tracking issue: `#686`
+- Original proxy request and implementation: `#38`, commit `d61baa63f4d90e2c776ef1f327f1d49b49657776`
+- Custom-Session regression lineage: `#111`, commits `b1d789bb76663293d31cb3b26e37fa6f5982f0f2` and `eaa767778fa97f4f889de23c884af9abdba2d93c`
+- Public contract: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerFromSessionBuilder.java`
+- Proxy routing: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerImpl.java`
+- Regression coverage: `modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/MailerTest.java`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/04-smtps-proxy-compatibility.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/04-smtps-proxy-compatibility.md
new file mode 100644
index 000000000..94b0a3f23
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/04-smtps-proxy-compatibility.md
@@ -0,0 +1,34 @@
+# Restore SMTPS proxy compatibility
+
+- Status: Done
+- Priority: Low
+- Work: Bug fix, integration tests and documentation
+
+## Problem
+
+Simple Java Mail rejected every SOCKS proxy combined with `TransportStrategy.SMTPS`, even though Angus Mail supports `mail.smtps.socks.host` and
+`mail.smtps.socks.port`. The restriction dated from 2016 and was repeated in `ProxyConfig` Javadoc. A Spring example also combined SMTPS with proxy
+credentials but omitted the proxy host and port.
+
+## Resolution
+
+Removed the obsolete guard and retained transport-specific property selection, so SMTPS now uses Angus Mail's `mail.smtps.socks.*` path. Added a local
+SOCKS-to-implicit-TLS integration test for anonymous proxying and for Simple Java Mail's authenticated-proxy bridge path. Corrected the API Javadoc and
+completed the Spring configuration example.
+
+## Acceptance criteria
+
+- [x] Anonymous SOCKS routing works with SMTPS.
+- [x] Authenticated-proxy bridge routing works with SMTPS.
+- [x] The integration test proves the SOCKS server accepted the connection and the TLS SMTP endpoint received the message.
+- [x] Proxy API guidance no longer repeats the obsolete restriction.
+- [x] The Spring sample forms a valid SMTPS and proxy configuration.
+
+## Evidence
+
+- GitHub issue: [#687](https://github.com/bbottema/simple-java-mail/issues/687)
+- Regression origin: `322ff855f7b946b7ee06fcb078c333872a66b369` (first released in 4.1.1)
+- Implementation: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerImpl.java`
+- Integration test: `modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/MailerSOCKSLiveTest.java`
+- API guidance: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/config/ProxyConfig.java`
+- Website guidance: `simplejavamail.org/src/pages/features.hbs` and `simplejavamail.org/src/pages/configuration.hbs`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/05-mailer-validation-scope.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/05-mailer-validation-scope.md
new file mode 100644
index 000000000..5ca9374d0
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/05-mailer-validation-scope.md
@@ -0,0 +1,34 @@
+# Define Mailer.validate scope
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation, Javadocs
+
+## Problem
+
+The website said `mailer.validate(email)` performed all checks or applied configured message rules. The API Javadoc also claimed that subject and
+content were required and referred to undefined "NPM notification flags." In practice the method validates the supplied `Email` as-is for sender and
+recipient presence, addresses and injection-sensitive fields. It does not apply mailer defaults or overrides, convert to MIME, or check final encoded
+message size.
+
+## Resolution
+
+Documented the exact direct-validation scope, including strict and lenient modes. The API now distinguishes validation of the supplied instance from
+the send path, which applies defaults and overrides before validation and performs MIME conversion and size enforcement afterward. Capabilities and
+Get Started now use the same concrete wording.
+
+## Acceptance criteria
+
+- [x] Validation scope is listed concretely.
+- [x] Defaults, overrides, conversion, and size checks are described separately.
+- [x] Strict and lenient validation modes are distinguished.
+- [x] Features and Get Started pages use consistent wording.
+
+## Evidence
+
+- Public API: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java`
+- Validation helpers: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/MailerHelper.java`
+- Documentation: `simplejavamail.org/src/pages/features.hbs`
+- Get Started: `simplejavamail.org/src/pages/download.hbs`
+- Send preparation: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerImpl.java`
+- MIME size enforcement: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/SessionBasedEmailToMimeMessageConverter.java`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/06-clear-validator-scope.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/06-clear-validator-scope.md
new file mode 100644
index 000000000..018d4798b
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/06-clear-validator-scope.md
@@ -0,0 +1,25 @@
+# Define clearEmailValidator scope
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation and source Javadocs
+
+## Problem
+
+`clearEmailValidator()` is described as turning validation off. It removes the configurable JMail address validator, while completeness, encoded-word, and CRLF/injection checks still run.
+
+## Plan
+
+Rename the example comment and list the checks that remain active. Link to `disablingAllClientValidation(...)` without presenting it as a routine recommendation.
+
+## Acceptance criteria
+
+- [x] “Turn off validation” is removed.
+- [x] Remaining validation layers are explicit.
+- [x] Security consequences of broader disabling are clear.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/features.hbs:1345`
+- API contract: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerGenericBuilder.java:769`
+- Validation implementation: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/MailerHelper.java:58` and `:139`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/07-starting-blank-and-defaults.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/07-starting-blank-and-defaults.md
new file mode 100644
index 000000000..a40512960
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/07-starting-blank-and-defaults.md
@@ -0,0 +1,40 @@
+# Put governance options after email starters
+
+- Status: Done
+- Priority: Medium
+- Work: API, tests, Javadocs, website documentation, and release notes
+
+## Problem
+
+`EmailBuilder.startingBlank()` is described as leaving the builder with defaults. It starts blank; mailer defaults and overrides are applied later during completion or send-time governance.
+
+The legacy pre-start opt-outs also contain two propagation defects: `startingBlank()` drops `ignoreOverrides`, while `copying(Email)` calls `ignoringOverrides(...)` twice and never propagates `ignoreDefaults`.
+
+## Plan
+
+Explain builder initialization, `buildEmail()`, `buildEmailCompletedWithDefaultsAndOverrides()`, and send-time governance as separate stages.
+
+Keep governance opt-outs on `EmailPopulatingBuilder`, after any starter has selected what kind of email is being built. Remove the historical pre-start API that existed to suppress eager builder defaults before the 8.0 governance overhaul.
+
+## Acceptance criteria
+
+- [x] `startingBlank()` is described as blank.
+- [x] Eligibility for later mailer defaults is not confused with already-applied values.
+- [x] The two build paths are contrasted with a small example.
+- [x] No-argument governance opt-outs are available after every email starter.
+- [x] The obsolete pre-start governance API is removed for 9.2.0.
+
+## Resolution
+
+- Added `EmailPopulatingBuilder.ignoringDefaults()` and `ignoringOverrides()` as Java convenience methods; the conditional boolean and CLI forms remain.
+- Removed governance state and methods from `EmailStartingBuilder`, along with `EmailBuilder.ignoringDefaults()`.
+- Updated internal conversion and test call sites to start the email operation first.
+- Added behavioral tests showing that the follow-up options control later default and override application.
+- Documented the source-level migration in the 9.2.0 notes and release notes.
+- Recorded the defects, their pre-8.0 lineage, and the chosen API correction in GitHub issue #689.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/features.hbs:203-207`
+- Builder creation: `modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailStartingBuilderImpl.java:65`
+- Build behavior: `modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailPopulatingBuilderImpl.java:397`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/08-converter-header-preservation.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/08-converter-header-preservation.md
new file mode 100644
index 000000000..e0137df49
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/08-converter-header-preservation.md
@@ -0,0 +1,31 @@
+# Describe converter header preservation accurately
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation
+
+## Problem
+
+Converter documentation promises headers remain intact, but structural headers are deliberately filtered and reconstructed.
+
+## Plan
+
+State that semantic fields and non-structural custom headers are preserved where possible. Link or list ignored headers such as `Received`, `Date`, address fields, `Subject`, content headers, and `Reply-To`.
+
+## Acceptance criteria
+
+- [x] “Headers intact” is removed.
+- [x] Structural reconstruction is explained.
+- [x] The documentation links to the canonical ignored-header list.
+
+## Resolution
+
+- Replaced the verbatim-preservation claim with the actual conversion boundary.
+- Distinguished semantic Email fields and preserved custom headers from reconstructed MIME structure and omitted transport history.
+- Linked the source-controlled filter so the exact header set does not have to be duplicated in prose.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/features.hbs:1424-1434`
+- Filter: `modules/core-module/src/main/java/org/simplejavamail/api/internal/general/HeadersToIgnoreWhenParsingExternalEmails.java:20-63`
+- Application: `modules/simple-java-mail/src/main/java/org/simplejavamail/converter/EmailConverter.java:827-832`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/09-generated-attachment-content-ids.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/09-generated-attachment-content-ids.md
new file mode 100644
index 000000000..67b06d8a3
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/09-generated-attachment-content-ids.md
@@ -0,0 +1,32 @@
+# Document generated attachment Content-IDs
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation
+
+## Problem
+
+The MIME example shows `Content-ID: ` for a normal attachment. Current fallback IDs are opaque `sjm-@simplejavamail.generated` values.
+
+## Plan
+
+Show an opaque generated placeholder and separately demonstrate the overload for a caller-supplied stable ID.
+
+## Acceptance criteria
+
+- [x] Default MIME output uses the current ID shape.
+- [x] Stable-ID behavior is shown only with an explicit ID.
+- [x] Migration notes and feature docs agree.
+
+## Resolution
+
+- Replaced the filename-shaped fallback in the attachment MIME example with a representative `sjm-@simplejavamail.generated` value.
+- Explained that a new opaque attachment ID is generated when the MimeMessage is produced unless the caller supplies one.
+- Clarified that embedded images retain their resource-name fallback so HTML `cid:` references continue to work.
+- Kept stable-ID behavior tied to the explicit Content-ID overload.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/features.hbs:890-901`
+- Generator: `modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageHelper.java:343-355`
+- Migration note: `simplejavamail.org/src/pages/migration-notes-9.0.0.hbs:215-224`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/10-serialization-transient-fields.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/10-serialization-transient-fields.md
new file mode 100644
index 000000000..ec2502f77
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/10-serialization-transient-fields.md
@@ -0,0 +1,47 @@
+# Overhaul Email serialization
+
+- Status: Done
+- Priority: Medium
+- Work: API, compatibility handling, tests, Javadocs, website documentation, and release notes
+
+## Problem
+
+The serialization section lists removed `Email.dkimPrivateKeyInputStream` and does not accurately reflect current transient state. More importantly,
+native Java serialization silently drops every attachment `DataSource`, the forwarded `MimeMessage`, and S/MIME signing configuration. A restored
+`Email` therefore looks intact but can no longer reproduce the message that was serialized.
+
+## Plan
+
+Make Java serialization a send-ready content snapshot. Materialize resource data into repeatable byte sources, preserve forwarded MIME content and
+S/MIME signing configuration, and define a clear compatibility failure for attachment bytes that pre-9.2.0 streams never contained. Preserve content
+rather than promising to reconstruct the runtime behavior of custom `DataSource` implementations.
+
+## Acceptance criteria
+
+- [x] Resource bytes and MIME metadata survive a Java serialization round trip.
+- [x] Forwarded MIME content and S/MIME signing configuration survive and can still be sent.
+- [x] Custom `DataSource` implementations are documented and tested as read-only content snapshots, not reconstructed objects.
+- [x] A genuine 9.1.7 fixture remains inspectable and fails clearly only when unavailable legacy content is used.
+- [x] Javadocs, feature documentation, migration notes, and release notes describe the same contract and security boundary.
+
+## Resolution
+
+- Added versioned custom serialization to `Email` and `AttachmentResource` without changing their existing `serialVersionUID` values.
+- Resource input streams are consumed and closed during serialization, then restored as independent, repeatable, read-only byte-backed data sources.
+- Forwarded messages are stored as RFC 822 bytes and restored with a neutral Jakarta Mail session.
+- `SmimeSigningConfig` and `Pkcs12Config` are now serializable, including the key material and passwords needed to preserve signing behavior.
+- Pre-9.2.0 resource metadata remains readable. Accessing bytes those releases never serialized now produces an explicit version-specific error instead
+ of a later null failure.
+- Added unit coverage for custom, pre-encoded, embedded and decrypted resources; custom source behavior; unreadable sources; forwarded messages;
+ S/MIME configuration; and legacy streams. Added a local SMTP round trip that signs and sends the restored email.
+- Recorded the behavior, historical design lineage, migration impact, and sensitive-data warning in GitHub issue #690.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/features.hbs` and `simplejavamail.org/src/pages/migration-notes-9.2.0.hbs`
+- Models: `modules/core-module/src/main/java/org/simplejavamail/api/email/Email.java` and `AttachmentResource.java`
+- Sensitive configuration: `modules/core-module/src/main/java/org/simplejavamail/api/email/config/SmimeSigningConfig.java` and
+ `modules/core-module/src/main/java/org/simplejavamail/api/mailer/config/Pkcs12Config.java`
+- Compatibility and round-trip tests: `modules/simple-java-mail/src/test/java/org/simplejavamail/api/email/EmailSerializationTest.java`
+- Send behavior: `modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/EmailSerializationSmtpTest.java`
+- Legacy fixture: `modules/simple-java-mail/src/test/resources/serialization/email-9.1.7.base64`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/11-bounce-address-implementation.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/11-bounce-address-implementation.md
new file mode 100644
index 000000000..a38739eca
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/11-bounce-address-implementation.md
@@ -0,0 +1,32 @@
+# Update bounce-address implementation details
+
+- Status: Done
+- Priority: Medium
+- Work: Website documentation and Javadocs
+
+## Problem
+
+The bounce section says `withBounceTo` sets `mail.smtp.from` on the shared Session. Current behavior applies envelope-from per message through a specialized SMTP message wrapper.
+
+## Plan
+
+Describe the observable envelope-from result without anchoring users to the obsolete Session implementation detail. Explain the difference from `Reply-To`.
+
+## Acceptance criteria
+
+- [x] The shared Session is no longer described as mutated.
+- [x] Per-message envelope-from semantics are clear.
+- [x] Bounce and Reply-To purposes are distinguished.
+
+## Resolution
+
+- Replaced the obsolete `mail.smtp.from`/shared-Session explanation with the current per-message SMTP envelope behavior.
+- Distinguished the transport-level envelope sender from the `Reply-To` message header and explained how `Return-Path` relates to it.
+- Removed the display-name example because SMTP `MAIL FROM` carries only the address.
+- Updated the public builder Javadocs with the same contract. No sending behavior changed.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/features.hbs`
+- Public API: `modules/core-module/src/main/java/org/simplejavamail/api/email/EmailPopulatingBuilder.java`
+- Implementation: `modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/SpecializedMimeMessageProducer.java`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/12-future-failure-semantics.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/12-future-failure-semantics.md
new file mode 100644
index 000000000..e186cb971
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/12-future-failure-semantics.md
@@ -0,0 +1,35 @@
+# Explain synchronous and asynchronous failures
+
+- Status: Done
+- Priority: Medium
+- Work: API, tests, documentation, release notes
+
+## Problem
+
+The future guidance implies all failures are represented by the returned `CompletableFuture`. Preparation and validation can throw before asynchronous scheduling, and synchronous sends can throw before returning an already-completed future.
+
+## Resolution
+
+Version 9.2.0 gives an asynchronous operation one normal failure channel. Defaults and overrides, validation, scheduling, conversion, connection and transport
+failures now complete the returned `CompletableFuture` exceptionally. Preparation and validation still run on the calling thread; only their reporting path
+changed. Synchronous calls continue to throw directly, and a null top-level argument remains an immediate contract violation.
+
+The Java 8 implementation adds an internal failed-future helper and catches `RuntimeException` only around asynchronous operation preparation and scheduling.
+Fatal `Error` instances are not translated. The same rule is applied to send, receipt send, simple batch and connection-test entry points.
+
+Public Javadocs, the capabilities and diagnostics pages, the 9.2 migration guide, and both release-note sources now describe the same contract. GitHub issue
+[#691](https://github.com/bbottema/simple-java-mail/issues/691) records the behavior change and its lineage through #148, #294 and #367.
+
+## Acceptance criteria
+
+- [x] Synchronous throws and asynchronous completion are distinguished.
+- [x] Preparation/validation timing is explicit.
+- [x] Diagnostics links back to the async handling example.
+- [x] Tests cover synchronous validation, asynchronous validation and transport, scheduling failures across every async entry point, and the null-argument boundary.
+
+## Evidence
+
+- Public contract: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java`
+- Implementation: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerImpl.java`
+- Tests: `modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/ResultHandlingTest.java`
+- Documentation: `simplejavamail.org/src/pages/features.hbs`, `debugging.hbs`, and `migration-notes-9.2.0.hbs`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/13-batch-retry-claims.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/13-batch-retry-claims.md
new file mode 100644
index 000000000..033681d89
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/13-batch-retry-claims.md
@@ -0,0 +1,26 @@
+# Remove batch retry claims
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation, Javadoc
+
+## Problem
+
+The batch module is recommended for retries, but it has no automatic retry policy. The same page later says failures are propagated without recovery.
+
+## Resolution
+
+Removed retry-oriented wording from the public Mailer Javadoc, the connection-reuse guidance and the 9.0 migration notes. The batch module is now described
+in terms of asynchronous queueing, throughput, pooled connections and clustered load balancing. Failed sends are reported to the application, which owns any
+retry policy.
+
+## Acceptance criteria
+
+- [x] No batch-module description promises automatic retries.
+- [x] Failure and retry ownership are consistent across pages.
+- [x] Cluster wording agrees with the dedicated cluster correction item.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/configuration.hbs` and `migration-notes-9.0.0.hbs`
+- Public API: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/14-outlook-module-dependencies.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/14-outlook-module-dependencies.md
new file mode 100644
index 000000000..f4ca3bdd1
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/14-outlook-module-dependencies.md
@@ -0,0 +1,26 @@
+# Update Outlook module dependencies
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation, build-derived inventory
+
+## Problem
+
+The module page lists Kryo and kryo-serializers for Outlook conversion, but the current Outlook module directly depends on `outlook-message-parser` and no longer declares Kryo.
+
+## Resolution
+
+Verified the module POM and runtime dependency tree, then replaced the stale Kryo inventory with the one direct Outlook-specific dependency:
+`org.simplejavamail:outlook-message-parser`. The page now explains that this parser handles Outlook OLE content through Apache POI and related conversion
+libraries without freezing its complete transitive dependency tree into the website.
+
+## Acceptance criteria
+
+- [x] Kryo dependencies are removed from the Outlook section.
+- [x] Direct dependencies match `modules/outlook-module/pom.xml`.
+- [x] The transitive implementation is described without presenting an exhaustive list.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/modules.hbs:101-111`
+- POM: `modules/outlook-module/pom.xml:25-38`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/15-karaf-artifact-note.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/15-karaf-artifact-note.md
new file mode 100644
index 000000000..76b4cb0f2
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/15-karaf-artifact-note.md
@@ -0,0 +1,28 @@
+# Remove the obsolete Karaf artifact note
+
+- Status: Done
+- Priority: Low
+- Work: Documentation
+
+## Problem
+
+The page shows `karaf-module` and then claims the artifact is temporarily named `simplejavamail-karaf-feature`.
+
+## Plan
+
+Remove the obsolete warning and verify every Karaf coordinate against the current module POM.
+
+## Resolution
+
+Removed the temporary-name warning from the module page and replaced the old artifact and feature names in the Karaf module README. The documented repository coordinate now uses `org.simplejavamail:karaf-module`, and the install command uses the `karaf-module` feature name generated by the module build.
+
+## Acceptance criteria
+
+- [x] Only the current `karaf-module` artifact name appears.
+- [x] Coordinates match the POM and generated feature descriptor.
+- [x] Search finds no remaining temporary-name references outside this historical audit record.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/modules.hbs:196-209`
+- POM: `modules/karaf-module/pom.xml:14-16`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/16-spring-provided-dependencies.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/16-spring-provided-dependencies.md
new file mode 100644
index 000000000..e704a96e9
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/16-spring-provided-dependencies.md
@@ -0,0 +1,28 @@
+# Describe Spring dependencies as provided
+
+- Status: Done
+- Priority: Low
+- Work: Documentation
+
+## Problem
+
+The module page says Spring support adds Spring core. The POM declares Spring core, context, and beans with `provided` scope; the application/runtime supplies them.
+
+## Plan
+
+Describe the module as integrating with an existing Spring or Spring Boot runtime and list the provided-scope expectation.
+
+## Resolution
+
+Reworded the module page to explain that the application supplies `spring-core`, `spring-context`, and `spring-beans`. The page no longer suggests that adding `spring-module` brings those libraries onto the runtime classpath.
+
+## Acceptance criteria
+
+- [x] No text promises Spring libraries transitively.
+- [x] The expected host application responsibility is clear.
+- [x] Module documentation matches the POM scopes.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/modules.hbs:181-193`
+- POM: `modules/spring-module/pom.xml:35-49`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/17-log4j-example-wording.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/17-log4j-example-wording.md
new file mode 100644
index 000000000..e97a1cae8
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/17-log4j-example-wording.md
@@ -0,0 +1,28 @@
+# Describe Log4j configuration as an example
+
+- Status: Done
+- Priority: Low
+- Work: Documentation
+
+## Problem
+
+Diagnostics says Maven users receive default Log4j2 XML configuration. The facade ships `log4j2_example.xml`, not an active default `log4j2.xml`.
+
+## Plan
+
+Call it an example configuration that users may copy and adapt. Make clear that SLF4J binding and logging configuration remain application choices.
+
+## Resolution
+
+Replaced the default-configuration claim with an explicit explanation that the application chooses its SLF4J backend and configuration. The bundled `log4j2_example.xml` resource is now presented only as a starting point that Log4j 2 users may copy and adapt.
+
+## Acceptance criteria
+
+- [x] “Default XML configuration” is removed.
+- [x] The resource name is stated accurately.
+- [x] The page does not imply Simple Java Mail selects the application logging backend.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/debugging.hbs:183-190`
+- Resource: `modules/simple-java-mail/src/main/resources/log4j2_example.xml`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/18-cli-exec-plugin-version.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/18-cli-exec-plugin-version.md
new file mode 100644
index 000000000..68d87a822
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/18-cli-exec-plugin-version.md
@@ -0,0 +1,28 @@
+# Update the CLI exec-plugin version
+
+- Status: Done
+- Priority: Low
+- Work: Documentation
+
+## Problem
+
+The CLI Maven example pins `exec-maven-plugin` 1.6.0 while the current project uses 3.6.3.
+
+## Plan
+
+Update the example and decide whether plugin versions should come from the website manifest/release inventory or a lightweight POM-derived check.
+
+## Resolution
+
+Updated the CLI Maven example from `exec-maven-plugin` 1.6.0 to 3.6.3, matching the version used by the CLI module itself. This build-tool version is maintained with the example rather than added to the website release inventory: it changes independently of Simple Java Mail releases, while an automated cross-repository POM check would unnecessarily couple the website build to the library repository.
+
+## Acceptance criteria
+
+- [x] The published plugin version matches the current CLI module build.
+- [x] The example runs successfully.
+- [x] The maintenance decision is recorded without expanding release-update automation.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/cli.hbs:204-222`
+- Current build: `modules/cli-module/pom.xml:131-132`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/19-base64-terminology.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/19-base64-terminology.md
new file mode 100644
index 000000000..11eb67194
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/03-behavior-and-operations/19-base64-terminology.md
@@ -0,0 +1,23 @@
+# Remove Base64 security terminology
+
+- Status: Done
+- Priority: Low
+- Work: Documentation
+
+## Problem
+
+Base64 content-transfer encoding is called an extra layer of obfuscation. That can be read as a security or confidentiality property, which Base64 does not provide.
+
+## Plan
+
+Describe Base64 in terms of binary safety and transport compatibility. Explicitly state that it is encoding, not encryption or protection.
+
+## Acceptance criteria
+
+- [x] “Obfuscation” is removed from the content-encoding section.
+- [x] The explanation focuses on transport characteristics.
+- [x] No encoding is presented as a security mechanism.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/features.hbs:565-568`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/01-mailer-lifecycle.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/01-mailer-lifecycle.md
new file mode 100644
index 000000000..b218937c1
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/01-mailer-lifecycle.md
@@ -0,0 +1,38 @@
+# Add Mailer lifecycle and ownership guidance
+
+- Status: Done
+- Priority: High
+- Work: Documentation, lifecycle tests
+
+## Gap
+
+The website warns only that batch connection pools keep the JVM alive. A non-batch asynchronous Mailer also owns a single-thread executor by default. `Mailer` is `AutoCloseable`, Spring closes managed mailer beans, and caller-provided executors remain caller-owned.
+
+## Plan
+
+Add one lifecycle section covering synchronous mailers, non-batch async executors, batch pools, Spring destruction, caller-owned executors, and waiting for futures before close. Link to it from every async and batch introduction.
+
+## Resolution
+
+Added one lifecycle and resource-ownership section to Configuration, with an ownership table and safe synchronous and asynchronous try-with-resources examples. The separate async, batch and module introductions now link to that section instead of each carrying their own shutdown instructions.
+
+The public Javadoc now presents `close()` as the normal lifecycle operation and explains how the historically named `shutdownConnectionPool()` also shuts down a Mailer-owned executor without batch-module. The `withExecutorService(...)` Javadoc now reflects that custom executors work with and without batch-module and remain caller-owned.
+
+Lifecycle tests verify that the default non-batch executor uses a non-daemon worker and is shut down with the Mailer, while a caller-provided executor is left running.
+
+## Acceptance criteria
+
+- [x] Users can identify who owns and closes every executor/pool.
+- [x] `close()` and `shutdownConnectionPool()` are explained together.
+- [x] The async try-with-resources caveat is explicit.
+- [x] Non-batch JVM-liveness behavior is documented.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/configuration.hbs:750-842`
+- Cross-links: `simplejavamail.org/src/pages/features.hbs:462-464,497-499`; `simplejavamail.org/src/pages/modules.hbs:64-70`
+- API: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java:218-250`
+- Custom executor API: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerGenericBuilder.java:400-425`
+- Default executor: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerGenericBuilderImpl.java:786-790`
+- Close behavior: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerImpl.java:552-558`
+- Lifecycle tests: `modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/internal/MailerImplTest.java:61-95`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/02-reply-and-body-api.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/02-reply-and-body-api.md
new file mode 100644
index 000000000..be086ab63
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/02-reply-and-body-api.md
@@ -0,0 +1,33 @@
+# Cover reply and body-editing APIs
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation
+
+## Gap
+
+The website guide omits `replyingToAll(...)`, custom HTML quoting templates, multi-recipient `withReplyTo(...)`, file-backed bodies, `appendText*`, `prependTextHTML`, and body-clearing methods.
+
+## Plan
+
+Expand the reply section into a compact API family overview with one complete reply-all example and one body-editing example. Link to Javadocs for overload detail.
+
+## Acceptance criteria
+
+- [x] Reply and reply-all behavior are both shown.
+- [x] `%s` quoting-template requirements are explicit.
+- [x] Plain and HTML body editing remain paired where appropriate.
+- [x] Reply-To and message recipients are not conflated.
+
+## Resolution
+
+Expanded the Capabilities page's conversation section into a compact reply, reply-all, and forwarding reference. It now includes a custom quote-template example, paired plain/HTML body editing, file-backed bodies, clear operations, and an explicit distinction between `Reply-To` headers and outgoing recipients. The section links to the complete builder Javadocs for overload-level detail.
+
+## Evidence
+
+- Website guide: `simplejavamail.org/src/pages/features.hbs:1565-1727`
+- Reply facade: `modules/simple-java-mail/src/main/java/org/simplejavamail/email/EmailBuilder.java:38-114`
+- Reply contract: `modules/core-module/src/main/java/org/simplejavamail/api/email/EmailStartingBuilder.java:80-190`
+- Reply implementation: `modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailStartingBuilderImpl.java:140-177`
+- Live reply-all coverage: `modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/MailerLiveTest.java:621-646`
+- Body API: `modules/core-module/src/main/java/org/simplejavamail/api/email/EmailPopulatingBuilder.java:180-224,355-480,1235-1240`
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/03-oauth2-token-lifecycle.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/03-oauth2-token-lifecycle.md
new file mode 100644
index 000000000..73189fd10
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/03-oauth2-token-lifecycle.md
@@ -0,0 +1,37 @@
+# Support refresh-aware OAuth2 access-token providers
+
+- Status: Done
+- Priority: Medium
+- Work: Code and documentation
+
+## Gap
+
+OAuth2 examples show one access token in the password position. That works for a short-lived mailer, but a reusable mailer or connection pool has no way to obtain a current token after the original one expires.
+
+## Plan
+
+Add a plain-Java, thread-safe access-token provider to the generic Mailer builder. Resolve it only when a physical SMTP connection is opened or reconnected, including pooled connections. Keep the existing fixed-token form, add optional Spring bean discovery without a Spring Security dependency, and document that the provider owns acquisition, caching and refresh.
+
+## Acceptance criteria
+
+- [x] A regular or custom-session Mailer can use an `OAuth2AccessTokenProvider`.
+- [x] Direct, open-connection, simple-batch and pooled paths resolve the provider at the physical connection boundary.
+- [x] Fixed tokens remain supported; mixed fixed/provider configuration fails clearly.
+- [x] Provider failures and blank results fail without exposing token material.
+- [x] Spring can auto-detect one provider bean without depending on Spring Security.
+- [x] Documentation distinguishes access tokens from provider-owned acquisition and refresh.
+- [x] The CLI and property surface do not pretend a runtime provider is string-configurable.
+
+## Evidence
+
+- Simple Java Mail issue: https://github.com/bbottema/simple-java-mail/issues/692
+- SMTP connection pool issue: https://github.com/simple-java-mail/smtp-connection-pool/issues/9
+- SMTP connection pool release: https://github.com/simple-java-mail/smtp-connection-pool/releases/tag/3.1.0
+- Historical fixed-token support: https://github.com/bbottema/simple-java-mail/issues/421
+- Public API: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/config/OAuth2AccessTokenProvider.java` and `MailerGenericBuilder.java`
+- Connection-time validation and resolution: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/OAuth2AccessTokenResolver.java` and `TransportConnectionHelper.java`
+- Batch and pool bridge: `modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/BatchSupport.java`, using `smtp-connection-pool` 3.1.0
+- Spring bean integration: `modules/spring-module/src/main/java/org/simplejavamail/springsupport/SimpleJavaMailSpringSupport.java`
+- Simple Java Mail commits: `7d732c1b` (implementation and tests), `b565bfb5` (pool dependency), and `7d67fe0c` (release documentation)
+- Website documentation commit: `be2bd8c` on `codex/website-relaunch`
+- Verification: full Java 8 `mvn clean verify -Ppublish-cli -DexcludeLiveServerTests=true`; focused provider, physical-connection, batch/pool, open-connection and Spring tests; website check/build and 1,314 internal links
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/04-open-connection-custom-mailer.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/04-open-connection-custom-mailer.md
new file mode 100644
index 000000000..e12aaba42
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/04-open-connection-custom-mailer.md
@@ -0,0 +1,29 @@
+# Document withOpenConnection limitations
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation
+
+## Gap
+
+The website shows `withOpenConnection(...)` but does not say it rejects custom mailers because Simple Java Mail does not own their underlying connection.
+
+## Plan
+
+Add the limitation next to every open-connection example and link the custom-mailer section to the normal alternative.
+
+## Acceptance criteria
+
+- [x] The custom-mailer restriction appears before users copy the example.
+- [x] The reason—connection ownership—is explained.
+- [x] Features and configuration pages agree.
+
+## Evidence
+
+- Documentation: `simplejavamail.org/src/pages/features.hbs`, `configuration.hbs`, and `migration-notes-9.0.0.hbs`
+- Custom-mailer guidance: `simplejavamail.org/src/pages/features.hbs#section-custom-mailer`
+- Website commit: `a839739` on `codex/website-relaunch`
+- Contract: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java`
+- Runtime guard: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/SendMailsWithOpenConnectionClosure.java`
+- Regression coverage: `MailerTest.testOpenConnection_sendEmails_rejectsCustomMailer`
+- Verification: website check/build and 1,318 internal links
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/05-configloader-snapshot-timing.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/05-configloader-snapshot-timing.md
new file mode 100644
index 000000000..e8a6a4b75
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/05-configloader-snapshot-timing.md
@@ -0,0 +1,26 @@
+# Explain ConfigLoader snapshot timing
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation
+
+## Gap
+
+The configuration guide does not say that reloading ConfigLoader affects subsequently created builders/mailers rather than live-reconfiguring existing objects.
+
+## Plan
+
+Add a concise timing model and an example that reloads configuration before creating a replacement Mailer.
+
+## Acceptance criteria
+
+- [x] Existing Mailers are clearly described as unaffected.
+- [x] Builder/object creation timing is explicit.
+- [x] Multi-environment examples create objects after loading their intended configuration.
+
+## Evidence
+
+- `ConfigLoader` Javadocs now describe its process-wide defaults, replacement-object semantics, and the requirement to start fresh builders after a reload for all three `loadProperties(...)` overloads.
+- `simplejavamail.org/src/pages/configuration.hbs` now has a dedicated "When changes take effect" section, a replacement-Mailer example, and explicit load-before-build ordering in the multi-environment example.
+- Architectural follow-up: [GitHub issue #693](https://github.com/bbottema/simple-java-mail/issues/693) tracks replacing the static loader with instance-based, injectable configuration for milestone `10.0.0`.
+- Verification: core-module Javadocs generated successfully; website type/check task, production build, and 1,321-link internal link check passed.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/06-environment-variable-syntax.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/06-environment-variable-syntax.md
new file mode 100644
index 000000000..783b064f3
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/06-environment-variable-syntax.md
@@ -0,0 +1,27 @@
+# Document environment-variable syntax
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation, tests
+
+## Gap
+
+Environment variables are listed as a source, but the fixed-key uppercase/underscore mapping is not shown. Wildcard extra-property and per-cluster namespaces are scanned differently and have limitations.
+
+## Plan
+
+Provide concrete fixed-key examples such as `SIMPLEJAVAMAIL_SMTP_HOST`, then document wildcard namespace handling and platform caveats separately.
+
+## Acceptance criteria
+
+- [x] At least three common environment-variable examples are shown.
+- [x] Fixed and wildcard mapping rules are distinguished.
+- [x] Tests anchor case and separator conversion.
+- [x] Cluster environment limitations are explicit.
+
+## Evidence
+
+- `simplejavamail.org/src/pages/configuration.hbs` now shows four common fixed-key environment variables, explains the uppercase/dot-to-underscore conversion, and separates it from literal wildcard scanning.
+- The guide explicitly recommends property files, exact dotted JVM system properties, or Java configuration for wildcard extra-property and per-cluster namespaces because literal dotted environment names are not portable.
+- `ConfigLoaderTest` now covers a multi-segment fixed key, literal dotted wildcard names, and the unsupported uppercase/underscore wildcard form without changing production behavior.
+- Verification: the targeted `ConfigLoaderTest` suite passed on JDK 8; website type/check task, production build, and 1,323-link internal link check passed.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/07-proxy-bridge-port-collisions.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/07-proxy-bridge-port-collisions.md
new file mode 100644
index 000000000..75fd53206
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/07-proxy-bridge-port-collisions.md
@@ -0,0 +1,28 @@
+# Document proxy bridge-port collisions
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation, possible code enhancement
+
+## Gap
+
+Each authenticated-proxy Mailer creates a local bridge on its configured port. Concurrent mailers using the same port can collide, but the guide only says the port can be changed.
+
+## Plan
+
+Explain port ownership and require distinct bridge ports for concurrently active authenticated-proxy mailers. Consider automatic ephemeral-port allocation as a separate enhancement.
+
+## Acceptance criteria
+
+- [x] Collision conditions are documented.
+- [x] A multi-mailer example uses distinct ports.
+- [x] The loopback-binding fix is complete before this item is closed.
+- [x] Any automatic allocation proposal is tracked separately.
+
+## Evidence
+
+- `MailerGenericBuilder.withProxyBridgePort(...)` now explains loopback binding, per-Mailer bridge ownership, sharing within one Mailer, collisions between separate Mailers, and the anonymous-proxy exception.
+- `simplejavamail.org/src/pages/configuration.hbs` now has a dedicated **Authenticated proxy bridge ports** section with a two-Mailer example using ports 1081 and 1082. The proxy capability links directly to it.
+- The loopback-only bridge fix is complete in [#676](https://github.com/bbottema/simple-java-mail/issues/676).
+- Automatic port allocation is tracked separately in [#694](https://github.com/bbottema/simple-java-mail/issues/694), including the session-update work required before port `0` can be supported.
+- Verification: core-module Javadocs passed; website type/check task, production build, and 1,325-link internal link check passed.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/08-custom-debug-printer.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/08-custom-debug-printer.md
new file mode 100644
index 000000000..1554396cd
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/08-custom-debug-printer.md
@@ -0,0 +1,26 @@
+# Document withDebugPrinter
+
+- Status: Done
+- Priority: Low
+- Work: Documentation
+
+## Gap
+
+The main diagnostics page covers built-in `SessionDebugOutput` targets but omits the Java-only `withDebugPrinter(PrintStream)` API.
+
+## Plan
+
+Add a compact example beside `withDebugOutput(...)`, including PrintStream ownership and why this API is excluded from the CLI.
+
+## Acceptance criteria
+
+- [x] The Java-only API is discoverable from Diagnostics.
+- [x] Built-in and custom output choices are contrasted.
+- [x] Stream lifecycle responsibility is stated.
+
+## Evidence
+
+- `MailerGenericBuilder.withDebugPrinter(...)` now states that Simple Java Mail does not close the supplied stream and that the caller must keep it open while the Mailer can use it.
+- Diagnostics now contrasts `SessionDebugOutput` with the Java-only `withDebugPrinter(PrintStream)` path and explains why properties and CLI arguments support only built-in targets.
+- A try-with-resources example declares the stream before the Mailer, causing the Mailer to close first and the caller-owned stream afterwards.
+- Verification: core-module Javadocs passed; website type/check task, production build, and 1,325-link internal link check passed.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/09-version-8-migration-guide.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/09-version-8-migration-guide.md
new file mode 100644
index 000000000..363d8eb93
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/09-version-8-migration-guide.md
@@ -0,0 +1,29 @@
+# Add the version 8 migration guide
+
+- Status: Done
+- Priority: Medium
+- Work: Documentation, navigation, sitemap
+
+## Gap
+
+The migration index recommends moving one major version at a time but jumps from 9 to 7. Version 8 changed defaults/overrides, validation, field opt-outs, and DKIM behavior.
+
+## Plan
+
+Create `migration-notes-8.0.0` from release history and relevant commits/tests, add it to navigation and sitemap, and link 7 → 8 → 9.
+
+## Acceptance criteria
+
+- [x] Every 8.0 breaking or behavioral change has a migration note.
+- [x] Migration navigation is sequential.
+- [x] New page is searchable and present in `sitemap.xml`.
+- [x] Examples use current APIs while explaining the old behavior.
+
+## Evidence
+
+- `simplejavamail.org/src/pages/migration-notes-8.0.0.hbs` covers the governance lifecycle and inspection model, field opt-outs, default S/MIME signing, converter changes, lenient validation, receipt fallbacks, CustomMailer behavior, DKIM configuration, copying fidelity, Outlook parsing, and low-level removals.
+- The guide was checked against issues #446–#452, the 7.9.1-to-8.0.0 public API diff, release history, implementation tests, and the copying-fidelity fix that shipped without its own issue.
+- The migration index, manifest, tracked sitemap, content plan, and 7 → 8 → 9 page sequence now include version 8.
+- Related Configuration and Security examples now use the current `EmailProperty` constants and preserve property defaults when installing a programmatic S/MIME default.
+- Verification: website type/check task, production build, 1,402-link internal link check, and local visual/deep-link inspection passed.
+- Website commit: `322fb0c docs(migration): add the version 8 upgrade guide [skip ci]`.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/10-templating-boundary.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/10-templating-boundary.md
new file mode 100644
index 000000000..c0926de49
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/04-missing-coverage/10-templating-boundary.md
@@ -0,0 +1,27 @@
+# Clarify the template-engine boundary
+
+- Status: Done
+- Priority: Low
+- Work: Documentation
+
+## Gap
+
+The content documentation does not state whether Simple Java Mail includes a general message-template engine. It does not; applications render Thymeleaf, Freemarker, Mustache, or other templates externally and pass the resulting body.
+
+## Plan
+
+Add one short note with a generic render-then-build flow. Distinguish general content templates from reply quoting templates.
+
+## Acceptance criteria
+
+- [x] No built-in general template engine is implied.
+- [x] The integration boundary is understandable without endorsing one engine.
+- [x] Reply quoting templates are identified as a separate feature.
+
+## Evidence
+
+- Capabilities now shows the engine-neutral render-then-build flow beside basic usage, including paired plain-text and HTML output.
+- The same note links reply quoting separately and explains the narrow `%s` substitution without presenting it as general message rendering.
+- Future template support, an engine-neutral extension point, and optional integrations are tracked in [#695](https://github.com/bbottema/simple-java-mail/issues/695) for 10.0.0.
+- Verification: website type/check task, production build, and 1,404-link internal link check passed.
+- Website commit: `96bdfeb docs(content): explain template integration [skip ci]`.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/05-code-and-javadoc-defects/01-reset-connection-pool-max-size.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/05-code-and-javadoc-defects/01-reset-connection-pool-max-size.md
new file mode 100644
index 000000000..e1444a7d2
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/05-code-and-javadoc-defects/01-reset-connection-pool-max-size.md
@@ -0,0 +1,30 @@
+# Fix resetConnectionPoolMaxSize
+
+- Status: Done
+- Priority: High
+- Work: Code, unit tests, Javadocs
+
+## Defect
+
+`resetConnectionPoolMaxSize()` calls `withConnectionPoolCoreSize(DEFAULT_CONNECTIONPOOL_MAX_SIZE)`. It changes the wrong property and leaves max size untouched.
+
+## Plan
+
+Change it to `withConnectionPoolMaxSize(DEFAULT_CONNECTIONPOOL_MAX_SIZE)` and add a regression test that first changes both core and max values.
+
+## Acceptance criteria
+
+- [x] Reset changes max size to its default.
+- [x] Core size remains unchanged.
+- [x] A regression test fails on the old implementation.
+- [x] Generated Javadocs and CLI help retain the correct contract.
+
+## Evidence
+
+- Contract: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerGenericBuilder.java:713-722`
+- Defect: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerGenericBuilderImpl.java:833-836`
+- Fixed by [#696](https://github.com/bbottema/simple-java-mail/issues/696): `resetConnectionPoolMaxSize()` now delegates to `withConnectionPoolMaxSize(DEFAULT_CONNECTIONPOOL_MAX_SIZE)`.
+- Regression coverage changes core and max independently, then verifies that only max returns to its default.
+- Focused verification: `mvn -pl modules/simple-java-mail -Dtest=MailerBuilderTest test` passed on JDK 8.
+- The public Javadoc contract was already correct and the existing API surface did not change, so generated Javadocs and CLI metadata require no update.
+- Implementation commit: `c04642bb fix(mailer): reset connection pool max size`.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/05-code-and-javadoc-defects/02-reset-connection-pool-claim-timeout.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/05-code-and-javadoc-defects/02-reset-connection-pool-claim-timeout.md
new file mode 100644
index 000000000..9efaca590
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/05-code-and-javadoc-defects/02-reset-connection-pool-claim-timeout.md
@@ -0,0 +1,33 @@
+# Fix resetConnectionPoolClaimTimeoutMillis
+
+- Status: Done
+- Priority: Medium
+- Work: Code, unit tests, documentation
+
+## Defect
+
+`resetConnectionPoolClaimTimeoutMillis()` calls `withConnectionPoolExpireAfterMillis(DEFAULT_CONNECTIONPOOL_CLAIMTIMEOUT_MILLIS)`. It changes expiry instead of the claim timeout.
+
+## Plan
+
+Call `withConnectionPoolClaimTimeoutMillis(DEFAULT_CONNECTIONPOOL_CLAIMTIMEOUT_MILLIS)` and add a regression test that keeps expiry independent. While here, replace “forever” in the website: `Integer.MAX_VALUE` milliseconds is about 24.9 days.
+
+## Acceptance criteria
+
+- [x] Reset changes the claim timeout only.
+- [x] Expire-after remains unchanged.
+- [x] The regression test fails on the old implementation.
+- [x] Documentation states the actual finite default.
+
+## Evidence
+
+- Contract: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerGenericBuilder.java:724-731`
+- Defect: `modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerGenericBuilderImpl.java:841-844`
+- Website wording: `simplejavamail.org/src/pages/configuration.hbs:839`
+- Fixed by [#697](https://github.com/bbottema/simple-java-mail/issues/697): the reset now delegates to `withConnectionPoolClaimTimeoutMillis(DEFAULT_CONNECTIONPOOL_CLAIMTIMEOUT_MILLIS)`.
+- Downstream verification confirmed that the configured integer is wrapped in a millisecond `Timeout` and passed to the pool's finite condition wait; `Integer.MAX_VALUE` milliseconds is approximately 24.9 days.
+- Regression coverage verifies that resetting claim timeout leaves expiry unchanged and that the existing `resetConnectionPoolExpireAfterMillis()` escape hatch leaves claim timeout unchanged.
+- Focused verification: `mvn -pl modules/simple-java-mail -Dtest=MailerBuilderTest test` passed on JDK 8, and core-module Javadocs generated successfully.
+- Website verification: type/check task, production build, and 1,404-link internal link check passed.
+- Implementation and source-Javadoc commit: `2f79ea57 fix(mailer): reset connection pool claim timeout`.
+- Website commit: `dda2394 docs(config): correct pool claim timeout default [skip ci]`.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/05-code-and-javadoc-defects/03-reset-disable-client-validation-javadoc.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/05-code-and-javadoc-defects/03-reset-disable-client-validation-javadoc.md
new file mode 100644
index 000000000..033746173
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/05-code-and-javadoc-defects/03-reset-disable-client-validation-javadoc.md
@@ -0,0 +1,29 @@
+# Correct resetDisableAllClientValidations Javadoc
+
+- Status: Done
+- Priority: Medium
+- Work: Source Javadocs, generated CLI verification
+
+## Defect
+
+The reset method references `DEFAULT_VERIFY_SERVER_IDENTITY`, so generated help reports an unrelated default for client-side validation.
+
+## Plan
+
+Reference `DEFAULT_DISABLE_ALL_CLIENTVALIDATION`, regenerate Javadocs and CLI help, and add a help snapshot assertion for the corrected default.
+
+## Acceptance criteria
+
+- [x] Source Javadoc names the correct constant.
+- [x] Generated CLI help reports the correct default.
+- [x] Website validation terminology agrees with the corrected contract.
+
+## Evidence
+
+- Defect: `modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerGenericBuilder.java:628-634`
+- Source Javadoc now describes the observable result—restoring blocking validation—and resolves `DEFAULT_DISABLE_ALL_CLIENTVALIDATION` to `false`.
+- The JDK 8 `publish-cli` profile regenerated `cli.data` and `therapi.data` from the corrected builder documentation.
+- `GenerateCliHelpTest` reproduces the old `true` output and now asserts that packaged help reports blocking validation with default `false`.
+- The existing website example already says `resetDisableAllClientValidations()` restores blocking validation; no website edit was required.
+- Verification: `mvn -pl modules/cli-module -Dtest=GenerateCliHelpTest test` passed on JDK 8 after metadata regeneration.
+- Implementation and metadata commit: `d45e06aa fix(cli): report validation reset default correctly`.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/05-code-and-javadoc-defects/04-stale-source-javadoc-links.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/05-code-and-javadoc-defects/04-stale-source-javadoc-links.md
new file mode 100644
index 000000000..77f83ea76
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/05-code-and-javadoc-defects/04-stale-source-javadoc-links.md
@@ -0,0 +1,32 @@
+# Replace stale source-Javadoc website links
+
+- Status: Done
+- Priority: Medium
+- Work: Source Javadocs, CLI footer, link checking
+
+## Defect
+
+Several source Javadocs and the CLI footer still use the removed hash-router URLs such as `/#/configuration`, `/#/cli`, and homepage section fragments.
+
+## Plan
+
+Replace them with current canonical routes and regenerate Javadocs/help. Cover the corrected CLI footer with focused help assertions; a broader Java-source scanning guard is deliberately out of scope.
+
+## Acceptance criteria
+
+- [x] Spring support links to `/configuration.html#section-spring-support`.
+- [x] CLI help links to `/cli.html`.
+- [x] DKIM and S/MIME source links point to Security sections.
+- [x] Focused CLI help assertions reject the old `simplejavamail.org/#/cli` footer.
+
+## Evidence
+
+- `modules/spring-module/src/main/java/org/simplejavamail/springsupport/SimpleJavaMailSpringSupport.java:27`
+- `modules/cli-module/src/main/java/org/simplejavamail/internal/clisupport/CliCommandLineProducer.java:148`
+- `modules/cli-module/src/main/java/org/simplejavamail/internal/clisupport/CliCommandLineConsumerUsageHelper.java:59`
+- `modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/SpecializedMimeMessageProducer.java:30-31`
+- Spring support now points directly to `configuration.html#section-spring-support`; DKIM and S/MIME point to their existing Security sections.
+- Both general command help and individual-option help now use `https://www.simplejavamail.org/cli.html`, with focused assertions covering both footer implementations.
+- Per maintainer direction, no broader Java-source URL scanner was added.
+- Verification: `GenerateCliHelpTest` passed on JDK 8, and Javadocs generated successfully for the facade and Spring modules.
+- Implementation commit: `75ece88d fix(docs): replace stale website routes`.
diff --git a/API_DOCUMENTATION_IMPROVEMENT_PLAN/README.md b/API_DOCUMENTATION_IMPROVEMENT_PLAN/README.md
new file mode 100644
index 000000000..5a9eb893f
--- /dev/null
+++ b/API_DOCUMENTATION_IMPROVEMENT_PLAN/README.md
@@ -0,0 +1,123 @@
+# API documentation improvement plan
+
+This backlog turns the 9.1.5 API-documentation audit into small, verifiable work items. Each finding has its own Markdown file so it can be discussed, implemented, reviewed, and closed independently.
+
+Audit baseline: Simple Java Mail 9.1.5, 5 August 2026.
+
+## Working method
+
+1. Work through the phases in order unless an item explicitly has no dependency.
+2. Change an item's `Status` from `Planned` to `In progress` before editing code or documentation.
+3. If documentation exposed a code defect, fix and test the code before describing the resulting behavior.
+4. Check every acceptance criterion in the item file.
+5. Run the phase gate, mark the item `Done`, and check it off below.
+
+Status vocabulary: `Planned`, `In progress`, `Blocked`, `Done`.
+
+## Phase 1 — Security and correctness
+
+These items correct claims that could lead users to deploy an insecure or operationally unsafe configuration.
+
+- [x] [Bind the authenticated SOCKS bridge to loopback](01-security-and-correctness/01-authenticated-socks-loopback.md)
+- [x] [Document the TLS trust model accurately](01-security-and-correctness/02-tls-trust-model.md)
+- [x] [Make embedded-image resolution a real containment boundary](01-security-and-correctness/03-embedded-image-containment.md)
+- [x] [Replace unsafe DKIM examples](01-security-and-correctness/04-dkim-safe-defaults.md)
+- [x] [Define S/MIME signature verification precisely](01-security-and-correctness/05-smime-verification-semantics.md)
+- [x] [Remove unsupported cluster failover claims](01-security-and-correctness/06-cluster-failover-claims.md)
+
+Phase gate: targeted security tests pass, security examples use safe defaults, and no page promises behavior the implementation does not provide.
+
+## Phase 2 — Copyable API examples
+
+These items make the examples compile against 9.1.5 and behave as their surrounding text promises.
+
+- [x] [Correct the validator reset example](02-copyable-api-examples/01-validator-reset-example.md)
+- [x] [Replace removed ConfigLoader overloads](02-copyable-api-examples/02-configloader-overloads.md)
+- [x] [Repair the delivery-receipt example](02-copyable-api-examples/03-delivery-receipt-example.md)
+- [x] [Update both reply content alternatives](02-copyable-api-examples/04-reply-content-alternatives.md)
+- [x] [Correct Outlook MSG conversion calls](02-copyable-api-examples/05-outlook-msg-conversion.md)
+- [x] [Restore dedicated CLI recipient flags](02-copyable-api-examples/06-cli-recipient-flags.md)
+- [x] [Add the CLI argument-file subcommand](02-copyable-api-examples/07-cli-argument-file-subcommand.md)
+- [x] [Correct the testConnection overload](02-copyable-api-examples/08-test-connection-overload.md)
+- [x] [Correct DKIM private-key overloads](02-copyable-api-examples/09-dkim-private-key-overloads.md)
+- [x] [Define the DKIM property key format](02-copyable-api-examples/10-dkim-property-key-format.md)
+- [x] [Use a valid S/MIME content cipher](02-copyable-api-examples/11-smime-content-cipher.md)
+- [x] [Repair remaining Java snippet syntax](02-copyable-api-examples/12-java-snippet-syntax.md)
+
+Phase gate: concrete Java examples have been reviewed for syntax and current API usage, and current CLI examples pass a smoke test.
+
+## Phase 3 — Behavioral and operational accuracy
+
+These items align explanations with actual defaults, lifecycle, conversion, module, and runtime behavior.
+
+- [x] [Clarify ConfigLoader replacement semantics](03-behavior-and-operations/01-configloader-replacement-semantics.md)
+- [x] [Resolve extra-property precedence](03-behavior-and-operations/02-extra-properties-precedence.md)
+- [x] [Explain proxying with a custom Session](03-behavior-and-operations/03-custom-session-proxy.md)
+- [x] [Document the SMTPS proxy restriction](03-behavior-and-operations/04-smtps-proxy-compatibility.md)
+- [x] [Define Mailer.validate scope](03-behavior-and-operations/05-mailer-validation-scope.md)
+- [x] [Define clearEmailValidator scope](03-behavior-and-operations/06-clear-validator-scope.md)
+- [x] [Separate startingBlank from mailer defaults](03-behavior-and-operations/07-starting-blank-and-defaults.md)
+- [x] [Describe converter header preservation accurately](03-behavior-and-operations/08-converter-header-preservation.md)
+- [x] [Document generated attachment Content-IDs](03-behavior-and-operations/09-generated-attachment-content-ids.md)
+- [x] [Update serialization transient fields](03-behavior-and-operations/10-serialization-transient-fields.md)
+- [x] [Update bounce-address implementation details](03-behavior-and-operations/11-bounce-address-implementation.md)
+- [x] [Explain synchronous and asynchronous failures](03-behavior-and-operations/12-future-failure-semantics.md)
+- [x] [Remove batch retry claims](03-behavior-and-operations/13-batch-retry-claims.md)
+- [x] [Update Outlook module dependencies](03-behavior-and-operations/14-outlook-module-dependencies.md)
+- [x] [Remove the obsolete Karaf artifact note](03-behavior-and-operations/15-karaf-artifact-note.md)
+- [x] [Describe Spring dependencies as provided](03-behavior-and-operations/16-spring-provided-dependencies.md)
+- [x] [Describe Log4j configuration as an example](03-behavior-and-operations/17-log4j-example-wording.md)
+- [x] [Update the CLI exec-plugin version](03-behavior-and-operations/18-cli-exec-plugin-version.md)
+- [x] [Remove Base64 security terminology](03-behavior-and-operations/19-base64-terminology.md)
+
+Phase gate: every default and operational claim has a source or test anchor, and dependency descriptions match current POMs.
+
+## Phase 4 — Missing API coverage
+
+These items add important public behavior that is currently absent from the guides.
+
+- [x] [Add Mailer lifecycle and ownership guidance](04-missing-coverage/01-mailer-lifecycle.md)
+- [x] [Cover reply and body-editing APIs](04-missing-coverage/02-reply-and-body-api.md)
+- [x] [Support refresh-aware OAuth2 access-token providers](04-missing-coverage/03-oauth2-token-lifecycle.md)
+- [x] [Document withOpenConnection limitations](04-missing-coverage/04-open-connection-custom-mailer.md)
+- [x] [Explain ConfigLoader snapshot timing](04-missing-coverage/05-configloader-snapshot-timing.md)
+- [x] [Document environment-variable syntax](04-missing-coverage/06-environment-variable-syntax.md)
+- [x] [Document proxy bridge-port collisions](04-missing-coverage/07-proxy-bridge-port-collisions.md)
+- [x] [Document withDebugPrinter](04-missing-coverage/08-custom-debug-printer.md)
+- [x] [Add the version 8 migration guide](04-missing-coverage/09-version-8-migration-guide.md)
+- [x] [Clarify the template-engine boundary](04-missing-coverage/10-templating-boundary.md)
+
+Phase gate: the newly covered APIs are reachable from the left navigation or a closely related section and are indexed by site search.
+
+## Phase 5 — Code and source-Javadoc defects
+
+These items repair implementation or source-Javadoc defects found while validating the website.
+
+- [x] [Fix resetConnectionPoolMaxSize](05-code-and-javadoc-defects/01-reset-connection-pool-max-size.md)
+- [x] [Fix resetConnectionPoolClaimTimeoutMillis](05-code-and-javadoc-defects/02-reset-connection-pool-claim-timeout.md)
+- [x] [Correct resetDisableAllClientValidations Javadoc](05-code-and-javadoc-defects/03-reset-disable-client-validation-javadoc.md)
+- [x] [Replace stale source-Javadoc website links](05-code-and-javadoc-defects/04-stale-source-javadoc-links.md)
+
+Phase gate: targeted unit tests cover reset contracts, core and facade Javadocs generate, and source-Javadoc links resolve to current routes.
+
+## Final verification
+
+From `simplejavamail.org`:
+
+```text
+npm run check
+npm run build
+npm run verifyLinks:internal
+npm run verifyLinks
+```
+
+From the repository root, run the targeted module tests for every code change, then the complete Maven test suite in a release-capable environment.
+
+The plan is complete when every item is `Done`, all checkboxes above are checked, website and Javadoc builds pass, concrete examples have been reviewed against the current API, and every claim matches the implementation branch for the release that documents it.
+
+## Verification record — 2026-08-08
+
+- Patch/audit branch `codex/api-documentation-audit`: `mvn clean verify -Ppublish-cli -DexcludeLiveServerTests=true` passed on Oracle JDK 8u152 across all 13 reactor modules. Surefire recorded 407 tests, no failures or errors, and one skipped test. Maven produced ten module Javadoc jars.
+- Minor-release branch `codex/9.2.0`: the same JDK 8 command passed across all 13 modules. Surefire recorded 423 tests, no failures or errors, and seven skipped tests. Maven produced ten module Javadoc jars.
+- The publish profile exposed stale checked-in Therapi Javadoc metadata on both branches. Regeneration was identical under JDK 8 and the CircleCI release JDK 21; the refreshed files are recorded in `b131f4ae` and `f36ff342` respectively.
+- Website branch `codex/website-relaunch` at `dda2394`: the TypeScript/content check, production build, Pagefind indexing, and link checks passed. The verifier checked 1,404 local links across 21 pages and found no broken links. Of 84 unique external links, 78 were reachable, one Stack Exchange link was blocked by HTTP 403 bot protection, and five javadoc.io requests timed out as non-failing warnings.
diff --git a/API_EXPANSION_WORKFLOW.md b/API_EXPANSION_WORKFLOW.md
new file mode 100644
index 000000000..3b7552125
--- /dev/null
+++ b/API_EXPANSION_WORKFLOW.md
@@ -0,0 +1,116 @@
+# API Expansion Workflow for Simple Java Mail
+
+This document serves as a blueprint for developers and coding agents when adding new fields or features to the Simple Java Mail API. Following these steps ensures that the new functionality is correctly integrated across all modules, including CLI support, message conversion, and module-specific processing.
+
+For surrounding mechanisms such as optional module loading, CLI data generation, MIME structure selection, and build instrumentation, see [PROJECT_MECHANISMS_CATALOGUE.md](PROJECT_MECHANISMS_CATALOGUE.md).
+
+---
+
+## 1. Core Model Expansion (`core-module`)
+
+The foundation of any new feature usually starts with updating the core model objects.
+
+- **Update Model Classes**: Add the new field to the relevant model class (e.g., `Email`, `Recipient`).
+ - Use Lombok `@Value` for immutability where appropriate.
+ - Ensure the class remains `Serializable`.
+ - Update `serialVersionUID` if the change breaks binary compatibility.
+- **Update Internal Interfaces**: If the model has an internal interface (e.g., `InternalEmail`), ensure it's updated if necessary.
+
+## 2. API Interface Expansion (`core-module`)
+
+New fields must be accessible through the fluent Builder API.
+
+- **Update Builder Interfaces**: Add new methods to the public builder interfaces (e.g., `EmailPopulatingBuilder`, `IRecipientsBuilder`, `IRecipientBuilder`).
+- **CLI Compatibility Rules**:
+ - **Parameter Types**: Use simple types (`String`, `boolean`, `int`, `long`) or types that have an existing `ValueInterpreter` in the `cli-module` (e.g., `X509Certificate`, `File`, `URL`, `Date`).
+ - **Avoid Collections**: Picocli mapping works best with individual values or arrays. Avoid `Collection` or `Map` in signatures intended for CLI use. Provide overloads if necessary.
+ - **Javadoc**: Provide complete Javadoc for all new methods and parameters. The CLI module uses this to generate help text.
+ - **Annotations**: Use `@Cli.ExcludeApi` for methods that should not be exposed to the CLI (e.g., those taking complex Java-only objects). Use `@Cli.OptionNameOverride` if the method name isn't ideal for a CLI flag. Use `@Cli.Optional` on parameters that may be omitted from the CLI; keep JetBrains `@Nullable` for Java/API nullability only.
+
+## 3. Core Implementation (`simple-java-mail`)
+
+Implement the new API methods and ensure data propagation.
+
+- **Update Builder Implementations**: Update `EmailPopulatingBuilderImpl`, `RecipientsBuilder`, `RecipientBuilder`, etc.
+- **CRITICAL: Data Propagation**:
+ - Ensure that "copy" methods (e.g., `withRecipient(Recipient)`) and delegation methods correctly copy the new field.
+ - Failure to do this will result in data being lost when `EmailBuilder.copying(email)` is used or when builders delegate to each other.
+- **Update Email Constructor**: Ensure the `Email` constructor copies the new field from the builder.
+- **Utility Classes**: Update `MiscUtil` if it contains helper methods for object creation or parsing (e.g., `interpretRecipient`).
+
+## 4. Message Conversion & Processing (`simple-java-mail`)
+
+The new field must eventually affect the produced `MimeMessage`.
+
+- **MimeMessageHelper**: Update this class if the new field translates directly to a standard MimeMessage header or property (e.g., a new recipient type or a standard header).
+- **SpecializedMimeMessageProducer**: Update the `populateMimeMessage` method if the new field requires logic to decide how the `MimeMessage` is constructed or if it triggers module-specific processing (like S/MIME or DKIM).
+
+## 5. Module-Specific Integration
+
+If the feature relates to a specific module, update that module.
+
+- **S/MIME (`smime-module`)**:
+ - Update `SMIMEModule` interface in `core-module`.
+ - Implement the logic in `SMIMESupport`.
+- **Outlook (`outlook-module`)**:
+ - Update `OutlookEmailConverter` if the new field has an equivalent in Outlook `.msg` files.
+- **Spring (`spring-module`)**:
+ - Update `SimpleJavaMailProperties` to include the new property.
+ - Update `SimpleJavaMailSpringSupport` to map the Spring property to the `ConfigLoader` and builders.
+
+## 6. Defaults & Overrides (EmailGovernance)
+
+Public API configuration should have parity across the Java builder API and the Java defaults/overrides mechanism. When a new user-facing field is added to `Email` or a related model and that value can be represented on a source object, integrate it with the governance layer. This allows projects that centralize behavior through default or override `Email` objects to use the same feature without per-message Java code.
+
+Only skip defaults/overrides integration when the value cannot sensibly be represented on the source model, when it depends on runtime state that cannot be copied, or when it is a per-recipient sub-field that should be set while constructing recipients instead. Document the reason in the implementing issue or PR.
+
+- Add EmailProperty entry (core-module)
+ - If the field is on Email and needs default/override resolution, add a corresponding constant to org.simplejavamail.internal.config.EmailProperty.
+ - Mark it as collection-based when the value is a collection so merging is applied instead of replacement.
+- Apply default values (simple-java-mail)
+ - In EmailGovernanceImpl.newDefaultsEmailWithDefaultDefaults(), derive a sensible default from ConfigLoader.Property if applicable and set it on the builder.
+- Apply defaults/overrides to provided Email (simple-java-mail)
+ - In EmailGovernanceImpl.produceEmailApplyingDefaultsAndOverrides(), resolve values using MiscUtil.overrideOrProvideOrDefaultProperty / overrideAndOrProvideAndOrDefaultCollection and apply them to the builder.
+ - Ensure ignoringDefaults / ignoringOverrides and the per-property suppression sets are respected (this comes for free when using the MiscUtil helpers).
+- Module-triggering fields
+ - If the new field influences downstream processing (e.g., per-recipient S/MIME), make sure SpecializedMimeMessageProducer considers the presence of the field when deciding to trigger the corresponding module.
+ - Ensure the corresponding module implementation tolerates null global config if the trigger is a per-item value.
+- Per-recipient fields
+ - Do not try to default/override sub-fields inside Recipient via governance. Instead, set them when building recipients (through IRecipientsBuilder / RecipientsBuilder) and let module logic act on their presence.
+
+## 7. Mailer Configuration API Expansion
+
+Mailer configuration API changes are separate from Email model/defaults/overrides governance. Use this path for SMTP/session/runtime behavior such as connection settings, proxy behavior, debug output, transport mode, trust settings, and other Mailer-owned state.
+
+- **Public API**: Add methods to `MailerGenericBuilder`, `MailerRegularBuilder`, or `MailerFromSessionBuilder` based on ownership. Provide complete Javadoc and CLI annotations because the CLI help text is generated from these builder APIs.
+- **Operational Ownership**: Add state to `OperationalConfig`, `ServerConfig`, `ProxyConfig`, or another existing Mailer config interface according to the behavior being configured.
+- **Property Defaults**: If the setting is property-friendly, add a `ConfigLoader.Property` entry and resolve it when creating the builder/config object. This keeps property-file driven projects configurable without Java code.
+- **Transport Strategy Mapping**: When a Mailer setting maps to Jakarta Mail properties, keep the `mail.smtp.*` / `mail.smtps.*` names behind `TransportStrategy` helper methods and apply them only after the effective strategy is known.
+- **Spring Mapping**: If the property belongs to the public configuration surface, add it to Spring support and Spring Boot metadata generation classes.
+- **Verification**: Test the Java builder path, property/config path, Spring mapping when applicable, and the final `Session` properties.
+- **Governance Boundary**: Do not wire Mailer connection/session settings into Email defaults/overrides. That mechanism applies to Email/message state and related model values.
+
+## 8. Configuration Support (`core-module`)
+
+Public API configuration should also have parity with property-backed configuration. When a Java API option represents configurable behavior and can be expressed as strings, booleans, numbers, enums, files, or other property-friendly values, expose it through configuration properties as well. This keeps property-file driven projects from needing a Java-only escape hatch for the same feature.
+
+Only skip property configuration when the value cannot be expressed safely or clearly in properties, has no sensible global default, or would require complex object construction that belongs in Java code. Document the reason in the implementing issue or PR.
+
+- **ConfigLoader**: Add a new entry to the `Property` enum.
+- **Data Resolution**: Ensure the new property is used in `EmailGovernanceImpl`, the Mailer builder/config object, or wherever defaults are applied.
+- **Spring Mapping**: If the property belongs to the public configuration surface, add the corresponding Spring property and map it through `SimpleJavaMailSpringSupport`.
+- **Dynamic Property Collections**: For collection-style namespaces such as `simplejavamail.defaults.connectionpool.clusters.*`, keep parsing and validation centralized in `ConfigLoader`. Spring support should forward the whole namespace into `ConfigLoader` and Spring Boot metadata should describe the nested shape, rather than duplicating alias/key resolution.
+
+## 9. Verification Surface Areas
+
+Always verify the following areas:
+
+- **Builder Chain**: Verify the field is preserved across multiple builder calls.
+- **Email Copying**: Use `EmailBuilder.copying(email).buildEmail()` and verify the field is still there.
+- **CLI Help**: Run the CLI with `--help` for the relevant command to ensure the new option is documented and has the correct parameter labels.
+- **End-to-End**: Verify the field actually affects the final `MimeMessage` (e.g., by inspecting the produced EML or using a dummy SMTP server).
+
+There are junit tests available to verify the above or provide a blueprint for new tests.
+
+---
+*Blueprint version 1.0*
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
new file mode 100644
index 000000000..0790c473b
--- /dev/null
+++ b/DEVELOPMENT.md
@@ -0,0 +1,70 @@
+# Developer Environment Setup
+
+This document records environment requirements and constraints for building Simple Java Mail.
+It is intended for both human developers and coding agents starting new sessions.
+
+For a catalogue of cross-cutting project mechanisms, see [PROJECT_MECHANISMS_CATALOGUE.md](PROJECT_MECHANISMS_CATALOGUE.md).
+For issue, Dependabot, and release handling workflows, see [MAINTAINER_WORKFLOW.md](MAINTAINER_WORKFLOW.md).
+
+---
+
+## Java Version
+
+**Simple Java Mail must remain Java 8-compatible. Use JDK 8 for dependency bumps and compatibility validation.**
+
+The system-wide `JAVA_HOME` may point to a newer JDK, but Simple Java Mail is **incompatible with Java 12+**
+for CLI metadata generation. Specifically:
+
+- The CLI module serialises `java.lang.reflect.Method` objects into `cli.data` using Kryo.
+ On Java 12+, the Java module system and internal JVM changes break this serialisation.
+- The `therapi-runtime-javadoc` Javadoc scanning used by the CLI module encounters NPEs on
+ synthetic/bridge methods exposed differently by newer JVM reflection APIs.
+
+**Before building, ensure a Java 8 JDK is active.**
+
+Use a local, gitignored `.maintainer-env.ps1` file for machine-specific paths:
+
+```powershell
+# .maintainer-env.ps1, not committed
+$env:JAVA_HOME = ""
+$env:PATH = "$env:JAVA_HOME\bin;$env:PATH"
+$env:MAVEN_OPTS = "-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT"
+$env:SJM_GH = ""
+```
+
+Then load it in the shell used for builds:
+
+```powershell
+. .\.maintainer-env.ps1
+java -version # should report 1.8.x
+```
+
+---
+
+## Build
+
+Standard full build (skipping tests and slow checks):
+
+```powershell
+mvn verify -DskipTests -Dmaven.javadoc.skip=true
+```
+
+After any build that ran `license:format`, clean up auto-generated headers before committing:
+
+```powershell
+mvn com.mycila:license-maven-plugin:3.0:remove
+```
+
+---
+
+## Known Build Constraints
+
+- **ossindex** (Sonatype vulnerability scan) has been removed from the build lifecycle.
+ It is configured with `` (empty phase) in the root `pom.xml` to unbind it.
+- **junit-pioneer** is pinned to `1.9.1` because the 2.x line ships Java 11 class files.
+ JUnit artifacts may be updated only while preserving Java 8 bytecode compatibility.
+- **cli-module** uses `log4j-slf4j2-impl` (not `log4j-slf4j-impl`) because `slf4j-api`
+ is at version 2.x which requires the SLF4J 2 bridge adapter.
+- **`cli.data`** (`modules/cli-module/src/main/resources/cli.data`) is a committed binary
+ generated by the CLI module's Kryo serialisation. It must be regenerated whenever the
+ CLI API changes. **Do not regenerate it on Java 12+ - use JDK 8.**
diff --git a/src/main/webapp/LICENSE b/LICENSE
similarity index 99%
rename from src/main/webapp/LICENSE
rename to LICENSE
index 717636af3..b9225929e 100644
--- a/src/main/webapp/LICENSE
+++ b/LICENSE
@@ -1,4 +1,3 @@
-
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -187,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2008 - 2016 Benny Bottema
+ Copyright 2009 Benny Bottema
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/MAINTAINER_WORKFLOW.md b/MAINTAINER_WORKFLOW.md
new file mode 100644
index 000000000..db096e661
--- /dev/null
+++ b/MAINTAINER_WORKFLOW.md
@@ -0,0 +1,505 @@
+# Maintainer Workflow
+
+This document describes the normal Simple Java Mail maintenance loop for coding agents and human maintainers.
+It is intentionally one workflow with switches, so prompts like these can all follow the same backbone:
+
+- "Can you pick up #743 and release it as patch?"
+- "There are a few issues open, have a look at them and fix them without releasing."
+- "Can you handle the dependabot PRs and release as patch?"
+- "Fix up that last issue and prepare for a minor release."
+
+For API additions, read [API_EXPANSION_WORKFLOW.md](API_EXPANSION_WORKFLOW.md) as part of the implementation phase.
+For cross-cutting mechanisms such as CLI metadata, optional modules, MIME resources, and instrumentation, read
+[PROJECT_MECHANISMS_CATALOGUE.md](PROJECT_MECHANISMS_CATALOGUE.md).
+
+---
+
+## 1. Interpret the Request
+
+Classify the request before editing anything:
+
+| Request shape | Main mode | Release? |
+|------------------------------------------------------|----------------------------------|-------------------------------------------------------------------------|
+| "Pick up #N and release it as patch" | Issue-driven fix | Yes, patch release after merge |
+| "Look at open issues and fix them without releasing" | Triage and implementation | No release; keep work on `develop` |
+| "Handle dependabot PRs and release as patch" | Dependency PR batch | Yes, patch release after verification |
+| "Fix the last issue and prepare for a minor release" | Implementation plus release prep | Usually stop before approving release unless explicitly told to release |
+
+If the user explicitly says not to release, do not release.
+If the user asks for or approves a patch or minor release at any point in the maintenance task, treat that as standing authorization
+to carry the matching release through CircleCI approval, Maven Central verification, GitHub release notes, and final branch sync.
+Release-context shorthand such as "new branch, patch" or "make this a minor" counts; do not stop later to ask for a second
+"release it" confirmation. This authorization remains active until the user withdraws it or changes the requested release level.
+
+Do not infer release authorization when "patch" only means a code diff or when "minor" merely describes the size of a change;
+the surrounding request must indicate a semantic-version release or invoke this release workflow.
+
+---
+
+## 2. Start Clean
+
+Always ground the session in live state:
+
+```powershell
+git status --short --branch
+git fetch --prune --tags origin
+git branch -vv
+```
+
+Load local machine paths from the gitignored maintainer environment file when it exists:
+
+```powershell
+if (Test-Path .\.maintainer-env.ps1) {
+ . .\.maintainer-env.ps1
+}
+```
+
+Use `gh` from `PATH`, or `$env:SJM_GH` from `.maintainer-env.ps1` when the executable needs an explicit local path:
+
+```powershell
+$gh = if ($env:SJM_GH) { $env:SJM_GH } else { "gh" }
+& $gh auth status
+```
+
+Local verification should use JDK 8:
+
+```powershell
+if (-not $env:JAVA_HOME) {
+ throw "Load .maintainer-env.ps1 or set JAVA_HOME to a Java 8 JDK first."
+}
+java -version # should report 1.8.x
+```
+
+Do not work around Maven Central or TLS failures with insecure SSL flags until the Windows trust-store option above has been tried.
+
+Past correction to preserve: a retry is not permission to change repo state. If a requested retry depends on a clean/synced branch gate,
+re-check the gate and proceed only if it already passes, unless the user explicitly approves syncing or fast-forwarding first.
+
+---
+
+## 3. Choose the Work Branch
+
+Implementation work normally happens on `develop`.
+
+```powershell
+git switch develop
+git pull --ff-only origin develop
+```
+
+Release work happens by merging `develop` into `master` after the release candidate is ready:
+
+```powershell
+git switch master
+git pull --ff-only origin master
+git merge --ff-only develop
+git push origin master
+```
+
+If a fast-forward merge is not possible, stop and inspect the divergence. Do not rewrite shared branches unless the user explicitly asks.
+
+---
+
+## 4. Triage GitHub Work
+
+Inspect issues or PRs directly through `gh`.
+
+```powershell
+& $gh issue view 743 --repo bbottema/simple-java-mail --comments --json number,title,body,labels,milestone,author,url,comments
+& $gh issue list --repo bbottema/simple-java-mail --state open --limit 50 --json number,title,labels,milestone,author,url
+& $gh pr list --repo bbottema/simple-java-mail --state open --limit 50 --json number,title,author,baseRefName,headRefName,url,labels,statusCheckRollup
+```
+
+This installed `gh` does not expose a top-level milestone command; use the API:
+
+```powershell
+& $gh api 'repos/bbottema/simple-java-mail/milestones?state=all&per_page=100' --paginate --jq '.[] | [.number,.title,.state,.due_on,.open_issues,.closed_issues] | @tsv'
+```
+
+Release milestones use the exact numeric release version as their title, without a `v` prefix. Once the target version is known,
+reuse its milestone or create it in the open state before release bookkeeping starts. Set `due_on` to the planned release date when
+creating the milestone; if the milestone already exists and is still open, fill or update its due date to the current plan:
+
+Keep release milestone descriptions empty. Release summaries, planning notes, and process context belong elsewhere. The only
+exception is a cross-version advisory that the maintainer explicitly asks to show on affected milestones; in that case, the
+description must contain only that notice.
+
+```powershell
+$version = "9.1.4"
+$plannedReleaseDate = "2026-08-05T00:00:00Z"
+$milestones = & $gh api repos/bbottema/simple-java-mail/milestones?state=all --paginate | ConvertFrom-Json
+$milestone = $milestones | Where-Object title -eq $version
+if (-not $milestone) {
+ $milestone = & $gh api -X POST repos/bbottema/simple-java-mail/milestones `
+ -f title=$version -f state=open -f due_on=$plannedReleaseDate | ConvertFrom-Json
+} elseif ($milestone.state -eq "open" -and
+ (-not $milestone.due_on -or ([DateTimeOffset]$milestone.due_on).UtcDateTime.ToString("yyyy-MM-ddT00:00:00Z") -ne $plannedReleaseDate)) {
+ $milestone = & $gh api -X PATCH repos/bbottema/simple-java-mail/milestones/$($milestone.number) `
+ -f due_on=$plannedReleaseDate | ConvertFrom-Json
+}
+```
+
+Assign every issue and PR represented by the release to that milestone. This includes closed Dependabot PRs whose update or
+compatibility decision is part of the release, even when the published notes summarize several automated PRs in one maintenance
+bullet or a maintainer roll-up PR. Do not add unrelated or superseded proposals that did not contribute to the release.
+GitHub treats pull requests as issues for milestone updates:
+
+```powershell
+& $gh api -X PATCH repos/bbottema/simple-java-mail/issues/671 -F milestone=$($milestone.number)
+```
+
+Before publishing, extract the repository issue and PR links from the version's release-note entries and verify that each linked
+Simple Java Mail item belongs to the same-version milestone. Also verify that any summarized Dependabot batch is fully represented
+in the milestone even if the compact release note does not link every constituent PR.
+
+```powershell
+& $gh api "repos/bbottema/simple-java-mail/issues?milestone=$($milestone.number)&state=all&per_page=100" --paginate --jq '.[] | [.number,.state,.title] | @tsv'
+```
+
+Close the milestone only after that membership check passes and every included item is closed. Read the published GitHub release
+date and write that actual release date to `due_on` while closing, replacing the earlier planned date when necessary:
+
+```powershell
+$publishedAt = & $gh release view $version --repo bbottema/simple-java-mail --json publishedAt --jq .publishedAt
+$actualReleaseDate = ([DateTimeOffset]$publishedAt).UtcDateTime.ToString("yyyy-MM-ddT00:00:00Z")
+& $gh api -X PATCH repos/bbottema/simple-java-mail/milestones/$($milestone.number) `
+ -f state=closed -f due_on=$actualReleaseDate
+```
+
+For historical bookkeeping, derive the actual date from the version's release notes or published GitHub release. A closed release
+milestone must never be left without a due date, because GitHub uses it to order milestones by release date.
+
+Use existing labels. Common labels include:
+
+- Added functionality, choose one when applicable: `enhancement` or `major feature`
+- Other work types: `bug`, `maintenance`, `documentation`, `security`, `dependencies`, `3rdparty-problem`
+- `Priority-Low`, `Priority-Medium`, `Priority-High`
+- `invalid`, `question`, `need-user-input`, `will close soon`
+
+`enhancement` and `major feature` are mutually exclusive levels of added functionality. Use `enhancement` for an incremental addition
+and `major feature` for a substantial new capability that deserves prominent treatment; a `major feature` can still ship in a SemVer
+minor release. Never apply both labels to one issue. Orthogonal labels such as `security` and a priority may be added alongside either
+one. Re-read the issue's current labels immediately before changing them, and treat a maintainer's removal or replacement of one of
+these labels as deliberate rather than restoring it from an older plan or task description.
+
+When an issue belongs upstream, confirm whether the fix belongs in a sibling repo first. If it does, create or update the upstream issue, fix and release the upstream library, then update Simple Java Mail.
+
+For sibling repositories:
+
+- Check hidden CI files such as `.circleci/config.yml`; do not stop at visible-file scans.
+- Verify the sibling repo is clean and aligned with its upstream before changing it.
+- Do not fast-forward, push, tag, or release sibling repositories unless that was explicitly requested or approved.
+- If a sibling repository is released as part of the fix, also complete its GitHub release bookkeeping: fixed issue comment/close,
+ milestone close, GitHub release for the tag, and any relevant usage example.
+- When copying or flattening parent/dependency configuration, verify the sibling source project is current and compare against the released Maven Central version. Do not bake stale local checkout state into this project.
+
+---
+
+## 5. Implement
+
+Read the relevant code and tests before editing. Use TDD when the issue is reproducible.
+
+For public API or config changes:
+
+- Follow [API_EXPANSION_WORKFLOW.md](API_EXPANSION_WORKFLOW.md).
+- Keep Java API, property configuration, Spring support, defaults/overrides, CLI exposure, and website/README docs aligned where applicable.
+- Do not expose low-level Jakarta Mail terminology when Simple Java Mail can provide a higher-level concept.
+- Prefer builder APIs that hide underlying property names and transport-specific details.
+
+For CLI-related changes:
+
+- CLI generation depends on Javadocs and builder reflection.
+- Regenerate and commit `modules/cli-module/src/main/resources/cli.data` and `modules/cli-module/src/main/resources/therapi.data` when the CLI surface changes.
+- Use `-Ppublish-cli` when verifying release packaging.
+- Avoid Java 12+ for local CLI metadata regeneration; use JDK 8.
+
+For dependency PRs:
+
+- Preserve Java 8 compatibility. Do not accept dependency lines that require Java 9+ or Java 11+.
+- Update `.github/dependabot.yml` ignore rules when Dependabot repeatedly proposes non-Java-8-compatible versions.
+- Keep release notes concise. Prefer one dependency-maintenance roll-up over one noisy bullet per automated PR unless the change matters to users.
+
+---
+
+## 6. Verify
+
+Use focused tests first, then full verification before release.
+
+Useful focused commands:
+
+```powershell
+mvn -pl modules/simple-java-mail -Dtest=SomeTest test
+mvn -pl modules/cli-module -am -Ppublish-cli -DskipTests package
+```
+
+Before merging to `master` for a release, run a full JDK 8 verification:
+
+```powershell
+mvn clean verify -Ppublish-cli -DexcludeLiveServerTests=true
+```
+
+If Norton or local certificate interception breaks Maven, retry with:
+
+```powershell
+$env:MAVEN_OPTS = "-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT"
+```
+
+After any build that ran `license:format`, remove generated headers before committing unless the release artifact itself is being checked:
+
+```powershell
+mvn com.mycila:license-maven-plugin:3.0:remove
+```
+
+For release packaging checks, confirm the standalone CLI artifacts are built:
+
+```powershell
+Get-ChildItem modules\cli-module\target\*standalone-cli*
+```
+
+For release artifact checks, inspect the published source jars rather than committing generated headers to the working tree.
+
+---
+
+## 7. Update Documentation and Release Notes
+
+For user-facing changes:
+
+- Update `README.md` release notes.
+- Keep `RELEASE.txt` in sync with the README release notes.
+- Keep `RELEASE_HISTORY.md` complete and stand-alone.
+- Update website source under `simplejavamail.org` for API/config documentation changes, but do not push the website unless explicitly approved.
+- Add migration notes for behavior changes, removed API, changed defaults, or compatibility-impacting fixes.
+
+Release-note retention:
+
+- `RELEASE.txt` keeps the full release history.
+- `RELEASE_HISTORY.md` also keeps the full release history and must stand alone. It starts with the latest release and continues through the archive; do not move notes out of it just because README shows recent progress too.
+- `README.md` shows recent progress, not the full archive. Keep everything under one `Latest progress` heading.
+- Keep the last major release visible in `README.md` until a newer major release exists, even after minor or patch releases ship.
+- Major releases create the primary release-note story. A narrative section such as `The Short Version` is acceptable for a major release when it helps explain the release line.
+- Regular minor and patch releases should usually be concise bullets that speak for themselves and link to GitHub issues for details. Do not force the major-release narrative format onto ordinary releases.
+- Keep the latest-release notes aligned across `README.md`, `RELEASE.txt`, `RELEASE_HISTORY.md`, and the GitHub release body, with detail level adjusted to each surface. Alignment does not permit the GitHub release body to delegate its content to another file.
+- Minor releases normally create their own primary release-note entry. If a minor release follows closely in the wake of a major release and is still part of that same release wave, absorb it into the current major-line notes instead.
+- Patch releases are absorbed into their parent minor release notes in the same primary note style. Do not create a separate patch-release story unless the user explicitly asks.
+- When a patch is absorbed, place each change in the section where it belongs: bug fixes under fixes, dependency bumps under dependencies, packaging fixes under build/release maintenance, and API/docs additions under the relevant feature or enhancement section.
+- The active release-note header may be a version range, for example `v9.0.0 - v9.0.2`, while the body remains organized by feature/fix/dependency sections.
+- When a release-note heading covers more than one version, prefix every bullet with the exact version that first released that change, for example `- **v9.0.2:** ...`. A bullet may omit its version only when its heading names exactly one version.
+- Within each section of a multi-version release-note entry, order the version-prefixed bullets by release number in descending order (newest first). Keep bullets from the same version together and retain their logical editorial order within that version group.
+- Never combine changes first released in different versions into one bullet. Split mixed maintenance or dependency summaries by release version so every prefix remains unambiguous.
+- Create one GitHub release for every published tag, including patches whose repository notes are absorbed into a version range. Never omit a patch release or fold it into an adjacent tag's GitHub release.
+- Every GitHub release title and body must describe one tag only; never roll multiple versions into a GitHub release title or treat another tag's changes as part of that release.
+- Every GitHub release body, including a patch release, must be a permanent, self-contained record of that tagged release. State what changed in that version and any user-relevant compatibility decisions directly in the body.
+- Links to `README.md`, `RELEASE_HISTORY.md`, pull requests, issues, or Maven Central are supporting references only. Never use a link or wording such as "see the release notes" as a substitute for the release's own change summary.
+- A patch release can remain concise and its repository notes can still be absorbed into the parent minor range, but its GitHub release body must independently explain the patch without requiring the reader to open another page.
+- Write GitHub release notes from facts fixed at the tag. Do not depend on mutable branch content or on documentation whose visible focus will change with a later release.
+- Keep build, test, packaging-validation, and release-process evidence out of GitHub release bodies. Verification belongs in the internal release checklist; published notes should contain only changes and compatibility information readers need.
+- Attach release assets only to their matching tag. Versioned asset filenames and artifact contents must agree with the GitHub release tag.
+- For older archived release groups only, a compact secondary format with each bullet prefixed by the concrete release version is fine.
+
+The website checkout is deploy-sensitive and separate from the root repo. Keep root and website status, staging, commit, and push flows separate.
+Use the root repo as source of truth for API/config names, and patch website docs only for proven mismatches.
+
+For non-user-facing maintenance:
+
+- Keep notes short or omit them.
+- Do not bloat release notes with internal CI details unless users may hit the behavior directly.
+- Avoid release-note text that only makes sense to maintainers, such as internal heap, metadata, or "remaining work" phrasing.
+- Group routine dependency updates into a compact roll-up unless a dependency change is directly relevant to users.
+
+For GitHub comments:
+
+- Closing a user issue should include a short summary and a usage example when useful.
+- Do not sign off comments with test evidence unless the user asks for it.
+- After a release, add a short availability comment to related GitHub issues that were fixed or materially affected by the release.
+- Comments should stand alone for the issue reporter. Avoid context-dependent wording such as "remaining" unless the comment also links the related issue or commit that explains the split.
+- When a fix is split across layers, link the related GitHub issues or commits so the thread explains the whole path.
+
+---
+
+## 8. Commit and Push
+
+Stage selectively and inspect the staged diff:
+
+```powershell
+git status --short
+git diff
+git add
+git diff --cached --check
+git diff --cached --stat
+git commit -m "fix(scope): concise summary"
+```
+
+Use semantic commit subjects: `action(subject): summary, multiline allowed`
+
+Keep unrelated changes out of the commit. Split docs, release notes, build fixes, and implementation work when they are independently meaningful.
+
+For non-code-only changes such as release-note cleanup, issue-bookkeeping docs, or website TODO notes, add `[skip ci]` to the commit subject unless the user explicitly wants CI to run.
+Do not use `[skip ci]` for implementation, build, dependency, generated CLI metadata, or release-lane fixes that need CI validation.
+
+Push implementation work to `develop` unless preparing a release:
+
+```powershell
+git push origin develop
+```
+
+---
+
+## 9. Close or Update Issues Without Releasing
+
+If the user asked to fix without releasing:
+
+1. Push `develop`.
+2. Update the issue with a concise summary and usage example when relevant.
+3. Apply existing labels and the intended milestone if known.
+4. Close only issues that are fully fixed.
+5. Leave release notes in an unreleased section.
+6. Report clean status and the issue links.
+
+Do not merge to `master` or approve CircleCI release gates.
+
+---
+
+## 10. Release
+
+Only release when the user asked for it. An earlier request or approval for a patch or minor release remains sufficient authorization
+throughout the task; do not require a second confirmation after implementation or issue follow-up.
+
+Before release:
+
+1. Perform the defensive Dependabot sweep below against the current `develop` branch.
+2. Merge every safe, release-ready patch-level library update selected by the sweep into `develop`.
+3. Run the full JDK 8 verification on the final release candidate, including the lifted dependency patches.
+4. Confirm README and `RELEASE.txt` are in sync and include a compact dependency-maintenance note when patches were lifted.
+5. Create or reuse the exact-version GitHub milestone, without a `v` prefix, set its due date to the planned release date, and keep it open during the release.
+6. Assign all release issues and PRs to it, including applicable closed Dependabot PRs and maintenance roll-ups.
+7. Cross-check the version's release-note issue and PR links against milestone membership.
+8. Confirm no unrelated local changes remain.
+9. Merge `develop` into `master` with a fast-forward merge.
+10. Push `master`.
+
+### Defensive Dependabot Sweep
+
+Run this sweep immediately before the final release verification for every release, not only when the original request mentions
+Dependabot. Its purpose is to let low-risk library patches travel with an already-planned release instead of requiring another
+release shortly afterward.
+
+```powershell
+& $gh pr list --repo bbottema/simple-java-mail --state open --author app/dependabot `
+ --json number,title,baseRefName,mergeStateStatus,statusCheckRollup,url
+```
+
+Inspect every open Dependabot PR and lift a patch-level Java library update into the release only when all of these are true:
+
+- The PR targets `develop`, is current or can be updated cleanly, and has no merge conflict.
+- The proposed version is a patch update. Do not classify an update from its title alone when the versioning scheme is unusual.
+- The library, its bytecode, and its transitive runtime dependencies remain Java 8-compatible.
+- Existing checks pass and the update does not introduce a known behavioral, API, packaging, or licensing change that deserves
+ separate release scope.
+- The patch can be merged before the final full JDK 8 verification and release-note freeze.
+
+Do not hold up or silently broaden the release for minor/major upgrades, failing or uncertain patches, incompatible Java baselines,
+or updates that need dedicated investigation. Leave those PRs for separate handling and record an ignore rule when an upgrade line
+can never support Java 8. For every patch that is lifted, add its PR to the release milestone and include it in the compact
+dependency-maintenance release-note entry before publishing.
+
+Do not modify project POM versions to prepare a release. The CircleCI release workflow owns version bumping and tagging.
+After release, the checked-in POM version should represent the current released version, not the next possible version.
+If CircleCI successfully published to Maven Central but failed to update the repository, repair the repository manually:
+
+```powershell
+mvn versions:set "-DnewVersion=x.y.z"
+git add pom.xml modules/*/pom.xml modules/cli-module/src/main/resources/therapi.data
+git commit -m "released x.y.z [skip ci]"
+git tag x.y.z
+git push origin master --tags
+```
+
+Only use that manual version/tag repair after confirming Maven Central has the version and CircleCI failed before pushing the release commit or tag.
+
+CircleCI will run `build-and-test` on `master`. The workflow exposes four approval gates:
+
+- `approve-deploy-patch-version`
+- `approve-deploy-minor-version`
+- `approve-deploy-major-version`
+- `approve-deploy-as-is-version`
+
+Approve only the requested gate. The CircleCI CLI is useful for setup, diagnostics, config, and pipeline commands, but the installed CLI may not expose workflow approval commands.
+When approval has to be automated, use the CircleCI API with the CLI token and identify the workflow and approval job first; do not approve by guesswork.
+
+After the deploy job finishes:
+
+1. Fetch tags and branch updates.
+2. Verify the new version exists in Maven Central.
+3. Verify the published sources contain license headers.
+4. Verify `cli-module` includes `standalone-cli.tar` and `standalone-cli.zip`.
+5. Create or update the GitHub release with a self-contained, tag-specific body that permanently records that version's changes and compatibility notes without internal verification evidence.
+6. Attach the release assets: CLI standalone archives and sample logging configs.
+7. Recheck that every release-note issue/PR and summarized Dependabot item is in the exact-version milestone.
+8. Confirm every milestone item is closed, set the milestone due date to the actual published release date, then close it.
+9. Fast-forward `develop` to `master` and push `develop`.
+
+If a published artifact is wrong or missing, assume the Central release is immutable. Fix the release lane and ship a patch release. Fold the patch changes into the parent repository notes using the release-note decision tree above, while giving the patch tag its own concise, self-contained GitHub release body.
+
+Useful checks:
+
+```powershell
+$version = "9.0.1"
+Invoke-WebRequest -UseBasicParsing -Uri "https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/$version/simple-java-mail-$version.pom"
+(Invoke-WebRequest -UseBasicParsing -Uri "https://repo1.maven.org/maven2/org/simplejavamail/cli-module/$version/").Links |
+ Select-Object -ExpandProperty href |
+ Where-Object { $_ -match "standalone-cli" }
+```
+
+For a normal release, create a GitHub release for the tag:
+
+```powershell
+& $gh release create $version --repo bbottema/simple-java-mail --title "v$version" --notes-file RELEASE_NOTES.md
+```
+
+For a special packaging patch, the repository notes still roll into the parent release range, but the patch tag must always have its own GitHub release with a body that states the packaging problem and correction directly. Do not publish a pointer-only release body or attach the patch's versioned artifacts to another tag.
+
+---
+
+## 11. Dependabot Patch Release
+
+Dependabot PR handling follows the same workflow with extra Java 8 caution:
+
+1. List open PRs and identify Dependabot PRs.
+2. Check each proposed dependency for Java 8 bytecode/runtime compatibility.
+3. Update or add `.github/dependabot.yml` ignores for impossible upgrade lines.
+4. Merge compatible PRs into `develop`.
+5. Run full JDK 8 verification.
+6. Once the release version is selected, create or reuse its exact-version milestone without a `v` prefix.
+7. Assign every Dependabot PR accounted for by the release, including closed PRs consolidated into a maintainer roll-up.
+8. Add a compact dependency-maintenance release-note entry.
+9. Cross-check the maintenance entry and its constituent PRs against milestone membership.
+10. Release as patch only if requested.
+
+If Dependabot keeps reopening the same incompatible upgrade, fix the ignore rule before trying to out-click it.
+
+---
+
+## 12. Definition of Done
+
+For a non-release task:
+
+- Implementation committed and pushed to `develop`.
+- Relevant tests pass or skipped tests are explained.
+- GitHub issues/PRs are updated.
+- Release notes are updated when user-facing.
+- Worktree is clean.
+
+For a release task:
+
+- The defensive Dependabot sweep was completed; every eligible patch-level library update was included or deliberately left for separate handling.
+- `master` and `develop` are aligned after release.
+- The release tag exists remotely.
+- Maven Central has the released artifacts.
+- CLI standalone ZIP/TAR exist for `cli-module`.
+- The GitHub release has the CLI standalone ZIP/TAR and sample logging config assets.
+- A separate GitHub release exists for the current tag; it is not folded into another tag's release.
+- The GitHub release body is self-contained and tag-specific: it identifies that version's changes and compatibility impact without relying on README, release-history, issue, or pull-request links for essential meaning.
+- The GitHub release body contains no build/test verification evidence or internal release-process commentary.
+- Every multi-version repository release-note section has version-prefixed bullets ordered newest first.
+- The exact-version GitHub milestone contains every release issue, PR, and accounted-for Dependabot item; all items and the milestone are closed, and its due date equals the actual published release date.
+- Related GitHub issues have a short release-availability comment when applicable.
+- Worktree is clean.
diff --git a/MIME_RESOURCE_NAMING_REPORT.md b/MIME_RESOURCE_NAMING_REPORT.md
new file mode 100644
index 000000000..c6a94f319
--- /dev/null
+++ b/MIME_RESOURCE_NAMING_REPORT.md
@@ -0,0 +1,279 @@
+# MIME Resource Naming And Content-ID Report
+
+This report scopes the long-running attachment, embedded image, resource name, filename, and Content-ID problem areas in Simple Java Mail. It is intended as the implementation guardrail for future fixes, so the next change can preserve the behavior that is already correct and address only the remaining ambiguity.
+
+## Current Behavioral Contract
+
+### Sending embedded images
+
+Manual embedded images still support the historical shorthand where the embedded resource name is the HTML `cid:` contract:
+
+```java
+EmailBuilder.startingBlank()
+ .withHTMLText("")
+ .withEmbeddedImage("logo", dataSource);
+```
+
+The caller-facing name passed to `withEmbeddedImage(name, dataSource)` is the value that HTML must reference as `cid:name`. It must not be silently repaired with a datasource extension. The MIME header wraps it as `Content-ID: `.
+
+The corrected API also supports an explicit Content-ID:
+
+```java
+EmailBuilder.startingBlank()
+ .withHTMLText("")
+ .withEmbeddedImage("logo.png", dataSource, "logo-2026");
+```
+
+For this overload, the resource name remains filename/resource metadata and the explicit `contentId` is the HTML reference identity. Surrounding angle brackets are tolerated at the API boundary and stripped before MIME output; CRLF and interior angle brackets are rejected as invalid header content.
+
+Current anchors:
+
+- [EmailPopulatingBuilder.java](modules/core-module/src/main/java/org/simplejavamail/api/email/EmailPopulatingBuilder.java) documents that embedded image `name` is the body reference name.
+- [EmailPopulatingBuilderImpl.java](modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailPopulatingBuilderImpl.java) requires a non-empty name for byte-array embedded images and requires either an explicit name, datasource name, or explicit Content-ID for datasource-backed embedded images.
+- [AttachmentResource.java](modules/core-module/src/main/java/org/simplejavamail/api/email/AttachmentResource.java) stores optional explicit Content-ID metadata separately from the resource name.
+- [MimeMessageHelper.java](modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageHelper.java) writes the final `Content-ID` header.
+
+### Sending attachments
+
+Attachments have two different identifiers:
+
+- The visible/download filename.
+- The MIME `Content-ID`.
+
+For visible filenames, the current fallback order is:
+
+1. explicit `AttachmentResource.getName()`
+2. `DataSource.getName()`
+3. generated `resource`
+
+For attachment Content-ID values, explicit `AttachmentResource.getContentId()` wins. If no explicit Content-ID is provided, the send-side fallback is an opaque generated ID of the form `sjm-@simplejavamail.generated`. This prevents clients from treating multiple same-name attachments as the same body part without deriving an invalid Content-ID from a user filename. Generated Simple Java Mail attachment IDs are transport-only and are dropped when parsing back into the clean `Email` model.
+
+Current anchor:
+
+- [MimeMessageHelper.java](modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageHelper.java)
+- [EmailConverter.userProvidedContentId(...)](modules/simple-java-mail/src/main/java/org/simplejavamail/converter/EmailConverter.java)
+
+### Dynamic embedded image resolution
+
+Dynamic embedded image resolution is a separate send-side path. If HTML contains an image source that is not already `cid:...`, the builder can resolve it from file, classpath, or URL settings, generate a random CID, add an embedded image under that CID, and rewrite the HTML to `cid:`.
+
+Current anchor:
+
+- [EmailPopulatingBuilderImpl.buildEmail()](modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailPopulatingBuilderImpl.java)
+
+### Receiving and parsing
+
+Parsing is deliberately more permissive than sending because real messages from Gmail, Outlook, Apple Mail, and other clients disagree about `Content-Disposition`, `Content-ID`, filenames, and `multipart/related` structure.
+
+Current parse rules:
+
+- A body part with a Content-ID can enter the CID map.
+- A body part without inline disposition, or without Content-ID, is also treated as an attachment.
+- Filename/name metadata and Content-ID metadata are parsed separately.
+- A real filename wins as the resource name; Content-ID is only a name fallback when the filename is missing or the parser's placeholder attachment name.
+- After parsing, CID-map entries not referenced by `cid:` in HTML are moved to attachments.
+- Since `#491`, a part with `Content-Disposition: attachment` and a Content-ID can be both a downloadable attachment and an embedded resource when HTML references that Content-ID.
+- Explicit/custom Content-ID values survive conversion into `AttachmentResource`; generated `sjm-...@simplejavamail.generated` values do not.
+
+Current anchors:
+
+- [MimeMessageParser.parseMimePartTree(...)](modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParser.java)
+- [MimeMessageParser.resolveInvalidEmbeddedImagesAsAttachments(...)](modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageParser.java)
+- [OutlookEmailConverter.java](modules/outlook-module/src/main/java/org/simplejavamail/internal/outlooksupport/converter/OutlookEmailConverter.java)
+
+## Implementation Status - 2026-07-03
+
+The MIME resource naming repair is implemented for the central non-Outlook issues in this problem area:
+
+- [#566](https://github.com/bbottema/simple-java-mail/issues/566): custom attachment Content-ID values now survive send/parse round trips instead of being overwritten by generated fallback IDs.
+- [#597](https://github.com/bbottema/simple-java-mail/issues/597): embedded image filename/name and HTML Content-ID can now differ through `withEmbeddedImage(name, dataSource, contentId)`.
+- [#602](https://github.com/bbottema/simple-java-mail/issues/602): parsing now keeps embedded filenames and Content-ID values separate; the parsed resource name no longer collapses to the CID when a real filename exists.
+- [#607](https://github.com/bbottema/simple-java-mail/issues/607): generated attachment Content-ID values no longer derive from possibly invalid filenames. The generated form is now opaque, ASCII, and domain-qualified: `sjm-@simplejavamail.generated`.
+
+Related behavior deliberately preserved:
+
+- `withEmbeddedImage(name, dataSource)` remains the convenience shorthand where `name` is the `cid:` body reference.
+- Explicit API names still override datasource names for visible filenames/resource labels.
+- Datasource names remain useful fallback metadata.
+- Attachment filename fallback still uses explicit name, datasource name, then generated `resource`.
+- Attachment Content-ID fallback remains unique per MIME part, but is no longer filename-derived.
+- Generated Simple Java Mail attachment Content-ID values are transport details and are not treated as caller-provided model data after parsing.
+
+Deferred or out-of-scope issues:
+
+- [#541](https://github.com/bbottema/simple-java-mail/issues/541): optional Content-Type `filename`/`name` parameter control is a separate output-parameter feature request.
+- [#573](https://github.com/bbottema/simple-java-mail/issues/573): pre-encoded attachment body support is a separate body-encoding feature.
+- [#599](https://github.com/bbottema/simple-java-mail/issues/599): SMTPUTF8 parsing/session behavior is separate from resource name and Content-ID identity.
+- [#605](https://github.com/bbottema/simple-java-mail/issues/605): broader per-part Content-Transfer-Encoding fidelity remains adjacent. Parsed attachment CTE metadata is preserved by the current resource model, but text-part CTE policy is not part of this repair.
+- [#606](https://github.com/bbottema/simple-java-mail/issues/606) and [#572](https://github.com/bbottema/simple-java-mail/issues/572): Outlook-specific items explicitly excluded from this repair pass.
+
+Verification notes:
+
+- New focused regression coverage was added to `EmailConverterTest` for `#566`, `#597`, `#602`, and `#607`.
+- The full `modules/simple-java-mail -am test` suite passes under `C:\Program Files\Java\jdk1.8.0_152` with 303 tests run, 0 failures/errors, and 12 skipped.
+- The test dependency `junit-pioneer` is kept at `1.9.1` because `2.3.0` ships Java 11 class files and breaks Java 8 test compilation.
+
+## Problem Areas
+
+### 1. API override name versus datasource name
+
+Root problem: caller-provided names and `DataSource.getName()` both influenced MIME output. Some datasources, especially file and URL datasources, expose source names that are not the desired outgoing name.
+
+Related issues and commits:
+
+- [#47](https://github.com/bbottema/simple-java-mail/issues/47): URL datasource source name overrode requested attachment name.
+- [#50](https://github.com/bbottema/simple-java-mail/pull/50), `f54794d9`, `b6bbe6a0`: introduced/finalized `NamedDataSource`.
+- [#151](https://github.com/bbottema/simple-java-mail/issues/151), `80d031e4`: do not overwrite a caller-provided filename extension with an invalid datasource extension.
+- [#175](https://github.com/bbottema/simple-java-mail/issues/175), `305a2d53`: parsing back attachment names with `<>` wrapping clarified that datasource override names are not always recoverable after MIME conversion.
+
+Implementation rule:
+
+- Treat explicit API names as stronger than datasource names.
+- Treat datasource names as fallback metadata only.
+- Do not assume a round trip can recover the original datasource name after an explicit override.
+
+### 2. Embedded image CID versus filename
+
+Root problem: embedded images were sometimes treated like attachments, causing file extensions, datasource names, or filename repair to mutate the CID that HTML references.
+
+Related issues and commits:
+
+- Old Issue 5, `d48055d1`: changed Content-ID to RFC-2387-style `<...>` wrapping.
+- `9280e589`, `51e72ed5`, `d832cc45`, `dee4a9c0`, `7aada14d`: early 2016 split between Content-ID, name, and filename, including angle-bracket handling.
+- [#68](https://github.com/bbottema/simple-java-mail/issues/68): embedded image name required/safeguarded.
+- [#307](https://github.com/bbottema/simple-java-mail/issues/307), [#310](https://github.com/bbottema/simple-java-mail/issues/310), `7f9e4089`: stopped stripping extensions from resource names because Outlook embedded image CIDs were being mangled.
+- [#332](https://github.com/bbottema/simple-java-mail/issues/332), `0392691a`: test coverage for Apple Mail-style embedded image Content-ID without filename extension.
+- [#440](https://github.com/bbottema/simple-java-mail/issues/440), `d561ff9c`: stopped adding datasource-derived extensions to manually named embedded image CIDs.
+
+Implementation rule:
+
+- For inline/embedded resources, the Content-ID must remain the exact embedded image name selected by the API or parser.
+- Filename extension repair belongs to attachment display names, not embedded image CIDs.
+- HTML should reference the embedded resource name as `cid:` without angle brackets.
+
+### 3. Duplicate attachment names
+
+Root problem: data structures and Content-ID generation previously assumed names were unique. Real mail can contain multiple attachments with the same visible filename.
+
+Related issues and commits:
+
+- [#219](https://github.com/bbottema/simple-java-mail/issues/219), [#249](https://github.com/bbottema/simple-java-mail/issues/249), [#310](https://github.com/bbottema/simple-java-mail/issues/310), [#351](https://github.com/bbottema/simple-java-mail/issues/351): parser rejected or collapsed duplicate names in several forms.
+- `72256ce7`: first duplicate-name attempt.
+- `9d8dda88`, `8632308e`: proper duplicate-name handling with entry identity beyond the display name.
+- [#480](https://github.com/bbottema/simple-java-mail/issues/480), `e943d372`: attachment Content-ID now gets a generated unique suffix so clients do not render same-name attachments as the same content.
+
+Implementation rule:
+
+- Visible attachment filenames do not need to be unique.
+- Internal body-part identity must not be keyed only by visible filename.
+- Attachment Content-ID uniqueness is a transport/rendering safety measure, not a user-facing body-reference API.
+
+### 4. Encoding and header decoding
+
+Root problem: filenames, Content-ID values, Content-Description, and parsed header values crossed between raw model values, encoded MIME headers, and decoded parser output at different times.
+
+Related issues and commits:
+
+- [#58](https://github.com/bbottema/simple-java-mail/issues/58), `7096c326`: non-English attachment and embedded image names; early RFC-2047 encoding.
+- [#131](https://github.com/bbottema/simple-java-mail/issues/131), `7f2eaa12`: `NamedDataSource` implements `EncodingAware`.
+- [#226](https://github.com/bbottema/simple-java-mail/pull/226), `21fe8ed9`, `478c7566`: filenames with spaces handled through `ParameterList`.
+- [#232](https://github.com/bbottema/simple-java-mail/issues/232), `ccd2ef12`: MIME text encoding moved from email-building to message-sending, keeping the `Email` model clean.
+- [#248](https://github.com/bbottema/simple-java-mail/pull/248), `52bd831e`: Content-Type `name` should use the complete filename.
+- [#271](https://github.com/bbottema/simple-java-mail/issues/271), `948555a4`: do not encode filenames in the model; scan names for CRLF injection instead.
+- [#293](https://github.com/bbottema/simple-java-mail/issues/293), `5368d30f`: decode parsed MIME values and encode attachment descriptions on send.
+- [#404](https://github.com/bbottema/simple-java-mail/issues/404), [#405](https://github.com/bbottema/simple-java-mail/issues/405), `c8b32ed8`: support attachment `Content-Description` and explicit attachment `Content-Transfer-Encoding`.
+- [#416](https://github.com/bbottema/simple-java-mail/pull/416), [#456](https://github.com/bbottema/simple-java-mail/issues/456): lenient content-transfer-encoding handling for values found in the wild.
+
+Implementation rule:
+
+- Keep model values decoded and human-meaningful.
+- Encode only at MIME output boundaries.
+- Decode at MIME parse boundaries before validation or model population.
+- Keep CRLF/header-injection checks after decode, not as a substitute for decode.
+
+### 5. Inline-versus-attachment classification on receive
+
+Root problem: `Content-Disposition` alone is not a reliable signal for whether a body part is an embedded resource, an attachment, or both.
+
+Related issues and commits:
+
+- [#34](https://github.com/bbottema/simple-java-mail/issues/34): missing disposition originally treated as attachment.
+- [#83](https://github.com/bbottema/simple-java-mail/issues/83), `a1fc1b9f`: inline attachments without Content-ID parsed as regular attachments.
+- [#103](https://github.com/bbottema/simple-java-mail/issues/103), `5727f431`: removed invalid `size=0` Content-Disposition hack.
+- [#179](https://github.com/bbottema/simple-java-mail/issues/179), `c79dd605`: inline/CID resources not referenced in HTML are treated as attachments.
+- [#202](https://github.com/bbottema/simple-java-mail/issues/202), `e3d90694`: fixed concurrent modification while moving invalid embedded images to attachments.
+- [#346](https://github.com/bbottema/simple-java-mail/issues/346), `d417203a`: parse MimeMessage without fetching attachment data; still return named datasources.
+- [#491](https://github.com/bbottema/simple-java-mail/issues/491), `5b704fac`: attachment-disposition parts with referenced Content-ID can be both attachment and embedded image.
+
+Implementation rule:
+
+- Parse `Content-Disposition` and `Content-ID` independently.
+- Use HTML `cid:` references as evidence that a Content-ID part is embedded.
+- Allow dual classification when the MIME source says attachment but the HTML references the Content-ID.
+
+### 6. Outlook-specific CID and fallback behavior
+
+Root problem: Outlook `.msg` files carry attachment names, long filenames, short DOS-like filenames, and ContentId attributes differently from MIME `.eml`.
+
+Related issues and commits:
+
+- [#200](https://github.com/bbottema/simple-java-mail/issues/200), `885f0ecf`: Outlook attachment name falls back to filename if proper name is empty.
+- [simple-java-mail #307](https://github.com/bbottema/simple-java-mail/issues/307): Outlook MSG to EML failed because embedded image Content-ID was changed.
+- [outlook-message-parser #10](https://github.com/bbottema/outlook-message-parser/issues/10): DOS-like short names misclassified embedded images as attachments; long filename needed as fallback.
+- [outlook-message-parser #19](https://github.com/bbottema/outlook-message-parser/pull/19): use Outlook's real ContentId attribute for CID attachments.
+- [outlook-message-parser #69](https://github.com/bbottema/outlook-message-parser/issues/69), [simple-java-mail #481](https://github.com/bbottema/simple-java-mail/issues/481): empty invalid embedded images from Outlook should not crash conversion unless they are actually used.
+- Adjacent parser issues: [outlook-message-parser #3](https://github.com/bbottema/outlook-message-parser/issues/3), [#9](https://github.com/bbottema/outlook-message-parser/issues/9), [#17](https://github.com/bbottema/outlook-message-parser/issues/17), [#23](https://github.com/bbottema/outlook-message-parser/issues/23), [#26](https://github.com/bbottema/outlook-message-parser/issues/26), [#27](https://github.com/bbottema/outlook-message-parser/issues/27).
+
+Implementation rule:
+
+- Prefer a real Outlook ContentId when present.
+- Keep filename and long-filename fallback logic as classification support only; do not let it mutate a known CID.
+- Invalid or empty Outlook attachments need context: ignore/tolerate them only when they are not used as embedded images.
+
+## RFCs Referenced In This Problem Area
+
+Central RFC references:
+
+- [RFC 2387](https://www.ietf.org/rfc/rfc2387.txt), MIME `multipart/related`: related parts form an aggregate object; related processing can take precedence over `Content-Disposition`; examples use body-part `Content-ID` references.
+- [RFC 2183](https://www.ietf.org/rfc/rfc2183.txt), `Content-Disposition`: defines `inline`, `attachment`, and `filename`, but does not make disposition sufficient to classify every related body part in real messages.
+- [RFC 2047](https://www.rfc-editor.org/rfc/rfc2047), non-ASCII text in message headers: referenced by code and by the non-English name fixes.
+- [RFC 5322](https://www.rfc-editor.org/rfc/rfc5322), Internet Message Format: current standard for `msg-id` syntax behind `Content-ID`, replacing the older RFC 2822 reference.
+- [RFC 1341](https://www.rfc-editor.org/rfc/rfc1341) and [RFC 1342](https://www.rfc-editor.org/rfc/rfc1342): older MIME/header-encoding references cited in `#293`; they are historical context for MIME body and non-ASCII header handling.
+
+Adjacent RFC references found in related code/docs:
+
+- RFC 2822: older message-id and email-address format reference superseded by RFC 5322.
+- RFC 2446: calendar method names.
+- RFC 5751: S/MIME.
+- RFC 8098: disposition notifications.
+- RFC 5321: SMTP display-name comment in MIME producer support code.
+
+## Target Invariants For Future Fixes
+
+Any new implementation should preserve these invariants:
+
+1. `withEmbeddedImage(name, dataSource)` means HTML references `cid:name`.
+2. `withEmbeddedImage(name, dataSource, contentId)` means HTML references `cid:contentId`, while `name` remains filename/resource metadata.
+3. `Content-ID` headers are angle-bracket-wrapped on MIME output, but HTML `cid:` values are not.
+4. Attachment filenames are display/download names and may duplicate.
+5. Attachment Content-IDs must be unique enough for mail clients not to collapse same-name attachments.
+6. Generated attachment Content-IDs must be valid opaque IDs, not filename-derived values.
+7. Filename extension repair must not mutate embedded image CIDs.
+8. Explicit API names win over datasource names for filenames/resource labels.
+9. Explicit API Content-IDs win over all generated fallback IDs.
+10. Datasource names are fallbacks, not authoritative identity.
+11. Parsed model values should be decoded; MIME headers should be encoded at output boundaries.
+12. Receiving logic must allow `attachment` plus referenced Content-ID to classify as both attachment and embedded image.
+13. Outlook conversion should prefer real ContentId attributes and use names/filenames only as fallbacks.
+
+## Implementation Checklist
+
+Before changing this area, re-check these surfaces together:
+
+- Builder API docs and validation in `EmailPopulatingBuilder` and `EmailPopulatingBuilderImpl`.
+- Send-side filename/resource-label derivation and Content-ID derivation in `MimeMessageHelper`.
+- Content-Type parameters, Content-Disposition filename, Content-ID, Content-Description, and Content-Transfer-Encoding output in `MimeMessageHelper`.
+- Parse-side Content-ID extraction, filename parsing, header decoding, and `cidMap` versus attachment-list population in `MimeMessageParser`.
+- HTML `cid:` extraction and invalid-embedded-resource fallback.
+- Outlook conversion through `OutlookEmailConverter` and the current `outlook-message-parser` version behavior.
+- Duplicate-name tests and round-trip conversion tests, especially `#307`, `#332`, `#440`, `#480`, and `#491` cases.
diff --git a/NOTICE.txt b/NOTICE.txt
index b1a46f664..08dedb8f9 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -1,17 +1,9 @@
=========================================================================
== NOTICE file for use with the Apache License, Version 2.0 ==
=========================================================================
-
- Simple Java Mail
- Copyright 2009-2017 Benny Bottema
- http://www.simplejavamail.org
-
- This product uses no commercial products.
-
- This product uses the following external (Open Source) libraries:
-
- - activation package from jre6
- - SLF4J (http://http://www.slf4j.org/)
- - Sun Java Mail API, shipped with JavaEE or as seperate download) (http://java.sun.com/products/javamail/)
- - Email Validation regular expressions, by Les Hazlewood, Casey Connor, Benny Bottema (https://github.com/bbottema/email-rfc2822-validator)
- - DKIM signing library https://github.com/markenwerk/java-utils-mail-dkim (based on https://github.com/usrflo/DKIM-for-JavaMail)
+
+ Simple Java Mail - Master Project
+ Copyright (C) 2009 Benny Bottema (benny@bennybottema.com)
+ https://www.simplejavamail.org
+
+ This product uses no commercial products.
\ No newline at end of file
diff --git a/PROJECT_MECHANISMS_CATALOGUE.md b/PROJECT_MECHANISMS_CATALOGUE.md
new file mode 100644
index 000000000..c1f529c21
--- /dev/null
+++ b/PROJECT_MECHANISMS_CATALOGUE.md
@@ -0,0 +1,188 @@
+# Project Mechanisms Catalogue
+
+This catalogue records project mechanisms that are easy to miss because they span modules, build steps, generated files, or runtime classpath behavior. It is meant to be read alongside [DEVELOPMENT.md](DEVELOPMENT.md) and [API_EXPANSION_WORKFLOW.md](API_EXPANSION_WORKFLOW.md).
+
+## Quick Index
+
+| Mechanism | Main reason it exists | Primary anchors |
+| --- | --- | --- |
+| API expansion workflow | Keep model, builders, CLI, config, conversion, and modules in sync when the public API grows. | [API_EXPANSION_WORKFLOW.md](API_EXPANSION_WORKFLOW.md) |
+| Dynamic module loading | Keep optional features out of the core runtime until their module jars are present and used. | [ModuleLoader.java](modules/simple-java-mail/src/main/java/org/simplejavamail/internal/moduleloader/ModuleLoader.java), [modules package](modules/core-module/src/main/java/org/simplejavamail/internal/modules) |
+| CLI generation from builder Javadocs | Turn builder API methods and Javadocs into picocli options and committed binary metadata. | [Cli.java](modules/core-module/src/main/java/org/simplejavamail/api/internal/clisupport/model/Cli.java), [BuilderApiToPicocliCommandsMapper.java](modules/cli-module/src/main/java/org/simplejavamail/internal/clisupport/BuilderApiToPicocliCommandsMapper.java), [CliSupport.java](modules/cli-module/src/main/java/org/simplejavamail/internal/clisupport/CliSupport.java), `modules/cli-module/src/main/resources/cli.data`, `modules/cli-module/src/main/resources/therapi.data` |
+| Async send and batch connection pooling | Reuse SMTP transports when the batch module is present; otherwise fall back to direct session transports. | [MailerImpl.java](modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerImpl.java), [TransportRunner.java](modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/util/TransportRunner.java), [BatchSupport.java](modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/BatchSupport.java) |
+| Authenticated SOCKS proxy bridge | Work around JavaMail's anonymous-only SOCKS support by running a local anonymous bridge to an authenticated remote proxy. | [MailerImpl.java](modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerImpl.java), [AnonymousSocks5Server.java](modules/core-module/src/main/java/org/simplejavamail/api/internal/authenticatedsockssupport/socks5server/AnonymousSocks5Server.java), [AuthenticatedSocksHelper.java](modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/AuthenticatedSocksHelper.java) |
+| Smart MIME structure selection | Choose the least complex RFC-compatible MIME structure for the actual email contents. | [MimeMessageProducerHelper.java](modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageProducerHelper.java), [SpecializedMimeMessageProducer.java](modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/SpecializedMimeMessageProducer.java), [MIME_RESOURCE_NAMING_REPORT.md](MIME_RESOURCE_NAMING_REPORT.md) |
+| Runtime non-null instrumentation | Preserve and enforce JetBrains nullability contracts through build-time bytecode instrumentation. | [pom.xml](pom.xml), `org.jetbrains.annotations.NotNull`, `org.jetbrains.annotations.Nullable` |
+
+## API Expansion Workflow
+
+The API expansion process is already documented in [API_EXPANSION_WORKFLOW.md](API_EXPANSION_WORKFLOW.md). Treat that file as the checklist for adding fields or fluent builder methods.
+
+Important connections to the other mechanisms in this catalogue:
+
+- New builder methods can automatically become CLI options if they are CLI-compatible and not annotated with `@Cli.ExcludeApi`.
+- New mail features usually need a MIME conversion decision in `MimeMessageHelper` or `SpecializedMimeMessageProducer`.
+- Module-specific features may require updates to a core module interface, a module implementation, and the runtime loader.
+- New fields that represent user-facing configuration may also need config defaults, overrides, Spring mapping, and CLI data regeneration.
+
+## Dynamic Module Loading
+
+The published `simple-java-mail` artifact declares several support modules as optional dependencies, including authenticated SOCKS, DKIM, S/MIME, batch, and Outlook support. The core implementation talks to these modules through small interfaces in [modules/core-module/src/main/java/org/simplejavamail/internal/modules](modules/core-module/src/main/java/org/simplejavamail/internal/modules), then uses reflection in [ModuleLoader.java](modules/simple-java-mail/src/main/java/org/simplejavamail/internal/moduleloader/ModuleLoader.java) to instantiate the implementation class only if the module jar is actually present.
+
+The pattern is:
+
+1. Define a stable interface in `core-module`, for example `BatchModule`, `SMIMEModule`, `DKIMModule`, `OutlookModule`, or `AuthenticatedSocksModule`.
+2. Implement that interface in the optional module, for example `BatchSupport`, `SMIMESupport`, `DKIMSigner`, `OutlookEmailConverter`, or `AuthenticatedSocksHelper`.
+3. Add a `ModuleLoader.loadXxxModule()` method with the implementation class name as a string.
+4. Use `MiscUtil.classAvailable(...)` for classpath detection where callers need an availability check.
+5. Keep optional module dependencies optional in `modules/simple-java-mail/pom.xml`; include runtime optional modules in the CLI assembly when the CLI should ship with them.
+
+Current usage:
+
+- Batch is checked with `ModuleLoader.batchModuleAvailable()` in `MailerImpl` and `TransportRunner`; when present, it registers a session pool and acquires pooled transports.
+- S/MIME and DKIM are checked or loaded by the MIME producer only when email content requests signing or encryption.
+- Outlook conversion calls `ModuleLoader.loadOutlookModule()` from `EmailConverter` when `.msg` conversion APIs are used.
+- Authenticated SOCKS support is loaded only when proxy settings include authentication.
+
+Gotchas:
+
+- `ModuleLoader` caches loaded module instances in a static map. Batch, S/MIME, and DKIM availability checks are also cached unless tests force a recheck.
+- Missing modules are a runtime failure only when the feature is requested. Do not add compile-time references from `simple-java-mail` implementation code to optional module classes.
+- Test helpers can force-disable/recheck modules through reflection because the loader is intentionally hidden from `core-module`.
+
+## CLI Generation From Builder API Javadocs
+
+The CLI is generated from the builder API rather than maintained as a fully separate option list. This gives the CLI near one-to-one feature parity with the Java builder API, including the same method documentation, but it also makes the CLI sensitive to API shape, Javadoc completeness, reflection behavior, and serialized metadata compatibility.
+
+Main flow:
+
+1. Builder API root types are listed in [CliSupport.java](modules/cli-module/src/main/java/org/simplejavamail/internal/clisupport/CliSupport.java): `EmailStartingBuilder`, `MailerRegularBuilder`, and `MailerFromSessionBuilder`.
+2. `BuilderApiToPicocliCommandsMapper.generateOptionsFromBuilderApi(...)` walks public methods on builder API nodes annotated with `@Cli.BuilderApiNode`.
+3. A method is accepted only if it passes `methodIsCliCompatible(...)`: it must be on a builder API node, must not have `@Cli.ExcludeApi`, must not be a bean accessor, must not take collection parameters, and must be convertible from string arguments.
+4. `@Cli.OptionNameOverride` can resolve name collisions or expose a CLI-specific option name.
+5. `@Cli.Optional` marks optional CLI parameters explicitly. Java nullability remains expressed with JetBrains `@Nullable`; it no longer drives CLI optionality.
+6. Method and parameter Javadocs are read through Therapi Runtime Javadoc and formatted for terminal output by `TherapiJavadocHelper` and `JavadocForCliFormatter`.
+7. Picocli command metadata is serialized with Kryo to `modules/cli-module/src/main/resources/cli.data`.
+8. Therapi lookups are cached to `modules/cli-module/src/main/resources/therapi.data`.
+
+The Javadoc part is unusual: the build uses Therapi's annotation processor to bake selected Javadoc into runtime-readable classes, then the CLI module reflects over builder methods, resolves those baked Javadocs, formats links and examples for terminal output, and stores the resulting CLI model in `cli.data` for faster startup. The result is clever and convenient, but brittle: Java version changes, bridge/synthetic methods, incomplete `@param` tags, method overload ambiguity, or stale binary metadata can all produce surprising CLI behavior.
+
+Regeneration:
+
+```powershell
+mvn -pl modules/cli-module -am -Ppublish-cli -DskipTests package
+```
+
+The `publish-cli` profile runs `demo.CliListAllSupportedOptionsDemoApp`, which calls `CliSupport.listUsagesForAllOptions()` and then persists the Therapi cache.
+
+Constraints:
+
+- Use JDK 8 for CLI data regeneration. [DEVELOPMENT.md](DEVELOPMENT.md) documents why Java 12+ breaks this path.
+- Every CLI-exposed method needs complete Javadoc, including `@param` text for every parameter. A parameter count mismatch becomes an assertion error in `TherapiJavadocHelper.getParamDescriptions(...)`.
+- Optional CLI arguments must be annotated with `@Cli.Optional`. Keep `@Nullable` as the Java/API nullability contract; do not use it as CLI metadata.
+- Methods using complex Java-only objects, collection/map parameters, ambiguous overloads, or APIs that are only a subset of a better option should be excluded with `@Cli.ExcludeApi(reason = "...")`.
+- New string-convertible types need a value converter registered in `BuilderApiToPicocliCommandsMapper`.
+
+## Async Send And Batch Connection Pooling
+
+`MailerImpl.sendMail(...)` and `MailerImpl.testConnection(...)` can run synchronously or return a `CompletableFuture`. The actual async wrapper is `AsyncOperationHelper` in `core-module`; when the batch module is available, `BatchSupport` delegates to the same helper but provides a default executor tuned for mail sending.
+
+Key pieces:
+
+- `MailerGenericBuilderImpl` chooses the executor service. With `batch-module` on the classpath, the default is `BatchModule.createDefaultExecutorService(...)`; otherwise it falls back to `Executors.newSingleThreadExecutor()`.
+- `NonJvmBlockingThreadPoolExecutor` is a fixed-size `ThreadPoolExecutor` with a `LinkedBlockingQueue`. If keep-alive time is greater than zero, core threads are allowed to time out so they do not keep the JVM alive forever.
+- `withThreadPoolSize(...)` and `simplejavamail.defaults.poolsize` limit concurrent async work, not queued backlog; the built-in executor does not expose a queue-capacity setting.
+- Bounded queues and custom rejection/backpressure behavior require `withExecutorService(...)`. Caller-provided executors are caller-owned, so Simple Java Mail leaves their lifecycle and shutdown to the application.
+- `BatchSupport.registerToCluster(...)` creates/registers SMTP connection pools using `SmtpConnectionPoolClustered`.
+- Cluster-specific property defaults are parsed from `simplejavamail.defaults.connectionpool.clusters.*` by `ConfigLoader` and overlaid on the global connection-pool defaults when `BatchSupport` registers a matching cluster key.
+- `TransportRunner` sends through `BatchModule.acquireTransport(...)` when batch is available; otherwise it opens a normal `Session.getTransport()` connection for the operation.
+- `LifecycleDelegatingTransportImpl` wraps the pooled transport so the caller can signal success with `release()` or failure with `invalidate()`.
+- `MailerImpl.shutdownConnectionPool()` shuts down the default executor if it is library-owned and delegates pool shutdown to the batch module when present.
+
+There is no direct `Phaser` usage in this repository's source tree. Batch coordination here is expressed through `CompletableFuture`, executor services, `AtomicInteger` proxy request tracking, and the external SMTP/object-pool libraries used by `batch-module`.
+
+## Authenticated SOCKS Proxy Bridge
+
+JavaMail supports SOCKS proxy properties, but not authenticated SOCKS proxy login. Simple Java Mail handles authenticated proxies by inserting a local anonymous SOCKS5 server between JavaMail and the real authenticated proxy.
+
+Flow:
+
+1. `MailerImpl.configureSessionWithProxy(...)` first writes normal SOCKS host/port settings for anonymous proxy usage.
+2. If the proxy config requires authentication, it rewrites the session SOCKS host to `localhost` and port to `proxyBridgePort`.
+3. It then loads `AuthenticatedSocksModule` and creates an `AnonymousSocks5Server`.
+4. `AuthenticatedSocksHelper` constructs `AnonymousSocks5ServerImpl` with an `AuthenticatingSocks5Bridge`.
+5. The local server accepts anonymous JavaMail SOCKS connections and the bridge opens authenticated sockets to the real remote proxy.
+6. `AbstractProxyServerSyncingClosure` starts the local bridge only while SMTP/test-connection work is active and stops it after the last in-flight SMTP request finishes.
+
+Concurrency and lifecycle:
+
+- `MailerImpl` tracks active SMTP requests with an `AtomicInteger`.
+- Bridge start/stop is synchronized around the proxy server instance.
+- The bridge server uses a fixed thread pool for accepted SOCKS sessions; each session pipes client and remote sockets until the pipe stops.
+
+Gotchas:
+
+- SMTPS plus proxy is rejected in `MailerImpl` because the underlying JavaMail combination is not supported.
+- The authenticated SOCKS module must be available when authenticated proxy settings are used.
+- `getProxyBridgePort()` exposes the local bridge port, not the remote proxy port.
+
+## Smart MIME Message Structure Selection
+
+Simple Java Mail avoids one oversized MIME structure for every email. Instead, it picks the least complex producer that matches the actual content. The selection is centralized in [MimeMessageProducerHelper.java](modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageProducerHelper.java).
+
+The selector computes three content dimensions in [SpecializedMimeMessageProducer.java](modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/SpecializedMimeMessageProducer.java):
+
+- Mixed content: attachments or forwarded email.
+- Related content: embedded images.
+- Alternative content: more than one body variant among plain text, HTML, and calendar text.
+
+Resource naming, embedded image `cid:` values, attachment filenames, duplicate attachment names, and parse-side inline/attachment classification have their own focused history report in [MIME_RESOURCE_NAMING_REPORT.md](MIME_RESOURCE_NAMING_REPORT.md).
+
+Those booleans map to eight producers:
+
+| Producer | Mixed | Related | Alternative |
+| --- | --- | --- | --- |
+| `MimeMessageProducerSimple` | no | no | no |
+| `MimeMessageProducerAlternative` | no | no | yes |
+| `MimeMessageProducerRelated` | no | yes | no |
+| `MimeMessageProducerMixed` | yes | no | no |
+| `MimeMessageProducerMixedRelated` | yes | yes | no |
+| `MimeMessageProducerMixedAlternative` | yes | no | yes |
+| `MimeMessageProducerRelatedAlternative` | no | yes | yes |
+| `MimeMessageProducerMixedRelatedAlternative` | yes | yes | yes |
+
+After the selected producer creates the body structure, `SpecializedMimeMessageProducer.populateMimeMessage(...)` applies additional wrappers in a fixed order:
+
+1. S/MIME signing.
+2. S/MIME encryption, including the per-recipient certificate path when any recipient has a `smimeCertificate`.
+3. DKIM signing.
+4. Bounce-to wrapping with `ImmutableDelegatingSMTPMessage`.
+
+Gotchas:
+
+- New body-part concepts usually require revisiting the selector dimensions and every affected producer.
+- DKIM and S/MIME are optional modules, but if the email requests them the corresponding module must be on the runtime classpath.
+- The producer starts from `MessageIdFixingMimeMessage` so custom message IDs survive later wrapping.
+
+## Runtime Non-Null Instrumentation
+
+The API and implementation use JetBrains `@NotNull` and `@Nullable` annotations heavily.
+
+`org.jetbrains:annotations` is compiled in for source-level and public API nullability annotations. Earlier versions also depended on `com.github.bbottema:jetbrains-runtime-annotations`, a runtime-retention fork of the JetBrains annotations, because the CLI inspected `@Nullable` by reflection to detect optional command arguments. CLI optionality is now represented separately with `@Cli.Optional`, so the runtime-retention fork is no longer needed.
+
+The root Maven build configures `se.eris:notnull-instrumenter-maven-plugin` to instrument main and test classes. The current configuration recognizes `org.jetbrains.annotations.Nullable` and `org.jetbrains.annotations.NotNull`, and excludes assertion helpers plus `ServerReply`.
+
+Implications:
+
+- Do not treat nullability annotations as cosmetic. They affect generated bytecode and runtime validation.
+- Adding or changing public builder API nullability can still affect the CLI ergonomics, but only when CLI optionality should follow. In that case add both `@Nullable` and `@Cli.Optional`; `BuilderApiToPicocliCommandsMapper` marks CLI parameters as required unless the parameter has `@Cli.Optional`.
+- Generated code or protocol enum-like classes may need explicit instrumenter exclusions if instrumentation changes behavior.
+
+## Related Mechanisms Worth Checking
+
+These are not expanded as separate catalogue entries yet, but they are common places to inspect when changing core behavior:
+
+- Config resolution and defaults/overrides: `ConfigLoader`, `EmailProperty`, and `EmailGovernanceImpl`.
+- Spring property mapping: `SimpleJavaMailProperties` and `SimpleJavaMailSpringSupport`.
+- Outlook and EML conversion: `EmailConverter`, `OutlookEmailConverter`, and `MimeMessageParser`.
+- Transport strategy properties: `TransportStrategy` and `MailerImpl.createMailSession(...)`.
diff --git a/README.md b/README.md
index 321db2dff..468d56325 100644
--- a/README.md
+++ b/README.md
@@ -1,179 +1,180 @@
-[](LICENSE) [](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.simplejavamail%22%20AND%20a%3A%22simple-java-mail%22) [](http://www.javadoc.io/doc/org.simplejavamail/simple-java-mail) [](https://travis-ci.org/bbottema/simple-java-mail) [](https://www.codacy.com/app/b-bottema/simple-java-mail)
-
-# Simple Java Mail #
-
-Simple Java Mail is the simplest to use lightweight mailing library for Java, while being able to send complex emails including **authenticated socks proxy**(!), **attachments**, **embedded images**, **custom headers and properties**, **robust address validation**, **build pattern** and even **DKIM signing** and **external configuration files** with **property overriding**. Just send your emails without dealing with RFC's.
-
-The Simple Java Mail library is a thin layer on top of the JavaMail smtp mailing API that allows users to define emails on a high abstraction level without having to deal with mumbo jumbo such a 'multipart' and 'mimemessage'.
-
-### [simplejavamail.org](http://www.simplejavamail.org) ###
-
-```java
-ConfigLoader.loadProperties("simplejavamail.properties"); // optional default
-ConfigLoader.loadProperties("overrides.properties"); // optional extra
-
-Email email = new Email();
-
-email.addRecipient("lollypop", "lolly.pop@somemail.com", Message.RecipientType.TO);
-email.setReplyToAddress("lollypop", "lolly.pop@othermail.com");
-email.addRecipient("C. Cane", "candycane@candyshop.org", Message.RecipientType.TO);
-email.addRecipient("C. Bo", "chocobo@candyshop.org", Message.RecipientType.CC);
-email.setSubject("hey");
-email.setText("We should meet up! ;)");
-email.setTextHTML("<img src='cid:wink1'><b>We should meet up!</b><img src='cid:wink2'>");
-email.addEmbeddedImage("wink1", imageByteArray, "image/png");
-email.addEmbeddedImage("wink2", imageDatesource);
-email.addAttachment("invitation", pdfByteArray, "application/pdf");
-email.addAttachment("dresscode", odfDatasource);
-
-email.signWithDomainKey(privateKeyData, "somemail.com", "selector");
-
-new Mailer(
- new ServerConfig("smtp.host.com", 587, "user@host.com", "password"),
- TransportStrategy.SMTP_TLS,
- new ProxyConfig("socksproxy.host.com", 1080, "proxy user", "proxy password")
-).sendMail(email);
-```
-
----
-
-
-Simple Java Mail is available in Maven Central:
-
-```
-
- org.simplejavamail
- simple-java-mail
- 4.1.1
-
-```
-
-### Latest Progress ###
-
-v4.1.1
-
-- [#50](https://github.com/bbottema/simple-java-mail/issues/50): bug fix for manual naming datasources
-
-
-v4.1.0
-
-- [#48](https://github.com/bbottema/simple-java-mail/issues/48): Added programmatic support trusting hosts for SSL connections
-- [#47](https://github.com/bbottema/simple-java-mail/issues/47): Honor given names, deduce extension from datasource name, and more robust support for parsing mimemessages
-
-
-v4.0.0
-
-- [#41](https://github.com/bbottema/simple-java-mail/issues/41): added support for fast parallel batch processing
-- [#42](https://github.com/bbottema/simple-java-mail/issues/42): **added support for config files**
-- [#43](https://github.com/bbottema/simple-java-mail/issues/43): removed logging implementation dependencies from distribution and documented various sample configs
-- [#39](https://github.com/bbottema/simple-java-mail/issues/39): simplified and renamed packages to reflect the domain name of the new website: [simplejavamail.org](http://www.simplejavamail.org)
-- [#38](https://github.com/bbottema/simple-java-mail/issues/38): added support for anonymous proxy
-- [#38](https://github.com/bbottema/simple-java-mail/issues/38): **added support for authenticated proxy**
-
-NOTE: All packages have been renamed to "org.simplejavamail.(..)".
-
-
-v3.1.1
-
-Major feature: DKIM support!
-
-- [#36](https://github.com/bbottema/simple-java-mail/issues/36): Added proper toString and equals methods for the Email classes
-- [#33](https://github.com/bbottema/simple-java-mail/issues/33): Added support for DKIM domain key signing
-
-
-v3.0.2
-
-- [#35](https://github.com/bbottema/simple-java-mail/issues/35): added proper .equals() and .toString() methods
-- [#34](https://github.com/bbottema/simple-java-mail/issues/34): Fixed bug when disposition is missing (assume it is an attachment)
-- #00: added findbugs support internally
-
-
-v3.0.1
-
- * [#31](https://github.com/bbottema/simple-java-mail/issues/31): Fixed EmailAddressCriteria.DEFAULT and clarified Javadoc
-
-
-v3.0.0
-
- * [#30](https://github.com/bbottema/simple-java-mail/issues/30): Improved the demonstration class to include attachments and embedded images
- * [#29](https://github.com/bbottema/simple-java-mail/issues/29): The package has been restructured for future maintenance, breaking backwards compatibility
- * [#28](https://github.com/bbottema/simple-java-mail/issues/28): Re-added improved email validation facility
- * [#22](https://github.com/bbottema/simple-java-mail/issues/22): Added conversion to and from MimeMessage. You can now consume and produce MimeMessage objects with simple-java-mail
-
-
-v2.5.1
-
- * [#25](https://github.com/bbottema/simple-java-mail/issues/25): Added finally clause that will always close socket properly in case of an exception
-
-
-v2.5
-
- * [#24](https://github.com/bbottema/simple-java-mail/issues/24): Updated dependencies SLF4J to 1.7.13 and switched to the updated javax mail package com.sun.mail:javax.mail 1.5.5
-
-
-v2.4
-
- * [#21](https://github.com/bbottema/simple-java-mail/issues/21): builder API uses CC and BCC recipient types incorrectly
-
-
-v2.3
-
- * [#19](https://github.com/bbottema/simple-java-mail/issues/19): supporting custom Session Properties now and emergency access to internal Session object.
-
-
-v2.2
-
- * [#3](https://github.com/bbottema/simple-java-mail/issues/3): turned off email regex validation by default, with the option to turn it back on
- * [#7](https://github.com/bbottema/simple-java-mail/issues/7): fixed NullPointerException when using your own Session instance
- * [#10](https://github.com/bbottema/simple-java-mail/issues/10): properly UTF-8 encode recipient addresses
- * [#14](https://github.com/bbottema/simple-java-mail/issues/14): switched to SLF4J, so you can easily use your own selected logging framework
- * [#17](https://github.com/bbottema/simple-java-mail/issues/17): Added [fluent interface](http://en.wikipedia.org/wiki/Builder_pattern) for building emails (see [manual](https://github.com/bbottema/simple-java-mail/wiki/Manual) for an example)
-
-
-v2.1
-
- * fixed character encoding for reply-to, from, to, body text and headers (to UTF-8)
- * fixed bug where Recipient was not public resulting in uncompilable code when calling email.getRecipients()
-
-
-v2.0
-
- * added support for adding open headers, such as 'X-Priority: 2'
-
-
-v1.9.1
-
- * updated for Maven support
-
-
-v1.9
-
- * added support for JavaMail's reply-to address
- * made port optional as to support port defaulting based on protocol
- * added transport strategy default in the createSession method
- * tightened up thrown exceptions (MailException instead of RuntimeException)
- * added and fixed [JavaDoc](http://simple-java-mail.googlecode.com/svn/trunk/javadoc/users/index.html)
-
-
-v1.8
-
- * Added support for TLS (tested with gmail)
-
-
-v1.7
-
-Added support for SSL! (tested with gmail)
-
- * improved argument validation when creating a Mailer without preconfigured Session instance
-
-known possible issue: SSL self-signed certificates might not work (yet). Please let me know by e-mail or create a new issue
-
-
-v1.6
-
-Completed migration to Java Simple Mail project.
-
- * removed all Vesijama references
- * updated TestMail demonstration class for clarification
- * updated readme.txt for test run instructions
- * included log4j.properties
+[](modules/simple-java-mail/LICENSE-2.0.txt)
+[](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.simplejavamail%22%20AND%20v%3A%229.2.0%22)
+[](https://www.javadoc.io/doc/org.simplejavamail/maven-master-project)
+[](https://app.codacy.com/gh/bbottema/simple-java-mail)
+
+
+# Simple Java Mail #
+
+Simple Java Mail is a robust Java mailing library built to make production email simple to use: rich content, recipient governance, signing and encryption, transport security, configuration, diagnostics, conversion, and high-throughput batch or clustered sending.
+
+It keeps those concerns behind a consistent high-level API, while still giving you fluent builders, property/Spring configuration, defaults and overrides, validation, logging, and lower-level Jakarta Mail escape hatches when you need them.
+
+Under the hood, Simple Java Mail sits on top of [Angus Mail](https://eclipse-ee4j.github.io/angus-mail/) (previously [Jakarta Mail](https://jakartaee.github.io/mail-api/README-JakartaMail)).
+
+Simple Java Mail remains Java 8-compatible; Java 8 is the source, target, and minimum supported runtime.
+
+### Documentation ###
+
+The full user documentation lives at [simplejavamail.org](https://www.simplejavamail.org). Start there for the minimal banner example, grand examples, feature guides, configuration reference, diagnostics, and module overview.
+
+- [Features](https://www.simplejavamail.org/features.html#navigation)
+- [Configuration and Spring support](https://www.simplejavamail.org/configuration.html#navigation)
+- [Security](https://www.simplejavamail.org/security.html#navigation)
+- [Logging and debugging](https://www.simplejavamail.org/debugging.html#navigation)
+- [Modules](https://www.simplejavamail.org/modules.html)
+- [CLI](https://www.simplejavamail.org/cli.html#navigation)
+- [9.2 migration notes](https://www.simplejavamail.org/migration-notes-9.2.0.html)
+- [9.0 migration notes](https://www.simplejavamail.org/migration-notes-9.0.0.html)
+
+### Installation ###
+
+Simple Java Mail is available in [Maven Central](https://search.maven.org/search?q=g:org.simplejavamail):
+
+```xml
+
+ org.simplejavamail
+ simple-java-mail
+ 9.2.0
+
+```
+
+Read about additional modules you can add here: [simplejavamail.org/modules](https://www.simplejavamail.org/modules.html).
+
+### Development ###
+
+- [Project mechanisms catalogue](PROJECT_MECHANISMS_CATALOGUE.md) for optional module loading, CLI metadata generation, MIME selection, proxy bridging, concurrency, and non-null instrumentation.
+- [API expansion workflow](API_EXPANSION_WORKFLOW.md) for adding public API fields or builder methods.
+- [Developer environment setup](DEVELOPMENT.md) for JDK and build constraints.
+
+### Latest progress ###
+
+[v9.2.0](https://github.com/bbottema/simple-java-mail/releases/tag/9.2.0) - [Maven Central](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/9.2.0/)
+
+- [#692](https://github.com/bbottema/simple-java-mail/issues/692): added refresh-aware OAuth2 access-token providers for long-lived Mailers.
+- [#691](https://github.com/bbottema/simple-java-mail/issues/691): routed all asynchronous send failures through the returned `CompletableFuture`.
+- [#690](https://github.com/bbottema/simple-java-mail/issues/690): made serialized emails preserve send-ready content and fail clearly when pre-9.2 streams lack resource data.
+- [#689](https://github.com/bbottema/simple-java-mail/issues/689): moved governance opt-outs after email starters and removed the faulty pre-start methods.
+- [#685](https://github.com/bbottema/simple-java-mail/issues/685): corrected extra Jakarta Mail property precedence to system properties, environment variables, then property files.
+- [#684](https://github.com/bbottema/simple-java-mail/issues/684): corrected the S/MIME configuration example to use a valid message-content cipher.
+- [#683](https://github.com/bbottema/simple-java-mail/issues/683): moved CLI-only recipient parsing out of `EmailPopulatingBuilder` without changing CLI behavior.
+- [#680](https://github.com/bbottema/simple-java-mail/issues/680): made S/MIME signature status represent only verification that actually occurred and preserve failed combined results.
+- [#679](https://github.com/bbottema/simple-java-mail/issues/679): rejected DKIM configurations that exclude the mandatory `From` header.
+- [`55586ee6`](https://github.com/bbottema/simple-java-mail/commit/55586ee6f0836d0277f4a85beb267f0089d2ed22): added explicit `file:` and `base64:` DKIM private-key sources while preserving unprefixed values.
+- [#678](https://github.com/bbottema/simple-java-mail/issues/678): contained embedded-image auto-resolution within configured file, classpath, and URL bases.
+- [#677](https://github.com/bbottema/simple-java-mail/issues/677): replaced trust-all TLS defaults with the JVM trust store while retaining hostname verification.
+- [#676](https://github.com/bbottema/simple-java-mail/issues/676): bound the authenticated SOCKS bridge to JVM loopback instead of wildcard interfaces.
+
+See the [9.2 migration guide](https://www.simplejavamail.org/migration-notes-9.2.0.html) for changes requiring source or configuration updates.
+
+[v9.1.0](https://github.com/bbottema/simple-java-mail/releases/tag/9.1.0) - [v9.1.8](https://github.com/bbottema/simple-java-mail/releases/tag/9.1.8) - [Maven Central](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/9.1.8/)
+
+> **Spring-module notice:** Versions 9.0.0 through 9.1.5 package test application settings that can override an application's YAML configuration and prevent SMTP connections. If you use `spring-module`, upgrade to 9.1.6 or later.
+
+> **CLI recipient notice:** Versions 9.0.0 through 9.1.6 cannot combine TO, CC, and BCC recipients in one CLI command. Repeated `--email:withRecipients` options are merged and rejected. Upgrade to 9.1.7 or later.
+
+- **v9.1.8:** [#686](https://github.com/bbottema/simple-java-mail/issues/686): **Custom-session proxy routing:** `usingSession(session).withProxy(...)` now updates the intended `mail.smtp.socks.*` route while leaving the rest of the caller's session configuration untouched.
+- **v9.1.8:** [#687](https://github.com/bbottema/simple-java-mail/issues/687): **SMTPS through SOCKS:** removed the obsolete restriction that rejected SOCKS proxying for implicit-TLS SMTP connections.
+- **v9.1.8:** [#696](https://github.com/bbottema/simple-java-mail/issues/696), [#697](https://github.com/bbottema/simple-java-mail/issues/697): **Connection-pool resets:** the max-size and claim-timeout reset methods now restore their own settings without changing the core size or connection expiry.
+- **v9.1.7:** [#682](https://github.com/bbottema/simple-java-mail/issues/682): **Dedicated CLI recipient options:** restore independent `--email:to`, `--email:cc`, and `--email:bcc` options so one command can combine all three recipient types.
+- **v9.1.6:** [#681](https://github.com/bbottema/simple-java-mail/issues/681): **Spring configuration isolation:** stop packaging the Spring test `application.properties` in `spring-module`. Sample local-bind, SMTP client-hostname, transfer-encoding, and other test values can no longer override an application's YAML configuration or break SMTP connections.
+- **v9.1.5:** [#674](https://github.com/bbottema/simple-java-mail/issues/674), [#675](https://github.com/bbottema/simple-java-mail/issues/675): **SOCKS5 domain framing:** place the port after the UTF-8-encoded domain bytes and decode domain replies after their length octet, fixing internationalized-host requests and reply diagnostics.
+- **v9.1.4:** [#669](https://github.com/bbottema/simple-java-mail/issues/669), [#670](https://github.com/bbottema/simple-java-mail/issues/670): **EML file stream ownership:** close streams created internally by the `File`-based EML conversion overloads after synchronous parsing, while leaving caller-provided `InputStream` ownership unchanged.
+- **v9.1.4:** **Java 8-compatible dependency and release-tool maintenance:** updated SpotBugs annotations to 4.10.3 ([#671](https://github.com/bbottema/simple-java-mail/pull/671)), the Central Publishing Maven Plugin to 0.11.0 ([#672](https://github.com/bbottema/simple-java-mail/pull/672)), and Objenesis to 3.6 ([#673](https://github.com/bbottema/simple-java-mail/pull/673)).
+- **v9.1.3:** [#668](https://github.com/bbottema/simple-java-mail/issues/668): **New Outlook inline images:** updated `outlook-message-parser` to 1.16.2 so native-HTML-only `.msg` files match inline `cid:` images correctly and trailing NUL terminators no longer leak into attachment metadata.
+- **v9.1.2:** **Dependency and Java 8-compatible build-tool maintenance:** updated JMail to 2.2.0 ([#663](https://github.com/bbottema/simple-java-mail/pull/663)), Zip4j to 2.11.6 ([#666](https://github.com/bbottema/simple-java-mail/pull/666)), Exec Maven Plugin to 3.6.3 ([#664](https://github.com/bbottema/simple-java-mail/pull/664)), Maven Enforcer Plugin to 3.6.3 ([#665](https://github.com/bbottema/simple-java-mail/pull/665)), and Maven JAR Plugin to 3.5.1 ([#667](https://github.com/bbottema/simple-java-mail/pull/667)).
+- **v9.1.1:** **Java 8 build-tool maintenance** ([#662](https://github.com/bbottema/simple-java-mail/pull/662)): updated annotations and Maven compiler, JAR, OSGi bundle, and Karaf tooling to Java 8-compatible versions, with Dependabot guards against newer-Java-only upgrade lines.
+- **v9.1.0:** [#653](https://github.com/bbottema/simple-java-mail/issues/653): a configurable SMTP client hostname for the `EHLO` / `HELO` command.
+- **v9.1.0:** [#654](https://github.com/bbottema/simple-java-mail/issues/654): SMTP submission receipts for reading the server acceptance response after a send.
+- **v9.1.0:** No breaking changes; existing `sendMail(...)` behavior is unchanged.
+
+[v9.0.0](https://github.com/bbottema/simple-java-mail/releases/tag/9.0.0) - [v9.0.4](https://github.com/bbottema/simple-java-mail/releases/tag/9.0.4) - [Maven Central](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/9.0.4/)
+
+> **Spring-module notice:** Versions 9.0.0 through 9.1.5 package test application settings that can override an application's YAML configuration and prevent SMTP connections. If you use `spring-module`, upgrade to 9.1.6 or later.
+
+#### The Short Version ####
+
+**Simple Java Mail has been going strong-ish for about 20 years**, and **9.0.0** rolls *roughly two years of backlog* into a new major release.
+
+```
+simple-java-mail
+└── outlook-message-parser
+ └── rtf-to-html
+└── java-utils-mail-dkim
+└── java-utils-mail-smime
+└── smtp-connection-pool
+ └── clustered-object-pool
+ └── generic-object-pool
+└── java-socks-proxy-server
+```
+
+Across Simple Java Mail and the supporting libraries that keep the stack moving, **more than 100 GitHub issues and PRs** were reviewed, fixed, merged, or closed while keeping the project **Java 8-compatible**.
+
+**Major features:** the dedicated recipient builder API, per-recipient S/MIME certificates, first-class Delivery Status Notification support, pre-encoded attachment and embedded-image sending, mailer-level DKIM defaults, and simple batch/open-connection sending without the batch module.
+**Enhancements:** broader Outlook conversion metadata, MIME resource handling, content-transfer encoding control, debug routing, local SMTP bind configuration, batch cluster configuration, and Java module support.
+
+#### Migration Note ####
+
+**This is a breaking major release.** The old recipient-addition method jungle has been cleaned up in favor of the **recipient builder API**, so code that relied on the removed recipient overloads needs to migrate. Start with the [9.0 migration notes](https://www.simplejavamail.org/migration-notes-9.0.0.html) before upgrading.
+
+#### Major Features ####
+
+- **v9.0.0:** [#613](https://github.com/bbottema/simple-java-mail/issues/613): **Recipient builder API:** added dedicated builders for constructing single recipients and recipient collections.
+- **v9.0.0:** [#297](https://github.com/bbottema/simple-java-mail/issues/297): **Per-recipient S/MIME certificates:** enabled encrypted mail for multiple recipients with different certificates.
+- **v9.0.0:** [#574](https://github.com/bbottema/simple-java-mail/issues/574): **Delivery Status Notification (DSN):** added first-class DSN configuration.
+- **v9.0.0:** [#573](https://github.com/bbottema/simple-java-mail/issues/573): **Pre-encoded resources:** added pre-encoded attachment and embedded-image APIs.
+- **v9.0.0:** [#196](https://github.com/bbottema/simple-java-mail/issues/196): **Mailer-level DKIM defaults:** added default DKIM signing configuration so DKIM can be configured once per `Mailer`.
+- **v9.0.0:** [#569](https://github.com/bbottema/simple-java-mail/issues/569): **Simple batch and open-connection sending:** added `sendMailsInSimpleBatch(...)` for sequential batch work without the batch module and `withOpenConnection(...)` for callback-scoped reuse of a single SMTP connection.
+
+#### Enhancements ####
+
+- **v9.0.2:** [#645](https://github.com/bbottema/simple-java-mail/issues/645): **Outlook last-modifier metadata:** exposed `PR_LAST_MODIFIER_NAME` / `0x3FFA` as `OutlookMessageData#getLastModifierName()` without treating it as sender identity.
+- **v9.0.0:** [#614](https://github.com/bbottema/simple-java-mail/issues/614): **Outlook conversion metadata:** added explicit result APIs for inspecting source `.msg` headers and metadata without copying structural headers into converted emails, resolving [#609](https://github.com/bbottema/simple-java-mail/issues/609).
+- **v9.0.0:** [#605](https://github.com/bbottema/simple-java-mail/issues/605): **Per-body content-transfer encoding:** added `Content-Transfer-Encoding` configuration for plain text, HTML, and calendar content.
+- **v9.0.0:** [#566](https://github.com/bbottema/simple-java-mail/issues/566), [#597](https://github.com/bbottema/simple-java-mail/issues/597), [#602](https://github.com/bbottema/simple-java-mail/issues/602), [#607](https://github.com/bbottema/simple-java-mail/issues/607): **MIME resource `Content-ID` handling:** improved explicit IDs, parsed embedded images, and generated fallback IDs.
+- **v9.0.0:** [#589](https://github.com/bbottema/simple-java-mail/issues/589): **Jakarta Mail debug output:** added configurable debug output routing.
+- **v9.0.0:** [#568](https://github.com/bbottema/simple-java-mail/issues/568): **Local SMTP bind address:** added configuration for multi-IP SMTP hosts.
+- **v9.0.0:** [#565](https://github.com/bbottema/simple-java-mail/issues/565), [#618](https://github.com/bbottema/simple-java-mail/issues/618): **Batch cluster configuration:** fixed Java API cluster configuration and added property-defined cluster configurations for property-file and Spring-configured clustered sending.
+- **v9.0.0:** [#572](https://github.com/bbottema/simple-java-mail/issues/572), [#571](https://github.com/bbottema/simple-java-mail/issues/571): **S/MIME conversion leniency:** tolerate unsupported S/MIME payloads during Outlook conversion and preserve parsed email content when signature verification fails.
+- **v9.0.0:** [#606](https://github.com/bbottema/simple-java-mail/issues/606): **MIME type sanitizing:** sanitize malformed resource MIME types before generating attachment and embedded-image headers.
+- **v9.0.0:** [#541](https://github.com/bbottema/simple-java-mail/issues/541): **Resource headers:** removed the non-standard `filename` parameter from resource `Content-Type` headers; filenames remain available through `Content-Disposition`.
+- **v9.0.0:** [#265](https://github.com/bbottema/simple-java-mail/issues/265), [#237](https://github.com/bbottema/simple-java-mail/issues/237): **Java module support:** added Java 9 module descriptors to the core and facade jars so modular applications can require `org.simplejavamail` directly.
+
+#### Fixes and Compatibility ####
+
+- **v9.0.4:** [#652](https://github.com/bbottema/simple-java-mail/issues/652): **RFC 2047 address validation:** reject encoded-word syntax inside address specs during validation while keeping encoded display names valid.
+- **v9.0.3:** [#651](https://github.com/bbottema/simple-java-mail/issues/651): **Outlook plain-text RTF rendering:** preserved line breaks without exposing browser-default `
` styling in converted HTML.
+- **v9.0.0:** [#615](https://github.com/bbottema/simple-java-mail/issues/615): **Async test connections:** fixed `MailerGenericBuilder.async()` so no-arg `testConnection()` uses the configured async default.
+- **v9.0.0:** [#611](https://github.com/bbottema/simple-java-mail/issues/611): **SMTPS custom SSL socket factories:** fixed custom SSL socket factory configuration for SMTPS mailers.
+- **v9.0.0:** [#535](https://github.com/bbottema/simple-java-mail/issues/535): **Async failure reporting:** let async send and connection-test failures surface through the returned `CompletableFuture` without duplicate framework error logs.
+- **v9.0.0:** [#583](https://github.com/bbottema/simple-java-mail/issues/583): **Java 25 CLI startup:** fixed CLI startup on Java 25.
+- **v9.0.0:** [#616](https://github.com/bbottema/simple-java-mail/issues/616): **CLI optional-argument detection:** removed the runtime JetBrains annotation fork from CLI optional-argument detection.
+- **v9.0.0:** **Standalone CLI command cleanup:** `send`, `connect`, and `validate` now wait for command work and close mailer resources, preventing batch-module resources from keeping the process alive.
+
+#### Dependency and Supporting-Library Updates ####
+
+- **v9.0.3:** **Angus runtime alignment:** bumped Angus Mail to 2.0.5 and added Angus Activation 2.0.3.
+- **v9.0.3:** **Logging dependencies:** bumped Log4j from 2.25.4 to 2.26.1.
+- **v9.0.2:** **Jakarta API alignment:** bumped Jakarta Mail API to 2.1.5 and Jakarta Activation API to 2.1.4.
+- **v9.0.0:** **Core dependency maintenance:** bumped JMail to 2.1.0 ([#634](https://github.com/bbottema/simple-java-mail/pull/634)), commons-io to 2.22.0 ([#579](https://github.com/bbottema/simple-java-mail/pull/579), [#627](https://github.com/bbottema/simple-java-mail/pull/627)), Kryo to 5.6.2 ([#586](https://github.com/bbottema/simple-java-mail/pull/586)), Zip4j to 2.11.5 ([#587](https://github.com/bbottema/simple-java-mail/pull/587)), SubEthaSMTP to 7.2.2 ([#593](https://github.com/bbottema/simple-java-mail/pull/593), [#632](https://github.com/bbottema/simple-java-mail/pull/632)), Angus Mail to 2.0.4 ([#604](https://github.com/bbottema/simple-java-mail/pull/604)), Objenesis to 3.5 ([#580](https://github.com/bbottema/simple-java-mail/pull/580), [#635](https://github.com/bbottema/simple-java-mail/pull/635)), Lombok to 1.18.46 ([#636](https://github.com/bbottema/simple-java-mail/pull/636)), AssertJ Core to 3.27.7 ([#622](https://github.com/bbottema/simple-java-mail/pull/622)), and SpotBugs annotations to 4.10.2 ([#629](https://github.com/bbottema/simple-java-mail/pull/629)).
+- **v9.0.0:** **Logging dependencies:** aligned Log4j to 2.25.4 ([#624](https://github.com/bbottema/simple-java-mail/pull/624)) and SLF4J API to 2.0.18 ([#631](https://github.com/bbottema/simple-java-mail/pull/631)), keeping the Log4j bridge on `log4j-slf4j2-impl` for SLF4J 2.x.
+
+##### Supporting Libraries #####
+
+- **v9.0.3:** **`outlook-message-parser` 1.16.1 / `rtf-to-html` 2.0.2:** fixed browser-default `
` styling in Outlook plain-text RTF conversion ([#651](https://github.com/bbottema/simple-java-mail/issues/651)).
+- **v9.0.2:** **`outlook-message-parser` 1.16.0:** added source last-modifier metadata used by `OutlookMessageData#getLastModifierName()`.
+- **v9.0.0:** **`utils-mail-dkim` 3.3.0:** added configurable DNS provider URL support for DKIM domain-key TXT lookups, fixed the published automatic module name, and kept packaged artifacts free of JaCoCo probes.
+- **v9.0.0:** **`clustered-object-pool` 4.0.1** ([#6](https://github.com/bbottema/clustered-object-pool/issues/6)): added cluster-specific Java configuration for pool defaults, claim timeout, and load balancing.
+- **v9.0.0:** **`smtp-connection-pool` 3.0.1** ([#8](https://github.com/simple-java-mail/smtp-connection-pool/issues/8)): pulled in `clustered-object-pool` 4.0.1 so the batch-module fix for [#565](https://github.com/bbottema/simple-java-mail/issues/565) can keep connection-pool defaults per cluster key.
+- **v9.0.0:** **`smtp-connection-pool` 3.0.0:** made clustered SMTP pools generic over their cluster-key type and kept already-unusable connections from surfacing as generic pool error logs during transport close.
+- **v9.0.0:** **`java-socks-proxy-server` 4.2.0:** updated SOCKS live tests to use dynamic proxy ports instead of fixed ports.
+- **v9.0.0:** **`outlook-message-parser` 1.15.0:** improved Outlook `.msg` conversion by preserving nested message attachment metadata, fixing sent-date extraction ([#534](https://github.com/bbottema/simple-java-mail/issues/534)), fixing recipient bucket parsing ([#504](https://github.com/bbottema/simple-java-mail/issues/504)), broadening S/MIME detection, improving RTF-only body conversion ([#576](https://github.com/bbottema/simple-java-mail/issues/576)), and updating Apache POI.
+
+##### Build and Test Maintenance #####
+
+- **v9.0.3:** **Build maintenance:** bumped Maven Source Plugin to 3.4.0, NotNull Instrumenter Maven Plugin to 1.1.1, and Mycila License Maven Plugin to 4.6.
+- **v9.0.2:** **Build maintenance:** bumped Maven Assembly Plugin to 3.8.0 and Nexus Staging Maven Plugin to 1.7.0, and extended Dependabot guards for Java 11-only plugin lines.
+- **v9.0.1:** **Release packaging:** restored generated license headers in published source JARs and enabled publication of the standalone CLI ZIP and TAR classifier artifacts.
+- **v9.0.0:** **Build plugins and test stack:** bumped Maven Surefire Plugin to 3.5.6 ([#592](https://github.com/bbottema/simple-java-mail/pull/592), [#625](https://github.com/bbottema/simple-java-mail/pull/625)), Maven Clean Plugin to 3.5.0 ([#626](https://github.com/bbottema/simple-java-mail/pull/626)), Appassembler Maven Plugin to 2.1.0 ([#581](https://github.com/bbottema/simple-java-mail/pull/581)), Exec Maven Plugin to 3.5.0 ([#582](https://github.com/bbottema/simple-java-mail/pull/582)), Maven Deploy Plugin to 3.1.4 ([#619](https://github.com/bbottema/simple-java-mail/pull/619)), Maven Install Plugin to 3.1.4 ([#639](https://github.com/bbottema/simple-java-mail/pull/639)), Maven Javadoc Plugin to 3.12.0 ([#637](https://github.com/bbottema/simple-java-mail/pull/637)), Maven GPG Plugin to 3.2.8 ([#621](https://github.com/bbottema/simple-java-mail/pull/621)), and JaCoCo Maven Plugin to 0.8.15 ([#638](https://github.com/bbottema/simple-java-mail/pull/638)); aligned JUnit Platform/Jupiter at 1.14.4/5.14.4 while preserving Java 8 compatibility ([#596](https://github.com/bbottema/simple-java-mail/pull/596), [#633](https://github.com/bbottema/simple-java-mail/pull/633)); kept JUnit Pioneer on 1.9.1 because 2.x is Java 11 bytecode ([#630](https://github.com/bbottema/simple-java-mail/pull/630)); added Java 8 Dependabot guards; and replaced live embedded-image URL tests with deterministic local coverage ([#617](https://github.com/bbottema/simple-java-mail/issues/617)).
+
+The full stand-alone release history is maintained in [RELEASE_HISTORY.md](RELEASE_HISTORY.md).
diff --git a/RELEASE.txt b/RELEASE.txt
index 8e0023d07..024ec4b91 100644
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -1,137 +1,882 @@
-http://www.simplejavamail.org
-
-
- org.simplejavamail
- simple-java-mail
- 4.1.1
-
-
-RELEASE NOTES Java Simple Mail
-
-v4.1.1
-
-- #50: bug fix for manual naming datasources
-
-
-v4.1.0
-
-- #48: Added programmatic support trusting hosts for SSL connections
-- #47: Honor given names, deduce extension from datasource name, and more robust support for parsing mimemessages
-
-
-v4.0.0
-
-- #41: added support for fast parallel batch processing
-- #42: **added support for config files**
-- #43: removed logging implementation dependencies from distribution and documented various sample configs
-- #39: simplified and renamed packages to reflect the domain name of the new website: simplejavamail.org
-- #38: added support for anonymous proxy
-- #38: **added support for authenticated proxy**
-
-NOTE: All packages have been renamed to "org.simplejavamail.(..)".
-
-
-v3.1.1
-
-Major feature: DKIM support!
-
-- #36: Added proper toString and equals methods for the Email classes
-- #33: Added support for DKIM domain key signing
-
-
-v3.0.2
-
-- #35: added proper .equals() and .toString() methods
-- #34: Fixed bug when disposition is missing (assume it is an attachment)
-- #00: added findbugs support internally
-
-
-v3.0.1
-
-- #31: Fixed EmailAddressCriteria.DEFAULT and clarified Javadoc
-
-
-v3.0.0
-
-- #30: Improved the demonstration class to include attachments and embedded images
-- #29: The package has been restructured for future maintenance, breaking backwards compatibility
-- #28: Re-added improved email validation facility
-- #22: Added conversion to and from MimeMessage. You can now consume and produce MimeMessage objects with simple-java-mail
-
-
-v2.5.1
-
-- #25: Added finally clausule that will always close socket properly in case of an exception
-
-
-v2.5
-
-- #24: Updated dependencies SLF4J to 1.7.13 and switched to the updated javax mail package com.sun.mail:javax.mail 1.5.5
-
-
-v2.4
-
-- #21: Builder API uses CC and BCC recipient types incorrectly
-
-
-v2.3
-
-- #19: supporting custom Session Properties now and emergency access to internal Session object.
-
-
-v2.2
-
-- #3: turned off email regex validation by default, with the option to turn it back on
-- #7: fixed NullPointerException when using your own Session instance
-- #10: properly UTF-8 encode recipient addresses
-- #14: switched to SLF4J, so you can easily use your own selected logging framework
-- #17: Added fluent interface for building emails
-
-
-v2.1
-
-- fixed character encoding for reply-to, from, to, body text and headers (to UTF-8)
-- fixed bug where Recipient was not public resulting in uncompilable code when calling email.getRecipients()
-
-
-v2.0
-
-- added support for open headers (such as X-Priority)
-
-
-v1.9.1
-
-- updated for Maven support (library hasn't changed, but 1.9 was released incorrectly to Maven Central)
-
-
-v1.9
-
-- added support for JavaMail's reply-to address
-- made port optional as to support port defaulting based on protocol
-- added transport strategy default in the createSession method
-- tightened up thrown exceptions (MailException instead of RuntimeException)
-- added and fixed JavaDoc
-
-
-v1.8
-
-Added support for TLS (tested with gmail)
-
-
-v1.7
-
-Added support for SSL! (tested with gmail)
-
-- improved argument validation when creating a Mailer without preconfigured Session instance
-
-know possible issue: SSL self-signed certificates might not work (yet). Please let me know by e-mail or create a new issue
-
-
-v1.6
-
-Completed migration to Java Simple Mail project.
-
-- removed all Vesijama references
-- updated TestMail demonstration class for clarification
-- updated readme.txt for test run instructions
-- included log4j.properties
+https://www.simplejavamail.org
+
+
+ org.simplejavamail
+ simple-java-mail
+ 9.2.0
+
+
+v9.2.0
+
+- #692: added refresh-aware OAuth2 access-token providers for long-lived Mailers.
+- #691: routed all asynchronous send failures through the returned CompletableFuture.
+- #690: made serialized emails preserve send-ready content and fail clearly when pre-9.2 streams lack resource data.
+- #689: moved governance opt-outs after email starters and removed the faulty pre-start methods.
+- #685: corrected extra Jakarta Mail property precedence to system properties, environment variables, then property files.
+- #684: corrected the S/MIME configuration example to use a valid message-content cipher.
+- #683: moved CLI-only recipient parsing out of EmailPopulatingBuilder without changing CLI behavior.
+- #680: made S/MIME signature status represent only verification that actually occurred and preserve failed combined results.
+- #679: rejected DKIM configurations that exclude the mandatory From header.
+- 55586ee6: added explicit file: and base64: DKIM private-key sources while preserving unprefixed values.
+- #678: contained embedded-image auto-resolution within configured file, classpath, and URL bases.
+- #677: replaced trust-all TLS defaults with the JVM trust store while retaining hostname verification.
+- #676: bound the authenticated SOCKS bridge to JVM loopback instead of wildcard interfaces.
+
+See https://www.simplejavamail.org/migration-notes-9.2.0.html for changes requiring source or configuration updates.
+
+v9.1.0 - v9.1.8
+
+> **Spring-module notice:** Versions 9.0.0 through 9.1.5 package test application settings that can override an application's YAML configuration and prevent SMTP connections. If you use spring-module, upgrade to 9.1.6 or later.
+
+> **CLI recipient notice:** Versions 9.0.0 through 9.1.6 cannot combine TO, CC, and BCC recipients in one CLI command. Repeated --email:withRecipients options are merged and rejected. Upgrade to 9.1.7 or later.
+
+- **v9.1.8:** #686: **Custom-session proxy routing:** usingSession(session).withProxy(...) now updates the intended mail.smtp.socks.* route while leaving the rest of the caller's session configuration untouched.
+- **v9.1.8:** #687: **SMTPS through SOCKS:** removed the obsolete restriction that rejected SOCKS proxying for implicit-TLS SMTP connections.
+- **v9.1.8:** #696, #697: **Connection-pool resets:** the max-size and claim-timeout reset methods now restore their own settings without changing the core size or connection expiry.
+- **v9.1.7:** #682: **Dedicated CLI recipient options:** restore independent --email:to, --email:cc, and --email:bcc options so one command can combine all three recipient types.
+- **v9.1.6:** #681: **Spring configuration isolation:** stop packaging the Spring test application.properties in spring-module. Sample local-bind, SMTP client-hostname, transfer-encoding, and other test values can no longer override an application's YAML configuration or break SMTP connections.
+- **v9.1.5:** #674, #675: **SOCKS5 domain framing:** place the port after the UTF-8-encoded domain bytes and decode domain replies after their length octet, fixing internationalized-host requests and reply diagnostics.
+- **v9.1.4:** #669, #670: **EML file stream ownership:** close streams created internally by the File-based EML conversion overloads after synchronous parsing, while leaving caller-provided InputStream ownership unchanged.
+- **v9.1.4:** **Java 8-compatible dependency and release-tool maintenance:** updated SpotBugs annotations to 4.10.3 (#671), the Central Publishing Maven Plugin to 0.11.0 (#672), and Objenesis to 3.6 (#673).
+- **v9.1.3:** #668: **New Outlook inline images:** updated outlook-message-parser to 1.16.2 so native-HTML-only .msg files match inline cid: images correctly and trailing NUL terminators no longer leak into attachment metadata.
+- **v9.1.2:** **Dependency and Java 8-compatible build-tool maintenance:** updated JMail to 2.2.0 (#663), Zip4j to 2.11.6 (#666), Exec Maven Plugin to 3.6.3 (#664), Maven Enforcer Plugin to 3.6.3 (#665), and Maven JAR Plugin to 3.5.1 (#667).
+- **v9.1.1:** **Java 8 build-tool maintenance** (#662): updated annotations and Maven compiler, JAR, OSGi bundle, and Karaf tooling to Java 8-compatible versions, with Dependabot guards against newer-Java-only upgrade lines.
+- **v9.1.0:** #653: a configurable SMTP client hostname for the EHLO / HELO command.
+- **v9.1.0:** #654: SMTP submission receipts for reading the server acceptance response after a send.
+- **v9.1.0:** No breaking changes; existing sendMail(...) behavior is unchanged.
+
+
+v9.0.0 - v9.0.4
+
+> **Spring-module notice:** Versions 9.0.0 through 9.1.5 package test application settings that can override an application's YAML configuration and prevent SMTP connections. If you use spring-module, upgrade to 9.1.6 or later.
+
+#### The Short Version ####
+
+**Simple Java Mail has been going strong-ish for about 20 years**, and **9.0.0** rolls *roughly two years of backlog* into a new major release.
+
+```
+simple-java-mail
+└── outlook-message-parser
+ └── rtf-to-html
+└── java-utils-mail-dkim
+└── java-utils-mail-smime
+└── smtp-connection-pool
+ └── clustered-object-pool
+ └── generic-object-pool
+└── java-socks-proxy-server
+```
+
+Across Simple Java Mail and the supporting libraries that keep the stack moving, **more than 100 GitHub issues and PRs** were reviewed, fixed, merged, or closed while keeping the project **Java 8-compatible**.
+
+**Major features:** the dedicated recipient builder API, per-recipient S/MIME certificates, first-class Delivery Status Notification support, pre-encoded attachment and embedded-image sending, mailer-level DKIM defaults, and simple batch/open-connection sending without the batch module.
+**Enhancements:** broader Outlook conversion metadata, MIME resource handling, content-transfer encoding control, debug routing, local SMTP bind configuration, batch cluster configuration, and Java module support.
+
+#### Migration Note ####
+
+**This is a breaking major release.** The old recipient-addition method jungle has been cleaned up in favor of the **recipient builder API**, so code that relied on the removed recipient overloads needs to migrate. Start with the 9.0 migration notes before upgrading: https://www.simplejavamail.org/migration-notes-9.0.0.html
+
+#### Major Features ####
+
+- **v9.0.0:** #613: **Recipient builder API:** added dedicated builders for constructing single recipients and recipient collections.
+- **v9.0.0:** #297: **Per-recipient S/MIME certificates:** enabled encrypted mail for multiple recipients with different certificates.
+- **v9.0.0:** #574: **Delivery Status Notification (DSN):** added first-class DSN configuration.
+- **v9.0.0:** #573: **Pre-encoded resources:** added pre-encoded attachment and embedded-image APIs.
+- **v9.0.0:** #196: **Mailer-level DKIM defaults:** added default DKIM signing configuration so DKIM can be configured once per Mailer.
+- **v9.0.0:** #569: **Simple batch and open-connection sending:** added sendMailsInSimpleBatch(...) for sequential batch work without the batch module and withOpenConnection(...) for callback-scoped reuse of a single SMTP connection.
+
+#### Enhancements ####
+
+- **v9.0.2:** #645: **Outlook last-modifier metadata:** exposed PR_LAST_MODIFIER_NAME / 0x3FFA as OutlookMessageData#getLastModifierName() without treating it as sender identity.
+- **v9.0.0:** #614: **Outlook conversion metadata:** added explicit result APIs for inspecting source .msg headers and metadata without copying structural headers into converted emails, resolving #609.
+- **v9.0.0:** #605: **Per-body content-transfer encoding:** added Content-Transfer-Encoding configuration for plain text, HTML, and calendar content.
+- **v9.0.0:** #566, #597, #602, #607: **MIME resource Content-ID handling:** improved explicit IDs, parsed embedded images, and generated fallback IDs.
+- **v9.0.0:** #589: **Jakarta Mail debug output:** added configurable debug output routing.
+- **v9.0.0:** #568: **Local SMTP bind address:** added configuration for multi-IP SMTP hosts.
+- **v9.0.0:** #565, #618: **Batch cluster configuration:** fixed Java API cluster configuration and added property-defined cluster configurations for property-file and Spring-configured clustered sending.
+- **v9.0.0:** #572, #571: **S/MIME conversion leniency:** tolerate unsupported S/MIME payloads during Outlook conversion and preserve parsed email content when signature verification fails.
+- **v9.0.0:** #606: **MIME type sanitizing:** sanitize malformed resource MIME types before generating attachment and embedded-image headers.
+- **v9.0.0:** #541: **Resource headers:** removed the non-standard filename parameter from resource Content-Type headers; filenames remain available through Content-Disposition.
+- **v9.0.0:** #265, #237: **Java module support:** added Java 9 module descriptors to the core and facade jars so modular applications can require org.simplejavamail directly.
+
+#### Fixes and Compatibility ####
+
+- **v9.0.4:** #652: **RFC 2047 address validation:** reject encoded-word syntax inside address specs during validation while keeping encoded display names valid.
+- **v9.0.3:** #651: **Outlook plain-text RTF rendering:** preserved line breaks without exposing browser-default
styling in converted HTML.
+- **v9.0.0:** #615: **Async test connections:** fixed MailerGenericBuilder.async() so no-arg testConnection() uses the configured async default.
+- **v9.0.0:** #611: **SMTPS custom SSL socket factories:** fixed custom SSL socket factory configuration for SMTPS mailers.
+- **v9.0.0:** #535: **Async failure reporting:** let async send and connection-test failures surface through the returned CompletableFuture without duplicate framework error logs.
+- **v9.0.0:** #583: **Java 25 CLI startup:** fixed CLI startup on Java 25.
+- **v9.0.0:** #616: **CLI optional-argument detection:** removed the runtime JetBrains annotation fork from CLI optional-argument detection.
+- **v9.0.0:** **Standalone CLI command cleanup:** send, connect, and validate now wait for command work and close mailer resources, preventing batch-module resources from keeping the process alive.
+
+#### Dependency and Supporting-Library Updates ####
+
+- **v9.0.3:** **Angus runtime alignment:** bumped Angus Mail to 2.0.5 and added Angus Activation 2.0.3.
+- **v9.0.3:** **Logging dependencies:** bumped Log4j from 2.25.4 to 2.26.1.
+- **v9.0.2:** **Jakarta API alignment:** bumped Jakarta Mail API to 2.1.5 and Jakarta Activation API to 2.1.4.
+- **v9.0.0:** **Core dependency maintenance:** bumped JMail to 2.1.0 (#634), commons-io to 2.22.0 (#579, #627), Kryo to 5.6.2 (#586), Zip4j to 2.11.5 (#587), SubEthaSMTP to 7.2.2 (#593, #632), Angus Mail to 2.0.4 (#604), Objenesis to 3.5 (#580, #635), Lombok to 1.18.46 (#636), AssertJ Core to 3.27.7 (#622), and SpotBugs annotations to 4.10.2 (#629).
+- **v9.0.0:** **Logging dependencies:** aligned Log4j to 2.25.4 (#624) and SLF4J API to 2.0.18 (#631), keeping the Log4j bridge on log4j-slf4j2-impl for SLF4J 2.x.
+
+##### Supporting Libraries #####
+
+- **v9.0.3:** **outlook-message-parser 1.16.1 / rtf-to-html 2.0.2:** fixed browser-default
styling in Outlook plain-text RTF conversion (#651).
+- **v9.0.2:** **outlook-message-parser 1.16.0:** added source last-modifier metadata used by OutlookMessageData#getLastModifierName().
+- **v9.0.0:** **utils-mail-dkim 3.3.0:** added configurable DNS provider URL support for DKIM domain-key TXT lookups, fixed the published automatic module name, and kept packaged artifacts free of JaCoCo probes.
+- **v9.0.0:** **clustered-object-pool 4.0.1** (#6): added cluster-specific Java configuration for pool defaults, claim timeout, and load balancing.
+- **v9.0.0:** **smtp-connection-pool 3.0.1** (#8): pulled in clustered-object-pool 4.0.1 so the batch-module fix for #565 can keep connection-pool defaults per cluster key.
+- **v9.0.0:** **smtp-connection-pool 3.0.0:** made clustered SMTP pools generic over their cluster-key type and kept already-unusable connections from surfacing as generic pool error logs during transport close.
+- **v9.0.0:** **java-socks-proxy-server 4.2.0:** updated SOCKS live tests to use dynamic proxy ports instead of fixed ports.
+- **v9.0.0:** **outlook-message-parser 1.15.0:** improved Outlook .msg conversion by preserving nested message attachment metadata, fixing sent-date extraction (#534), fixing recipient bucket parsing (#504), broadening S/MIME detection, improving RTF-only body conversion (#576), and updating Apache POI.
+
+##### Build and Test Maintenance #####
+
+- **v9.0.3:** **Build maintenance:** bumped Maven Source Plugin to 3.4.0, NotNull Instrumenter Maven Plugin to 1.1.1, and Mycila License Maven Plugin to 4.6.
+- **v9.0.2:** **Build maintenance:** bumped Maven Assembly Plugin to 3.8.0 and Nexus Staging Maven Plugin to 1.7.0, and extended Dependabot guards for Java 11-only plugin lines.
+- **v9.0.1:** **Release packaging:** restored generated license headers in published source JARs and enabled publication of the standalone CLI ZIP and TAR classifier artifacts.
+- **v9.0.0:** **Build plugins and test stack:** bumped Maven Surefire Plugin to 3.5.6 (#592, #625), Maven Clean Plugin to 3.5.0 (#626), Appassembler Maven Plugin to 2.1.0 (#581), Exec Maven Plugin to 3.5.0 (#582), Maven Deploy Plugin to 3.1.4 (#619), Maven Install Plugin to 3.1.4 (#639), Maven Javadoc Plugin to 3.12.0 (#637), Maven GPG Plugin to 3.2.8 (#621), and JaCoCo Maven Plugin to 0.8.15 (#638); aligned JUnit Platform/Jupiter at 1.14.4/5.14.4 while preserving Java 8 compatibility (#596, #633); kept JUnit Pioneer on 1.9.1 because 2.x is Java 11 bytecode (#630); added Java 8 Dependabot guards; and replaced live embedded-image URL tests with deterministic local coverage (#617).
+
+v8.12.0 - v8.12.6
+
+- v8.12.6 (18-April-2025): #595: [bug] Spring configuration - fix support for simplejavamail.extraproperties
+- v8.12.5 (05-March-2025): Bumped PATCH versions of various dependencies (#553, #554, #555, #562, #567)
+- v8.12.4 (12-December-2024): #558: [bug] Mailer.close() exception because it attempts to shutdown batch-module connection pools, even if not available on the classpath
+- v8.12.3 (25-November-2024): #563: [enhancement] Add getter for Authenticated SOCKS server port
+- v8.12.2 (05-October-2024): #552: [bug] support iCalendar events with METHOD defined in body instead of Content-Type
+- v8.12.1 (02-October-2024): #533: [maintenance] Bump com.github.therapi:therapi-runtime-javadoc-scribe from 0.13.0 to 0.15.0
+- v8.12.1 (02-October-2024): #532: [maintenance] Bump com.sanctionco.jmail:jmail from 1.4.1 to 1.6.3
+- v8.12.1 (02-October-2024): #531: [maintenance] Bump com.github.bbottema:java-socks-proxy-server from 4.0.0 to 4.1.2
+- v8.12.1 (02-October-2024): #528: [maintenance] Bump com.github.davidmoten:subethasmtp from 7.0.1 to 7.1.1
+- v8.12.1 (02-October-2024): #522: [maintenance] Bump jakarta.annotation:jakarta.annotation-api from 1.3.5 to 3.0.0
+- v8.12.0 (26-September-2024): #550: [bug] Environment variables are not being loaded properly
+- v8.12.0 (26-September-2024): #538: [bug] System properties are only read if configuration file exists in class path
+- v8.12.0 (26-September-2024): #546: [Enhancement] Trim whitespace in encoder values for Content-Encoding
+
+
+v8.11.0 - v8.11.3
+
+- v8.11.3 (15-September-2024): #543: [Enhancement] reduce log spam from the batch-module
+- v8.11.2 (08-June-2024): #530: [Bug] After converting Outlook .msg to EML, bullet lists have duplicate numbering HTML converted from RTF
+- v8.11.1 (28-May-2024): #529: Bump smtp-connection-pool from 2.3.2 to 2.3.3 which improves performance and fixes a rare ConcurrentModificationException
+- v8.11.1 (28-May-2024): #527: Bump org.slf4j:slf4j-api from 2.0.9 to 2.0.13
+- v8.11.1 (28-May-2024): #523: Bump org.jacoco:jacoco-maven-plugin from 0.8.5 to 0.8.12
+- v8.11.0 (25-May-2024): #526: When reading .msg files the RTF converted to HTML is garbled in some cases where the appropriate charset is not detected properly
+
+NOTE: v8.11.0 contains many (minor) dependency updates to resolve CVE issues, including:
+ - parent POM upgrade: Upgrades test dependencies as well as SLF4J versions
+ - outlook-message-parser 1.13.2 -> 1.14.0
+ - java-reflection 4.0.1 -> 4.0.2
+ - smtp-connection-pool 2.3.1 -> 2.3.2
+ - utils-mail-dkim 3.1.0 -> 3.2.0
+ - jakarta.mail-api 2.1.2 -> 2.1.3
+ - angus-mail 2.0.2 -> 2.0.3
+ - therapi-runtime-javadoc 0.13.0 -> 0.15.0
+ - kryo 5.0.0-RC1 -> 5.6.0
+ - maven-assembly-plugin 3.1.0 -> 3.7.1 (only for the CLI module during build)
+
+
+v8.10.0 - v8.10.1
+
+- v8.10.1 (04-May-2024): #510: Update upstream dependency generic-object-pool, which solves a critical bug when there are exceptions during allocation
+- v8.10.0 (30-April-2024): #508: [enhancement+bug] Make EmailConverter API more consistent regarding Session parameter, don't use `Session.getDefaultInstance` anymore and fix bug where `emlToEmailBuilder` used `emlToMimeMessage`
+
+
+v8.10.0 (30-April-2024)
+
+- #508: [enhancement+bug] Make EmailConverter API more consistent regarding Session parameter, don't use `Session.getDefaultInstance` anymore and fix bug where `emlToEmailBuilder` used `emlToMimeMessage`
+
+
+v8.9.0 (26-April-2024)
+
+- #507: [security] Update 3rd party dependencies to get rid of all currently known CVE issues (see issue for details)
+
+
+v8.8.0 - v8.8.4
+
+- v8.8.4 (23-April-2024): #506: Upgrade utils-mail-smime dependency to 2.3.2, to resolve CVE issue in bouncycastle
+- v8.8.3 (13-April-2024): #502: [Bug] Message headers not treated with case insensitivity as per RFC, causing deviating headers to slip through the filters
+- v8.8.2 (05-April-2024): #495: Add config support for 'verifyingServerIdentity' with SMTP, also: since Angus 1.1.0 server identity checks are on by default and can be countered by mailerBuilder.verifyingServerIdentity(false)
+- v8.8.2 (05-April-2024): #501: [dependency] Update outlook-message-parser dependency, which has improved support for X500 addresses
+- v8.8.1 (04-April-2024): #500: [bug] Fix parsing addresses from headers in EML files, like a Disposition-Notification-To with umlaut
+- v8.8.0 (22-March-2024): #499: [Enhancement] Expose finer-grained DKIM configuration through the builder api and disable 'l-param' by default)
+
+NOTE: this release changes the default for DKIM signing from 'l-param' true to false. If you rely on this feature, you need to enable it explicitly (see the updated https://www.simplejavamail.org/security.html#section-sending-dkim).
+
+
+v8.7.0 - v8.7.1
+
+- v8.7.1 (20-March-2024): #498: [Enhancement] Make S/MIME algorithms configurable (signature algorithm for signing, key encapsulation and cipher algorithms for encryption)
+- v8.7.1 (20-March-2024): #497: [Bug] Order of attachments is lost when converting a MimeMessage to an Email
+- v8.7.0 (20-March-2024): don't use this version: versioning messed up
+
+NOTE: this breaks the API for S/MIME related builder methods. Refer to the [S/MIME documentation](https://www.simplejavamail.org/security.html#section-sending-smime) for the new API.
+
+
+v8.6.0 - v8.6.3
+
+- v8.6.3 (13-February-2024): #491: [bug] Attachment body parts should separately parse Content-Disposition and ContentID, possible resulting in an downloadable attachment that is also embedded
+- v8.6.2 (27-January-2024): #493: [bug] don't require smime-module when adding collection of headers (also used when copying email)
+- v8.6.1 (18-January-2024): #487: Move header filtering from MimeMessageParser to EmailConverter, thereby enabling access to all parsed headers when using MimeMessageParser directly
+- v8.6.1 (18-January-2024): #489: Finished update to Angus Mail by updating activation dependency
+- v8.6.0 (17-January-2024): #489: Update to Angus Mail
+
+NOTE: this release switches to Angus Mail which should be a transparent change, but if you encounter any issues, please report them.
+ One known issue is that Angus, since 1.1.0, performs server identity checks by default, which was previously disabled for SMTP. If you encounter issues with this, you can disable it with `mailerBuilder.verifyingServerIdentity(false)` and starting from 8.8.2, this also works with SMTP transport strategy (see #495).
+
+
+v8.5.0 - v8.5.1
+
+- v8.5.1 (15-December-2023): #486: [dependency] Handle Outlook's Non-Standard S/MIME Signed Messages
+- v8.5.0 (13-December-2023): #484: [bug] Addresses passed as string are not always interpreted correctly
+
+
+v8.4.0 (12-December-2023)
+
+- #483: Enhancement: add native support for overriding envelope-level receiver(s)
+
+
+v8.3.0 - v8.3.5
+
+- v8.3.5 (10-December-2023): #482: Bug: 'IllegalArgumentException: emailAddressList is required' when parsing mail with incorrect recipients from Outlook message
+- v8.3.4 (08-December-2023): #481: Enhancement: don't crash on invalid empty attachments when parsing Outlook messages
+- v8.3.3 (03-December-2023): #477: Enhancement: Support Exchange proprietary addresses (X.500 DAP)
+- v8.3.2 (26-November-2023): #480: Bug: Multiple attachments with same name get the same Content-ID, causing them to refer to the same file content
+- v8.3.1 (09-October-2023): #440: Bug: names manually specified for embedded images are overridden and have extension added, breaking cid: references in HTML body
+- v8.3.0 (09-October-2023): #475: Enhancement: Add configuration metadata for Spring Boot application properties
+
+
+v8.2.0 (02-October-2023)
+
+- #473: Bugfix: Add missing support for multiple reply-to addresses
+
+
+v8.1.0 - v8.1.3
+
+- v8.1.3 (14-July-2023): #467: Security: Medium severity vulnerability is detected in org.bouncycastle transitive dependency
+- v8.1.3 (14-July-2023): #466: Maintenance: Let Mailer implement AutoCloseable, so it shuts down the connection pool automatically when disposed of by Spring
+- v8.1.2 (23-June-2023): #465: Regression bug #461: Simple Java Mail always requires DKIM/SMIME modules
+- v8.1.1 (07-June-2023): #461: Bugfix: Fixed MessageID not preserved when signing/encrypting with S/MIME and/or DKIM
+- v8.1.0 (15-April-2023): #458: Missing osgi headers (#288) and added support for Apache Karaf
+- v8.1.0 (15-April-2023): #288: Maintenance: missing OSGI package-exports from core-module
+
+
+v8.0.0 - v8.0.1
+
+- v8.0.1 (30-April-2023): #456: Enhancement: make Content-Transfer encoder detection more lenient, supporting more values from the wild
+- v8.0.0 (08-March-2023): #451: Feature: Make defaults and overrides a first class feature
+- v8.0.0 (08-March-2023): #452: Enhancement: with ".disableAllClientValidation(true)", also ignore errors from the completeness check
+- v8.0.0 (08-March-2023): #450: Bug: when using dispositionNotificationTo or returnReceiptTo mode, when the corresponding emails are not filled, it fails even though it should fall back to replyTo or From
+- v8.0.0 (08-March-2023): #449: Bug: IllegalArgumentException on parsing empty header name and value (when parsing Outlook message)
+- v8.0.0 (08-March-2023): #448: Bug: withEmailDefaults and withEmailOverrides does not work with CustomMailer
+- v8.0.0 (08-March-2023): #447: Enhancement: allow defaults/overrides to ignore individual fields (turn off for specific properties)
+- v8.0.0 (08-March-2023): #446: Enhancement: add missing defaults properties for DKIM
+
+This release changes how Email instances are built, or more specifically, when defaults and overrides are applied.
+There are now overloaded build methods that provide similar behaviour as previous versions.
+
+
+v7.9.0 - v7.9.1
+
+- v7.9.1 (22-February-2023): #444: Bugfix: encoded delimited recipients in EML not parsed properly
+- v7.9.0 (21-February-2023): #344: Enhancement: make DKIM signing more flexible by allowing header exclusions in DKIM signature
+
+
+v7.8.0 - v7.8.3
+
+- v7.8.3 (21-February-2023): #293: Bugfix: Decoding missing in a few placed when parsing MimeMessage or sending an Email
+- v7.8.2 (09-February-2023): #442: Enhancement: Simple Java Mail should throw an exception when trying to utilize S/MIME with smime-module on the classpath
+- v7.8.1 (01-February-2023): #438: Bug: properly Fail-Fast in case of Transport claim timeout in the batch-module, rather than running into NPE further down the line
+- v7.8.0 (24-January-2023): #436: Enhancement: add mailerBuilder.withTransportModeLoggingOnly() as mailer API entry point
+- v7.8.0 (24-January-2023): #435: Enhancement: SMTP server config should be optional in case a CustomMailer is used
+- v7.8.0 (24-January-2023): #427: Feature: set a maximum email size on Mailer level which throws EmailToBig exception when exceeded
+
+
+v7.7.0 - [v7.7.1](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C7.7.1%7Cjar)
+
+- v7.7.1 (18-January-2023): #434: Regression bug in #430: Email parameter missing in CustomMailer interface
+- v7.7.0 (17-January-2023): #430: Enhancement: auto-reconnect (if needed) when reclaiming a Transport connection from the SMTP connection
+- v7.7.0 (17-January-2023): #383: Feature: be able to set defaults and overrides on the Mailer level, rather than email or global level
+
+7.7.0 moves the conversion of Email to MimeMessage to after a Transport instance has been selected (in case of a cluster of SMTP servers),
+so we can apply defaults/overrides on the Mailer level, meaning you can configure 'global' values for individual SMTP servers
+(like a fixed FROM per server).
+
+
+v7.6.0 (05-January-2022)
+
+- #421: Enhancement: Add support for OAUTH2 authentication
+
+
+v7.5.0 - v7.5.2
+
+- v7.5.2 (25-December-2022): #429: Bug: wrong username property used when password authentication is not needed
+- v7.5.1 (12-December-2022): #416: Bug: Support encoder names regardless of their case ("base64" is the same as "BASE64")
+- v7.5.1 (12-December-2022): #424: Maintenance: bump JMail dependency from 1.2.1 to 1.4.1
+- v7.5.0 (28-July-2022): #411: Enhancement: expose validation sub steps in the MailerHelper class for the completeness check, CRLF inject scans and address validations
+- v7.5.0 (28-July-2022): #410: Bug: CRLF injection scan missing for dispositionNotificationTo and returnReceiptTo
+- v7.5.0 (28-July-2022): #390: Enhancement: make client sided validation optional, turning off address validation and CRLF injection detection
+
+
+v7.4.0 (19-July-2022)
+
+- #407: Enhancement: Process all Outlook message headers, either copying the as-is or translating them to respective Simple Java Mail API calls
+- #404: Minor bugfix: the new attachment's contentDescription was missing in Email.toString()
+
+
+v7.3.0 (15-July-2022)
+
+- #405: Feature: Expand email builder API to support forced content Content-Transfer-Encoding for attachments, like quoted-printable, base64, 7BIT and others
+- #404: Feature: Expand email builder API to support Content-Description on attachments
+
+
+v7.2.0 - v7.2.1
+
+- v7.2.1 (13-July-2022): #396: Enhancement: make Outlook support tolerant of invalid/empty nested Outlook message attachments
+- v7.2.0 (13-July-2022): #399: Feature: Expand email builder API to support selective content encoding, like quoted-printable, base64, 7BIT and others
+
+
+v7.1.0 - v7.1.3
+- v7.1.3 (12-July-2022): #403: Security: Bump zip4j (only used during testing)
+- v7.1.2 (12-July-2022): #401: Enhancement: Add HEIC and WEBP support when dynamically resolving embedded images from classpath
+- v7.1.2 (12-July-2022): #402: Security: Update Log4j to 2.17.1
+- v7.1.2 (12-July-2022): #393: Security: Update Apache POI and POI Scratchpad
+- v7.1.1 (27-March-2022): #387: Bug: memory leak in SMPT connection pool when 3rd party deallocation failed with exception
+- v7.1.0 (25-January-2022): #379: Maintenance: Adjust dependencies and make Java 9+ friendly
+
+
+v7.0.1 - v7.0.2
+
+- v7.0.2 (25-January-2022): #329: Enhancement: Exceptions cause error-level logging in addition to rethrowing the exception, but should just include the message in a custom exception
+- v7.0.2 (25-January-2022): #378: Bug: package org.simplejavamail.internal.modules causes split package problem in Java9+
+- v7.0.1 (22-January-2022): #375: Bug: [batch-module](https://www.simplejavamail.org/features.html#section-sending-asynchronously) gives error when there is a [custom mailer](https://www.simplejavamail.org/features.html#section-custom-mailer)
+
+
+v7.0.0 (2-January-2022)
+
+It has been two years since the last major release, but 7.0.0 finally here!
+
+#### What's new ####
+
+Major features:
+- #322: Simple Java Mail migrated to Java 8 finally (see notes below)
+- #295: And also switched to JavaMail's successor Jakarta Mail 2.0.1 (see notes below)
+- #323: Solved the great CLI performance problem (now executes near instantly)
+- #319: Replaced the underlying regex-based email-address validation library with the lexer based [JMail](https://github.com/RohanNagar/jmail), which is faster, correcter, documented better and is more up-to-date with RFC's
+- #367: The sendMail/testConnection methods now has proper support for `CompletableFuture`
+
+Bugfixes:
+- #352: Bug: names regex groups are not supported in Android JVM
+- #326: Bug: NullPointer when parsing Outlook Message with nested empty Outlook message
+- #330: Bug: cli expected --mailer arguments duplicated 3 times
+- #324: Bug: Add back missing log4j2 for CLI library
+
+Maintenance:
+- #368: Resolve log4j (Java8) [vulnerability](https://logging.apache.org/log4j/2.x/security.html) in Simple Java Mail's CLI module
+- #330: Improved feedback from failing CLI commands
+- #327: Implement toString() for Mailer instances for debugging purposes
+
+#### About the migration ####
+
+Updating to Java8/Jakarta 2.0.1 posed a challenge as the 3rd party S/MIME library https://github.com/markenwerk/java-utils-mail-smime has been abandoned/archived while developing Simple Java Mail. Furthermore, it was still under LGPL3 license while everything else is ApacheV2.
+
+Thankfully, I obtained permissions from the maintainers -as well as original developers from decades ago on SourceForge- to take both java-utils-mail-smime and java-utils-mail-dkim under my wings at Simple Java Mail and change the licensing model! You can now post issues and pull requests here:
+- https://github.com/simple-java-mail/java-utils-mail-smime
+- https://github.com/simple-java-mail/java-utils-mail-dkim
+
+
+v6.7.0 - v6.7.6
+
+- v6.7.6 (22-January-2022): #375: Bug: batch-module gives error when there is a custom mailer
+- v6.7.5 (26-December-2021): #338: Enhancement: Also return AsyncResponse from plain Mailer.sendEmail(singleArgument) as async can be configured through MailerBuilder now
+- v6.7.4 (25-December-2021): #331: Enhancement: Coalesce empty SMTP server arguments to null to support CLI better
+- v6.7.3 (25-December-2021): #335: Bugfix: Precondition nonNull check also checks nonEmpty and breaks on clearEmailAddressCriteria
+- v6.7.2 (25-December-2021): #318: Maintenance: Allow zero data attachments so Outlook message conversions don't crash and burn
+- v6.7.1 (25-December-2021): #346: Bugfix: Add option to parse MimeMessage without fetching attachment data from server - Properly return named datasource without fetching all the data if unwanted
+- v6.7.0 (25-December-2021): #356: Enhancement: Improve for support for Android < 8.0 (Fix NoClassDefFoundError)
+- v6.7.0 (25-December-2021): #351: Bugfix: emlToEmail() and mimeMessageToEmail() break on mesages with duplicate names and legacy empty nested messages
+- v6.7.0 (25-December-2021): #347: Maintenance: Reduce log-spam and prevent exception on every module availability check
+- v6.7.0 (25-December-2021): #346: Feature: Add option to parse MimeMessage without fetching attachment data from server
+
+
+v6.6.0 - v6.6.2
+
+- v6.6.2 (23-December-2021): #365: Resolve log4j vulnerability in Simple Java Mail
+- v6.6.1 (12-June-2021): #321: Ignore malformed recipient addresses and continue parsing email data
+- v6.6.0 (1-June-2021): #320: Added default S/MIME signing to Mailer lever and fixed crippling performance bug
+
+
+v6.5.0 - v6.5.4
+
+- v6.5.4 (22-May-2021): #315: Enhancement: Nested attachments of Outlook message attachments are now preserved as standard EML MimeMessage attachments
+ **NOTE:** This _removes_ the Kryo dependency
+- v6.5.3 (4-May-2021): #314: Bugfix: Nested attachments of Outlook message attachments are now preserved, by utilizing Kryo
+ **NOTE:** This add Kryo as extra dependency to the outlook-module (and is subsequently removed in 6.5.4)
+- v6.5.2 (15-April-2021): #307: Bugfix: text/calendar as string -> ClassCastException (if calendar type is not Inputstream)
+- v6.5.1 (10-April-2021): #307 / #310: Bugfix: embedded image name got mangled if they included an extension
+- v6.5.0 (16-February-2021): #298: Enhancement: Nested Outlook messages aren't discarded anymore, but parsed to serialized Email objects
+- v6.5.0 (16-February-2021): #292: Bugfix: NullPointerException in SmimeUtilFixed when protocol is missing (which is valid)
+- v6.5.0 (16-February-2021): #289: Bugfix: Support multiple headers with same key
+
+This release breaks all GET/SET api regarding headers used as map (Map -> Map>)
+This release might break api in the rare case you relied on the attachment list and you have nested Outlook .msg attachments (previously omitted in the results)
+
+
+v6.4.0 - v6.4.5
+
+- v6.4.5 (13-Februari-2021): #306: Maintenance: Update outlook-message-parser to 1.7.9
+- v6.4.5 (13-Februari-2021): #304: Regression bugfix: batch-module needed for sending mails async, basic version should work without
+- v6.4.5 (13-Februari-2021): #303: Bugfix: EML Attachments are modified/have the wrong size
+- v6.4.4 (25-October-2019): #294: Always invoke async success/exception handlers even if set after sending email (behaving more like promises/futures)
+- v6.4.4 (25-October-2019): #291: On Exception only log the email ID at error level and log the whole email at trace level
+- v6.4.4 (25-October-2019): #290: Only perform expensive logging logic if respective logging level is enabled
+- v6.4.3 (6-August-2019): #284: Improved support for Calendar attachments created by gMail
+- v6.4.3 (6-August-2019): #283: Bugfix: Fix support for reading Calendar attachments with quoted-printable transfer-type
+- v6.4.2 (3-August-2019): #281: Bugfix: Fix support for reading Calendar attachments
+- v6.4.1 (26-July-2019): #252: Bugfix: Added missing support for S/MIME envloped signing
+- v6.4.0 (19-July-2019): #268: Bugfix: Immediately resolve InputStreams when building emails, don't reuse (which resulted in errors)
+ * This primarily affects the builder api for S/MIME and DKIM signing / encryption.
+
+
+v6.3.0 - v6.3.2 (11-July-2020 - 12-July-2020)
+
+- #271: Bugfix: Attachment (file)names with special characters should not be encoded
+- #248: Bugfix: MimeMessageHelper: use complete filename as resource name
+- #279: Allow extra Session properties configured through simplejavamail.properties
+- #277: Add API for using custom SSLSocketFactory
+
+
+v6.2.0 (6-July-2020)
+
+This release adds the following major new feature:
+
+- #260: Add support for dynamic datasource resolution (file/url/classpath) for embedded images in HTML body
+
+
+v6.1.0 (5-July-2020)
+
+- #264: Switch from AssertionError to IllegalStateException
+- Bumped outlook-message-parser from 1.7.3 to 1.7.5
+ - bugfix for parsing chinese unsent Outlook messages
+ - bugfix Outlook attachments with special characters in the name
+- Bumped email-rfc2822-validator from 2.1.3 to 2.2.0
+ - bugfix properly handle brackets in email addresses when allowed
+- Bumped log4j-core from 2.6.1 to 2.13.2
+
+
+v6.0.2 - v6.0.5 (21-Januari-2020 - 13-June-2020)
+
+- #270: Bug: CLI module missing Jetbrains @Nullable annotation dependency needed in runtime
+- #262: Bug: Executor settings passed to the builder are ignored
+- #249: Bug: MimeMessageParser doesn't handle multiple attachments with the same name correctly
+- #245: Bug: JDK9+ Incorrect JPMS Automatic-Module-Name
+- #246: Bug: Sending async emails with and without the Batch module cause lingering threads
+
+
+v6.0.0-rc1 - v6.0.1 (18-December-2019 - 24-January-2020)
+
+After almost two years of development the next major release 6.0.0 is finally here! And what a doozy it is, with the following major new features:
+
+The core library is now even smaller compared to the 5.x.x series going from 183kb to 134kb!
+
+- [CLI support!!](https://github.com/bbottema/simple-java-mail/issues/156),
+- major performance improvement with [advanced batch processing](https://github.com/bbottema/simple-java-mail/issues/214) including support for mail server clusters.
+- You can now replace the final sending of emails with [your own logic](https://github.com/bbottema/simple-java-mail/issues/121), using a 3rd party service of your choice.
+- 6.0.0 also includes support for [S/MIME signed and encrypted emails](https://github.com/bbottema/simple-java-mail/issues/187)!
+- All 3rd party dependencies have been made optional by splitting up Simple Java Mail into easy to use [modules](https://github.com/bbottema/simple-java-mail/issues/183).
+- You can now monitor and [handle async processing](https://github.com/bbottema/simple-java-mail/issues/148) using Futures.
+- MimeMessage results are now [structurally matched](https://github.com/bbottema/simple-java-mail/issues/144) to specific needs (only using alternative/mixed etc. when needed)
+
+Here's the complete list of changes:
+
+#### New features and enhancements ####
+- #183: To manage all the optional dependencies and related code, Simple Java Mail should be split up into modules
+- #156: Add CLI support
+- #214: Support more advanced batch processing use cases
+- #187: Simple Java Mail should have optional support for signed S/MIME attachments
+- #121: Introduce interfaces for validation and sending, so these steps can be customized
+- #144: Simple Java Mail should tailor the MimeMessage structure to specific needs
+- #138: Add support for Calendar events (iCalendar vEvent)
+- #235: Be able to fix the sent date for a new email
+- #232: Improve encoding of attachment file names
+- #222: Add config property support for trusting hosts and verifying server identity
+- #212: Authenticated proxy server started even if already running, raising exception
+- #207: Implement more comprehensive ThreadPoolExecutor and expose config options
+- #211: SpringSupport should expose the intermediate builder for customization
+- #193: Simple Java Mail should use default server ports when not provided by the user
+
+#### Bugs solved ####
+- #242: Renamed log4j2.xml to log4j2_example.xml so it doesn't clash with project config
+- #241: EmailConverter.outlookMsgToEmail duplicates recipients
+- #239: List of Recipients not ordered as added (insertion order not maintained)
+- #236: Message ID should be mapped from Outlook messages as well
+- #210: Connection/session timeout properties not set when not sending in batch mode
+- #201: When parsing Outlook message, FROM address should default to a dummy address when missing
+- #200: When parsing Outlook message, attachment name doesn't fallback on filename if proper name is empty
+- #161: When reading (chinese) .msg files, HTML converted from RTF is completely garbled (encoding issue)
+- #159: Can not parse email with blank email address headers
+- #139: Multiple Bodyparts of same Content-Type not supported for text/html & text/plain within Multipart/mixed or Multipart/alternative
+- #151: Attachment's file extension overwritten by resource's invalid extension
+
+#### Maintenance updates ####
+- #165: Move away from Findbugs (unofficial JSR-305) annotations
+- #164: The DKIM dependency has been updated to benefit from the newer Apache V2 license
+- #164: The DKIM dependency has been updated to benefit from the newer Apache V2 license
+- #184: Update JavaMail dependency to 1.6.2, adding support for UTF-8 charset
+- #186: Update JavaMail dependency to 1.6.2, adding support for authenticated HTTP web proxy
+- #146: Added OSGI manifest and switched to spotbugs
+
+#### Included changes from the outlook-message-parser dependency ####
+- v6.0.1, v1.7.3: #27: When from name/address are not available (unsent emails), these fields are filled with binary garbage
+- v6.0.1, v1.7.2: #26: To email address is not handled properly when name is omitted
+- v6.0.0, v1.7.1: #25: NPE on ClientSubmitTime when original message has not been sent yet
+- v6.0.0, v1.7.1: #23: Bug: __nameid_ directory should not be parsed (and causing invalid HTML body)
+- v6.0.0, v1.7.0: #18: Upgrade Apache POI 3.9 -> 4.x (but managed back for Simple Java Mail due to incompatibility with Java 7)
+- v6.0.0, v1.6.0: #21: Multiple TO recipients are not handles properly
+- v6.0.0, v1.5.0: #20: CC and BCC recipients are not parsed properly
+- v6.0.0, v1.5.0: #19: Use real Outlook ContentId Attribute to resolve CID Attachments
+- v6.0.0, v1.4.1: #17: Fixed encoding error for UTF-8's Windows legacy name (cp)65001
+- v6.0.0, v1.4.0: #9: Replaced the RFC to HTML converter with a brand new RFC-compliant convert! (thanks to @fadeyev!)
+- v6.0.0, v1.3.0: #14: Dependency problem with Java9+, missing Jakarta Activation Framework
+- v6.0.0, v1.3.0: #13: HTML start tags with extra space not handled correctly
+- v6.0.0, v1.3.0: #11: SimpleRTF2HTMLConverter inserts too many tags
+- v6.0.0, v1.3.0: #10: Embedded images with DOS-like names are classified as attachments
+- v6.0.0, v1.3.0: #9: SimpleRTF2HTMLConverter removes some valid tags during conversion
+- v6.0.0, v1.2.1: Ignore non S/MIME related content types when extracting S/MIME metadata
+- v6.0.0, v1.2.1: Added toString and equals methods to the S/MIME data classes
+- v6.0.0, v1.1.21: Upgraded mediatype recognition based on file extension for incomplete attachments
+- v6.0.0, v1.1.21: Added / improved support for public S/MIME meta data
+- v6.0.0, v1.1.20: #7: Fix missing S/MIME header details that are needed to determine the type of S/MIME application
+- v6.0.0, v1.1.19: Log rtf compression error, but otherwise ignore it and keep going and extract what we can.
+
+**A big shout out to @dnault ([runtime javadoc](https://github.com/dnault/therapi-runtime-javadoc)), @remkop ([picocli](https://picocli.info/)) and @markenwerk
+([S/MIME](https://github.com/markenwerk/java-utils-mail-smime) and [DKIM](https://github.com/markenwerk/java-utils-mail-dkim)) for working with me to make the
+libraries work with JDK7+ and do what Simple Java Mail needed! Finally a great many thanks the numerous contributors on Simple Java Mail as well as
+[outlook-message-parser](https://github.com/bbottema/outlook-message-parser) - this release would not be there without you.**
+
+
+v5.5.0 - [v5.5.1](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C5.5.1%7Cjar)
+
+- v5.5.1 (20-October-2019): #230: Bugfix: Missing address value in address headers (ie. Return-Path) not handled properly, resulting in Exception
+- v5.5.0 (15-October-2019): #229: Bugfix: Timeouts not working for synchronous sendMail calls.
+
+ If you had connection properties configured for non-async send jobs, only now they will actually start to take effect.
+
+
+v5.4.0 (28-August-2019)
+
+- #221: API bugfix: server identity verification should not be tied to host trusting
+- #226: Bug fix: Attachments with spaces in name are not handled properly
+- #218: Enhancement: make Email serializable
+- #227: Enhancement: Make parsing recipients from EML file more lenient
+- #225: Enhancement: Clarify dependency on Jakarta Activation: DataSources no longer work on Java 9+
+
+
+v5.3.0 (16-August-2019)
+
+- #215: Enhancement: Make DKIM header canonicalization RELAXED instead of SIMPLE
+
+Note this release should have no impact, but nonetheless is a minor update so you can determine for yourself if this update would cause issues.
+The release changes DKIM header canonicalization from SIMPLE to RELAXED.
+
+
+v5.2.1 (16-August-2019)
+
+- #219: Bug: MimeMessageParser rejects attachments with duplicate names
+
+
+v5.2.0 (7-July-2019)
+
+- #213: Update from javax.mail:1.6.0 to jakarta.mail:1.6.3
+
+Note that dependencies that switched as well have been updated as part of this change. This includes the optional DKIM library and the email validation library:
+- net.markenwerk:utils-mail-dkim (1.1.10 -> 1.2.0)
+- com.github.bbottema:emailaddress-rfc2822 (1.1.2 -> 2.1.3)
+
+
+v5.1.1 - v5.1.7
+
+- v5.1.7 (22-May-2019): #171 Header validation tripping on known safe emails due to References header
+- v5.1.6 (27-April-2019): #204A Concurrent exception when an async process starts when the previous connection pool didn't shutdown in time
+- v5.1.6 (27-April-2019): #204B Exceptions in threads are now caught and logged and don't bubble up anymore
+- v5.1.5 (24-April-2019): #202 Fixed ConcurrentModificationException when moving invalid embedded images as regular attachments
+- v5.1.4 (5-April-2019): Fixed missing mimetype for attachments when parsing Outlook messages where mimeTag was not included
+- v5.1.3 (15-Januari-2019): Updated to newer rfc-validator version, which fixed a regression bug in that library
+- v5.1.2 (9-Januari-2019): #189 Bugfix for missing timeout config for .testConnection() function
+- v5.1.1 (22-December-2018): #190 Fix for transitive dependency clash because of emailaddress-rfc2822 library
+
+
+v5.1.0 (21-November-2018)
+
+- #179 You can now test the connection to the SMTP server
+
+
+v5.0.1 - v5.0.8
+
+- v5.0.8 (27-Oktober-2018): #178 Fix the annoying vulnerability Github report about spring-core
+- v5.0.7 (27-Oktober-2018): #175 Attachment names are not always parsed properly from MimeMessage
+- v5.0.6 (3-Oktober-2018): #167 Email addresses validated despite cleared validation validation criteria
+- v5.0.5 (3-Oktober-2018): #137 When replying to an email with HTML, the result body is empty
+- v5.0.4 (22-September-2018): #168 Properties aquired through ConfigLoader should be typed explicitly and converted if necessary
+- v5.0.3 (11-April-2018): #136 ServerConfig class should be public API
+- v5.0.2 (7-April-2018): #135 trustingAllHosts should be public on the Builder API
+- v5.0.2 (7-April-2018): #131 NamedDataSource should implement EncodingAware
+- v5.0.1 (10-March-2018): #130 java.lang.ClassNotFoundException: net.markenwerk.utils.mail.dkim.DkimMessage. Solves the issue of missing optional class DKIM even when not used
+
+
+v5.0.0 (14-Februari-2018)
+
+Also see the migrating notes (https://www.simplejavamail.org/migration-notes-5.0.0.html#navigation)
+
+New features
+- #116 You can now test the connection to the SMTP server
+- #115 Create mailers with a very robust MailerBuilder API, able to ignore defaults as well
+- #114 Create emails with a very robust EmailBuilder API, able to ignore defaults as well. Now includes support for InternetAddress. Also copy
+emails.
+- #107 You can now easily forward or reply to emails!
+
+Maintenance updates
+
+Complete JavaDoc overhaul. Navigating the JavaDoc should be much more consistent now (builder API being the single *public* source of truth).
+
+- #122 The email-rfc2822-validator library has been made a proper Maven dependency (not packaged along anymore)
+- #120 The DKIM library has been made an optional proper Maven dependency (not packaged along anymore)
+- #119 Switched optional Spring dependency version to property and now testing with 4.3.11.RELEASE
+- #113 Updated the underlying JavaMail to 1.6.0
+
+Security updates
+- #111 Protocol properties for SMTPS are now applied properly
+- #105 SMTP tries to upgrade to TLS while SMTP_TLS now enforces it and for both SMTP_TLS and SMTPS, mail.smtp.ssl.checkserveridentity is set
+to true (https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html)
+
+Bugfixes
+- #110 Trusted hosts should be space-delimited
+- #109 Email headers should be allowed to be empty (now conversion errors can occur as well)
+- #103 Converting to MimeMessage results in an invalid Content-Disposition for attachments
+
+
+v4.4.5 (2-September-2017)
+
+- #101 API backwards compatibility update, reinstate old addRecipient API as deprecated (sorry for removing it abruptly)
+
+
+v4.4.4 (21-August-2017)
+
+API usability release. *This relase streamlined the recipient setters, breaking backwards compatibility (but straightforward to fix)*
+
+- #95 Feature: Add support native API for setting Return-Receipt-To header
+- #93 Feature: Add support native API for setting Disposition-Notification-To header
+- #91 *Feature: Add support for parsing preformatted email addresses that include both name and address*
+- #94 Bugfix: A single EmailBuilder would build emails that all share the same collections for recipients, attachments and embedded images
+- #98 Bugfix: Subject and body content should be optional
+
+v4.3.0 (12-August-2017)
+
+Security and timeout release.
+
+This version safeguards against SMTP injection attack from external values entering the library through *Email* instance. Also, this release
+introduces default/configurable timeouts for connecting, reading and writing when sending an email.
+
+- #89 Support multiple delimited recipient addresses sharing the same TO/CC/BCC name
+- #88 Safeguard subject property (and others) against SMTP CRLF injection attacks
+- #85 Apply configurable timeouts when sending emails
+- #83 Parse INLINE attachments without ID as regular attachments when converting (mostly applicable to Apple emails)
+
+
+v4.2.3 (21-May-2017)
+
+- #79: Enhancement: define custom message ID on the Email object
+- #74: v4.2.3-java-6-release: A java6 version with limited capabilities:
+ I've released a customised java6 release with a customised outlook-message-parser 1.1.16-java6-release. This is the last java6 release I will do,
+ as it is simply too much manual labor to create a limited second edition.
+
+ For this edition, I've removed the JDK7 Phaser completely which has the following consequences:
+
+ - If authenticated proxy is used, the bridging proxy server will not be shut down automatically (and might not run the second time)
+ - If mails are sent in async mode, the connection pool will not be shut down anymore by itself
+
+ This means your server/application might not stop properly due to lingering processes. To be completely safe, only send emails in sync mode (used by default) and don't use authenticated proxy config.
+
+
+v4.2.2 (10-May-2017)
+
+- #73: Patch: fix for sending emails in async mode, which makes sure the connection pool is always closed when the last *known* email has been sent. Without
+this fix, the connection pool keeps any parent process running (main thread or Tomcat for example) until a hard kill.
+
+
+v4.2.1 (12-Feb-2017)
+
+Patch: streamlined convenience methods for adding recipients.
+
+
+v4.2.0 (12-Feb-2017)
+
+Major feature: Using the EmailConverter you can now convert between Outlook .msg, EML, MimeMessage and Email!
+
+- #66: Feature: convert email to EML
+- #65: Feature: read outlook messages from .msg file
+- #64: Feature: Added support for logging-only mode that skips the actual sending of emails
+- #63: Feature: Already including in previous patch update: Spring support (read properties from Spring context)
+- #69: Enhancement: Expanded EmailBuilder API to inlude more options for setting (multiple) recipients
+- #70: Enhancement: Most public API now have defensive null-checks for required fields (Fail Fast support)
+- #68: Bugfix: Name should be required for embedded images (added safeguards)
+- #67: Bugfix: Error when name was omitted for attachment
+- minor: added methods on AttachmentResource that reads back the content as (encoded) String
+- other: internal testing is now done using Wiser SMTP test server for testing live sending emails
+
+Note: Starting this release, there will always be a Java6 compatible release as well versioned: "x.y.z-java6-release"
+
+
+v4.1.3 (28-Jan-2017)
+
+- #61: Feature: Add support for providing your own Properties object
+- #63: Feature: Spring support (read properties from Spring context)
+- #58: Bugfix: Add support for non-English attachment and embedded image names
+- #62: Bugfix: Empty properties loaded from config should be considered null
+
+NOTE: ConfigLoader moved from /internal/util to /util
+
+
+v4.1.2 (07-Nov-2016)
+
+- #52: bug fix for windows / linux disparity when checking socket status
+- #56: bug fix for IOException when signing dkim with a File reference
+
+
+v4.1.1 (30-Jul-2016)
+
+- #50: bug fix for manual naming datasources
+
+
+v4.1.0 (22-Jul-2016)
+
+- #48: Added programmatic support trusting hosts for SSL connections
+- #47: Honor given names, deduce extension from datasource name, and more robust support for parsing mimemessages
+
+
+v4.0.0 (05-Jul-2016)
+
+- #41: added support for fast parallel batch processing
+- #42: **added support for config files**
+- #43: removed logging implementation dependencies from distribution and documented various sample configs
+- #39: simplified and renamed packages to reflect the domain name of the new website: simplejavamail.org
+- #38: added support for anonymous proxy
+- #38: **added support for authenticated proxy**
+
+NOTE: All packages have been renamed to "org.simplejavamail.(..)"
+NOTE: Switched to Java 7
+
+
+v3.1.1 (11-May-2016)
+
+Major feature: DKIM support!
+
+- #36: Added proper toString and equals methods for the Email classes
+- #33: Added support for DKIM domain key signing
+
+NOTE: this is the last release still using Java 6. Next release will be using Java 7.
+/edit: starting with 4.2.0 every release will now have a "x.y.z-java6-release" release as well
+
+
+v3.0.2 (07-May-2016)
+
+- #35: added proper .equals() and .toString() methods
+- #34: Fixed bug when disposition is missing (assume it is an attachment)
+- #00: added findbugs support internally
+
+
+v3.0.1 (29-Feb-2016)
+
+- #31: Fixed EmailAddressCriteria.DEFAULT and clarified Javadoc
+
+
+v3.0.0 (26-Feb-2016)
+
+- #30: Improved the demonstration class to include attachments and embedded images
+- #29: The package has been restructured for future maintenance, breaking backwards compatibility
+- #28: Re-added improved email validation facility
+- #22: Added conversion to and from MimeMessage. You can now consume and produce MimeMessage objects with simple-java-mail
+
+
+v2.5.1 (19-Jan-2016)
+
+- #25: Added finally clausule that will always close socket properly in case of an exception
+
+
+v2.5 (19-Jan-2016)
+
+- #24: Updated dependencies SLF4J to 1.7.13 and switched to the updated javax mail package com.sun.mail:javax.mail 1.5.5
+
+
+v2.4 (12-Aug-2015)
+
+- #21: Builder API uses CC and BCC recipient types incorrectly
+
+
+v2.3 (21-Jul-2015)
+
+- #19: supporting custom Session Properties now and emergency access to internal Session object.
+
+
+v2.2 (09-May-2015)
+
+- #3: turned off email regex validation by default, with the option to turn it back on
+- #7: fixed NullPointerException when using your own Session instance
+- #10: properly UTF-8 encode recipient addresses
+- #14: switched to SLF4J, so you can easily use your own selected logging framework
+- #17: Added fluent interface for building emails
+
+
+v2.1 (09-Aug-2012)
+
+- fixed character encoding for reply-to, from, to, body text and headers (to UTF-8)
+- fixed bug where Recipient was not public resulting in uncompilable code when calling email.getRecipients()
+
+
+v2.0 (20-Aug-2011)
+
+- added support for open headers (such as X-Priority)
+
+
+v1.9.1 (08-Aug-2011)
+
+- updated for Maven support (library hasn't changed, but 1.9 was released incorrectly to Maven Central)
+
+
+v1.9 (6-Aug-2011)
+
+- added support for JavaMail's reply-to address
+- made port optional as to support port defaulting based on protocol
+- added transport strategy default in the createSession method
+- tightened up thrown exceptions (MailException instead of RuntimeException)
+- added and fixed JavaDoc
+
+
+v1.8
+
+Added support for TLS (tested with gmail)
+
+
+v1.7 (22-Mar-2011)
+
+Added support for SSL! (tested with gmail)
+
+- improved argument validation when creating a Mailer without preconfigured Session instance
+
+know possible issue: SSL self-signed certificates might not work (yet). Please let me know by e-mail or create a new issue
+
+
+v1.6
+
+Completed migration to Java Simple Mail project.
+
+- removed all Vesijama references
+- updated TestMail demonstration class for clarification
+- updated readme.txt for test run instructions
+- included log4j.properties
+
+
+v1.4 (15-Jan-2011)
+
+
+vX.X (26-Apr-2009)
+
+- Initial upload to Google Code.
diff --git a/RELEASE_HISTORY.md b/RELEASE_HISTORY.md
new file mode 100644
index 000000000..98ad55305
--- /dev/null
+++ b/RELEASE_HISTORY.md
@@ -0,0 +1,890 @@
+# Release History
+
+Full Simple Java Mail release history. [README.md](README.md#latest-progress) also mirrors recent progress for quick scanning.
+
+## [v9.2.0](https://github.com/bbottema/simple-java-mail/releases/tag/9.2.0) - [Maven Central](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/9.2.0/)
+
+- [#692](https://github.com/bbottema/simple-java-mail/issues/692): added refresh-aware OAuth2 access-token providers for long-lived Mailers.
+- [#691](https://github.com/bbottema/simple-java-mail/issues/691): routed all asynchronous send failures through the returned `CompletableFuture`.
+- [#690](https://github.com/bbottema/simple-java-mail/issues/690): made serialized emails preserve send-ready content and fail clearly when pre-9.2 streams lack resource data.
+- [#689](https://github.com/bbottema/simple-java-mail/issues/689): moved governance opt-outs after email starters and removed the faulty pre-start methods.
+- [#685](https://github.com/bbottema/simple-java-mail/issues/685): corrected extra Jakarta Mail property precedence to system properties, environment variables, then property files.
+- [#684](https://github.com/bbottema/simple-java-mail/issues/684): corrected the S/MIME configuration example to use a valid message-content cipher.
+- [#683](https://github.com/bbottema/simple-java-mail/issues/683): moved CLI-only recipient parsing out of `EmailPopulatingBuilder` without changing CLI behavior.
+- [#680](https://github.com/bbottema/simple-java-mail/issues/680): made S/MIME signature status represent only verification that actually occurred and preserve failed combined results.
+- [#679](https://github.com/bbottema/simple-java-mail/issues/679): rejected DKIM configurations that exclude the mandatory `From` header.
+- [`55586ee6`](https://github.com/bbottema/simple-java-mail/commit/55586ee6f0836d0277f4a85beb267f0089d2ed22): added explicit `file:` and `base64:` DKIM private-key sources while preserving unprefixed values.
+- [#678](https://github.com/bbottema/simple-java-mail/issues/678): contained embedded-image auto-resolution within configured file, classpath, and URL bases.
+- [#677](https://github.com/bbottema/simple-java-mail/issues/677): replaced trust-all TLS defaults with the JVM trust store while retaining hostname verification.
+- [#676](https://github.com/bbottema/simple-java-mail/issues/676): bound the authenticated SOCKS bridge to JVM loopback instead of wildcard interfaces.
+
+See the [9.2 migration guide](https://www.simplejavamail.org/migration-notes-9.2.0.html) for changes requiring source or configuration updates.
+
+[v9.1.0](https://github.com/bbottema/simple-java-mail/releases/tag/9.1.0) - [v9.1.8](https://github.com/bbottema/simple-java-mail/releases/tag/9.1.8) - [Maven Central](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/9.1.8/)
+
+> **Spring-module notice:** Versions 9.0.0 through 9.1.5 package test application settings that can override an application's YAML configuration and prevent SMTP connections. If you use `spring-module`, upgrade to 9.1.6 or later.
+
+> **CLI recipient notice:** Versions 9.0.0 through 9.1.6 cannot combine TO, CC, and BCC recipients in one CLI command. Repeated `--email:withRecipients` options are merged and rejected. Upgrade to 9.1.7 or later.
+
+- **v9.1.8:** [#686](https://github.com/bbottema/simple-java-mail/issues/686): **Custom-session proxy routing:** `usingSession(session).withProxy(...)` now updates the intended `mail.smtp.socks.*` route while leaving the rest of the caller's session configuration untouched.
+- **v9.1.8:** [#687](https://github.com/bbottema/simple-java-mail/issues/687): **SMTPS through SOCKS:** removed the obsolete restriction that rejected SOCKS proxying for implicit-TLS SMTP connections.
+- **v9.1.8:** [#696](https://github.com/bbottema/simple-java-mail/issues/696), [#697](https://github.com/bbottema/simple-java-mail/issues/697): **Connection-pool resets:** the max-size and claim-timeout reset methods now restore their own settings without changing the core size or connection expiry.
+- **v9.1.7:** [#682](https://github.com/bbottema/simple-java-mail/issues/682): **Dedicated CLI recipient options:** restore independent `--email:to`, `--email:cc`, and `--email:bcc` options so one command can combine all three recipient types.
+- **v9.1.6:** [#681](https://github.com/bbottema/simple-java-mail/issues/681): **Spring configuration isolation:** stop packaging the Spring test `application.properties` in `spring-module`. Sample local-bind, SMTP client-hostname, transfer-encoding, and other test values can no longer override an application's YAML configuration or break SMTP connections.
+- **v9.1.5:** [#674](https://github.com/bbottema/simple-java-mail/issues/674), [#675](https://github.com/bbottema/simple-java-mail/issues/675): **SOCKS5 domain framing:** place the port after the UTF-8-encoded domain bytes and decode domain replies after their length octet, fixing internationalized-host requests and reply diagnostics.
+- **v9.1.4:** [#669](https://github.com/bbottema/simple-java-mail/issues/669), [#670](https://github.com/bbottema/simple-java-mail/issues/670): **EML file stream ownership:** close streams created internally by the `File`-based EML conversion overloads after synchronous parsing, while leaving caller-provided `InputStream` ownership unchanged.
+- **v9.1.4:** **Java 8-compatible dependency and release-tool maintenance:** updated SpotBugs annotations to 4.10.3 ([#671](https://github.com/bbottema/simple-java-mail/pull/671)), the Central Publishing Maven Plugin to 0.11.0 ([#672](https://github.com/bbottema/simple-java-mail/pull/672)), and Objenesis to 3.6 ([#673](https://github.com/bbottema/simple-java-mail/pull/673)).
+- **v9.1.3:** [#668](https://github.com/bbottema/simple-java-mail/issues/668): **New Outlook inline images:** updated `outlook-message-parser` to 1.16.2 so native-HTML-only `.msg` files match inline `cid:` images correctly and trailing NUL terminators no longer leak into attachment metadata.
+- **v9.1.2:** **Dependency and Java 8-compatible build-tool maintenance:** updated JMail to 2.2.0 ([#663](https://github.com/bbottema/simple-java-mail/pull/663)), Zip4j to 2.11.6 ([#666](https://github.com/bbottema/simple-java-mail/pull/666)), Exec Maven Plugin to 3.6.3 ([#664](https://github.com/bbottema/simple-java-mail/pull/664)), Maven Enforcer Plugin to 3.6.3 ([#665](https://github.com/bbottema/simple-java-mail/pull/665)), and Maven JAR Plugin to 3.5.1 ([#667](https://github.com/bbottema/simple-java-mail/pull/667)).
+- **v9.1.1:** **Java 8 build-tool maintenance** ([#662](https://github.com/bbottema/simple-java-mail/pull/662)): updated annotations and Maven compiler, JAR, OSGi bundle, and Karaf tooling to Java 8-compatible versions, with Dependabot guards against newer-Java-only upgrade lines.
+- **v9.1.0:** [#653](https://github.com/bbottema/simple-java-mail/issues/653): a configurable SMTP client hostname for the `EHLO` / `HELO` command.
+- **v9.1.0:** [#654](https://github.com/bbottema/simple-java-mail/issues/654): SMTP submission receipts for reading the server acceptance response after a send.
+- **v9.1.0:** No breaking changes; existing `sendMail(...)` behavior is unchanged.
+
+
+[v9.0.0](https://github.com/bbottema/simple-java-mail/releases/tag/9.0.0) - [v9.0.4](https://github.com/bbottema/simple-java-mail/releases/tag/9.0.4) - [Maven Central](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/9.0.4/)
+
+> **Spring-module notice:** Versions 9.0.0 through 9.1.5 package test application settings that can override an application's YAML configuration and prevent SMTP connections. If you use `spring-module`, upgrade to 9.1.6 or later.
+
+#### The Short Version ####
+
+**Simple Java Mail has been going strong-ish for about 20 years**, and **9.0.0** rolls *roughly two years of backlog* into a new major release.
+
+```
+simple-java-mail
+└── outlook-message-parser
+ └── rtf-to-html
+└── java-utils-mail-dkim
+└── java-utils-mail-smime
+└── smtp-connection-pool
+ └── clustered-object-pool
+ └── generic-object-pool
+└── java-socks-proxy-server
+```
+
+Across Simple Java Mail and the supporting libraries that keep the stack moving, **more than 100 GitHub issues and PRs** were reviewed, fixed, merged, or closed while keeping the project **Java 8-compatible**.
+
+**Major features:** the dedicated recipient builder API, per-recipient S/MIME certificates, first-class Delivery Status Notification support, pre-encoded attachment and embedded-image sending, mailer-level DKIM defaults, and simple batch/open-connection sending without the batch module.
+**Enhancements:** broader Outlook conversion metadata, MIME resource handling, content-transfer encoding control, debug routing, local SMTP bind configuration, batch cluster configuration, and Java module support.
+
+#### Migration Note ####
+
+**This is a breaking major release.** The old recipient-addition method jungle has been cleaned up in favor of the **recipient builder API**, so code that relied on the removed recipient overloads needs to migrate. Start with the [9.0 migration notes](https://www.simplejavamail.org/migration-notes-9.0.0.html) before upgrading.
+
+#### Major Features ####
+
+- **v9.0.0:** [#613](https://github.com/bbottema/simple-java-mail/issues/613): **Recipient builder API:** added dedicated builders for constructing single recipients and recipient collections.
+- **v9.0.0:** [#297](https://github.com/bbottema/simple-java-mail/issues/297): **Per-recipient S/MIME certificates:** enabled encrypted mail for multiple recipients with different certificates.
+- **v9.0.0:** [#574](https://github.com/bbottema/simple-java-mail/issues/574): **Delivery Status Notification (DSN):** added first-class DSN configuration.
+- **v9.0.0:** [#573](https://github.com/bbottema/simple-java-mail/issues/573): **Pre-encoded resources:** added pre-encoded attachment and embedded-image APIs.
+- **v9.0.0:** [#196](https://github.com/bbottema/simple-java-mail/issues/196): **Mailer-level DKIM defaults:** added default DKIM signing configuration so DKIM can be configured once per `Mailer`.
+- **v9.0.0:** [#569](https://github.com/bbottema/simple-java-mail/issues/569): **Simple batch and open-connection sending:** added `sendMailsInSimpleBatch(...)` for sequential batch work without the batch module and `withOpenConnection(...)` for callback-scoped reuse of a single SMTP connection.
+
+#### Enhancements ####
+
+- **v9.0.2:** [#645](https://github.com/bbottema/simple-java-mail/issues/645): **Outlook last-modifier metadata:** exposed `PR_LAST_MODIFIER_NAME` / `0x3FFA` as `OutlookMessageData#getLastModifierName()` without treating it as sender identity.
+- **v9.0.0:** [#614](https://github.com/bbottema/simple-java-mail/issues/614): **Outlook conversion metadata:** added explicit result APIs for inspecting source `.msg` headers and metadata without copying structural headers into converted emails, resolving [#609](https://github.com/bbottema/simple-java-mail/issues/609).
+- **v9.0.0:** [#605](https://github.com/bbottema/simple-java-mail/issues/605): **Per-body content-transfer encoding:** added `Content-Transfer-Encoding` configuration for plain text, HTML, and calendar content.
+- **v9.0.0:** [#566](https://github.com/bbottema/simple-java-mail/issues/566), [#597](https://github.com/bbottema/simple-java-mail/issues/597), [#602](https://github.com/bbottema/simple-java-mail/issues/602), [#607](https://github.com/bbottema/simple-java-mail/issues/607): **MIME resource `Content-ID` handling:** improved explicit IDs, parsed embedded images, and generated fallback IDs.
+- **v9.0.0:** [#589](https://github.com/bbottema/simple-java-mail/issues/589): **Jakarta Mail debug output:** added configurable debug output routing.
+- **v9.0.0:** [#568](https://github.com/bbottema/simple-java-mail/issues/568): **Local SMTP bind address:** added configuration for multi-IP SMTP hosts.
+- **v9.0.0:** [#565](https://github.com/bbottema/simple-java-mail/issues/565), [#618](https://github.com/bbottema/simple-java-mail/issues/618): **Batch cluster configuration:** fixed Java API cluster configuration and added property-defined cluster configurations for property-file and Spring-configured clustered sending.
+- **v9.0.0:** [#572](https://github.com/bbottema/simple-java-mail/issues/572), [#571](https://github.com/bbottema/simple-java-mail/issues/571): **S/MIME conversion leniency:** tolerate unsupported S/MIME payloads during Outlook conversion and preserve parsed email content when signature verification fails.
+- **v9.0.0:** [#606](https://github.com/bbottema/simple-java-mail/issues/606): **MIME type sanitizing:** sanitize malformed resource MIME types before generating attachment and embedded-image headers.
+- **v9.0.0:** [#541](https://github.com/bbottema/simple-java-mail/issues/541): **Resource headers:** removed the non-standard `filename` parameter from resource `Content-Type` headers; filenames remain available through `Content-Disposition`.
+- **v9.0.0:** [#265](https://github.com/bbottema/simple-java-mail/issues/265), [#237](https://github.com/bbottema/simple-java-mail/issues/237): **Java module support:** added Java 9 module descriptors to the core and facade jars so modular applications can require `org.simplejavamail` directly.
+
+#### Fixes and Compatibility ####
+
+- **v9.0.4:** [#652](https://github.com/bbottema/simple-java-mail/issues/652): **RFC 2047 address validation:** reject encoded-word syntax inside address specs during validation while keeping encoded display names valid.
+- **v9.0.3:** [#651](https://github.com/bbottema/simple-java-mail/issues/651): **Outlook plain-text RTF rendering:** preserved line breaks without exposing browser-default `
` styling in converted HTML.
+- **v9.0.0:** [#615](https://github.com/bbottema/simple-java-mail/issues/615): **Async test connections:** fixed `MailerGenericBuilder.async()` so no-arg `testConnection()` uses the configured async default.
+- **v9.0.0:** [#611](https://github.com/bbottema/simple-java-mail/issues/611): **SMTPS custom SSL socket factories:** fixed custom SSL socket factory configuration for SMTPS mailers.
+- **v9.0.0:** [#535](https://github.com/bbottema/simple-java-mail/issues/535): **Async failure reporting:** let async send and connection-test failures surface through the returned `CompletableFuture` without duplicate framework error logs.
+- **v9.0.0:** [#583](https://github.com/bbottema/simple-java-mail/issues/583): **Java 25 CLI startup:** fixed CLI startup on Java 25.
+- **v9.0.0:** [#616](https://github.com/bbottema/simple-java-mail/issues/616): **CLI optional-argument detection:** removed the runtime JetBrains annotation fork from CLI optional-argument detection.
+- **v9.0.0:** **Standalone CLI command cleanup:** `send`, `connect`, and `validate` now wait for command work and close mailer resources, preventing batch-module resources from keeping the process alive.
+
+#### Dependency and Supporting-Library Updates ####
+
+- **v9.0.3:** **Angus runtime alignment:** bumped Angus Mail to 2.0.5 and added Angus Activation 2.0.3.
+- **v9.0.3:** **Logging dependencies:** bumped Log4j from 2.25.4 to 2.26.1.
+- **v9.0.2:** **Jakarta API alignment:** bumped Jakarta Mail API to 2.1.5 and Jakarta Activation API to 2.1.4.
+- **v9.0.0:** **Core dependency maintenance:** bumped JMail to 2.1.0 ([#634](https://github.com/bbottema/simple-java-mail/pull/634)), commons-io to 2.22.0 ([#579](https://github.com/bbottema/simple-java-mail/pull/579), [#627](https://github.com/bbottema/simple-java-mail/pull/627)), Kryo to 5.6.2 ([#586](https://github.com/bbottema/simple-java-mail/pull/586)), Zip4j to 2.11.5 ([#587](https://github.com/bbottema/simple-java-mail/pull/587)), SubEthaSMTP to 7.2.2 ([#593](https://github.com/bbottema/simple-java-mail/pull/593), [#632](https://github.com/bbottema/simple-java-mail/pull/632)), Angus Mail to 2.0.4 ([#604](https://github.com/bbottema/simple-java-mail/pull/604)), Objenesis to 3.5 ([#580](https://github.com/bbottema/simple-java-mail/pull/580), [#635](https://github.com/bbottema/simple-java-mail/pull/635)), Lombok to 1.18.46 ([#636](https://github.com/bbottema/simple-java-mail/pull/636)), AssertJ Core to 3.27.7 ([#622](https://github.com/bbottema/simple-java-mail/pull/622)), and SpotBugs annotations to 4.10.2 ([#629](https://github.com/bbottema/simple-java-mail/pull/629)).
+- **v9.0.0:** **Logging dependencies:** aligned Log4j to 2.25.4 ([#624](https://github.com/bbottema/simple-java-mail/pull/624)) and SLF4J API to 2.0.18 ([#631](https://github.com/bbottema/simple-java-mail/pull/631)), keeping the Log4j bridge on `log4j-slf4j2-impl` for SLF4J 2.x.
+
+##### Supporting Libraries #####
+
+- **v9.0.3:** **`outlook-message-parser` 1.16.1 / `rtf-to-html` 2.0.2:** fixed browser-default `
` styling in Outlook plain-text RTF conversion ([#651](https://github.com/bbottema/simple-java-mail/issues/651)).
+- **v9.0.2:** **`outlook-message-parser` 1.16.0:** added source last-modifier metadata used by `OutlookMessageData#getLastModifierName()`.
+- **v9.0.0:** **`utils-mail-dkim` 3.3.0:** added configurable DNS provider URL support for DKIM domain-key TXT lookups, fixed the published automatic module name, and kept packaged artifacts free of JaCoCo probes.
+- **v9.0.0:** **`clustered-object-pool` 4.0.1** ([#6](https://github.com/bbottema/clustered-object-pool/issues/6)): added cluster-specific Java configuration for pool defaults, claim timeout, and load balancing.
+- **v9.0.0:** **`smtp-connection-pool` 3.0.1** ([#8](https://github.com/simple-java-mail/smtp-connection-pool/issues/8)): pulled in `clustered-object-pool` 4.0.1 so the batch-module fix for [#565](https://github.com/bbottema/simple-java-mail/issues/565) can keep connection-pool defaults per cluster key.
+- **v9.0.0:** **`smtp-connection-pool` 3.0.0:** made clustered SMTP pools generic over their cluster-key type and kept already-unusable connections from surfacing as generic pool error logs during transport close.
+- **v9.0.0:** **`java-socks-proxy-server` 4.2.0:** updated SOCKS live tests to use dynamic proxy ports instead of fixed ports.
+- **v9.0.0:** **`outlook-message-parser` 1.15.0:** improved Outlook `.msg` conversion by preserving nested message attachment metadata, fixing sent-date extraction ([#534](https://github.com/bbottema/simple-java-mail/issues/534)), fixing recipient bucket parsing ([#504](https://github.com/bbottema/simple-java-mail/issues/504)), broadening S/MIME detection, improving RTF-only body conversion ([#576](https://github.com/bbottema/simple-java-mail/issues/576)), and updating Apache POI.
+
+##### Build and Test Maintenance #####
+
+- **v9.0.3:** **Build maintenance:** bumped Maven Source Plugin to 3.4.0, NotNull Instrumenter Maven Plugin to 1.1.1, and Mycila License Maven Plugin to 4.6.
+- **v9.0.2:** **Build maintenance:** bumped Maven Assembly Plugin to 3.8.0 and Nexus Staging Maven Plugin to 1.7.0, and extended Dependabot guards for Java 11-only plugin lines.
+- **v9.0.1:** **Release packaging:** restored generated license headers in published source JARs and enabled publication of the standalone CLI ZIP and TAR classifier artifacts.
+- **v9.0.0:** **Build plugins and test stack:** bumped Maven Surefire Plugin to 3.5.6 ([#592](https://github.com/bbottema/simple-java-mail/pull/592), [#625](https://github.com/bbottema/simple-java-mail/pull/625)), Maven Clean Plugin to 3.5.0 ([#626](https://github.com/bbottema/simple-java-mail/pull/626)), Appassembler Maven Plugin to 2.1.0 ([#581](https://github.com/bbottema/simple-java-mail/pull/581)), Exec Maven Plugin to 3.5.0 ([#582](https://github.com/bbottema/simple-java-mail/pull/582)), Maven Deploy Plugin to 3.1.4 ([#619](https://github.com/bbottema/simple-java-mail/pull/619)), Maven Install Plugin to 3.1.4 ([#639](https://github.com/bbottema/simple-java-mail/pull/639)), Maven Javadoc Plugin to 3.12.0 ([#637](https://github.com/bbottema/simple-java-mail/pull/637)), Maven GPG Plugin to 3.2.8 ([#621](https://github.com/bbottema/simple-java-mail/pull/621)), and JaCoCo Maven Plugin to 0.8.15 ([#638](https://github.com/bbottema/simple-java-mail/pull/638)); aligned JUnit Platform/Jupiter at 1.14.4/5.14.4 while preserving Java 8 compatibility ([#596](https://github.com/bbottema/simple-java-mail/pull/596), [#633](https://github.com/bbottema/simple-java-mail/pull/633)); kept JUnit Pioneer on 1.9.1 because 2.x is Java 11 bytecode ([#630](https://github.com/bbottema/simple-java-mail/pull/630)); added Java 8 Dependabot guards; and replaced live embedded-image URL tests with deterministic local coverage ([#617](https://github.com/bbottema/simple-java-mail/issues/617)).
+
+
+v8.12.0 - [v8.12.6](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/8.12.6/)
+
+- v8.12.6 (18-April-2025): [#595](https://github.com/bbottema/simple-java-mail/issues/595): [bug] Spring configuration - fix support for simplejavamail.extraproperties
+- v8.12.5 (05-March-2025): Bumped PATCH versions of various dependencies ([#553](https://github.com/bbottema/simple-java-mail/issues/553), [#554](https://github.com/bbottema/simple-java-mail/issues/554), [#555](https://github.com/bbottema/simple-java-mail/issues/555), [#562](https://github.com/bbottema/simple-java-mail/issues/562), [#567](https://github.com/bbottema/simple-java-mail/issues/567))
+- v8.12.4 (12-December-2024): [#558](https://github.com/bbottema/simple-java-mail/issues/558): [bug] Mailer.close() exception because it attempts to shutdown batch-module connection pools, even if not available on the classpath
+- v8.12.3 (25-November-2024): [#563](https://github.com/bbottema/simple-java-mail/issues/563): [enhancement] Add getter for Authenticated SOCKS server port
+- v8.12.2 (05-October-2024): [#552](https://github.com/bbottema/simple-java-mail/issues/552): [bug] support iCalendar events with METHOD defined in body instead of Content-Type
+- v8.12.1 (02-October-2024): [#533](https://github.com/bbottema/simple-java-mail/issues/533): [maintenance] Bump com.github.therapi:therapi-runtime-javadoc-scribe from 0.13.0 to 0.15.0
+- v8.12.1 (02-October-2024): [#532](https://github.com/bbottema/simple-java-mail/issues/532): [maintenance] Bump com.sanctionco.jmail:jmail from 1.4.1 to 1.6.3
+- v8.12.1 (02-October-2024): [#531](https://github.com/bbottema/simple-java-mail/issues/531): [maintenance] Bump com.github.bbottema:java-socks-proxy-server from 4.0.0 to 4.1.2
+- v8.12.1 (02-October-2024): [#528](https://github.com/bbottema/simple-java-mail/issues/528): [maintenance] Bump com.github.davidmoten:subethasmtp from 7.0.1 to 7.1.1
+- v8.12.1 (02-October-2024): [#522](https://github.com/bbottema/simple-java-mail/issues/522): [maintenance] Bump jakarta.annotation:jakarta.annotation-api from 1.3.5 to 3.0.0
+- v8.12.0 (26-September-2024): [#550](https://github.com/bbottema/simple-java-mail/issues/550): [bug] Environment variables are not being loaded properly
+- v8.12.0 (26-September-2024): [#538](https://github.com/bbottema/simple-java-mail/issues/538): [bug] System properties are only read if configuration file exists in class path
+- v8.12.0 (26-September-2024): [#546](https://github.com/bbottema/simple-java-mail/issues/546): [Enhancement] Trim whitespace in encoder values for Content-Encoding
+
+
+v8.11.0 - [v8.11.3](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/8.11.3/)
+
+- v8.11.3 (15-September-2024): [#543](https://github.com/bbottema/simple-java-mail/issues/543): [Enhancement] reduce log spam from the batch-module
+- v8.11.2 (08-June-2024): [#530](https://github.com/bbottema/simple-java-mail/pull/530): [Bug] After converting Outlook .msg to EML, bullet lists have duplicate numbering HTML converted from RTF
+- v8.11.1 (28-May-2024): [#529](https://github.com/bbottema/simple-java-mail/pull/529): Bump smtp-connection-pool from 2.3.2 to 2.3.3 which improves performance and fixes a rare ConcurrentModificationException
+- v8.11.1 (28-May-2024): [#527](https://github.com/bbottema/simple-java-mail/pull/527): Bump org.slf4j:slf4j-api from 2.0.9 to 2.0.13
+- v8.11.1 (28-May-2024): [#523](https://github.com/bbottema/simple-java-mail/pull/523): Bump org.jacoco:jacoco-maven-plugin from 0.8.5 to 0.8.12
+- v8.11.0 (25-May-2024): [#526](https://github.com/bbottema/simple-java-mail/pull/526): When reading .msg files the RTF converted to HTML is garbled in some cases where the appropriate charset is not detected properly
+
+NOTE: v8.11.0 contains many (minor) dependency updates to resolve CVE issues, including:
+ - parent POM upgrade: Upgrades test dependencies as well as SLF4J versions
+ - outlook-message-parser 1.13.2 -> 1.14.0
+ - java-reflection 4.0.1 -> 4.0.2
+ - smtp-connection-pool 2.3.1 -> 2.3.2
+ - utils-mail-dkim 3.1.0 -> 3.2.0
+ - jakarta.mail-api 2.1.2 -> 2.1.3
+ - angus-mail 2.0.2 -> 2.0.3
+ - therapi-runtime-javadoc 0.13.0 -> 0.15.0
+ - kryo 5.0.0-RC1 -> 5.6.0
+ - maven-assembly-plugin 3.1.0 -> 3.7.1 (only for the CLI module during build)
+
+
+v8.10.0 - [v8.10.1](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/8.10.1/)
+
+- v8.10.1 (04-May-2024): [#510](https://github.com/bbottema/simple-java-mail/pull/510): Update upstream dependency generic-object-pool, which solves a critical bug when there are exceptions during allocation
+- v8.10.0 (30-April-2024): [#508](https://github.com/bbottema/simple-java-mail/pull/508): [enhancement+bug] Make EmailConverter API more consistent regarding Session parameter, don't use `Session.getDefaultInstance` anymore and fix bug where `emlToEmailBuilder` used `emlToMimeMessage`
+
+
+[v8.9.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C8.9.0%7Cjar) (26-April-2024)
+
+- [#507](https://github.com/bbottema/simple-java-mail/issues/507): [security] Update 3rd party dependencies to get rid of all currently known CVE issues (see issue for details)
+
+
+v8.8.0 - [v8.8.4](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/8.8.4/)
+
+- v8.8.4 (23-April-2024): [#506](https://github.com/bbottema/simple-java-mail/pull/506): [CVE] Upgrade utils-mail-smime dependency to 2.3.2, to resolve CVE issue in bouncycastle
+- v8.8.3 (13-April-2024): [#502](https://github.com/bbottema/simple-java-mail/issues/502): [Bug] Message headers not treated with case insensitivity as per RFC, causing deviating headers to slip through the filters
+- v8.8.2 (05-April-2024): [#495](https://github.com/bbottema/simple-java-mail/issues/495): Add config support for 'verifyingServerIdentity' with SMTP, also: since Angus 1.1.0 (8.6.0) server identity checks are on by default and can be countered by mailerBuilder.verifyingServerIdentity(false)
+- v8.8.2 (05-April-2024): [#501](https://github.com/bbottema/simple-java-mail/issues/501): [dependency] Update outlook-message-parser dependency, which has improved support for X500 addresses
+- v8.8.2 (05-April-2024): [#499 (fix)](https://github.com/bbottema/simple-java-mail/issues/499): [maintenance] Added missing finer-grained DKIM Spring Boot properties
+- v8.8.1 (04-April-2024): [#500](https://github.com/bbottema/simple-java-mail/issues/500): [bug] Fix parsing addresses from headers in EML files, like a Disposition-Notification-To with umlaut
+- v8.8.0 (22-March-2024): [#499](https://github.com/bbottema/simple-java-mail/issues/499): [Enhancement] Expose finer-grained DKIM configuration through the builder api and disable 'l-param' by default)
+
+NOTE: release 8.8.0 changes the default for DKIM signing from 'l-param' true to false. If you rely on this feature, you need to enable it explicitly. Refer to the [DKIM documentation](https://www.simplejavamail.org/security.html#section-sending-dkim) for the update.
+
+
+v8.7.0 - [v8.7.1](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/8.7.1/)
+
+- v8.7.1 (20-March-2024): [#498](https://github.com/bbottema/simple-java-mail/issues/498): Make S/MIME algorithms configurable (signature algorithm for signing, key encapsulation and cipher algorithms for encryption)
+- v8.7.1 (20-March-2024): [#497](https://github.com/bbottema/simple-java-mail/issues/497): Order of attachments is lost when converting a MimeMessage to an Email
+- v8.7.0 (20-March-2024): don't use this version: versioning messed up
+
+NOTE: this breaks the API for S/MIME related builder methods. Refer to the [S/MIME documentation](https://www.simplejavamail.org/security.html#section-sending-smime) for the new API.
+
+
+v8.6.0 - [v8.6.3](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/8.6.3/)
+
+- v8.6.3 (13-February-2024): [#491](https://github.com/bbottema/simple-java-mail/issues/491): [bug] Attachment body parts should separately parse Content-Disposition and ContentID, possible resulting in a downloadable attachment that is also embedded
+- v8.6.2 (27-January-2024): [#493](https://github.com/bbottema/simple-java-mail/issues/493): [bug] don't require smime-module when adding collection of headers (also used when copying email)
+- v8.6.1 (18-January-2024): [#487](https://github.com/bbottema/simple-java-mail/issues/487): Move header filtering from MimeMessageParser to EmailConverter, thereby enabling access to all parsed headers when using MimeMessageParser directly
+- v8.6.1 (18-January-2024): [#489](https://github.com/bbottema/simple-java-mail/issues/489): Finished update to Angus Mail by updating activation dependency
+- v8.6.0 (17-January-2024): [#489](https://github.com/bbottema/simple-java-mail/issues/489): Update to Angus Mail
+
+NOTE: this release switches to Angus Mail which should be a transparent change, but if you encounter any issues, please report them.
+ One known issue is that Angus, since 1.1.0, performs server identity checks by default, which was previously disabled for SMTP. If you encounter issues with this, you can disable it with `mailerBuilder.verifyingServerIdentity(false)` and starting from 8.8.2, this also works with SMTP transport strategy (see [#495](https://github.com/bbottema/simple-java-mail/issues/495)).
+
+
+v8.5.0 - [v8.5.1](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/8.5.1/)
+
+- v8.5.1 (15-December-2023): [#486](https://github.com/bbottema/simple-java-mail/issues/486): [dependency] Handle Outlook's Non-Standard S/MIME Signed Messages
+- v8.5.0 (13-December-2023): [#484](https://github.com/bbottema/simple-java-mail/issues/484): [bug] Addresses passed as string are not always interpreted correctly
+
+
+[v8.4.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C8.4.0%7Cjar) (12-December-2023)
+
+- [#483](https://github.com/bbottema/simple-java-mail/issues/483): Enhancement: add native support for overriding envelope-level receiver(s)
+
+
+v8.3.0 - [v8.3.5](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/8.3.5/)
+
+- v8.3.5 (10-December-2023): [#482](https://github.com/bbottema/simple-java-mail/issues/482) Bug: 'IllegalArgumentException: emailAddressList is required' when parsing mail with incorrect recipients from Outlook message
+- v8.3.4 (08-December-2023): [#481](https://github.com/bbottema/simple-java-mail/issues/481) Enhancement: don't crash on invalid empty embedded images when parsing Outlook messages
+- v8.3.3 (03-December-2023): [#477](https://github.com/bbottema/simple-java-mail/issues/477) Enhancement: Support Exchange proprietary addresses (X.500 DAP)
+- v8.3.2 (26-November-2023): [#480](https://github.com/bbottema/simple-java-mail/issues/480) Bug: Multiple attachments with same name get the same Content-ID, causing them to refer to the same file content
+- v8.3.1 (09-October-2023): [#440](https://github.com/bbottema/simple-java-mail/issues/440) Bug: names manually specified for embedded images are overridden and have extension added, breaking cid: references in HTML body
+- v8.3.0 (09-October-2023): [#475](https://github.com/bbottema/simple-java-mail/issues/475) Enhancement: Add configuration metadata for Spring Boot application properties
+
+
+[v8.2.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C8.2.0%7Cjar) (02-October-2023)
+
+- [#473](https://github.com/bbottema/simple-java-mail/issues/473) Bugfix: Add missing support for multiple reply-to addresses
+
+
+v8.1.0 - [v8.1.3](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/8.1.3/)
+
+- v8.1.3 (14-July-2023): [#467](https://github.com/bbottema/simple-java-mail/issues/467) Security: Medium severity vulnerability is detected in org.bouncycastle transitive dependency
+- v8.1.3 (14-July-2023): [#466](https://github.com/bbottema/simple-java-mail/issues/466) Maintenance: Let Mailer implement AutoCloseable, so it shuts down the connection pool automatically when disposed of by Spring
+- v8.1.2 (23-June-2023): [#465](https://github.com/bbottema/simple-java-mail/issues/465) Regression bug #461: Simple Java Mail always requires DKIM/SMIME modules
+- v8.1.1 (07-June-2023): [#461](https://github.com/bbottema/simple-java-mail/issues/461) Bugfix: Fixed MessageID not preserved when signing/encrypting with S/MIME and/or DKIM
+- v8.1.0 (15-April-2023): [#458](https://github.com/bbottema/simple-java-mail/pull/458) Missing osgi headers (#288) and added support for Apache Karaf
+- v8.1.0 (15-April-2023): [#288](https://github.com/bbottema/simple-java-mail/issues/288) Maintenance: missing OSGI package-exports from core-module
+
+
+v8.0.0 - [v8.0.1](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/8.0.1/)
+
+- v8.0.1 (30-April-2023): [#456](https://github.com/bbottema/simple-java-mail/issues/456): Enhancement: make Content-Transfer encoder detection more lenient, supporting more values from the wild
+- v8.0.0 (08-March-2023): [#451](https://github.com/bbottema/simple-java-mail/issues/451): Feature: Make defaults and overrides a first class feature
+- v8.0.0 (08-March-2023): [#452](https://github.com/bbottema/simple-java-mail/issues/452): Enhancement: with ".disableAllClientValidation(true)", also ignore errors from the completeness check
+- v8.0.0 (08-March-2023): [#450](https://github.com/bbottema/simple-java-mail/issues/450): Bug: when using dispositionNotificationTo or returnReceiptTo mode, when the corresponding emails are not filled, it fails even though it should fall back to replyTo or From
+- v8.0.0 (08-March-2023): [#449](https://github.com/bbottema/simple-java-mail/issues/449): Bug: IllegalArgumentException on parsing empty header name and value (when parsing Outlook message)
+- v8.0.0 (08-March-2023): [#448](https://github.com/bbottema/simple-java-mail/issues/448): Bug: withEmailDefaults and withEmailOverrides does not work with CustomMailer
+- v8.0.0 (08-March-2023): [#447](https://github.com/bbottema/simple-java-mail/issues/447): Enhancement: allow defaults/overrides to ignore individual fields (turn off for specific properties)
+- v8.0.0 (08-March-2023): [#446](https://github.com/bbottema/simple-java-mail/issues/446): Enhancement: add missing defaults properties for DKIM
+
+**This release changes how Email instances are built, or more specifically, when defaults and overrides are applied.
+There are now overloaded build methods that provide similar behaviour as previous versions.**
+
+
+v7.9.0 - [v7.9.1](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/7.9.1/)
+
+- v7.9.1 (22-February-2023): [#444](https://github.com/bbottema/simple-java-mail/issues/444) Bugfix: encoded delimited recipients in EML not parsed properly
+- v7.9.0 (21-February-2023): [#344](https://github.com/bbottema/simple-java-mail/issues/344) Enhancement: make DKIM signing more flexible by allowing header exclusions in DKIM signature
+
+
+v7.8.0 - [v7.8.3](https://repo1.maven.org/maven2/org/simplejavamail/simple-java-mail/7.8.3/)
+
+- v7.8.3 (21-February-2023): [#293](https://github.com/bbottema/simple-java-mail/issues/293) Bugfix: Decoding missing in a few placed when parsing MimeMessage or sending an Email
+- v7.8.2 (09-February-2023): [#442](https://github.com/bbottema/simple-java-mail/issues/442) Enhancement: Simple Java Mail should throw an exception when trying to utilize S/MIME with smime-module on the classpath
+- v7.8.1 (01-February-2023): [#438](https://github.com/bbottema/simple-java-mail/issues/438) Bug: properly Fail-Fast in case of Transport claim timeout in the batch-module, rather than running into NPE further down the line
+- v7.8.0 (24-January-2023): [#436](https://github.com/bbottema/simple-java-mail/issues/436) Enhancement: add mailerBuilder.withTransportModeLoggingOnly() as mailer API entry point
+- v7.8.0 (24-January-2023): [#435](https://github.com/bbottema/simple-java-mail/issues/435) Enhancement: SMTP server config should be optional in case a CustomMailer is used
+- v7.8.0 (24-January-2023): [#427](https://github.com/bbottema/simple-java-mail/issues/427) Feature: set a maximum email size on Mailer level which throws EmailToBig exception when exceeded
+
+
+v7.7.0 - [v7.7.1](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C7.7.1%7Cjar)
+
+- v7.7.1 (18-January-2023): [#434](https://github.com/bbottema/simple-java-mail/issues/434) Regression bug in #430: Email parameter missing in CustomMailer interface
+- v7.7.0 (17-January-2023): [#430](https://github.com/bbottema/simple-java-mail/issues/430) Enhancement: auto-reconnect (if needed) when reclaiming a Transport connection from the SMTP connection
+- v7.7.0 (17-January-2023): [#383](https://github.com/bbottema/simple-java-mail/issues/383) Feature: be able to set defaults and overrides on the Mailer level, rather than email or global level
+
+7.7.0 moves the conversion of Email to MimeMessage to after a Transport instance has been selected (in case of a cluster of SMTP servers),
+so we can apply defaults/overrides on the Mailer level, meaning you can configure 'global' values for individual SMTP servers
+(like a fixed FROM per server).
+
+
+[v7.6.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C7.6.0%7Cjar) (05-January-2023)
+
+- [#421](https://github.com/bbottema/simple-java-mail/issues/421) Enhancement: Add support for OAUTH2 authentication
+
+
+v7.5.0 - [v7.5.2](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C7.5.2%7Cjar)
+
+- v7.5.2 (25-December-2022): [#429](https://github.com/bbottema/simple-java-mail/issues/429) Bug: wrong username property used when password authentication is not needed
+- v7.5.1 (12-December-2022): [#416](https://github.com/bbottema/simple-java-mail/issues/416) Bug: Support encoder names regardless of their case ("base64" is the same as "BASE64")
+- v7.5.1 (12-December-2022): [#424](https://github.com/bbottema/simple-java-mail/issues/424) Maintenance: bump JMail dependency from 1.2.1 to 1.4.1
+- v7.5.0 (28-July-2022): [#411](https://github.com/bbottema/simple-java-mail/issues/411) Enhancement: expose validation sub steps in the MailerHelper class for the completeness check, CRLF inject scans and address validations
+- v7.5.0 (28-July-2022): [#410](https://github.com/bbottema/simple-java-mail/issues/410) Bug: CRLF injection scan missing for dispositionNotificationTo and returnReceiptTo
+- v7.5.0 (28-July-2022): [#390](https://github.com/bbottema/simple-java-mail/issues/390) Enhancement: make client sided validation optional, turning off address validation and CRLF injection detection
+
+
+[v7.5.1](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C7.5.1%7Cjar) (12-December-2022)
+
+- [#416](https://github.com/bbottema/simple-java-mail/issues/416) Bug: Support encoder names regardless of their case ("base64" is the same as "BASE64")
+- [#424](https://github.com/bbottema/simple-java-mail/issues/424) Maintenance: bump JMail dependency from 1.2.1 to 1.4.1
+
+
+[v7.5.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C7.5.0%7Cjar) (28-July-2022)
+
+- [#411](https://github.com/bbottema/simple-java-mail/issues/411) Enhancement: expose validation sub steps in the MailerHelper class for the completeness check, CRLF inject scans and address validations
+- [#410](https://github.com/bbottema/simple-java-mail/issues/410) Bug: CRLF injection scan missing for dispositionNotificationTo and returnReceiptTo
+- [#390](https://github.com/bbottema/simple-java-mail/issues/390) Enhancement: make client sided validation optional, turning off address validation and CRLF injection detection
+
+
+[v7.4.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C7.4.0%7Cjar) (19-July-2022)
+
+- [#407](https://github.com/bbottema/simple-java-mail/issues/407) Enhancement: Process all Outlook message headers, either copying the as-is or translating them to respective Simple Java Mail API calls
+- [#404](https://github.com/bbottema/simple-java-mail/issues/404) Minor bugfix: the new attachment's contentDescription was missing in Email.toString()
+
+
+[v7.3.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C7.3.0%7Cjar) (15-July-2022)
+
+- [#405](https://github.com/bbottema/simple-java-mail/issues/405) Feature: Expand email builder API to support forced content Content-Transfer-Encoding for attachments, like quoted-printable, base64, 7BIT and others
+- [#404](https://github.com/bbottema/simple-java-mail/issues/404) Feature: Expand email builder API to support Content-Description on attachments
+
+
+v7.2.0 - [v7.2.1](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C7.2.1%7Cjar)
+
+- v7.2.1 (13-July-2022): [#396](https://github.com/bbottema/simple-java-mail/issues/396) Enhancement: make Outlook support tolerant of invalid/empty nested Outlook message attachments
+- v7.2.0 (13-July-2022): [#399](https://github.com/bbottema/simple-java-mail/issues/399) Feature: Expand email builder API to support [selective content encoding](https://www.simplejavamail.org/features.html#section-content-transfer-encoding), like quoted-printable, base64, 7BIT and others
+
+
+v7.1.0 - [v7.1.3](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C7.1.3%7Cjar)
+
+- v7.1.3 (12-July-2022): [#403](https://github.com/bbottema/simple-java-mail/issues/403) Security: Bump zip4j (only used during testing)
+- v7.1.2 (12-July-2022): [#401](https://github.com/bbottema/simple-java-mail/issues/401) Enhancement: Add HEIC and WEBP support when dynamically resolving embedded images from classpath
+- v7.1.2 (12-July-2022): [#402](https://github.com/bbottema/simple-java-mail/issues/402) Security: Update Log4j to 2.17.1
+- v7.1.2 (12-July-2022): [#393](https://github.com/bbottema/simple-java-mail/issues/393) Security: Update Apache POI and POI Scratchpad
+- v7.1.1 (27-March-2022): [#387](https://github.com/bbottema/simple-java-mail/issues/387) Bug: memory leak in SMPT connection pool when 3rd party deallocation failed with exception
+- v7.1.0 (25-January-2022): [#379](https://github.com/bbottema/simple-java-mail/issues/379) Maintenance: Adjust dependencies and make Java 9+ friendly
+
+
+v7.0.1 - [v7.0.2](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C7.0.2%7Cjar)
+
+- v7.0.2 (25-January-2022): [#329](https://github.com/bbottema/simple-java-mail/issues/329) Enhancement: Exceptions cause error-level logging in addition to rethrowing the exception, but should just include the message in a custom exception
+- v7.0.2 (25-January-2022): [#378](https://github.com/bbottema/simple-java-mail/issues/378) Bug: package org.simplejavamail.internal.modules causes split package problem in Java9+
+- v7.0.1 (22-January-2022): [#375](https://github.com/bbottema/simple-java-mail/issues/375) Bug: [batch-module](https://www.simplejavamail.org/features.html#section-sending-asynchronously) gives error when there is a [custom mailer](https://www.simplejavamail.org/features.html#section-custom-mailer)
+
+
+[v7.0.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C7.0.0%7Cjar) (2-January-2022)
+
+It has been two years since the last major release, but 7.0.0 finally here!
+
+#### What's new ####
+
+Major features:
+- [#322](https://github.com/bbottema/simple-java-mail/issues/322) Simple Java Mail migrated to Java 8 finally (see notes below)
+- [#295](https://github.com/bbottema/simple-java-mail/issues/295) And also switched to JavaMail's successor Jakarta Mail 2.0.1 (see notes below)
+- [#323](https://github.com/bbottema/simple-java-mail/issues/323) Solved the great CLI performance problem (now executes near instantly)
+- [#319](https://github.com/bbottema/simple-java-mail/issues/319) Replaced the underlying regex-based email-address validation library with the lexer based [JMail](https://github.com/RohanNagar/jmail), which is faster, [correcter](https://www.rohannagar.com/jmail/), documented better and is more up-to-date with RFC's
+- [#367](https://github.com/bbottema/simple-java-mail/issues/367) The sendMail/testConnection methods now have proper support for `CompletableFuture`
+
+Bugfixes:
+- [#352](https://github.com/bbottema/simple-java-mail/issues/352) Bug: names regex groups are not supported in Android JVM
+- [#326](https://github.com/bbottema/simple-java-mail/issues/326) Bug: NullPointer when parsing Outlook Message with nested empty Outlook message
+- [#330](https://github.com/bbottema/simple-java-mail/issues/330) Bug: cli expected --mailer arguments duplicated 3 times
+- [#324](https://github.com/bbottema/simple-java-mail/issues/324) Bug: Add back missing log4j2 for CLI library
+
+Maintenance:
+- [#368](https://github.com/bbottema/simple-java-mail/issues/368) Resolve log4j (Java8) [vulnerability](https://logging.apache.org/log4j/2.x/security.html) in Simple Java Mail's CLI module
+- [#330](https://github.com/bbottema/simple-java-mail/issues/330) Improved feedback from failing CLI commands
+- [#327](https://github.com/bbottema/simple-java-mail/issues/327) Implement toString() for Mailer instances for debugging purposes
+
+#### About the migration ####
+
+Updating to Java8/Jakarta 2.0.1 posed a challenge as the 3rd party S/MIME library [java-utils-mail-smime](https://github.com/markenwerk/java-utils-mail-smime) has been abandoned/archived while developing Simple Java Mail. Furthermore, it was still under LGPL3 license while everything else is ApacheV2.
+
+Thankfully, I obtained permissions from the maintainers -as well as original developers from decades ago on SourceForge- to take both java-utils-mail-smime and java-utils-mail-dkim under my wings at Simple Java Mail and change the licensing model! You can now post issues and pull requests here:
+- [simple-java-mail/java-utils-mail-smime](https://github.com/simple-java-mail/java-utils-mail-smime)
+- [simple-java-mail/java-utils-mail-dkim](https://github.com/simple-java-mail/java-utils-mail-dkim)
+
+
+
+v6.7.0 - [v6.7.6](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C6.7.6%7Cjar)
+
+- v6.7.6 (22-January-2022): [#375](https://github.com/bbottema/simple-java-mail/issues/375) Bug: [batch-module](https://www.simplejavamail.org/features.html#section-sending-asynchronously) gives error when there is a [custom mailer](https://www.simplejavamail.org/features.html#section-custom-mailer)
+- v6.7.5 (26-December-2021): [#338](https://github.com/bbottema/simple-java-mail/issues/338) Enhancement: Also return AsyncResponse from plain Mailer.sendEmail(singleArgument) as async can be configured through MailerBuilder now
+- v6.7.4 (25-December-2021): [#331](https://github.com/bbottema/simple-java-mail/issues/331) Enhancement: Coalesce empty SMTP server arguments to null to support CLI better
+- v6.7.3 (25-December-2021): [#335](https://github.com/bbottema/simple-java-mail/issues/335) Bugfix: Precondition nonNull check also checks nonEmpty and breaks on clearEmailAddressCriteria
+- v6.7.2 (25-December-2021): [#318](https://github.com/bbottema/simple-java-mail/issues/318) Maintenance: Allow zero data attachments so Outlook message conversions don't crash and burn
+- v6.7.1 (25-December-2021): [#346](https://github.com/bbottema/simple-java-mail/issues/346) Bugfix: Add option to parse MimeMessage without fetching attachment data from server - Properly return named datasource without fetching all the data if unwanted
+- v6.7.0 (25-December-2021): [#356](https://github.com/bbottema/simple-java-mail/issues/356) Enhancement: Improve for support for Android < 8.0 (Fix NoClassDefFoundError)
+- v6.7.0 (25-December-2021): [#351](https://github.com/bbottema/simple-java-mail/issues/351) Bugfix: emlToEmail() and mimeMessageToEmail() break on mesages with duplicate names and legacy empty nested messages
+- v6.7.0 (25-December-2021): [#347](https://github.com/bbottema/simple-java-mail/issues/347) Maintenance: Reduce log-spam and prevent exception on every module availability check
+- v6.7.0 (25-December-2021): [#346](https://github.com/bbottema/simple-java-mail/issues/346) Feature: Add option to parse MimeMessage [without fetching attachment data](https://www.simplejavamail.org/features.html#section-converting) from server
+
+
+v6.6.0 - [v6.6.2](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C6.6.2%7Cjar)
+
+- v6.6.2 (23-December-2021): [#365](https://github.com/bbottema/simple-java-mail/issues/365) Security: Resolve log4j vulnerability in Simple Java Mail
+- v6.6.1 (12-June-2021): [#321](https://github.com/bbottema/simple-java-mail/issues/321) Enhancement: Ignore malformed recipient addresses and continue parsing email data
+- v6.6.0 (1-June-2021): [#320](https://github.com/bbottema/simple-java-mail/issues/320) Enhancement: Added [default S/MIME signing](https://www.simplejavamail.org/features.html#section-sending-smime) to Mailer level and fixed crippling performance bug
+
+
+v6.5.0 - [v6.5.4](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C6.5.4%7Cjar)
+
+- v6.5.4 (22-May-2021): [#315](https://github.com/bbottema/simple-java-mail/issues/315) Enhancement: Nested attachments of Outlook message attachments are now preserved as standard EML MimeMessage attachments
+
+ **NOTE:** This _removes_ the Kryo dependency
+
+- v6.5.3 (4-May-2021): [#314](https://github.com/bbottema/simple-java-mail/issues/314) Bugfix: Nested attachments of Outlook message attachments are now preserved, by utilizing Kryo
+
+ **NOTE:** This add Kryo as extra dependency to the outlook-module (and is subsequently removed in 6.5.4)
+
+- v6.5.2 (15-April-2021): [#311](https://github.com/bbottema/simple-java-mail/issues/311) Bugfix: text/calendar as string -> ClassCastException (if calendar type is not Inputstream)
+- v6.5.1 (10-April-2021): [#307](https://github.com/bbottema/simple-java-mail/issues/307) / [#310](https://github.com/bbottema/simple-java-mail/issues/310) Bugfix: embedded image resource name got mangled
+- v6.5.0 (16-February-2021): [#298](https://github.com/bbottema/simple-java-mail/issues/298) Enhancement: Nested Outlook messages aren't discarded anymore, but parsed to serialized Email objects
+- v6.5.0 (16-February-2021): [#292](https://github.com/bbottema/simple-java-mail/issues/292) Bugfix: NullPointerException in SmimeUtilFixed when protocol is missing (which is valid)
+- v6.5.0 (16-February-2021): [#289](https://github.com/bbottema/simple-java-mail/issues/289) Bugfix: Support multiple headers with same key
+
+This release breaks all GET/SET api regarding headers used as map (Map<String, T> -> Map<String, Collection<T>>)
+This release might break api in the rare case you relied on the attachment list and you have nested Outlook .msg attachments (previously omitted in the results)
+
+
+v6.4.0 - [v6.4.5](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C6.4.5%7Cjar)
+
+- v6.4.5 (13-Februari-2021): [#306](https://github.com/bbottema/simple-java-mail/issues/306) Maintenance: Update outlook-message-parser to 1.7.9
+- v6.4.5 (13-Februari-2021): [#304](https://github.com/bbottema/simple-java-mail/issues/304) Regression bugfix: batch-module needed for sending mails async, basic version should work without
+- v6.4.5 (13-Februari-2021): [#303](https://github.com/bbottema/simple-java-mail/issues/303) Bugfix: EML Attachments are modified/have the wrong size
+- v6.4.4 (25-October-2020): [#294](https://github.com/bbottema/simple-java-mail/issues/294) Always invoke async success/exception handlers even if set after sending email (behaving more like promises/futures)
+- v6.4.4 (25-October-2020): [#291](https://github.com/bbottema/simple-java-mail/issues/291) On Exception only log the email ID at error level and log the whole email at trace level
+- v6.4.4 (25-October-2020): [#290](https://github.com/bbottema/simple-java-mail/issues/290) Only perform expensive logging logic if respective logging level is enabled
+- v6.4.3 (6-August-2020): [#284](https://github.com/bbottema/simple-java-mail/issues/284) Improved support for Calendar attachments created by gMail
+- v6.4.3 (6-August-2020): [#283](https://github.com/bbottema/simple-java-mail/issues/283) Bugfix: Fix support for reading Calendar attachments with quoted-printable transfer-type
+- v6.4.2 (3-August-2020): [#281](https://github.com/bbottema/simple-java-mail/issues/281) Bugfix: Fix support for reading Calendar attachments
+- v6.4.1 (26-July-2020): [#252](https://github.com/bbottema/simple-java-mail/issues/252) Bugfix: Added missing support for S/MIME enveloped signing
+- v6.4.0 (19-July-2020): [#268](https://github.com/bbottema/simple-java-mail/issues/268) Immediately resolve InputStreams when building emails, don't reuse
+ * This primarily affects the builder api for S/MIME and DKIM signing / encryption.
+
+
+v6.3.0 - [v6.3.2](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C6.3.2%7Cjar) (11-July-2020 - 12-July-2020)
+
+- [#271](https://github.com/bbottema/simple-java-mail/issues/271) Bugfix: Attachment (file)names with special characters should not be encoded
+- [#248](https://github.com/bbottema/simple-java-mail/issues/248) Bugfix: MimeMessageHelper: use complete filename as resource name
+- [#279](https://github.com/bbottema/simple-java-mail/issues/279) Allow [extra Session properties](https://www.simplejavamail.org/features.html#section-custom-properties) configured through simplejavamail.properties
+- [#277](https://github.com/bbottema/simple-java-mail/issues/277) Add API for using [custom SSLSocketFactory](http://localhost:3000/features.html#section-ssl-tls)
+
+
+[v6.2.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C6.2.0%7Cjar) (9-July-2020)
+
+This release adds the following major new feature:
+
+- [#260](https://github.com/bbottema/simple-java-mail/issues/260) **Add support for [dynamic datasource resolution](https://www.simplejavamail.org/features.html#section-embedding)** (file/url/classpath) for embedded images in HTML body
+
+
+[v6.1.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C6.1.0%7Cjar) (5-July-2020)
+
+- [#264](https://github.com/bbottema/simple-java-mail/issues/264) Switch from AssertionError to IllegalStateException
+- Bumped outlook-message-parser from 1.7.3 to 1.7.5
+ - bugfix for parsing chinese unsent Outlook messages
+ - bugfix Outlook attachments with special characters in the name
+- Bumped email-rfc2822-validator from 2.1.3 to 2.2.0
+ - bugfix properly handle brackets in email addresses when allowed
+- Bumped log4j-core from 2.6.1 to 2.13.2
+
+
+v6.0.2 - [v6.0.5](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C6.0.5%7Cjar) (21-January-2020 - 13-June-2020)
+
+- [#270](https://github.com/bbottema/simple-java-mail/issues/270) Bug: CLI module missing Jetbrains @Nullable annotation dependency needed in runtime
+- [#262](https://github.com/bbottema/simple-java-mail/issues/262) Bug: Executor settings passed to the builder are ignored
+- [#249](https://github.com/bbottema/simple-java-mail/issues/249) Bug: MimeMessageParser doesn't handle multiple attachments with the same name correctly
+- [#245](https://github.com/bbottema/simple-java-mail/issues/245) Bug: JDK9+ Incorrect JPMS Automatic-Module-Name
+- [#246](https://github.com/bbottema/simple-java-mail/issues/246) Bug: Sending async emails with and without the Batch module cause lingering threads
+
+
+v6.0.0-rc1 - [v6.0.1](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C6.0.1%7Cjar) (18-December-2019 - 24-January-2020)
+
+After almost two years of development the next major release 6.0.0 is finally here! And what a doozy it is, with the following major new features:
+
+The core library is now even smaller compared to the 5.x.x series going from 183kb to 134kb!
+
+- [CLI support!!](https://www.simplejavamail.org/cli.html#navigation),
+- major performance improvement with [advanced batch processing](https://www.simplejavamail.org/configuration.html#section-batch-and-clustering) including support for mail server clusters.
+- You can now replace the final sending of emails with [your own logic](https://www.simplejavamail.org/features.html#section-custom-mailer), using a 3rd party service of your choice.
+- 6.0.0 also includes support for [S/MIME signed and encrypted emails](https://www.simplejavamail.org/features.html#section-smime)!
+- All 3rd party dependencies have been made optional by splitting up Simple Java Mail into easy to use [modules](https://www.simplejavamail.org/modules.html#navigation).
+- You can now monitor and [handle async processing](https://www.simplejavamail.org/features.html#section-handling-async-result) using Futures.
+- MimeMessage results are now [structurally matched](https://www.simplejavamail.org/rfc-compliant.html#section-explore-multipart) to specific needs (only using alternative/mixed etc. when needed)
+
+Here's the complete list of changes:
+
+#### New features and enhancements ####
+- [#183](https://github.com/bbottema/simple-java-mail/issues/183) To manage all the optional dependencies and related code, Simple Java Mail should be [split up into modules](https://www.simplejavamail.org/modules.html#navigation)
+- [#156](https://github.com/bbottema/simple-java-mail/issues/156) Add [CLI support](https://www.simplejavamail.org/cli.html#navigation)
+- [#214](https://github.com/bbottema/simple-java-mail/issues/214) Support more [advanced batch processing](https://www.simplejavamail.org/features.html#section-sending-asynchronously) use cases
+- [#187](https://github.com/bbottema/simple-java-mail/issues/187) Simple Java Mail should have optional support for signed [S/MIME attachments](https://www.simplejavamail.org/modules.html#smime-module)
+- [#121](https://github.com/bbottema/simple-java-mail/issues/121) Introduce interfaces for validation and sending, so these steps can be customized
+- [#144](https://github.com/bbottema/simple-java-mail/issues/144) Simple Java Mail should [tailor the MimeMessage structure](https://www.simplejavamail.org/migration-notes-6.0.0.html#mimemessage-structure) to specific needs
+- [#138](https://github.com/bbottema/simple-java-mail/issues/138) Add support for [Calendar events](https://www.simplejavamail.org/features.html#section-icalendar-vevent) (iCalendar vEvent)
+- [#235](https://github.com/bbottema/simple-java-mail/issues/235) Be able to [fix the sent date](https://www.simplejavamail.org/features.html#section-custom-sentdate) for a new email
+- [#232](https://github.com/bbottema/simple-java-mail/issues/232) Improve encoding of attachment file names
+- [#222](https://github.com/bbottema/simple-java-mail/issues/222) Add config property support for trusting hosts and verifying server identity
+- [#212](https://github.com/bbottema/simple-java-mail/issues/212) Authenticated proxy server started even if already running, raising exception
+- [#207](https://github.com/bbottema/simple-java-mail/issues/207) Implement more comprehensive ThreadPoolExecutor and expose config options
+- [#211](https://github.com/bbottema/simple-java-mail/issues/211) SpringSupport should expose the intermediate builder for customization
+- [#193](https://github.com/bbottema/simple-java-mail/issues/193) Simple Java Mail should use default server ports when not provided by the user
+
+#### Bugs solved ####
+- [#242](https://github.com/bbottema/simple-java-mail/issues/242) Renamed log4j2.xml to log4j2_example.xml, so it doesn't clash with project config
+- [#241](https://github.com/bbottema/simple-java-mail/issues/241) EmailConverter.outlookMsgToEmail duplicates recipients
+- [#239](https://github.com/bbottema/simple-java-mail/issues/239) List of Recipients not ordered as added (insertion order not maintained)
+- [#236](https://github.com/bbottema/simple-java-mail/issues/236) Message ID should be mapped from Outlook messages as well
+- [#210](https://github.com/bbottema/simple-java-mail/issues/210) Connection/session timeout properties not set when not sending in batch mode
+- [#201](https://github.com/bbottema/simple-java-mail/issues/201) When parsing Outlook message, FROM address should default to a dummy address when missing
+- [#200](https://github.com/bbottema/simple-java-mail/issues/200) When parsing Outlook message, attachment name doesn't fall back on filename if proper name is empty
+- [#161](https://github.com/bbottema/simple-java-mail/issues/161) When reading (chinese) .msg files, HTML converted from RTF is completely garbled (encoding issue)
+- [#159](https://github.com/bbottema/simple-java-mail/issues/159) Can not parse email with blank email address headers
+- [#139](https://github.com/bbottema/simple-java-mail/issues/139) Multiple Bodyparts of same Content-Type not supported for text/html & text/plain within Multipart/mixed or Multipart/alternative
+- [#151](https://github.com/bbottema/simple-java-mail/issues/151) Attachment's file extension overwritten by resource's invalid extension
+
+#### Maintenance updates ####
+- [#165](https://github.com/bbottema/simple-java-mail/issues/165) Move away from Findbugs (unofficial JSR-305) annotations
+- [#164](https://github.com/bbottema/simple-java-mail/issues/164) The DKIM dependency has been updated to benefit from the newer Apache V2 license
+- [#164](https://github.com/bbottema/simple-java-mail/issues/164) The DKIM dependency has been updated to benefit from the newer Apache V2 license
+- [#184](https://github.com/bbottema/simple-java-mail/issues/184) Update JavaMail dependency to 1.6.2, adding support for UTF-8 charset
+- [#186](https://github.com/bbottema/simple-java-mail/issues/186) Update JavaMail dependency to 1.6.2, adding support for authenticated HTTP web proxy
+- [#146](https://github.com/bbottema/simple-java-mail/issues/146) Added OSGI manifest and switched to spotbugs
+
+#### Included changes from outlook-message-parser ####
+- v6.0.1, v1.7.3: [#27](https://github.com/bbottema/outlook-message-parser/issues/27) When from name/address are not available (unsent emails), these fields are filled with binary garbage
+- v6.0.1, v1.7.2: [#26](https://github.com/bbottema/outlook-message-parser/issues/26) To email address is not handled properly when name is omitted
+- v6.0.0, v1.7.1: [#25](https://github.com/bbottema/outlook-message-parser/issues/25) NPE on ClientSubmitTime when original message has not been sent yet
+- v6.0.0, v1.7.1: [#23](https://github.com/bbottema/outlook-message-parser/issues/23) Bug: __nameid_ directory should not be parsed (and causing invalid HTML body)
+- v6.0.0, v1.7.0: [#18](https://github.com/bbottema/outlook-message-parser/issues/18) Upgrade Apache POI 3.9 -> 4.x (but managed back for Simple Java Mail due to incompatibility with Java 7)
+- v6.0.0, v1.6.0: [#21](https://github.com/bbottema/outlook-message-parser/issues/21) Multiple TO recipients are not handles properly
+- v6.0.0, v1.5.0: [#20](https://github.com/bbottema/outlook-message-parser/issues/20) CC and BCC recipients are not parsed properly
+- v6.0.0, v1.5.0: [#19](https://github.com/bbottema/outlook-message-parser/issues/19) Use real Outlook ContentId Attribute to resolve CID Attachments
+- v6.0.0, v1.4.1: [#17](https://github.com/bbottema/outlook-message-parser/issues/17) Fixed encoding error for UTF-8's Windows legacy name (cp)65001
+- v6.0.0, v1.4.0: [#9](https://github.com/bbottema/outlook-message-parser/issues/9) Replaced the RFC to HTML converter with a brand new RFC-compliant convert! (thanks to @fadeyev!)
+- v6.0.0, v1.3.0: [#14](https://github.com/bbottema/outlook-message-parser/issues/14) Dependency problem with Java9+, missing Jakarta Activation Framework
+- v6.0.0, v1.3.0: [#13](https://github.com/bbottema/outlook-message-parser/issues/13) HTML start tags with extra space not handled correctly
+- v6.0.0, v1.3.0: [#11](https://github.com/bbottema/outlook-message-parser/issues/11) SimpleRTF2HTMLConverter inserts too many tags
+- v6.0.0, v1.3.0: [#10](https://github.com/bbottema/outlook-message-parser/issues/10) Embedded images with DOS-like names are classified as attachments
+- v6.0.0, v1.3.0: [#9](https://github.com/bbottema/outlook-message-parser/issues/9) SimpleRTF2HTMLConverter removes some valid tags during conversion
+- v6.0.0, v1.2.1: Ignore non S/MIME related content types when extracting S/MIME metadata
+- v6.0.0, v1.2.1: Added toString and equals methods to the S/MIME data classes
+- v6.0.0, v1.1.21: Upgraded mediatype recognition based on file extension for incomplete attachments
+- v6.0.0, v1.1.21: Added / improved support for public S/MIME meta data
+- v6.0.0, v1.1.20: [#7](https://github.com/bbottema/outlook-message-parser/issues/7) Fix missing S/MIME header details that are needed to determine the type of S/MIME application
+- v6.0.0, v1.1.19: Log rtf compression error, but otherwise ignore it and keep going and extract what we can.
+
+**A big shout-out to @dnault ([runtime javadoc](https://github.com/dnault/therapi-runtime-javadoc)), @remkop ([picocli](https://picocli.info/)) and @markenwerk
+([S/MIME](https://github.com/markenwerk/java-utils-mail-smime) and [DKIM](https://github.com/markenwerk/java-utils-mail-dkim)) for working with me to make the
+libraries work with JDK7+ and do what Simple Java Mail needed! Finally, a great many thanks the numerous contributors on Simple Java Mail as well as
+[outlook-message-parser](https://github.com/bbottema/outlook-message-parser) - this release would not be there without you.**
+
+
+v5.5.0 - [v5.5.1](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C5.5.1%7Cjar)
+
+- v5.5.1 (20-October-2019): [#230](https://github.com/bbottema/simple-java-mail/issues/230) Bugfix: Missing address value in address headers (i.e. Return-Path) not handled properly, resulting in Exception
+- v5.5.0 (15-October-2019): [#229](https://github.com/bbottema/simple-java-mail/issues/229) Bugfix: Timeouts not working for synchronous sendMail calls.
+
+ If you had connection properties configured for non-async send jobs, only now they will actually start to take effect.
+
+
+[v5.4.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C5.4.0%7Cjar) (28-August-2019)
+
+- [#221](https://github.com/bbottema/simple-java-mail/issues/221) API bugfix: server identity verification should not be tied to host trusting
+- [#226](https://github.com/bbottema/simple-java-mail/issues/226) Bug fix: Attachments with spaces in name are not handled properly
+- [#218](https://github.com/bbottema/simple-java-mail/issues/218) Enhancement: make Email serializable
+- [#227](https://github.com/bbottema/simple-java-mail/issues/227) Enhancement: Make parsing recipients from EML file more lenient
+- [#225](https://github.com/bbottema/simple-java-mail/issues/225) Enhancement: Clarify dependency on Jakarta Activation: DataSources no longer work on Java 9+
+
+
+[v5.3.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C5.3.0%7Cjar) (16-August-2019)
+
+- [#215](https://github.com/bbottema/simple-java-mail/issues/215) Bug: Current DKIM header canonicalization can lead to invalid DKIM
+
+Note this release should have no impact, but nonetheless is a minor update, so you can determine for yourself if this update would cause issues.
+The release changes DKIM header canonicalization from SIMPLE to RELAXED.
+
+
+[v5.2.1](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C5.2.1%7Cjar) (16-August-2019)
+
+- [#219](https://github.com/bbottema/simple-java-mail/issues/219) Bug: MimeMessageParser rejects attachments with duplicate names
+
+
+[v5.2.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C5.2.0%7Cjar) (7-July-2019)
+
+- [#213](https://github.com/bbottema/simple-java-mail/issues/213) Update from javax.mail:1.6.0 to jakarta.mail:1.6.3
+
+Note that dependencies that switched as well have been updated as part of this change. This includes the optional DKIM library and the email validation library:
+- net.markenwerk:utils-mail-dkim (1.1.10 -> 1.2.0)
+- com.github.bbottema:emailaddress-rfc2822 (1.1.2 -> 2.1.3)
+
+
+v5.1.1 - [v5.1.7](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C5.1.7%7Cjar)
+
+- v5.1.7 (22-May-2019): [#171](https://github.com/bbottema/simple-java-mail/issues/171) Header validation tripping on known safe emails due to References header
+- v5.1.6 (27-April-2019): [#204](https://github.com/bbottema/simple-java-mail/issues/204) A Concurrent exception when an async process starts when the previous connection pool didn't shut down in time
+- v5.1.6 (27-April-2019): [#204](https://github.com/bbottema/simple-java-mail/issues/204) B Exceptions in threads are now caught and logged and don't bubble up anymore. Note that more comprehensive exception handling will be available in 6.0.0 ([#148](https://github.com/bbottema/simple-java-mail/issues/148)).
+- v5.1.5 (24-April-2019): [#202](https://github.com/bbottema/simple-java-mail/issues/202) Fixed ConcurrentModificationException when moving invalid embedded images as regular attachments
+- v5.1.4 (5-April-2019): [#163](https://github.com/bbottema/simple-java-mail/issues/163) Fixed missing mimetype for attachments when parsing Outlook messages where mimeTag was not included
+- v5.1.3 (15-Januari-2019): Updated to newer rfc-validator version, which fixed a regression bug in that library
+- v5.1.2 (9-Januari-2019): [#189](https://github.com/bbottema/simple-java-mail/issues/189) Bugfix for missing timeout config for .testConnection() function
+- v5.1.1 (22-December-2018): [#190](https://github.com/bbottema/simple-java-mail/issues/190) Fix for transitive dependency clash because of emailaddress-rfc2822 library
+
+
+[v5.1.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C5.1.0%7Cjar) (21-November-2018)
+
+- [#179](https://github.com/bbottema/simple-java-mail/issues/179) You can now [test the connection](https://www.simplejavamail.org/features.html#section-connection-test) to the SMTP server
+
+
+v5.0.1 - [v5.0.8](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C5.0.8%7Cjar)
+
+- v5.0.8 (27-Oktober-2018): [#178](https://github.com/bbottema/simple-java-mail/issues/178) Fix the annoying vulnerability GitHub report about spring-core
+- v5.0.7 (27-Oktober-2018): [#175](https://github.com/bbottema/simple-java-mail/issues/175) Attachment names are not always parsed properly from MimeMessage
+- v5.0.6 (3-Oktober-2018): [#167](https://github.com/bbottema/simple-java-mail/issues/167) Email addresses validated despite cleared validation criteria
+- v5.0.5 (3-Oktober-2018): [#137](https://github.com/bbottema/simple-java-mail/issues/137) When replying to an email with HTML, the result body is empty
+- v5.0.4 (22-September-2018): [#168](https://github.com/bbottema/simple-java-mail/issues/168) Properties aquired through ConfigLoader should be typed explicitly and converted if necessary
+- v5.0.3 (11-April-2018): [#136](https://github.com/bbottema/simple-java-mail/issues/136) ServerConfig class should be public API
+- v5.0.2 (7-April-2018): [#135](https://github.com/bbottema/simple-java-mail/issues/135) trustingAllHosts should be public on the Builder API
+- v5.0.2 (7-April-2018): [#131](https://github.com/bbottema/simple-java-mail/issues/131) NamedDataSource should implement EncodingAware
+- v5.0.1 (10-March-2018): [#130](https://github.com/bbottema/simple-java-mail/issues/130) java.lang.ClassNotFoundException: net.markenwerk.utils.mail.dkim.DkimMessage. Solves the issue of missing optional class DKIM even when not used
+
+
+[v5.0.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C5.0.0%7Cjar) (14-Februari-2018)
+
+Also see the [migration notes](https://www.simplejavamail.org/migration-notes-5.0.0.html#navigation)
+
+#### New features ####
+- [#116](https://github.com/bbottema/simple-java-mail/issues/116) You can now test the connection to the SMTP server
+- [#115](https://github.com/bbottema/simple-java-mail/issues/115) Create mailers with a very robust MailerBuilder API, able to ignore defaults as well
+- [#114](https://github.com/bbottema/simple-java-mail/issues/114) Create emails with a very robust EmailBuilder API, able to ignore defaults as well. Now includes support for InternetAddress. Also copy emails.
+- [#107](https://github.com/bbottema/simple-java-mail/issues/107) You can now easily forward or reply to emails!
+
+#### Security updates ####
+- [#111](https://github.com/bbottema/simple-java-mail/issues/111) Protocol properties for SMTPS are now applied properly
+- [#105](https://github.com/bbottema/simple-java-mail/issues/105) SMTP tries to upgrade to TLS while SMTP_TLS now enforces it and for both SMTP_TLS and SMTPS, [mail.smtp.ssl.checkserveridentity](https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html) is set to true
+
+#### Maintenance updates ####
+
+Complete [Javadoc](https://www.javadoc.io/doc/org.simplejavamail/simple-java-mail) overhaul. Navigating the Javadoc should be much more consistent now (builder API being the single *public* source of truth).
+
+- [#122](https://github.com/bbottema/simple-java-mail/issues/122) The email-rfc2822-validator library has been made a proper Maven dependency (not packaged along anymore)
+- [#120](https://github.com/bbottema/simple-java-mail/issues/120) The DKIM library has been made an optional proper Maven dependency (not packaged along anymore)
+- [#119](https://github.com/bbottema/simple-java-mail/issues/119) Switched optional Spring dependency version to property and now testing with 4.3.11.RELEASE
+- [#113](https://github.com/bbottema/simple-java-mail/issues/113) Updated the underlying JavaMail to 1.6.0
+
+#### Bugfixes ####
+- [#110](https://github.com/bbottema/simple-java-mail/issues/110) Trusted hosts should be space-delimited
+- [#109](https://github.com/bbottema/simple-java-mail/issues/109) Email headers should be allowed to be empty (now conversion errors can occur as well)
+- [#103](https://github.com/bbottema/simple-java-mail/issues/103) Converting to MimeMessage results in an invalid Content-Disposition for attachments
+
+
+[v4.4.5](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C4.4.5%7Cjar) (2-September-2017)
+
+- [#101](https://github.com/bbottema/simple-java-mail/issues/101) API backwards compatibility update, reinstate old addRecipient API as deprecated (sorry for removing it abruptly)
+
+
+[v4.4.4](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C4.4.4%7Cjar) (23-August-2017)
+
+API usability release. **This relase streamlined the recipient setters, breaking backwards compatibility (but straightforward to fix)**
+
+- [#95](https://github.com/bbottema/simple-java-mail/issues/95) Feature: Add support native API for [setting Return-Receipt-To](https://www.simplejavamail.org/features.html#section-return-receipt) header
+- [#93](https://github.com/bbottema/simple-java-mail/issues/93) Feature: Add support native API for setting [Disposition-Notification-To](https://www.simplejavamail.org/features.html#section-return-receipt) header
+- [#91](https://github.com/bbottema/simple-java-mail/issues/91) **Feature: Add support for parsing [preformatted email addresses](https://www.simplejavamail.org/features.html#section-add-recipients) that include both name and address**
+- [#94](https://github.com/bbottema/simple-java-mail/issues/94) Bugfix: A single EmailBuilder would build emails that all share the same collections for recipients, attachments and embedded images
+- [#98](https://github.com/bbottema/simple-java-mail/issues/98) Bugfix: Subject and body content should be optional
+
+
+[v4.3.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C4.3.0%7Cjar) (12-August-2017)
+
+Security and timeout release.
+
+This version safeguards against SMTP injection attack from external values entering the library through *Email* instance. Also, this release introduces default/configurable timeouts for connecting, reading and writing when sending an email.
+
+- [#89](https://github.com/bbottema/simple-java-mail/issues/89) Support multiple delimited recipient addresses sharing the same TO/CC/BCC name
+- [#88](https://github.com/bbottema/simple-java-mail/issues/88) **Safeguard subject property (and others) against SMTP CRLF injection attacks**
+- [#85](https://github.com/bbottema/simple-java-mail/issues/85) **Apply configurable timeouts when sending emails**
+- [#83](https://github.com/bbottema/simple-java-mail/issues/83) Parse INLINE attachments without ID as regular attachments when converting (mostly applicable to Apple emails)
+
+
+[v4.2.3](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C4.2.3%7Cjar) (21-May-2017)
+
+- [#79](https://github.com/bbottema/simple-java-mail/issues/79): Enhancement: define custom message ID on the Email object
+- [#74](https://github.com/bbottema/simple-java-mail/issues/74): v4.2.3-java-6-release: A java6 version with limited capabilities:
+ I've released a customised java6 release with a customised outlook-message-parser 1.1.16-java6-release. **This is the last java6 release** I will do, as it is simply too much manual labor to create a limited second edition.
+
+ For this edition, I've removed the JDK7 Phaser completely which has the following consequences:
+
+ - If authenticated proxy is used, the bridging proxy server will not be shut down automatically (and might not run the second time)
+ - If mails are sent in async mode, the connection pool will not be shut down anymore by itself
+
+ This means your server/application might not stop properly due to lingering processes. To be completely safe, only send emails in sync mode (used by default) and don't use authenticated proxy config.
+
+
+[v4.2.2](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C4.2.2%7Cjar) (10-May-2017)
+
+- [#73](https://github.com/bbottema/simple-java-mail/issues/73): Patch: fix for sending emails in async mode, which makes sure the connection pool is always closed when the last *known* email has been sent. Without this fix, the connection pool keeps any parent process running (main thread or Tomcat for example) until a hard kill.
+
+
+[v4.2.1](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C4.2.1%7Cjar) (12-Feb-2017)
+
+Patch: streamlined convenience methods for adding recipients.
+
+
+[v4.2.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C4.2.0%7Cjar) (12-Feb-2017)
+
+**Major feature: Using the EmailConverter you can now [convert between](https://www.simplejavamail.org/features.html#section-converting) Outlook .msg, EML, MimeMessage and Email**!
+
+- [#66](https://github.com/bbottema/simple-java-mail/issues/66): Feature: [convert](https://www.simplejavamail.org/features.html#section-converting) email to EML
+- [#65](https://github.com/bbottema/simple-java-mail/issues/65): Feature: [read outlook messages](https://www.simplejavamail.org/modules.html#outlook-module) from .msg file
+- [#64](https://github.com/bbottema/simple-java-mail/issues/64): **Feature: Added support for [logging-only mode](https://www.simplejavamail.org/debugging.html#section-debug-mode) that skips the actual sending of emails**
+- [#63](https://github.com/bbottema/simple-java-mail/issues/63): Feature: Already including in previous patch update: [Spring support](https://www.simplejavamail.org/modules.html#spring-module) (read properties from Spring context)
+- [#69](https://github.com/bbottema/simple-java-mail/issues/69): Enhancement: Expanded EmailBuilder API to inlude more options for setting (multiple) recipients
+- [#70](https://github.com/bbottema/simple-java-mail/issues/70): Enhancement: Most public API now have defensive null-checks for required fields (Fail Fast support)
+- [#68](https://github.com/bbottema/simple-java-mail/issues/68): Bugfix: Name should be required for embedded images (added safeguards)
+- [#67](https://github.com/bbottema/simple-java-mail/issues/67): Bugfix: Error when name was omitted for attachment
+- minor: added methods on AttachmentResource that reads back the content as (encoded) String
+- other: internal testing is now done using Wiser SMTP test server for testing live sending emails
+
+**Note**: Starting this release, there will always be a Java6 compatible release as well versioned: "x.y.z-java6-release"
+
+
+[v4.1.3](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C4.1.3%7Cjar) (28-Jan-2017)
+
+- [#61](https://github.com/bbottema/simple-java-mail/issues/61): Feature: Add support for [providing your own Properties](https://www.simplejavamail.org/features.html#section-custom-properties) object
+- [#63](https://github.com/bbottema/simple-java-mail/issues/63): **Feature: [Spring support](https://www.simplejavamail.org/modules.html#spring-module) (read properties from Spring context)**
+- [#58](https://github.com/bbottema/simple-java-mail/issues/58): Bugfix: Add support for non-English attachment and embedded image names
+- [#62](https://github.com/bbottema/simple-java-mail/issues/62): Bugfix: Empty properties loaded from config should be considered null
+
+**NOTE**: ConfigLoader moved from `/internal/util` to `/util`
+
+
+[v4.1.2](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C4.1.2%7Cjar) (07-Nov-2016)
+
+- [#52](https://github.com/bbottema/simple-java-mail/issues/52): bug fix for windows / linux disparity when checking socket status
+- [#56](https://github.com/bbottema/simple-java-mail/issues/56): bug fix for IOException when signing dkim with a File reference
+
+
+[v4.1.1](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C4.1.1%7Cjar) (30-Jul-2016)
+
+- [#50](https://github.com/bbottema/simple-java-mail/issues/50): bug fix for manual naming datasources
+
+
+[v4.1.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C4.1.0%7Cjar) (22-Jul-2016)
+
+- [#48](https://github.com/bbottema/simple-java-mail/issues/48): Added programmatic support trusting hosts for SSL connections
+- [#47](https://github.com/bbottema/simple-java-mail/issues/47): Honor given names, deduce extension from datasource name, and more robust support for parsing mimemessages
+
+
+[v4.0.0](https://search.maven.org/#artifactdetails%7Corg.simplejavamail%7Csimple-java-mail%7C4.0.0%7Cjar) (05-Jul-2016)
+
+- [#41](https://github.com/bbottema/simple-java-mail/issues/41): added support for fast parallel batch processing
+- [#42](https://github.com/bbottema/simple-java-mail/issues/42): **added support for config files**
+- [#43](https://github.com/bbottema/simple-java-mail/issues/43): removed logging implementation dependencies from distribution and documented various sample configs
+- [#39](https://github.com/bbottema/simple-java-mail/issues/39): simplified and renamed packages to reflect the domain name of the new website: [simplejavamail.org](https://www.simplejavamail.org)
+- [#38](https://github.com/bbottema/simple-java-mail/issues/38): added support for anonymous proxy
+- [#38](https://github.com/bbottema/simple-java-mail/issues/38): **added support for authenticated proxy**
+
+**NOTE**: All packages have been renamed to "org.simplejavamail.(..)"
+**NOTE**: Switched to Java 7
+
+
+[v3.1.1](https://search.maven.org/#artifactdetails%7Corg.codemonkey.simplejavamail%7Csimple-java-mail%7C3.1.1%7Cjar) (11-May-2016)
+
+**Major feature: DKIM support**!
+
+- [#36](https://github.com/bbottema/simple-java-mail/issues/36): Added proper toString and equals methods for the Email classes
+- [#33](https://github.com/bbottema/simple-java-mail/issues/33): Added [support for DKIM](https://www.simplejavamail.org/features.html#section-dkim) domain key signing
+
+*NOTE*: this is the last release still using Java 6. Next release will be using Java 7.
+/edit: starting with 4.2.0 every release will now have a "x.y.z-java6-release" release as well
+
+
+[v3.0.2](https://search.maven.org/#artifactdetails%7Corg.codemonkey.simplejavamail%7Csimple-java-mail%7C3.0.2%7Cjar) (07-May-2016)
+
+- [#35](https://github.com/bbottema/simple-java-mail/issues/35): added proper .equals() and .toString() methods
+- [#34](https://github.com/bbottema/simple-java-mail/issues/34): Fixed bug when disposition is missing (assume it is an attachment)
+- other: added findbugs support internally
+
+
+[v3.0.1](https://search.maven.org/#artifactdetails%7Corg.codemonkey.simplejavamail%7Csimple-java-mail%7C3.0.1%7Cjar) (29-Feb-2016)
+
+ * [#31](https://github.com/bbottema/simple-java-mail/issues/31): Fixed EmailAddressCriteria.DEFAULT and clarified Javadoc
+
+
+[v3.0.0](https://search.maven.org/#artifactdetails%7Corg.codemonkey.simplejavamail%7Csimple-java-mail%7C3.0.0%7Cjar) (26-Feb-2016)
+
+ * [#30](https://github.com/bbottema/simple-java-mail/issues/30): Improved the demonstration class to include attachments and embedded images
+ * [#29](https://github.com/bbottema/simple-java-mail/issues/29): The package has been restructured for future maintenance, breaking backwards compatibility
+ * [#28](https://github.com/bbottema/simple-java-mail/issues/28): Re-added improved email validation facility
+ * [#22](https://github.com/bbottema/simple-java-mail/issues/22): Added conversion to and from MimeMessage. You can now consume and produce MimeMessage objects with simple-java-mail
+
+
+[v2.5.1](https://search.maven.org/#artifactdetails%7Corg.codemonkey.simplejavamail%7Csimple-java-mail%7C2.5.1%7Cjar) (19-Jan-2016)
+
+ * [#25](https://github.com/bbottema/simple-java-mail/issues/25): Added finally clause that will always close socket properly in case of an exception
+
+
+[v2.5](https://search.maven.org/#artifactdetails%7Corg.codemonkey.simplejavamail%7Csimple-java-mail%7C2.5%7Cjar) (19-Jan-2016)
+
+ * [#24](https://github.com/bbottema/simple-java-mail/issues/24): Updated dependencies SLF4J to 1.7.13 and switched to the updated javax mail package com.sun.mail:javax.mail 1.5.5
+
+
+[v2.4](https://search.maven.org/#artifactdetails%7Corg.codemonkey.simplejavamail%7Csimple-java-mail%7C2.4%7Cjar) (12-Aug-2015)
+
+ * [#21](https://github.com/bbottema/simple-java-mail/issues/21): builder API uses CC and BCC recipient types incorrectly
+
+
+[v2.3](https://search.maven.org/#artifactdetails%7Corg.codemonkey.simplejavamail%7Csimple-java-mail%7C2.3%7Cjar) (21-Jul-2015)
+
+ * [#19](https://github.com/bbottema/simple-java-mail/issues/19): supporting custom Session Properties now and emergency access to internal Session object.
+
+
+[v2.2](https://search.maven.org/#artifactdetails%7Corg.codemonkey.simplejavamail%7Csimple-java-mail%7C2.2%7Cjar) (09-May-2015)
+
+ * [#3](https://github.com/bbottema/simple-java-mail/issues/3): turned off email regex validation by default, with the option to turn it back on
+ * [#7](https://github.com/bbottema/simple-java-mail/issues/7): fixed NullPointerException when using your own Session instance
+ * [#10](https://github.com/bbottema/simple-java-mail/issues/10): properly UTF-8 encode recipient addresses
+ * [#14](https://github.com/bbottema/simple-java-mail/issues/14): switched to [SLF4J](https://www.slf4j.org/), so you can easily use your own selected logging framework
+ * [#17](https://github.com/bbottema/simple-java-mail/issues/17): Added [fluent interface](https://en.wikipedia.org/wiki/Builder_pattern) for building emails (see [here](https://www.simplejavamail.org/#section-builder-api) for an example)
+
+
+[v2.1](https://search.maven.org/#artifactdetails%7Corg.codemonkey.simplejavamail%7Csimple-java-mail%7C2.1%7Cjar) (09-Aug-2012)
+
+ * fixed character encoding for reply-to, from, to, body text and headers (to UTF-8)
+ * fixed bug where Recipient was not public resulting in uncompilable code when calling email.getRecipients()
+
+
+[v2.0](https://search.maven.org/#artifactdetails%7Corg.codemonkey.simplejavamail%7Csimple-java-mail%7C2.0%7Cjar) (20-Aug-2011)
+
+ * added support for adding open headers, such as 'X-Priority: 2'
+
+
+[v1.9.1](https://search.maven.org/#artifactdetails%7Corg.codemonkey.simplejavamail%7Csimple-java-mail%7C1.9.1%7Cjar) (08-Aug-2011)
+
+ * updated for Maven support
+
+
+v1.9 (6-Aug-2011)
+
+ * added support for JavaMail's reply-to address
+ * made port optional as to support port defaulting based on protocol
+ * added transport strategy default in the createSession method
+ * tightened up thrown exceptions (MailException instead of RuntimeException)
+ * added and fixed [Javadoc](https://www.javadoc.io/doc/org.simplejavamail/simple-java-mail)
+
+
+v1.8
+
+ * Added support for TLS (tested with gmail)
+
+
+v1.7 (22-Mar-2011)
+
+Added support for SSL! (tested with gmail)
+
+ * improved argument validation when creating a Mailer without preconfigured Session instance
+
+known possible issue: SSL self-signed certificates might not work (yet). Please let me know by e-mail or create a new issue
+
+
+v1.6
+
+Completed migration to Java Simple Mail project.
+
+ * removed all Vesijama references
+ * updated TestMail demonstration class for clarification
+ * updated readme.txt for test run instructions
+ * included log4j.properties
+
+
+v1.4 (15-Jan-2011)
+
+
+vX.X (26-Apr-2009)
+
+ * Initial upload to Google Code.
diff --git a/how to release.txt b/how to release.txt
index b9400e785..302899ba8 100644
--- a/how to release.txt
+++ b/how to release.txt
@@ -1,30 +1,72 @@
- 1. update release notes and github readme page (don't commit)
- 2. remove SNAPSHOT version
- 3. mvn -DperformRelease=true clean deploy
+Prerequisite:
+
+You need GPG installed (comes along with GIT installation in the \bin folder) and you need to create a keyring, used for signing artifacts.
+If you have an existing key, simply import it:
+
+gpg --allow-secret-key-import --import .gpg
+
+That, or you can install one of the binaries to import the key, from here: https://www.gnupg.org/download/index.html
+
+To import the keyring in a CI/CD environment (CircleCI, Azure Devops, Bamboo, Jenkins etc.), you need to import the keyring
+externally (or us a private Repo). Alternatively you can import the ASCII export of the key configured as a property secret
+so you can ECHO it to a file during the build. Once exposed as file, you can use it to sign:
+
+To generate the ASCII export of a key so you can store it a secret property in your build environment:
+ - gpg --no-default-keyring --armor --secret-keyring ./secring.gpg --keyring ./pubring.gpg --export-secret-key USER > secring.gpg.asc
+ - sed ':a;N;$!ba;s/\n/\\n/g' secring.gpg.asc, OR
+ - base64 encode the text
+
+To be able to have your CI/CD script also commit the new version, you need to add github.com's official host finger print.
+ - https://serverfault.com/a/701637/157338
+ - https://discuss.circleci.com/t/error-sending-to-agent-inappropriate-ioctl-for-device/17465/2
+ - https://medium.com/@Joachim8675309/generating-gpg-key-files-cc447431415a
+ - http://frankiesardo.github.io/posts/2015-04-19-automate-everything-with-circleci.html
+ - https://github.com/toknapp/arweave4s/blob/master/.circleci/config.yml
+ - https://serverfault.com/questions/447028/non-interactive-git-clone-ssh-fingerprint-prompt
+ - https://superuser.com/questions/232373/how-to-tell-git-which-private-key-to-use
+
+To release through CI/CD pipeline in CircleCI
+
+ 1. run the defensive Dependabot sweep from MAINTAINER_WORKFLOW.md, merge safe patch-level library updates, and rerun full JDK 8 verification
+ 2. update release notes, RELEASE_HISTORY.md and the github readme page according to MAINTAINER_WORKFLOW.md, then push changes
+ 3. create or reuse the exact-version GitHub milestone with the planned release date as its due date, assign all release issues/PRs (including accounted-for closed Dependabot work), and cross-check the release-note links
+ 4. go to the CI/CD pipeline https://circleci.com/gh/bbottema/simple-java-mail
+ 5. select the version release needed
+ 6. after a successful release, verify every milestone item is closed, replace the milestone due date with the actual published release date, and close the milestone
+
+To release manually:
+
+ 1. run the defensive Dependabot sweep from MAINTAINER_WORKFLOW.md, merge safe patch-level library updates, and rerun full JDK 8 verification
+ 2. update release notes, RELEASE_HISTORY.md and the github readme page according to MAINTAINER_WORKFLOW.md (don't commit)
+ 3. create or reuse the exact-version GitHub milestone with the planned release date as its due date, assign all release issues/PRs (including accounted-for closed Dependabot work), and cross-check the release-note links
+ 4. remove SNAPSHOT version from the property
+ 5. mvn -DperformRelease=true clean deploy
(set password in settings.xml or use local pgp key password, for which the public key must have been sent to a public key server,
eg: gpg --keyserver hkp://keyserver.ubuntu.com --send-keys 05AC6403)
server needed in settings.xml (see below)
- 4. add new SNAPSHOT version and commit everything
-
-maven's settings.xml:
-
-
- ossrh
- sonatype user
- sonatype password
-
-
-To have a global gpg password so that it will use that automatically:
-
-
-
- gpg
-
- gpg
- letmein
-
-
-
-
- gpg
-
\ No newline at end of file
+ 6. Go to https://oss.sonatype.org and release the artifact so it is submitted to Maven Central
+ 7. set the exact-version milestone due date to the actual published release date and close it after every included issue and PR is closed
+ 8. add new SNAPSHOT version to the property, execute and commit everything
+
+ maven's settings.xml:
+
+
+ ossrh
+ sonatype user
+ sonatype password
+
+
+ To have a global gpg password so that it will use that automatically:
+
+
+
+ gpg
+
+ gpg
+ password
+
+
+
+
+ gpg
+
diff --git a/lib/activation.jar b/lib/activation.jar
deleted file mode 100644
index 29a59a9ee..000000000
Binary files a/lib/activation.jar and /dev/null differ
diff --git a/lib/mailapi-1.4.4.jar b/lib/mailapi-1.4.4.jar
deleted file mode 100644
index fc37e895b..000000000
Binary files a/lib/mailapi-1.4.4.jar and /dev/null differ
diff --git a/lib/smtp-1.4.4.jar b/lib/smtp-1.4.4.jar
deleted file mode 100644
index 90d4794de..000000000
Binary files a/lib/smtp-1.4.4.jar and /dev/null differ
diff --git a/modules/authenticated-socks-module/.gitignore b/modules/authenticated-socks-module/.gitignore
new file mode 100644
index 000000000..dc48b22fc
--- /dev/null
+++ b/modules/authenticated-socks-module/.gitignore
@@ -0,0 +1,2 @@
+*.iml
+target
\ No newline at end of file
diff --git a/LICENSE-2.0.txt b/modules/authenticated-socks-module/LICENSE-2.0.txt
similarity index 94%
rename from LICENSE-2.0.txt
rename to modules/authenticated-socks-module/LICENSE-2.0.txt
index 4ad04c3c0..d639b1e24 100644
--- a/LICENSE-2.0.txt
+++ b/modules/authenticated-socks-module/LICENSE-2.0.txt
@@ -185,18 +185,4 @@
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright 2016 Benny Bottema
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ identification within third-party archives.
\ No newline at end of file
diff --git a/modules/authenticated-socks-module/NOTICE.txt b/modules/authenticated-socks-module/NOTICE.txt
new file mode 100644
index 000000000..695f76510
--- /dev/null
+++ b/modules/authenticated-socks-module/NOTICE.txt
@@ -0,0 +1,11 @@
+ =========================================================================
+ == NOTICE file for use with the Apache License, Version 2.0 ==
+ =========================================================================
+
+ Simple Java Mail - Authenticated SOCKS module
+ Copyright (C) 2009 Benny Bottema (benny@bennybottema.com)
+ https://www.simplejavamail.org
+
+ This product uses no commercial products.
+
+ This product uses no external (Open Source) libraries:
diff --git a/modules/authenticated-socks-module/pom.xml b/modules/authenticated-socks-module/pom.xml
new file mode 100644
index 000000000..74cf0710b
--- /dev/null
+++ b/modules/authenticated-socks-module/pom.xml
@@ -0,0 +1,31 @@
+
+
+
+ 4.0.0
+
+
+ org.simplejavamail
+ maven-master-project
+ 9.2.0
+ ../../pom.xml
+
+
+ authenticated-socks-module
+ jar
+ Simple Java Mail - Authenticated SOCKS module
+ Simple API, Complex Emails. Now with SOCKS5 support
+
+
+ org.simplejavamail.authenticatedsocks
+
+
+
+
+
+ org.simplejavamail
+ core-module
+ ${project.version}
+
+
+
diff --git a/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/AuthenticatedSocksHelper.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/AuthenticatedSocksHelper.java
new file mode 100644
index 000000000..6717516bd
--- /dev/null
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/AuthenticatedSocksHelper.java
@@ -0,0 +1,19 @@
+package org.simplejavamail.internal.authenticatedsockssupport;
+
+import org.jetbrains.annotations.NotNull;
+import org.simplejavamail.api.internal.authenticatedsockssupport.socks5server.AnonymousSocks5Server;
+import org.simplejavamail.api.mailer.config.ProxyConfig;
+import org.simplejavamail.internal.authenticatedsockssupport.socks5server.AnonymousSocks5ServerImpl;
+import org.simplejavamail.internal.modules.AuthenticatedSocksModule;
+
+import static org.simplejavamail.internal.util.Preconditions.checkNonEmptyArgument;
+
+@SuppressWarnings("unused")
+public class AuthenticatedSocksHelper implements AuthenticatedSocksModule {
+
+ @Override
+ public AnonymousSocks5Server createAnonymousSocks5Server(@NotNull ProxyConfig socksProxyConfig) {
+ final Integer proxyBridgePort = checkNonEmptyArgument(socksProxyConfig.getProxyBridgePort(), "proxyBridgePort");
+ return new AnonymousSocks5ServerImpl(new AuthenticatingSocks5Bridge(socksProxyConfig), proxyBridgePort);
+ }
+}
\ No newline at end of file
diff --git a/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/AuthenticatingSocks5Bridge.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/AuthenticatingSocks5Bridge.java
new file mode 100644
index 000000000..42d0eb47e
--- /dev/null
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/AuthenticatingSocks5Bridge.java
@@ -0,0 +1,60 @@
+package org.simplejavamail.internal.authenticatedsockssupport;
+
+import org.jetbrains.annotations.NotNull;
+import org.simplejavamail.api.internal.authenticatedsockssupport.common.Socks5Bridge;
+import org.simplejavamail.api.mailer.config.ProxyConfig;
+import org.simplejavamail.internal.authenticatedsockssupport.socks5client.ProxyCredentials;
+import org.simplejavamail.internal.authenticatedsockssupport.socks5client.Socks5;
+import org.simplejavamail.internal.authenticatedsockssupport.socks5client.SocksSocket;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import static org.simplejavamail.internal.util.Preconditions.verifyNonnullOrEmpty;
+
+/**
+ * Please refer to {@link Socks5Bridge}.
+ */
+public class AuthenticatingSocks5Bridge implements Socks5Bridge {
+ private static final Logger LOGGER = LoggerFactory.getLogger("socks5bridge");
+
+ @NotNull private final ProxyConfig proxyConfig;
+ @NotNull private final String remoteProxyHost;
+ @NotNull private final Integer remoteProxyPort;
+
+ AuthenticatingSocks5Bridge(final ProxyConfig proxyConfig) {
+ this.proxyConfig = proxyConfig;
+ this.remoteProxyHost = verifyNonnullOrEmpty(proxyConfig.getRemoteProxyHost());
+ this.remoteProxyPort = verifyNonnullOrEmpty(proxyConfig.getRemoteProxyPort());
+ }
+
+ /**
+ * Refer to {@link Socks5Bridge#connect(String, InetAddress, int)}.
+ */
+ @Override
+ public Socket connect(final String sessionId, final InetAddress remoteServerAddress, final int remoteServerPort)
+ throws IOException {
+ return proxyConfig.requiresAuthentication() ?
+ createSocketAuthenticated(sessionId, remoteServerAddress, remoteServerPort) :
+ createSocketPlainAnonymous(sessionId, remoteServerAddress, remoteServerPort);
+ }
+
+ private Socket createSocketAuthenticated(final String sessionId, final InetAddress remoteServerAddress, final int remoteServerPort)
+ throws IOException {
+ LOGGER.info("SESSION[{}] bridging to remote proxy {}", sessionId, proxyConfig);
+ final Socks5 proxyAuth = new Socks5(new InetSocketAddress(remoteProxyHost, remoteProxyPort));
+ proxyAuth.setCredentials(new ProxyCredentials(proxyConfig.getUsername(), proxyConfig.getPassword()));
+ return new SocksSocket(proxyAuth, proxyAuth.createProxySocket(), new InetSocketAddress(remoteServerAddress, remoteServerPort));
+ }
+
+ private Socket createSocketPlainAnonymous(final String sessionId, final InetAddress remoteServerAddress, final int remoteServerPort)
+ throws IOException {
+ LOGGER.info("SESSION[{}] bridging anonymously to remote proxy {}:{}", sessionId, remoteProxyHost, remoteProxyPort);
+ final Socks5 socksProxyAnonymous = new Socks5(new InetSocketAddress(remoteProxyHost, remoteProxyPort));
+ return new SocksSocket(socksProxyAnonymous, new InetSocketAddress(remoteServerAddress, remoteServerPort));
+ }
+}
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/common/SocksException.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/common/SocksException.java
similarity index 62%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/common/SocksException.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/common/SocksException.java
index 89102d4dc..63f28611a 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/common/SocksException.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/common/SocksException.java
@@ -1,12 +1,11 @@
+package org.simplejavamail.internal.authenticatedsockssupport.common;
-
-package org.simplejavamail.mailer.internal.socks.common;
-
+@SuppressWarnings("serial")
public class SocksException extends RuntimeException {
-
- private static final String serverReplyMessage[] = { "General SOCKS server failure", "Connection not allowed by ruleset",
+
+ private static final String[] serverReplyMessage = {"General SOCKS server failure", "Connection not allowed by ruleset",
"Network " + "unreachable", "Host unreachable", "Connection refused", "TTL expired", "Command not " + "supported",
- "Address type not supported" };
+ "Address type not supported"};
public SocksException(final String msg) {
super(msg);
@@ -17,13 +16,12 @@ public SocksException(final String msg, final Exception e) {
}
public static SocksException serverReplyException(final byte reply) {
- int code = reply;
- code = code & 0xff;
- if (code < 0 || code > 0x08) {
+ int code = reply & 0xff;
+ if (code > 0x08) {
return new SocksException("Unknown reply");
}
code = code - 1;
return new SocksException(serverReplyMessage[code]);
}
-}
+}
\ No newline at end of file
diff --git a/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/KeyStoreInfo.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/KeyStoreInfo.java
new file mode 100644
index 000000000..ab3cbb44c
--- /dev/null
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/KeyStoreInfo.java
@@ -0,0 +1,42 @@
+package org.simplejavamail.internal.authenticatedsockssupport.socks5client;
+
+import static java.util.Objects.requireNonNull;
+
+@SuppressWarnings("SameParameterValue")
+class KeyStoreInfo {
+
+ private final String keyStorePath;
+ private final String password;
+ private final String type /*= "JKS"*/;
+
+ /**
+ * Delegates to {@link #KeyStoreInfo(String, String, String)} using "JKS" as type.
+ */
+ @SuppressWarnings("unused")
+ public KeyStoreInfo(final String keyStorePath, final String password) {
+ this(keyStorePath, password, "JKS");
+ }
+
+ public KeyStoreInfo(final String keyStorePath, final String password, final String type) {
+ this.keyStorePath = requireNonNull(keyStorePath, "Argument [keyStorePath] may not be null");
+ this.password = requireNonNull(password, "Argument [password] may not be null");
+ this.type = requireNonNull(type, "Argument [type] may not be null");
+ }
+
+ public String getKeyStorePath() {
+ return keyStorePath;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return "[KEY STORE] PATH:" + keyStorePath + " PASSWORD:xxx" + " TYPE:" + type;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/ProxyCredentials.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/ProxyCredentials.java
similarity index 61%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/ProxyCredentials.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/ProxyCredentials.java
index 04067037c..e832a8ec3 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/ProxyCredentials.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/ProxyCredentials.java
@@ -1,8 +1,6 @@
+package org.simplejavamail.internal.authenticatedsockssupport.socks5client;
-
-package org.simplejavamail.mailer.internal.socks.socks5client;
-
-import org.simplejavamail.internal.util.MiscUtil;
+import static java.util.Objects.requireNonNull;
public class ProxyCredentials {
@@ -15,7 +13,7 @@ public ProxyCredentials() {
public ProxyCredentials(final String username, final String password) {
this.username = username;
- this.password = MiscUtil.checkNotNull(password, "Argument [password] may not be null");
+ this.password = requireNonNull(password, "Argument [password] may not be null");
}
public String getUsername() {
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SSLConfiguration.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SSLConfiguration.java
similarity index 72%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SSLConfiguration.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SSLConfiguration.java
index 2f1d17b2f..fecce60f6 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SSLConfiguration.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SSLConfiguration.java
@@ -1,9 +1,6 @@
+package org.simplejavamail.internal.authenticatedsockssupport.socks5client;
-
-package org.simplejavamail.mailer.internal.socks.socks5client;
-
-import org.simplejavamail.mailer.internal.socks.common.SocksException;
-import org.simplejavamail.internal.util.MiscUtil;
+import org.simplejavamail.internal.authenticatedsockssupport.common.SocksException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -13,9 +10,16 @@
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.io.IOException;
+import java.security.KeyManagementException;
import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+import static java.util.Objects.requireNonNull;
-public class SSLConfiguration {
+class SSLConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(SSLConfiguration.class);
@@ -23,15 +27,15 @@ public class SSLConfiguration {
private final KeyStoreInfo trustKeyStoreInfo;
@SuppressWarnings("SameParameterValue")
- public SSLConfiguration(final KeyStoreInfo keyStoreInfo, final KeyStoreInfo trustKeyStoreInfo) {
+ private SSLConfiguration(final KeyStoreInfo keyStoreInfo, final KeyStoreInfo trustKeyStoreInfo) {
this.keyStoreInfo = keyStoreInfo;
this.trustKeyStoreInfo = trustKeyStoreInfo;
}
public SSLSocketFactory getSSLSocketFactory()
throws SocksException {
- MiscUtil.checkNotNull(trustKeyStoreInfo, "trustKeyStoreInfo may not be null");
- FileInputStream s1 = null;
+ requireNonNull(trustKeyStoreInfo, "trustKeyStoreInfo may not be null");
+ FileInputStream s1 = null;
FileInputStream s2 = null;
try {
final SSLContext context = SSLContext.getInstance("SSL");
@@ -57,9 +61,8 @@ public SSLSocketFactory getSSLSocketFactory()
}
LOGGER.info("SSL: Trust key store:{}", trustKeyStoreInfo.getKeyStorePath());
return context.getSocketFactory();
- } catch (final Exception e) {
- LOGGER.error(e.getMessage(), e);
- throw new SocksException(e.getMessage());
+ } catch (IOException | KeyManagementException | KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException e) {
+ throw new SocksException(e.getMessage(), e);
} finally {
tryCloseStream(s1);
tryCloseStream(s2);
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SSLSocks5.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SSLSocks5.java
similarity index 94%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SSLSocks5.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SSLSocks5.java
index 9c85b8d71..c98aca3b7 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SSLSocks5.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SSLSocks5.java
@@ -1,6 +1,4 @@
-
-
-package org.simplejavamail.mailer.internal.socks.socks5client;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5client;
import java.io.IOException;
import java.net.InetAddress;
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/Socks5.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/Socks5.java
similarity index 77%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/Socks5.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/Socks5.java
index 04672cc9d..0a434769e 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/Socks5.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/Socks5.java
@@ -1,42 +1,40 @@
+package org.simplejavamail.internal.authenticatedsockssupport.socks5client;
-
-package org.simplejavamail.mailer.internal.socks.socks5client;
+import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.*;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+
+import static java.util.Objects.requireNonNull;
public class Socks5 {
private static final int SOCKS_DEFAULT_PORT = 1080;
- public static final byte AUTHENTICATION_SUCCEEDED = 0x00;
+ static final byte AUTHENTICATION_SUCCEEDED = 0x00;
+ @Nullable
private Socks5 chainProxy;
private ProxyCredentials credentials = new ProxyCredentials();
private InetAddress inetAddress;
+ @SuppressWarnings("UnusedAssignment")
private int port = SOCKS_DEFAULT_PORT;
+ @Nullable
private Socket proxySocket;
private SocksAuthenticationHelper socksAuthenticationHelper = new SocksAuthenticationHelper();
private boolean alwaysResolveAddressLocally = false;
- public Socks5(final InetSocketAddress socketAddress, final String username, final String password) {
- this(socketAddress);
- setCredentials(new ProxyCredentials(username, password));
- }
-
- public Socks5(final String host, final int port)
- throws UnknownHostException {
- this(InetAddress.getByName(host), port);
- }
-
Socks5(final InetAddress inetAddress, final int port) {
this(new InetSocketAddress(inetAddress, port));
}
@@ -46,19 +44,12 @@ public Socks5(final InetSocketAddress socketAddress) {
}
@SuppressWarnings("SameParameterValue")
- private Socks5(final Socks5 chainProxy, final InetSocketAddress socketAddress) {
+ private Socks5(@Nullable final Socks5 chainProxy, final InetSocketAddress socketAddress) {
inetAddress = socketAddress.getAddress();
port = socketAddress.getPort();
this.setChainProxy(chainProxy);
}
- public Socks5(final String host, final int port, final ProxyCredentials credentials)
- throws UnknownHostException {
- this.inetAddress = InetAddress.getByName(host);
- this.port = port;
- this.credentials = credentials;
- }
-
public void buildConnection()
throws IOException {
if (inetAddress == null) {
@@ -79,23 +70,23 @@ public void requestConnect(final String host, final int port)
throws IOException {
if (!alwaysResolveAddressLocally) {
// resolve address in SOCKS server
- SocksCommandSender.send(proxySocket, host, port);
+ SocksCommandSender.send(requireNonNull(proxySocket, "proxySocket"), host, port);
} else {
// resolve address in local.
final InetAddress address = InetAddress.getByName(host);
- SocksCommandSender.send(proxySocket, address, port);
+ SocksCommandSender.send(requireNonNull(proxySocket, "proxySocket"), address, port);
}
}
public void requestConnect(final InetAddress address, final int port)
throws IOException {
- SocksCommandSender.send(proxySocket, address, port);
+ SocksCommandSender.send(requireNonNull(proxySocket, "proxySocket"), address, port);
}
public void requestConnect(final SocketAddress address)
throws IOException {
- SocksCommandSender.send(proxySocket, address);
+ SocksCommandSender.send(requireNonNull(proxySocket, "proxySocket"), address);
}
public int getPort() {
@@ -107,22 +98,23 @@ Socks5 setPort(final int port) {
return this;
}
+ @Nullable
public Socket getProxySocket() {
return proxySocket;
}
- public void setProxySocket(final Socket proxySocket) {
+ public void setProxySocket(@Nullable final Socket proxySocket) {
this.proxySocket = proxySocket;
}
public InputStream getInputStream()
throws IOException {
- return proxySocket.getInputStream();
+ return requireNonNull(proxySocket, "proxySocket").getInputStream();
}
public OutputStream getOutputStream()
throws IOException {
- return proxySocket.getOutputStream();
+ return requireNonNull(proxySocket, "proxySocket").getOutputStream();
}
public ProxyCredentials getCredentials() {
@@ -150,11 +142,12 @@ public Socks5 copy() {
return socks5;
}
+ @Nullable
public Socks5 getChainProxy() {
return chainProxy;
}
- Socks5 setChainProxy(final Socks5 chainProxy) {
+ Socks5 setChainProxy(@Nullable final Socks5 chainProxy) {
this.chainProxy = chainProxy;
return this;
}
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SocksAuthenticationHelper.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SocksAuthenticationHelper.java
similarity index 84%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SocksAuthenticationHelper.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SocksAuthenticationHelper.java
index dcbe99277..0877a299f 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SocksAuthenticationHelper.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SocksAuthenticationHelper.java
@@ -1,8 +1,6 @@
+package org.simplejavamail.internal.authenticatedsockssupport.socks5client;
-
-package org.simplejavamail.mailer.internal.socks.socks5client;
-
-import org.simplejavamail.mailer.internal.socks.common.SocksException;
+import org.simplejavamail.internal.authenticatedsockssupport.common.SocksException;
import org.simplejavamail.internal.util.MiscUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -13,6 +11,7 @@
import java.net.Socket;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
final class SocksAuthenticationHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(SocksAuthenticationHelper.class);
@@ -40,12 +39,12 @@ public static boolean shouldAuthenticate(final Socket socket)
outputStream.write(bufferSent);
outputStream.flush();
- LOGGER.trace("{}", MiscUtil.buildLogString(bufferSent, false));
+ LOGGER.trace("{}", MiscUtil.buildLogStringForSOCKSCommunication(bufferSent, false));
// Received data.
final InputStream inputStream = socket.getInputStream();
final byte[] receivedData = read2Bytes(inputStream);
- LOGGER.trace("{}", MiscUtil.buildLogString(receivedData, true));
+ LOGGER.trace("{}", MiscUtil.buildLogStringForSOCKSCommunication(receivedData, true));
if (receivedData[0] != (int) SOCKS_VERSION) {
throw new SocksException("Remote server don't support SOCKS5");
}
@@ -58,8 +57,8 @@ public static boolean shouldAuthenticate(final Socket socket)
public static void performUserPasswordAuthentication(final Socks5 socksProxy)
throws IOException {
- MiscUtil.checkNotNull(socksProxy, "Argument [socksProxy] may not be null");
- final ProxyCredentials credentials = socksProxy.getCredentials();
+ requireNonNull(socksProxy, "Argument [socksProxy] may not be null");
+ final ProxyCredentials credentials = socksProxy.getCredentials();
if (credentials == null) {
throw new SocksException("Need Username/Password authentication");
}
@@ -83,12 +82,12 @@ public static void performUserPasswordAuthentication(final Socks5 socksProxy)
outputStream.write(bufferSent);
outputStream.flush();
// logger send bytes
- LOGGER.trace("{}", MiscUtil.buildLogString(bufferSent, false));
+ LOGGER.trace("{}", MiscUtil.buildLogStringForSOCKSCommunication(bufferSent, false));
final byte[] authenticationResult = new byte[2];
checkEnd(inputStream.read(authenticationResult));
// logger
- LOGGER.trace("{}", MiscUtil.buildLogString(authenticationResult, true));
+ LOGGER.trace("{}", MiscUtil.buildLogStringForSOCKSCommunication(authenticationResult, true));
if (authenticationResult[1] != Socks5.AUTHENTICATION_SUCCEEDED) {
// Close connection if authentication is failed.
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SocksCommandSender.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SocksCommandSender.java
similarity index 82%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SocksCommandSender.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SocksCommandSender.java
index 186631004..4b1dc2c7e 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SocksCommandSender.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SocksCommandSender.java
@@ -1,9 +1,8 @@
+package org.simplejavamail.internal.authenticatedsockssupport.socks5client;
-
-package org.simplejavamail.mailer.internal.socks.socks5client;
-
+import org.jetbrains.annotations.NotNull;
+import org.simplejavamail.internal.authenticatedsockssupport.common.SocksException;
import org.simplejavamail.internal.util.MiscUtil;
-import org.simplejavamail.mailer.internal.socks.common.SocksException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -39,12 +38,12 @@ final class SocksCommandSender {
private static final byte ATYPE_IPV6 = 0x04;
private static final int REP_SUCCEEDED = 0x00;
- public static void send(final Socket socket, final InetAddress address, final int port)
+ public static void send(@NotNull final Socket socket, final InetAddress address, final int port)
throws IOException {
send(socket, new InetSocketAddress(address, port));
}
- public static void send(final Socket socket, final SocketAddress socketAddress)
+ public static void send(@NotNull final Socket socket, final SocketAddress socketAddress)
throws IOException {
if (!(socketAddress instanceof InetSocketAddress)) {
throw new IllegalArgumentException("Unsupported address type");
@@ -79,16 +78,17 @@ public static void send(final Socket socket, final SocketAddress socketAddress)
outputStream.write(bufferSent);
outputStream.flush();
- LOGGER.trace("{}", MiscUtil.buildLogString(bufferSent, false));
+ LOGGER.trace("{}", MiscUtil.buildLogStringForSOCKSCommunication(bufferSent, false));
checkServerReply(inputStream);
}
- public static void send(final Socket socket, final String host, final int port)
- throws IOException {
+ public static void send(@NotNull final Socket socket, final String host, final int port)
+ throws IOException, SocksException {
final InputStream inputStream = socket.getInputStream();
final OutputStream outputStream = socket.getOutputStream();
- final int lengthOfHost = host.getBytes(UTF_8).length;
+ final byte[] bytesOfHost = host.getBytes(UTF_8);
+ final int lengthOfHost = bytesOfHost.length;
final byte[] bufferSent = new byte[7 + lengthOfHost];
bufferSent[0] = SOCKS_VERSION;
@@ -96,20 +96,19 @@ public static void send(final Socket socket, final String host, final int port)
bufferSent[2] = RESERVED;
bufferSent[3] = ATYPE_DOMAINNAME;
bufferSent[4] = (byte) lengthOfHost;
- final byte[] bytesOfHost = host.getBytes(UTF_8);
System.arraycopy(bytesOfHost, 0, bufferSent, 5, lengthOfHost);// copy host bytes.
- bufferSent[5 + host.length()] = (byte) ((port & 0xff00) >> 8);
- bufferSent[6 + host.length()] = (byte) (port & 0xff);
+ bufferSent[5 + lengthOfHost] = (byte) ((port & 0xff00) >> 8);
+ bufferSent[6 + lengthOfHost] = (byte) (port & 0xff);
outputStream.write(bufferSent);
outputStream.flush();
- LOGGER.trace("{}", MiscUtil.buildLogString(bufferSent, false));
+ LOGGER.trace("{}", MiscUtil.buildLogStringForSOCKSCommunication(bufferSent, false));
checkServerReply(inputStream);
}
private static void checkServerReply(final InputStream inputStream)
- throws IOException {
+ throws IOException, SocksException {
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int temp = 0;
for (int i = 0; i < 4; i++) {
@@ -140,7 +139,7 @@ private static void checkServerReply(final InputStream inputStream)
throw new SocksException("Address type not support, type value: " + addressType);
}
final byte[] receivedData = byteArrayOutputStream.toByteArray();
- LOGGER.trace("{}", MiscUtil.buildLogString(receivedData, true));
+ LOGGER.trace("{}", MiscUtil.buildLogStringForSOCKSCommunication(receivedData, true));
final byte[] addressBytes;
final byte[] portBytes = new byte[2];
@@ -161,9 +160,9 @@ private static void checkServerReply(final InputStream inputStream)
int size = receivedData[4];
size = size & 0xFF;
addressBytes = new byte[size];
- System.arraycopy(receivedData, 4, addressBytes, 0, size);
- portBytes[0] = receivedData[4 + size];
- portBytes[1] = receivedData[5 + size];
+ System.arraycopy(receivedData, 5, addressBytes, 0, size);
+ portBytes[0] = receivedData[5 + size];
+ portBytes[1] = receivedData[6 + size];
LOGGER.debug("Server replied:Address as host:{}, port:{}", new String(addressBytes, UTF_8),
(MiscUtil.toInt(portBytes[0]) << 8) | (MiscUtil.toInt(portBytes[1])));
} else if (receivedData[3] == ADDRESS_TYPE_IPV6) {
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SocksSocket.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SocksSocket.java
similarity index 69%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SocksSocket.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SocksSocket.java
index 3495efd9a..0c6122eb2 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5client/SocksSocket.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SocksSocket.java
@@ -1,20 +1,25 @@
+package org.simplejavamail.internal.authenticatedsockssupport.socks5client;
-
-package org.simplejavamail.mailer.internal.socks.socks5client;
-
-import org.simplejavamail.mailer.internal.socks.common.SocksException;
-import org.simplejavamail.internal.util.MiscUtil;
+import org.jetbrains.annotations.NotNull;
+import org.simplejavamail.internal.authenticatedsockssupport.common.SocksException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.*;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
+import static java.util.Objects.requireNonNull;
+
public class SocksSocket extends Socket {
private static final Logger LOGGER = LoggerFactory.getLogger(SocksSocket.class);
@@ -29,9 +34,9 @@ public class SocksSocket extends Socket {
private SocksSocket(final Socks5 proxy, final String remoteServerHost, final int remoteServerPort)
throws IOException {
- this.proxy = MiscUtil.checkNotNull(proxy, "Argument [proxy] may not be null").copy();
+ this.proxy = requireNonNull(proxy, "Argument [proxy] may not be null").copy();
this.proxy.setProxySocket(proxySocket);
- this.remoteServerHost = MiscUtil.checkNotNull(remoteServerHost, "Argument [remoteServerHost] may not be null");
+ this.remoteServerHost = requireNonNull(remoteServerHost, "Argument [remoteServerHost] may not be null");
this.remoteServerPort = remoteServerPort;
this.proxy.buildConnection();
proxySocket = this.proxy.getProxySocket();
@@ -46,9 +51,9 @@ private SocksSocket(final Socks5 proxy, final InetAddress address, final int por
public SocksSocket(final Socks5 proxy, final InetSocketAddress socketAddress)
throws IOException {
- MiscUtil.checkNotNull(proxy, "Argument [proxy] may not be null");
- MiscUtil.checkNotNull(socketAddress, "Argument [socketAddress] may not be null");
- this.proxy = proxy.copy();
+ requireNonNull(proxy, "Argument [proxy] may not be null");
+ requireNonNull(socketAddress, "Argument [socketAddress] may not be null");
+ this.proxy = proxy.copy();
this.remoteServerHost = socketAddress.getHostString();
this.remoteServerPort = socketAddress.getPort();
this.proxy.buildConnection();
@@ -71,9 +76,9 @@ public SocksSocket(final Socks5 proxy, final Socket proxySocket, final InetSocke
@SuppressWarnings("WeakerAccess")
public SocksSocket(final Socks5 proxy, final Socket proxySocket) {
- MiscUtil.checkNotNull(proxy, "Argument [proxy] may not be null");
- MiscUtil.checkNotNull(proxySocket, "Argument [proxySocket] may not be null");
- if (proxySocket.isConnected()) {
+ requireNonNull(proxy, "Argument [proxy] may not be null");
+ requireNonNull(proxySocket, "Argument [proxySocket] may not be null");
+ if (proxySocket.isConnected()) {
throw new IllegalArgumentException("Proxy socket should be unconnected");
}
this.proxySocket = proxySocket;
@@ -95,7 +100,7 @@ private void initProxyChain()
Socks5 pre = proxy;
for (final Socks5 chain : proxyChain) {
pre.requestConnect(chain.getInetAddress(), chain.getPort());
- proxy.getChainProxy().buildConnection();
+ requireNonNull(proxy.getChainProxy(), "chainProxy").buildConnection();
pre = chain;
}
}
@@ -119,7 +124,7 @@ public void connect(final SocketAddress endpoint, final int timeout)
remoteServerHost = ((InetSocketAddress) endpoint).getHostName();
remoteServerPort = ((InetSocketAddress) endpoint).getPort();
- proxy.getProxySocket().setSoTimeout(timeout);
+ getProxySocket().setSoTimeout(timeout);
proxy.buildConnection();
initProxyChain();
proxy.requestConnect(endpoint);
@@ -129,19 +134,19 @@ public void connect(final SocketAddress endpoint, final int timeout)
@Override
public InputStream getInputStream()
throws IOException {
- return proxy.getProxySocket().getInputStream();
+ return getProxySocket().getInputStream();
}
@Override
public OutputStream getOutputStream()
throws IOException {
- return proxy.getProxySocket().getOutputStream();
+ return getProxySocket().getOutputStream();
}
@Override
public void bind(final SocketAddress bindpoint)
throws IOException {
- proxy.getProxySocket().bind(bindpoint);
+ getProxySocket().bind(bindpoint);
}
@Override
@@ -155,7 +160,7 @@ public InetAddress getInetAddress() {
@Override
public InetAddress getLocalAddress() {
- return proxy.getProxySocket().getLocalAddress();
+ return getProxySocket().getLocalAddress();
}
@SuppressWarnings("SuspiciousGetterSetter")
@@ -166,136 +171,136 @@ public int getPort() {
@Override
public int getLocalPort() {
- return proxy.getProxySocket().getLocalPort();
+ return getProxySocket().getLocalPort();
}
@Override
public SocketAddress getRemoteSocketAddress() {
- return proxy.getProxySocket().getRemoteSocketAddress();
+ return getProxySocket().getRemoteSocketAddress();
}
@Override
public SocketAddress getLocalSocketAddress() {
- return proxy.getProxySocket().getLocalSocketAddress();
+ return getProxySocket().getLocalSocketAddress();
}
@Override
public SocketChannel getChannel() {
- return proxy.getProxySocket().getChannel();
+ return getProxySocket().getChannel();
}
@Override
public boolean getTcpNoDelay()
throws SocketException {
- return proxy.getProxySocket().getTcpNoDelay();
+ return getProxySocket().getTcpNoDelay();
}
@Override
public void setTcpNoDelay(final boolean on)
throws SocketException {
- proxy.getProxySocket().setTcpNoDelay(on);
+ getProxySocket().setTcpNoDelay(on);
}
@Override
public void setSoLinger(final boolean on, final int linger)
throws SocketException {
- proxy.getProxySocket().setSoLinger(on, linger);
+ getProxySocket().setSoLinger(on, linger);
}
@Override
public int getSoLinger()
throws SocketException {
- return proxy.getProxySocket().getSoLinger();
+ return getProxySocket().getSoLinger();
}
@Override
public void sendUrgentData(final int data)
throws IOException {
- proxy.getProxySocket().sendUrgentData(data);
+ getProxySocket().sendUrgentData(data);
}
@Override
public boolean getOOBInline()
throws SocketException {
- return proxy.getProxySocket().getOOBInline();
+ return getProxySocket().getOOBInline();
}
@Override
public void setOOBInline(final boolean on)
throws SocketException {
- proxy.getProxySocket().setOOBInline(on);
+ getProxySocket().setOOBInline(on);
}
@Override
public synchronized int getSoTimeout()
throws SocketException {
- return proxy.getProxySocket().getSoTimeout();
+ return getProxySocket().getSoTimeout();
}
@Override
public synchronized void setSoTimeout(final int timeout)
throws SocketException {
- proxy.getProxySocket().setSoTimeout(timeout);
+ getProxySocket().setSoTimeout(timeout);
}
@Override
public synchronized int getSendBufferSize()
throws SocketException {
- return proxy.getProxySocket().getSendBufferSize();
+ return getProxySocket().getSendBufferSize();
}
@Override
public synchronized void setSendBufferSize(final int size)
throws SocketException {
- proxy.getProxySocket().setSendBufferSize(size);
+ getProxySocket().setSendBufferSize(size);
}
@Override
public synchronized int getReceiveBufferSize()
throws SocketException {
- return proxy.getProxySocket().getReceiveBufferSize();
+ return getProxySocket().getReceiveBufferSize();
}
@Override
public synchronized void setReceiveBufferSize(final int size)
throws SocketException {
- proxy.getProxySocket().setReceiveBufferSize(size);
+ getProxySocket().setReceiveBufferSize(size);
}
@Override
public boolean getKeepAlive()
throws SocketException {
- return proxy.getProxySocket().getKeepAlive();
+ return getProxySocket().getKeepAlive();
}
@Override
public void setKeepAlive(final boolean on)
throws SocketException {
- proxy.getProxySocket().setKeepAlive(on);
+ getProxySocket().setKeepAlive(on);
}
@Override
public int getTrafficClass()
throws SocketException {
- return proxy.getProxySocket().getTrafficClass();
+ return getProxySocket().getTrafficClass();
}
@Override
public void setTrafficClass(final int tc)
throws SocketException {
- proxy.getProxySocket().setTrafficClass(tc);
+ getProxySocket().setTrafficClass(tc);
}
@Override
public boolean getReuseAddress()
throws SocketException {
- return proxy.getProxySocket().getReuseAddress();
+ return getProxySocket().getReuseAddress();
}
@Override
public void setReuseAddress(final boolean on)
throws SocketException {
- proxy.getProxySocket().setReuseAddress(on);
+ getProxySocket().setReuseAddress(on);
}
@Override
@@ -310,43 +315,47 @@ public synchronized void close()
@Override
public void shutdownInput()
throws IOException {
- proxy.getProxySocket().shutdownInput();
+ getProxySocket().shutdownInput();
}
@Override
public void shutdownOutput()
throws IOException {
- proxy.getProxySocket().shutdownOutput();
+ getProxySocket().shutdownOutput();
}
@Override
public boolean isConnected() {
- return proxy.getProxySocket().isConnected();
+ return getProxySocket().isConnected();
}
@Override
public boolean isBound() {
- return proxy.getProxySocket().isBound();
+ return getProxySocket().isBound();
}
@Override
public boolean isClosed() {
- return proxy.getProxySocket().isClosed();
+ return getProxySocket().isClosed();
}
@Override
public boolean isInputShutdown() {
- return proxy.getProxySocket().isInputShutdown();
+ return getProxySocket().isInputShutdown();
}
@Override
public boolean isOutputShutdown() {
- return proxy.getProxySocket().isOutputShutdown();
+ return getProxySocket().isOutputShutdown();
}
@Override
public void setPerformancePreferences(final int connectionTime, final int latency, final int bandwidth) {
- proxy.getProxySocket().setPerformancePreferences(connectionTime, latency, bandwidth);
+ getProxySocket().setPerformancePreferences(connectionTime, latency, bandwidth);
}
-}
+ @NotNull
+ private Socket getProxySocket() {
+ return requireNonNull(proxy.getProxySocket(), "proxySocket");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/AnonymousSocks5Server.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/AnonymousSocks5ServerImpl.java
similarity index 61%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/AnonymousSocks5Server.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/AnonymousSocks5ServerImpl.java
index fc2bccae1..de2d6201f 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/AnonymousSocks5Server.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/AnonymousSocks5ServerImpl.java
@@ -1,43 +1,44 @@
-package org.simplejavamail.mailer.internal.socks.socks5server;
-
-import org.simplejavamail.mailer.internal.socks.common.Socks5Bridge;
-import org.simplejavamail.mailer.internal.socks.common.SocksException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server;
import java.io.IOException;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import org.jetbrains.annotations.Nullable;
+import org.simplejavamail.api.internal.authenticatedsockssupport.common.Socks5Bridge;
+import org.simplejavamail.api.internal.authenticatedsockssupport.socks5server.AnonymousSocks5Server;
+import org.simplejavamail.internal.authenticatedsockssupport.common.SocksException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
/**
- * SOCKS server that accepts anonymous connections from JavaMail.
- *
- * Java Mail only support anonymous SOCKS proxies; in order to support authenticated proxies, we need to create a man-in-the-middle: which is the
- * {@link AnonymousSocks5Server}.
+ * @see AnonymousSocks5Server
*/
-public class AnonymousSocks5Server implements Runnable {
-
+public class AnonymousSocks5ServerImpl implements AnonymousSocks5Server {
+
private static final Logger LOGGER = LoggerFactory.getLogger(AnonymousSocks5Server.class);
-
+
private final Socks5Bridge socks5Bridge;
private final int proxyBridgePort;
-
+
private ExecutorService threadPool;
private ServerSocket serverSocket;
- private boolean stopping = false;
- private boolean running = false;
-
- public AnonymousSocks5Server(final Socks5Bridge socks5Bridge, final int proxyBridgePort) {
+ private volatile boolean stopping = false;
+ private volatile boolean running = false;
+
+ public AnonymousSocks5ServerImpl(final Socks5Bridge socks5Bridge, final int proxyBridgePort) {
this.socks5Bridge = socks5Bridge;
this.proxyBridgePort = proxyBridgePort;
}
-
+
/**
- * Binds the port and starts a thread to listen to incoming proxy connections from JavaMail.
+ * @see AnonymousSocks5Server#start()
*/
+ @Override
public void start() {
if (running) {
throw new IllegalStateException("server already running!");
@@ -47,13 +48,14 @@ public void start() {
this.threadPool = Executors.newFixedThreadPool(100);
this.serverSocket = new ServerSocket();
this.serverSocket.setReuseAddress(true);
- this.serverSocket.bind(new InetSocketAddress(proxyBridgePort));
+ this.serverSocket.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), proxyBridgePort));
} catch (final IOException e) {
throw new SocksException("error preparing socks5bridge server for authenticated proxy session", e);
}
new Thread(this).start();
}
-
+
+ @Override
public void stop() {
stopping = true;
try {
@@ -62,13 +64,14 @@ public void stop() {
throw new SocksException(e.getMessage(), e);
}
}
-
+
@Override
public void run() {
LOGGER.info("Starting proxy server at port {}", serverSocket.getLocalPort());
while (!stopping) {
try {
LOGGER.info("waiting for new connection...");
+ @SuppressWarnings("SocketOpenedButNotSafelyClosed") // socket is closed elsewhere
final Socket socket = serverSocket.accept();
socket.setSoTimeout(10000);
threadPool.execute(new Socks5Handler(new SocksSession(socket), socks5Bridge));
@@ -81,9 +84,9 @@ public void run() {
running = false;
stopping = false;
}
-
+
private void checkIoException(final Exception e) {
- if (e.getMessage().equals("socket closed")) {
+ if (e.getMessage().equalsIgnoreCase("socket closed")) {
LOGGER.debug("socket closed");
} else {
running = false;
@@ -91,12 +94,27 @@ private void checkIoException(final Exception e) {
throw new SocksException("server crashed...", e);
}
}
-
+
+ @Override
public boolean isStopping() {
return stopping;
}
-
+
+ @Override
public boolean isRunning() {
return running;
}
-}
\ No newline at end of file
+
+ @Override
+ public int getLocalPort() {
+ if (serverSocket == null) {
+ return -1;
+ }
+ return serverSocket.getLocalPort();
+ }
+
+ @Nullable
+ InetAddress getLocalAddress() {
+ return serverSocket == null ? null : serverSocket.getInetAddress();
+ }
+}
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/Socks5Handler.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/Socks5Handler.java
similarity index 85%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/Socks5Handler.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/Socks5Handler.java
index 2d7bb4ca1..6669f33db 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/Socks5Handler.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/Socks5Handler.java
@@ -1,12 +1,12 @@
-package org.simplejavamail.mailer.internal.socks.socks5server;
-
-import org.simplejavamail.mailer.internal.socks.common.Socks5Bridge;
-import org.simplejavamail.mailer.internal.socks.common.SocksException;
-import org.simplejavamail.mailer.internal.socks.socks5server.io.SocketPipe;
-import org.simplejavamail.mailer.internal.socks.socks5server.msg.CommandMessage;
-import org.simplejavamail.mailer.internal.socks.socks5server.msg.CommandResponseMessage;
-import org.simplejavamail.mailer.internal.socks.socks5server.msg.MethodSelectionMessage;
-import org.simplejavamail.mailer.internal.socks.socks5server.msg.ServerReply;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server;
+
+import org.simplejavamail.api.internal.authenticatedsockssupport.common.Socks5Bridge;
+import org.simplejavamail.internal.authenticatedsockssupport.common.SocksException;
+import org.simplejavamail.internal.authenticatedsockssupport.socks5server.io.SocketPipe;
+import org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg.CommandMessage;
+import org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg.CommandResponseMessage;
+import org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg.MethodSelectionMessage;
+import org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg.ServerReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -87,6 +87,7 @@ private void doConnect(final SocksSession session, final CommandMessage commandM
bindPort = socket.getLocalPort();
reply = ServerReply.SUCCEEDED;
} catch (final IOException e) {
+ //noinspection IfCanBeSwitch
if (e.getMessage().equals("Connection refused")) {
reply = ServerReply.CONNECTION_REFUSED;
} else if (e.getMessage().equals("Operation timed out")) {
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/SocksSession.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/SocksSession.java
similarity index 96%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/SocksSession.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/SocksSession.java
index 4cb045546..c9eee1a23 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/SocksSession.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/SocksSession.java
@@ -1,4 +1,4 @@
-package org.simplejavamail.mailer.internal.socks.socks5server;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/io/SocketPipe.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/io/SocketPipe.java
similarity index 93%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/io/SocketPipe.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/io/SocketPipe.java
index ae8e8f486..16111f1d2 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/io/SocketPipe.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/io/SocketPipe.java
@@ -1,4 +1,4 @@
-package org.simplejavamail.mailer.internal.socks.socks5server.io;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server.io;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -8,7 +8,7 @@
/**
* The class SocketPipe represents pipe that can transfer data from one socket to another socket. The tow socket should be
- * connected sockets. If any of the them occurred error the pipe will close all of them.
+ * connected sockets. If any error occurred, the pipe will close all of them.
*/
public class SocketPipe {
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/io/StreamPipe.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/io/StreamPipe.java
similarity index 94%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/io/StreamPipe.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/io/StreamPipe.java
index edbfe9066..d09dcea0b 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/io/StreamPipe.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/io/StreamPipe.java
@@ -1,4 +1,4 @@
-package org.simplejavamail.mailer.internal.socks.socks5server.io;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server.io;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -10,7 +10,7 @@
import java.util.List;
/**
- * The class StreamPipe represents a pipe the can transfer data source a input stream destination a output stream.
+ * The class StreamPipe represents a pipe the can transfer data source an input stream destination an output stream.
*/
class StreamPipe implements Runnable {
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/AddressType.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/AddressType.java
similarity index 75%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/AddressType.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/AddressType.java
index d586d6335..8b6a62ffb 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/AddressType.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/AddressType.java
@@ -1,4 +1,4 @@
-package org.simplejavamail.mailer.internal.socks.socks5server.msg;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg;
final class AddressType {
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/CommandMessage.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/CommandMessage.java
similarity index 82%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/CommandMessage.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/CommandMessage.java
index 44e3b51fc..a34f5ccc8 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/CommandMessage.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/CommandMessage.java
@@ -1,6 +1,6 @@
-package org.simplejavamail.mailer.internal.socks.socks5server.msg;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg;
-import org.simplejavamail.mailer.internal.socks.common.SocksException;
+import org.simplejavamail.internal.authenticatedsockssupport.common.SocksException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -10,7 +10,6 @@
import java.net.UnknownHostException;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.simplejavamail.mailer.internal.socks.socks5server.msg.StreamUtil.checkEnd;
public class CommandMessage {
@@ -28,11 +27,11 @@ public void read(final InputStream inputStream)
throws IOException {
LOGGER.trace("CommandMessage.read");
- checkEnd(inputStream.read()); // version, unused
- command = checkEnd(inputStream.read());
+ StreamUtil.checkEnd(inputStream.read()); // version, unused
+ command = StreamUtil.checkEnd(inputStream.read());
- checkEnd(inputStream.read());
- final int addressType = checkEnd(inputStream.read());
+ StreamUtil.checkEnd(inputStream.read());
+ final int addressType = StreamUtil.checkEnd(inputStream.read());
if (!AddressType.isSupport(addressType) && socksServerReplyException == null) {
socksServerReplyException = new SocksServerReplyException(ServerReply.ADDRESS_TYPE_NOT_SUPPORTED);
@@ -46,7 +45,7 @@ public void read(final InputStream inputStream)
break;
case AddressType.DOMAIN_NAME:
- final int domainLength = checkEnd(inputStream.read());
+ final int domainLength = StreamUtil.checkEnd(inputStream.read());
if (domainLength < 1) {
throw new SocksException("Length of domain must great than 0");
}
@@ -79,7 +78,7 @@ private static byte[] read(final InputStream inputStream, final int length)
throws IOException {
final byte[] bytes = new byte[length];
for (int i = 0; i < length; i++) {
- bytes[i] = (byte) checkEnd(inputStream.read());
+ bytes[i] = (byte) StreamUtil.checkEnd(inputStream.read());
}
return bytes;
}
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/CommandResponseMessage.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/CommandResponseMessage.java
similarity index 88%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/CommandResponseMessage.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/CommandResponseMessage.java
index 26b531c06..baaaede8f 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/CommandResponseMessage.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/CommandResponseMessage.java
@@ -1,6 +1,6 @@
-package org.simplejavamail.mailer.internal.socks.socks5server.msg;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg;
-import org.simplejavamail.mailer.internal.socks.socks5server.Socks5Handler;
+import org.simplejavamail.internal.authenticatedsockssupport.socks5server.Socks5Handler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/MethodSelectionMessage.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/MethodSelectionMessage.java
similarity index 58%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/MethodSelectionMessage.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/MethodSelectionMessage.java
index 34ef0667f..d35fb7d4d 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/MethodSelectionMessage.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/MethodSelectionMessage.java
@@ -1,4 +1,4 @@
-package org.simplejavamail.mailer.internal.socks.socks5server.msg;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -6,8 +6,6 @@
import java.io.IOException;
import java.io.InputStream;
-import static org.simplejavamail.mailer.internal.socks.socks5server.msg.StreamUtil.checkEnd;
-
public final class MethodSelectionMessage {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodSelectionMessage.class);
@@ -15,10 +13,10 @@ public final class MethodSelectionMessage {
public static int readVersion(final InputStream inputStream)
throws IOException {
LOGGER.trace("MethodSelectionMessage.read");
- final int version = checkEnd(inputStream.read());
- final int methodNum = checkEnd(inputStream.read());
+ final int version = StreamUtil.checkEnd(inputStream.read());
+ final int methodNum = StreamUtil.checkEnd(inputStream.read());
for (int i = 0; i < methodNum; i++) {
- checkEnd(inputStream.read()); // read method byte
+ StreamUtil.checkEnd(inputStream.read()); // read method byte
}
return version;
}
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/ServerReply.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/ServerReply.java
similarity index 70%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/ServerReply.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/ServerReply.java
index 4884e4e02..a516092cc 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/ServerReply.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/ServerReply.java
@@ -1,7 +1,9 @@
-package org.simplejavamail.mailer.internal.socks.socks5server.msg;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg;
+
+import org.jetbrains.annotations.Nullable;
/**
- * The enumeration ServerReply represents reply of servers will SOCKS client send a command request to the SOCKS server.
+ * The enumeration ServerReply represents reply of servers when a SOCKS client sends a command request to the SOCKS server.
*/
public enum ServerReply {
@@ -21,9 +23,10 @@ public enum ServerReply {
private final byte value;
+ @Nullable
private final String errorMessage;
- ServerReply(final int value, final String errorMessage) {
+ ServerReply(final int value, @Nullable final String errorMessage) {
this.value = (byte) value;
this.errorMessage = errorMessage;
}
@@ -32,7 +35,8 @@ public byte getValue() {
return value;
}
+ @Nullable
public String getErrorMessage() {
return errorMessage;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/SocksServerReplyException.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/SocksServerReplyException.java
similarity index 62%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/SocksServerReplyException.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/SocksServerReplyException.java
index 173a8109e..2c312420e 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/SocksServerReplyException.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/SocksServerReplyException.java
@@ -1,7 +1,8 @@
-package org.simplejavamail.mailer.internal.socks.socks5server.msg;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg;
-import org.simplejavamail.mailer.internal.socks.common.SocksException;
+import org.simplejavamail.internal.authenticatedsockssupport.common.SocksException;
+@SuppressWarnings("serial")
public class SocksServerReplyException extends SocksException {
private final ServerReply serverReply;
diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/StreamUtil.java b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/StreamUtil.java
similarity index 71%
rename from src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/StreamUtil.java
rename to modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/StreamUtil.java
index cf8267e1b..bbc82d501 100644
--- a/src/main/java/org/simplejavamail/mailer/internal/socks/socks5server/msg/StreamUtil.java
+++ b/modules/authenticated-socks-module/src/main/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/msg/StreamUtil.java
@@ -1,4 +1,4 @@
-package org.simplejavamail.mailer.internal.socks.socks5server.msg;
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server.msg;
import java.io.IOException;
diff --git a/modules/authenticated-socks-module/src/main/javadoc/index.html b/modules/authenticated-socks-module/src/main/javadoc/index.html
new file mode 100644
index 000000000..94a172530
--- /dev/null
+++ b/modules/authenticated-socks-module/src/main/javadoc/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+ About this module
+
+
+
+ If present on the classpath, this module is used internally by the main Simple Java Mail library.
+ By itself this module has no public API.
+
+
+
\ No newline at end of file
diff --git a/modules/authenticated-socks-module/src/test/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SocksCommandSenderTest.java b/modules/authenticated-socks-module/src/test/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SocksCommandSenderTest.java
new file mode 100644
index 000000000..1dcb7c81a
--- /dev/null
+++ b/modules/authenticated-socks-module/src/test/java/org/simplejavamail/internal/authenticatedsockssupport/socks5client/SocksCommandSenderTest.java
@@ -0,0 +1,99 @@
+package org.simplejavamail.internal.authenticatedsockssupport.socks5client;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.config.Property;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class SocksCommandSenderTest {
+
+ @Test
+ public void sendsPortAfterEncodedDomainBytes() throws Exception {
+ final ByteArrayOutputStream request = new ByteArrayOutputStream();
+ final byte[] host = "例.com".getBytes(UTF_8);
+
+ SocksCommandSender.send(socket(request, successfulIpv4Reply()), "例.com", 443);
+
+ final ByteArrayOutputStream expectedRequest = new ByteArrayOutputStream();
+ expectedRequest.write(new byte[] { 5, 1, 0, 3, (byte) host.length });
+ expectedRequest.write(host);
+ expectedRequest.write(new byte[] { 1, (byte) 187 });
+ assertArrayEquals(expectedRequest.toByteArray(), request.toByteArray());
+ }
+
+ @Test
+ public void readsDomainReplyAfterLengthByte() throws Exception {
+ final CapturingAppender appender = new CapturingAppender();
+ final org.apache.logging.log4j.core.Logger logger =
+ (org.apache.logging.log4j.core.Logger) LogManager.getLogger(SocksCommandSender.class);
+ final Level previousLevel = logger.getLevel();
+ appender.start();
+ logger.addAppender(appender);
+ logger.setLevel(Level.DEBUG);
+
+ try {
+ SocksCommandSender.send(socket(new ByteArrayOutputStream(), successfulDomainReply("example.com", 443)), "target", 25);
+
+ assertTrue(appender.messages.contains("Server replied:Address as host:example.com, port:443"));
+ } finally {
+ logger.setLevel(previousLevel);
+ logger.removeAppender(appender);
+ appender.stop();
+ }
+ }
+
+ private static Socket socket(final ByteArrayOutputStream request, final byte[] reply) {
+ return new Socket() {
+ @Override
+ public InputStream getInputStream() {
+ return new ByteArrayInputStream(reply);
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ return request;
+ }
+ };
+ }
+
+ private static byte[] successfulIpv4Reply() {
+ return new byte[] { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
+ }
+
+ private static byte[] successfulDomainReply(final String host, final int port) throws Exception {
+ final byte[] hostBytes = host.getBytes(UTF_8);
+ final ByteArrayOutputStream reply = new ByteArrayOutputStream();
+ reply.write(new byte[] { 5, 0, 0, 3, (byte) hostBytes.length });
+ reply.write(hostBytes);
+ reply.write(new byte[] { (byte) (port >> 8), (byte) port });
+ return reply.toByteArray();
+ }
+
+ private static class CapturingAppender extends AbstractAppender {
+
+ private final List messages = new ArrayList<>();
+
+ private CapturingAppender() {
+ super("socks-command-sender-test", null, null, true, Property.EMPTY_ARRAY);
+ }
+
+ @Override
+ public void append(final LogEvent event) {
+ messages.add(event.getMessage().getFormattedMessage());
+ }
+ }
+}
diff --git a/modules/authenticated-socks-module/src/test/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/AnonymousSocks5ServerImplTest.java b/modules/authenticated-socks-module/src/test/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/AnonymousSocks5ServerImplTest.java
new file mode 100644
index 000000000..4fb5cf302
--- /dev/null
+++ b/modules/authenticated-socks-module/src/test/java/org/simplejavamail/internal/authenticatedsockssupport/socks5server/AnonymousSocks5ServerImplTest.java
@@ -0,0 +1,39 @@
+package org.simplejavamail.internal.authenticatedsockssupport.socks5server;
+
+import java.net.InetAddress;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class AnonymousSocks5ServerImplTest {
+
+ @Test
+ void bindsTheBridgeToLoopbackOnly() throws InterruptedException {
+ final AnonymousSocks5ServerImpl server = new AnonymousSocks5ServerImpl(
+ (sessionId, remoteServerAddress, remoteServerPort) -> {
+ throw new UnsupportedOperationException("The bridge must not be used by this binding test");
+ },
+ 0);
+
+ server.start();
+ try {
+ final InetAddress localAddress = server.getLocalAddress();
+ assertNotNull(localAddress);
+ assertTrue(localAddress.isLoopbackAddress());
+ assertTrue(server.getLocalPort() > 0);
+ } finally {
+ server.stop();
+ waitUntilStopped(server);
+ }
+ }
+
+ private static void waitUntilStopped(final AnonymousSocks5ServerImpl server) throws InterruptedException {
+ for (int elapsedMillis = 0; server.isRunning() && elapsedMillis < 1000; elapsedMillis += 10) {
+ Thread.sleep(10);
+ }
+ assertFalse(server.isRunning(), "The SOCKS bridge should stop after its socket closes");
+ }
+}
diff --git a/modules/batch-module/.gitignore b/modules/batch-module/.gitignore
new file mode 100644
index 000000000..dc48b22fc
--- /dev/null
+++ b/modules/batch-module/.gitignore
@@ -0,0 +1,2 @@
+*.iml
+target
\ No newline at end of file
diff --git a/modules/batch-module/LICENSE-2.0.txt b/modules/batch-module/LICENSE-2.0.txt
new file mode 100644
index 000000000..d639b1e24
--- /dev/null
+++ b/modules/batch-module/LICENSE-2.0.txt
@@ -0,0 +1,188 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
\ No newline at end of file
diff --git a/modules/batch-module/NOTICE.txt b/modules/batch-module/NOTICE.txt
new file mode 100644
index 000000000..d48127094
--- /dev/null
+++ b/modules/batch-module/NOTICE.txt
@@ -0,0 +1,13 @@
+ =========================================================================
+ == NOTICE file for use with the Apache License, Version 2.0 ==
+ =========================================================================
+
+ Simple Java Mail - Batch module
+ Copyright (C) 2009 Benny Bottema (benny@bennybottema.com)
+ https://www.simplejavamail.org
+
+ This product uses no commercial products.
+
+ This product uses the following external (Open Source) libraries:
+
+ - smtp-connection-pool https://github.com/simple-java-mail/smtp-connection-pool
\ No newline at end of file
diff --git a/modules/batch-module/pom.xml b/modules/batch-module/pom.xml
new file mode 100644
index 000000000..c61a39d06
--- /dev/null
+++ b/modules/batch-module/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+ 4.0.0
+
+
+ org.simplejavamail
+ maven-master-project
+ 9.2.0
+ ../../pom.xml
+
+
+ batch-module
+ jar
+ Simple Java Mail - Batch module
+ Simple API, Complex Emails. High performance.
+
+
+ org.simplejavamail.batch
+
+
+
+
+
+
+ org.simplejavamail
+ core-module
+ ${project.version}
+
+
+
+
+ org.simplejavamail
+ smtp-connection-pool
+ 3.1.0
+
+
+
diff --git a/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/BatchException.java b/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/BatchException.java
new file mode 100644
index 000000000..bcbf5fc5d
--- /dev/null
+++ b/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/BatchException.java
@@ -0,0 +1,14 @@
+package org.simplejavamail.internal.batchsupport;
+
+class BatchException extends RuntimeException {
+
+ static final String ERROR_ACQUIRING_KEYED_POOLABLE = "Was unable to obtain a poolable object for key:\t%n%s";
+
+ BatchException(final String msg, final Throwable cause) {
+ super(msg, cause);
+ }
+
+ BatchException(final String msg) {
+ super(msg);
+ }
+}
\ No newline at end of file
diff --git a/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/BatchSupport.java b/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/BatchSupport.java
new file mode 100644
index 000000000..df7c99fd4
--- /dev/null
+++ b/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/BatchSupport.java
@@ -0,0 +1,154 @@
+package org.simplejavamail.internal.batchsupport;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import jakarta.mail.Session;
+import lombok.val;
+import org.bbottema.clusteredobjectpool.core.api.ResourceKey.ResourceClusterAndPoolKey;
+import org.bbottema.genericobjectpool.PoolableObject;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.simplejavamail.api.internal.batchsupport.LifecycleDelegatingTransport;
+import org.simplejavamail.api.mailer.config.OperationalConfig;
+import org.simplejavamail.api.mailer.config.TransportStrategy;
+import org.simplejavamail.internal.batchsupport.concurrent.NonJvmBlockingThreadPoolExecutor;
+import org.simplejavamail.internal.modules.BatchModule;
+import org.simplejavamail.internal.util.concurrent.AsyncOperationHelper;
+import org.simplejavamail.smtpconnectionpool.SessionTransport;
+import org.simplejavamail.smtpconnectionpool.SmtpConnectionPool;
+import org.simplejavamail.smtpconnectionpool.SmtpConnectionPoolClustered;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+import static java.util.Optional.ofNullable;
+import static java.util.concurrent.CompletableFuture.completedFuture;
+import static org.simplejavamail.internal.batchsupport.BatchException.ERROR_ACQUIRING_KEYED_POOLABLE;
+import static org.simplejavamail.internal.batchsupport.ClusterHelper.compareClusterConfig;
+import static org.simplejavamail.internal.batchsupport.ClusterHelper.configureSmtpClusterConfig;
+
+/**
+ * This class only serves to hide the Batch implementation behind an easy-to-load-with-reflection class.
+ */
+@SuppressWarnings("unused") // it is used through reflection
+public class BatchSupport implements BatchModule {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BatchSupport.class);
+
+ // no need to make this static, because this module itself is already static in the ModuleLoader
+ @Nullable private SmtpConnectionPoolClustered smtpConnectionPool;
+
+ /**
+ * @see BatchModule#executeAsync(String, Runnable)
+ */
+ @Override
+ public CompletableFuture executeAsync(@NotNull final String processName, @NotNull final Runnable operation) {
+ return AsyncOperationHelper.executeAsync(processName, operation);
+ }
+
+ /**
+ * @see BatchModule#executeAsync(ExecutorService, String, Runnable)
+ */
+ @NotNull
+ @Override
+ public CompletableFuture executeAsync(@NotNull final ExecutorService executorService, @NotNull final String processName, @NotNull final Runnable operation) {
+ return AsyncOperationHelper.executeAsync(executorService, processName, operation);
+ }
+
+ /**
+ * @see BatchModule#createDefaultExecutorService(int, int)
+ */
+ @NotNull
+ @Override
+ public ExecutorService createDefaultExecutorService(final int threadPoolSize, final int keepAliveTime) {
+ return new NonJvmBlockingThreadPoolExecutor(threadPoolSize, keepAliveTime);
+ }
+
+ /**
+ * @see BatchModule#registerToCluster(OperationalConfig, UUID, Session)
+ */
+ @Override
+ public synchronized void registerToCluster(@NotNull final OperationalConfig operationalConfig, @NotNull final UUID clusterKey, @NotNull final Session session) {
+ ensureClusterInitialized(operationalConfig);
+ ensureClusterRegistered(operationalConfig, clusterKey);
+ final ResourceClusterAndPoolKey poolKey = new ResourceClusterAndPoolKey<>(clusterKey, session);
+ if (!requireNonNull(smtpConnectionPool).isPoolRegistered(poolKey)) {
+ smtpConnectionPool.registerResourcePool(poolKey);
+ }
+ }
+
+ private void ensureClusterInitialized(@NotNull OperationalConfig operationalConfig) {
+ if (smtpConnectionPool == null) {
+ LOGGER.warn("Starting SMTP connection pool cluster: JVM won't shutdown until the pool is manually closed with mailer.shutdownConnectionPool() (for each mailer in the cluster)");
+ smtpConnectionPool = new SmtpConnectionPoolClustered<>(configureSmtpClusterConfig(operationalConfig));
+ }
+ }
+
+ private void ensureClusterRegistered(@NotNull OperationalConfig operationalConfig, @NotNull UUID clusterKey) {
+ val smtpConnectionPool = requireNonNull(this.smtpConnectionPool);
+ if (!smtpConnectionPool.isClusterRegistered(clusterKey)) {
+ smtpConnectionPool.registerResourceCluster(clusterKey, configureSmtpClusterConfig(operationalConfig, clusterKey).getConfigBuilder().build());
+ } else if (compareClusterConfig(operationalConfig, clusterKey, smtpConnectionPool.getClusterConfig(clusterKey))) {
+ LOGGER.warn("SMTP Connection pool cluster {} is already configured with pool defaults from the first Mailer instance in that cluster, ignoring relevant properties from {}",
+ clusterKey, operationalConfig);
+ }
+ }
+
+ /**
+ * @see BatchModule#acquireTransport(UUID, Session, boolean)
+ */
+ @NotNull
+ @Override
+ @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH", justification = "This is bullshit, Spotbugs. There's a requireNonNull() right in front of you, you numbnuts")
+ public LifecycleDelegatingTransport acquireTransport(@NotNull final UUID clusterKey, @NotNull final Session session, boolean stickySession) {
+ val smtpConnectionPool = requireNonNull(this.smtpConnectionPool, "Connection pool used before it was initialized. This shouldn't be possible.");
+ checkConfigureOAuth2Token(session);
+
+ return ofNullable(getSessionTransportPoolableObject(smtpConnectionPool, clusterKey, session, stickySession))
+ .map(LifecycleDelegatingTransportImpl::new)
+ .orElseThrow(() -> new BatchException(format(ERROR_ACQUIRING_KEYED_POOLABLE, session)));
+ }
+
+ @Nullable
+ private PoolableObject getSessionTransportPoolableObject(SmtpConnectionPoolClustered smtpConnectionPool, UUID clusterKey, Session session, boolean stickySession) {
+ try {
+ return stickySession
+ ? smtpConnectionPool.claimResourceFromPool(new ResourceClusterAndPoolKey<>(clusterKey, session))
+ : smtpConnectionPool.claimResourceFromCluster(clusterKey);
+ } catch (InterruptedException e) {
+ throw new BatchException(format(ERROR_ACQUIRING_KEYED_POOLABLE, session), e);
+ }
+ }
+
+ // since the SMTP connection pool doesn't know about Simple Java Mail,
+ // it won't know where to look for the OAUTH2 token unless we copy the property
+ private void checkConfigureOAuth2Token(Session session) {
+ val props = session.getProperties();
+ if (props.containsKey(TransportStrategy.OAUTH2_TOKEN_PROPERTY)) {
+ props.setProperty(SmtpConnectionPool.OAUTH2_TOKEN_PROPERTY,
+ props.getProperty(TransportStrategy.OAUTH2_TOKEN_PROPERTY));
+ }
+ if (props.containsKey(TransportStrategy.OAUTH2_TOKEN_PROVIDER_PROPERTY)) {
+ props.put(SmtpConnectionPool.OAUTH2_TOKEN_PROVIDER_PROPERTY,
+ props.get(TransportStrategy.OAUTH2_TOKEN_PROVIDER_PROPERTY));
+ }
+ }
+
+ /**
+ * @see BatchModule#shutdownConnectionPools(Session)
+ */
+ @NotNull
+ @Override
+ public Future shutdownConnectionPools(@NotNull Session session) {
+ if (smtpConnectionPool == null) {
+ LOGGER.warn("user requested connection pool shutdown, but there is no connection pool to shut down (yet)");
+ return completedFuture(null);
+ }
+ return smtpConnectionPool.shutdownPool(session);
+ }
+}
diff --git a/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/ClusterHelper.java b/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/ClusterHelper.java
new file mode 100644
index 000000000..516110a6e
--- /dev/null
+++ b/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/ClusterHelper.java
@@ -0,0 +1,104 @@
+package org.simplejavamail.internal.batchsupport;
+
+import jakarta.mail.Session;
+import org.bbottema.clusteredobjectpool.core.ClusterConfig;
+import org.bbottema.clusteredobjectpool.core.api.LoadBalancingStrategy;
+import org.bbottema.clusteredobjectpool.cyclingstrategies.RandomAccessLoadBalancing;
+import org.bbottema.clusteredobjectpool.cyclingstrategies.RoundRobinLoadBalancing;
+import org.bbottema.genericobjectpool.expirypolicies.TimeoutSinceLastAllocationExpirationPolicy;
+import org.bbottema.genericobjectpool.util.Timeout;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.simplejavamail.api.mailer.config.ConnectionPoolClusterConfig;
+import org.simplejavamail.api.mailer.config.OperationalConfig;
+import org.simplejavamail.smtpconnectionpool.SessionTransport;
+import org.simplejavamail.smtpconnectionpool.SmtpClusterConfig;
+
+import java.util.UUID;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.simplejavamail.api.mailer.config.LoadBalancingStrategy.ROUND_ROBIN;
+
+final class ClusterHelper {
+ private ClusterHelper() {
+ // utility class
+ }
+
+ @NotNull
+ static SmtpClusterConfig configureSmtpClusterConfig(@NotNull final OperationalConfig operationalConfig) {
+ return configureSmtpClusterConfig(operationalConfig, null);
+ }
+
+ @NotNull
+ static SmtpClusterConfig configureSmtpClusterConfig(@NotNull final OperationalConfig operationalConfig, @Nullable final UUID clusterKey) {
+ final ConnectionPoolClusterConfig clusterConfig = determineClusterConfig(operationalConfig, clusterKey);
+ SmtpClusterConfig smtpClusterConfig = new SmtpClusterConfig<>();
+ smtpClusterConfig.getConfigBuilder()
+ .defaultCorePoolSize(determineCoreSize(operationalConfig, clusterConfig))
+ .defaultMaxPoolSize(determineMaxSize(operationalConfig, clusterConfig))
+ .claimTimeout(new Timeout(determineClaimTimeoutMillis(operationalConfig, clusterConfig), MILLISECONDS))
+ .loadBalancingStrategy(createPoolLoadBalancingStrategy(determineConfiguredLoadBalancingStrategy(operationalConfig, clusterConfig)))
+ .defaultExpirationPolicy(new TimeoutSinceLastAllocationExpirationPolicy<>(determineExpireAfterMillis(operationalConfig, clusterConfig), MILLISECONDS));
+ return smtpClusterConfig;
+ }
+
+ static boolean compareClusterConfig(@NotNull final OperationalConfig operationalConfig, final ClusterConfig config) {
+ return compareClusterConfig(operationalConfig, null, config);
+ }
+
+ static boolean compareClusterConfig(@NotNull final OperationalConfig operationalConfig, @Nullable final UUID clusterKey, final ClusterConfig config) {
+ final ConnectionPoolClusterConfig clusterConfig = determineClusterConfig(operationalConfig, clusterKey);
+ return config.getDefaultCorePoolSize() != determineCoreSize(operationalConfig, clusterConfig) ||
+ config.getDefaultMaxPoolSize() != determineMaxSize(operationalConfig, clusterConfig) ||
+ !config.getClaimTimeout().equals(new Timeout(determineClaimTimeoutMillis(operationalConfig, clusterConfig), MILLISECONDS)) ||
+ config.getLoadBalancingStrategy().getClass() != createPoolLoadBalancingStrategy(determineConfiguredLoadBalancingStrategy(operationalConfig, clusterConfig)).getClass() ||
+ !config.getDefaultExpirationPolicy().equals(new TimeoutSinceLastAllocationExpirationPolicy(determineExpireAfterMillis(operationalConfig, clusterConfig), MILLISECONDS));
+ }
+
+ @Nullable
+ private static ConnectionPoolClusterConfig determineClusterConfig(@NotNull final OperationalConfig operationalConfig, @Nullable final UUID clusterKey) {
+ return clusterKey != null
+ ? operationalConfig.getConnectionPoolClusterConfigs().get(clusterKey)
+ : null;
+ }
+
+ private static int determineCoreSize(@NotNull final OperationalConfig operationalConfig, @Nullable final ConnectionPoolClusterConfig clusterConfig) {
+ return clusterConfig != null && clusterConfig.getCoreSize() != null
+ ? clusterConfig.getCoreSize()
+ : operationalConfig.getConnectionPoolCoreSize();
+ }
+
+ private static int determineMaxSize(@NotNull final OperationalConfig operationalConfig, @Nullable final ConnectionPoolClusterConfig clusterConfig) {
+ return clusterConfig != null && clusterConfig.getMaxSize() != null
+ ? clusterConfig.getMaxSize()
+ : operationalConfig.getConnectionPoolMaxSize();
+ }
+
+ private static int determineClaimTimeoutMillis(@NotNull final OperationalConfig operationalConfig, @Nullable final ConnectionPoolClusterConfig clusterConfig) {
+ return clusterConfig != null && clusterConfig.getClaimTimeoutMillis() != null
+ ? clusterConfig.getClaimTimeoutMillis()
+ : operationalConfig.getConnectionPoolClaimTimeoutMillis();
+ }
+
+ private static int determineExpireAfterMillis(@NotNull final OperationalConfig operationalConfig, @Nullable final ConnectionPoolClusterConfig clusterConfig) {
+ return clusterConfig != null && clusterConfig.getExpireAfterMillis() != null
+ ? clusterConfig.getExpireAfterMillis()
+ : operationalConfig.getConnectionPoolExpireAfterMillis();
+ }
+
+ @NotNull
+ private static org.simplejavamail.api.mailer.config.LoadBalancingStrategy determineConfiguredLoadBalancingStrategy(@NotNull final OperationalConfig operationalConfig,
+ @Nullable final ConnectionPoolClusterConfig clusterConfig) {
+ return clusterConfig != null && clusterConfig.getLoadBalancingStrategy() != null
+ ? clusterConfig.getLoadBalancingStrategy()
+ : operationalConfig.getConnectionPoolLoadBalancingStrategy();
+ }
+
+ @SuppressWarnings("rawtypes")
+ @NotNull
+ private static LoadBalancingStrategy createPoolLoadBalancingStrategy(@NotNull final org.simplejavamail.api.mailer.config.LoadBalancingStrategy loadBalancingStrategy) {
+ return loadBalancingStrategy == ROUND_ROBIN
+ ? new RoundRobinLoadBalancing<>()
+ : new RandomAccessLoadBalancing<>();
+ }
+}
diff --git a/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/LifecycleDelegatingTransportImpl.java b/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/LifecycleDelegatingTransportImpl.java
new file mode 100644
index 000000000..7193324c5
--- /dev/null
+++ b/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/LifecycleDelegatingTransportImpl.java
@@ -0,0 +1,42 @@
+package org.simplejavamail.internal.batchsupport;
+
+import jakarta.mail.Session;
+import jakarta.mail.Transport;
+import org.bbottema.genericobjectpool.PoolableObject;
+import org.jetbrains.annotations.NotNull;
+import org.simplejavamail.api.internal.batchsupport.LifecycleDelegatingTransport;
+import org.simplejavamail.smtpconnectionpool.SessionTransport;
+
+/**
+ * Wraps {@link PoolableObject} to implement {@link LifecycleDelegatingTransport}, so transport resources
+ * can be used outside the batchmodule and released to be reused in connection pool.
+ */
+class LifecycleDelegatingTransportImpl implements LifecycleDelegatingTransport {
+ private final PoolableObject pooledTransport;
+
+ LifecycleDelegatingTransportImpl(final PoolableObject pooledTransport) {
+ this.pooledTransport = pooledTransport;
+ }
+
+ @NotNull
+ @Override
+ public Session getSessionUsedToObtainTransport() {
+ return pooledTransport.getAllocatedObject().getSession();
+ }
+
+ @NotNull
+ @Override
+ public Transport getTransport() {
+ return pooledTransport.getAllocatedObject().getTransport();
+ }
+
+ @Override
+ public void signalTransportUsed() {
+ pooledTransport.release();
+ }
+
+ @Override
+ public void signalTransportFailed() {
+ pooledTransport.invalidate();
+ }
+}
\ No newline at end of file
diff --git a/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/concurrent/NamedThreadFactory.java b/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/concurrent/NamedThreadFactory.java
new file mode 100644
index 000000000..e09582fdc
--- /dev/null
+++ b/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/concurrent/NamedThreadFactory.java
@@ -0,0 +1,31 @@
+package org.simplejavamail.internal.batchsupport.concurrent;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static java.lang.String.format;
+import static java.lang.Thread.currentThread;
+
+class NamedThreadFactory implements ThreadFactory {
+ private final AtomicInteger threadNumber = new AtomicInteger(1);
+ private final ThreadGroup group;
+ private final String threadName;
+
+ NamedThreadFactory(@NotNull final String threadName) {
+ SecurityManager s = System.getSecurityManager();
+ group = (s != null) ? s.getThreadGroup() : currentThread().getThreadGroup();
+ this.threadName = threadName;
+ }
+
+ @NotNull
+ public Thread newThread(@NotNull Runnable r) {
+ Thread t = new Thread(group, r, format("%s %d", threadName, threadNumber.getAndIncrement()));
+ if (t.isDaemon())
+ t.setDaemon(false);
+ if (t.getPriority() != Thread.NORM_PRIORITY)
+ t.setPriority(Thread.NORM_PRIORITY);
+ return t;
+ }
+}
\ No newline at end of file
diff --git a/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/concurrent/NonJvmBlockingThreadPoolExecutor.java b/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/concurrent/NonJvmBlockingThreadPoolExecutor.java
new file mode 100644
index 000000000..2036fdf11
--- /dev/null
+++ b/modules/batch-module/src/main/java/org/simplejavamail/internal/batchsupport/concurrent/NonJvmBlockingThreadPoolExecutor.java
@@ -0,0 +1,37 @@
+package org.simplejavamail.internal.batchsupport.concurrent;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import static java.lang.String.format;
+
+/**
+ * Unbounded ThreadPoolExecutor that won't block the JVM from shutting down,
+ * unless the keepAliveTime is explicitly set to zero by user config.
+ *
+ * @see ThreadPoolExecutor
+ * @see LinkedBlockingQueue
+ * @see NamedThreadFactory
+ */
+public class NonJvmBlockingThreadPoolExecutor extends ThreadPoolExecutor {
+
+ private static int counter = 1;
+
+ @SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
+ public NonJvmBlockingThreadPoolExecutor(int threadPoolSize, int threadPoolKeepAliveTime) {
+ super(threadPoolSize,
+ threadPoolSize,
+ threadPoolKeepAliveTime,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(),
+ new NamedThreadFactory(format("Simple Java Mail async mail sender, executor %s / thread", counter++)));
+ // if a timeout is configured, the user wants threads to die off automatically,
+ // so they won't block the JVM from shutting down
+ if (threadPoolKeepAliveTime > 0) {
+ allowCoreThreadTimeOut(true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/batch-module/src/main/javadoc/index.html b/modules/batch-module/src/main/javadoc/index.html
new file mode 100644
index 000000000..78c3b0ecf
--- /dev/null
+++ b/modules/batch-module/src/main/javadoc/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+ About this module
+
+
+
+ If present on the classpath, this module is used internally by the main Simple Java Mail library.
+ By itself this module has no public API.
+
+
+
\ No newline at end of file
diff --git a/modules/batch-module/src/test/java/org/simplejavamail/internal/batchsupport/BatchSupportTest.java b/modules/batch-module/src/test/java/org/simplejavamail/internal/batchsupport/BatchSupportTest.java
new file mode 100644
index 000000000..bc6f2acec
--- /dev/null
+++ b/modules/batch-module/src/test/java/org/simplejavamail/internal/batchsupport/BatchSupportTest.java
@@ -0,0 +1,165 @@
+package org.simplejavamail.internal.batchsupport;
+
+import jakarta.mail.Session;
+import jakarta.mail.Transport;
+import org.bbottema.clusteredobjectpool.core.ClusterConfig;
+import org.junit.jupiter.api.Test;
+import org.simplejavamail.api.internal.batchsupport.LifecycleDelegatingTransport;
+import org.simplejavamail.api.mailer.config.ConnectionPoolClusterConfig;
+import org.simplejavamail.api.mailer.config.LoadBalancingStrategy;
+import org.simplejavamail.api.mailer.config.OperationalConfig;
+import org.simplejavamail.smtpconnectionpool.SessionTransport;
+import org.simplejavamail.smtpconnectionpool.SmtpConnectionPool;
+import org.simplejavamail.smtpconnectionpool.SmtpConnectionPoolClustered;
+
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.simplejavamail.api.mailer.config.TransportStrategy.OAUTH2_TOKEN_PROVIDER_PROPERTY;
+
+class BatchSupportTest {
+
+ @Test
+ void acquireTransportShouldBridgeAndResolveOAuth2TokenProvider() throws Exception {
+ BatchSupport batchSupport = new BatchSupport();
+ UUID cluster = UUID.randomUUID();
+ Session session = mock(Session.class);
+ Transport transport = mock(Transport.class);
+ Properties properties = new Properties();
+ properties.setProperty("mail.smtp.user", "user@example.com");
+ AtomicInteger calls = new AtomicInteger();
+ Supplier provider = () -> "token-" + calls.incrementAndGet();
+ properties.put(OAUTH2_TOKEN_PROVIDER_PROPERTY, provider);
+ when(session.getProperties()).thenReturn(properties);
+ when(session.getTransport()).thenReturn(transport);
+
+ try {
+ batchSupport.registerToCluster(operationalConfig(0, 1, 1000, 5000, LoadBalancingStrategy.ROUND_ROBIN), cluster, session);
+ LifecycleDelegatingTransport pooledTransport = batchSupport.acquireTransport(cluster, session, true);
+
+ assertThat(properties.get(SmtpConnectionPool.OAUTH2_TOKEN_PROVIDER_PROPERTY)).isSameAs(provider);
+ assertThat(calls).hasValue(1);
+ verify(transport).connect("user@example.com", "token-1");
+ pooledTransport.signalTransportUsed();
+ } finally {
+ batchSupport.shutdownConnectionPools(session).get();
+ }
+ }
+
+ @Test
+ void registerToClusterUsesSeparatePoolConfigPerClusterKey() throws Exception {
+ BatchSupport batchSupport = new BatchSupport();
+ UUID clusterA = UUID.randomUUID();
+ UUID clusterB = UUID.randomUUID();
+ Session sessionA = session();
+ Session sessionB = session();
+
+ try {
+ batchSupport.registerToCluster(operationalConfig(0, 2, 100, 5000, LoadBalancingStrategy.ROUND_ROBIN), clusterA, sessionA);
+ batchSupport.registerToCluster(operationalConfig(0, 7, 200, 6000, LoadBalancingStrategy.RANDOM_ACCESS), clusterB, sessionB);
+
+ SmtpConnectionPoolClustered smtpConnectionPool = smtpConnectionPool(batchSupport);
+ assertPoolConfig(smtpConnectionPool.getClusterConfig(clusterA), 0, 2, 100, "RoundRobinLoadBalancing");
+ assertPoolConfig(smtpConnectionPool.getClusterConfig(clusterB), 0, 7, 200, "RandomAccessLoadBalancing");
+ } finally {
+ batchSupport.shutdownConnectionPools(sessionA).get();
+ batchSupport.shutdownConnectionPools(sessionB).get();
+ }
+ }
+
+ @Test
+ void registerToClusterKeepsFirstPoolConfigForSameClusterKey() throws Exception {
+ BatchSupport batchSupport = new BatchSupport();
+ UUID cluster = UUID.randomUUID();
+ Session sessionA = session();
+ Session sessionB = session();
+
+ try {
+ batchSupport.registerToCluster(operationalConfig(0, 2, 100, 5000, LoadBalancingStrategy.ROUND_ROBIN), cluster, sessionA);
+ batchSupport.registerToCluster(operationalConfig(0, 7, 200, 6000, LoadBalancingStrategy.RANDOM_ACCESS), cluster, sessionB);
+
+ SmtpConnectionPoolClustered smtpConnectionPool = smtpConnectionPool(batchSupport);
+ assertPoolConfig(smtpConnectionPool.getClusterConfig(cluster), 0, 2, 100, "RoundRobinLoadBalancing");
+ } finally {
+ batchSupport.shutdownConnectionPools(sessionA).get();
+ batchSupport.shutdownConnectionPools(sessionB).get();
+ }
+ }
+
+ @Test
+ void registerToClusterUsesPropertyConfiguredOverridesForMatchingClusterKey() throws Exception {
+ BatchSupport batchSupport = new BatchSupport();
+ UUID configuredCluster = UUID.randomUUID();
+ UUID fallbackCluster = UUID.randomUUID();
+ Session configuredSession = session();
+ Session fallbackSession = session();
+ Map configuredClusters = new HashMap<>();
+ configuredClusters.put(configuredCluster, ConnectionPoolClusterConfig.builder()
+ .coreSize(0)
+ .maxSize(9)
+ .claimTimeoutMillis(300)
+ .expireAfterMillis(7000)
+ .loadBalancingStrategy(LoadBalancingStrategy.RANDOM_ACCESS)
+ .build());
+
+ try {
+ OperationalConfig operationalConfig = operationalConfig(1, 2, 100, 5000, LoadBalancingStrategy.ROUND_ROBIN, configuredClusters);
+ batchSupport.registerToCluster(operationalConfig, configuredCluster, configuredSession);
+ batchSupport.registerToCluster(operationalConfig, fallbackCluster, fallbackSession);
+
+ SmtpConnectionPoolClustered smtpConnectionPool = smtpConnectionPool(batchSupport);
+ assertPoolConfig(smtpConnectionPool.getClusterConfig(configuredCluster), 0, 9, 300, "RandomAccessLoadBalancing");
+ assertPoolConfig(smtpConnectionPool.getClusterConfig(fallbackCluster), 1, 2, 100, "RoundRobinLoadBalancing");
+ } finally {
+ batchSupport.shutdownConnectionPools(configuredSession).get();
+ batchSupport.shutdownConnectionPools(fallbackSession).get();
+ }
+ }
+
+ private static OperationalConfig operationalConfig(int corePoolSize, int maxPoolSize, int claimTimeoutMillis, int expireAfterMillis,
+ LoadBalancingStrategy loadBalancingStrategy) {
+ return operationalConfig(corePoolSize, maxPoolSize, claimTimeoutMillis, expireAfterMillis, loadBalancingStrategy, Collections.emptyMap());
+ }
+
+ private static OperationalConfig operationalConfig(int corePoolSize, int maxPoolSize, int claimTimeoutMillis, int expireAfterMillis,
+ LoadBalancingStrategy loadBalancingStrategy,
+ Map connectionPoolClusterConfigs) {
+ OperationalConfig operationalConfig = mock(OperationalConfig.class);
+ when(operationalConfig.getConnectionPoolCoreSize()).thenReturn(corePoolSize);
+ when(operationalConfig.getConnectionPoolMaxSize()).thenReturn(maxPoolSize);
+ when(operationalConfig.getConnectionPoolClaimTimeoutMillis()).thenReturn(claimTimeoutMillis);
+ when(operationalConfig.getConnectionPoolExpireAfterMillis()).thenReturn(expireAfterMillis);
+ when(operationalConfig.getConnectionPoolLoadBalancingStrategy()).thenReturn(loadBalancingStrategy);
+ when(operationalConfig.getConnectionPoolClusterConfigs()).thenReturn(connectionPoolClusterConfigs);
+ return operationalConfig;
+ }
+
+ private static Session session() {
+ return Session.getInstance(new Properties());
+ }
+
+ private static void assertPoolConfig(ClusterConfig clusterConfig, int corePoolSize, int maxPoolSize,
+ long claimTimeoutMillis, String loadBalancingStrategyClassName) {
+ assertThat(clusterConfig.getDefaultCorePoolSize()).isEqualTo(corePoolSize);
+ assertThat(clusterConfig.getDefaultMaxPoolSize()).isEqualTo(maxPoolSize);
+ assertThat(clusterConfig.getClaimTimeout().getDurationMs()).isEqualTo(claimTimeoutMillis);
+ assertThat(clusterConfig.getLoadBalancingStrategy().getClass().getSimpleName()).isEqualTo(loadBalancingStrategyClassName);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static SmtpConnectionPoolClustered smtpConnectionPool(BatchSupport batchSupport) throws Exception {
+ Field field = BatchSupport.class.getDeclaredField("smtpConnectionPool");
+ field.setAccessible(true);
+ return (SmtpConnectionPoolClustered) field.get(batchSupport);
+ }
+}
diff --git a/modules/cli-module/.gitignore b/modules/cli-module/.gitignore
new file mode 100644
index 000000000..dc48b22fc
--- /dev/null
+++ b/modules/cli-module/.gitignore
@@ -0,0 +1,2 @@
+*.iml
+target
\ No newline at end of file
diff --git a/modules/cli-module/LICENSE-2.0.txt b/modules/cli-module/LICENSE-2.0.txt
new file mode 100644
index 000000000..d639b1e24
--- /dev/null
+++ b/modules/cli-module/LICENSE-2.0.txt
@@ -0,0 +1,188 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
\ No newline at end of file
diff --git a/modules/cli-module/NOTICE.txt b/modules/cli-module/NOTICE.txt
new file mode 100644
index 000000000..b00843877
--- /dev/null
+++ b/modules/cli-module/NOTICE.txt
@@ -0,0 +1,14 @@
+ =========================================================================
+ == NOTICE file for use with the Apache License, Version 2.0 ==
+ =========================================================================
+
+ Simple Java Mail - CLI module
+ Copyright (C) 2009 Benny Bottema (benny@bennybottema.com)
+ https://www.simplejavamail.org
+
+ This product uses no commercial products.
+
+ This product uses the following external (Open Source) libraries:
+
+ - therapi-runtime-javadoc (https://github.com/dnault/therapi-runtime-javadoc)
+ - Picocli (https://github.com/remkop/picocli)
diff --git a/modules/cli-module/pom.xml b/modules/cli-module/pom.xml
new file mode 100644
index 000000000..f6e75fe71
--- /dev/null
+++ b/modules/cli-module/pom.xml
@@ -0,0 +1,200 @@
+
+
+
+ 4.0.0
+
+
+ org.simplejavamail
+ maven-master-project
+ 9.2.0
+ ../../pom.xml
+
+
+ cli-module
+ jar
+ Simple Java Mail - CLI module
+ Simple API, Complex Emails. Now with CLI support
+
+
+ org.simplejavamail.cli
+
+
+
+
+
+
+ org.simplejavamail
+ simple-java-mail
+ ${project.version}
+ compile
+
+
+ com.github.bbottema
+ java-reflection
+ 4.1.1
+
+
+
+
+ info.picocli
+ picocli
+ 3.9.6
+
+
+
+
+ com.github.therapi
+ therapi-runtime-javadoc
+ 0.15.0
+
+
+
+
+ org.simplejavamail
+ authenticated-socks-module
+ ${project.version}
+ runtime
+ true
+
+
+ org.simplejavamail
+ dkim-module
+ ${project.version}
+ runtime
+ true
+
+
+ org.simplejavamail
+ smime-module
+ ${project.version}
+ runtime
+ true
+
+
+ org.simplejavamail
+ batch-module
+ ${project.version}
+ runtime
+ true
+
+
+ org.simplejavamail
+ outlook-module
+ ${project.version}
+ runtime
+ true
+
+
+
+
+ com.esotericsoftware
+ kryo
+ 5.6.2
+ compile
+
+
+
+
+ org.apache.logging.log4j
+ log4j-slf4j2-impl
+ runtime
+
+
+ org.apache.logging.log4j
+ log4j-api
+ runtime
+
+
+ org.apache.logging.log4j
+ log4j-core
+ runtime
+
+
+
+
+ org.simplejavamail
+ core-test-module
+ ${project.version}
+ test
+
+
+
+
+
+ publish-cli
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.6.3
+
+
+ generateCliData
+ package
+
+ java
+
+
+
+
+ demo.CliListAllSupportedOptionsDemoApp
+ test
+
+
+
+ org.codehaus.mojo
+ appassembler-maven-plugin
+ 2.1.0
+
+ src/main/resources
+ true
+ flat
+ lib
+ true
+
+ unix
+ windows
+
+
+
+ org.simplejavamail.cli.SimpleJavaMail
+ sjm
+
+
+
+
+
+ package
+
+ assemble
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.8.0
+
+
+ src/assembly/standalone-cli-descriptor.xml
+
+
+
+
+ assemble-all
+ package
+
+ single
+
+
+
+
+
+
+
+
+
diff --git a/modules/cli-module/src/assembly/standalone-cli-descriptor.xml b/modules/cli-module/src/assembly/standalone-cli-descriptor.xml
new file mode 100644
index 000000000..333e89696
--- /dev/null
+++ b/modules/cli-module/src/assembly/standalone-cli-descriptor.xml
@@ -0,0 +1,35 @@
+
+ standalone-cli
+
+ tar
+ zip
+
+ false
+
+
+ ${project.build.directory}/appassembler
+ ./
+
+ **/**
+
+
+
+ ${project.basedir}
+ ./
+
+ NOTICE.txt
+
+
+
+ ${maven.multiModuleProjectDirectory}
+ ./
+
+ LICENSE-2.0.txt
+ RELEASE.txt
+
+
+
+
\ No newline at end of file
diff --git a/modules/cli-module/src/main/java/org/simplejavamail/cli/SimpleJavaMail.java b/modules/cli-module/src/main/java/org/simplejavamail/cli/SimpleJavaMail.java
new file mode 100644
index 000000000..eaf20ba45
--- /dev/null
+++ b/modules/cli-module/src/main/java/org/simplejavamail/cli/SimpleJavaMail.java
@@ -0,0 +1,18 @@
+package org.simplejavamail.cli;
+
+import static org.simplejavamail.internal.clisupport.CliSupport.runCLI;
+
+/**
+ * Entry class when using the command line interface. Wires all the args into the CLI support.
+ *
+ * For CLI usage help, simply add the {@code --help} flag or add it to any available option, like so:
+ * {@code sjm send --mailer:async--help}.
+ *
+ * @see More about the CLI module
+ * @see How to use simple Java Mail CLI
+ */
+public class SimpleJavaMail {
+ public static void main(String[] args) {
+ runCLI(args);
+ }
+}
\ No newline at end of file
diff --git a/modules/cli-module/src/main/java/org/simplejavamail/cli/package-info.java b/modules/cli-module/src/main/java/org/simplejavamail/cli/package-info.java
new file mode 100644
index 000000000..e3d51e800
--- /dev/null
+++ b/modules/cli-module/src/main/java/org/simplejavamail/cli/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * Contains the exposed API of this module which is the entry class to invoke from the command line.
+ *