Sovite.AuthResults (sovite v0.6.0)

Copy Markdown View Source

The Authentication-Results: header field (RFC 8601), which tells the next hop and the user's mail client what this server found out about a message:

Authentication-Results: mx.example.org;
        spf=pass smtp.mailfrom=alice@example.com;
        dkim=pass header.d=example.com header.s=sel header.b=AbCdEf12

The first value, the authserv-id, names the server that did the checks. Results claiming to be ours that arrive with a message are forged, so they must be removed before ours are added (RFC 8601 §5), see strip/2.

A result is a map:

%{method: "spf", result: "pass", reason: nil, comment: nil,
  properties: [{"smtp.mailfrom", "alice@example.com"}]}

Summary

Types

A property such as {"smtp.mailfrom", "a@example.com"} or {"header.d", "example.com"}.

Functions

Returns the authserv-id of a field value (the text after Authentication-Results:), unquoted.

Returns the whole header field, ending in CRLF.

Parses a field value into its authserv-id and results.

Removes every Authentication-Results: field whose authserv-id is authserv_id, compared without regard to case. Fields whose authserv-id cannot be read are kept.

Returns the field value, without the field name and the final CRLF, one result per folded line.

Types

property()

@type property() :: {String.t(), String.t()}

A property such as {"smtp.mailfrom", "a@example.com"} or {"header.d", "example.com"}.

result()

@type result() :: %{
  :method => String.t(),
  :result => String.t(),
  optional(:reason) => String.t() | nil,
  optional(:properties) => [property()],
  optional(:comment) => String.t() | nil
}

Functions

authserv_id(value)

@spec authserv_id(String.t()) :: {:ok, String.t()} | :error

Returns the authserv-id of a field value (the text after Authentication-Results:), unquoted.

iex> Sovite.AuthResults.authserv_id(" (checked) mx.example.org 1; spf=pass")
{:ok, "mx.example.org"}
iex> Sovite.AuthResults.authserv_id(" ; spf=pass")
:error

field(authserv_id, results)

@spec field(String.t(), [result()]) :: String.t()

Returns the whole header field, ending in CRLF.

iex> Sovite.AuthResults.field("mx.example.org", [])
"Authentication-Results: mx.example.org; none\r\n"

parse(value)

@spec parse(String.t()) :: {:ok, String.t(), [result()]} | :error

Parses a field value into its authserv-id and results.

Comments and the version after the authserv-id are dropped, and so is a method version (dkim/1). Method, result and property names are lower-cased; values are kept as written. Parsing is lenient about what real servers send, such as unquoted base64 in header.b.

iex> Sovite.AuthResults.parse("mx.example.org; spf=pass (ok) smtp.mailfrom=a@example.com")
{:ok, "mx.example.org",
 [%{method: "spf", result: "pass", reason: nil, properties: [{"smtp.mailfrom", "a@example.com"}]}]}

strip(fields, authserv_id)

Removes every Authentication-Results: field whose authserv-id is authserv_id, compared without regard to case. Fields whose authserv-id cannot be read are kept.

value(authserv_id, results)

@spec value(String.t(), [result()]) :: String.t()

Returns the field value, without the field name and the final CRLF, one result per folded line.

iex> Sovite.AuthResults.value("mx.example.org", [
...>   %{method: "spf", result: "pass", properties: [{"smtp.mailfrom", "a@example.com"}]}
...> ])
"mx.example.org;\r\n\tspf=pass smtp.mailfrom=a@example.com"
iex> Sovite.AuthResults.value("mx.example.org", [])
"mx.example.org; none"

A :reason is always a quoted-string; a :comment follows the result in parentheses. Property values that are not a plain token or address are quoted. Control characters in them are replaced by spaces, so no value can end the field early.