forked from migadu/mailex
1
0
Fork 0

initial commit

This commit is contained in:
Dejan Strbac 2015-11-25 17:10:43 +01:00
commit a7a13da6c3
16 changed files with 1372 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/_build
/cover
/deps
erl_crash.dump
*.ez

54
README.md Normal file
View File

@ -0,0 +1,54 @@
# Mailex
Simple wrapper around gen_smtp for sending emails.
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed as:
1. Add mailex to your list of dependencies in `mix.exs`:
def deps do
[{:mailex, "~> 0.0.1"}]
end
2. Ensure mailex is started before your application:
def application do
[applications: [:mailex]]
end
To create an email:
email = %Mailex.Email{
from: %Mailex.Address{name: "Dejan Strbac", address: "me@dejanstrbac.com"},
to: [%Mailex.Address{name: "Dejo", address: "dejan.strbac@gmail.com"}],
subject: "Hi there",
text: "Hello World",
attachments: [Mailex.Attachment.inline!("test/data/logo.gif")]
}
To render it:
Mailex.Render.render(email)
To dump emails to console, just deliver without config:
Mailex.deliver(email)
To use a smtp server, provide settings:
Mailex.deliver(email, [
relay: "smtp.migadu.com",
username: "USERNAME",
password: "PASSWORD",
port: 587,
tls: :always
ssl: true,
auth: :always
])

30
config/config.exs Normal file
View File

@ -0,0 +1,30 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config
# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
# if you want to provide default values for your application for
# 3rd-party users, it should be done in your "mix.exs" file.
# You can configure for your application as:
#
# config :mailex, key: :value
#
# And access this configuration in your application as:
#
# Application.get_env(:mailex, :key)
#
# Or configure a 3rd-party app:
#
# config :logger, level: :info
#
# It is also possible to import configuration files, relative to this
# directory. For example, you can emulate configuration per environment
# by uncommenting the line below and defining dev.exs, test.exs and such.
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
# import_config "#{Mix.env}.exs"

348
dejan.eml Normal file
View File

@ -0,0 +1,348 @@
From: Dejan Strbac <dejan@migadu.com>
To: Dejan <dejan@strbac.com>, Dex <dejan@migadu.com>
Subject:
Cc: Dejan <dejan@strbac.com>, Dex <dejan@migadu.com>
Bcc: Dejan <dejan@strbac.com>, Dex <dejan@migadu.com>
Reply-To: Dejan <dejan@strbac.com>, Dex <dejan@migadu.com>
Content-Type: multipart/mixed;
boundary="_=4d394y4p3v1f036r2w0g3b6r4m43364a=_"
MIME-Version: 1.0
Date: Wed, 25 Nov 2015 12:59:06 +0100
Message-ID: <e50255c45c68c4d19a12ac539aea1c7c@dejans-mbp>
--_=4d394y4p3v1f036r2w0g3b6r4m43364a=_
Content-Type: image/gif
Content-Disposition: attachment;
filename=logo.gif
Content-Transfer-Encoding: base64
R0lGODlhAQFYAPf2AD+e1gAlPYiZpLvFyx+Oz0Rfce7w8hE0SpmosczT2DNRZGZ8iyJCV93i5XeL
mFVufqq2vhSIzBGHzKPQ61Gn2T6d1SCOzzyc1RCGyxKHzBOIzEyk2E+m2fz9/haJzbjb8BWJzT2d
1SqT0Uii2CiS0fj7/UGf1jSY0yGPz4C/5PX6/ZzN6lCm2SaR0ECe1q/X7v3+/1So2jqb1drs90ah
1xeKzcPh8hqLziSQ0Pr8/mSx3iWR0CeS0C6V0huMzqnT7E2l2RmLzTaZ1IbC5VKn2kOg1lOo2ozF
5jCW0iKPzx2Nzjia1Faq2/n8/nm74lmr21qs216u3CmT0U6l2aDP62ay3uDv+Lba74rE5nG34J7O
6rTZ74jD5Y/G50Wh13O44ZrM6Vyt3C2V0lir2yOQ0DGX0/f7/YG/5LHY7vH4/ByMzhiKzXy94x6N
zmq0336+47LY7p3N6o7G5zKX09Tp9jWZ0zea1Nns95HH52y13+n0+sbi8+r0+uby+iuU0crk9H29
483m9KTR68vl9GWx3kei18/n9WGv3fL4/KjT7IvE5pTJ6JfK6Wu03yyU0Umj2Ha64S+W0r7e8Uuk
2Eqj2PP5/ILA5EKf1qzV7XC34Oz1+7nc8KrU7fv9/nW54Veq2zOY0+/3/OPx+eTx+eLw+VWp2sDf
8Xu842iz3t3u+GOw3cnk8+jz+tXq9oXB5Tuc1c7m9JXJ6NDn9YnD5tHo9USg13i74jmb1I3F5qfS
7Fus3LXa74PA5HK44YfC5Xq84vb6/WKw3ZnL6XS54b/f8Xe64l+u3Lfb72ey3sTh8vT5/Wmz32Cv
3aHP6+v1++fz+r3e8ZDH57zd8dfr9sLg8szl9G624KLQ66XR7JbK6N/v+LDX7oTB5O72+5vM6dbq
9u32+6bS7JPI6JLI5+Xy+W214LPZ797u+Nzt97vd8KvV7brc8PD3/JjL6Z/O6sjj82+24MHg8q7W
7l2t3H++4w+Gy////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEA
AAAh+QQFMgD2ACwAAAAAAQFYAAAI/wDtCRxIsKDBgwgTKlzIsKHDhxAjSpxIsaLFixgzatzIsaPH
jyBDihxJsqTJkyhTqlzJsqXLlzBjypxJs6bNmzhz6tzJs6fPn0CDCh1KtKjRo0iTKl3KtKnTp1Cj
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
--_=4d394y4p3v1f036r2w0g3b6r4m43364a=_
Content-Type: text/plain;
charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
Hello World
--_=4d394y4p3v1f036r2w0g3b6r4m43364a=_--

33
lib/mailex.ex Normal file
View File

@ -0,0 +1,33 @@
defmodule Mailex do
@config_defaults [
relay: nil,
username: "",
password: "",
port: 25,
ssl: false,
tls: :never,
auth: :always
]
def deliver(email, config \\ []) do
config = Keyword.merge(@config_defaults, config)
message = email |> Mailex.Render.render
from = email.from |> Mailex.Address.envelope_format
to = email.to |> Mailex.Address.envelope_format
if Keyword.get(config, :relay) do
envelope = { from, to, message }
case :gen_smtp_client.send_blocking(envelope, config) do
{ :error, msg } -> { :error, msg }
msg -> { :ok, msg }
end
else
IO.puts "\n\n[[[ Mailex ]]]\n\nFROM: #{from}\nTO: #{to}\n"
IO.puts "RAW START -------\n#{message}\nRAW END -------"
{:ok, "dumped to console"}
end
end
end

32
lib/mailex/address.ex Normal file
View File

@ -0,0 +1,32 @@
defmodule Mailex.Address do
defstruct name: nil, address: nil
def rfc_822_format(emails) when is_list(emails), do:
emails |> Enum.map(&rfc_822_format(&1))
def rfc_822_format(email) when is_map(email) do
if email.name do
"#{email.name} <#{email.address}>"
else
name = email.address |>
String.split("@") |>
List.first |>
String.split(~r/([^\w\s]|_)/) |>
Enum.map(&String.capitalize/1) |>
Enum.join " "
"#{name} <#{email.address}>"
end
end
def envelope_format(emails) when is_list(emails), do:
emails |> Enum.map(&envelope_format(&1))
def envelope_format(email) when is_map(email), do:
"<#{email.address}>"
end

694
lib/mailex/attachment.ex Normal file
View File

@ -0,0 +1,694 @@
defmodule Mailex.Attachment do
defstruct filename: nil, type: nil, data: nil
@mime_types [
{ ".3dm", "x-world", "x-3dmf" },
{ ".3dmf", "x-world", "x-3dmf" },
{ ".a", "application", "octet-stream" },
{ ".aab", "application", "x-authorware-bin" },
{ ".aam", "application", "x-authorware-map" },
{ ".aas", "application", "x-authorware-seg" },
{ ".abc", "text", "vnd.abc" },
{ ".acgi", "text", "html" },
{ ".afl", "video", "animaflex" },
{ ".ai", "application", "postscript" },
{ ".aif", "audio", "aiff" },
{ ".aif", "audio", "x-aiff" },
{ ".aifc", "audio", "aiff" },
{ ".aifc", "audio", "x-aiff" },
{ ".aiff", "audio", "aiff" },
{ ".aiff", "audio", "x-aiff" },
{ ".aim", "application", "x-aim" },
{ ".aip", "text", "x-audiosoft-intra" },
{ ".ani", "application", "x-navi-animation" },
{ ".aos", "application", "x-nokia-9000-communicator-add-on-software" },
{ ".aps", "application", "mime" },
{ ".arc", "application", "octet-stream" },
{ ".arj", "application", "arj" },
{ ".arj", "application", "octet-stream" },
{ ".art", "image", "x-jg" },
{ ".asf", "video", "x-ms-asf" },
{ ".asm", "text", "x-asm" },
{ ".asp", "text", "asp" },
{ ".asx", "application", "x-mplayer2" },
{ ".asx", "video", "x-ms-asf" },
{ ".asx", "video", "x-ms-asf-plugin" },
{ ".au", "audio", "basic" },
{ ".au", "audio", "x-au" },
{ ".avi", "application", "x-troff-msvideo" },
{ ".avi", "video", "avi" },
{ ".avi", "video", "msvideo" },
{ ".avi", "video", "x-msvideo" },
{ ".avs", "video", "avs-video" },
{ ".bcpio", "application", "x-bcpio" },
{ ".bin", "application", "mac-binary" },
{ ".bin", "application", "macbinary" },
{ ".bin", "application", "octet-stream" },
{ ".bin", "application", "x-binary" },
{ ".bin", "application", "x-macbinary" },
{ ".bm", "image", "bmp" },
{ ".bmp", "image", "bmp" },
{ ".bmp", "image", "x-windows-bmp" },
{ ".boo", "application", "book" },
{ ".book", "application", "book" },
{ ".boz", "application", "x-bzip2" },
{ ".bsh", "application", "x-bsh" },
{ ".bz", "application", "x-bzip" },
{ ".bz2", "application", "x-bzip2" },
{ ".c", "text", "plain" },
{ ".c", "text", "x-c" },
{ ".c++", "text", "plain" },
{ ".cat", "application", "vnd.ms-pki.seccat" },
{ ".cc", "text", "plain" },
{ ".cc", "text", "x-c" },
{ ".ccad", "application", "clariscad" },
{ ".cco", "application", "x-cocoa" },
{ ".cdf", "application", "cdf" },
{ ".cdf", "application", "x-cdf" },
{ ".cdf", "application", "x-netcdf" },
{ ".cer", "application", "pkix-cert" },
{ ".cer", "application", "x-x509-ca-cert" },
{ ".cha", "application", "x-chat" },
{ ".chat", "application", "x-chat" },
{ ".class", "application", "java" },
{ ".class", "application", "java-byte-code" },
{ ".class", "application", "x-java-class" },
{ ".com", "application", "octet-stream" },
{ ".com", "text", "plain" },
{ ".conf", "text", "plain" },
{ ".cpio", "application", "x-cpio" },
{ ".cpp", "text", "x-c" },
{ ".cpt", "application", "mac-compactpro" },
{ ".cpt", "application", "x-compactpro" },
{ ".cpt", "application", "x-cpt" },
{ ".crl", "application", "pkcs-crl" },
{ ".crl", "application", "pkix-crl" },
{ ".crt", "application", "pkix-cert" },
{ ".crt", "application", "x-x509-ca-cert" },
{ ".crt", "application", "x-x509-user-cert" },
{ ".csh", "application", "x-csh" },
{ ".csh", "text", "x-script.csh" },
{ ".css", "application", "x-pointplus" },
{ ".css", "text", "css" },
{ ".cxx", "text", "plain" },
{ ".dcr", "application", "x-director" },
{ ".deepv", "application", "x-deepv" },
{ ".def", "text", "plain" },
{ ".der", "application", "x-x509-ca-cert" },
{ ".dif", "video", "x-dv" },
{ ".dir", "application", "x-director" },
{ ".dl", "video", "dl" },
{ ".dl", "video", "x-dl" },
{ ".doc", "application", "msword" },
{ ".dot", "application", "msword" },
{ ".dp", "application", "commonground" },
{ ".drw", "application", "drafting" },
{ ".dump", "application", "octet-stream" },
{ ".dv", "video", "x-dv" },
{ ".dvi", "application", "x-dvi" },
{ ".dwf", "drawing", "x-dwf, (old)" },
{ ".dwf", "model", "vnd.dwf" },
{ ".dwg", "application", "acad" },
{ ".dwg", "image", "vnd.dwg" },
{ ".dwg", "image", "x-dwg" },
{ ".dxf", "application", "dxf" },
{ ".dxf", "image", "vnd.dwg" },
{ ".dxf", "image", "x-dwg" },
{ ".dxr", "application", "x-director" },
{ ".el", "text", "x-script.elisp" },
{ ".elc", "application", "x-bytecode.elisp, (compiled, elisp)" },
{ ".elc", "application", "x-elc" },
{ ".env", "application", "x-envoy" },
{ ".eps", "application", "postscript" },
{ ".es", "application", "x-esrehber" },
{ ".etx", "text", "x-setext" },
{ ".evy", "application", "envoy" },
{ ".evy", "application", "x-envoy" },
{ ".exe", "application", "octet-stream" },
{ ".f", "text", "plain" },
{ ".f", "text", "x-fortran" },
{ ".f77", "text", "x-fortran" },
{ ".f90", "text", "plain" },
{ ".f90", "text", "x-fortran" },
{ ".fdf", "application", "vnd.fdf" },
{ ".fif", "application", "fractals" },
{ ".fif", "image", "fif" },
{ ".fli", "video", "fli" },
{ ".fli", "video", "x-fli" },
{ ".flo", "image", "florian" },
{ ".flx", "text", "vnd.fmi.flexstor" },
{ ".fmf", "video", "x-atomic3d-feature" },
{ ".for", "text", "plain" },
{ ".for", "text", "x-fortran" },
{ ".fpx", "image", "vnd.fpx" },
{ ".fpx", "image", "vnd.net-fpx" },
{ ".frl", "application", "freeloader" },
{ ".funk", "audio", "make" },
{ ".g", "text", "plain" },
{ ".g3", "image", "g3fax" },
{ ".gif", "image", "gif" },
{ ".gl", "video", "gl" },
{ ".gl", "video", "x-gl" },
{ ".gsd", "audio", "x-gsm" },
{ ".gsm", "audio", "x-gsm" },
{ ".gsp", "application", "x-gsp" },
{ ".gss", "application", "x-gss" },
{ ".gtar", "application", "x-gtar" },
{ ".gz", "application", "x-compressed" },
{ ".gz", "application", "x-gzip" },
{ ".gzip", "application", "x-gzip" },
{ ".gzip", "multipart", "x-gzip" },
{ ".h", "text", "plain" },
{ ".h", "text", "x-h" },
{ ".hdf", "application", "x-hdf" },
{ ".help", "application", "x-helpfile" },
{ ".hgl", "application", "vnd.hp-hpgl" },
{ ".hh", "text", "plain" },
{ ".hh", "text", "x-h" },
{ ".hlb", "text", "x-script" },
{ ".hlp", "application", "hlp" },
{ ".hlp", "application", "x-helpfile" },
{ ".hlp", "application", "x-winhelp" },
{ ".hpg", "application", "vnd.hp-hpgl" },
{ ".hpgl", "application", "vnd.hp-hpgl" },
{ ".hqx", "application", "binhex" },
{ ".hqx", "application", "binhex4" },
{ ".hqx", "application", "mac-binhex" },
{ ".hqx", "application", "mac-binhex40" },
{ ".hqx", "application", "x-binhex40" },
{ ".hqx", "application", "x-mac-binhex40" },
{ ".hta", "application", "hta" },
{ ".htc", "text", "x-component" },
{ ".htm", "text", "html" },
{ ".html", "text", "html" },
{ ".htmls", "text", "html" },
{ ".htt", "text", "webviewhtml" },
{ ".htx", "text", "html" },
{ ".ice", "x-conference", "x-cooltalk" },
{ ".ico", "image", "x-icon" },
{ ".idc", "text", "plain" },
{ ".ief", "image", "ief" },
{ ".iefs", "image", "ief" },
{ ".iges", "application", "iges" },
{ ".iges", "model", "iges" },
{ ".igs", "application", "iges" },
{ ".igs", "model", "iges" },
{ ".ima", "application", "x-ima" },
{ ".imap", "application", "x-httpd-imap" },
{ ".inf", "application", "inf" },
{ ".ins", "application", "x-internett-signup" },
{ ".ip", "application", "x-ip2" },
{ ".isu", "video", "x-isvideo" },
{ ".it", "audio", "it" },
{ ".iv", "application", "x-inventor" },
{ ".ivr", "i-world", "i-vrml" },
{ ".ivy", "application", "x-livescreen" },
{ ".jam", "audio", "x-jam" },
{ ".jav", "text", "plain" },
{ ".jav", "text", "x-java-source" },
{ ".java", "text", "plain" },
{ ".java", "text", "x-java-source" },
{ ".jcm", "application", "x-java-commerce" },
{ ".jfif", "image", "jpeg" },
{ ".jfif", "image", "pjpeg" },
{ ".jfif-tbnl", "image", "jpeg" },
{ ".jpe", "image", "jpeg" },
{ ".jpe", "image", "pjpeg" },
{ ".jpeg", "image", "jpeg" },
{ ".jpeg", "image", "pjpeg" },
{ ".jpg", "image", "jpeg" },
{ ".jpg", "image", "pjpeg" },
{ ".jps", "image", "x-jps" },
{ ".js", "application", "x-javascript" },
{ ".js", "application", "javascript" },
{ ".js", "application", "ecmascript" },
{ ".js", "text", "javascript" },
{ ".js", "text", "ecmascript" },
{ ".jut", "image", "jutvision" },
{ ".kar", "audio", "midi" },
{ ".kar", "music", "x-karaoke" },
{ ".ksh", "application", "x-ksh" },
{ ".ksh", "text", "x-script.ksh" },
{ ".la", "audio", "nspaudio" },
{ ".la", "audio", "x-nspaudio" },
{ ".lam", "audio", "x-liveaudio" },
{ ".latex", "application", "x-latex" },
{ ".lha", "application", "lha" },
{ ".lha", "application", "octet-stream" },
{ ".lha", "application", "x-lha" },
{ ".lhx", "application", "octet-stream" },
{ ".list", "text", "plain" },
{ ".lma", "audio", "nspaudio" },
{ ".lma", "audio", "x-nspaudio" },
{ ".log", "text", "plain" },
{ ".lsp", "application", "x-lisp" },
{ ".lsp", "text", "x-script.lisp" },
{ ".lst", "text", "plain" },
{ ".lsx", "text", "x-la-asf" },
{ ".ltx", "application", "x-latex" },
{ ".lzh", "application", "octet-stream" },
{ ".lzh", "application", "x-lzh" },
{ ".lzx", "application", "lzx" },
{ ".lzx", "application", "octet-stream" },
{ ".lzx", "application", "x-lzx" },
{ ".m", "text", "plain" },
{ ".m", "text", "x-m" },
{ ".m1v", "video", "mpeg" },
{ ".m2a", "audio", "mpeg" },
{ ".m2v", "video", "mpeg" },
{ ".m3u", "audio", "x-mpequrl" },
{ ".man", "application", "x-troff-man" },
{ ".map", "application", "x-navimap" },
{ ".mar", "text", "plain" },
{ ".mbd", "application", "mbedlet" },
{ ".mc$", "application", "x-magic-cap-package-1.0" },
{ ".mcd", "application", "mcad" },
{ ".mcd", "application", "x-mathcad" },
{ ".mcf", "image", "vasa" },
{ ".mcf", "text", "mcf" },
{ ".mcp", "application", "netmc" },
{ ".me", "application", "x-troff-me" },
{ ".mht", "message", "rfc822" },
{ ".mhtml", "message", "rfc822" },
{ ".mid", "application", "x-midi" },
{ ".mid", "audio", "midi" },
{ ".mid", "audio", "x-mid" },
{ ".mid", "audio", "x-midi" },
{ ".mid", "music", "crescendo" },
{ ".mid", "x-music", "x-midi" },
{ ".midi", "application", "x-midi" },
{ ".midi", "audio", "midi" },
{ ".midi", "audio", "x-mid" },
{ ".midi", "audio", "x-midi" },
{ ".midi", "music", "crescendo" },
{ ".midi", "x-music", "x-midi" },
{ ".mif", "application", "x-frame" },
{ ".mif", "application", "x-mif" },
{ ".mime", "message", "rfc822" },
{ ".mime", "www", "mime" },
{ ".mjf", "audio", "x-vnd.audioexplosion.mjuicemediafile" },
{ ".mjpg", "video", "x-motion-jpeg" },
{ ".mm", "application", "base64" },
{ ".mm", "application", "x-meme" },
{ ".mme", "application", "base64" },
{ ".mod", "audio", "mod" },
{ ".mod", "audio", "x-mod" },
{ ".moov", "video", "quicktime" },
{ ".mov", "video", "quicktime" },
{ ".movie", "video", "x-sgi-movie" },
{ ".mobileconfig", "application", "x-apple-aspen-config" },
{ ".mp2", "audio", "mpeg" },
{ ".mp2", "audio", "x-mpeg" },
{ ".mp2", "video", "mpeg" },
{ ".mp2", "video", "x-mpeg" },
{ ".mp2", "video", "x-mpeq2a" },
{ ".mp3", "audio", "mpeg3" },
{ ".mp3", "audio", "x-mpeg-3" },
{ ".mp3", "video", "mpeg" },
{ ".mp3", "video", "x-mpeg" },
{ ".mpa", "audio", "mpeg" },
{ ".mpa", "video", "mpeg" },
{ ".mpc", "application", "x-project" },
{ ".mpe", "video", "mpeg" },
{ ".mpeg", "video", "mpeg" },
{ ".mpg", "audio", "mpeg" },
{ ".mpg", "video", "mpeg" },
{ ".mpga", "audio", "mpeg" },
{ ".mpp", "application", "vnd.ms-project" },
{ ".mpt", "application", "x-project" },
{ ".mpv", "application", "x-project" },
{ ".mpx", "application", "x-project" },
{ ".mrc", "application", "marc" },
{ ".ms", "application", "x-troff-ms" },
{ ".mv", "video", "x-sgi-movie" },
{ ".my", "audio", "make" },
{ ".mzz", "application", "x-vnd.audioexplosion.mzz" },
{ ".nap", "image", "naplps" },
{ ".naplps", "image", "naplps" },
{ ".nc", "application", "x-netcdf" },
{ ".ncm", "application", "vnd.nokia.configuration-message" },
{ ".nif", "image", "x-niff" },
{ ".niff", "image", "x-niff" },
{ ".nix", "application", "x-mix-transfer" },
{ ".nsc", "application", "x-conference" },
{ ".nvd", "application", "x-navidoc" },
{ ".o", "application", "octet-stream" },
{ ".oda", "application", "oda" },
{ ".omc", "application", "x-omc" },
{ ".omcd", "application", "x-omcdatamaker" },
{ ".omcr", "application", "x-omcregerator" },
{ ".p", "text", "x-pascal" },
{ ".p10", "application", "pkcs10" },
{ ".p10", "application", "x-pkcs10" },
{ ".p12", "application", "pkcs-12" },
{ ".p12", "application", "x-pkcs12" },
{ ".p7a", "application", "x-pkcs7-signature" },
{ ".p7c", "application", "pkcs7-mime" },
{ ".p7c", "application", "x-pkcs7-mime" },
{ ".p7m", "application", "pkcs7-mime" },
{ ".p7m", "application", "x-pkcs7-mime" },
{ ".p7r", "application", "x-pkcs7-certreqresp" },
{ ".p7s", "application", "pkcs7-signature" },
{ ".part", "application", "pro_eng" },
{ ".pas", "text", "pascal" },
{ ".pbm", "image", "x-portable-bitmap" },
{ ".pcl", "application", "vnd.hp-pcl" },
{ ".pcl", "application", "x-pcl" },
{ ".pct", "image", "x-pict" },
{ ".pcx", "image", "x-pcx" },
{ ".pdb", "chemical", "x-pdb" },
{ ".pdf", "application", "pdf" },
{ ".pfunk", "audio", "make" },
{ ".pfunk", "audio", "make.my.funk" },
{ ".pgm", "image", "x-portable-graymap" },
{ ".pgm", "image", "x-portable-greymap" },
{ ".pic", "image", "pict" },
{ ".pict", "image", "pict" },
{ ".pkg", "application", "x-newton-compatible-pkg" },
{ ".pko", "application", "vnd.ms-pki.pko" },
{ ".pl", "text", "plain" },
{ ".pl", "text", "x-script.perl" },
{ ".plx", "application", "x-pixclscript" },
{ ".pm", "image", "x-xpixmap" },
{ ".pm", "text", "x-script.perl-module" },
{ ".pm4", "application", "x-pagemaker" },
{ ".pm5", "application", "x-pagemaker" },
{ ".png", "image", "png" },
{ ".pnm", "application", "x-portable-anymap" },
{ ".pnm", "image", "x-portable-anymap" },
{ ".pot", "application", "mspowerpoint" },
{ ".pot", "application", "vnd.ms-powerpoint" },
{ ".pov", "model", "x-pov" },
{ ".ppa", "application", "vnd.ms-powerpoint" },
{ ".ppm", "image", "x-portable-pixmap" },
{ ".pps", "application", "mspowerpoint" },
{ ".pps", "application", "vnd.ms-powerpoint" },
{ ".ppt", "application", "mspowerpoint" },
{ ".ppt", "application", "powerpoint" },
{ ".ppt", "application", "vnd.ms-powerpoint" },
{ ".ppt", "application", "x-mspowerpoint" },
{ ".ppz", "application", "mspowerpoint" },
{ ".pre", "application", "x-freelance" },
{ ".prt", "application", "pro_eng" },
{ ".ps", "application", "postscript" },
{ ".psd", "application", "octet-stream" },
{ ".pvu", "paleovu", "x-pv" },
{ ".pwz", "application", "vnd.ms-powerpoint" },
{ ".py", "text", "x-script.phyton" },
{ ".pyc", "applicaiton", "x-bytecode.python" },
{ ".qcp", "audio", "vnd.qcelp" },
{ ".qd3", "x-world", "x-3dmf" },
{ ".qd3d", "x-world", "x-3dmf" },
{ ".qif", "image", "x-quicktime" },
{ ".qt", "video", "quicktime" },
{ ".qtc", "video", "x-qtc" },
{ ".qti", "image", "x-quicktime" },
{ ".qtif", "image", "x-quicktime" },
{ ".ra", "audio", "x-pn-realaudio" },
{ ".ra", "audio", "x-pn-realaudio-plugin" },
{ ".ra", "audio", "x-realaudio" },
{ ".ram", "audio", "x-pn-realaudio" },
{ ".ras", "application", "x-cmu-raster" },
{ ".ras", "image", "cmu-raster" },
{ ".ras", "image", "x-cmu-raster" },
{ ".rast", "image", "cmu-raster" },
{ ".rexx", "text", "x-script.rexx" },
{ ".rf", "image", "vnd.rn-realflash" },
{ ".rgb", "image", "x-rgb" },
{ ".rm", "application", "vnd.rn-realmedia" },
{ ".rm", "audio", "x-pn-realaudio" },
{ ".rmi", "audio", "mid" },
{ ".rmm", "audio", "x-pn-realaudio" },
{ ".rmp", "audio", "x-pn-realaudio" },
{ ".rmp", "audio", "x-pn-realaudio-plugin" },
{ ".rng", "application", "ringing-tones" },
{ ".rng", "application", "vnd.nokia.ringing-tone" },
{ ".rnx", "application", "vnd.rn-realplayer" },
{ ".roff", "application", "x-troff" },
{ ".rp", "image", "vnd.rn-realpix" },
{ ".rpm", "audio", "x-pn-realaudio-plugin" },
{ ".rt", "text", "richtext" },
{ ".rt", "text", "vnd.rn-realtext" },
{ ".rtf", "application", "rtf" },
{ ".rtf", "application", "x-rtf" },
{ ".rtf", "text", "richtext" },
{ ".rtx", "application", "rtf" },
{ ".rtx", "text", "richtext" },
{ ".rv", "video", "vnd.rn-realvideo" },
{ ".s", "text", "x-asm" },
{ ".s3m", "audio", "s3m" },
{ ".saveme", "application", "octet-stream" },
{ ".sbk", "application", "x-tbook" },
{ ".scm", "application", "x-lotusscreencam" },
{ ".scm", "text", "x-script.guile" },
{ ".scm", "text", "x-script.scheme" },
{ ".scm", "video", "x-scm" },
{ ".sdml", "text", "plain" },
{ ".sdp", "application", "sdp" },
{ ".sdp", "application", "x-sdp" },
{ ".sdr", "application", "sounder" },
{ ".sea", "application", "sea" },
{ ".sea", "application", "x-sea" },
{ ".set", "application", "set" },
{ ".sgm", "text", "sgml" },
{ ".sgm", "text", "x-sgml" },
{ ".sgml", "text", "sgml" },
{ ".sgml", "text", "x-sgml" },
{ ".sh", "application", "x-bsh" },
{ ".sh", "application", "x-sh" },
{ ".sh", "application", "x-shar" },
{ ".sh", "text", "x-script.sh" },
{ ".shar", "application", "x-bsh" },
{ ".shar", "application", "x-shar" },
{ ".shtml", "text", "html" },
{ ".shtml", "text", "x-server-parsed-html" },
{ ".sid", "audio", "x-psid" },
{ ".sit", "application", "x-sit" },
{ ".sit", "application", "x-stuffit" },
{ ".skd", "application", "x-koan" },
{ ".skm", "application", "x-koan" },
{ ".skp", "application", "x-koan" },
{ ".skt", "application", "x-koan" },
{ ".sl", "application", "x-seelogo" },
{ ".smi", "application", "smil" },
{ ".smil", "application", "smil" },
{ ".snd", "audio", "basic" },
{ ".snd", "audio", "x-adpcm" },
{ ".sol", "application", "solids" },
{ ".spc", "application", "x-pkcs7-certificates" },
{ ".spc", "text", "x-speech" },
{ ".spl", "application", "futuresplash" },
{ ".spr", "application", "x-sprite" },
{ ".sprite", "application", "x-sprite" },
{ ".src", "application", "x-wais-source" },
{ ".ssi", "text", "x-server-parsed-html" },
{ ".ssm", "application", "streamingmedia" },
{ ".sst", "application", "vnd.ms-pki.certstore" },
{ ".step", "application", "step" },
{ ".stl", "application", "sla" },
{ ".stl", "application", "vnd.ms-pki.stl" },
{ ".stl", "application", "x-navistyle" },
{ ".stp", "application", "step" },
{ ".sv4cpio", "application", "x-sv4cpio" },
{ ".sv4crc", "application", "x-sv4crc" },
{ ".svf", "image", "vnd.dwg" },
{ ".svf", "image", "x-dwg" },
{ ".svr", "application", "x-world" },
{ ".svr", "x-world", "x-svr" },
{ ".swf", "application", "x-shockwave-flash" },
{ ".t", "application", "x-troff" },
{ ".talk", "text", "x-speech" },
{ ".tar", "application", "x-tar" },
{ ".tbk", "application", "toolbook" },
{ ".tbk", "application", "x-tbook" },
{ ".tcl", "application", "x-tcl" },
{ ".tcl", "text", "x-script.tcl" },
{ ".tcsh", "text", "x-script.tcsh" },
{ ".tex", "application", "x-tex" },
{ ".texi", "application", "x-texinfo" },
{ ".texinfo", "application", "x-texinfo" },
{ ".text", "application", "plain" },
{ ".text", "text", "plain" },
{ ".tgz", "application", "gnutar" },
{ ".tgz", "application", "x-compressed" },
{ ".tif", "image", "tiff" },
{ ".tif", "image", "x-tiff" },
{ ".tiff", "image", "tiff" },
{ ".tiff", "image", "x-tiff" },
{ ".tr", "application", "x-troff" },
{ ".tsi", "audio", "tsp-audio" },
{ ".tsp", "application", "dsptype" },
{ ".tsp", "audio", "tsplayer" },
{ ".tsv", "text", "tab-separated-values" },
{ ".turbot", "image", "florian" },
{ ".txt", "text", "plain" },
{ ".uil", "text", "x-uil" },
{ ".uni", "text", "uri-list" },
{ ".unis", "text", "uri-list" },
{ ".unv", "application", "i-deas" },
{ ".uri", "text", "uri-list" },
{ ".uris", "text", "uri-list" },
{ ".ustar", "application", "x-ustar" },
{ ".ustar", "multipart", "x-ustar" },
{ ".uu", "application", "octet-stream" },
{ ".uu", "text", "x-uuencode" },
{ ".uue", "text", "x-uuencode" },
{ ".vcd", "application", "x-cdlink" },
{ ".vcs", "text", "x-vcalendar" },
{ ".vda", "application", "vda" },
{ ".vdo", "video", "vdo" },
{ ".vew", "application", "groupwise" },
{ ".viv", "video", "vivo" },
{ ".viv", "video", "vnd.vivo" },
{ ".vivo", "video", "vivo" },
{ ".vivo", "video", "vnd.vivo" },
{ ".vmd", "application", "vocaltec-media-desc" },
{ ".vmf", "application", "vocaltec-media-file" },
{ ".voc", "audio", "voc" },
{ ".voc", "audio", "x-voc" },
{ ".vos", "video", "vosaic" },
{ ".vox", "audio", "voxware" },
{ ".vqe", "audio", "x-twinvq-plugin" },
{ ".vqf", "audio", "x-twinvq" },
{ ".vql", "audio", "x-twinvq-plugin" },
{ ".vrml", "application", "x-vrml" },
{ ".vrml", "model", "vrml" },
{ ".vrml", "x-world", "x-vrml" },
{ ".vrt", "x-world", "x-vrt" },
{ ".vsd", "application", "x-visio" },
{ ".vst", "application", "x-visio" },
{ ".vsw", "application", "x-visio" },
{ ".w60", "application", "wordperfect6.0" },
{ ".w61", "application", "wordperfect6.1" },
{ ".w6w", "application", "msword" },
{ ".wav", "audio", "wav" },
{ ".wav", "audio", "x-wav" },
{ ".wb1", "application", "x-qpro" },
{ ".wbmp", "image", "vnd.wap.wbmp" },
{ ".web", "application", "vnd.xara" },
{ ".wiz", "application", "msword" },
{ ".wk1", "application", "x-123" },
{ ".wmf", "windows", "metafile" },
{ ".wml", "text", "vnd.wap.wml" },
{ ".wmlc", "application", "vnd.wap.wmlc" },
{ ".wmls", "text", "vnd.wap.wmlscript" },
{ ".wmlsc", "application", "vnd.wap.wmlscriptc" },
{ ".word", "application", "msword" },
{ ".wp", "application", "wordperfect" },
{ ".wp5", "application", "wordperfect" },
{ ".wp5", "application", "wordperfect6.0" },
{ ".wp6", "application", "wordperfect" },
{ ".wpd", "application", "wordperfect" },
{ ".wpd", "application", "x-wpwin" },
{ ".wq1", "application", "x-lotus" },
{ ".wri", "application", "mswrite" },
{ ".wri", "application", "x-wri" },
{ ".wrl", "application", "x-world" },
{ ".wrl", "model", "vrml" },
{ ".wrl", "x-world", "x-vrml" },
{ ".wrz", "model", "vrml" },
{ ".wrz", "x-world", "x-vrml" },
{ ".wsc", "text", "scriplet" },
{ ".wsrc", "application", "x-wais-source" },
{ ".wtk", "application", "x-wintalk" },
{ ".xbm", "image", "x-xbitmap" },
{ ".xbm", "image", "x-xbm" },
{ ".xbm", "image", "xbm" },
{ ".xdr", "video", "x-amt-demorun" },
{ ".xgz", "xgl", "drawing" },
{ ".xif", "image", "vnd.xiff" },
{ ".xl", "application", "excel" },
{ ".xla", "application", "excel" },
{ ".xla", "application", "x-excel" },
{ ".xla", "application", "x-msexcel" },
{ ".xlb", "application", "excel" },
{ ".xlb", "application", "vnd.ms-excel" },
{ ".xlb", "application", "x-excel" },
{ ".xlc", "application", "excel" },
{ ".xlc", "application", "vnd.ms-excel" },
{ ".xlc", "application", "x-excel" },
{ ".xld", "application", "excel" },
{ ".xld", "application", "x-excel" },
{ ".xlk", "application", "excel" },
{ ".xlk", "application", "x-excel" },
{ ".xll", "application", "excel" },
{ ".xll", "application", "vnd.ms-excel" },
{ ".xll", "application", "x-excel" },
{ ".xlm", "application", "excel" },
{ ".xlm", "application", "vnd.ms-excel" },
{ ".xlm", "application", "x-excel" },
{ ".xls", "application", "excel" },
{ ".xls", "application", "vnd.ms-excel" },
{ ".xls", "application", "x-excel" },
{ ".xls", "application", "x-msexcel" },
{ ".xlt", "application", "excel" },
{ ".xlt", "application", "x-excel" },
{ ".xlv", "application", "excel" },
{ ".xlv", "application", "x-excel" },
{ ".xlw", "application", "excel" },
{ ".xlw", "application", "vnd.ms-excel" },
{ ".xlw", "application", "x-excel" },
{ ".xlw", "application", "x-msexcel" },
{ ".xm", "audio", "xm" },
{ ".xml", "application", "xml" },
{ ".xml", "text", "xml" },
{ ".xmz", "xgl", "movie" },
{ ".xpix", "application", "x-vnd.ls-xpix" },
{ ".xpm", "image", "x-xpixmap" },
{ ".xpm", "image", "xpm" },
{ ".x-png", "image", "png" },
{ ".xsr", "video", "x-amt-showrun" },
{ ".xwd", "image", "x-xwd" },
{ ".xwd", "image", "x-xwindowdump" },
{ ".xyz", "chemical", "x-pdb" },
{ ".z", "application", "x-compress" },
{ ".z", "application", "x-compressed" },
{ ".zip", "application", "x-compressed" },
{ ".zip", "application", "x-zip-compressed" },
{ ".zip", "application", "zip" },
{ ".zip", "multipart", "x-zip" },
{ ".zoo", "application", "octet-stream" },
{ ".zsh", "text", "x-script.zsh" }
]
def inline(path, filename \\ nil) do
case path |> Path.expand |> File.read do
{ :ok, data } ->
{
:ok,
%Mailex.Attachment{
filename: filename || Path.basename(path),
type: guess_mime_type(path),
data: data
}
}
{ :error, message } -> { :error, message }
end
end
def inline!(path, filename \\ nil) do
case inline(path, filename) do
{ :ok, attachment } -> attachment
{ :error, message } -> throw message
end
end
def mime_types, do: @mime_types
def guess_mime_type(path) do
extension = Path.extname(path)
type = Enum.find mime_types, fn({ext, _, _}) -> ext == extension end
if (type) do
{ elem(type, 1), elem(type, 2) }
else
{ "application", "octet-stream" }
end
end
end

5
lib/mailex/email.ex Normal file
View File

@ -0,0 +1,5 @@
defmodule Mailex.Email do
defstruct subject: nil, from: nil, reply_to: nil, to: nil, cc: nil, bcc: nil, attachments: nil, html: nil, text: nil
end

137
lib/mailex/render.ex Normal file
View File

@ -0,0 +1,137 @@
defmodule Mailex.Render do
alias Mailex.Address
def render(email) do
mimemail_args = []
if email.text, do:
mimemail_args = [ { :plain, email.text } | mimemail_args]
if email.html, do:
mimemail_args = [ { :html, email.html } | mimemail_args]
if email.attachments, do:
mimemail_args = [ Enum.map(email.attachments, fn(a) -> { :attachment, a.data, a } end) | mimemail_args ]
mimemail_args |> List.flatten |> to_tuple(email) |> :mimemail.encode
end
def to_tuple(part, _email) when is_tuple(part) do
{
mime_type_for(part),
mime_subtype_for(part),
[],
parameters_for(part),
elem(part, 1)
}
end
def to_tuple(parts, email) when is_list(parts) do
{
mime_type_for(parts),
mime_subtype_for(parts),
headers_for(email),
[],
Enum.map(parts, &to_tuple(&1, email))
}
end
def parameters_for({:attachment, _body, attachment}) do
[
{ "transfer-encoding", "base64" },
content_type_params_for(attachment),
disposition_for(attachment),
disposition_params_for(attachment)
]
end
def parameters_for(_part) do
[
{ "transfer-encoding", "quoted-printable" },
{ "content-type-params", [] },
{ "disposition", "inline" },
{ "disposition-params", [] }
]
end
def content_type_params_for(attachment) do
{ "content-type-params", [{ "name", attachment.filename }] }
end
def disposition_for(_attachment) do
{ "disposition", "attachment" }
end
def disposition_params_for(attachment) do
{ "disposition-params", [{ "filename", attachment.filename }] }
end
def mime_type_for(parts) when is_list(parts) do
"multipart"
end
def mime_type_for({_type, _}) do
"text"
end
def mime_type_for({_, _, attachment}) do
elem(attachment.type, 0)
end
def mime_subtype_for(parts) when is_list(parts) do
if Enum.find parts, fn(part) -> elem(part, 0) == :attachment end do
"mixed"
else
"alternative"
end
end
def mime_subtype_for({type, _}) do
type
end
def mime_subtype_for({_, _, attachment}) do
elem(attachment.type, 1)
end
def headers_for(email) do
headers = []
if email.reply_to, do:
headers = [ { "Reply-To", email.reply_to |> stringify_addresses } ]
if email.bcc, do:
headers = [ { "Bcc", email.bcc |> stringify_addresses } | headers ]
if email.cc, do:
headers = [ { "Cc", email.cc |> stringify_addresses } | headers ]
[ { "From", email.from |> stringify_addresses },
{ "To", email.to |> stringify_addresses },
{ "Subject", email.subject || "" } | headers ]
end
def stringify_addresses(addresses) do
addresses = addresses |> Address.rfc_822_format
if is_list(addresses) do
Enum.join(addresses, ", ")
else
addresses
end
end
end

23
mix.exs Normal file
View File

@ -0,0 +1,23 @@
defmodule Mailex.Mixfile do
use Mix.Project
def project do
[app: :mailex,
version: "0.0.1",
elixir: "~> 1.1",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps]
end
def application do
[ applications: [:ssl, :crypto, :eiconv, :gen_smtp]]
end
defp deps do
[
{ :eiconv, github: "zotonic/eiconv" },
{ :gen_smtp, ">= 0.9.0" }
]
end
end

2
mix.lock Normal file
View File

@ -0,0 +1,2 @@
%{"eiconv": {:git, "https://github.com/zotonic/eiconv.git", "644fb5e7bd6640fbd073f4d28957914ea979aea0", []},
"gen_smtp": {:hex, :gen_smtp, "0.9.0"}}

BIN
test/.DS_Store vendored Normal file

Binary file not shown.

BIN
test/data/logo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

8
test/mailex_test.exs Normal file
View File

@ -0,0 +1,8 @@
defmodule MailexTest do
use ExUnit.Case
doctest Mailex
test "the truth" do
assert 1 + 1 == 2
end
end

1
test/test_helper.exs Normal file
View File

@ -0,0 +1 @@
ExUnit.start()