From 36f2275dc9f6c58163e4e07f8ace9d75e96033c7 Mon Sep 17 00:00:00 2001
From: Ekaterina Vaartis <vaartis@cock.li>
Date: Wed, 11 Sep 2019 22:58:55 +0300
Subject: [PATCH] A feature for shareable emoji packs, use it in download_from
 & tests

---
 .../web/emoji_api/emoji_api_controller.ex     | 115 ++++++++++--------
 .../web/nodeinfo/nodeinfo_controller.ex       |   1 +
 test/web/emoji_api_controller_test.exs        |  22 ++++
 3 files changed, 88 insertions(+), 50 deletions(-)

diff --git a/lib/pleroma/web/emoji_api/emoji_api_controller.ex b/lib/pleroma/web/emoji_api/emoji_api_controller.ex
index a83f8af57..36ca2c804 100644
--- a/lib/pleroma/web/emoji_api/emoji_api_controller.ex
+++ b/lib/pleroma/web/emoji_api/emoji_api_controller.ex
@@ -153,64 +153,79 @@ 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
-    full_pack =
-      "#{address}/api/pleroma/emoji/packs/list"
+    shareable_packs_available =
+      "#{address}/nodeinfo/2.1.json"
       |> Tesla.get!()
       |> Map.get(:body)
       |> Jason.decode!()
-      |> Map.get(name)
+      |> Map.get("features")
+      |> Enum.member?("shareable_emoji_packs")
 
-    pack_info_res =
-      case full_pack["pack"] do
-        %{"share-files" => true, "can-download" => true, "download-sha256" => sha} ->
-          {:ok,
-           %{
-             sha: sha,
-             uri: "#{address}/api/pleroma/emoji/packs/download_shared/#{name}"
-           }}
+    if shareable_packs_available do
+      full_pack =
+        "#{address}/api/pleroma/emoji/packs/list"
+        |> Tesla.get!()
+        |> Map.get(:body)
+        |> Jason.decode!()
+        |> Map.get(name)
 
-        %{"fallback-src" => src, "fallback-src-sha256" => sha} when is_binary(src) ->
-          {:ok,
-           %{
-             sha: sha,
-             uri: src,
-             fallback: true
-           }}
+      pack_info_res =
+        case full_pack["pack"] do
+          %{"share-files" => true, "can-download" => true, "download-sha256" => sha} ->
+            {:ok,
+             %{
+               sha: sha,
+               uri: "#{address}/api/pleroma/emoji/packs/download_shared/#{name}"
+             }}
 
-        _ ->
-          {:error, "The pack was not set as shared and there is no fallback src to download from"}
+          %{"fallback-src" => src, "fallback-src-sha256" => sha} when is_binary(src) ->
+            {:ok,
+             %{
+               sha: sha,
+               uri: src,
+               fallback: true
+             }}
+
+          _ ->
+            {:error,
+             "The pack was not set as shared and there is no fallback src to download from"}
+        end
+
+      with {:ok, %{sha: sha, uri: uri} = pinfo} <- pack_info_res,
+           %{body: emoji_archive} <- Tesla.get!(uri),
+           {_, true} <- {:checksum, Base.decode16!(sha) == :crypto.hash(:sha256, emoji_archive)} do
+        local_name = data["as"] || name
+        pack_dir = Path.join(@emoji_dir_path, local_name)
+        File.mkdir_p!(pack_dir)
+
+        files = Enum.map(full_pack["files"], fn {_, path} -> to_charlist(path) end)
+        # Fallback cannot contain a pack.json file
+        files = if pinfo[:fallback], do: files, else: ['pack.json'] ++ files
+
+        {:ok, _} = :zip.unzip(emoji_archive, cwd: to_charlist(pack_dir), file_list: files)
+
+        # Fallback can't contain a pack.json file, since that would cause the fallback-src-sha256
+        # in it to depend on itself
+        if pinfo[:fallback] do
+          pack_file_path = Path.join(pack_dir, "pack.json")
+
+          File.write!(pack_file_path, Jason.encode!(full_pack, pretty: true))
+        end
+
+        json(conn, "ok")
+      else
+        {:error, e} ->
+          conn |> put_status(:internal_server_error) |> json(%{error: e})
+
+        {:checksum, _} ->
+          conn
+          |> put_status(:internal_server_error)
+          |> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"})
       end
-
-    with {:ok, %{sha: sha, uri: uri} = pinfo} <- pack_info_res,
-         %{body: emoji_archive} <- Tesla.get!(uri),
-         {_, true} <- {:checksum, Base.decode16!(sha) == :crypto.hash(:sha256, emoji_archive)} do
-      local_name = data["as"] || name
-      pack_dir = Path.join(@emoji_dir_path, local_name)
-      File.mkdir_p!(pack_dir)
-
-      files = Enum.map(full_pack["files"], fn {_, path} -> to_charlist(path) end)
-      # Fallback cannot contain a pack.json file
-      files = if pinfo[:fallback], do: files, else: ['pack.json'] ++ files
-
-      {:ok, _} = :zip.unzip(emoji_archive, cwd: to_charlist(pack_dir), file_list: files)
-
-      # Fallback can't contain a pack.json file, since that would cause the fallback-src-sha256
-      # in it to depend on itself
-      if pinfo[:fallback] do
-        pack_file_path = Path.join(pack_dir, "pack.json")
-
-        File.write!(pack_file_path, Jason.encode!(full_pack, pretty: true))
-      end
-
-      json(conn, "ok")
     else
-      {:error, e} ->
-        conn |> put_status(:internal_server_error) |> json(%{error: e})
-
-      {:checksum, _} ->
-        conn
-        |> put_status(:internal_server_error)
-        |> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"})
+      conn
+      |> put_status(:internal_server_error)
+      |> json(%{error: "The requested instance does not support sharing emoji packs"})
     end
   end
 
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
index ee14cfd6b..192984242 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
@@ -57,6 +57,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
         "mastodon_api_streaming",
         "polls",
         "pleroma_explicit_addressing",
+        "shareable_emoji_packs",
         if Config.get([:media_proxy, :enabled]) do
           "media_proxy"
         end,
diff --git a/test/web/emoji_api_controller_test.exs b/test/web/emoji_api_controller_test.exs
index 38d11cdce..1af4d3720 100644
--- a/test/web/emoji_api_controller_test.exs
+++ b/test/web/emoji_api_controller_test.exs
@@ -54,6 +54,12 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
     end)
 
     mock(fn
+      %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} ->
+        json(%{features: []})
+
+      %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
+        json(%{features: ["shareable_emoji_packs"]})
+
       %{
         method: :get,
         url: "https://example.com/api/pleroma/emoji/packs/list"
@@ -87,6 +93,22 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
 
     conn = build_conn() |> assign(:user, admin)
 
+    assert (conn
+            |> put_req_header("content-type", "application/json")
+            |> post(
+              emoji_api_path(
+                conn,
+                :download_from
+              ),
+              %{
+                instance_address: "https://old-instance",
+                pack_name: "test_pack",
+                as: "test_pack2"
+              }
+              |> Jason.encode!()
+            )
+            |> json_response(500))["error"] =~ "does not support"
+
     assert conn
            |> put_req_header("content-type", "application/json")
            |> post(