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}