Compare commits

..

1 Commits

Author SHA1 Message Date
Paolo Barbolini
5793a1b9a9 docs: clarify that Credentials::new takes bearer token for xoauth2 2025-03-25 01:42:56 +01:00
20 changed files with 264 additions and 642 deletions

View File

@@ -1,57 +1,3 @@
<a name="v0.11.17"></a>
### v0.11.17 (2025-06-06)
#### Features
* Add support for `rustls-platform-verifier` ([#1081])
#### Misc
* Change readme example to use `Mailbox::new` instead of string parsing ([#1090])
* Replace futures-util `Mutex` with std `Mutex` in `AsyncStubTransport` ([#1091])
* Avoid duplicate `abort_concurrent` implementation ([#1092])
[#1081]: https://github.com/lettre/lettre/pull/1081
[#1090]: https://github.com/lettre/lettre/pull/1090
[#1091]: https://github.com/lettre/lettre/pull/1091
[#1092]: https://github.com/lettre/lettre/pull/1092
<a name="v0.11.16"></a>
### 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
<a name="v0.11.15"></a> <a name="v0.11.15"></a>
### v0.11.15 (2025-03-10) ### v0.11.15 (2025-03-10)

464
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "lettre" name = "lettre"
# remember to update html_root_url and README.md (Cargo.toml example and deps.rs badge) # remember to update html_root_url and README.md (Cargo.toml example and deps.rs badge)
version = "0.11.17" version = "0.11.15"
description = "Email client" description = "Email client"
readme = "README.md" readme = "README.md"
homepage = "https://lettre.rs" homepage = "https://lettre.rs"
@@ -49,9 +49,8 @@ percent-encoding = { version = "2.3", optional = true }
## tls ## tls
native-tls = { version = "0.2.9", optional = true } # feature native-tls = { version = "0.2.9", optional = true } # feature
rustls = { version = "0.23.18", default-features = false, features = ["logging", "std", "tls12"], optional = true } 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 } rustls-native-certs = { version = "0.8", optional = true }
webpki-roots = { version = "1.0.0", optional = true } webpki-roots = { version = "0.26", optional = true }
boring = { version = "4", optional = true } boring = { version = "4", optional = true }
# async # async
@@ -74,7 +73,6 @@ sha2 = { version = "0.10", features = ["oid"], optional = true }
rsa = { version = "0.9", optional = true } rsa = { version = "0.9", optional = true }
ed25519-dalek = { version = "2", optional = true } ed25519-dalek = { version = "2", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]
## web-time for wasm support ## web-time for wasm support
web-time = { version = "1.1.0", optional = true } web-time = { version = "1.1.0", optional = true }

View File

@@ -28,8 +28,8 @@
</div> </div>
<div align="center"> <div align="center">
<a href="https://deps.rs/crate/lettre/0.11.17"> <a href="https://deps.rs/crate/lettre/0.11.15">
<img src="https://deps.rs/crate/lettre/0.11.17/status.svg" <img src="https://deps.rs/crate/lettre/0.11.15/status.svg"
alt="dependency status" /> alt="dependency status" />
</a> </a>
</div> </div>
@@ -73,9 +73,9 @@ use lettre::{Message, SmtpTransport, Transport};
fn main() { fn main() {
let email = Message::builder() let email = Message::builder()
.from(Mailbox::new("NoBody".to_owned(), "nobody@domain.tld".parse().unwrap())) .from("NoBody <nobody@domain.tld>".parse().unwrap())
.reply_to(Mailbox::new("Yuin".to_owned(), "yuin@domain.tld".parse().unwrap())) .reply_to("Yuin <yuin@domain.tld>".parse().unwrap())
.to(Mailbox::new("Hei".to_owned(), "hei@domain.tld".parse().unwrap())) .to("Hei <hei@domain.tld>".parse().unwrap())
.subject("Happy new year") .subject("Happy new year")
.header(ContentType::TEXT_PLAIN) .header(ContentType::TEXT_PLAIN)
.body(String::from("Be happy!")) .body(String::from("Be happy!"))

View File

@@ -163,7 +163,6 @@ impl Envelope {
} }
#[cfg(feature = "builder")] #[cfg(feature = "builder")]
#[cfg_attr(docsrs, doc(cfg(feature = "builder")))]
impl TryFrom<&Headers> for Envelope { impl TryFrom<&Headers> for Envelope {
type Error = Error; type Error = Error;

View File

@@ -93,20 +93,17 @@
//! When the `rustls` feature is enabled, one of the following verification backends //! When the `rustls` feature is enabled, one of the following verification backends
//! MUST also be enabled. //! MUST also be enabled.
//! //!
//! * **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`])
//! * **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`]) //! * **webpki-roots**: verify TLS certificates against Mozilla's root certificates (see [`webpki-roots`])
//! //!
//! The following packages will need to be installed in order for the build //! For the `rustls-native-certs` backend to work correctly, the following packages
//! stage and the compiled program to run properly. //! will need to be installed in order for the build stage and the compiled program
//! to run properly.
//! //!
//! | Verification backend | Distro | Build-time packages | Runtime packages | //! | Distro | Build-time packages | Runtime packages |
//! | --------------------- | ------------ | -------------------------- | ---------------------------- | //! | ------------ | -------------------------- | ---------------------------- |
//! | `rustls-platform-verifier` | Debian | none | `ca-certificates` | //! | Debian | none | `ca-certificates` |
//! | `rustls-platform-verifier` | Alpine Linux | none | `ca-certificates` | //! | 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 //! ### Sendmail transport
//! //!
@@ -154,7 +151,6 @@
//! [AWS-LC]: https://github.com/aws/aws-lc //! [AWS-LC]: https://github.com/aws/aws-lc
//! [`aws-lc-rs`]: https://crates.io/crates/aws-lc-rs //! [`aws-lc-rs`]: https://crates.io/crates/aws-lc-rs
//! [`ring`]: https://crates.io/crates/ring //! [`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 //! [`rustls-native-certs`]: https://crates.io/crates/rustls-native-certs
//! [`webpki-roots`]: https://crates.io/crates/webpki-roots //! [`webpki-roots`]: https://crates.io/crates/webpki-roots
//! [Tokio 1.x]: https://docs.rs/tokio/1 //! [Tokio 1.x]: https://docs.rs/tokio/1
@@ -162,7 +158,7 @@
//! [mime 0.3]: https://docs.rs/mime/0.3 //! [mime 0.3]: https://docs.rs/mime/0.3
//! [DKIM]: https://datatracker.ietf.org/doc/html/rfc6376 //! [DKIM]: https://datatracker.ietf.org/doc/html/rfc6376
#![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.17")] #![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.15")]
#![doc(html_favicon_url = "https://lettre.rs/favicon.ico")] #![doc(html_favicon_url = "https://lettre.rs/favicon.ico")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")] #![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")]
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
@@ -212,13 +208,12 @@ mod compiletime_checks {
#[cfg(all( #[cfg(all(
feature = "rustls", feature = "rustls",
not(feature = "rustls-platform-verifier"),
not(feature = "rustls-native-certs"), not(feature = "rustls-native-certs"),
not(feature = "webpki-roots") not(feature = "webpki-roots")
))] ))]
compile_error!( compile_error!(
"feature `rustls` also requires either the `rustls-platform-verifier`, the `rustls-native-certs` "feature `rustls` also requires either the `rustls-native-certs` or the `webpki-roots` feature to
or the `webpki-roots` feature to be enabled" be enabled"
); );
#[cfg(all(feature = "native-tls", feature = "boring-tls"))] #[cfg(all(feature = "native-tls", feature = "boring-tls"))]

View File

@@ -1,6 +1,6 @@
use std::time::SystemTime; use std::time::SystemTime;
#[cfg(all(feature = "web", target_arch = "wasm32"))] #[cfg(feature = "web")]
pub(crate) fn now() -> SystemTime { pub(crate) fn now() -> SystemTime {
fn to_std_systemtime(time: web_time::SystemTime) -> std::time::SystemTime { fn to_std_systemtime(time: web_time::SystemTime) -> std::time::SystemTime {
let duration = time let duration = time
@@ -18,7 +18,7 @@ pub(crate) fn now() -> SystemTime {
to_std_systemtime(web_time::SystemTime::now()) to_std_systemtime(web_time::SystemTime::now())
} }
#[cfg(not(all(feature = "web", target_arch = "wasm32")))] #[cfg(not(feature = "web"))]
pub(crate) fn now() -> SystemTime { pub(crate) fn now() -> SystemTime {
// FIXME: change to #[expect(clippy::disallowed_methods, reason = "the `web` feature is disabled")] // FIXME: change to #[expect(clippy::disallowed_methods, reason = "the `web` feature is disabled")]
#[allow(clippy::disallowed_methods)] #[allow(clippy::disallowed_methods)]

View File

@@ -34,7 +34,6 @@ impl Error {
/// Returns true if the error is an envelope serialization or deserialization error /// Returns true if the error is an envelope serialization or deserialization error
#[cfg(feature = "file-transport-envelope")] #[cfg(feature = "file-transport-envelope")]
#[cfg_attr(docsrs, doc(cfg(feature = "file-transport-envelope")))]
pub fn is_envelope(&self) -> bool { pub fn is_envelope(&self) -> bool {
matches!(self.inner.kind, Kind::Envelope) matches!(self.inner.kind, Kind::Envelope)
} }

View File

@@ -173,7 +173,7 @@ pub struct FileTransport {
} }
/// Asynchronously writes the content and the envelope information to a file /// Asynchronously writes the content and the envelope information to a file
#[derive(Debug)] #[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio1", feature = "async-std1"))))] #[cfg_attr(docsrs, doc(cfg(any(feature = "tokio1", feature = "async-std1"))))]
#[cfg(any(feature = "async-std1", feature = "tokio1"))] #[cfg(any(feature = "async-std1", feature = "tokio1"))]
@@ -199,7 +199,6 @@ impl FileTransport {
/// Writes the email content in eml format and the envelope /// Writes the email content in eml format and the envelope
/// in json format. /// in json format.
#[cfg(feature = "file-transport-envelope")] #[cfg(feature = "file-transport-envelope")]
#[cfg_attr(docsrs, doc(cfg(feature = "file-transport-envelope")))]
pub fn with_envelope<P: AsRef<Path>>(path: P) -> FileTransport { pub fn with_envelope<P: AsRef<Path>>(path: P) -> FileTransport {
FileTransport { FileTransport {
path: PathBuf::from(path.as_ref()), path: PathBuf::from(path.as_ref()),
@@ -212,7 +211,6 @@ impl FileTransport {
/// ///
/// Reads the envelope and the raw message content. /// Reads the envelope and the raw message content.
#[cfg(feature = "file-transport-envelope")] #[cfg(feature = "file-transport-envelope")]
#[cfg_attr(docsrs, doc(cfg(feature = "file-transport-envelope")))]
pub fn read(&self, email_id: &str) -> Result<(Envelope, Vec<u8>), Error> { pub fn read(&self, email_id: &str) -> Result<(Envelope, Vec<u8>), Error> {
use std::fs; use std::fs;
@@ -251,7 +249,6 @@ where
/// Writes the email content in eml format and the envelope /// Writes the email content in eml format and the envelope
/// in json format. /// in json format.
#[cfg(feature = "file-transport-envelope")] #[cfg(feature = "file-transport-envelope")]
#[cfg_attr(docsrs, doc(cfg(feature = "file-transport-envelope")))]
pub fn with_envelope<P: AsRef<Path>>(path: P) -> Self { pub fn with_envelope<P: AsRef<Path>>(path: P) -> Self {
Self { Self {
inner: FileTransport::with_envelope(path), inner: FileTransport::with_envelope(path),
@@ -263,7 +260,6 @@ where
/// ///
/// Reads the envelope and the raw message content. /// Reads the envelope and the raw message content.
#[cfg(feature = "file-transport-envelope")] #[cfg(feature = "file-transport-envelope")]
#[cfg_attr(docsrs, doc(cfg(feature = "file-transport-envelope")))]
pub async fn read(&self, email_id: &str) -> Result<(Envelope, Vec<u8>), Error> { pub async fn read(&self, email_id: &str) -> Result<(Envelope, Vec<u8>), Error> {
let eml_file = self.inner.path.join(format!("{email_id}.eml")); let eml_file = self.inner.path.join(format!("{email_id}.eml"));
let eml = E::fs_read(&eml_file).await.map_err(error::io)?; let eml = E::fs_read(&eml_file).await.map_err(error::io)?;
@@ -276,16 +272,6 @@ where
} }
} }
#[cfg(any(feature = "async-std1", feature = "tokio1"))]
impl<E: Executor> Clone for AsyncFileTransport<E> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
marker_: PhantomData,
}
}
}
impl Transport for FileTransport { impl Transport for FileTransport {
type Ok = Id; type Ok = Id;
type Error = Error; type Error = Error;

View File

@@ -234,7 +234,7 @@ where
/// a proper URL encoder, like the following cargo script: /// a proper URL encoder, like the following cargo script:
/// ///
/// ```rust /// ```rust
/// # const TOML: &str = r#" /// # let _ = r#"
/// #!/usr/bin/env cargo /// #!/usr/bin/env cargo
/// ///
/// //! ```cargo /// //! ```cargo

View File

@@ -19,6 +19,9 @@ pub struct Credentials {
impl Credentials { impl Credentials {
/// Create a `Credentials` struct from username and password /// Create a `Credentials` struct from username and password
///
/// When using [`Mechanism::Xoauth2`], `password` is the raw
/// bearer access token.
pub fn new(username: String, password: String) -> Credentials { pub fn new(username: String, password: String) -> Credentials {
Credentials { Credentials {
authentication_identity: username, authentication_identity: username,

View File

@@ -54,7 +54,6 @@ impl AsyncSmtpConnection {
/// ///
/// Sends EHLO and parses server information /// Sends EHLO and parses server information
#[cfg(feature = "tokio1")] #[cfg(feature = "tokio1")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio1")))]
pub async fn connect_with_transport( pub async fn connect_with_transport(
stream: Box<dyn AsyncTokioStream>, stream: Box<dyn AsyncTokioStream>,
hello_name: &ClientId, hello_name: &ClientId,
@@ -95,7 +94,6 @@ impl AsyncSmtpConnection {
/// # } /// # }
/// ``` /// ```
#[cfg(feature = "tokio1")] #[cfg(feature = "tokio1")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio1")))]
pub async fn connect_tokio1<T: tokio1_crate::net::ToSocketAddrs>( pub async fn connect_tokio1<T: tokio1_crate::net::ToSocketAddrs>(
server: T, server: T,
timeout: Option<Duration>, timeout: Option<Duration>,
@@ -114,7 +112,6 @@ impl AsyncSmtpConnection {
/// ///
/// Sends EHLO and parses server information /// Sends EHLO and parses server information
#[cfg(feature = "async-std1")] #[cfg(feature = "async-std1")]
#[cfg_attr(docsrs, doc(cfg(feature = "async-std1")))]
pub async fn connect_asyncstd1<T: async_std::net::ToSocketAddrs>( pub async fn connect_asyncstd1<T: async_std::net::ToSocketAddrs>(
server: T, server: T,
timeout: Option<Duration>, timeout: Option<Duration>,
@@ -379,10 +376,6 @@ impl AsyncSmtpConnection {
/// The X509 certificate of the server (DER encoded) /// The X509 certificate of the server (DER encoded)
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))] #[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn peer_certificate(&self) -> Result<Vec<u8>, Error> { pub fn peer_certificate(&self) -> Result<Vec<u8>, Error> {
self.stream.get_ref().peer_certificate() self.stream.get_ref().peer_certificate()
} }
@@ -399,14 +392,12 @@ impl AsyncSmtpConnection {
/// as the TLSA records match the leaf or issuer certificates. /// as the TLSA records match the leaf or issuer certificates.
/// It cannot be called on non Boring TLS streams. /// It cannot be called on non Boring TLS streams.
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "boring-tls")))]
pub fn tls_verify_result(&self) -> Result<(), Error> { pub fn tls_verify_result(&self) -> Result<(), Error> {
self.stream.get_ref().tls_verify_result() self.stream.get_ref().tls_verify_result()
} }
/// All the X509 certificates of the chain (DER encoded) /// All the X509 certificates of the chain (DER encoded)
#[cfg(any(feature = "rustls", feature = "boring-tls"))] #[cfg(any(feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "rustls", feature = "boring-tls"))))]
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> { pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
self.stream.get_ref().certificate_chain() self.stream.get_ref().certificate_chain()
} }

View File

@@ -9,11 +9,11 @@ use std::{
#[cfg(feature = "async-std1")] #[cfg(feature = "async-std1")]
use async_std::net::{TcpStream as AsyncStd1TcpStream, ToSocketAddrs as AsyncStd1ToSocketAddrs}; use async_std::net::{TcpStream as AsyncStd1TcpStream, ToSocketAddrs as AsyncStd1ToSocketAddrs};
use futures_io::{ use futures_io::{
AsyncRead as FuturesAsyncRead, AsyncWrite as FuturesAsyncWrite, Error as IoError, AsyncRead as FuturesAsyncRead, AsyncWrite as FuturesAsyncWrite, Error as IoError, ErrorKind,
Result as IoResult, Result as IoResult,
}; };
#[cfg(feature = "async-std1-rustls")] #[cfg(feature = "async-std1-rustls")]
use futures_rustls::client::TlsStream as AsyncStd1RustlsStream; use futures_rustls::client::TlsStream as AsyncStd1RustlsTlsStream;
#[cfg(any(feature = "tokio1-rustls", feature = "async-std1-rustls"))] #[cfg(any(feature = "tokio1-rustls", feature = "async-std1-rustls"))]
use rustls::pki_types::ServerName; use rustls::pki_types::ServerName;
#[cfg(feature = "tokio1-boring-tls")] #[cfg(feature = "tokio1-boring-tls")]
@@ -28,7 +28,7 @@ use tokio1_crate::net::{
#[cfg(feature = "tokio1-native-tls")] #[cfg(feature = "tokio1-native-tls")]
use tokio1_native_tls_crate::TlsStream as Tokio1TlsStream; use tokio1_native_tls_crate::TlsStream as Tokio1TlsStream;
#[cfg(feature = "tokio1-rustls")] #[cfg(feature = "tokio1-rustls")]
use tokio1_rustls::client::TlsStream as Tokio1RustlsStream; use tokio1_rustls::client::TlsStream as Tokio1RustlsTlsStream;
#[cfg(any( #[cfg(any(
feature = "tokio1-native-tls", feature = "tokio1-native-tls",
@@ -79,7 +79,7 @@ enum InnerAsyncNetworkStream {
Tokio1NativeTls(Tokio1TlsStream<Box<dyn AsyncTokioStream>>), Tokio1NativeTls(Tokio1TlsStream<Box<dyn AsyncTokioStream>>),
/// Encrypted Tokio 1.x TCP stream /// Encrypted Tokio 1.x TCP stream
#[cfg(feature = "tokio1-rustls")] #[cfg(feature = "tokio1-rustls")]
Tokio1Rustls(Tokio1RustlsStream<Box<dyn AsyncTokioStream>>), Tokio1RustlsTls(Tokio1RustlsTlsStream<Box<dyn AsyncTokioStream>>),
/// Encrypted Tokio 1.x TCP stream /// Encrypted Tokio 1.x TCP stream
#[cfg(feature = "tokio1-boring-tls")] #[cfg(feature = "tokio1-boring-tls")]
Tokio1BoringTls(Tokio1SslStream<Box<dyn AsyncTokioStream>>), Tokio1BoringTls(Tokio1SslStream<Box<dyn AsyncTokioStream>>),
@@ -88,7 +88,7 @@ enum InnerAsyncNetworkStream {
AsyncStd1Tcp(AsyncStd1TcpStream), AsyncStd1Tcp(AsyncStd1TcpStream),
/// Encrypted Tokio 1.x TCP stream /// Encrypted Tokio 1.x TCP stream
#[cfg(feature = "async-std1-rustls")] #[cfg(feature = "async-std1-rustls")]
AsyncStd1Rustls(AsyncStd1RustlsStream<AsyncStd1TcpStream>), AsyncStd1RustlsTls(AsyncStd1RustlsTlsStream<AsyncStd1TcpStream>),
/// Can't be built /// Can't be built
None, None,
} }
@@ -113,16 +113,17 @@ impl AsyncNetworkStream {
s.get_ref().get_ref().get_ref().peer_addr() s.get_ref().get_ref().get_ref().peer_addr()
} }
#[cfg(feature = "tokio1-rustls")] #[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1Rustls(s) => s.get_ref().0.peer_addr(), InnerAsyncNetworkStream::Tokio1RustlsTls(s) => s.get_ref().0.peer_addr(),
#[cfg(feature = "tokio1-boring-tls")] #[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(s) => s.get_ref().peer_addr(), InnerAsyncNetworkStream::Tokio1BoringTls(s) => s.get_ref().peer_addr(),
#[cfg(feature = "async-std1")] #[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(s) => s.peer_addr(), InnerAsyncNetworkStream::AsyncStd1Tcp(s) => s.peer_addr(),
#[cfg(feature = "async-std1-rustls")] #[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1Rustls(s) => s.get_ref().0.peer_addr(), InnerAsyncNetworkStream::AsyncStd1RustlsTls(s) => s.get_ref().0.peer_addr(),
InnerAsyncNetworkStream::None => { InnerAsyncNetworkStream::None => {
debug_assert!(false, "InnerAsyncNetworkStream::None must never be built"); debug_assert!(false, "InnerAsyncNetworkStream::None must never be built");
Err(IoError::other( Err(IoError::new(
ErrorKind::Other,
"InnerAsyncNetworkStream::None must never be built", "InnerAsyncNetworkStream::None must never be built",
)) ))
} }
@@ -130,13 +131,11 @@ impl AsyncNetworkStream {
} }
#[cfg(feature = "tokio1")] #[cfg(feature = "tokio1")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio1")))]
pub fn use_existing_tokio1(stream: Box<dyn AsyncTokioStream>) -> AsyncNetworkStream { pub fn use_existing_tokio1(stream: Box<dyn AsyncTokioStream>) -> AsyncNetworkStream {
AsyncNetworkStream::new(InnerAsyncNetworkStream::Tokio1Tcp(stream)) AsyncNetworkStream::new(InnerAsyncNetworkStream::Tokio1Tcp(stream))
} }
#[cfg(feature = "tokio1")] #[cfg(feature = "tokio1")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio1")))]
pub async fn connect_tokio1<T: Tokio1ToSocketAddrs>( pub async fn connect_tokio1<T: Tokio1ToSocketAddrs>(
server: T, server: T,
timeout: Option<Duration>, timeout: Option<Duration>,
@@ -203,7 +202,6 @@ impl AsyncNetworkStream {
} }
#[cfg(feature = "async-std1")] #[cfg(feature = "async-std1")]
#[cfg_attr(docsrs, doc(cfg(feature = "async-std1")))]
pub async fn connect_asyncstd1<T: AsyncStd1ToSocketAddrs>( pub async fn connect_asyncstd1<T: AsyncStd1ToSocketAddrs>(
server: T, server: T,
timeout: Option<Duration>, timeout: Option<Duration>,
@@ -323,7 +321,7 @@ impl AsyncNetworkStream {
match tls_parameters.connector { match tls_parameters.connector {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerTlsParameters::NativeTls { connector } => { InnerTlsParameters::NativeTls(connector) => {
#[cfg(not(feature = "tokio1-native-tls"))] #[cfg(not(feature = "tokio1-native-tls"))]
panic!("built without the tokio1-native-tls feature"); panic!("built without the tokio1-native-tls feature");
@@ -340,7 +338,7 @@ impl AsyncNetworkStream {
}; };
} }
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerTlsParameters::Rustls { config } => { InnerTlsParameters::RustlsTls(config) => {
#[cfg(not(feature = "tokio1-rustls"))] #[cfg(not(feature = "tokio1-rustls"))]
panic!("built without the tokio1-rustls feature"); panic!("built without the tokio1-rustls feature");
@@ -356,21 +354,18 @@ impl AsyncNetworkStream {
.connect(domain.to_owned(), tcp_stream) .connect(domain.to_owned(), tcp_stream)
.await .await
.map_err(error::connection)?; .map_err(error::connection)?;
Ok(InnerAsyncNetworkStream::Tokio1Rustls(stream)) Ok(InnerAsyncNetworkStream::Tokio1RustlsTls(stream))
}; };
} }
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerTlsParameters::BoringTls { InnerTlsParameters::BoringTls(connector) => {
connector,
accept_invalid_hostnames,
} => {
#[cfg(not(feature = "tokio1-boring-tls"))] #[cfg(not(feature = "tokio1-boring-tls"))]
panic!("built without the tokio1-boring-tls feature"); panic!("built without the tokio1-boring-tls feature");
#[cfg(feature = "tokio1-boring-tls")] #[cfg(feature = "tokio1-boring-tls")]
return { return {
let mut config = connector.configure().map_err(error::connection)?; let mut config = connector.configure().map_err(error::connection)?;
config.set_verify_hostname(accept_invalid_hostnames); config.set_verify_hostname(tls_parameters.accept_invalid_hostnames);
let stream = tokio1_boring::connect(config, &domain, tcp_stream) let stream = tokio1_boring::connect(config, &domain, tcp_stream)
.await .await
@@ -391,11 +386,11 @@ impl AsyncNetworkStream {
match tls_parameters.connector { match tls_parameters.connector {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerTlsParameters::NativeTls { connector } => { InnerTlsParameters::NativeTls(connector) => {
panic!("native-tls isn't supported with async-std yet. See https://github.com/lettre/lettre/pull/531#issuecomment-757893531"); panic!("native-tls isn't supported with async-std yet. See https://github.com/lettre/lettre/pull/531#issuecomment-757893531");
} }
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerTlsParameters::Rustls { config } => { InnerTlsParameters::RustlsTls(config) => {
#[cfg(not(feature = "async-std1-rustls"))] #[cfg(not(feature = "async-std1-rustls"))]
panic!("built without the async-std1-rustls feature"); panic!("built without the async-std1-rustls feature");
@@ -411,11 +406,11 @@ impl AsyncNetworkStream {
.connect(domain.to_owned(), tcp_stream) .connect(domain.to_owned(), tcp_stream)
.await .await
.map_err(error::connection)?; .map_err(error::connection)?;
Ok(InnerAsyncNetworkStream::AsyncStd1Rustls(stream)) Ok(InnerAsyncNetworkStream::AsyncStd1RustlsTls(stream))
}; };
} }
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerTlsParameters::BoringTls { .. } => { InnerTlsParameters::BoringTls(connector) => {
panic!("boring-tls isn't supported with async-std yet."); panic!("boring-tls isn't supported with async-std yet.");
} }
} }
@@ -428,19 +423,18 @@ impl AsyncNetworkStream {
#[cfg(feature = "tokio1-native-tls")] #[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(_) => true, InnerAsyncNetworkStream::Tokio1NativeTls(_) => true,
#[cfg(feature = "tokio1-rustls")] #[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1Rustls(_) => true, InnerAsyncNetworkStream::Tokio1RustlsTls(_) => true,
#[cfg(feature = "tokio1-boring-tls")] #[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(_) => true, InnerAsyncNetworkStream::Tokio1BoringTls(_) => true,
#[cfg(feature = "async-std1")] #[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(_) => false, InnerAsyncNetworkStream::AsyncStd1Tcp(_) => false,
#[cfg(feature = "async-std1-rustls")] #[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1Rustls(_) => true, InnerAsyncNetworkStream::AsyncStd1RustlsTls(_) => true,
InnerAsyncNetworkStream::None => false, InnerAsyncNetworkStream::None => false,
} }
} }
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "boring-tls")))]
pub fn tls_verify_result(&self) -> Result<(), Error> { pub fn tls_verify_result(&self) -> Result<(), Error> {
match &self.inner { match &self.inner {
#[cfg(feature = "tokio1")] #[cfg(feature = "tokio1")]
@@ -450,7 +444,7 @@ impl AsyncNetworkStream {
#[cfg(feature = "tokio1-native-tls")] #[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(_) => panic!("Unsupported"), InnerAsyncNetworkStream::Tokio1NativeTls(_) => panic!("Unsupported"),
#[cfg(feature = "tokio1-rustls")] #[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1Rustls(_) => panic!("Unsupported"), InnerAsyncNetworkStream::Tokio1RustlsTls(_) => panic!("Unsupported"),
#[cfg(feature = "tokio1-boring-tls")] #[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(stream) => { InnerAsyncNetworkStream::Tokio1BoringTls(stream) => {
stream.ssl().verify_result().map_err(error::tls) stream.ssl().verify_result().map_err(error::tls)
@@ -460,11 +454,10 @@ impl AsyncNetworkStream {
Err(error::client("Connection is not encrypted")) Err(error::client("Connection is not encrypted"))
} }
#[cfg(feature = "async-std1-rustls")] #[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1Rustls(_) => panic!("Unsupported"), InnerAsyncNetworkStream::AsyncStd1RustlsTls(_) => panic!("Unsupported"),
InnerAsyncNetworkStream::None => panic!("InnerNetworkStream::None must never be built"), InnerAsyncNetworkStream::None => panic!("InnerNetworkStream::None must never be built"),
} }
} }
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> { pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
match &self.inner { match &self.inner {
#[cfg(feature = "tokio1")] #[cfg(feature = "tokio1")]
@@ -474,7 +467,7 @@ impl AsyncNetworkStream {
#[cfg(feature = "tokio1-native-tls")] #[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(_) => panic!("Unsupported"), InnerAsyncNetworkStream::Tokio1NativeTls(_) => panic!("Unsupported"),
#[cfg(feature = "tokio1-rustls")] #[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1Rustls(stream) => Ok(stream InnerAsyncNetworkStream::Tokio1RustlsTls(stream) => Ok(stream
.get_ref() .get_ref()
.1 .1
.peer_certificates() .peer_certificates()
@@ -495,7 +488,7 @@ impl AsyncNetworkStream {
Err(error::client("Connection is not encrypted")) Err(error::client("Connection is not encrypted"))
} }
#[cfg(feature = "async-std1-rustls")] #[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1Rustls(stream) => Ok(stream InnerAsyncNetworkStream::AsyncStd1RustlsTls(stream) => Ok(stream
.get_ref() .get_ref()
.1 .1
.peer_certificates() .peer_certificates()
@@ -522,7 +515,7 @@ impl AsyncNetworkStream {
.to_der() .to_der()
.map_err(error::tls)?), .map_err(error::tls)?),
#[cfg(feature = "tokio1-rustls")] #[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1Rustls(stream) => Ok(stream InnerAsyncNetworkStream::Tokio1RustlsTls(stream) => Ok(stream
.get_ref() .get_ref()
.1 .1
.peer_certificates() .peer_certificates()
@@ -542,7 +535,7 @@ impl AsyncNetworkStream {
Err(error::client("Connection is not encrypted")) Err(error::client("Connection is not encrypted"))
} }
#[cfg(feature = "async-std1-rustls")] #[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1Rustls(stream) => Ok(stream InnerAsyncNetworkStream::AsyncStd1RustlsTls(stream) => Ok(stream
.get_ref() .get_ref()
.1 .1
.peer_certificates() .peer_certificates()
@@ -582,7 +575,7 @@ impl FuturesAsyncRead for AsyncNetworkStream {
} }
} }
#[cfg(feature = "tokio1-rustls")] #[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1Rustls(s) => { InnerAsyncNetworkStream::Tokio1RustlsTls(s) => {
let mut b = Tokio1ReadBuf::new(buf); let mut b = Tokio1ReadBuf::new(buf);
match Pin::new(s).poll_read(cx, &mut b) { match Pin::new(s).poll_read(cx, &mut b) {
Poll::Ready(Ok(())) => Poll::Ready(Ok(b.filled().len())), Poll::Ready(Ok(())) => Poll::Ready(Ok(b.filled().len())),
@@ -602,7 +595,7 @@ impl FuturesAsyncRead for AsyncNetworkStream {
#[cfg(feature = "async-std1")] #[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(s) => Pin::new(s).poll_read(cx, buf), InnerAsyncNetworkStream::AsyncStd1Tcp(s) => Pin::new(s).poll_read(cx, buf),
#[cfg(feature = "async-std1-rustls")] #[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1Rustls(s) => Pin::new(s).poll_read(cx, buf), InnerAsyncNetworkStream::AsyncStd1RustlsTls(s) => Pin::new(s).poll_read(cx, buf),
InnerAsyncNetworkStream::None => { InnerAsyncNetworkStream::None => {
debug_assert!(false, "InnerAsyncNetworkStream::None must never be built"); debug_assert!(false, "InnerAsyncNetworkStream::None must never be built");
Poll::Ready(Ok(0)) Poll::Ready(Ok(0))
@@ -624,13 +617,13 @@ impl FuturesAsyncWrite for AsyncNetworkStream {
#[cfg(feature = "tokio1-native-tls")] #[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(s) => Pin::new(s).poll_write(cx, buf), InnerAsyncNetworkStream::Tokio1NativeTls(s) => Pin::new(s).poll_write(cx, buf),
#[cfg(feature = "tokio1-rustls")] #[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1Rustls(s) => Pin::new(s).poll_write(cx, buf), InnerAsyncNetworkStream::Tokio1RustlsTls(s) => Pin::new(s).poll_write(cx, buf),
#[cfg(feature = "tokio1-boring-tls")] #[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(s) => Pin::new(s).poll_write(cx, buf), InnerAsyncNetworkStream::Tokio1BoringTls(s) => Pin::new(s).poll_write(cx, buf),
#[cfg(feature = "async-std1")] #[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(s) => Pin::new(s).poll_write(cx, buf), InnerAsyncNetworkStream::AsyncStd1Tcp(s) => Pin::new(s).poll_write(cx, buf),
#[cfg(feature = "async-std1-rustls")] #[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1Rustls(s) => Pin::new(s).poll_write(cx, buf), InnerAsyncNetworkStream::AsyncStd1RustlsTls(s) => Pin::new(s).poll_write(cx, buf),
InnerAsyncNetworkStream::None => { InnerAsyncNetworkStream::None => {
debug_assert!(false, "InnerAsyncNetworkStream::None must never be built"); debug_assert!(false, "InnerAsyncNetworkStream::None must never be built");
Poll::Ready(Ok(0)) Poll::Ready(Ok(0))
@@ -645,13 +638,13 @@ impl FuturesAsyncWrite for AsyncNetworkStream {
#[cfg(feature = "tokio1-native-tls")] #[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(s) => Pin::new(s).poll_flush(cx), InnerAsyncNetworkStream::Tokio1NativeTls(s) => Pin::new(s).poll_flush(cx),
#[cfg(feature = "tokio1-rustls")] #[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1Rustls(s) => Pin::new(s).poll_flush(cx), InnerAsyncNetworkStream::Tokio1RustlsTls(s) => Pin::new(s).poll_flush(cx),
#[cfg(feature = "tokio1-boring-tls")] #[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(s) => Pin::new(s).poll_flush(cx), InnerAsyncNetworkStream::Tokio1BoringTls(s) => Pin::new(s).poll_flush(cx),
#[cfg(feature = "async-std1")] #[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(s) => Pin::new(s).poll_flush(cx), InnerAsyncNetworkStream::AsyncStd1Tcp(s) => Pin::new(s).poll_flush(cx),
#[cfg(feature = "async-std1-rustls")] #[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1Rustls(s) => Pin::new(s).poll_flush(cx), InnerAsyncNetworkStream::AsyncStd1RustlsTls(s) => Pin::new(s).poll_flush(cx),
InnerAsyncNetworkStream::None => { InnerAsyncNetworkStream::None => {
debug_assert!(false, "InnerAsyncNetworkStream::None must never be built"); debug_assert!(false, "InnerAsyncNetworkStream::None must never be built");
Poll::Ready(Ok(())) Poll::Ready(Ok(()))
@@ -666,13 +659,13 @@ impl FuturesAsyncWrite for AsyncNetworkStream {
#[cfg(feature = "tokio1-native-tls")] #[cfg(feature = "tokio1-native-tls")]
InnerAsyncNetworkStream::Tokio1NativeTls(s) => Pin::new(s).poll_shutdown(cx), InnerAsyncNetworkStream::Tokio1NativeTls(s) => Pin::new(s).poll_shutdown(cx),
#[cfg(feature = "tokio1-rustls")] #[cfg(feature = "tokio1-rustls")]
InnerAsyncNetworkStream::Tokio1Rustls(s) => Pin::new(s).poll_shutdown(cx), InnerAsyncNetworkStream::Tokio1RustlsTls(s) => Pin::new(s).poll_shutdown(cx),
#[cfg(feature = "tokio1-boring-tls")] #[cfg(feature = "tokio1-boring-tls")]
InnerAsyncNetworkStream::Tokio1BoringTls(s) => Pin::new(s).poll_shutdown(cx), InnerAsyncNetworkStream::Tokio1BoringTls(s) => Pin::new(s).poll_shutdown(cx),
#[cfg(feature = "async-std1")] #[cfg(feature = "async-std1")]
InnerAsyncNetworkStream::AsyncStd1Tcp(s) => Pin::new(s).poll_close(cx), InnerAsyncNetworkStream::AsyncStd1Tcp(s) => Pin::new(s).poll_close(cx),
#[cfg(feature = "async-std1-rustls")] #[cfg(feature = "async-std1-rustls")]
InnerAsyncNetworkStream::AsyncStd1Rustls(s) => Pin::new(s).poll_close(cx), InnerAsyncNetworkStream::AsyncStd1RustlsTls(s) => Pin::new(s).poll_close(cx),
InnerAsyncNetworkStream::None => { InnerAsyncNetworkStream::None => {
debug_assert!(false, "InnerAsyncNetworkStream::None must never be built"); debug_assert!(false, "InnerAsyncNetworkStream::None must never be built");
Poll::Ready(Ok(())) Poll::Ready(Ok(()))

View File

@@ -300,10 +300,6 @@ impl SmtpConnection {
/// The X509 certificate of the server (DER encoded) /// The X509 certificate of the server (DER encoded)
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))] #[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn peer_certificate(&self) -> Result<Vec<u8>, Error> { pub fn peer_certificate(&self) -> Result<Vec<u8>, Error> {
self.stream.get_ref().peer_certificate() self.stream.get_ref().peer_certificate()
} }
@@ -320,14 +316,12 @@ impl SmtpConnection {
/// as the TLSA records match the leaf or issuer certificates. /// as the TLSA records match the leaf or issuer certificates.
/// It cannot be called on non Boring TLS streams. /// It cannot be called on non Boring TLS streams.
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "boring-tls")))]
pub fn tls_verify_result(&self) -> Result<(), Error> { pub fn tls_verify_result(&self) -> Result<(), Error> {
self.stream.get_ref().tls_verify_result() self.stream.get_ref().tls_verify_result()
} }
/// All the X509 certificates of the chain (DER encoded) /// All the X509 certificates of the chain (DER encoded)
#[cfg(any(feature = "rustls", feature = "boring-tls"))] #[cfg(any(feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "rustls", feature = "boring-tls"))))]
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> { pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
self.stream.get_ref().certificate_chain() self.stream.get_ref().certificate_chain()
} }

View File

@@ -37,7 +37,7 @@ enum InnerNetworkStream {
NativeTls(TlsStream<TcpStream>), NativeTls(TlsStream<TcpStream>),
/// Encrypted TCP stream /// Encrypted TCP stream
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
Rustls(StreamOwned<ClientConnection, TcpStream>), RustlsTls(StreamOwned<ClientConnection, TcpStream>),
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
BoringTls(SslStream<TcpStream>), BoringTls(SslStream<TcpStream>),
/// Can't be built /// Can't be built
@@ -60,7 +60,7 @@ impl NetworkStream {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(s) => s.get_ref().peer_addr(), InnerNetworkStream::NativeTls(s) => s.get_ref().peer_addr(),
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerNetworkStream::Rustls(s) => s.get_ref().peer_addr(), InnerNetworkStream::RustlsTls(s) => s.get_ref().peer_addr(),
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(s) => s.get_ref().peer_addr(), InnerNetworkStream::BoringTls(s) => s.get_ref().peer_addr(),
InnerNetworkStream::None => { InnerNetworkStream::None => {
@@ -80,7 +80,7 @@ impl NetworkStream {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(s) => s.get_ref().shutdown(how), InnerNetworkStream::NativeTls(s) => s.get_ref().shutdown(how),
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerNetworkStream::Rustls(s) => s.get_ref().shutdown(how), InnerNetworkStream::RustlsTls(s) => s.get_ref().shutdown(how),
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(s) => s.get_ref().shutdown(how), InnerNetworkStream::BoringTls(s) => s.get_ref().shutdown(how),
InnerNetworkStream::None => { InnerNetworkStream::None => {
@@ -174,30 +174,27 @@ impl NetworkStream {
) -> Result<InnerNetworkStream, Error> { ) -> Result<InnerNetworkStream, Error> {
Ok(match &tls_parameters.connector { Ok(match &tls_parameters.connector {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerTlsParameters::NativeTls { connector } => { InnerTlsParameters::NativeTls(connector) => {
let stream = connector let stream = connector
.connect(tls_parameters.domain(), tcp_stream) .connect(tls_parameters.domain(), tcp_stream)
.map_err(error::connection)?; .map_err(error::connection)?;
InnerNetworkStream::NativeTls(stream) InnerNetworkStream::NativeTls(stream)
} }
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerTlsParameters::Rustls { config } => { InnerTlsParameters::RustlsTls(connector) => {
let domain = ServerName::try_from(tls_parameters.domain()) let domain = ServerName::try_from(tls_parameters.domain())
.map_err(|_| error::connection("domain isn't a valid DNS name"))?; .map_err(|_| error::connection("domain isn't a valid DNS name"))?;
let connection = ClientConnection::new(Arc::clone(config), domain.to_owned()) let connection = ClientConnection::new(Arc::clone(connector), domain.to_owned())
.map_err(error::connection)?; .map_err(error::connection)?;
let stream = StreamOwned::new(connection, tcp_stream); let stream = StreamOwned::new(connection, tcp_stream);
InnerNetworkStream::Rustls(stream) InnerNetworkStream::RustlsTls(stream)
} }
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerTlsParameters::BoringTls { InnerTlsParameters::BoringTls(connector) => {
connector,
accept_invalid_hostnames,
} => {
let stream = connector let stream = connector
.configure() .configure()
.map_err(error::connection)? .map_err(error::connection)?
.verify_hostname(*accept_invalid_hostnames) .verify_hostname(tls_parameters.accept_invalid_hostnames)
.connect(tls_parameters.domain(), tcp_stream) .connect(tls_parameters.domain(), tcp_stream)
.map_err(error::connection)?; .map_err(error::connection)?;
InnerNetworkStream::BoringTls(stream) InnerNetworkStream::BoringTls(stream)
@@ -211,7 +208,7 @@ impl NetworkStream {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(_) => true, InnerNetworkStream::NativeTls(_) => true,
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerNetworkStream::Rustls(_) => true, InnerNetworkStream::RustlsTls(_) => true,
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(_) => true, InnerNetworkStream::BoringTls(_) => true,
InnerNetworkStream::None => { InnerNetworkStream::None => {
@@ -222,14 +219,13 @@ impl NetworkStream {
} }
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "boring-tls")))]
pub fn tls_verify_result(&self) -> Result<(), Error> { pub fn tls_verify_result(&self) -> Result<(), Error> {
match &self.inner { match &self.inner {
InnerNetworkStream::Tcp(_) => Err(error::client("Connection is not encrypted")), InnerNetworkStream::Tcp(_) => Err(error::client("Connection is not encrypted")),
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(_) => panic!("Unsupported"), InnerNetworkStream::NativeTls(_) => panic!("Unsupported"),
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerNetworkStream::Rustls(_) => panic!("Unsupported"), InnerNetworkStream::RustlsTls(_) => panic!("Unsupported"),
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(stream) => { InnerNetworkStream::BoringTls(stream) => {
stream.ssl().verify_result().map_err(error::tls) stream.ssl().verify_result().map_err(error::tls)
@@ -239,14 +235,13 @@ impl NetworkStream {
} }
#[cfg(any(feature = "rustls", feature = "boring-tls"))] #[cfg(any(feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "rustls", feature = "boring-tls"))))]
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> { pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
match &self.inner { match &self.inner {
InnerNetworkStream::Tcp(_) => Err(error::client("Connection is not encrypted")), InnerNetworkStream::Tcp(_) => Err(error::client("Connection is not encrypted")),
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(_) => panic!("Unsupported"), InnerNetworkStream::NativeTls(_) => panic!("Unsupported"),
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerNetworkStream::Rustls(stream) => Ok(stream InnerNetworkStream::RustlsTls(stream) => Ok(stream
.conn .conn
.peer_certificates() .peer_certificates()
.unwrap() .unwrap()
@@ -266,10 +261,6 @@ impl NetworkStream {
} }
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))] #[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn peer_certificate(&self) -> Result<Vec<u8>, Error> { pub fn peer_certificate(&self) -> Result<Vec<u8>, Error> {
match &self.inner { match &self.inner {
InnerNetworkStream::Tcp(_) => Err(error::client("Connection is not encrypted")), InnerNetworkStream::Tcp(_) => Err(error::client("Connection is not encrypted")),
@@ -281,7 +272,7 @@ impl NetworkStream {
.to_der() .to_der()
.map_err(error::tls)?), .map_err(error::tls)?),
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerNetworkStream::Rustls(stream) => Ok(stream InnerNetworkStream::RustlsTls(stream) => Ok(stream
.conn .conn
.peer_certificates() .peer_certificates()
.unwrap() .unwrap()
@@ -305,7 +296,7 @@ impl NetworkStream {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(stream) => stream.get_ref().set_read_timeout(duration), InnerNetworkStream::NativeTls(stream) => stream.get_ref().set_read_timeout(duration),
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerNetworkStream::Rustls(stream) => stream.get_ref().set_read_timeout(duration), InnerNetworkStream::RustlsTls(stream) => stream.get_ref().set_read_timeout(duration),
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(stream) => stream.get_ref().set_read_timeout(duration), InnerNetworkStream::BoringTls(stream) => stream.get_ref().set_read_timeout(duration),
InnerNetworkStream::None => { InnerNetworkStream::None => {
@@ -323,7 +314,7 @@ impl NetworkStream {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(stream) => stream.get_ref().set_write_timeout(duration), InnerNetworkStream::NativeTls(stream) => stream.get_ref().set_write_timeout(duration),
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerNetworkStream::Rustls(stream) => stream.get_ref().set_write_timeout(duration), InnerNetworkStream::RustlsTls(stream) => stream.get_ref().set_write_timeout(duration),
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(stream) => stream.get_ref().set_write_timeout(duration), InnerNetworkStream::BoringTls(stream) => stream.get_ref().set_write_timeout(duration),
InnerNetworkStream::None => { InnerNetworkStream::None => {
@@ -341,7 +332,7 @@ impl Read for NetworkStream {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(s) => s.read(buf), InnerNetworkStream::NativeTls(s) => s.read(buf),
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerNetworkStream::Rustls(s) => s.read(buf), InnerNetworkStream::RustlsTls(s) => s.read(buf),
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(s) => s.read(buf), InnerNetworkStream::BoringTls(s) => s.read(buf),
InnerNetworkStream::None => { InnerNetworkStream::None => {
@@ -359,7 +350,7 @@ impl Write for NetworkStream {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(s) => s.write(buf), InnerNetworkStream::NativeTls(s) => s.write(buf),
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerNetworkStream::Rustls(s) => s.write(buf), InnerNetworkStream::RustlsTls(s) => s.write(buf),
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(s) => s.write(buf), InnerNetworkStream::BoringTls(s) => s.write(buf),
InnerNetworkStream::None => { InnerNetworkStream::None => {
@@ -375,7 +366,7 @@ impl Write for NetworkStream {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
InnerNetworkStream::NativeTls(s) => s.flush(), InnerNetworkStream::NativeTls(s) => s.flush(),
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
InnerNetworkStream::Rustls(s) => s.flush(), InnerNetworkStream::RustlsTls(s) => s.flush(),
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
InnerNetworkStream::BoringTls(s) => s.flush(), InnerNetworkStream::BoringTls(s) => s.flush(),
InnerNetworkStream::None => { InnerNetworkStream::None => {

View File

@@ -65,16 +65,6 @@ pub enum TlsVersion {
/// connecting to a local server. /// connecting to a local server.
#[derive(Clone)] #[derive(Clone)]
#[allow(missing_copy_implementations)] #[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 { pub enum Tls {
/// Insecure (plaintext) connection only. /// Insecure (plaintext) connection only.
/// ///
@@ -148,25 +138,14 @@ impl Debug for Tls {
/// Source for the base set of root certificates to trust. /// Source for the base set of root certificates to trust.
#[allow(missing_copy_implementations)] #[allow(missing_copy_implementations)]
#[derive(Clone, Debug, Default)] #[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 { pub enum CertificateStore {
/// Use the default for the TLS backend. /// Use the default for the TLS backend.
/// ///
/// For native-tls, this will use the system certificate store on Windows, the keychain on /// For native-tls, this will use the system certificate store on Windows, the keychain on
/// macOS, and OpenSSL directories on Linux (usually `/etc/ssl`). /// macOS, and OpenSSL directories on Linux (usually `/etc/ssl`).
/// ///
/// For rustls, this will use the system certificate verifier if the `rustls-platform-verifier` /// For rustls, this will also use the system store if the `rustls-native-certs` feature is
/// feature is enabled. If the `rustls-native-certs` feature is enabled, system certificate /// enabled, or will fall back to `webpki-roots`.
/// 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. /// The boring-tls backend uses the same logic as OpenSSL on all platforms.
#[default] #[default]
@@ -182,34 +161,16 @@ pub enum CertificateStore {
/// Parameters to use for secure clients /// Parameters to use for secure clients
#[derive(Clone)] #[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 struct TlsParameters {
pub(crate) connector: InnerTlsParameters, pub(crate) connector: InnerTlsParameters,
/// The domain name which is expected in the TLS certificate from the server /// The domain name which is expected in the TLS certificate from the server
pub(super) domain: String, pub(super) domain: String,
#[cfg(feature = "boring-tls")]
pub(super) accept_invalid_hostnames: bool,
} }
/// Builder for `TlsParameters` /// Builder for `TlsParameters`
#[derive(Debug, Clone)] #[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 { pub struct TlsParametersBuilder {
domain: String, domain: String,
cert_store: CertificateStore, cert_store: CertificateStore,
@@ -260,8 +221,6 @@ impl TlsParametersBuilder {
/// Controls whether certificates with an invalid hostname are accepted /// Controls whether certificates with an invalid hostname are accepted
/// ///
/// This option is silently disabled when using `rustls-platform-verifier`.
///
/// Defaults to `false`. /// Defaults to `false`.
/// ///
/// # Warning /// # Warning
@@ -285,10 +244,6 @@ impl TlsParametersBuilder {
/// ///
/// Defaults to [`Tlsv12`][TlsVersion::Tlsv12]. /// Defaults to [`Tlsv12`][TlsVersion::Tlsv12].
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))] #[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
pub fn set_min_tls_version(mut self, min_tls_version: TlsVersion) -> Self { pub fn set_min_tls_version(mut self, min_tls_version: TlsVersion) -> Self {
self.min_tls_version = min_tls_version; self.min_tls_version = min_tls_version;
self self
@@ -373,8 +328,10 @@ impl TlsParametersBuilder {
let connector = tls_builder.build().map_err(error::tls)?; let connector = tls_builder.build().map_err(error::tls)?;
Ok(TlsParameters { Ok(TlsParameters {
connector: InnerTlsParameters::NativeTls { connector }, connector: InnerTlsParameters::NativeTls(connector),
domain: self.domain, domain: self.domain,
#[cfg(feature = "boring-tls")]
accept_invalid_hostnames: self.accept_invalid_hostnames,
}) })
} }
@@ -432,11 +389,9 @@ impl TlsParametersBuilder {
.map_err(error::tls)?; .map_err(error::tls)?;
let connector = tls_builder.build(); let connector = tls_builder.build();
Ok(TlsParameters { Ok(TlsParameters {
connector: InnerTlsParameters::BoringTls { connector: InnerTlsParameters::BoringTls(connector),
connector,
accept_invalid_hostnames: self.accept_invalid_hostnames,
},
domain: self.domain, domain: self.domain,
accept_invalid_hostnames: self.accept_invalid_hostnames,
}) })
} }
@@ -464,10 +419,7 @@ impl TlsParametersBuilder {
// Build TLS config // Build TLS config
let mut root_cert_store = RootCertStore::empty(); let mut root_cert_store = RootCertStore::empty();
#[cfg(all( #[cfg(feature = "rustls-native-certs")]
not(feature = "rustls-platform-verifier"),
feature = "rustls-native-certs"
))]
fn load_native_roots(store: &mut RootCertStore) { fn load_native_roots(store: &mut RootCertStore) {
let rustls_native_certs::CertificateResult { certs, errors, .. } = let rustls_native_certs::CertificateResult { certs, errors, .. } =
rustls_native_certs::load_native_certs(); rustls_native_certs::load_native_certs();
@@ -487,26 +439,11 @@ impl TlsParametersBuilder {
store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
} }
#[cfg_attr(not(feature = "rustls-platform-verifier"), allow(unused_mut))]
let mut extra_roots = None::<Vec<CertificateDer<'static>>>;
match self.cert_store { match self.cert_store {
CertificateStore::Default => { CertificateStore::Default => {
#[cfg(feature = "rustls-platform-verifier")] #[cfg(feature = "rustls-native-certs")]
{
extra_roots = Some(Vec::new());
}
#[cfg(all(
not(feature = "rustls-platform-verifier"),
feature = "rustls-native-certs"
))]
load_native_roots(&mut root_cert_store); load_native_roots(&mut root_cert_store);
#[cfg(all(not(feature = "rustls-native-certs"), feature = "webpki-roots"))]
#[cfg(all(
not(feature = "rustls-platform-verifier"),
not(feature = "rustls-native-certs"),
feature = "webpki-roots"
))]
load_webpki_roots(&mut root_cert_store); load_webpki_roots(&mut root_cert_store);
} }
#[cfg(all(feature = "rustls", feature = "webpki-roots"))] #[cfg(all(feature = "rustls", feature = "webpki-roots"))]
@@ -517,17 +454,11 @@ impl TlsParametersBuilder {
} }
for cert in self.root_certs { for cert in self.root_certs {
for rustls_cert in cert.rustls { for rustls_cert in cert.rustls {
#[cfg(feature = "rustls-platform-verifier")]
if let Some(extra_roots) = &mut extra_roots {
extra_roots.push(rustls_cert.clone());
}
root_cert_store.add(rustls_cert).map_err(error::tls)?; root_cert_store.add(rustls_cert).map_err(error::tls)?;
} }
} }
let tls = if self.accept_invalid_certs let tls = if self.accept_invalid_certs || self.accept_invalid_hostnames {
|| (extra_roots.is_none() && self.accept_invalid_hostnames)
{
let verifier = InvalidCertsVerifier { let verifier = InvalidCertsVerifier {
ignore_invalid_hostnames: self.accept_invalid_hostnames, ignore_invalid_hostnames: self.accept_invalid_hostnames,
ignore_invalid_certs: self.accept_invalid_certs, ignore_invalid_certs: self.accept_invalid_certs,
@@ -537,23 +468,7 @@ impl TlsParametersBuilder {
tls.dangerous() tls.dangerous()
.with_custom_certificate_verifier(Arc::new(verifier)) .with_custom_certificate_verifier(Arc::new(verifier))
} else { } else {
#[cfg(feature = "rustls-platform-verifier")] tls.with_root_certificates(root_cert_store)
if let Some(extra_roots) = extra_roots {
tls.dangerous().with_custom_certificate_verifier(Arc::new(
rustls_platform_verifier::Verifier::new_with_extra_roots(
extra_roots,
crypto_provider,
)
.map_err(error::tls)?,
))
} else {
tls.with_root_certificates(root_cert_store)
}
#[cfg(not(feature = "rustls-platform-verifier"))]
{
tls.with_root_certificates(root_cert_store)
}
}; };
let tls = if let Some(identity) = self.identity { let tls = if let Some(identity) = self.identity {
@@ -565,10 +480,10 @@ impl TlsParametersBuilder {
}; };
Ok(TlsParameters { Ok(TlsParameters {
connector: InnerTlsParameters::Rustls { connector: InnerTlsParameters::RustlsTls(Arc::new(tls)),
config: Arc::new(tls),
},
domain: self.domain, domain: self.domain,
#[cfg(feature = "boring-tls")]
accept_invalid_hostnames: self.accept_invalid_hostnames,
}) })
} }
} }
@@ -577,14 +492,11 @@ impl TlsParametersBuilder {
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]
pub(crate) enum InnerTlsParameters { pub(crate) enum InnerTlsParameters {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
NativeTls { connector: TlsConnector }, NativeTls(TlsConnector),
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
Rustls { config: Arc<ClientConfig> }, RustlsTls(Arc<ClientConfig>),
#[cfg(feature = "boring-tls")] #[cfg(feature = "boring-tls")]
BoringTls { BoringTls(SslConnector),
connector: SslConnector,
accept_invalid_hostnames: bool,
},
} }
impl TlsParameters { impl TlsParameters {
@@ -633,16 +545,6 @@ impl TlsParameters {
/// A certificate that can be used with [`TlsParametersBuilder::add_root_certificate`] /// A certificate that can be used with [`TlsParametersBuilder::add_root_certificate`]
#[derive(Clone)] #[derive(Clone)]
#[allow(missing_copy_implementations)] #[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 { pub struct Certificate {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
native_tls: native_tls::Certificate, native_tls: native_tls::Certificate,
@@ -706,16 +608,6 @@ impl Debug for Certificate {
/// An identity that can be used with [`TlsParametersBuilder::identify_with`] /// An identity that can be used with [`TlsParametersBuilder::identify_with`]
#[allow(missing_copy_implementations)] #[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 { pub struct Identity {
#[cfg(feature = "native-tls")] #[cfg(feature = "native-tls")]
native_tls: native_tls::Identity, native_tls: native_tls::Identity,

View File

@@ -13,7 +13,6 @@ use super::{
pub(crate) trait TransportBuilder { pub(crate) trait TransportBuilder {
fn new<T: Into<String>>(server: T) -> Self; fn new<T: Into<String>>(server: T) -> Self;
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
fn tls(self, tls: super::Tls) -> Self; fn tls(self, tls: super::Tls) -> Self;
fn port(self, port: u16) -> Self; fn port(self, port: u16) -> Self;
fn credentials(self, credentials: Credentials) -> Self; fn credentials(self, credentials: Credentials) -> Self;
@@ -25,7 +24,6 @@ impl TransportBuilder for SmtpTransportBuilder {
Self::new(server) Self::new(server)
} }
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
fn tls(self, tls: super::Tls) -> Self { fn tls(self, tls: super::Tls) -> Self {
self.tls(tls) self.tls(tls)
} }
@@ -49,7 +47,6 @@ impl TransportBuilder for AsyncSmtpTransportBuilder {
Self::new(server) Self::new(server)
} }
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
fn tls(self, tls: super::Tls) -> Self { fn tls(self, tls: super::Tls) -> Self {
self.tls(tls) self.tls(tls)
} }

View File

@@ -149,7 +149,11 @@ impl<E: Executor> Pool<E> {
pub(crate) async fn shutdown(&self) { pub(crate) async fn shutdown(&self) {
let connections = { self.connections.lock().await.take() }; let connections = { self.connections.lock().await.take() };
if let Some(connections) = connections { if let Some(connections) = connections {
abort_concurrent(connections.into_iter().map(ParkedConnection::unpark)).await; stream::iter(connections)
.for_each_concurrent(8, |conn| async move {
conn.unpark().abort().await;
})
.await;
} }
if let Some(handle) = self.handle.get() { if let Some(handle) = self.handle.get() {

View File

@@ -177,7 +177,7 @@ impl SmtpTransport {
/// a proper URL encoder, like the following cargo script: /// a proper URL encoder, like the following cargo script:
/// ///
/// ```rust /// ```rust
/// # const TOML: &str = r#" /// # let _ = r#"
/// #!/usr/bin/env cargo /// #!/usr/bin/env cargo
/// ///
/// //! ```cargo /// //! ```cargo

View File

@@ -43,11 +43,13 @@
use std::{ use std::{
error::Error as StdError, error::Error as StdError,
fmt, fmt,
sync::{Arc, Mutex}, sync::{Arc, Mutex as StdMutex},
}; };
#[cfg(any(feature = "tokio1", feature = "async-std1"))] #[cfg(any(feature = "tokio1", feature = "async-std1"))]
use async_trait::async_trait; 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"))] #[cfg(any(feature = "tokio1", feature = "async-std1"))]
use crate::AsyncTransport; use crate::AsyncTransport;
@@ -70,7 +72,7 @@ impl StdError for Error {}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct StubTransport { pub struct StubTransport {
response: Result<(), Error>, response: Result<(), Error>,
message_log: Arc<Mutex<Vec<(Envelope, String)>>>, message_log: Arc<StdMutex<Vec<(Envelope, String)>>>,
} }
/// This transport logs messages and always returns the given response /// This transport logs messages and always returns the given response
@@ -79,7 +81,7 @@ pub struct StubTransport {
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio1", feature = "async-std1"))))] #[cfg_attr(docsrs, doc(cfg(any(feature = "tokio1", feature = "async-std1"))))]
pub struct AsyncStubTransport { pub struct AsyncStubTransport {
response: Result<(), Error>, response: Result<(), Error>,
message_log: Arc<Mutex<Vec<(Envelope, String)>>>, message_log: Arc<FuturesMutex<Vec<(Envelope, String)>>>,
} }
impl StubTransport { impl StubTransport {
@@ -87,7 +89,7 @@ impl StubTransport {
pub fn new(response: Result<(), Error>) -> Self { pub fn new(response: Result<(), Error>) -> Self {
Self { Self {
response, response,
message_log: Arc::new(Mutex::new(vec![])), message_log: Arc::new(StdMutex::new(vec![])),
} }
} }
@@ -95,7 +97,7 @@ impl StubTransport {
pub fn new_ok() -> Self { pub fn new_ok() -> Self {
Self { Self {
response: Ok(()), response: Ok(()),
message_log: Arc::new(Mutex::new(vec![])), message_log: Arc::new(StdMutex::new(vec![])),
} }
} }
@@ -103,7 +105,7 @@ impl StubTransport {
pub fn new_error() -> Self { pub fn new_error() -> Self {
Self { Self {
response: Err(Error), response: Err(Error),
message_log: Arc::new(Mutex::new(vec![])), message_log: Arc::new(StdMutex::new(vec![])),
} }
} }
@@ -122,7 +124,7 @@ impl AsyncStubTransport {
pub fn new(response: Result<(), Error>) -> Self { pub fn new(response: Result<(), Error>) -> Self {
Self { Self {
response, response,
message_log: Arc::new(Mutex::new(vec![])), message_log: Arc::new(FuturesMutex::new(vec![])),
} }
} }
@@ -130,7 +132,7 @@ impl AsyncStubTransport {
pub fn new_ok() -> Self { pub fn new_ok() -> Self {
Self { Self {
response: Ok(()), response: Ok(()),
message_log: Arc::new(Mutex::new(vec![])), message_log: Arc::new(FuturesMutex::new(vec![])),
} }
} }
@@ -138,14 +140,14 @@ impl AsyncStubTransport {
pub fn new_error() -> Self { pub fn new_error() -> Self {
Self { Self {
response: Err(Error), response: Err(Error),
message_log: Arc::new(Mutex::new(vec![])), message_log: Arc::new(FuturesMutex::new(vec![])),
} }
} }
/// Return all logged messages sent using [`AsyncTransport::send_raw`] /// Return all logged messages sent using [`AsyncTransport::send_raw`]
#[cfg(any(feature = "tokio1", feature = "async-std1"))] #[cfg(any(feature = "tokio1", feature = "async-std1"))]
pub async fn messages(&self) -> Vec<(Envelope, String)> { pub async fn messages(&self) -> Vec<(Envelope, String)> {
self.message_log.lock().unwrap().clone() self.message_log.lock().await.clone()
} }
} }
@@ -171,7 +173,7 @@ impl AsyncTransport for AsyncStubTransport {
async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> { async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
self.message_log self.message_log
.lock() .lock()
.unwrap() .await
.push((envelope.clone(), String::from_utf8_lossy(email).into())); .push((envelope.clone(), String::from_utf8_lossy(email).into()));
self.response self.response
} }