From d1c4d0740467455b4fd158c9c3d1b79e500a79f9 Mon Sep 17 00:00:00 2001 From: Oneric Date: Fri, 8 Mar 2024 03:06:40 +0100 Subject: [PATCH] Convert StealEmoji to pack.json This will decouple filenames from shortcodes and allow more image formats to work instead of only those included in the auto-load glob. (Albeit we still saved other formats to disk, wasting space) Furthermore, this will allow us to make final URL paths infeasible to predict. --- lib/pleroma/emoji/pack.ex | 16 ++++- .../activity_pub/mrf/steal_emoji_policy.ex | 58 +++++++++++++------ 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex index 30ed5a8bd..f007cde65 100644 --- a/lib/pleroma/emoji/pack.ex +++ b/lib/pleroma/emoji/pack.ex @@ -92,7 +92,7 @@ defmodule Pleroma.Emoji.Pack do end) end - @spec add_file(t(), String.t(), Path.t(), Plug.Upload.t()) :: + @spec add_file(t(), String.t(), Path.t(), Plug.Upload.t() | binary()) :: {:ok, t()} | {:error, File.posix() | atom()} def add_file(%Pack{} = pack, _, _, %Plug.Upload{content_type: "application/zip"} = file) do @@ -140,6 +140,14 @@ defmodule Pleroma.Emoji.Pack do end def add_file(%Pack{} = pack, shortcode, filename, %Plug.Upload{} = file) do + try_add_file(pack, shortcode, filename, file) + end + + def add_file(%Pack{} = pack, shortcode, filename, filedata) when is_binary(filedata) do + try_add_file(pack, shortcode, filename, filedata) + end + + defp try_add_file(%Pack{} = pack, shortcode, filename, file) do with :ok <- validate_not_empty([shortcode, filename]), :ok <- validate_emoji_not_exists(shortcode), {:ok, updated_pack} <- do_add_file(pack, shortcode, filename, file) do @@ -485,6 +493,12 @@ defmodule Pleroma.Emoji.Pack do end end + defp save_file(file_data, pack, filename) when is_binary(file_data) do + file_path = Path.join(pack.path, filename) + create_subdirs(file_path) + File.write(file_path, file_data, [:binary]) + end + defp put_emoji(pack, shortcode, filename) do files = Map.put(pack.files, shortcode, filename) %{pack | files: files, files_count: length(Map.keys(files))} diff --git a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex index da6b8275d..ed421d93e 100644 --- a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex @@ -6,10 +6,41 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do require Logger alias Pleroma.Config + alias Pleroma.Emoji.Pack @moduledoc "Detect new emojis by their shortcode and steals them" @behaviour Pleroma.Web.ActivityPub.MRF.Policy + @pack_name "stolen" + + defp create_pack() do + with {:ok, pack} = Pack.create(@pack_name) do + Pack.save_metadata( + %{ + "description" => "Collection of emoji auto-stolen from other instances", + "homepage" => Pleroma.Web.Endpoint.url(), + "can-download" => false, + "share-files" => false + }, + pack + ) + end + end + + defp load_or_create_pack() do + case Pack.load_pack(@pack_name) do + {:ok, pack} -> {:ok, pack} + {:error, :enoent} -> create_pack() + e -> e + end + end + + defp add_emoji(shortcode, extension, filedata) do + {:ok, pack} = load_or_create_pack() + filename = shortcode <> "." <> extension + Pack.add_file(pack, shortcode, filename, filedata) + end + defp accept_host?(host), do: host in Config.get([:mrf_steal_emoji, :hosts], []) defp shortcode_matches?(shortcode, pattern) when is_binary(pattern) do @@ -33,15 +64,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do !valid_shortcode? or rejected_shortcode? or emoji_installed? end - defp steal_emoji(%{} = response, {shortcode, extension}, emoji_dir_path) do - file_path = Path.join(emoji_dir_path, shortcode <> "." <> extension) - - case File.write(file_path, response.body) do - :ok -> + defp steal_emoji(%{} = response, {shortcode, extension}) do + case add_emoji(shortcode, extension, response.body) do + {:ok, _} -> shortcode e -> - Logger.warning("MRF.StealEmojiPolicy: Failed to write to #{file_path}: #{inspect(e)}") + Logger.warning( + "MRF.StealEmojiPolicy: Failed to add #{shortcode}.#{extension}: #{inspect(e)}" + ) + nil end end @@ -56,7 +88,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do end end - defp maybe_steal_emoji({shortcode, url}, emoji_dir_path) do + defp maybe_steal_emoji({shortcode, url}) do url = Pleroma.Web.MediaProxy.url(url) with {:ok, %{status: status} = response} when status in 200..299 <- Pleroma.HTTP.get(url) do @@ -64,7 +96,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do extension = get_extension_if_safe(response) if byte_size(response.body) <= size_limit and extension do - steal_emoji(response, {shortcode, extension}, emoji_dir_path) + steal_emoji(response, {shortcode, extension}) else Logger.debug( "MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{size_limit} B)" @@ -86,18 +118,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do if host != Pleroma.Web.Endpoint.host() and accept_host?(host) do installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - emoji_dir_path = - Config.get( - [:mrf_steal_emoji, :path], - Path.join(Config.get([:instance, :static_dir]), "emoji/stolen") - ) - - File.mkdir_p(emoji_dir_path) - new_emojis = foreign_emojis |> Enum.reject(&reject_emoji?(&1, installed_emoji)) - |> Enum.map(&maybe_steal_emoji(&1, emoji_dir_path)) + |> Enum.map(&maybe_steal_emoji(&1)) |> Enum.filter(& &1) if !Enum.empty?(new_emojis) do