lettre/lib.rs
1//! Lettre is an email library that allows creating and sending messages. It provides:
2//!
3//! * An easy to use email builder
4//! * Pluggable email transports
5//! * Unicode support
6//! * Secure defaults
7//! * Async support
8//!
9//! Lettre requires Rust 1.74 or newer.
10//!
11//! ## Features
12//!
13//! This section lists each lettre feature and briefly explains it.
14//! More info about each module can be found in the corresponding module page.
15//!
16//! Features with `📫` near them are enabled by default.
17//!
18//! ### Typed message builder
19//!
20//! _Strongly typed [`message`] builder_
21//!
22//! * **builder** 📫: Enable the [`Message`] builder
23//! * **hostname** 📫: Try to use the actual system hostname in the `Message-ID` header
24//!
25//! ### SMTP transport
26//!
27//! _Send emails using [`SMTP`]_
28//!
29//! * **smtp-transport** 📫: Enable the SMTP transport
30//! * **pool** 📫: Connection pool for SMTP transport
31//! * **hostname** 📫: Try to use the actual system hostname for the SMTP `CLIENTID`
32//!
33//! #### SMTP over TLS via the native-tls crate
34//!
35//! _Secure SMTP connections using TLS from the `native-tls` crate_
36//!
37//! Uses schannel on Windows, Security-Framework on macOS, and OpenSSL
38//! on all other platforms.
39//!
40//! * **native-tls** 📫: TLS support for the synchronous version of the API
41//! * **tokio1-native-tls**: TLS support for the `tokio1` async version of the API
42//!
43//! NOTE: native-tls isn't supported with `async-std`
44//!
45//! ##### Building lettre with OpenSSL
46//!
47//! When building lettre with native-tls on a system that makes
48//! use of OpenSSL, the following packages will need to be installed
49//! in order for the build and the compiled program to run properly.
50//!
51//! | Distro | Build-time packages | Runtime packages |
52//! | ------------ | -------------------------- | ---------------------------- |
53//! | Debian | `pkg-config`, `libssl-dev` | `libssl3`, `ca-certificates` |
54//! | Alpine Linux | `pkgconf`, `openssl-dev` | `libssl3`, `ca-certificates` |
55//!
56//! #### SMTP over TLS via the boring crate (Boring TLS)
57//!
58//! _Secure SMTP connections using TLS from the `boring-tls` crate_
59//!
60//! * **boring-tls**: TLS support for the synchronous version of the API
61//! * **tokio1-boring-tls**: TLS support for the `tokio1` async version of the API
62//!
63//! NOTE: boring-tls isn't supported with `async-std`
64//!
65//! #### SMTP over TLS via the rustls crate
66//!
67//! _Secure SMTP connections using TLS from the `rustls` crate_
68//!
69//! * **rustls**: TLS support for the synchronous version of the API
70//! * **tokio1-rustls**: TLS support for the `tokio1` async version of the API
71//! * **async-std1-rustls**: TLS support for the `async-std1` async version of the API
72//!
73//! ##### rustls crypto backends
74//!
75//! _The crypto implementation to use with rustls_
76//!
77//! When the `rustls` feature is enabled, one of the following crypto backends MUST also
78//! be enabled.
79//!
80//! * **aws-lc-rs**: use [AWS-LC] (via [`aws-lc-rs`]) as the `rustls` crypto backend
81//! * **ring**: use [`ring`] as the `rustls` crypto backend
82//!
83//! When enabling `aws-lc-rs`, the `fips` feature can also be enabled to have
84//! rustls use the FIPS certified module of AWS-LC.
85//!
86//! `aws-lc-rs` may require cmake on some platforms to compile.
87//! `fips` always requires cmake and the Go compiler to compile.
88//!
89//! ##### rustls certificate verification backend
90//!
91//! _The TLS certificate verification backend to use with rustls_
92//!
93//! When the `rustls` feature is enabled, one of the following verification backends
94//! MUST also be enabled.
95//!
96//! * **rustls-platform-verifier**: verify TLS certificate using the OS's native certificate store (see [`rustls-platform-verifier`])
97//! * **rustls-native-certs**: verify TLS certificates using the platform's native certificate store (see [`rustls-native-certs`]) - when in doubt use `rustls-platform-verifier`
98//! * **webpki-roots**: verify TLS certificates against Mozilla's root certificates (see [`webpki-roots`])
99//!
100//! The following packages will need to be installed in order for the build
101//! stage and the compiled program to run properly.
102//!
103//! | Verification backend | Distro | Build-time packages | Runtime packages |
104//! | --------------------- | ------------ | -------------------------- | ---------------------------- |
105//! | `rustls-platform-verifier` | Debian | none | `ca-certificates` |
106//! | `rustls-platform-verifier` | Alpine Linux | none | `ca-certificates` |
107//! | `rustls-native-certs` | Debian | none | `ca-certificates` |
108//! | `rustls-native-certs` | Alpine Linux | none | `ca-certificates` |
109//! | `webpki-roots` | any | none | none |
110//!
111//! ### Sendmail transport
112//!
113//! _Send emails using the [`sendmail`] command_
114//!
115//! * **sendmail-transport**: Enable the `sendmail` transport
116//!
117//! ### File transport
118//!
119//! _Save emails as an `.eml` [`file`]_
120//!
121//! * **file-transport**: Enable the file transport (saves emails into an `.eml` file)
122//! * **file-transport-envelope**: Allow writing the envelope into a JSON file (additionally saves envelopes into a `.json` file)
123//!
124//! ### Async execution runtimes
125//!
126//! _Use [tokio] or [async-std] as an async execution runtime for sending emails_
127//!
128//! The correct runtime version must be chosen in order for lettre to work correctly.
129//! For example, when sending emails from a Tokio 1.x context, the Tokio 1.x executor
130//! ([`Tokio1Executor`]) must be used. Using a different version (for example Tokio 0.2.x),
131//! or async-std, would result in a runtime panic.
132//!
133//! * **tokio1**: Allow to asynchronously send emails using [Tokio 1.x]
134//! * **async-std1**: Allow to asynchronously send emails using [async-std 1.x]
135//!
136//! NOTE: native-tls isn't supported with `async-std`
137//!
138//! ### Misc features
139//!
140//! _Additional features_
141//!
142//! * **serde**: Serialization/Deserialization of entities
143//! * **tracing**: Logging using the `tracing` crate
144//! * **mime03**: Allow creating a [`ContentType`] from an existing [mime 0.3] `Mime` struct
145//! * **dkim**: Add support for signing email with DKIM
146//! * **web**: WebAssembly support using the `web-time` crate for time operations
147//!
148//! [`SMTP`]: crate::transport::smtp
149//! [`sendmail`]: crate::transport::sendmail
150//! [`file`]: crate::transport::file
151//! [`ContentType`]: crate::message::header::ContentType
152//! [tokio]: https://docs.rs/tokio/1
153//! [async-std]: https://docs.rs/async-std/1
154//! [AWS-LC]: https://github.com/aws/aws-lc
155//! [`aws-lc-rs`]: https://crates.io/crates/aws-lc-rs
156//! [`ring`]: https://crates.io/crates/ring
157//! [`rustls-platform-verifier`]: https://crates.io/crates/rustls-platform-verifier
158//! [`rustls-native-certs`]: https://crates.io/crates/rustls-native-certs
159//! [`webpki-roots`]: https://crates.io/crates/webpki-roots
160//! [Tokio 1.x]: https://docs.rs/tokio/1
161//! [async-std 1.x]: https://docs.rs/async-std/1
162//! [mime 0.3]: https://docs.rs/mime/0.3
163//! [DKIM]: https://datatracker.ietf.org/doc/html/rfc6376
164
165#![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.17")]
166#![doc(html_favicon_url = "https://lettre.rs/favicon.ico")]
167#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")]
168#![forbid(unsafe_code)]
169#![deny(
170 unreachable_pub,
171 missing_copy_implementations,
172 trivial_casts,
173 trivial_numeric_casts,
174 unstable_features,
175 unused_import_braces,
176 rust_2018_idioms,
177 clippy::string_add,
178 clippy::string_add_assign,
179 clippy::clone_on_ref_ptr,
180 clippy::verbose_file_reads,
181 clippy::unnecessary_self_imports,
182 clippy::string_to_string,
183 clippy::mem_forget,
184 clippy::cast_lossless,
185 clippy::inefficient_to_string,
186 clippy::inline_always,
187 clippy::linkedlist,
188 clippy::macro_use_imports,
189 clippy::manual_assert,
190 clippy::unnecessary_join,
191 clippy::wildcard_imports,
192 clippy::str_to_string,
193 clippy::empty_structs_with_brackets,
194 clippy::zero_sized_map_values,
195 clippy::manual_let_else,
196 clippy::semicolon_if_nothing_returned,
197 clippy::unnecessary_wraps,
198 clippy::doc_markdown,
199 clippy::explicit_iter_loop,
200 clippy::redundant_closure_for_method_calls,
201 // Rust 1.86: clippy::unnecessary_semicolon,
202)]
203#![cfg_attr(docsrs, feature(doc_cfg))]
204
205#[cfg(not(lettre_ignore_tls_mismatch))]
206mod compiletime_checks {
207 #[cfg(all(feature = "rustls", not(feature = "aws-lc-rs"), not(feature = "ring")))]
208 compile_error!(
209 "feature `rustls` also requires either the `aws-lc-rs` or the `ring` feature to
210 be enabled"
211 );
212
213 #[cfg(all(
214 feature = "rustls",
215 not(feature = "rustls-platform-verifier"),
216 not(feature = "rustls-native-certs"),
217 not(feature = "webpki-roots")
218 ))]
219 compile_error!(
220 "feature `rustls` also requires either the `rustls-platform-verifier`, the `rustls-native-certs`
221 or the `webpki-roots` feature to be enabled"
222 );
223
224 #[cfg(all(feature = "native-tls", feature = "boring-tls"))]
225 compile_error!("feature \"native-tls\" and feature \"boring-tls\" cannot be enabled at the same time, otherwise
226 the executable will fail to link.");
227
228 #[cfg(all(
229 feature = "tokio1",
230 feature = "native-tls",
231 not(feature = "tokio1-native-tls")
232 ))]
233 compile_error!("Lettre is being built with the `tokio1` and the `native-tls` features, but the `tokio1-native-tls` feature hasn't been turned on.
234 If you were trying to opt into `rustls` and did not activate `native-tls`, disable the default-features of lettre in `Cargo.toml` and manually add the required features.
235 Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");
236
237 #[cfg(all(feature = "tokio1", feature = "rustls", not(feature = "tokio1-rustls")))]
238 compile_error!("Lettre is being built with the `tokio1` and the `rustls` features, but the `tokio1-rustls` feature hasn't been turned on.
239 If you'd like to use `native-tls` make sure that the `rustls` feature hasn't been enabled by mistake.
240 Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");
241
242 #[cfg(all(
243 feature = "tokio1",
244 feature = "boring-tls",
245 not(feature = "tokio1-boring-tls")
246 ))]
247 compile_error!("Lettre is being built with the `tokio1` and the `boring-tls` features, but the `tokio1-boring-tls` feature hasn't been turned on.
248 If you'd like to use `boring-tls` make sure that the `rustls` feature hasn't been enabled by mistake.
249 Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");
250
251 #[cfg(all(feature = "async-std1", feature = "native-tls"))]
252 compile_error!("Lettre is being built with the `async-std1` and the `native-tls` features, but the async-std integration doesn't support native-tls yet.
253If you'd like to work on the issue please take a look at https://github.com/lettre/lettre/issues/576.
254If you were trying to opt into `rustls` and did not activate `native-tls`, disable the default-features of lettre in `Cargo.toml` and manually add the required features.
255Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");
256
257 #[cfg(all(
258 feature = "async-std1",
259 feature = "rustls",
260 not(feature = "async-std1-rustls")
261 ))]
262 compile_error!("Lettre is being built with the `async-std1` and the `rustls` features, but the `async-std1-rustls` feature hasn't been turned on.
263If you'd like to use `native-tls` make sure that the `rustls` hasn't been enabled by mistake.
264Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");
265}
266
267pub mod address;
268#[cfg(any(feature = "smtp-transport", feature = "dkim"))]
269mod base64;
270pub mod error;
271#[cfg(any(feature = "tokio1", feature = "async-std1"))]
272mod executor;
273#[cfg(feature = "builder")]
274#[cfg_attr(docsrs, doc(cfg(feature = "builder")))]
275pub mod message;
276#[cfg(feature = "rustls")]
277mod rustls_crypto;
278mod time;
279pub mod transport;
280
281use std::error::Error as StdError;
282
283#[cfg(feature = "async-std1")]
284pub use self::executor::AsyncStd1Executor;
285#[cfg(any(feature = "tokio1", feature = "async-std1"))]
286pub use self::executor::Executor;
287#[cfg(feature = "tokio1")]
288pub use self::executor::Tokio1Executor;
289#[cfg(any(feature = "tokio1", feature = "async-std1"))]
290#[doc(inline)]
291pub use self::transport::AsyncTransport;
292pub use crate::address::Address;
293#[cfg(feature = "builder")]
294#[doc(inline)]
295pub use crate::message::Message;
296#[cfg(all(
297 feature = "file-transport",
298 any(feature = "tokio1", feature = "async-std1")
299))]
300#[doc(inline)]
301pub use crate::transport::file::AsyncFileTransport;
302#[cfg(feature = "file-transport")]
303#[doc(inline)]
304pub use crate::transport::file::FileTransport;
305#[cfg(all(
306 feature = "sendmail-transport",
307 any(feature = "tokio1", feature = "async-std1")
308))]
309#[doc(inline)]
310pub use crate::transport::sendmail::AsyncSendmailTransport;
311#[cfg(feature = "sendmail-transport")]
312#[doc(inline)]
313pub use crate::transport::sendmail::SendmailTransport;
314#[cfg(all(
315 feature = "smtp-transport",
316 any(feature = "tokio1", feature = "async-std1")
317))]
318pub use crate::transport::smtp::AsyncSmtpTransport;
319#[cfg(feature = "smtp-transport")]
320pub use crate::transport::smtp::SmtpTransport;
321#[doc(inline)]
322pub use crate::transport::Transport;
323use crate::{address::Envelope, error::Error};
324
325pub(crate) type BoxError = Box<dyn StdError + Send + Sync>;
326
327#[cfg(test)]
328#[cfg(feature = "builder")]
329mod test {
330 use super::*;
331 use crate::message::{header, header::Headers, Mailbox, Mailboxes};
332
333 #[test]
334 fn envelope_from_headers() {
335 let from = Mailboxes::new().with("kayo@example.com".parse().unwrap());
336 let to = Mailboxes::new().with("amousset@example.com".parse().unwrap());
337
338 let mut headers = Headers::new();
339 headers.set(header::From(from));
340 headers.set(header::To(to));
341
342 assert_eq!(
343 Envelope::try_from(&headers).unwrap(),
344 Envelope::new(
345 Some(Address::new("kayo", "example.com").unwrap()),
346 vec![Address::new("amousset", "example.com").unwrap()]
347 )
348 .unwrap()
349 );
350 }
351
352 #[test]
353 fn envelope_from_headers_sender() {
354 let from = Mailboxes::new().with("kayo@example.com".parse().unwrap());
355 let sender = Mailbox::new(None, "kayo2@example.com".parse().unwrap());
356 let to = Mailboxes::new().with("amousset@example.com".parse().unwrap());
357
358 let mut headers = Headers::new();
359 headers.set(header::From::from(from));
360 headers.set(header::Sender::from(sender));
361 headers.set(header::To::from(to));
362
363 assert_eq!(
364 Envelope::try_from(&headers).unwrap(),
365 Envelope::new(
366 Some(Address::new("kayo2", "example.com").unwrap()),
367 vec![Address::new("amousset", "example.com").unwrap()]
368 )
369 .unwrap()
370 );
371 }
372
373 #[test]
374 fn envelope_from_headers_no_to() {
375 let from = Mailboxes::new().with("kayo@example.com".parse().unwrap());
376 let sender = Mailbox::new(None, "kayo2@example.com".parse().unwrap());
377
378 let mut headers = Headers::new();
379 headers.set(header::From::from(from));
380 headers.set(header::Sender::from(sender));
381
382 assert!(Envelope::try_from(&headers).is_err(),);
383 }
384}