Sovite.Core.Bounce (sovite v0.6.0)

Copy Markdown View Source

The bounce service: tells senders about failed and delayed delivery, and about successful delivery when they ask for it (DSN, RFC 3461).

Notifications are built with Sovite.DSN and queued like any other message, from the null sender (MAIL FROM:<>). The rules:

  • Failed recipients are reported to the sender in one notification per delivery attempt.
  • Delay warnings (queue.delay_warning) are sent once per message.
  • The NOTIFY parameter of each recipient decides which of these are sent for it; that is up to the caller. Success notifications (NOTIFY=SUCCESS) report recipients delivered to a mailbox, relayed to a server that does not support DSN, or expanded by an alias to several addresses.
  • Notifications carry the ENVID and each recipient's ORCPT. A failure notification for a message sent with RET=FULL returns the whole message, unless it is larger than bounce.size_limit or binary (BINARYMIME); otherwise, and for every other notification, only the header section.
  • Nothing is ever sent to the null sender (RFC 5321 §6.1, RFC 3834): a failed notification is a double bounce. It is reported to bounce.double_bounce_recipient when set, otherwise only logged. A failed double-bounce report is only logged, so notifications can never loop.
  • Notifications about a REQUIRETLS message are sent with REQUIRETLS too (RFC 8689 §4.3), and only include the message's header section, as if RET=HDRS (RFC 8689 §5).
  • Notifications about internationalized mail (internationalized addresses, or UTF-8 header fields in a message sent with SMTPUTF8) are RFC 6533 ones, sent with SMTPUTF8, see Sovite.DSN.

Summary

Types

Options: :hostname, :directory (the spool), :max_lifetime (milliseconds, for "will retry until"), :double_bounce_recipient (or nil), and optionally :expand, a function that turns the notification's recipient into the addresses to queue it for (aliases), and :size_limit, the largest message returned whole (bytes, default 50000).

Where the original message is, as returned by Sovite.Queue.Spool.load/2, possibly with more keys.

Functions

Reports recipients (address and Sovite.Queue.Record.details() pairs) of the message in entry.

Types

opts()

@type opts() :: %{
  :hostname => String.t(),
  :directory => Path.t(),
  :max_lifetime => pos_integer(),
  :double_bounce_recipient => String.t() | nil,
  optional(:expand) => (String.t() -> [String.t(), ...]),
  optional(:size_limit) => non_neg_integer()
}

Options: :hostname, :directory (the spool), :max_lifetime (milliseconds, for "will retry until"), :double_bounce_recipient (or nil), and optionally :expand, a function that turns the notification's recipient into the addresses to queue it for (aliases), and :size_limit, the largest message returned whole (bytes, default 50000).

source()

@type source() :: %{
  :path => Path.t(),
  :message_offset => non_neg_integer(),
  :message_size => non_neg_integer(),
  :prefix => binary(),
  optional(atom()) => term()
}

Where the original message is, as returned by Sovite.Queue.Spool.load/2, possibly with more keys.

Functions

notify(kind, entry, source, recipients, opts)

@spec notify(
  :failure | :delay | :success,
  Sovite.Queue.Entry.t(),
  source(),
  [{String.t(), map()}],
  opts()
) :: {:ok, String.t() | nil} | {:error, term()}

Reports recipients (address and Sovite.Queue.Record.details() pairs) of the message in entry.

Returns {:ok, queue_id} for the queued notification, {:ok, nil} when the rules say not to send one, or {:error, reason} if it could not be queued; the caller should then try again later.