lettre/transport/smtp/mod.rs
1//! The SMTP transport sends emails using the SMTP protocol.
2//!
3//! This SMTP client follows [RFC
4//! 5321](https://tools.ietf.org/html/rfc5321), and is designed to efficiently send emails from an
5//! application to a relay email server, as it relies as much as possible on the relay server
6//! for sanity and RFC compliance checks.
7//!
8//! It implements the following extensions:
9//!
10//! * 8BITMIME ([RFC 6152](https://tools.ietf.org/html/rfc6152))
11//! * AUTH ([RFC 4954](https://tools.ietf.org/html/rfc4954)) with PLAIN, LOGIN and XOAUTH2 mechanisms
12//! * STARTTLS ([RFC 2487](https://tools.ietf.org/html/rfc2487))
13//!
14//! #### SMTP Transport
15//!
16//! This transport uses the SMTP protocol to send emails over the network (locally or remotely).
17//!
18//! It is designed to be:
19//!
20//! * Secured: connections are encrypted by default
21//! * Modern: unicode support for email contents and sender/recipient addresses when compatible
22//! * Fast: supports connection reuse and pooling
23//!
24//! This client is designed to send emails to a relay server, and should *not* be used to send
25//! emails directly to the destination server.
26//!
27//! The relay server can be the local email server, a specific host or a third-party service.
28//!
29//! #### Simple example with authentication
30//!
31//! A good starting point for sending emails via SMTP relay is to
32//! do the following:
33//!
34//! ```rust,no_run
35//! # #[cfg(all(feature = "builder", any(feature = "native-tls", feature = "rustls")))]
36//! # fn test() -> Result<(), Box<dyn std::error::Error>> {
37//! use lettre::{
38//! message::header::ContentType,
39//! transport::smtp::authentication::{Credentials, Mechanism},
40//! Message, SmtpTransport, Transport,
41//! };
42//!
43//! let email = Message::builder()
44//! .from("NoBody <nobody@domain.tld>".parse()?)
45//! .reply_to("Yuin <yuin@domain.tld>".parse()?)
46//! .to("Hei <hei@domain.tld>".parse()?)
47//! .subject("Happy new year")
48//! .header(ContentType::TEXT_PLAIN)
49//! .body(String::from("Be happy!"))?;
50//!
51//! // Create the SMTPS transport
52//! let sender = SmtpTransport::relay("smtp.example.com")?
53//! // Add credentials for authentication
54//! .credentials(Credentials::new(
55//! "username".to_owned(),
56//! "password".to_owned(),
57//! ))
58//! // Optionally configure expected authentication mechanism
59//! .authentication(vec![Mechanism::Plain])
60//! .build();
61//!
62//! // Send the email via remote relay
63//! sender.send(&email)?;
64//! # Ok(())
65//! # }
66//! ```
67//!
68//! #### Shortening configuration
69//!
70//! It can be very repetitive to ask the user for every SMTP connection parameter.
71//! In some cases this can be simplified by using a connection URI instead.
72//!
73//! For more information take a look at [`SmtpTransport::from_url`] or [`AsyncSmtpTransport::from_url`].
74//!
75//! ```rust,no_run
76//! # #[cfg(all(feature = "builder", any(feature = "native-tls", feature = "rustls")))]
77//! # fn test() -> Result<(), Box<dyn std::error::Error>> {
78//! use lettre::{
79//! message::header::ContentType,
80//! transport::smtp::authentication::{Credentials, Mechanism},
81//! Message, SmtpTransport, Transport,
82//! };
83//!
84//! let email = Message::builder()
85//! .from("NoBody <nobody@domain.tld>".parse()?)
86//! .reply_to("Yuin <yuin@domain.tld>".parse()?)
87//! .to("Hei <hei@domain.tld>".parse()?)
88//! .subject("Happy new year")
89//! .header(ContentType::TEXT_PLAIN)
90//! .body(String::from("Be happy!"))?;
91//!
92//! // Create the SMTPS transport
93//! let sender = SmtpTransport::from_url("smtps://username:password@smtp.example.com")?.build();
94//!
95//! // Send the email via remote relay
96//! sender.send(&email)?;
97//! # Ok(())
98//! # }
99//! ```
100//!
101//! #### Advanced configuration with custom TLS settings
102//!
103//! ```rust,no_run
104//! # #[cfg(all(feature = "builder", any(feature = "native-tls", feature = "rustls")))]
105//! # fn test() -> Result<(), Box<dyn std::error::Error>> {
106//! use std::fs;
107//!
108//! use lettre::{
109//! message::header::ContentType,
110//! transport::smtp::client::{Certificate, Tls, TlsParameters},
111//! Message, SmtpTransport, Transport,
112//! };
113//!
114//! let email = Message::builder()
115//! .from("NoBody <nobody@domain.tld>".parse()?)
116//! .reply_to("Yuin <yuin@domain.tld>".parse()?)
117//! .to("Hei <hei@domain.tld>".parse()?)
118//! .subject("Happy new year")
119//! .header(ContentType::TEXT_PLAIN)
120//! .body(String::from("Be happy!"))?;
121//!
122//! // Custom TLS configuration - Use a self signed certificate
123//! let cert = fs::read("self-signed.crt")?;
124//! let cert = Certificate::from_pem(&cert)?;
125//! let tls = TlsParameters::builder(/* TLS SNI value */ "smtp.example.com".to_owned())
126//! .add_root_certificate(cert)
127//! .build()?;
128//!
129//! // Create the SMTPS transport
130//! let sender = SmtpTransport::relay("smtp.example.com")?
131//! .tls(Tls::Wrapper(tls))
132//! .build();
133//!
134//! // Send the email via remote relay
135//! sender.send(&email)?;
136//! # Ok(())
137//! # }
138//! ```
139//!
140//! #### Connection pooling
141//!
142//! [`SmtpTransport`] and [`AsyncSmtpTransport`] store connections in
143//! a connection pool by default. This avoids connecting and disconnecting
144//! from the relay server for every message the application tries to send. For the connection pool
145//! to work the instance of the transport **must** be reused.
146//! In a webserver context it may go about this:
147//!
148//! ```rust,no_run
149//! # #[cfg(all(feature = "builder", any(feature = "native-tls", feature = "rustls")))]
150//! # fn test() {
151//! use lettre::{
152//! message::header::ContentType,
153//! transport::smtp::{authentication::Credentials, PoolConfig},
154//! Message, SmtpTransport, Transport,
155//! };
156//! #
157//! # type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
158//!
159//! /// The global application state
160//! #[derive(Debug)]
161//! struct AppState {
162//! smtp: SmtpTransport,
163//! // ... other global application parameters
164//! }
165//!
166//! impl AppState {
167//! pub fn new(smtp_url: &str) -> Result<Self> {
168//! let smtp = SmtpTransport::from_url(smtp_url)?.build();
169//! Ok(Self { smtp })
170//! }
171//! }
172//!
173//! fn handle_request(app_state: &AppState) -> Result<String> {
174//! let email = Message::builder()
175//! .from("NoBody <nobody@domain.tld>".parse()?)
176//! .reply_to("Yuin <yuin@domain.tld>".parse()?)
177//! .to("Hei <hei@domain.tld>".parse()?)
178//! .subject("Happy new year")
179//! .header(ContentType::TEXT_PLAIN)
180//! .body(String::from("Be happy!"))?;
181//!
182//! // Send the email via remote relay
183//! app_state.smtp.send(&email)?;
184//!
185//! Ok("The email has successfully been sent!".to_owned())
186//! }
187//! # }
188//! ```
189
190use std::time::Duration;
191
192use client::Tls;
193
194#[cfg(any(feature = "tokio1", feature = "async-std1"))]
195pub use self::async_transport::{AsyncSmtpTransport, AsyncSmtpTransportBuilder};
196#[cfg(feature = "pool")]
197pub use self::pool::PoolConfig;
198pub use self::{
199 error::Error,
200 transport::{SmtpTransport, SmtpTransportBuilder},
201};
202#[cfg(any(feature = "native-tls", feature = "rustls", feature = "boring-tls"))]
203use crate::transport::smtp::client::TlsParameters;
204use crate::transport::smtp::{
205 authentication::{Credentials, Mechanism, DEFAULT_MECHANISMS},
206 client::SmtpConnection,
207 extension::ClientId,
208 response::Response,
209};
210
211#[cfg(any(feature = "tokio1", feature = "async-std1"))]
212mod async_transport;
213pub mod authentication;
214pub mod client;
215pub mod commands;
216mod connection_url;
217mod error;
218pub mod extension;
219#[cfg(feature = "pool")]
220mod pool;
221pub mod response;
222mod transport;
223pub(super) mod util;
224
225// Registered port numbers:
226// https://www.iana.
227// org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
228
229/// Default smtp port
230pub const SMTP_PORT: u16 = 25;
231/// Default submission port
232pub const SUBMISSION_PORT: u16 = 587;
233/// Default submission over TLS port
234///
235/// Defined in [RFC8314](https://tools.ietf.org/html/rfc8314)
236pub const SUBMISSIONS_PORT: u16 = 465;
237
238/// Default timeout
239const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60);
240
241#[derive(Debug, Clone)]
242struct SmtpInfo {
243 /// Name sent during EHLO
244 hello_name: ClientId,
245 /// Server we are connecting to
246 server: String,
247 /// Port to connect to
248 port: u16,
249 /// TLS security configuration
250 tls: Tls,
251 /// Optional enforced authentication mechanism
252 authentication: Vec<Mechanism>,
253 /// Credentials
254 credentials: Option<Credentials>,
255 /// Define network timeout
256 /// It can be changed later for specific needs (like a different timeout for each SMTP command)
257 timeout: Option<Duration>,
258}
259
260impl Default for SmtpInfo {
261 fn default() -> Self {
262 Self {
263 server: "localhost".to_owned(),
264 port: SMTP_PORT,
265 hello_name: ClientId::default(),
266 credentials: None,
267 authentication: DEFAULT_MECHANISMS.into(),
268 timeout: Some(DEFAULT_TIMEOUT),
269 tls: Tls::None,
270 }
271 }
272}