lettre/transport/smtp/
transport.rs#[cfg(feature = "pool")]
use std::sync::Arc;
use std::time::Duration;
#[cfg(feature = "pool")]
use super::pool::sync_impl::Pool;
#[cfg(feature = "pool")]
use super::PoolConfig;
use super::{ClientId, Credentials, Error, Mechanism, Response, SmtpConnection, SmtpInfo};
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
use super::{Tls, TlsParameters, SUBMISSIONS_PORT, SUBMISSION_PORT};
use crate::{address::Envelope, Transport};
#[cfg_attr(docsrs, doc(cfg(feature = "smtp-transport")))]
#[derive(Clone)]
pub struct SmtpTransport {
#[cfg(feature = "pool")]
inner: Arc<Pool>,
#[cfg(not(feature = "pool"))]
inner: SmtpClient,
}
impl Transport for SmtpTransport {
type Ok = Response;
type Error = Error;
fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
let mut conn = self.inner.connection()?;
let result = conn.send(envelope, email)?;
#[cfg(not(feature = "pool"))]
conn.quit()?;
Ok(result)
}
}
impl SmtpTransport {
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
)]
pub fn relay(relay: &str) -> Result<SmtpTransportBuilder, Error> {
let tls_parameters = TlsParameters::new(relay.into())?;
Ok(Self::builder_dangerous(relay)
.port(SUBMISSIONS_PORT)
.tls(Tls::Wrapper(tls_parameters)))
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
)]
pub fn starttls_relay(relay: &str) -> Result<SmtpTransportBuilder, Error> {
let tls_parameters = TlsParameters::new(relay.into())?;
Ok(Self::builder_dangerous(relay)
.port(SUBMISSION_PORT)
.tls(Tls::Required(tls_parameters)))
}
pub fn unencrypted_localhost() -> SmtpTransport {
Self::builder_dangerous("localhost").build()
}
pub fn builder_dangerous<T: Into<String>>(server: T) -> SmtpTransportBuilder {
let new = SmtpInfo {
server: server.into(),
..Default::default()
};
SmtpTransportBuilder {
info: new,
#[cfg(feature = "pool")]
pool_config: PoolConfig::default(),
}
}
pub fn test_connection(&self) -> Result<bool, Error> {
let mut conn = self.inner.connection()?;
let is_connected = conn.test_connected();
#[cfg(not(feature = "pool"))]
conn.quit()?;
Ok(is_connected)
}
}
#[derive(Debug, Clone)]
pub struct SmtpTransportBuilder {
info: SmtpInfo,
#[cfg(feature = "pool")]
pool_config: PoolConfig,
}
impl SmtpTransportBuilder {
pub fn hello_name(mut self, name: ClientId) -> Self {
self.info.hello_name = name;
self
}
pub fn credentials(mut self, credentials: Credentials) -> Self {
self.info.credentials = Some(credentials);
self
}
pub fn authentication(mut self, mechanisms: Vec<Mechanism>) -> Self {
self.info.authentication = mechanisms;
self
}
pub fn timeout(mut self, timeout: Option<Duration>) -> Self {
self.info.timeout = timeout;
self
}
pub fn port(mut self, port: u16) -> Self {
self.info.port = port;
self
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls")))
)]
pub fn tls(mut self, tls: Tls) -> Self {
self.info.tls = tls;
self
}
#[cfg(feature = "pool")]
#[cfg_attr(docsrs, doc(cfg(feature = "pool")))]
pub fn pool_config(mut self, pool_config: PoolConfig) -> Self {
self.pool_config = pool_config;
self
}
pub fn build(self) -> SmtpTransport {
let client = SmtpClient { info: self.info };
#[cfg(feature = "pool")]
let client = Pool::new(self.pool_config, client);
SmtpTransport { inner: client }
}
}
#[derive(Debug, Clone)]
pub struct SmtpClient {
info: SmtpInfo,
}
impl SmtpClient {
pub fn connection(&self) -> Result<SmtpConnection, Error> {
#[allow(clippy::match_single_binding)]
let tls_parameters = match self.info.tls {
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
Tls::Wrapper(ref tls_parameters) => Some(tls_parameters),
_ => None,
};
#[allow(unused_mut)]
let mut conn = SmtpConnection::connect::<(&str, u16)>(
(self.info.server.as_ref(), self.info.port),
self.info.timeout,
&self.info.hello_name,
tls_parameters,
None,
)?;
#[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))]
match self.info.tls {
Tls::Opportunistic(ref tls_parameters) => {
if conn.can_starttls() {
conn.starttls(tls_parameters, &self.info.hello_name)?;
}
}
Tls::Required(ref tls_parameters) => {
conn.starttls(tls_parameters, &self.info.hello_name)?;
}
_ => (),
}
if let Some(credentials) = &self.info.credentials {
conn.auth(&self.info.authentication, credentials)?;
}
Ok(conn)
}
}