Make this all internal only for now

This commit is contained in:
Paolo Barbolini
2025-05-02 07:20:20 +02:00
parent b7482f0232
commit d31490a2a9
8 changed files with 72 additions and 111 deletions

View File

@@ -4,10 +4,7 @@ use lettre::{
message::header::ContentType,
transport::smtp::{
authentication::Credentials,
client::{
tls::{native_tls::Certificate, NativeTls, TlsParametersBuilder},
Tls,
},
client::{Certificate, Tls, TlsParameters},
},
Message, SmtpTransport, Transport,
};
@@ -27,9 +24,9 @@ fn main() {
// Use a custom certificate stored on disk to securely verify the server's certificate
let pem_cert = fs::read("certificate.pem").unwrap();
let cert = Certificate::from_pem(&pem_cert).unwrap();
let tls = TlsParametersBuilder::<NativeTls>::new("smtp.server.com".to_owned())
let tls = TlsParameters::builder("smtp.server.com".to_owned())
.add_root_certificate(cert)
.build_legacy()
.build()
.unwrap();
let creds = Credentials::new("smtp_username".to_owned(), "smtp_password".to_owned());

View File

@@ -34,14 +34,12 @@ pub use self::async_net::AsyncNetworkStream;
pub use self::async_net::AsyncTokioStream;
use self::net::NetworkStream;
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
pub(super) use self::tls::deprecated::InnerTlsParameters;
pub(super) use self::tls::current::InnerTlsParameters;
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[allow(deprecated)]
pub use self::tls::deprecated::TlsVersion;
#[allow(deprecated)]
pub use self::tls::current::TlsVersion;
pub use self::{
connection::SmtpConnection,
tls::deprecated::{Certificate, Identity, Tls, TlsParameters, TlsParametersBuilder},
tls::current::{Certificate, Identity, Tls, TlsParameters, TlsParametersBuilder},
};
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
@@ -50,7 +48,7 @@ mod async_connection;
mod async_net;
mod connection;
mod net;
pub mod tls;
mod tls;
/// The codec used for transparency
#[derive(Debug)]

View File

@@ -55,21 +55,21 @@ pub(super) fn build_connector(
#[derive(Debug, Clone, Default)]
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub enum CertificateStore {
pub(super) enum CertificateStore {
#[default]
System,
None,
}
#[derive(Clone)]
pub struct Certificate(pub(super) boring::x509::X509);
pub(super) struct Certificate(pub(super) boring::x509::X509);
impl Certificate {
pub fn from_pem(pem: &[u8]) -> Result<Self, Error> {
pub(super) fn from_pem(pem: &[u8]) -> Result<Self, Error> {
Ok(Self(boring::x509::X509::from_pem(pem).map_err(error::tls)?))
}
pub fn from_der(der: &[u8]) -> Result<Self, Error> {
pub(super) fn from_der(der: &[u8]) -> Result<Self, Error> {
Ok(Self(boring::x509::X509::from_der(der).map_err(error::tls)?))
}
}
@@ -81,13 +81,13 @@ impl Debug for Certificate {
}
#[derive(Clone)]
pub struct Identity {
pub(super) struct Identity {
pub(super) chain: boring::x509::X509,
pub(super) key: boring::pkey::PKey<boring::pkey::Private>,
}
impl Identity {
pub fn from_pem(pem: &[u8], key: &[u8]) -> Result<Self, Error> {
pub(super) fn from_pem(pem: &[u8], key: &[u8]) -> Result<Self, Error> {
let chain = boring::x509::X509::from_pem(pem).map_err(error::tls)?;
let key = boring::pkey::PKey::private_key_from_pem(key).map_err(error::tls)?;
Ok(Self { chain, key })
@@ -102,7 +102,7 @@ impl Debug for Identity {
#[derive(Debug, Copy, Clone, Default)]
#[non_exhaustive]
pub enum MinTlsVersion {
pub(super) enum MinTlsVersion {
Tlsv10,
Tlsv11,
#[default]

View File

@@ -7,7 +7,6 @@ use crate::transport::smtp::{error, Error};
#[derive(Debug, Copy, Clone)]
#[non_exhaustive]
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[deprecated]
pub enum TlsVersion {
/// TLS 1.0
///
@@ -120,8 +119,6 @@ impl Debug for Tls {
/// Source for the base set of root certificates to trust.
#[allow(missing_copy_implementations)]
#[derive(Clone, Debug, Default)]
#[deprecated]
#[allow(deprecated)]
pub enum CertificateStore {
/// Use the default for the TLS backend.
///
@@ -153,29 +150,22 @@ pub struct TlsParameters {
/// Builder for `TlsParameters`
#[derive(Debug, Clone)]
#[deprecated]
pub struct TlsParametersBuilder {
domain: String,
#[allow(deprecated)]
cert_store: CertificateStore,
#[allow(deprecated)]
root_certs: Vec<Certificate>,
#[allow(deprecated)]
identity: Option<Identity>,
accept_invalid_hostnames: bool,
accept_invalid_certs: bool,
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[allow(deprecated)]
min_tls_version: TlsVersion,
}
#[allow(deprecated)]
impl TlsParametersBuilder {
/// Creates a new builder for `TlsParameters`
pub fn new(domain: String) -> Self {
Self {
domain,
#[allow(deprecated)]
cert_store: CertificateStore::Default,
root_certs: Vec::new(),
identity: None,
@@ -187,8 +177,6 @@ impl TlsParametersBuilder {
}
/// Set the source for the base set of root certificates to trust.
#[deprecated]
#[allow(deprecated)]
pub fn certificate_store(mut self, cert_store: CertificateStore) -> Self {
self.cert_store = cert_store;
self
@@ -272,7 +260,6 @@ impl TlsParametersBuilder {
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
#[deprecated]
pub fn build(self) -> Result<TlsParameters, Error> {
#[cfg(feature = "rustls")]
return self.build_rustls();
@@ -286,7 +273,6 @@ impl TlsParametersBuilder {
#[cfg(feature = "native-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
pub fn build_native(self) -> Result<TlsParameters, Error> {
#[allow(deprecated)]
let cert_store = match self.cert_store {
CertificateStore::Default => super::native_tls::CertificateStore::System,
CertificateStore::None => super::native_tls::CertificateStore::None,
@@ -330,7 +316,6 @@ impl TlsParametersBuilder {
#[cfg(feature = "boring-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "boring-tls")))]
pub fn build_boring(self) -> Result<TlsParameters, Error> {
#[allow(deprecated)]
let cert_store = match self.cert_store {
CertificateStore::Default => super::boring_tls::CertificateStore::System,
CertificateStore::None => super::boring_tls::CertificateStore::None,
@@ -367,7 +352,6 @@ impl TlsParametersBuilder {
#[cfg(feature = "rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
pub fn build_rustls(self) -> Result<TlsParameters, Error> {
#[allow(deprecated)]
let cert_store = match self.cert_store {
#[cfg(feature = "rustls-native-certs")]
CertificateStore::Default => super::rustls::CertificateStore::NativeCerts,
@@ -430,14 +414,11 @@ impl TlsParameters {
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls")))
)]
#[allow(deprecated)]
pub fn new(domain: String) -> Result<Self, Error> {
TlsParametersBuilder::new(domain).build()
}
/// Creates a new `TlsParameters` builder
#[deprecated]
#[allow(deprecated)]
pub fn builder(domain: String) -> TlsParametersBuilder {
TlsParametersBuilder::new(domain)
}
@@ -445,8 +426,6 @@ impl TlsParameters {
/// Creates a new `TlsParameters` using native-tls
#[cfg(feature = "native-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
#[deprecated]
#[allow(deprecated)]
pub fn new_native(domain: String) -> Result<Self, Error> {
TlsParametersBuilder::new(domain).build_native()
}
@@ -454,8 +433,6 @@ impl TlsParameters {
/// Creates a new `TlsParameters` using rustls
#[cfg(feature = "rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
#[deprecated]
#[allow(deprecated)]
pub fn new_rustls(domain: String) -> Result<Self, Error> {
TlsParametersBuilder::new(domain).build_rustls()
}
@@ -463,8 +440,6 @@ impl TlsParameters {
/// Creates a new `TlsParameters` using boring
#[cfg(feature = "boring-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "boring-tls")))]
#[deprecated]
#[allow(deprecated)]
pub fn new_boring(domain: String) -> Result<Self, Error> {
TlsParametersBuilder::new(domain).build_boring()
}
@@ -477,7 +452,6 @@ impl TlsParameters {
/// A certificate that can be used with [`TlsParametersBuilder::add_root_certificate`]
#[derive(Clone)]
#[allow(missing_copy_implementations)]
#[deprecated]
pub struct Certificate {
#[cfg(feature = "native-tls")]
native_tls: super::native_tls::Certificate,
@@ -488,7 +462,6 @@ pub struct Certificate {
}
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[allow(deprecated)]
impl Certificate {
/// Create a `Certificate` from a DER encoded certificate
pub fn from_der(der: Vec<u8>) -> Result<Self, Error> {
@@ -504,28 +477,17 @@ impl Certificate {
/// Create a `Certificate` from a PEM encoded certificate
pub fn from_pem(pem: &[u8]) -> Result<Self, Error> {
#[cfg(feature = "rustls")]
let rustls_cert = {
use rustls::pki_types::{self, pem::PemObject as _, CertificateDer};
CertificateDer::pem_slice_iter(pem)
.map(|cert| Ok(super::rustls::Certificate(cert?)))
.collect::<Result<Vec<_>, pki_types::pem::Error>>()
.map_err(|_| error::tls("invalid certificates"))?
};
Ok(Self {
#[cfg(feature = "native-tls")]
native_tls: super::native_tls::Certificate::from_pem(pem)?,
#[cfg(feature = "rustls")]
rustls: rustls_cert,
rustls: super::rustls::Certificate::from_pem_bundle(pem)?,
#[cfg(feature = "boring-tls")]
boring_tls: super::boring_tls::Certificate::from_pem(pem)?,
})
}
}
#[allow(deprecated)]
impl Debug for Certificate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Certificate").finish()
@@ -535,7 +497,6 @@ impl Debug for Certificate {
/// An identity that can be used with [`TlsParametersBuilder::identify_with`]
#[derive(Clone)]
#[allow(missing_copy_implementations)]
#[deprecated]
pub struct Identity {
#[cfg(feature = "native-tls")]
native_tls: super::native_tls::Identity,
@@ -545,7 +506,6 @@ pub struct Identity {
boring_tls: super::boring_tls::Identity,
}
#[allow(deprecated)]
impl Debug for Identity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Identity").finish()
@@ -553,7 +513,6 @@ impl Debug for Identity {
}
#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
#[allow(deprecated)]
impl Identity {
pub fn from_pem(pem: &[u8], key: &[u8]) -> Result<Self, Error> {
Ok(Self {

View File

@@ -2,17 +2,17 @@ use crate::transport::smtp::Error;
#[cfg(feature = "boring-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "boring-tls")))]
pub mod boring_tls;
pub(super) mod deprecated;
pub(super) mod boring_tls;
pub(super) mod current;
#[cfg(feature = "native-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
pub mod native_tls;
pub(super) mod native_tls;
#[cfg(feature = "rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
pub mod rustls;
pub(super) mod rustls;
#[derive(Debug)]
pub struct TlsParametersBuilder<B: TlsBackend> {
struct TlsParametersBuilder<B: TlsBackend> {
domain: String,
cert_store: B::CertificateStore,
root_certs: Vec<B::Certificate>,
@@ -23,7 +23,7 @@ pub struct TlsParametersBuilder<B: TlsBackend> {
}
impl<B: TlsBackend> TlsParametersBuilder<B> {
pub fn new(domain: String) -> Self {
fn new(domain: String) -> Self {
Self {
domain,
cert_store: Default::default(),
@@ -35,44 +35,44 @@ impl<B: TlsBackend> TlsParametersBuilder<B> {
}
}
pub fn certificate_store(mut self, cert_store: B::CertificateStore) -> Self {
fn certificate_store(mut self, cert_store: B::CertificateStore) -> Self {
self.cert_store = cert_store;
self
}
pub fn add_root_certificate(mut self, cert: B::Certificate) -> Self {
fn add_root_certificate(mut self, cert: B::Certificate) -> Self {
self.root_certs.push(cert);
self
}
pub fn identify_with(mut self, identity: B::Identity) -> Self {
fn identify_with(mut self, identity: B::Identity) -> Self {
self.identity = Some(identity);
self
}
pub fn min_tls_version(mut self, min_tls_version: B::MinTlsVersion) -> Self {
fn min_tls_version(mut self, min_tls_version: B::MinTlsVersion) -> Self {
self.min_tls_version = min_tls_version;
self
}
pub fn dangerous_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> Self {
fn dangerous_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> Self {
self.accept_invalid_certs = accept_invalid_certs;
self
}
pub fn dangerous_accept_invalid_hostnames(mut self, accept_invalid_hostnames: bool) -> Self {
fn dangerous_accept_invalid_hostnames(mut self, accept_invalid_hostnames: bool) -> Self {
self.accept_invalid_hostnames = accept_invalid_hostnames;
self
}
pub fn build_legacy(self) -> Result<self::deprecated::TlsParameters, Error> {
fn build_legacy(self) -> Result<self::current::TlsParameters, Error> {
let domain = self.domain.clone();
let connector = B::__build_legacy_connector(self)?;
Ok(self::deprecated::TlsParameters { connector, domain })
Ok(self::current::TlsParameters { connector, domain })
}
}
pub trait TlsBackend: private::SealedTlsBackend {
trait TlsBackend: private::SealedTlsBackend {
type CertificateStore: Default;
type Certificate;
type Identity;
@@ -85,7 +85,7 @@ pub trait TlsBackend: private::SealedTlsBackend {
#[allow(private_interfaces)]
fn __build_legacy_connector(
builder: TlsParametersBuilder<Self>,
) -> Result<self::deprecated::InnerTlsParameters, Error>;
) -> Result<self::current::InnerTlsParameters, Error>;
}
#[cfg(feature = "boring-tls")]
@@ -93,7 +93,7 @@ pub trait TlsBackend: private::SealedTlsBackend {
#[derive(Debug)]
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub struct BoringTls;
pub(super) struct BoringTls;
#[cfg(feature = "boring-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "boring-tls")))]
@@ -103,6 +103,7 @@ impl TlsBackend for BoringTls {
type Identity = self::boring_tls::Identity;
type MinTlsVersion = self::boring_tls::MinTlsVersion;
#[allow(private_interfaces)]
fn __build_connector(builder: TlsParametersBuilder<Self>) -> Result<Self::Connector, Error> {
self::boring_tls::build_connector(builder)
}
@@ -110,10 +111,10 @@ impl TlsBackend for BoringTls {
#[allow(private_interfaces)]
fn __build_legacy_connector(
builder: TlsParametersBuilder<Self>,
) -> Result<self::deprecated::InnerTlsParameters, Error> {
) -> Result<self::current::InnerTlsParameters, Error> {
let accept_invalid_hostnames = builder.accept_invalid_hostnames;
Self::__build_connector(builder).map(|connector| {
self::deprecated::InnerTlsParameters::BoringTls {
self::current::InnerTlsParameters::BoringTls {
connector,
accept_invalid_hostnames,
}
@@ -126,7 +127,7 @@ impl TlsBackend for BoringTls {
#[derive(Debug)]
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub struct NativeTls;
pub(super) struct NativeTls;
#[cfg(feature = "native-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
@@ -136,6 +137,7 @@ impl TlsBackend for NativeTls {
type Identity = self::native_tls::Identity;
type MinTlsVersion = self::native_tls::MinTlsVersion;
#[allow(private_interfaces)]
fn __build_connector(builder: TlsParametersBuilder<Self>) -> Result<Self::Connector, Error> {
self::native_tls::build_connector(builder)
}
@@ -143,9 +145,9 @@ impl TlsBackend for NativeTls {
#[allow(private_interfaces)]
fn __build_legacy_connector(
builder: TlsParametersBuilder<Self>,
) -> Result<self::deprecated::InnerTlsParameters, Error> {
) -> Result<self::current::InnerTlsParameters, Error> {
Self::__build_connector(builder)
.map(|connector| self::deprecated::InnerTlsParameters::NativeTls { connector })
.map(|connector| self::current::InnerTlsParameters::NativeTls { connector })
}
}
@@ -154,7 +156,7 @@ impl TlsBackend for NativeTls {
#[derive(Debug)]
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub struct Rustls;
pub(super) struct Rustls;
#[cfg(feature = "rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
@@ -164,6 +166,7 @@ impl TlsBackend for Rustls {
type Identity = self::rustls::Identity;
type MinTlsVersion = self::rustls::MinTlsVersion;
#[allow(private_interfaces)]
fn __build_connector(builder: TlsParametersBuilder<Self>) -> Result<Self::Connector, Error> {
self::rustls::build_connector(builder)
}
@@ -171,15 +174,14 @@ impl TlsBackend for Rustls {
#[allow(private_interfaces)]
fn __build_legacy_connector(
builder: TlsParametersBuilder<Self>,
) -> Result<self::deprecated::InnerTlsParameters, Error> {
) -> Result<self::current::InnerTlsParameters, Error> {
Self::__build_connector(builder)
.map(|config| self::deprecated::InnerTlsParameters::Rustls { config })
.map(|config| self::current::InnerTlsParameters::Rustls { config })
}
}
mod private {
// FIXME: this should be `pub(super)` but the `private_bounds` lint doesn't like it
pub trait SealedTlsBackend: Sized {
pub(super) trait SealedTlsBackend: Sized {
type Connector;
}

View File

@@ -38,23 +38,23 @@ pub(super) fn build_connector(
#[derive(Debug, Clone, Default)]
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub enum CertificateStore {
pub(super) enum CertificateStore {
#[default]
System,
None,
}
#[derive(Clone)]
pub struct Certificate(pub(super) native_tls::Certificate);
pub(super) struct Certificate(pub(super) native_tls::Certificate);
impl Certificate {
pub fn from_pem(pem: &[u8]) -> Result<Self, Error> {
pub(super) fn from_pem(pem: &[u8]) -> Result<Self, Error> {
Ok(Self(
native_tls::Certificate::from_pem(pem).map_err(error::tls)?,
))
}
pub fn from_der(der: &[u8]) -> Result<Self, Error> {
pub(super) fn from_der(der: &[u8]) -> Result<Self, Error> {
Ok(Self(
native_tls::Certificate::from_der(der).map_err(error::tls)?,
))
@@ -68,10 +68,10 @@ impl Debug for Certificate {
}
#[derive(Clone)]
pub struct Identity(pub(super) native_tls::Identity);
pub(super) struct Identity(pub(super) native_tls::Identity);
impl Identity {
pub fn from_pem(pem: &[u8], key: &[u8]) -> Result<Self, Error> {
pub(super) fn from_pem(pem: &[u8], key: &[u8]) -> Result<Self, Error> {
Ok(Self(
native_tls::Identity::from_pkcs8(pem, key).map_err(error::tls)?,
))
@@ -86,7 +86,7 @@ impl Debug for Identity {
#[derive(Debug, Copy, Clone, Default)]
#[non_exhaustive]
pub enum MinTlsVersion {
pub(super) enum MinTlsVersion {
Tlsv10,
Tlsv11,
#[default]

View File

@@ -80,7 +80,7 @@ pub(super) fn build_connector(
#[derive(Debug, Clone, Default)]
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub enum CertificateStore {
pub(super) enum CertificateStore {
#[cfg(feature = "rustls-native-certs")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))]
#[cfg_attr(feature = "rustls-native-certs", default)]
@@ -100,10 +100,11 @@ pub enum CertificateStore {
}
#[derive(Clone)]
pub struct Certificate(pub(super) pki_types::CertificateDer<'static>);
pub(super) struct Certificate(pub(super) pki_types::CertificateDer<'static>);
impl Certificate {
pub fn from_pem(pem: &[u8]) -> Result<Self, Error> {
#[allow(dead_code)]
pub(super) fn from_pem(pem: &[u8]) -> Result<Self, Error> {
use rustls::pki_types::pem::PemObject as _;
Ok(Self(
@@ -112,7 +113,16 @@ impl Certificate {
))
}
pub fn from_der(der: Vec<u8>) -> Result<Self, Error> {
pub(super) fn from_pem_bundle(pem: &[u8]) -> Result<Vec<Self>, Error> {
use rustls::pki_types::pem::PemObject as _;
pki_types::CertificateDer::pem_slice_iter(pem)
.map(|cert| Ok(Self(cert?)))
.collect::<Result<Vec<_>, pki_types::pem::Error>>()
.map_err(|_| error::tls("invalid certificate"))
}
pub(super) fn from_der(der: Vec<u8>) -> Result<Self, Error> {
Ok(Self(der.into()))
}
}
@@ -123,13 +133,13 @@ impl Debug for Certificate {
}
}
pub struct Identity {
pub(super) struct Identity {
pub(super) chain: Vec<pki_types::CertificateDer<'static>>,
pub(super) key: pki_types::PrivateKeyDer<'static>,
}
impl Identity {
pub fn from_pem(pem: &[u8], key: &[u8]) -> Result<Self, Error> {
pub(super) fn from_pem(pem: &[u8], key: &[u8]) -> Result<Self, Error> {
use rustls::pki_types::pem::PemObject as _;
let key = match pki_types::PrivateKeyDer::from_pem_slice(key) {
@@ -164,7 +174,7 @@ impl Debug for Identity {
#[derive(Debug, Copy, Clone, Default)]
#[non_exhaustive]
pub enum MinTlsVersion {
pub(super) enum MinTlsVersion {
#[default]
Tlsv12,
Tlsv13,

View File

@@ -107,10 +107,7 @@
//!
//! use lettre::{
//! message::header::ContentType,
//! transport::smtp::client::{
//! tls::{native_tls::Certificate, NativeTls, TlsParametersBuilder},
//! Tls,
//! },
//! transport::smtp::client::{Certificate, Tls, TlsParameters},
//! Message, SmtpTransport, Transport,
//! };
//!
@@ -125,11 +122,9 @@
//! // Custom TLS configuration - Use a self signed certificate
//! let cert = fs::read("self-signed.crt")?;
//! let cert = Certificate::from_pem(&cert)?;
//! let tls = TlsParametersBuilder::<NativeTls>::new(
//! /* TLS SNI value */ "smtp.example.com".to_owned(),
//! )
//! .add_root_certificate(cert)
//! .build_legacy()?;
//! let tls = TlsParameters::builder(/* TLS SNI value */ "smtp.example.com".to_owned())
//! .add_root_certificate(cert)
//! .build()?;
//!
//! // Create the SMTPS transport
//! let sender = SmtpTransport::relay("smtp.example.com")?