MailAddress v0.3.0 MailAddress View Source

Functions to handle RFC5321 Mail Addresses.

The library implements functions for handling email addresses as specified mostly by RFC5321. A large chunk of the address syntax is implemented, with a few exceptions:

  • Handling of general address literals in domains (IPv4 and IPv6 address literals are supported).
  • Handling of internationalized addresses (UTF8, punycode etc).

Creating Addresses

Addresses may be created a number of ways:

  • %MailAddress{} - this will create a null address.

  • Calling new/3 - this will directly assign a local and domain part.

  • Calling MailAddress.Parser.parse/2 - this will parse a string into an address.

Examples

iex> %MailAddress{}
#MailAddress<>

iex> {:ok, addr} = MailAddress.new("test", "example.org")
iex> addr
#MailAddress<test@example.org>

iex> {:ok, addr, ""} = MailAddress.Parser.parse("test@example.org")
iex> addr
#MailAddress<test@example.org>

Modifying Addresses

Addresses can be modified by a number of functions, which return a new address with the appropriate update:

Querying Addresses

Addresses can be queryied for their components:

Comparing and Encoding Addresses

Parsing Addresses

The module MailAddress.Parser contains parsing code.

Specifying Options

The MailAddress.Options struct is used to store options for configuring the library. Checks are applied after every change/creation operation.

Protocols

The library implements the Inspect and String.Chars protocols for MailAddress structs.

The Inspect protocol is used in the iex shell and by inspect/2 to pretty-print the MailAddress struct contents.

The String.Chars protocol enables a MailAddress struct to be directly converted into an encoded string.

Link to this section Summary

Types

Error return type - a tuple containing :error and a reason string

Represents an IPv4 or IPv6 address

Success return type - a tuple containing :ok and a MailAddress struct

t()

The MailAddress struct

Functions

Address struct

Returns the decoded address literal domain (if any), or nil otherwise

Checks whether address has an address literal domain part

Applies checks and optional domain downcasing to given address using passed options

Returns the domain part of the address

Checks whether address has a domain part

Compares domain of given address with domain (case-insensitively). Returns true if the domains are the same, or false otherwise

Returns address safely encoded, optionally (and by default) bracketed

Checks whether addr_1 and addr_2 are the same. The local parts are compared case sensitively, whilst the domain parts are compare case insensitively

Returns the local part of the address

Checks whether address has local part set

Compares address local parts (case-sensitively). The second parameter may be either a string or a MailAddress struct. Returns true if the local parts are the same, or false otherwise

Checks whether domain part of address is ‘localhost’, or the domain is an address literal and is [127.0.0.1] or [IPv6:::1]

Checks to see if the given string is “localhost” or equivalent ([127.0.0.1] or [IPv6:::1])

Checks whether the local part of the given address needs quoting

Creates a new MailAddress setting both local and domain parts at the same time using the provided (or default) options

Checks whether the address in null (no local part and no domain)

Sets the domain part of the address using the provided (or default) options

Sets the local part of the address using the provided (or default) options

Link to this section Types

Link to this type error() View Source
error() :: {:error, String.t()}

Error return type - a tuple containing :error and a reason string.

Represents an IPv4 or IPv6 address.

Link to this type success() View Source
success() ::
  {:ok,
   %MailAddress{
     address_literal: term(),
     domain: term(),
     local_part: term(),
     needs_quoting: term()
   }}

Success return type - a tuple containing :ok and a MailAddress struct.

Link to this type t() View Source
t() :: %MailAddress{
  address_literal: nil | ip_address(),
  domain: String.t(),
  local_part: String.t(),
  needs_quoting: boolean()
}

The MailAddress struct.

Link to this section Functions

Address struct.

The struct SHOULD be treated as opaque and not tampered with directly as it may change, and the needs_quoting field is cached.

Callers should use the appropriate functions to get/set fields which ensures that everything remains in-sync and valid.

Link to this function address_literal(mail_address) View Source
address_literal(MailAddress.t()) :: String.t()

Returns the decoded address literal domain (if any), or nil otherwise.

Examples

iex> MailAddress.address_literal(%MailAddress{})
nil

iex> {:ok, addr} = MailAddress.new("test", "[192.168.0.1]", %MailAddress.Options{allow_address_literal: true})
iex> MailAddress.address_literal(addr)
{192, 168, 0, 1}
Link to this function address_literal?(mail_address) View Source
address_literal?(MailAddress.t()) :: boolean()

Checks whether address has an address literal domain part.

Examples

iex> MailAddress.address_literal?(%MailAddress{})
false

iex> {:ok, addr} = MailAddress.new("test", "[192.168.0.1]", %MailAddress.Options{allow_address_literal: true})
iex> MailAddress.address_literal?(addr)
true

Applies checks and optional domain downcasing to given address using passed options.

This function is automatically called as required by other functions in the package, so doesn’t normally need to be called unless you are messing with the MailAddress struct directly (which isn’t a good idea).

If successful, returns {:ok, new_address}, otherwise returns {:error, error_message}.

Returns the domain part of the address.

Examples

iex> MailAddress.domain(%MailAddress{})
""

iex> {:ok, addr} = MailAddress.new("test", "example.org")
iex> MailAddress.domain(addr)
"example.org"
Link to this function domain?(mail_address) View Source
domain?(MailAddress.t()) :: boolean()

Checks whether address has a domain part.

Examples

iex> MailAddress.domain?(%MailAddress{})
false

iex> {:ok, addr} = MailAddress.new("test", "example.org")
iex> MailAddress.domain?(addr)
true
Link to this function domains_equal?(addr, domain) View Source
domains_equal?(MailAddress.t(), String.t() | MailAddress.t()) :: boolean()

Compares domain of given address with domain (case-insensitively). Returns true if the domains are the same, or false otherwise.

Examples

iex> {:ok, addr_1} = MailAddress.new("test", "example.org")
iex> {:ok, addr_2} = MailAddress.new("another", "example.org")
iex> {:ok, addr_3} = MailAddress.new("test", "localhost", %MailAddress.Options{allow_localhost: true})
iex> MailAddress.domains_equal?(addr_1, "example.org")
true
iex> MailAddress.domains_equal?(addr_2, "EXAMPLE.ORG")
true
iex> MailAddress.domains_equal?(addr_1, "something_else")
false
iex> MailAddress.domains_equal?(addr_1, addr_2)
true
iex> MailAddress.domains_equal?(addr_1, %MailAddress{})
false
iex> MailAddress.domains_equal?(addr_3, "localhost")
true
iex> MailAddress.domains_equal?(addr_3, "[127.0.0.1]")
true
iex> MailAddress.domains_equal?(addr_3, "[IPv6:::1]")
true
Link to this function encode(addr, bracket \\ true) View Source
encode(MailAddress.t(), boolean()) :: String.t()

Returns address safely encoded, optionally (and by default) bracketed.

Examples

iex> MailAddress.encode(%MailAddress{}, false)
""

iex> MailAddress.encode(%MailAddress{}, true)
"<>"

iex> {:ok, addr, ""} = MailAddress.Parser.parse("test@example.org")
iex> MailAddress.encode(addr, true)
"<test@example.org>"

iex> {:ok, addr, ""} = MailAddress.Parser.parse("\"@test\"@example.org")
iex> MailAddress.encode(addr, true)
"<\"\\@test\"@example.org>"

Checks whether addr_1 and addr_2 are the same. The local parts are compared case sensitively, whilst the domain parts are compare case insensitively.

Examples

iex> {:ok, addr_1} = MailAddress.new("test", "example.org")
iex> {:ok, addr_2} = MailAddress.new("test", "ExAmPlE.ORG")
iex> MailAddress.equal?(addr_1, addr_2)
true
iex> {:ok, addr_3} = MailAddress.new("fred", "ExAmPlE.ORG")
iex> MailAddress.equal?(addr_1, addr_3)
false
Link to this function local_part(mail_address) View Source
local_part(MailAddress.t()) :: String.t()

Returns the local part of the address.

Examples

iex> {:ok, addr} = MailAddress.new("test", "example.org")
iex> MailAddress.local_part(addr)
"test"
Link to this function local_part?(mail_address) View Source
local_part?(MailAddress.t()) :: boolean()

Checks whether address has local part set.

Examples

iex> MailAddress.local_part?(%MailAddress{})
false

iex> {:ok, addr} = MailAddress.new("test", "example.org")
iex> MailAddress.local_part?(addr)
true
Link to this function local_parts_equal?(arg1, local_part) View Source
local_parts_equal?(MailAddress.t(), MailAddress.t()) :: boolean()

Compares address local parts (case-sensitively). The second parameter may be either a string or a MailAddress struct. Returns true if the local parts are the same, or false otherwise.

Examples

iex> {:ok, addr_1} = MailAddress.new("test", "example.org")
iex> {:ok, addr_2} = MailAddress.new("test", "example.com")
iex> MailAddress.local_parts_equal?(addr_1, addr_2)
true
iex> MailAddress.local_parts_equal?(addr_1, "test")
true
iex> MailAddress.local_parts_equal?(addr_2, "TEST")
false
Link to this function localhost?(mail_address) View Source
localhost?(MailAddress.t()) :: boolean()

Checks whether domain part of address is ‘localhost’, or the domain is an address literal and is [127.0.0.1] or [IPv6:::1].

Examples

iex> {:ok, addr_1} = MailAddress.new("test", "example.org")
iex> MailAddress.localhost?(addr_1)
false

iex> {:ok, addr_2} = MailAddress.new("test", "localhost", %MailAddress.Options{allow_localhost: true})
iex> MailAddress.localhost?(addr_2)
true

iex> {:ok, addr_3} = MailAddress.new("test", "[127.0.0.1]", %MailAddress.Options{allow_address_literal: true, allow_localhost: true})
iex> MailAddress.localhost?(addr_3)
true

iex> {:ok, addr_4} = MailAddress.new("test", "[192.168.0.1]", %MailAddress.Options{allow_address_literal: true, allow_localhost: true})
iex> MailAddress.localhost?(addr_4)
false

iex> {:ok, addr_5} = MailAddress.new("test", "[IPv6:::1]", %MailAddress.Options{allow_address_literal: true, allow_localhost: true})
iex> MailAddress.localhost?(addr_5)
true
Link to this function localhost_string?(str) View Source
localhost_string?(String.t()) :: boolean()

Checks to see if the given string is “localhost” or equivalent ([127.0.0.1] or [IPv6:::1]).

Examples:

iex> MailAddress.localhost_string?("test")
false

iex> MailAddress.localhost_string?("LOCALHOST")
true

iex> MailAddress.localhost_string?("[127.0.0.1]")
true

iex> MailAddress.localhost_string?("[127.0.0.1")
false

iex> MailAddress.localhost_string?("[192.168.0.1]")
false

iex> MailAddress.localhost_string?("[IPv6:::1]")
true
Link to this function needs_quoting?(mail_address) View Source
needs_quoting?(MailAddress.t()) :: boolean()

Checks whether the local part of the given address needs quoting.

The needs_quoting flag on the address is updated when the address is changed, so calling this function is inexpensive.

Link to this function new(local, domain, options \\ %MailAddress.Options{}) View Source

Creates a new MailAddress setting both local and domain parts at the same time using the provided (or default) options.

NOTE: the local part isn’t parsed - it is just checked to ensure that it only contains valid characters. This means that the local part should be raw rather than quoted form.

Returns either {:ok, new_address} or {:error, error_reason}.

Examples

iex> {:ok, addr} = MailAddress.new("test", "example.org")
iex> addr
#MailAddress<test@example.org>

iex> {:ok, addr} = MailAddress.new("@test", "example.org")
iex> addr
#MailAddress<"\@test"@example.org>

iex> MailAddress.new("test", "example.org!")
{:error, "invalid domain"}

Checks whether the address in null (no local part and no domain).

Examples

iex> MailAddress.null?(%MailAddress{})
true

iex> {:ok, addr} = MailAddress.new("test", "example.org")
iex> MailAddress.null?(addr)
false

iex> {:ok, addr} = MailAddress.new("", "", %MailAddress.Options{allow_null: true})
iex> MailAddress.null?(addr)
true
Link to this function set_domain(addr, domain, options \\ %MailAddress.Options{}) View Source
set_domain(MailAddress.t(), String.t(), MailAddress.Options.t()) ::
  {:ok, MailAddress.t()} | error()

Sets the domain part of the address using the provided (or default) options.

Returns either {:ok, new_address} or {:error, error_reason}.

Examples

iex> {:ok, addr} = MailAddress.set_domain(%MailAddress{}, "test", %MailAddress.Options{allow_null_local_part: true})
iex> MailAddress.domain(addr)
"test"

iex> {:ok, addr} = MailAddress.new("test", "example.com")
iex> MailAddress.domain(addr)
"example.com"
iex> {:ok, addr} = MailAddress.set_domain(addr, "example.org")
iex> MailAddress.domain(addr)
"example.org"
Link to this function set_local_part(addr, local, options \\ %MailAddress.Options{}) View Source
set_local_part(MailAddress.t(), String.t(), MailAddress.Options.t()) ::
  {:ok, MailAddress.t()} | error()

Sets the local part of the address using the provided (or default) options.

NOTE: the local part isn’t parsed - it is just checked to ensure that it only contains valid characters, consequently it should be in raw unquoted format.

Returns either {:ok, new_address} or {:error, error_reason}.

Examples

iex> {:ok, addr} = MailAddress.set_local_part(%MailAddress{}, "test", %MailAddress.Options{require_domain: false})
iex> MailAddress.local_part(addr)
"test"

iex> MailAddress.set_domain(%MailAddress{}, "test", %MailAddress.Options{allow_null_local_part: false})
{:error, "local part can't be null"}

iex> {:ok, addr} = MailAddress.new("test", "example.org")
iex> MailAddress.local_part(addr)
"test"
iex> {:ok, addr} = MailAddress.set_local_part(addr, "other")
iex> MailAddress.local_part(addr)
"other"