From 32e066464ab9b439a3db2857cf66fdd6a3a4131b Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Sun, 2 Feb 2025 13:38:03 +0100 Subject: [PATCH] docs: improve SMTP transport `from_url` (#1032) --- src/transport/smtp/async_transport.rs | 120 ++++++++++++++----------- src/transport/smtp/transport.rs | 124 +++++++++++++++----------- 2 files changed, 137 insertions(+), 107 deletions(-) diff --git a/src/transport/smtp/async_transport.rs b/src/transport/smtp/async_transport.rs index c796115..4baebd2 100644 --- a/src/transport/smtp/async_transport.rs +++ b/src/transport/smtp/async_transport.rs @@ -191,54 +191,72 @@ where /// Creates a `AsyncSmtpTransportBuilder` from a connection URL /// - /// The protocol, credentials, host and port can be provided in a single URL. - /// Use the scheme `smtp` for an unencrypted relay (optionally in combination with the - /// `tls` parameter to allow/require STARTTLS) or `smtps` for SMTP over TLS. - /// The path section of the url can be used to set an alternative name for - /// the HELO / EHLO command. - /// For example `smtps://username:password@smtp.example.com/client.example.com:465` - /// will set the HELO / EHLO name `client.example.com`. + /// The protocol, credentials, host, port and EHLO name can be provided + /// in a single URL. This may be simpler than having to configure SMTP + /// through multiple configuration parameters and then having to pass + /// those options to lettre. /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - ///
schemetls parameterexampleremarks
smtps-smtps://smtp.example.comSMTP over TLS, recommended method
smtprequiredsmtp://smtp.example.com?tls=requiredSMTP with STARTTLS required, when SMTP over TLS is not available
smtpopportunisticsmtp://smtp.example.com?tls=opportunistic - /// SMTP with optionally STARTTLS when supported by the server. - /// Caution: this method is vulnerable to a man-in-the-middle attack. - /// Not recommended for production use. - ///
smtp-smtp://smtp.example.comUnencrypted SMTP, not recommended for production use.
+ /// The URL is created in the following way: + /// `scheme://user:pass@hostname:port/ehlo-name?tls=TLS`. + /// + /// `user` (Username) and `pass` (Password) are optional in case the + /// SMTP relay doesn't require authentication. When `port` is not + /// configured it is automatically determined based on the `scheme`. + /// `ehlo-name` optionally overwrites the hostname sent for the EHLO + /// command. `TLS` controls whether STARTTLS is simply enabled + /// (`opportunistic` - not enough to prevent man-in-the-middle attacks) + /// or `required` (require the server to upgrade the connection to + /// STARTTLS, otherwise fail on suspicion of main-in-the-middle attempt). + /// + /// Use the following table to construct your SMTP url: + /// + /// | scheme | `tls` query parameter | example | default port | remarks | + /// | ------- | --------------------- | -------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- | + /// | `smtps` | unset | `smtps://user:pass@hostname:port` | 465 | SMTP over TLS, recommended method | + /// | `smtp` | `required` | `smtp://user:pass@hostname:port?tls=required` | 587 | SMTP with STARTTLS required, when SMTP over TLS is not available | + /// | `smtp` | `opportunistic` | `smtp://user:pass@hostname:port?tls=opportunistic` | 587 | SMTP with optionally STARTTLS when supported by the server. Not suitable for production use: vulnerable to a man-in-the-middle attack | + /// | `smtp` | unset | `smtp://user:pass@hostname:port` | 587 | Always unencrypted SMTP. Not suitable for production use: sends all data unencrypted | + /// + /// IMPORTANT: some parameters like `user` and `pass` cannot simply + /// be concatenated to construct the final URL because special characters + /// contained within the parameter may confuse the URL decoder. + /// Manually URL encode the parameters before concatenating them or use + /// a proper URL encoder, like the following cargo script: + /// + /// ```rust + /// # let _ = r#" + /// #!/usr/bin/env cargo + /// + /// //! ```cargo + /// //! [dependencies] + /// //! url = "2" + /// //! ``` + /// # "#; + /// + /// use url::Url; + /// + /// fn main() { + /// // don't touch this line + /// let mut url = Url::parse("foo://bar").unwrap(); + /// + /// // configure the scheme (`smtp` or `smtps`) here. + /// url.set_scheme("smtps").unwrap(); + /// // configure the username and password. + /// // remove the following two lines if unauthenticated. + /// url.set_username("username").unwrap(); + /// url.set_password(Some("password")).unwrap(); + /// // configure the hostname + /// url.set_host(Some("smtp.example.com")).unwrap(); + /// // configure the port - only necessary if using a non-default port + /// url.set_port(Some(465)).unwrap(); + /// // configure the EHLO name + /// url.set_path("ehlo-name"); + /// + /// println!("{url}"); + /// } + /// ``` + /// + /// The connection URL can then be used in the following way: /// /// ```rust,no_run /// use lettre::{ @@ -262,15 +280,11 @@ where /// let mailer: AsyncSmtpTransport = /// AsyncSmtpTransport::::from_url( /// "smtps://username:password@smtp.example.com:465", - /// ) - /// .unwrap() + /// )? /// .build(); /// /// // Send the email - /// match mailer.send(email).await { - /// Ok(_) => println!("Email sent successfully!"), - /// Err(e) => panic!("Could not send email: {e:?}"), - /// } + /// mailer.send(email).await?; /// # Ok(()) /// # } /// ``` diff --git a/src/transport/smtp/transport.rs b/src/transport/smtp/transport.rs index 179d886..edf039a 100644 --- a/src/transport/smtp/transport.rs +++ b/src/transport/smtp/transport.rs @@ -139,54 +139,72 @@ impl SmtpTransport { /// Creates a `SmtpTransportBuilder` from a connection URL /// - /// The protocol, credentials, host and port can be provided in a single URL. - /// Use the scheme `smtp` for an unencrypted relay (optionally in combination with the - /// `tls` parameter to allow/require STARTTLS) or `smtps` for SMTP over TLS. - /// The path section of the url can be used to set an alternative name for - /// the HELO / EHLO command. - /// For example `smtps://username:password@smtp.example.com/client.example.com:465` - /// will set the HELO / EHLO name `client.example.com`. + /// The protocol, credentials, host, port and EHLO name can be provided + /// in a single URL. This may be simpler than having to configure SMTP + /// through multiple configuration parameters and then having to pass + /// those options to lettre. /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - ///
schemetls parameterexampleremarks
smtps-smtps://smtp.example.comSMTP over TLS, recommended method
smtprequiredsmtp://smtp.example.com?tls=requiredSMTP with STARTTLS required, when SMTP over TLS is not available
smtpopportunisticsmtp://smtp.example.com?tls=opportunistic - /// SMTP with optionally STARTTLS when supported by the server. - /// Caution: this method is vulnerable to a man-in-the-middle attack. - /// Not recommended for production use. - ///
smtp-smtp://smtp.example.comUnencrypted SMTP, not recommended for production use.
+ /// The URL is created in the following way: + /// `scheme://user:pass@hostname:port/ehlo-name?tls=TLS`. + /// + /// `user` (Username) and `pass` (Password) are optional in case the + /// SMTP relay doesn't require authentication. When `port` is not + /// configured it is automatically determined based on the `scheme`. + /// `ehlo-name` optionally overwrites the hostname sent for the EHLO + /// command. `TLS` controls whether STARTTLS is simply enabled + /// (`opportunistic` - not enough to prevent man-in-the-middle attacks) + /// or `required` (require the server to upgrade the connection to + /// STARTTLS, otherwise fail on suspicion of main-in-the-middle attempt). + /// + /// Use the following table to construct your SMTP url: + /// + /// | scheme | `tls` query parameter | example | default port | remarks | + /// | ------- | --------------------- | -------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- | + /// | `smtps` | unset | `smtps://user:pass@hostname:port` | 465 | SMTP over TLS, recommended method | + /// | `smtp` | `required` | `smtp://user:pass@hostname:port?tls=required` | 587 | SMTP with STARTTLS required, when SMTP over TLS is not available | + /// | `smtp` | `opportunistic` | `smtp://user:pass@hostname:port?tls=opportunistic` | 587 | SMTP with optionally STARTTLS when supported by the server. Not suitable for production use: vulnerable to a man-in-the-middle attack | + /// | `smtp` | unset | `smtp://user:pass@hostname:port` | 587 | Always unencrypted SMTP. Not suitable for production use: sends all data unencrypted | + /// + /// IMPORTANT: some parameters like `user` and `pass` cannot simply + /// be concatenated to construct the final URL because special characters + /// contained within the parameter may confuse the URL decoder. + /// Manually URL encode the parameters before concatenating them or use + /// a proper URL encoder, like the following cargo script: + /// + /// ```rust + /// # let _ = r#" + /// #!/usr/bin/env cargo + /// + /// //! ```cargo + /// //! [dependencies] + /// //! url = "2" + /// //! ``` + /// # "#; + /// + /// use url::Url; + /// + /// fn main() { + /// // don't touch this line + /// let mut url = Url::parse("foo://bar").unwrap(); + /// + /// // configure the scheme (`smtp` or `smtps`) here. + /// url.set_scheme("smtps").unwrap(); + /// // configure the username and password. + /// // remove the following two lines if unauthenticated. + /// url.set_username("username").unwrap(); + /// url.set_password(Some("password")).unwrap(); + /// // configure the hostname + /// url.set_host(Some("smtp.example.com")).unwrap(); + /// // configure the port - only necessary if using a non-default port + /// url.set_port(Some(465)).unwrap(); + /// // configure the EHLO name + /// url.set_path("ehlo-name"); + /// + /// println!("{url}"); + /// } + /// ``` + /// + /// The connection URL can then be used in the following way: /// /// ```rust,no_run /// use lettre::{ @@ -194,6 +212,7 @@ impl SmtpTransport { /// SmtpTransport, Transport, /// }; /// + /// # fn main() -> Result<(), Box> { /// let email = Message::builder() /// .from("NoBody ".parse().unwrap()) /// .reply_to("Yuin ".parse().unwrap()) @@ -204,15 +223,12 @@ impl SmtpTransport { /// .unwrap(); /// /// // Open a remote connection to example - /// let mailer = SmtpTransport::from_url("smtps://username:password@smtp.example.com:465") - /// .unwrap() - /// .build(); + /// let mailer = SmtpTransport::from_url("smtps://username:password@smtp.example.com")?.build(); /// /// // Send the email - /// match mailer.send(&email) { - /// Ok(_) => println!("Email sent successfully!"), - /// Err(e) => panic!("Could not send email: {e:?}"), - /// } + /// mailer.send(&email)?; + /// # Ok(()) + /// # } /// ``` #[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))] #[cfg_attr(