commit a7a13da6c32898e5f0646a819f2ad2dc2f42608e Author: Dejan Strbac Date: Wed Nov 25 17:10:43 2015 +0100 initial commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..7acdae2 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..755b605 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/_build +/cover +/deps +erl_crash.dump +*.ez diff --git a/README.md b/README.md new file mode 100644 index 0000000..d7cc888 --- /dev/null +++ b/README.md @@ -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 + ]) diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..ee9b537 --- /dev/null +++ b/config/config.exs @@ -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" diff --git a/dejan.eml b/dejan.eml new file mode 100644 index 0000000..c85cf79 --- /dev/null +++ b/dejan.eml @@ -0,0 +1,348 @@ +From: Dejan Strbac +To: Dejan , Dex +Subject: +Cc: Dejan , Dex +Bcc: Dejan , Dex +Reply-To: Dejan , Dex +Content-Type: multipart/mixed; + boundary="_=4d394y4p3v1f036r2w0g3b6r4m43364a=_" +MIME-Version: 1.0 +Date: Wed, 25 Nov 2015 12:59:06 +0100 +Message-ID: + + +--_=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=_-- diff --git a/lib/mailex.ex b/lib/mailex.ex new file mode 100644 index 0000000..5764f25 --- /dev/null +++ b/lib/mailex.ex @@ -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 \ No newline at end of file diff --git a/lib/mailex/address.ex b/lib/mailex/address.ex new file mode 100644 index 0000000..2247bd3 --- /dev/null +++ b/lib/mailex/address.ex @@ -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 \ No newline at end of file diff --git a/lib/mailex/attachment.ex b/lib/mailex/attachment.ex new file mode 100644 index 0000000..cdc6484 --- /dev/null +++ b/lib/mailex/attachment.ex @@ -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 diff --git a/lib/mailex/email.ex b/lib/mailex/email.ex new file mode 100644 index 0000000..504db55 --- /dev/null +++ b/lib/mailex/email.ex @@ -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 diff --git a/lib/mailex/render.ex b/lib/mailex/render.ex new file mode 100644 index 0000000..e825452 --- /dev/null +++ b/lib/mailex/render.ex @@ -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 \ No newline at end of file diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..fece09f --- /dev/null +++ b/mix.exs @@ -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 diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..ff40e80 --- /dev/null +++ b/mix.lock @@ -0,0 +1,2 @@ +%{"eiconv": {:git, "https://github.com/zotonic/eiconv.git", "644fb5e7bd6640fbd073f4d28957914ea979aea0", []}, + "gen_smtp": {:hex, :gen_smtp, "0.9.0"}} diff --git a/test/.DS_Store b/test/.DS_Store new file mode 100644 index 0000000..faa1621 Binary files /dev/null and b/test/.DS_Store differ diff --git a/test/data/logo.gif b/test/data/logo.gif new file mode 100644 index 0000000..fd6f8b8 Binary files /dev/null and b/test/data/logo.gif differ diff --git a/test/mailex_test.exs b/test/mailex_test.exs new file mode 100644 index 0000000..81491e3 --- /dev/null +++ b/test/mailex_test.exs @@ -0,0 +1,8 @@ +defmodule MailexTest do + use ExUnit.Case + doctest Mailex + + test "the truth" do + assert 1 + 1 == 2 + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start()