--[[
Copyright (c) 2019, Vsevolod Stakhov <vsevolod@highsecure.ru>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--

--[[[
-- @module lua_mime
-- This module contains helper functions to modify mime parts
--]]

local rspamd_util = require "rspamd_util"

local exports = {}

local function newline(task)
  local t = task:get_newlines_type()

  if t == 'cr' then
    return '\r'
  elseif t == 'lf' then
    return '\n'
  end

  return '\r\n'
end


--[[[
-- @function lua_mime.add_text_footer(task, html_footer, text_footer)
-- Adds a footer to all text parts in a message. It returns a table with the following
-- fields:
-- * out: new content (body only)
-- * need_rewrite_ct: boolean field that means if we must rewrite content type
-- * new_ct: new content type (type => string, subtype => string)
--]]
exports.add_text_footer = function(task, html_footer, text_footer)
  local newline_s = newline(task)
  local res = {}
  local out = {}
  local text_parts = task:get_text_parts()

  if not (html_footer or text_footer) or not (text_parts and #text_parts > 0) then
    return false
  end

  local function do_append_footer(part, footer, is_multipart)
    local tp = part:get_text()
    local ct = 'text/plain'
    local cte = 'quoted-printable'

    if tp:is_html() then
      ct = 'text/html'
    end

    if part:get_cte() == '7bit' then
      cte = '7bit'
    end

    if is_multipart then
      out[#out + 1] = string.format('Content-Type: %s; charset=utf-8%s'..
          'Content-Transfer-Encoding: %s',
          ct, newline_s, cte)
      out[#out + 1] = ''
    end

    local content = tostring(tp:get_content('raw_utf') or '')
    local double_nline = newline_s .. newline_s
    local nlen = #double_nline
    -- Hack, if part ends with 2 newline, then we append it after footer
    if content:sub(-(nlen), nlen + 1) == double_nline then
      content = string.format('%s%s',
          content:sub(-(#newline_s), #newline_s + 1), -- content without last newline
          footer)
      out[#out + 1] = {rspamd_util.encode_qp(content,
          80, task:get_newlines_type()), true}
      out[#out + 1] = ''
    else
      content = content .. footer
      out[#out + 1] = {rspamd_util.encode_qp(content,
          80, task:get_newlines_type()), true}
      out[#out + 1] = ''
    end

  end

  if html_footer or text_footer then
    -- We need to take extra care about content-type and cte
    local ct = task:get_header('Content-Type')
    if ct then
      ct = rspamd_util.parse_content_type(ct, task:get_mempool())
    end

    if ct then
      if ct.type and ct.type == 'text' then
        if ct.subtype then
          if html_footer and (ct.subtype == 'html' or ct.subtype == 'htm') then
            res.need_rewrite_ct = true
          elseif text_footer and ct.subtype == 'plain' then
            res.need_rewrite_ct = true
          end
        else
          if text_footer then
            res.need_rewrite_ct = true
          end
        end

        res.new_ct = ct
      end
    else

      if text_parts then

        if #text_parts == 1 then
          res.need_rewrite_ct = true
          res.new_ct = {
            type = 'text',
            subtype = 'plain'
          }
        elseif #text_parts > 1 then
          -- XXX: in fact, it cannot be
          res.new_ct = {
            type = 'multipart',
            subtype = 'mixed'
          }
        end
      end
    end
  end

  local boundaries = {}
  local cur_boundary

  for _,part in ipairs(task:get_parts()) do
    local boundary = part:get_boundary()
    if part:is_multipart() then
      if cur_boundary then
        out[#out + 1] = string.format('--%s',
            boundaries[#boundaries])
      end

      boundaries[#boundaries + 1] = boundary or '--XXX'
      cur_boundary = boundary

      local rh = part:get_raw_headers()
      if #rh > 0 then
        out[#out + 1] = {rh, true}
      end
    elseif part:is_message() then
      if boundary then
        if cur_boundary and boundary ~= cur_boundary then
          -- Need to close boundary
          out[#out + 1] = string.format('--%s--%s',
              boundaries[#boundaries], newline_s)
          table.remove(boundaries)
          cur_boundary = nil
        end
        out[#out + 1] = string.format('--%s',
            boundary)
      end

      out[#out + 1] = {part:get_raw_headers(), true}
    else
      local append_footer = false
      local skip_footer = part:is_attachment()

      local parent = part:get_parent()
      if parent then
        local t,st = parent:get_type()

        if t == 'multipart' and st == 'signed' then
          -- Do not modify signed parts
          skip_footer = true
        end
      end
      if text_footer and part:is_text() then
        local tp = part:get_text()

        if not tp:is_html() then
          append_footer = text_footer
        end
      end

      if html_footer and part:is_text() then
        local tp = part:get_text()

        if tp:is_html() then
          append_footer = html_footer
        end
      end

      if boundary then
        if cur_boundary and boundary ~= cur_boundary then
          -- Need to close boundary
          out[#out + 1] = string.format('--%s--%s',
              boundaries[#boundaries], newline_s)
          table.remove(boundaries)
          cur_boundary = boundary
        end
        out[#out + 1] = string.format('--%s',
            boundary)
      end

      if append_footer and not skip_footer then
        do_append_footer(part, append_footer,
            parent and parent:is_multipart())
      else
        out[#out + 1] = {part:get_raw_headers(), true}
        out[#out + 1] = {part:get_raw_content(), false}
      end
    end
  end

  -- Close remaining
  local b = table.remove(boundaries)
  while b do
    out[#out + 1] = string.format('--%s--', b)
    if #boundaries > 0 then
      out[#out + 1] = ''
    end
    b = table.remove(boundaries)
  end

  res.out = out

  return res
end

-- All mime extensions with corresponding content types
exports.full_extensions_map = {
  {"323", "text/h323"},
  {"3g2", "video/3gpp2"},
  {"3gp", "video/3gpp"},
  {"3gp2", "video/3gpp2"},
  {"3gpp", "video/3gpp"},
  {"7z", "application/x-7z-compressed"},
  {"aa", "audio/audible"},
  {"AAC", "audio/aac"},
  {"aaf", "application/octet-stream"},
  {"aax", "audio/vnd.audible.aax"},
  {"ac3", "audio/ac3"},
  {"aca", "application/octet-stream"},
  {"accda", "application/msaccess.addin"},
  {"accdb", "application/msaccess"},
  {"accdc", "application/msaccess.cab"},
  {"accde", "application/msaccess"},
  {"accdr", "application/msaccess.runtime"},
  {"accdt", "application/msaccess"},
  {"accdw", "application/msaccess.webapplication"},
  {"accft", "application/msaccess.ftemplate"},
  {"acx", "application/internet-property-stream"},
  {"AddIn", "text/xml"},
  {"ade", "application/msaccess"},
  {"adobebridge", "application/x-bridge-url"},
  {"adp", "application/msaccess"},
  {"ADT", "audio/vnd.dlna.adts"},
  {"ADTS", "audio/aac"},
  {"afm", "application/octet-stream"},
  {"ai", "application/postscript"},
  {"aif", "audio/aiff"},
  {"aifc", "audio/aiff"},
  {"aiff", "audio/aiff"},
  {"air", "application/vnd.adobe.air-application-installer-package+zip"},
  {"amc", "application/mpeg"},
  {"anx", "application/annodex"},
  {"apk", "application/vnd.android.package-archive" },
  {"application", "application/x-ms-application"},
  {"art", "image/x-jg"},
  {"asa", "application/xml"},
  {"asax", "application/xml"},
  {"ascx", "application/xml"},
  {"asd", "application/octet-stream"},
  {"asf", "video/x-ms-asf"},
  {"ashx", "application/xml"},
  {"asi", "application/octet-stream"},
  {"asm", "text/plain"},
  {"asmx", "application/xml"},
  {"aspx", "application/xml"},
  {"asr", "video/x-ms-asf"},
  {"asx", "video/x-ms-asf"},
  {"atom", "application/atom+xml"},
  {"au", "audio/basic"},
  {"avi", "video/x-msvideo"},
  {"axa", "audio/annodex"},
  {"axs", "application/olescript"},
  {"axv", "video/annodex"},
  {"bas", "text/plain"},
  {"bcpio", "application/x-bcpio"},
  {"bin", "application/octet-stream"},
  {"bmp", "image/bmp"},
  {"c", "text/plain"},
  {"cab", "application/octet-stream"},
  {"caf", "audio/x-caf"},
  {"calx", "application/vnd.ms-office.calx"},
  {"cat", "application/vnd.ms-pki.seccat"},
  {"cc", "text/plain"},
  {"cd", "text/plain"},
  {"cdda", "audio/aiff"},
  {"cdf", "application/x-cdf"},
  {"cer", "application/x-x509-ca-cert"},
  {"cfg", "text/plain"},
  {"chm", "application/octet-stream"},
  {"class", "application/x-java-applet"},
  {"clp", "application/x-msclip"},
  {"cmd", "text/plain"},
  {"cmx", "image/x-cmx"},
  {"cnf", "text/plain"},
  {"cod", "image/cis-cod"},
  {"config", "application/xml"},
  {"contact", "text/x-ms-contact"},
  {"coverage", "application/xml"},
  {"cpio", "application/x-cpio"},
  {"cpp", "text/plain"},
  {"crd", "application/x-mscardfile"},
  {"crl", "application/pkix-crl"},
  {"crt", "application/x-x509-ca-cert"},
  {"cs", "text/plain"},
  {"csdproj", "text/plain"},
  {"csh", "application/x-csh"},
  {"csproj", "text/plain"},
  {"css", "text/css"},
  {"csv", {"application/vnd.ms-excel", "text/csv", "text/plain"}},
  {"cur", "application/octet-stream"},
  {"cxx", "text/plain"},
  {"dat", {"application/octet-stream", "application/ms-tnef"}},
  {"datasource", "application/xml"},
  {"dbproj", "text/plain"},
  {"dcr", "application/x-director"},
  {"def", "text/plain"},
  {"deploy", "application/octet-stream"},
  {"der", "application/x-x509-ca-cert"},
  {"dgml", "application/xml"},
  {"dib", "image/bmp"},
  {"dif", "video/x-dv"},
  {"dir", "application/x-director"},
  {"disco", "text/xml"},
  {"divx", "video/divx"},
  {"dll", "application/x-msdownload"},
  {"dll.config", "text/xml"},
  {"dlm", "text/dlm"},
  {"doc", "application/msword"},
  {"docm", "application/vnd.ms-word.document.macroEnabled.12"},
  {"docx", {
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    "application/msword",
    "application/vnd.ms-word.document.12",
    "application/octet-stream",
  }},
  {"dot", "application/msword"},
  {"dotm", "application/vnd.ms-word.template.macroEnabled.12"},
  {"dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
  {"dsp", "application/octet-stream"},
  {"dsw", "text/plain"},
  {"dtd", "text/xml"},
  {"dtsConfig", "text/xml"},
  {"dv", "video/x-dv"},
  {"dvi", "application/x-dvi"},
  {"dwf", "drawing/x-dwf"},
  {"dwg", {"application/acad", "image/vnd.dwg"}},
  {"dwp", "application/octet-stream"},
  {"dxf", "application/x-dxf" },
  {"dxr", "application/x-director"},
  {"eml", "message/rfc822"},
  {"emz", "application/octet-stream"},
  {"eot", "application/vnd.ms-fontobject"},
  {"eps", "application/postscript"},
  {"etl", "application/etl"},
  {"etx", "text/x-setext"},
  {"evy", "application/envoy"},
  {"exe", {
    "application/x-dosexec",
    "application/x-msdownload",
    "application/x-executable",
  }},
  {"exe.config", "text/xml"},
  {"fdf", "application/vnd.fdf"},
  {"fif", "application/fractals"},
  {"filters", "application/xml"},
  {"fla", "application/octet-stream"},
  {"flac", "audio/flac"},
  {"flr", "x-world/x-vrml"},
  {"flv", "video/x-flv"},
  {"fsscript", "application/fsharp-script"},
  {"fsx", "application/fsharp-script"},
  {"generictest", "application/xml"},
  {"gif", "image/gif"},
  {"gpx", "application/gpx+xml"},
  {"group", "text/x-ms-group"},
  {"gsm", "audio/x-gsm"},
  {"gtar", "application/x-gtar"},
  {"gz", {"application/gzip", "application/x-gzip"}},
  {"h", "text/plain"},
  {"hdf", "application/x-hdf"},
  {"hdml", "text/x-hdml"},
  {"hhc", "application/x-oleobject"},
  {"hhk", "application/octet-stream"},
  {"hhp", "application/octet-stream"},
  {"hlp", "application/winhlp"},
  {"hpp", "text/plain"},
  {"hqx", "application/mac-binhex40"},
  {"hta", "application/hta"},
  {"htc", "text/x-component"},
  {"htm", "text/html"},
  {"html", "text/html"},
  {"htt", "text/webviewhtml"},
  {"hxa", "application/xml"},
  {"hxc", "application/xml"},
  {"hxd", "application/octet-stream"},
  {"hxe", "application/xml"},
  {"hxf", "application/xml"},
  {"hxh", "application/octet-stream"},
  {"hxi", "application/octet-stream"},
  {"hxk", "application/xml"},
  {"hxq", "application/octet-stream"},
  {"hxr", "application/octet-stream"},
  {"hxs", "application/octet-stream"},
  {"hxt", "text/html"},
  {"hxv", "application/xml"},
  {"hxw", "application/octet-stream"},
  {"hxx", "text/plain"},
  {"i", "text/plain"},
  {"ico", "image/x-icon"},
  {"ics", {"text/calendar", "application/octet-stream"}},
  {"idl", "text/plain"},
  {"ief", "image/ief"},
  {"iii", "application/x-iphone"},
  {"inc", "text/plain"},
  {"inf", "application/octet-stream"},
  {"ini", "text/plain"},
  {"inl", "text/plain"},
  {"ins", "application/x-internet-signup"},
  {"ipa", "application/x-itunes-ipa"},
  {"ipg", "application/x-itunes-ipg"},
  {"ipproj", "text/plain"},
  {"ipsw", "application/x-itunes-ipsw"},
  {"iqy", "text/x-ms-iqy"},
  {"isp", "application/x-internet-signup"},
  {"ite", "application/x-itunes-ite"},
  {"itlp", "application/x-itunes-itlp"},
  {"itms", "application/x-itunes-itms"},
  {"itpc", "application/x-itunes-itpc"},
  {"IVF", "video/x-ivf"},
  {"jar", "application/java-archive"},
  {"java", "application/octet-stream"},
  {"jck", "application/liquidmotion"},
  {"jcz", "application/liquidmotion"},
  {"jfif", "image/pjpeg"},
  {"jnlp", "application/x-java-jnlp-file"},
  {"jpb", "application/octet-stream"},
  {"jpe", {"image/jpeg", "image/pjpeg"}},
  {"jpeg", {"image/jpeg", "image/pjpeg"}},
  {"jpg", {"image/jpeg", "image/pjpeg"}},
  {"js", "application/javascript"},
  {"json", "application/json"},
  {"jsx", "text/jscript"},
  {"jsxbin", "text/plain"},
  {"latex", "application/x-latex"},
  {"library-ms", "application/windows-library+xml"},
  {"lit", "application/x-ms-reader"},
  {"loadtest", "application/xml"},
  {"lpk", "application/octet-stream"},
  {"lsf", "video/x-la-asf"},
  {"lst", "text/plain"},
  {"lsx", "video/x-la-asf"},
  {"lzh", "application/octet-stream"},
  {"m13", "application/x-msmediaview"},
  {"m14", "application/x-msmediaview"},
  {"m1v", "video/mpeg"},
  {"m2t", "video/vnd.dlna.mpeg-tts"},
  {"m2ts", "video/vnd.dlna.mpeg-tts"},
  {"m2v", "video/mpeg"},
  {"m3u", "audio/x-mpegurl"},
  {"m3u8", "audio/x-mpegurl"},
  {"m4a", {"audio/m4a", "audio/x-m4a"}},
  {"m4b", "audio/m4b"},
  {"m4p", "audio/m4p"},
  {"m4r", "audio/x-m4r"},
  {"m4v", "video/x-m4v"},
  {"mac", "image/x-macpaint"},
  {"mak", "text/plain"},
  {"man", "application/x-troff-man"},
  {"manifest", "application/x-ms-manifest"},
  {"map", "text/plain"},
  {"master", "application/xml"},
  {"mbox", "application/mbox"},
  {"mda", "application/msaccess"},
  {"mdb", "application/x-msaccess"},
  {"mde", "application/msaccess"},
  {"mdp", "application/octet-stream"},
  {"me", "application/x-troff-me"},
  {"mfp", "application/x-shockwave-flash"},
  {"mht", "message/rfc822"},
  {"mhtml", "message/rfc822"},
  {"mid", "audio/mid"},
  {"midi", "audio/mid"},
  {"mix", "application/octet-stream"},
  {"mk", "text/plain"},
  {"mmf", "application/x-smaf"},
  {"mno", "text/xml"},
  {"mny", "application/x-msmoney"},
  {"mod", "video/mpeg"},
  {"mov", "video/quicktime"},
  {"movie", "video/x-sgi-movie"},
  {"mp2", "video/mpeg"},
  {"mp2v", "video/mpeg"},
  {"mp3", "audio/mpeg"},
  {"mp4", "video/mp4"},
  {"mp4v", "video/mp4"},
  {"mpa", "video/mpeg"},
  {"mpe", "video/mpeg"},
  {"mpeg", "video/mpeg"},
  {"mpf", "application/vnd.ms-mediapackage"},
  {"mpg", "video/mpeg"},
  {"mpp", "application/vnd.ms-project"},
  {"mpv2", "video/mpeg"},
  {"mqv", "video/quicktime"},
  {"ms", "application/x-troff-ms"},
  {"msg", "application/vnd.ms-outlook"},
  {"msi", {"application/x-msi", "application/octet-stream"}},
  {"mso", "application/octet-stream"},
  {"mts", "video/vnd.dlna.mpeg-tts"},
  {"mtx", "application/xml"},
  {"mvb", "application/x-msmediaview"},
  {"mvc", "application/x-miva-compiled"},
  {"mxp", "application/x-mmxp"},
  {"nc", "application/x-netcdf"},
  {"nsc", "video/x-ms-asf"},
  {"nws", "message/rfc822"},
  {"ocx", "application/octet-stream"},
  {"oda", "application/oda"},
  {"odb", "application/vnd.oasis.opendocument.database"},
  {"odc", "application/vnd.oasis.opendocument.chart"},
  {"odf", "application/vnd.oasis.opendocument.formula"},
  {"odg", "application/vnd.oasis.opendocument.graphics"},
  {"odh", "text/plain"},
  {"odi", "application/vnd.oasis.opendocument.image"},
  {"odl", "text/plain"},
  {"odm", "application/vnd.oasis.opendocument.text-master"},
  {"odp", "application/vnd.oasis.opendocument.presentation"},
  {"ods", "application/vnd.oasis.opendocument.spreadsheet"},
  {"odt", "application/vnd.oasis.opendocument.text"},
  {"oga", "audio/ogg"},
  {"ogg", "audio/ogg"},
  {"ogv", "video/ogg"},
  {"ogx", "application/ogg"},
  {"one", "application/onenote"},
  {"onea", "application/onenote"},
  {"onepkg", "application/onenote"},
  {"onetmp", "application/onenote"},
  {"onetoc", "application/onenote"},
  {"onetoc2", "application/onenote"},
  {"opus", "audio/ogg"},
  {"orderedtest", "application/xml"},
  {"osdx", "application/opensearchdescription+xml"},
  {"otf", "application/font-sfnt"},
  {"otg", "application/vnd.oasis.opendocument.graphics-template"},
  {"oth", "application/vnd.oasis.opendocument.text-web"},
  {"otp", "application/vnd.oasis.opendocument.presentation-template"},
  {"ots", "application/vnd.oasis.opendocument.spreadsheet-template"},
  {"ott", "application/vnd.oasis.opendocument.text-template"},
  {"oxt", "application/vnd.openofficeorg.extension"},
  {"p10", "application/pkcs10"},
  {"p12", "application/x-pkcs12"},
  {"p7b", "application/x-pkcs7-certificates"},
  {"p7c", "application/pkcs7-mime"},
  {"p7m", "application/pkcs7-mime"},
  {"p7r", "application/x-pkcs7-certreqresp"},
  {"p7s", {"application/pkcs7-signature", "text/plain"}},
  {"pbm", "image/x-portable-bitmap"},
  {"pcast", "application/x-podcast"},
  {"pct", "image/pict"},
  {"pcx", "application/octet-stream"},
  {"pcz", "application/octet-stream"},
  {"pdf", "application/pdf"},
  {"pfb", "application/octet-stream"},
  {"pfm", "application/octet-stream"},
  {"pfx", "application/x-pkcs12"},
  {"pgm", "image/x-portable-graymap"},
  {"pic", "image/pict"},
  {"pict", "image/pict"},
  {"pkgdef", "text/plain"},
  {"pkgundef", "text/plain"},
  {"pko", "application/vnd.ms-pki.pko"},
  {"pls", "audio/scpls"},
  {"pma", "application/x-perfmon"},
  {"pmc", "application/x-perfmon"},
  {"pml", "application/x-perfmon"},
  {"pmr", "application/x-perfmon"},
  {"pmw", "application/x-perfmon"},
  {"png", "image/png"},
  {"pnm", "image/x-portable-anymap"},
  {"pnt", "image/x-macpaint"},
  {"pntg", "image/x-macpaint"},
  {"pnz", "image/png"},
  {"pot", "application/vnd.ms-powerpoint"},
  {"potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"},
  {"potx", "application/vnd.openxmlformats-officedocument.presentationml.template"},
  {"ppa", "application/vnd.ms-powerpoint"},
  {"ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"},
  {"ppm", "image/x-portable-pixmap"},
  {"pps", "application/vnd.ms-powerpoint"},
  {"ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"},
  {"ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
  {"ppt", "application/vnd.ms-powerpoint"},
  {"pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"},
  {"pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
  {"prf", "application/pics-rules"},
  {"prm", "application/octet-stream"},
  {"prx", "application/octet-stream"},
  {"ps", "application/postscript"},
  {"psc1", "application/PowerShell"},
  {"psd", "application/octet-stream"},
  {"psess", "application/xml"},
  {"psm", "application/octet-stream"},
  {"psp", "application/octet-stream"},
  {"pst", "application/vnd.ms-outlook"},
  {"pub", "application/x-mspublisher"},
  {"pwz", "application/vnd.ms-powerpoint"},
  {"qht", "text/x-html-insertion"},
  {"qhtm", "text/x-html-insertion"},
  {"qt", "video/quicktime"},
  {"qti", "image/x-quicktime"},
  {"qtif", "image/x-quicktime"},
  {"qtl", "application/x-quicktimeplayer"},
  {"qxd", "application/octet-stream"},
  {"ra", "audio/x-pn-realaudio"},
  {"ram", "audio/x-pn-realaudio"},
  {"rar", {"application/x-rar-compressed", "application/x-rar", "application/octet-stream"}},
  {"ras", "image/x-cmu-raster"},
  {"rat", "application/rat-file"},
  {"rc", "text/plain"},
  {"rc2", "text/plain"},
  {"rct", "text/plain"},
  {"rdlc", "application/xml"},
  {"reg", "text/plain"},
  {"resx", "application/xml"},
  {"rf", "image/vnd.rn-realflash"},
  {"rgb", "image/x-rgb"},
  {"rgs", "text/plain"},
  {"rm", "application/vnd.rn-realmedia"},
  {"rmi", "audio/mid"},
  {"rmp", "application/vnd.rn-rn_music_package"},
  {"roff", "application/x-troff"},
  {"rpm", "audio/x-pn-realaudio-plugin"},
  {"rqy", "text/x-ms-rqy"},
  {"rtf", {"application/rtf","application/msword", "text/richtext", "text/rtf"}},
  {"rtx", "text/richtext"},
  {"rvt", "application/octet-stream" },
  {"ruleset", "application/xml"},
  {"s", "text/plain"},
  {"safariextz", "application/x-safari-safariextz"},
  {"scd", "application/x-msschedule"},
  {"scr", "text/plain"},
  {"sct", "text/scriptlet"},
  {"sd2", "audio/x-sd2"},
  {"sdp", "application/sdp"},
  {"sea", "application/octet-stream"},
  {"searchConnector-ms", "application/windows-search-connector+xml"},
  {"setpay", "application/set-payment-initiation"},
  {"setreg", "application/set-registration-initiation"},
  {"settings", "application/xml"},
  {"sgimb", "application/x-sgimb"},
  {"sgml", "text/sgml"},
  {"sh", "application/x-sh"},
  {"shar", "application/x-shar"},
  {"shtml", "text/html"},
  {"sit", "application/x-stuffit"},
  {"sitemap", "application/xml"},
  {"skin", "application/xml"},
  {"skp", "application/x-koan" },
  {"sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12"},
  {"sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"},
  {"slk", "application/vnd.ms-excel"},
  {"sln", "text/plain"},
  {"slupkg-ms", "application/x-ms-license"},
  {"smd", "audio/x-smd"},
  {"smi", "application/octet-stream"},
  {"smx", "audio/x-smd"},
  {"smz", "audio/x-smd"},
  {"snd", "audio/basic"},
  {"snippet", "application/xml"},
  {"snp", "application/octet-stream"},
  {"sol", "text/plain"},
  {"sor", "text/plain"},
  {"spc", "application/x-pkcs7-certificates"},
  {"spl", "application/futuresplash"},
  {"spx", "audio/ogg"},
  {"src", "application/x-wais-source"},
  {"srf", "text/plain"},
  {"SSISDeploymentManifest", "text/xml"},
  {"ssm", "application/streamingmedia"},
  {"sst", "application/vnd.ms-pki.certstore"},
  {"stl", "application/vnd.ms-pki.stl"},
  {"sv4cpio", "application/x-sv4cpio"},
  {"sv4crc", "application/x-sv4crc"},
  {"svc", "application/xml"},
  {"svg", "image/svg+xml"},
  {"swf", "application/x-shockwave-flash"},
  {"step", "application/step"},
  {"stp", "application/step"},
  {"t", "application/x-troff"},
  {"tar", "application/x-tar"},
  {"tcl", "application/x-tcl"},
  {"testrunconfig", "application/xml"},
  {"testsettings", "application/xml"},
  {"tex", "application/x-tex"},
  {"texi", "application/x-texinfo"},
  {"texinfo", "application/x-texinfo"},
  {"tgz", "application/x-compressed"},
  {"thmx", "application/vnd.ms-officetheme"},
  {"thn", "application/octet-stream"},
  {"tif", {"image/tiff", "application/octet-stream"}},
  {"tiff", "image/tiff"},
  {"tlh", "text/plain"},
  {"tli", "text/plain"},
  {"toc", "application/octet-stream"},
  {"tr", "application/x-troff"},
  {"trm", "application/x-msterminal"},
  {"trx", "application/xml"},
  {"ts", "video/vnd.dlna.mpeg-tts"},
  {"tsv", "text/tab-separated-values"},
  {"ttf", "application/font-sfnt"},
  {"tts", "video/vnd.dlna.mpeg-tts"},
  {"txt", "text/plain"},
  {"u32", "application/octet-stream"},
  {"uls", "text/iuls"},
  {"user", "text/plain"},
  {"ustar", "application/x-ustar"},
  {"vb", "text/plain"},
  {"vbdproj", "text/plain"},
  {"vbk", "video/mpeg"},
  {"vbproj", "text/plain"},
  {"vbs", "text/vbscript"},
  {"vcf", {"text/x-vcard", "text/vcard"}},
  {"vcproj", "application/xml"},
  {"vcs", "text/plain"},
  {"vcxproj", "application/xml"},
  {"vddproj", "text/plain"},
  {"vdp", "text/plain"},
  {"vdproj", "text/plain"},
  {"vdx", "application/vnd.ms-visio.viewer"},
  {"vml", "text/xml"},
  {"vscontent", "application/xml"},
  {"vsct", "text/xml"},
  {"vsd", "application/vnd.visio"},
  {"vsi", "application/ms-vsi"},
  {"vsix", "application/vsix"},
  {"vsixlangpack", "text/xml"},
  {"vsixmanifest", "text/xml"},
  {"vsmdi", "application/xml"},
  {"vspscc", "text/plain"},
  {"vss", "application/vnd.visio"},
  {"vsscc", "text/plain"},
  {"vssettings", "text/xml"},
  {"vssscc", "text/plain"},
  {"vst", "application/vnd.visio"},
  {"vstemplate", "text/xml"},
  {"vsto", "application/x-ms-vsto"},
  {"vsw", "application/vnd.visio"},
  {"vsx", "application/vnd.visio"},
  {"vtx", "application/vnd.visio"},
  {"wav", "audio/wav"},
  {"wave", "audio/wav"},
  {"wax", "audio/x-ms-wax"},
  {"wbk", "application/msword"},
  {"wbmp", "image/vnd.wap.wbmp"},
  {"wcm", "application/vnd.ms-works"},
  {"wdb", "application/vnd.ms-works"},
  {"wdp", "image/vnd.ms-photo"},
  {"webarchive", "application/x-safari-webarchive"},
  {"webm", "video/webm"},
  {"webp", "image/webp"},
  {"webtest", "application/xml"},
  {"wiq", "application/xml"},
  {"wiz", "application/msword"},
  {"wks", "application/vnd.ms-works"},
  {"WLMP", "application/wlmoviemaker"},
  {"wlpginstall", "application/x-wlpg-detect"},
  {"wlpginstall3", "application/x-wlpg3-detect"},
  {"wm", "video/x-ms-wm"},
  {"wma", "audio/x-ms-wma"},
  {"wmd", "application/x-ms-wmd"},
  {"wmf", "application/x-msmetafile"},
  {"wml", "text/vnd.wap.wml"},
  {"wmlc", "application/vnd.wap.wmlc"},
  {"wmls", "text/vnd.wap.wmlscript"},
  {"wmlsc", "application/vnd.wap.wmlscriptc"},
  {"wmp", "video/x-ms-wmp"},
  {"wmv", "video/x-ms-wmv"},
  {"wmx", "video/x-ms-wmx"},
  {"wmz", "application/x-ms-wmz"},
  {"woff", "application/font-woff"},
  {"wpl", "application/vnd.ms-wpl"},
  {"wps", "application/vnd.ms-works"},
  {"wri", "application/x-mswrite"},
  {"wrl", "x-world/x-vrml"},
  {"wrz", "x-world/x-vrml"},
  {"wsc", "text/scriptlet"},
  {"wsdl", "text/xml"},
  {"wvx", "video/x-ms-wvx"},
  {"x", "application/directx"},
  {"xaf", "x-world/x-vrml"},
  {"xaml", "application/xaml+xml"},
  {"xap", "application/x-silverlight-app"},
  {"xbap", "application/x-ms-xbap"},
  {"xbm", "image/x-xbitmap"},
  {"xdr", "text/plain"},
  {"xht", "application/xhtml+xml"},
  {"xhtml", "application/xhtml+xml"},
  {"xla", "application/vnd.ms-excel"},
  {"xlam", "application/vnd.ms-excel.addin.macroEnabled.12"},
  {"xlc", "application/vnd.ms-excel"},
  {"xld", "application/vnd.ms-excel"},
  {"xlk", "application/vnd.ms-excel"},
  {"xll", "application/vnd.ms-excel"},
  {"xlm", "application/vnd.ms-excel"},
  {"xls", {
    "application/excel",
    "application/vnd.ms-excel",
    "application/vnd.ms-office",
    "application/x-excel",
    "application/octet-stream"
  }},
  {"xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"},
  {"xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"},
  {"xlsx", {
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    "application/vnd.ms-excel.12",
    "application/octet-stream"
  }},
  {"xlt", "application/vnd.ms-excel"},
  {"xltm", "application/vnd.ms-excel.template.macroEnabled.12"},
  {"xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"},
  {"xlw", "application/vnd.ms-excel"},
  {"xml", {"application/xml", "text/xml", "application/octet-stream"}},
  {"xmp", "application/octet-stream" },
  {"xmta", "application/xml"},
  {"xof", "x-world/x-vrml"},
  {"XOML", "text/plain"},
  {"xpm", "image/x-xpixmap"},
  {"xps", "application/vnd.ms-xpsdocument"},
  {"xrm-ms", "text/xml"},
  {"xsc", "application/xml"},
  {"xsd", "text/xml"},
  {"xsf", "text/xml"},
  {"xsl", "text/xml"},
  {"xslt", "text/xml"},
  {"xsn", "application/octet-stream"},
  {"xss", "application/xml"},
  {"xspf", "application/xspf+xml"},
  {"xtp", "application/octet-stream"},
  {"xwd", "image/x-xwindowdump"},
  {"z", "application/x-compress"},
  {"zip", {
    "application/zip",
    "application/x-zip-compressed",
    "application/octet-stream"
  }},
  {"zlib", "application/zlib"},
}

-- Used to match extension by content type
exports.reversed_extensions_map = {}

if #exports.reversed_extensions_map == 0 then
  -- Fill reversed extensions map
  for _,pair in ipairs(exports.full_extensions_map) do
    local ext, ct = pair[1], pair[2]

    if type(ct) == 'table' then
      for _,rct in ipairs(ct) do
        exports.reversed_extensions_map[rct] = ext
      end
    else
      exports.reversed_extensions_map[ct] = ext
    end
  end
end

return exports