Packages
swoosh
1.16.7
1.26.3
1.26.2
1.26.1
1.26.0
1.25.3
1.25.2
1.25.1
1.25.0
1.24.0
1.23.1
1.23.0
1.22.1
1.22.0
1.21.0
1.20.1
1.20.0
1.19.9
1.19.8
1.19.7
1.19.6
1.19.5
1.19.4
1.19.3
1.19.2
1.19.1
1.19.0
1.18.4
1.18.3
1.18.2
1.18.1
1.18.0
1.17.10
1.17.9
1.17.8
retired
1.17.7
retired
1.17.6
1.17.5
1.17.4
retired
1.17.3
1.17.2
1.17.1
1.17.0
1.16.12
1.16.11
1.16.10
1.16.9
1.16.8
1.16.7
1.16.6
1.16.5
1.16.4
1.16.3
1.16.2
retired
1.16.1
retired
1.16.0
1.15.3
1.15.2
1.15.1
1.15.0
1.14.4
1.14.3
1.14.2
1.14.1
1.14.0
1.13.0
1.12.0
1.11.6
1.11.5
1.11.4
1.11.3
1.11.2
1.11.1
retired
1.11.0
1.10.3
1.10.2
1.10.1
1.10.0
1.9.1
1.9.0
1.8.3
1.8.2
1.8.1
1.8.0
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.2
1.5.1
1.5.0
1.4.0
1.3.11
1.3.10
1.3.9
1.3.8
retired
1.3.7
1.3.6
retired
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.2
1.2.1
1.2.0
1.1.2
1.1.1
1.1.0
1.0.9
1.0.8
retired
1.0.7
retired
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.25.6
0.25.5
0.25.4
0.25.3
retired
0.25.2
0.25.1
0.25.0
0.24.4
0.24.3
0.24.2
0.24.1
0.24.0
0.23.5
0.23.4
0.23.3
0.23.2
0.23.1
0.23.0
0.22.2
0.22.1
0.22.0
0.21.1
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.1
0.16.0
0.15.0
0.14.0
0.13.0
0.12.1
0.12.0
0.11.0
0.10.0
0.9.1
0.9.0
0.8.1
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
Compose, deliver and test your emails easily in Elixir. Supports SMTP, Sendgrid, Mandrill, Postmark, Mailgun and many more out of the box. Preview your emails in the browser. Test your email sending code.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/swoosh/adapters/mua.ex
defmodule Swoosh.Adapters.Mua do
@moduledoc """
An adapter that sends email using the SMTP protocol.
> ### Dependency {: .info}
>
> Underneath this adapter uses [Mua,](https://github.com/ruslandoga/mua) and
> [Mail](https://github.com/DockYard/elixir-mail), and
> [Castore](https://github.com/elixir-mint/castore) libraries,
> add them to your mix.exs file.
## Example
# mix.exs
def deps do
[
{:swoosh, "~> 1.3"},
{:mua, "~> 0.1.0"},
{:mail, "~> 0.3.0"},
{:castore, "~> 1.0"}
]
end
# config/config.exs for sending email directly
config :sample, Sample.Mailer,
adapter: Swoosh.Adapters.Mua
# config/config.exs for sending email via a relay
config :sample, Sample.Mailer,
adapter: Swoosh.Adapters.Mua,
relay: "smtp.matrix.com",
port: 1025,
auth: [username: "neo", password: "one"]
# lib/sample/mailer.ex
defmodule Sample.Mailer do
use Swoosh.Mailer, otp_app: :sample
end
For supported configuration options, please see [`option()`](#t:option/0)
## Sending email directly
When `relay` option is omitted, this adapter will send email directly to
the receivers' host. All receivers must be on the same host, otherwise
`Swoosh.Adapters.Mua.MultihostError` is raised.
In this configuration, you need to ensure that your application can make
outgoing connections to port 25 and that your sender domain has appropriate
DNS records set, e.g. SPF or DKIM.
> #### Short-lived connections {: .warning}
>
> Note that each `deliver` call results in a new connection to the receiver's
> email server.
## Sending email via a relay
When `relay` option is set, this adapter will send email through that relay.
The relay would usually require authentication. For example, you can use your own
GMail account with an app password.
> #### Short-lived connections {: .warning}
>
> Note that each `deliver` call results in a new connection to the relay.
> This is less efficient than `gen_smtp` which reuses the long-lived connection.
> Further versions of this adapter might fix this if it ends up being a big problem ;)
## CA certificates
By default [`CAStore.file_path/0`](https://hexdocs.pm/castore/CAStore.html#file_path/0) is used for
[`:cacertfile`,](https://www.erlang.org/doc/man/ssl#type-client_cafile) but you can provide your
own or use [the system ones](https://www.erlang.org/doc/man/public_key#cacerts_get-0) and supply them
in as [`:cacerts`](https://www.erlang.org/doc/man/ssl#type-client_cacerts)
:ok = :public_key.cacerts_load()
[_ | _] = cacerts = :public_key.cacerts_get()
config :sample, Sample.Mailer,
adapter: Swoosh.Adapters.Mua,
transport_opts: [
cacerts: cacerts
]
Note that when using `:cacertfile` option, the certificates are decoded on each new connection.
To cache the decoded certificates, set `:persistent_term` for `:mua` to true.
config :mua, persistent_term: true
"""
use Swoosh.Adapter, required_deps: [mail: Mail, mua: Mua]
defmodule MultihostError do
@moduledoc """
Raised when no relay is used and recipients contain addresses across multiple hosts.
For example:
email =
Swoosh.Email.new(
to: {"Mua", "mua@github.com"},
cc: [{"Swoosh", "mua@swoosh.github.com"}]
)
Swoosh.Adapters.Mua.deliver(email, _no_relay_config = [])
Fields:
- `:hosts` - the hosts for the recipients, `["github.com", "swoosh.github.com"]` in the example above
"""
@type t :: %__MODULE__{hosts: [Mua.host()]}
defexception [:hosts]
def message(%__MODULE__{hosts: hosts}) do
"expected all recipients to be on the same host, got: " <> Enum.join(hosts, ", ")
end
end
@type option :: Mua.option() | {:relay, Mua.host()}
@spec deliver(Swoosh.Email.t(), [option]) ::
{:ok, Swoosh.Email.t()} | {:error, Mua.error() | MultihostError.t()}
def deliver(email, config) do
recipients = recipients(email)
recipients_by_host =
if relay = Keyword.get(config, :relay) do
[{relay, recipients}]
else
recipients
|> Enum.group_by(&recipient_host/1)
|> Map.to_list()
end
case recipients_by_host do
[{host, recipients}] ->
sender = address(email.from)
message = render(email)
with {:ok, _receipt} <- Mua.easy_send(host, sender, recipients, message, config) do
{:ok, email}
end
[_ | _] = multihost ->
{:error, MultihostError.exception(hosts: :proplists.get_keys(multihost))}
end
end
defp address({_, address}) when is_binary(address), do: address
defp address(address) when is_binary(address), do: address
defp recipient_host(address) do
[_username, host] = String.split(address, "@")
host
end
defp recipients(%Swoosh.Email{to: to, cc: cc, bcc: bcc}) do
(List.wrap(to) ++ List.wrap(cc) ++ List.wrap(bcc))
|> Enum.map(&address/1)
|> Enum.uniq()
end
defp render(email) do
Mail.build_multipart()
|> maybe(&Mail.put_from/2, email.from)
|> maybe(&Mail.put_to/2, email.to)
|> maybe(&Mail.put_cc/2, email.cc)
|> maybe(&Mail.put_bcc/2, email.bcc)
|> maybe(&Mail.put_subject/2, email.subject)
|> maybe(&Mail.put_text/2, email.text_body)
|> maybe(&Mail.put_html/2, email.html_body)
|> maybe(&put_headers/2, email.headers)
|> maybe(&put_attachments/2, email.attachments)
|> Mail.render()
end
defp maybe(mail, _fun, empty) when empty in [nil, [], %{}], do: mail
defp maybe(mail, fun, value), do: fun.(mail, value)
defp put_attachments(mail, attachments) do
Enum.reduce(attachments, mail, fn attachment, mail ->
%Swoosh.Attachment{filename: filename, content_type: content_type} = attachment
data = Swoosh.Attachment.get_content(attachment)
Mail.put_attachment(mail, {filename, data}, headers: [content_type: content_type])
end)
end
defp put_headers(mail, headers) do
Enum.reduce(headers, mail, fn {key, value}, mail ->
Mail.Message.put_header(mail, key, value)
end)
end
end