howdy/mail/smtp

Send mail through an SMTP server, such as your provider’s relay or a local Mailpit.

import howdy/mail
import howdy/mail/smtp

let assert Ok(config) = smtp.from_env()
let mailer = mail.mailer(smtp.adapter(config))

Each send opens a connection, delivers one message and closes it. The call waits for the server to accept the message, so a refusal comes back as an error rather than a bounce. That wait happens in the calling process: a request handler that sends mail is held until the server answers, or the timeout passes. A failure the server calls temporary, such as a 4xx reply or an unreachable host, is tried again when retries allows it, with backoff between attempts; by default it is not, and the error is returned to the caller.

Types

pub opaque type Config
pub type Error {
  MissingUrl(variable: String)
  InvalidUrl
  UnsupportedTls(mode: String)
}

Constructors

  • MissingUrl(variable: String)

    The environment variable is unset or empty.

  • InvalidUrl

    The URL is not smtp://user:password@host:port or smtps://.... It is not repeated here because it may contain a password.

  • UnsupportedTls(mode: String)

    A tls other than none, starttls or implicit.

How the connection is encrypted.

pub type Tls {
  NoTls
  StartTls(verify: Bool)
  ImplicitTls(verify: Bool)
}

Constructors

  • NoTls

    Unencrypted. Only for a server on the same host or a private network, such as Mailpit in development.

  • StartTls(verify: Bool)

    Connect in plain text and upgrade with STARTTLS, usually on port 587. A server that does not offer the upgrade is refused rather than used unencrypted. verify checks the certificate against the system’s CAs and the host name.

  • ImplicitTls(verify: Bool)

    TLS from the first byte, usually on port 465.

Values

pub fn adapter(config: Config) -> mail.Adapter
pub fn backoff(config: Config, milliseconds: Int) -> Config

How long to wait before the first retry, in milliseconds; each retry after it waits twice as long as the one before. Default 500.

pub fn credentials(
  config: Config,
  username username: String,
  password password: String,
) -> Config

Authenticate with AUTH. Without credentials, none is attempted.

pub fn from_env() -> Result(Config, Error)

Read the server URL from SMTP_URL.

pub fn from_url(url: String) -> Result(Config, Error)

Parse smtp://user:password@host:port or smtps://.... Percent-encode an @ in the user name as %40.

smtps means TLS from the first byte, on port 465 unless another is given. smtp means STARTTLS on port 587, except for loopback addresses and dotless host names, where TLS is off. Certificates are verified. ?tls=none, ?tls=starttls or ?tls=implicit chooses explicitly.

pub fn helo(config: Config, name: String) -> Config

The name this host gives in EHLO. Defaults to its fully qualified domain name.

pub fn host(config: Config) -> String
pub fn new(host: String) -> Config

A server at host on port 587 with verified STARTTLS, or no TLS for loopback addresses and dotless names such as a Compose service called mailpit.

pub fn port(config: Config, port: Int) -> Config
pub fn port_of(config: Config) -> Int
pub fn retries(config: Config, count: Int) -> Config

How many more times to try a send whose failure was temporary: the server could not be reached, timed out, or answered 4xx. Default 0, so a temporary failure comes straight back as Unavailable. A refusal is never tried again. Every attempt happens in the calling process, so a send can take up to retries + 1 times timeout, plus the backoff.

pub fn timeout(config: Config, milliseconds: Int) -> Config

How long to wait for the connection to open. Default 10 seconds. Once connected, each reply from the server is waited for up to gen_smtp’s own limit of twenty minutes.

pub fn tls(config: Config, tls: Tls) -> Config
pub fn tls_of(config: Config) -> Tls
pub fn username(config: Config) -> option.Option(String)
✨ Search Document