logo

pleroma

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

steal_emoji_policy.ex (4729B)


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