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 @@
- - + dependency status
@@ -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 }