Merge branch 'docs/kyclos' into 'develop'
[akkoma] / lib / pleroma / web / pleroma_api / controllers / emoji_api_controller.ex
index b7eede6c9e729f5b6b1b912093843b414a9015bf..a2f6d2287f5a4f2966f2977532f979e0c22ba9d0 100644 (file)
@@ -1,8 +1,26 @@
 defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
   use Pleroma.Web, :controller
 
+  alias Pleroma.Plugs.OAuthScopesPlug
+
   require Logger
 
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write"], admin: true}
+    when action in [
+           :create,
+           :delete,
+           :download_from,
+           :list_from,
+           :import_from_fs,
+           :update_file,
+           :update_metadata
+         ]
+  )
+
+  plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
+
   def emoji_dir_path do
     Path.join(
       Pleroma.Config.get!([:instance, :static_dir]),
@@ -10,10 +28,31 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
     )
   end
 
+  @doc """
+  Lists packs from the remote instance.
+
+  Since JS cannot ask remote instances for their packs due to CPS, it has to
+  be done by the server
+  """
+  def list_from(conn, %{"instance_address" => address}) do
+    address = String.trim(address)
+
+    if shareable_packs_available(address) do
+      list_resp =
+        "#{address}/api/pleroma/emoji/packs" |> Tesla.get!() |> Map.get(:body) |> Jason.decode!()
+
+      json(conn, list_resp)
+    else
+      conn
+      |> put_status(:internal_server_error)
+      |> json(%{error: "The requested instance does not support sharing emoji packs"})
+    end
+  end
+
   @doc """
   Lists the packs available on the instance as JSON.
 
-  The information is public and does not require authentification. The format is
+  The information is public and does not require authentication. The format is
   a map of "pack directory name" to pack.json contents.
   """
   def list_packs(conn, _params) do
@@ -156,6 +195,22 @@ keeping it in cache for #{div(cache_ms, 1000)}s")
     end
   end
 
+  defp shareable_packs_available(address) do
+    "#{address}/.well-known/nodeinfo"
+    |> Tesla.get!()
+    |> Map.get(:body)
+    |> Jason.decode!()
+    |> Map.get("links")
+    |> List.last()
+    |> Map.get("href")
+    # Get the actual nodeinfo address and fetch it
+    |> Tesla.get!()
+    |> Map.get(:body)
+    |> Jason.decode!()
+    |> get_in(["metadata", "features"])
+    |> Enum.member?("shareable_emoji_packs")
+  end
+
   @doc """
   An admin endpoint to request downloading a pack named `pack_name` from the instance
   `instance_address`.
@@ -164,21 +219,9 @@ keeping it in cache for #{div(cache_ms, 1000)}s")
   from that instance, otherwise it will be downloaded from the fallback source, if there is one.
   """
   def download_from(conn, %{"instance_address" => address, "pack_name" => name} = data) do
-    shareable_packs_available =
-      "#{address}/.well-known/nodeinfo"
-      |> Tesla.get!()
-      |> Map.get(:body)
-      |> Jason.decode!()
-      |> List.last()
-      |> Map.get("href")
-      # Get the actual nodeinfo address and fetch it
-      |> Tesla.get!()
-      |> Map.get(:body)
-      |> Jason.decode!()
-      |> get_in(["metadata", "features"])
-      |> Enum.member?("shareable_emoji_packs")
-
-    if shareable_packs_available do
+    address = String.trim(address)
+
+    if shareable_packs_available(address) do
       full_pack =
         "#{address}/api/pleroma/emoji/packs/list"
         |> Tesla.get!()
@@ -530,11 +573,14 @@ keeping it in cache for #{div(cache_ms, 1000)}s")
   assumed to be emojis and stored in the new `pack.json` file.
   """
   def import_from_fs(conn, _params) do
-    with {:ok, results} <- File.ls(emoji_dir_path()) do
+    emoji_path = emoji_dir_path()
+
+    with {:ok, %{access: :read_write}} <- File.stat(emoji_path),
+         {:ok, results} <- File.ls(emoji_path) do
       imported_pack_names =
         results
         |> Enum.filter(fn file ->
-          dir_path = Path.join(emoji_dir_path(), file)
+          dir_path = Path.join(emoji_path, file)
           # Find the directories that do NOT have pack.json
           File.dir?(dir_path) and not File.exists?(Path.join(dir_path, "pack.json"))
         end)
@@ -542,6 +588,11 @@ keeping it in cache for #{div(cache_ms, 1000)}s")
 
       json(conn, imported_pack_names)
     else
+      {:ok, %{access: _}} ->
+        conn
+        |> put_status(:internal_server_error)
+        |> json(%{error: "Error: emoji pack directory must be writable"})
+
       {:error, _} ->
         conn
         |> put_status(:internal_server_error)