mime.ex (3234B)
1 # Pleroma: A lightweight social networking server 2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> 3 # SPDX-License-Identifier: AGPL-3.0-only 4 5 defmodule Pleroma.MIME do 6 @moduledoc """ 7 Returns the mime-type of a binary and optionally a normalized file-name. 8 """ 9 @default "application/octet-stream" 10 @read_bytes 35 11 12 @spec file_mime_type(String.t(), String.t()) :: 13 {:ok, content_type :: String.t(), filename :: String.t()} | {:error, any()} | :error 14 def file_mime_type(path, filename) do 15 with {:ok, content_type} <- file_mime_type(path), 16 filename <- fix_extension(filename, content_type) do 17 {:ok, content_type, filename} 18 end 19 end 20 21 @spec file_mime_type(String.t()) :: {:ok, String.t()} | {:error, any()} | :error 22 def file_mime_type(filename) do 23 File.open(filename, [:read], fn f -> 24 check_mime_type(IO.binread(f, @read_bytes)) 25 end) 26 end 27 28 def bin_mime_type(binary, filename) do 29 with {:ok, content_type} <- bin_mime_type(binary), 30 filename <- fix_extension(filename, content_type) do 31 {:ok, content_type, filename} 32 end 33 end 34 35 @spec bin_mime_type(binary()) :: {:ok, String.t()} | :error 36 def bin_mime_type(<<head::binary-size(@read_bytes), _::binary>>) do 37 {:ok, check_mime_type(head)} 38 end 39 40 def bin_mime_type(_), do: :error 41 42 def mime_type(<<_::binary>>), do: {:ok, @default} 43 44 defp fix_extension(filename, content_type) do 45 parts = String.split(filename, ".") 46 47 new_filename = 48 if length(parts) > 1 do 49 Enum.drop(parts, -1) |> Enum.join(".") 50 else 51 Enum.join(parts) 52 end 53 54 cond do 55 content_type == "application/octet-stream" -> 56 filename 57 58 ext = List.first(MIME.extensions(content_type)) -> 59 new_filename <> "." <> ext 60 61 true -> 62 Enum.join([new_filename, String.split(content_type, "/") |> List.last()], ".") 63 end 64 end 65 66 defp check_mime_type(<<0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, _::binary>>) do 67 "image/png" 68 end 69 70 defp check_mime_type(<<0x47, 0x49, 0x46, 0x38, _, 0x61, _::binary>>) do 71 "image/gif" 72 end 73 74 defp check_mime_type(<<0xFF, 0xD8, 0xFF, _::binary>>) do 75 "image/jpeg" 76 end 77 78 defp check_mime_type(<<0x1A, 0x45, 0xDF, 0xA3, _::binary>>) do 79 "video/webm" 80 end 81 82 defp check_mime_type(<<0x00, 0x00, 0x00, _, 0x66, 0x74, 0x79, 0x70, _::binary>>) do 83 "video/mp4" 84 end 85 86 defp check_mime_type(<<0x49, 0x44, 0x33, _::binary>>) do 87 "audio/mpeg" 88 end 89 90 defp check_mime_type(<<255, 251, _, 68, 0, 0, 0, 0, _::binary>>) do 91 "audio/mpeg" 92 end 93 94 defp check_mime_type( 95 <<0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00, _::size(160), 0x80, 0x74, 0x68, 0x65, 96 0x6F, 0x72, 0x61, _::binary>> 97 ) do 98 "video/ogg" 99 end 100 101 defp check_mime_type(<<0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00, _::binary>>) do 102 "audio/ogg" 103 end 104 105 defp check_mime_type(<<"RIFF", _::binary-size(4), "WAVE", _::binary>>) do 106 "audio/wav" 107 end 108 109 defp check_mime_type(<<"RIFF", _::binary-size(4), "WEBP", _::binary>>) do 110 "image/webp" 111 end 112 113 defp check_mime_type(<<"RIFF", _::binary-size(4), "AVI.", _::binary>>) do 114 "video/avi" 115 end 116 117 defp check_mime_type(_) do 118 @default 119 end 120 end