defmodule Mailman.Attachment do
  @moduledoc """
  A struct defining an attachable file. It automatically detect the mime type
  based on file extension.
  """

  defstruct file_name: "",
            mime_type: "",
            mime_sub_type: "",
            data: "",
            disposition: ""

  @type t :: %__MODULE__{
          file_name: String.t(),
          mime_type: String.t(),
          data: String.t(),
          disposition: String.t()
        }

  @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"},
    {".csv", "text/csv"},
    {".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"}
  ]

  defp get_attachment_data_via_http(url) do
    if is_valid_url?(url) do
      case HTTPoison.get(url, recv_timeout: 60_000) do
        {:ok, response} ->
            case response.status_code do
            200 -> {:ok, response.body}
            _ -> {:error, :invalid_http_response}
            end
        {:error, message} ->
          {:error, message}
      end
    else
      {:error, :invalid_url}
    end
  end

  defp get_attachment_data_from_disk(file_path) do
    file_path
    |> Path.expand()
    |> File.read()
  end

  @spec get_attachment_data(String.t()) :: {:ok, String.t()} | {:error, String.t()}
  defp get_attachment_data(url_or_file_path) do
    if is_valid_url?(url_or_file_path) do
      get_attachment_data_via_http(url_or_file_path)
    else
      get_attachment_data_from_disk(url_or_file_path)
    end
  end

  def create(url_or_file_path, disposition, file_name \\ nil, mime_tuple \\ nil) do
    data = get_attachment_data(url_or_file_path)

    {mime_type, mime_subtype} =
      mime_tuple || mime_type_and_subtype_from_extension(url_or_file_path)

    case data do
      {:ok, data} ->
        {:ok,
         %Mailman.Attachment{
           file_name: file_name || Path.basename(url_or_file_path),
           mime_type: mime_type,
           mime_sub_type: mime_subtype,
           data: data,
           disposition: disposition
         }}

      {:error, message} ->
        {:error, message}
    end
  end

  @doc """
  Get the attachment struct with disposition 'inline'.
  """
  @spec inline(String.t(), String.t(), {String.t(), String.t()}) ::
          {:ok, t} | {:error, String.t()}
  def inline(url_or_file_path, file_name \\ nil, mime_tuple \\ nil) do
    create(url_or_file_path, "inline", file_name, mime_tuple)
  end

  @doc """
  Get the attachment struct with disposition 'inline'. Throw an error if
  anything goes wrong.
  """
  @spec inline(String.t(), String.t(), {String.t(), String.t()}) :: t
  def inline!(url_or_file_path, file_name \\ nil, mime_tuple \\ nil) do
    case inline(url_or_file_path, file_name, mime_tuple) do
      {:ok, attachment} -> attachment
      {:error, message} -> throw(message)
    end
  end

  @doc """
  Get the attachment struct with disposition 'attachment'.
  """
  def attach(url_or_file_path, file_name \\ nil, mime_tuple \\ nil) do
    create(url_or_file_path, "attachment", file_name, mime_tuple)
  end

  @doc """
  Get the attachment struct with disposition 'attachment'. Throw an error if
  anything goes wrong.
  """
  def attach!(url_or_file_path, file_name \\ nil, mime_tuple \\ nil) do
    case attach(url_or_file_path, file_name, mime_tuple) do
      {:ok, attachment} -> attachment
      {:error, message} -> throw(message)
    end
  end

  def mime_type_and_subtype_from_extension(path) do
    extension = Path.extname(path)

    mime_types()
    |> Enum.find(fn {ext, _} ->
      ext == extension
    end)
    |> case do
      nil -> "application/octet-stream"
      {_ext, mimetype} -> mimetype
    end
    |> String.split("/")
    |> List.to_tuple()
  end

  def mime_types do
    @mime_types
  end

  def is_valid_url?(str) do
    case URI.parse(str) do
      %URI{scheme: nil} -> false
      %URI{host: nil} -> false
      %URI{path: nil} -> false
      uri -> match?({:ok, _}, :inet.gethostbyname(to_charlist(uri.host)))
    end
  end
end
