logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://anongit.hacktivis.me/git/pleroma.git/

steal_emoji_policy.ex (4777B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
  5. require Logger
  6. alias Pleroma.Config
  7. @moduledoc "Detect new emojis by their shortcode and steals them"
  8. @behaviour Pleroma.Web.ActivityPub.MRF.Policy
  9. defp accept_host?(host), do: host in Config.get([:mrf_steal_emoji, :hosts], [])
  10. defp shortcode_matches?(shortcode, pattern) when is_binary(pattern) do
  11. shortcode == pattern
  12. end
  13. defp shortcode_matches?(shortcode, pattern) do
  14. String.match?(shortcode, pattern)
  15. end
  16. defp reject_emoji?({shortcode, _url}, installed_emoji) do
  17. valid_shortcode? = String.match?(shortcode, ~r/^[a-zA-Z0-9_-]+$/)
  18. rejected_shortcode? =
  19. [:mrf_steal_emoji, :rejected_shortcodes]
  20. |> Config.get([])
  21. |> Enum.any?(fn pattern -> shortcode_matches?(shortcode, pattern) end)
  22. emoji_installed? = Enum.member?(installed_emoji, shortcode)
  23. !valid_shortcode? or rejected_shortcode? or emoji_installed?
  24. end
  25. defp steal_emoji({shortcode, url}, emoji_dir_path) do
  26. url = Pleroma.Web.MediaProxy.url(url)
  27. with {:ok, %{status: status} = response} when status in 200..299 <- Pleroma.HTTP.get(url) do
  28. size_limit = Config.get([:mrf_steal_emoji, :size_limit], 50_000)
  29. if byte_size(response.body) <= size_limit do
  30. extension =
  31. url
  32. |> URI.parse()
  33. |> Map.get(:path)
  34. |> Path.basename()
  35. |> Path.extname()
  36. extension = if extension == "", do: ".png", else: extension
  37. shortcode = Path.basename(shortcode)
  38. file_path = Path.join(emoji_dir_path, shortcode <> extension)
  39. case File.write(file_path, response.body) do
  40. :ok ->
  41. shortcode
  42. e ->
  43. Logger.warning("MRF.StealEmojiPolicy: Failed to write to #{file_path}: #{inspect(e)}")
  44. nil
  45. end
  46. else
  47. Logger.debug(
  48. "MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{size_limit} B)"
  49. )
  50. nil
  51. end
  52. else
  53. e ->
  54. Logger.warning("MRF.StealEmojiPolicy: Failed to fetch #{url}: #{inspect(e)}")
  55. nil
  56. end
  57. end
  58. @impl true
  59. def filter(%{"object" => %{"emoji" => foreign_emojis, "actor" => actor}} = activity) do
  60. host = URI.parse(actor).host
  61. if host != Pleroma.Web.Endpoint.host() and accept_host?(host) do
  62. installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end)
  63. emoji_dir_path =
  64. Config.get(
  65. [:mrf_steal_emoji, :path],
  66. Path.join(Config.get([:instance, :static_dir]), "emoji/stolen")
  67. )
  68. File.mkdir_p(emoji_dir_path)
  69. new_emojis =
  70. foreign_emojis
  71. |> Enum.reject(&reject_emoji?(&1, installed_emoji))
  72. |> Enum.map(&steal_emoji(&1, emoji_dir_path))
  73. |> Enum.filter(& &1)
  74. if !Enum.empty?(new_emojis) do
  75. Logger.info("Stole new emojis: #{inspect(new_emojis)}")
  76. Pleroma.Emoji.reload()
  77. end
  78. end
  79. {:ok, activity}
  80. end
  81. def filter(activity), do: {:ok, activity}
  82. @impl true
  83. @spec config_description :: %{
  84. children: [
  85. %{
  86. description: <<_::272, _::_*256>>,
  87. key: :hosts | :rejected_shortcodes | :size_limit,
  88. suggestions: [any(), ...],
  89. type: {:list, :string} | {:list, :string} | :integer
  90. },
  91. ...
  92. ],
  93. description: <<_::448>>,
  94. key: :mrf_steal_emoji,
  95. label: <<_::80>>,
  96. related_policy: <<_::352>>
  97. }
  98. def config_description do
  99. %{
  100. key: :mrf_steal_emoji,
  101. related_policy: "Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy",
  102. label: "MRF Emojis",
  103. description: "Steals emojis from selected instances when it sees them.",
  104. children: [
  105. %{
  106. key: :hosts,
  107. type: {:list, :string},
  108. description: "List of hosts to steal emojis from",
  109. suggestions: [""]
  110. },
  111. %{
  112. key: :rejected_shortcodes,
  113. type: {:list, :string},
  114. description: """
  115. A list of patterns or matches to reject shortcodes with.
  116. Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
  117. """,
  118. suggestions: ["foo", ~r/foo/]
  119. },
  120. %{
  121. key: :size_limit,
  122. type: :integer,
  123. description: "File size limit (in bytes), checked before an emoji is saved to the disk",
  124. suggestions: ["100000"]
  125. }
  126. ]
  127. }
  128. end
  129. @impl true
  130. def describe do
  131. {:ok, %{}}
  132. end
  133. end