diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97285cf..eec02a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,39 @@
+
+### v0.11.16 (2025-05-12)
+
+#### Features
+
+* Always implement `Clone` for `AsyncFileTransport` ([#1075])
+
+#### Changes
+
+* `Tls`, `CertificateStore`, `TlsParameters`, `TlsParametersBuilder`, `Certificate` and `Identity`
+ are now marked as deprecated when no TLS backend is enabled. They will be properly feature gated
+ in lettre v0.12 ([#1084])
+
+#### Misc
+
+* Gate `web-time` behind `cfg(target_arch = "wasm32")]` ([#1086])
+* Add missing `#[doc(cfg(...))]` attributes ([#1086])
+* Upgrade `webpki-roots` to v1 ([#1088])
+* Cleanup internal `TlsParameters` and `(Async)NetworkStream` structures ([#1082])
+* Feature gate internal `TransportBuilder::tls` to avoid recursive call site warnings ([#1083])
+* Fix workaround for embedding cargo script in rustdoc output ([#1077])
+* Fix `clippy::io_other_error` warnings ([#1078])
+* Upgrade semver compatible dependencies ([#1076], [#1079], [#1080])
+
+[#1075]: https://github.com/lettre/lettre/pull/1075
+[#1076]: https://github.com/lettre/lettre/pull/1076
+[#1077]: https://github.com/lettre/lettre/pull/1077
+[#1078]: https://github.com/lettre/lettre/pull/1078
+[#1079]: https://github.com/lettre/lettre/pull/1079
+[#1080]: https://github.com/lettre/lettre/pull/1080
+[#1082]: https://github.com/lettre/lettre/pull/1082
+[#1083]: https://github.com/lettre/lettre/pull/1083
+[#1084]: https://github.com/lettre/lettre/pull/1084
+[#1086]: https://github.com/lettre/lettre/pull/1086
+[#1088]: https://github.com/lettre/lettre/pull/1088
+
### v0.11.15 (2025-03-10)
diff --git a/Cargo.lock b/Cargo.lock
index ff8b648..4c73145 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -291,7 +291,7 @@ dependencies = [
"miniz_oxide",
"object",
"rustc-demangle",
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -437,6 +437,12 @@ dependencies = [
"shlex",
]
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
[[package]]
name = "cexpr"
version = "0.6.0"
@@ -534,6 +540,16 @@ dependencies = [
"cc",
]
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
[[package]]
name = "concurrent-queue"
version = "2.5.0"
@@ -1241,6 +1257,28 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
[[package]]
name = "jobserver"
version = "0.1.33"
@@ -1287,7 +1325,7 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "lettre"
-version = "0.11.15"
+version = "0.11.16"
dependencies = [
"async-std",
"async-trait",
@@ -1316,6 +1354,7 @@ dependencies = [
"rsa",
"rustls",
"rustls-native-certs",
+ "rustls-platform-verifier",
"serde",
"serde_json",
"sha2",
@@ -1346,7 +1385,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -1972,9 +2011,9 @@ dependencies = [
[[package]]
name = "rustls"
-version = "0.23.25"
+version = "0.23.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c"
+checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
dependencies = [
"aws-lc-rs",
"log",
@@ -2005,10 +2044,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
[[package]]
-name = "rustls-webpki"
-version = "0.103.1"
+name = "rustls-platform-verifier"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
+checksum = "eda84358ed17f1f354cf4b1909ad346e6c7bc2513e8c40eb08e0157aa13a9070"
+dependencies = [
+ "core-foundation 0.10.0",
+ "core-foundation-sys",
+ "jni",
+ "log",
+ "once_cell",
+ "rustls",
+ "rustls-native-certs",
+ "rustls-platform-verifier-android",
+ "rustls-webpki",
+ "security-framework 3.2.0",
+ "security-framework-sys",
+ "webpki-root-certs",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustls-platform-verifier-android"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435"
dependencies = [
"aws-lc-rs",
"ring",
@@ -2277,6 +2343,26 @@ dependencies = [
"windows-sys 0.59.0",
]
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
[[package]]
name = "thread_local"
version = "1.1.8"
@@ -2603,10 +2689,19 @@ dependencies = [
]
[[package]]
-name = "webpki-roots"
-version = "0.26.8"
+name = "webpki-root-certs"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9"
+checksum = "01a83f7e1a9f8712695c03eabe9ed3fbca0feff0152f33f12593e5a6303cb1a4"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb"
dependencies = [
"rustls-pki-types",
]
@@ -2661,7 +2756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
dependencies = [
"windows-core",
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -2670,7 +2765,16 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
]
[[package]]
@@ -2679,7 +2783,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -2688,7 +2792,22 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
]
[[package]]
@@ -2697,28 +2816,46 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
]
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@@ -2731,24 +2868,48 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
diff --git a/Cargo.toml b/Cargo.toml
index 1eea7ce..815ab50 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "lettre"
# remember to update html_root_url and README.md (Cargo.toml example and deps.rs badge)
-version = "0.11.15"
+version = "0.11.16"
description = "Email client"
readme = "README.md"
homepage = "https://lettre.rs"
@@ -49,8 +49,9 @@ percent-encoding = { version = "2.3", optional = true }
## tls
native-tls = { version = "0.2.9", optional = true } # feature
rustls = { version = "0.23.18", default-features = false, features = ["logging", "std", "tls12"], optional = true }
+rustls-platform-verifier = { version = "0.6.0", optional = true }
rustls-native-certs = { version = "0.8", optional = true }
-webpki-roots = { version = "0.26", optional = true }
+webpki-roots = { version = "1.0.0", optional = true }
boring = { version = "4", optional = true }
# async
diff --git a/README.md b/README.md
index 6b43f7f..487300f 100644
--- a/README.md
+++ b/README.md
@@ -28,8 +28,8 @@
@@ -73,9 +73,9 @@ use lettre::{Message, SmtpTransport, Transport};
fn main() {
let email = Message::builder()
- .from("NoBody ".parse().unwrap())
- .reply_to("Yuin ".parse().unwrap())
- .to("Hei ".parse().unwrap())
+ .from(Mailbox::new("NoBody".to_owned(), "nobody@domain.tld".parse().unwrap()))
+ .reply_to(Mailbox::new("Yuin".to_owned(), "yuin@domain.tld".parse().unwrap()))
+ .to(Mailbox::new("Hei".to_owned(), "hei@domain.tld".parse().unwrap()))
.subject("Happy new year")
.header(ContentType::TEXT_PLAIN)
.body(String::from("Be happy!"))
diff --git a/src/lib.rs b/src/lib.rs
index 10c6260..f0a1bf9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -93,17 +93,20 @@
//! When the `rustls` feature is enabled, one of the following verification backends
//! MUST also be enabled.
//!
-//! * **rustls-native-certs**: verify TLS certificates using the platform's native certificate store (see [`rustls-native-certs`])
+//! * **rustls-platform-verifier**: verify TLS certificate using the OS's native certificate store (see [`rustls-platform-verifier`])
+//! * **rustls-native-certs**: verify TLS certificates using the platform's native certificate store (see [`rustls-native-certs`]) - when in doubt use `rustls-platform-verifier`
//! * **webpki-roots**: verify TLS certificates against Mozilla's root certificates (see [`webpki-roots`])
//!
-//! For the `rustls-native-certs` backend to work correctly, the following packages
-//! will need to be installed in order for the build stage and the compiled program
-//! to run properly.
+//! The following packages will need to be installed in order for the build
+//! stage and the compiled program to run properly.
//!
-//! | Distro | Build-time packages | Runtime packages |
-//! | ------------ | -------------------------- | ---------------------------- |
-//! | Debian | none | `ca-certificates` |
-//! | Alpine Linux | none | `ca-certificates` |
+//! | Verification backend | Distro | Build-time packages | Runtime packages |
+//! | --------------------- | ------------ | -------------------------- | ---------------------------- |
+//! | `rustls-platform-verifier` | Debian | none | `ca-certificates` |
+//! | `rustls-platform-verifier` | Alpine Linux | none | `ca-certificates` |
+//! | `rustls-native-certs` | Debian | none | `ca-certificates` |
+//! | `rustls-native-certs` | Alpine Linux | none | `ca-certificates` |
+//! | `webpki-roots` | any | none | none |
//!
//! ### Sendmail transport
//!
@@ -151,6 +154,7 @@
//! [AWS-LC]: https://github.com/aws/aws-lc
//! [`aws-lc-rs`]: https://crates.io/crates/aws-lc-rs
//! [`ring`]: https://crates.io/crates/ring
+//! [`rustls-platform-verifier`]: https://crates.io/crates/rustls-platform-verifier
//! [`rustls-native-certs`]: https://crates.io/crates/rustls-native-certs
//! [`webpki-roots`]: https://crates.io/crates/webpki-roots
//! [Tokio 1.x]: https://docs.rs/tokio/1
@@ -158,7 +162,7 @@
//! [mime 0.3]: https://docs.rs/mime/0.3
//! [DKIM]: https://datatracker.ietf.org/doc/html/rfc6376
-#![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.15")]
+#![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.16")]
#![doc(html_favicon_url = "https://lettre.rs/favicon.ico")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")]
#![forbid(unsafe_code)]
@@ -208,12 +212,13 @@ mod compiletime_checks {
#[cfg(all(
feature = "rustls",
+ not(feature = "rustls-platform-verifier"),
not(feature = "rustls-native-certs"),
not(feature = "webpki-roots")
))]
compile_error!(
- "feature `rustls` also requires either the `rustls-native-certs` or the `webpki-roots` feature to
- be enabled"
+ "feature `rustls` also requires either the `rustls-platform-verifier`, the `rustls-native-certs`
+ or the `webpki-roots` feature to be enabled"
);
#[cfg(all(feature = "native-tls", feature = "boring-tls"))]
diff --git a/src/transport/smtp/client/tls/current.rs b/src/transport/smtp/client/tls/current.rs
index a38e030..5f61a50 100644
--- a/src/transport/smtp/client/tls/current.rs
+++ b/src/transport/smtp/client/tls/current.rs
@@ -47,6 +47,16 @@ pub enum TlsVersion {
/// connecting to a local server.
#[derive(Clone)]
#[allow(missing_copy_implementations)]
+#[cfg_attr(
+ not(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")),
+ deprecated(
+ note = "starting from lettre v0.12 `Tls` won't be available when none of the TLS backends are enabled"
+ )
+)]
+#[cfg_attr(
+ docsrs,
+ doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
+)]
pub enum Tls {
/// Insecure (plaintext) connection only.
///
@@ -120,14 +130,25 @@ impl Debug for Tls {
/// Source for the base set of root certificates to trust.
#[allow(missing_copy_implementations)]
#[derive(Clone, Debug, Default)]
+#[cfg_attr(
+ not(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")),
+ deprecated(
+ note = "starting from lettre v0.12 `CertificateStore` won't be available when none of the TLS backends are enabled"
+ )
+)]
+#[cfg_attr(
+ docsrs,
+ doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
+)]
pub enum CertificateStore {
/// Use the default for the TLS backend.
///
/// For native-tls, this will use the system certificate store on Windows, the keychain on
/// macOS, and OpenSSL directories on Linux (usually `/etc/ssl`).
///
- /// For rustls, this will also use the system store if the `rustls-native-certs` feature is
- /// enabled, or will fall back to `webpki-roots`.
+ /// For rustls, this will use the system certificate verifier if the `rustls-platform-verifier`
+ /// feature is enabled. If the `rustls-native-certs` feature is enabled, system certificate
+ /// store will be used. Otherwise, it will fall back to `webpki-roots`.
///
/// The boring-tls backend uses the same logic as OpenSSL on all platforms.
#[default]
@@ -143,12 +164,32 @@ pub enum CertificateStore {
/// Parameters to use for secure clients
#[derive(Clone)]
+#[cfg_attr(
+ not(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")),
+ deprecated(
+ note = "starting from lettre v0.12 `TlsParameters` won't be available when none of the TLS backends are enabled"
+ )
+)]
+#[cfg_attr(
+ docsrs,
+ doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
+)]
pub struct TlsParameters {
pub(in crate::transport::smtp) inner: InnerTlsParameters,
}
/// Builder for `TlsParameters`
#[derive(Debug, Clone)]
+#[cfg_attr(
+ not(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")),
+ deprecated(
+ note = "starting from lettre v0.12 `TlsParametersBuilder` won't be available when none of the TLS backends are enabled"
+ )
+)]
+#[cfg_attr(
+ docsrs,
+ doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
+)]
pub struct TlsParametersBuilder {
domain: String,
cert_store: CertificateStore,
@@ -199,6 +240,8 @@ impl TlsParametersBuilder {
/// Controls whether certificates with an invalid hostname are accepted
///
+ /// This option is silently disabled when using `rustls-platform-verifier`.
+ ///
/// Defaults to `false`.
///
/// # Warning
@@ -356,10 +399,7 @@ impl TlsParametersBuilder {
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
pub fn build_rustls(self) -> Result {
let cert_store = match self.cert_store {
- #[cfg(feature = "rustls-native-certs")]
- CertificateStore::Default => super::rustls::CertificateStore::NativeCerts,
- #[cfg(all(not(feature = "rustls-native-certs"), feature = "webpki-roots"))]
- CertificateStore::Default => super::rustls::CertificateStore::WebpkiRoots,
+ CertificateStore::Default => super::rustls::CertificateStore::default(),
#[cfg(feature = "webpki-roots")]
CertificateStore::WebpkiRoots => super::rustls::CertificateStore::WebpkiRoots,
CertificateStore::None => super::rustls::CertificateStore::None,
@@ -465,6 +505,16 @@ impl TlsParameters {
/// A certificate that can be used with [`TlsParametersBuilder::add_root_certificate`]
#[derive(Clone)]
#[allow(missing_copy_implementations)]
+#[cfg_attr(
+ not(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")),
+ deprecated(
+ note = "starting from lettre v0.12 `Certificate` won't be available when none of the TLS backends are enabled"
+ )
+)]
+#[cfg_attr(
+ docsrs,
+ doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
+)]
pub struct Certificate {
#[cfg(feature = "native-tls")]
native_tls: super::native_tls::Certificate,
@@ -510,6 +560,16 @@ impl Debug for Certificate {
/// An identity that can be used with [`TlsParametersBuilder::identify_with`]
#[derive(Clone)]
#[allow(missing_copy_implementations)]
+#[cfg_attr(
+ not(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")),
+ deprecated(
+ note = "starting from lettre v0.12 `Identity` won't be available when none of the TLS backends are enabled"
+ )
+)]
+#[cfg_attr(
+ docsrs,
+ doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
+)]
pub struct Identity {
#[cfg(feature = "native-tls")]
native_tls: super::native_tls::Identity,
diff --git a/src/transport/smtp/client/tls/rustls.rs b/src/transport/smtp/client/tls/rustls.rs
index 948a7a4..d938864 100644
--- a/src/transport/smtp/client/tls/rustls.rs
+++ b/src/transport/smtp/client/tls/rustls.rs
@@ -29,8 +29,18 @@ pub(super) fn build_connector(
// Build TLS config
let mut root_cert_store = RootCertStore::empty();
+ #[cfg(feature = "rustls-platform-verifier")]
+ let mut extra_roots = Vec::new();
match builder.cert_store {
+ #[cfg(feature = "rustls-platform-verifier")]
+ CertificateStore::PlatformVerifier => {
+ extra_roots = builder
+ .root_certs
+ .iter()
+ .map(|cert| cert.0.clone())
+ .collect();
+ }
#[cfg(feature = "rustls-native-certs")]
CertificateStore::NativeCerts => {
let rustls_native_certs::CertificateResult { certs, errors, .. } =
@@ -55,17 +65,32 @@ pub(super) fn build_connector(
root_cert_store.add(cert.0).map_err(error::tls)?;
}
- let tls = if builder.accept_invalid_certs || builder.accept_invalid_hostnames {
- let verifier = InvalidCertsVerifier {
- ignore_invalid_hostnames: builder.accept_invalid_hostnames,
- ignore_invalid_certs: builder.accept_invalid_certs,
- roots: root_cert_store,
- crypto_provider,
- };
- tls.dangerous()
- .with_custom_certificate_verifier(Arc::new(verifier))
- } else {
- tls.with_root_certificates(root_cert_store)
+ let tls = match (
+ builder.cert_store,
+ builder.accept_invalid_certs,
+ builder.accept_invalid_hostnames,
+ ) {
+ #[cfg(feature = "rustls-platform-verifier")]
+ (CertificateStore::PlatformVerifier, false, _) => {
+ tls.dangerous().with_custom_certificate_verifier(Arc::new(
+ rustls_platform_verifier::Verifier::new_with_extra_roots(
+ extra_roots,
+ crypto_provider,
+ )
+ .map_err(error::tls)?,
+ ))
+ }
+ (_, true, _) | (_, _, true) => {
+ let verifier = InvalidCertsVerifier {
+ ignore_invalid_hostnames: builder.accept_invalid_hostnames,
+ ignore_invalid_certs: builder.accept_invalid_certs,
+ roots: root_cert_store,
+ crypto_provider,
+ };
+ tls.dangerous()
+ .with_custom_certificate_verifier(Arc::new(verifier))
+ }
+ _ => tls.with_root_certificates(root_cert_store),
};
let tls = if let Some(identity) = builder.identity {
@@ -116,19 +141,37 @@ impl AsRef for ServerName {
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub(super) enum CertificateStore {
+ #[cfg(feature = "rustls-platform-verifier")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "rustls-platform-verifier")))]
+ #[cfg_attr(feature = "rustls-platform-verifier", default)]
+ PlatformVerifier,
#[cfg(feature = "rustls-native-certs")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))]
- #[cfg_attr(feature = "rustls-native-certs", default)]
+ #[cfg_attr(
+ all(
+ not(feature = "rustls-platform-verifier"),
+ feature = "rustls-native-certs",
+ ),
+ default
+ )]
NativeCerts,
#[cfg(feature = "webpki-roots")]
#[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))]
#[cfg_attr(
- all(feature = "webpki-roots", not(feature = "rustls-native-certs")),
+ all(
+ not(feature = "rustls-platform-verifier"),
+ not(feature = "rustls-native-certs"),
+ feature = "webpki-roots",
+ ),
default
)]
WebpkiRoots,
#[cfg_attr(
- all(not(feature = "webpki-roots"), not(feature = "rustls-native-certs")),
+ all(
+ not(feature = "webpki-roots"),
+ not(feature = "rustls-platform-verifier"),
+ not(feature = "rustls-native-certs")
+ ),
default
)]
None,
diff --git a/src/transport/smtp/pool/async_impl.rs b/src/transport/smtp/pool/async_impl.rs
index 7f17be8..d1af45a 100644
--- a/src/transport/smtp/pool/async_impl.rs
+++ b/src/transport/smtp/pool/async_impl.rs
@@ -149,11 +149,7 @@ impl Pool {
pub(crate) async fn shutdown(&self) {
let connections = { self.connections.lock().await.take() };
if let Some(connections) = connections {
- stream::iter(connections)
- .for_each_concurrent(8, |conn| async move {
- conn.unpark().abort().await;
- })
- .await;
+ abort_concurrent(connections.into_iter().map(ParkedConnection::unpark)).await;
}
if let Some(handle) = self.handle.get() {
diff --git a/src/transport/stub/mod.rs b/src/transport/stub/mod.rs
index a76bceb..d9546fb 100644
--- a/src/transport/stub/mod.rs
+++ b/src/transport/stub/mod.rs
@@ -43,13 +43,11 @@
use std::{
error::Error as StdError,
fmt,
- sync::{Arc, Mutex as StdMutex},
+ sync::{Arc, Mutex},
};
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
use async_trait::async_trait;
-#[cfg(any(feature = "tokio1", feature = "async-std1"))]
-use futures_util::lock::Mutex as FuturesMutex;
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
use crate::AsyncTransport;
@@ -72,7 +70,7 @@ impl StdError for Error {}
#[derive(Debug, Clone)]
pub struct StubTransport {
response: Result<(), Error>,
- message_log: Arc>>,
+ message_log: Arc>>,
}
/// This transport logs messages and always returns the given response
@@ -81,7 +79,7 @@ pub struct StubTransport {
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio1", feature = "async-std1"))))]
pub struct AsyncStubTransport {
response: Result<(), Error>,
- message_log: Arc>>,
+ message_log: Arc>>,
}
impl StubTransport {
@@ -89,7 +87,7 @@ impl StubTransport {
pub fn new(response: Result<(), Error>) -> Self {
Self {
response,
- message_log: Arc::new(StdMutex::new(vec![])),
+ message_log: Arc::new(Mutex::new(vec![])),
}
}
@@ -97,7 +95,7 @@ impl StubTransport {
pub fn new_ok() -> Self {
Self {
response: Ok(()),
- message_log: Arc::new(StdMutex::new(vec![])),
+ message_log: Arc::new(Mutex::new(vec![])),
}
}
@@ -105,7 +103,7 @@ impl StubTransport {
pub fn new_error() -> Self {
Self {
response: Err(Error),
- message_log: Arc::new(StdMutex::new(vec![])),
+ message_log: Arc::new(Mutex::new(vec![])),
}
}
@@ -124,7 +122,7 @@ impl AsyncStubTransport {
pub fn new(response: Result<(), Error>) -> Self {
Self {
response,
- message_log: Arc::new(FuturesMutex::new(vec![])),
+ message_log: Arc::new(Mutex::new(vec![])),
}
}
@@ -132,7 +130,7 @@ impl AsyncStubTransport {
pub fn new_ok() -> Self {
Self {
response: Ok(()),
- message_log: Arc::new(FuturesMutex::new(vec![])),
+ message_log: Arc::new(Mutex::new(vec![])),
}
}
@@ -140,14 +138,14 @@ impl AsyncStubTransport {
pub fn new_error() -> Self {
Self {
response: Err(Error),
- message_log: Arc::new(FuturesMutex::new(vec![])),
+ message_log: Arc::new(Mutex::new(vec![])),
}
}
/// Return all logged messages sent using [`AsyncTransport::send_raw`]
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
pub async fn messages(&self) -> Vec<(Envelope, String)> {
- self.message_log.lock().await.clone()
+ self.message_log.lock().unwrap().clone()
}
}
@@ -173,7 +171,7 @@ impl AsyncTransport for AsyncStubTransport {
async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result {
self.message_log
.lock()
- .await
+ .unwrap()
.push((envelope.clone(), String::from_utf8_lossy(email).into()));
self.response
}