Merge branch 'following-relationships-optimizations' into 'develop'
[akkoma] / lib / pleroma / web / activity_pub / transmogrifier.ex
index 37e48574171e119b7a8cd4ca22dbf8f3eeb1333f..39feae285593400e358116a99fd9ffcd17de8840 100644 (file)
@@ -13,6 +13,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   alias Pleroma.Repo
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.ObjectValidator
+  alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
+  alias Pleroma.Web.ActivityPub.Pipeline
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.Federator
@@ -202,16 +205,46 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     |> Map.put("conversation", context)
   end
 
+  defp add_if_present(map, _key, nil), do: map
+
+  defp add_if_present(map, key, value) do
+    Map.put(map, key, value)
+  end
+
   def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachment) do
     attachments =
       Enum.map(attachment, fn data ->
-        media_type = data["mediaType"] || data["mimeType"]
-        href = data["url"] || data["href"]
-        url = [%{"type" => "Link", "mediaType" => media_type, "href" => href}]
+        url =
+          cond do
+            is_list(data["url"]) -> List.first(data["url"])
+            is_map(data["url"]) -> data["url"]
+            true -> nil
+          end
 
-        data
-        |> Map.put("mediaType", media_type)
-        |> Map.put("url", url)
+        media_type =
+          cond do
+            is_map(url) && is_binary(url["mediaType"]) -> url["mediaType"]
+            is_binary(data["mediaType"]) -> data["mediaType"]
+            is_binary(data["mimeType"]) -> data["mimeType"]
+            true -> nil
+          end
+
+        href =
+          cond do
+            is_map(url) && is_binary(url["href"]) -> url["href"]
+            is_binary(data["url"]) -> data["url"]
+            is_binary(data["href"]) -> data["href"]
+          end
+
+        attachment_url =
+          %{"href" => href}
+          |> add_if_present("mediaType", media_type)
+          |> add_if_present("type", Map.get(url || %{}, "type"))
+
+        %{"url" => [attachment_url]}
+        |> add_if_present("mediaType", media_type)
+        |> add_if_present("type", data["type"])
+        |> add_if_present("name", data["name"])
       end)
 
     Map.put(object, "attachment", attachments)
@@ -229,7 +262,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     Map.put(object, "url", url["href"])
   end
 
-  def fix_url(%{"type" => "Video", "url" => url} = object) when is_list(url) do
+  def fix_url(%{"type" => object_type, "url" => url} = object)
+      when object_type in ["Video", "Audio"] and is_list(url) do
     first_element = Enum.at(url, 0)
 
     link_element = Enum.find(url, fn x -> is_map(x) and x["mimeType"] == "text/html" end)
@@ -398,7 +432,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
         %{"type" => "Create", "object" => %{"type" => objtype} = object} = data,
         options
       )
-      when objtype in ["Article", "Event", "Note", "Video", "Page", "Question", "Answer"] do
+      when objtype in ["Article", "Event", "Note", "Video", "Page", "Question", "Answer", "Audio"] do
     actor = Containment.get_actor(data)
 
     data =
@@ -609,17 +643,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     |> handle_incoming(options)
   end
 
-  def handle_incoming(
-        %{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data,
-        _options
-      ) do
-    with actor <- Containment.get_actor(data),
-         {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
-         {:ok, object} <- get_obj_helper(object_id),
-         {:ok, activity, _object} <- ActivityPub.like(actor, object, id, false) do
+  def handle_incoming(%{"type" => "Like"} = data, _options) do
+    with {_, {:ok, cast_data_sym}} <-
+           {:casting_data,
+            data |> LikeValidator.cast_data() |> Ecto.Changeset.apply_action(:insert)},
+         cast_data = ObjectValidator.stringify_keys(Map.from_struct(cast_data_sym)),
+         :ok <- ObjectValidator.fetch_actor_and_object(cast_data),
+         {_, {:ok, cast_data}} <- {:ensure_context_presence, ensure_context_presence(cast_data)},
+         {_, {:ok, cast_data}} <-
+           {:ensure_recipients_presence, ensure_recipients_presence(cast_data)},
+         {_, {:ok, activity, _meta}} <-
+           {:common_pipeline, Pipeline.common_pipeline(cast_data, local: false)} do
       {:ok, activity}
     else
-      _e -> :error
+      e -> {:error, e}
     end
   end
 
@@ -1243,4 +1280,45 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   def maybe_fix_user_url(data), do: data
 
   def maybe_fix_user_object(data), do: maybe_fix_user_url(data)
+
+  defp ensure_context_presence(%{"context" => context} = data) when is_binary(context),
+    do: {:ok, data}
+
+  defp ensure_context_presence(%{"object" => object} = data) when is_binary(object) do
+    with %{data: %{"context" => context}} when is_binary(context) <- Object.normalize(object) do
+      {:ok, Map.put(data, "context", context)}
+    else
+      _ ->
+        {:error, :no_context}
+    end
+  end
+
+  defp ensure_context_presence(_) do
+    {:error, :no_context}
+  end
+
+  defp ensure_recipients_presence(%{"to" => [_ | _], "cc" => [_ | _]} = data),
+    do: {:ok, data}
+
+  defp ensure_recipients_presence(%{"object" => object} = data) do
+    case Object.normalize(object) do
+      %{data: %{"actor" => actor}} ->
+        data =
+          data
+          |> Map.put("to", [actor])
+          |> Map.put("cc", data["cc"] || [])
+
+        {:ok, data}
+
+      nil ->
+        {:error, :no_object}
+
+      _ ->
+        {:error, :no_actor}
+    end
+  end
+
+  defp ensure_recipients_presence(_) do
+    {:error, :no_object}
+  end
 end