Builds delivery status notifications (RFC 3464), the messages that tell a sender their mail was not delivered (or not yet), or was delivered when they asked to know (DSN, RFC 3461).
A notification is a multipart/report (RFC 6522) with three parts: a
human-readable explanation, a message/delivery-status report for
programs, and the headers of the original message
(text/rfc822-headers), or for a failure with :content the whole
message (message/rfc822).
DSN.build(%{
kind: :failure,
reporting_mta: "mx.example.com",
from: "MAILER-DAEMON@mx.example.com",
to: "alice@example.com",
recipients: [
%{recipient: "bob@example.net", status: "5.1.1", remote_mta: "mx.example.net",
diagnostic: "550 5.1.1 User unknown"}
],
headers: "From: alice@example.com\r\nSubject: hi\r\n\r\n"
})The result is a complete message with CRLF line endings. Send it with
the null reverse-path (MAIL FROM:<>), as RFC 5321 §6.1 requires, so a
failing notification never causes another one.
Text from remote servers (diagnostics) is untrusted: control and
non-ASCII characters are replaced with ?, and long values are cut,
so it cannot inject header fields or MIME boundaries.
Internationalized notifications
A notification about internationalized mail is an RFC 6533 one (see
global?/1): the report is message/global-delivery-status, the
headers are message/global-headers, the explanation is UTF-8, and
internationalized addresses are given with the utf-8 address type
(Final-Recipient: utf-8; jürgen@example.com). Untrusted text keeps
its UTF-8 characters then, but never control characters. Such a
notification must be sent with SMTPUTF8.
Summary
Functions
Builds the notification message. Returns the message and its body type:
:"8bitmime" if it contains 8-bit bytes (the original headers, or an
internationalized notification), otherwise :"7bit".
Whether the notification for report is an internationalized one
(RFC 6533): an address in it is internationalized, or the original
message was sent with SMTPUTF8 and has UTF-8 header fields.
Types
@type recipient() :: %{ :recipient => String.t(), :status => String.t(), optional(:action) => :delivered | :relayed | :expanded, optional(:original_recipient) => {String.t(), String.t()} | nil, optional(:remote_mta) => String.t() | nil, optional(:diagnostic) => String.t() | nil, optional(:reason) => String.t() | nil, optional(:last_attempt) => DateTime.t() | nil }
One recipient in the report.
:recipient- the address. Required.:status- enhanced status code, such as"5.1.1". Required.:remote_mta- host name of the server that gave:diagnostic.:diagnostic- the remote server's SMTP reply.:reason- a local explanation, used when there is no SMTP reply (for example "Host or domain name not found").:last_attempt- time of the last delivery attempt.:action- for:successreports, what happened::delivered(to the mailbox),:relayed(to a server that does not send notifications), or:expanded(to the members of an alias). Defaults to:delivered.:original_recipient- theORCPTthe client gave, asSovite.SMTP.DSNParams.orcpt().
@type report() :: %{ :kind => :failure | :delay | :success, :reporting_mta => String.t(), :from => String.t(), :to => String.t(), :recipients => [recipient(), ...], optional(:headers) => binary() | nil, optional(:content) => binary() | nil, optional(:envid) => String.t() | nil, optional(:future_release) => String.t() | nil, optional(:queue_id) => String.t() | nil, optional(:smtputf8) => boolean(), optional(:arrival_date) => DateTime.t() | nil, optional(:will_retry_until) => DateTime.t() | nil, optional(:date) => DateTime.t(), optional(:message_id) => String.t(), optional(:boundary) => String.t() }
The report.
:kind-:failure(the listed recipients will never get the message),:delay(still trying), or:success(delivered). Required.:reporting_mta- this server's host name. Required.:from- address in theFrom:field, usuallyMAILER-DAEMON@<host>. Required.:to- the original sender. Required.:recipients- at least one. Required.:headers- the original message's header section.:content- for:failure, the whole original message, returned instead of:headers(RET=FULL).:envid- theENVIDthe client gave.:future_release- the original message'sFUTURERELEASErequest (RFC 4865 §5), as"for;3600"or"until;<date-time>".:queue_id- the original message's queue ID.:smtputf8- the original message was sent withSMTPUTF8, so its header fields may be UTF-8 (RFC 6532).:arrival_date- when the original message was received.:will_retry_until- for:delay, when delivery will be given up.:date,:message_id,:boundary- default to now, a new ID, and a random boundary.
Functions
Builds the notification message. Returns the message and its body type:
:"8bitmime" if it contains 8-bit bytes (the original headers, or an
internationalized notification), otherwise :"7bit".
Whether the notification for report is an internationalized one
(RFC 6533): an address in it is internationalized, or the original
message was sent with SMTPUTF8 and has UTF-8 header fields.