logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma

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