add license boilerplate to pleroma core
[akkoma] / lib / pleroma / web / activity_pub / transmogrifier.ex
index 1f886839e9c152cb11edfb9888bd5ba7643f008a..315571e1a4d92d2187b23a8b6c7857af94192947 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   @moduledoc """
   A module to handle coding from internal to wire ActivityPub and back.
@@ -37,9 +41,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   @doc """
   Checks that an imported AP object's actor matches the domain it came from.
   """
-  def contain_origin(id, %{"actor" => nil}), do: :error
+  def contain_origin(_id, %{"actor" => nil}), do: :error
 
-  def contain_origin(id, %{"actor" => actor} = params) do
+  def contain_origin(id, %{"actor" => _actor} = params) do
     id_uri = URI.parse(id)
     actor_uri = URI.parse(get_actor(params))
 
@@ -50,9 +54,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     end
   end
 
-  def contain_origin_from_id(id, %{"id" => nil}), do: :error
+  def contain_origin_from_id(_id, %{"id" => nil}), do: :error
 
-  def contain_origin_from_id(id, %{"id" => other_id} = params) do
+  def contain_origin_from_id(id, %{"id" => other_id} = _params) do
     id_uri = URI.parse(id)
     other_uri = URI.parse(other_id)
 
@@ -69,8 +73,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   def fix_object(object) do
     object
     |> fix_actor
-    |> fix_attachments
     |> fix_url
+    |> fix_attachments
     |> fix_context
     |> fix_in_reply_to
     |> fix_emoji
@@ -170,8 +174,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     attachments =
       attachment
       |> Enum.map(fn data ->
-        url = [%{"type" => "Link", "mediaType" => data["mediaType"], "href" => data["url"]}]
-        Map.put(data, "url", url)
+        media_type = data["mediaType"] || data["mimeType"]
+        href = data["url"] || data["href"]
+
+        url = [%{"type" => "Link", "mediaType" => media_type, "href" => href}]
+
+        data
+        |> Map.put("mediaType", media_type)
+        |> Map.put("url", url)
       end)
 
     object
@@ -190,7 +200,22 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     |> Map.put("url", url["href"])
   end
 
-  def fix_url(%{"url" => url} = object) when is_list(url) do
+  def fix_url(%{"type" => "Video", "url" => url} = object) when is_list(url) do
+    first_element = Enum.at(url, 0)
+
+    link_element =
+      url
+      |> Enum.filter(fn x -> is_map(x) end)
+      |> Enum.filter(fn x -> x["mimeType"] == "text/html" end)
+      |> Enum.at(0)
+
+    object
+    |> Map.put("attachment", [first_element])
+    |> Map.put("url", link_element["href"])
+  end
+
+  def fix_url(%{"type" => object_type, "url" => url} = object)
+      when object_type != "Video" and is_list(url) do
     first_element = Enum.at(url, 0)
 
     url_string =
@@ -266,6 +291,32 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
 
   def fix_content_map(object), do: object
 
+  defp mastodon_follow_hack(%{"id" => id, "actor" => follower_id}, followed) do
+    with true <- id =~ "follows",
+         %User{local: true} = follower <- User.get_cached_by_ap_id(follower_id),
+         %Activity{} = activity <- Utils.fetch_latest_follow(follower, followed) do
+      {:ok, activity}
+    else
+      _ -> {:error, nil}
+    end
+  end
+
+  defp mastodon_follow_hack(_, _), do: {:error, nil}
+
+  defp get_follow_activity(follow_object, followed) do
+    with object_id when not is_nil(object_id) <- Utils.get_ap_id(follow_object),
+         {_, %Activity{} = activity} <- {:activity, Activity.get_by_ap_id(object_id)} do
+      {:ok, activity}
+    else
+      # Can't find the activity. This might a Mastodon 2.3 "Accept"
+      {:activity, nil} ->
+        mastodon_follow_hack(follow_object, followed)
+
+      _ ->
+        {:error, nil}
+    end
+  end
+
   # disallow objects with bogus IDs
   def handle_incoming(%{"id" => nil}), do: :error
   def handle_incoming(%{"id" => ""}), do: :error
@@ -331,34 +382,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     end
   end
 
-  defp mastodon_follow_hack(%{"id" => id, "actor" => follower_id}, followed) do
-    with true <- id =~ "follows",
-         %User{local: true} = follower <- User.get_cached_by_ap_id(follower_id),
-         %Activity{} = activity <- Utils.fetch_latest_follow(follower, followed) do
-      {:ok, activity}
-    else
-      _ -> {:error, nil}
-    end
-  end
-
-  defp mastodon_follow_hack(_), do: {:error, nil}
-
-  defp get_follow_activity(follow_object, followed) do
-    with object_id when not is_nil(object_id) <- Utils.get_ap_id(follow_object),
-         {_, %Activity{} = activity} <- {:activity, Activity.get_by_ap_id(object_id)} do
-      {:ok, activity}
-    else
-      # Can't find the activity. This might a Mastodon 2.3 "Accept"
-      {:activity, nil} ->
-        mastodon_follow_hack(follow_object, followed)
-
-      _ ->
-        {:error, nil}
-    end
-  end
-
   def handle_incoming(
-        %{"type" => "Accept", "object" => follow_object, "actor" => actor, "id" => id} = data
+        %{"type" => "Accept", "object" => follow_object, "actor" => _actor, "id" => _id} = data
       ) do
     with actor <- get_actor(data),
          %User{} = followed <- User.get_or_fetch_by_ap_id(actor),
@@ -374,7 +399,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
              local: false
            }) do
       if not User.following?(follower, followed) do
-        {:ok, follower} = User.follow(follower, followed)
+        {:ok, _follower} = User.follow(follower, followed)
       end
 
       {:ok, activity}
@@ -384,7 +409,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   end
 
   def handle_incoming(
-        %{"type" => "Reject", "object" => follow_object, "actor" => actor, "id" => id} = data
+        %{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => _id} = data
       ) do
     with actor <- get_actor(data),
          %User{} = followed <- User.get_or_fetch_by_ap_id(actor),
@@ -408,7 +433,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   end
 
   def handle_incoming(
-        %{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = data
+        %{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data
       ) do
     with actor <- get_actor(data),
          %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
@@ -421,7 +446,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   end
 
   def handle_incoming(
-        %{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = data
+        %{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data
       ) do
     with actor <- get_actor(data),
          %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
@@ -447,7 +472,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
       update_data =
         new_user_data
         |> Map.take([:name, :bio, :avatar])
-        |> Map.put(:info, Map.merge(actor.info, %{"banner" => banner, "locked" => locked}))
+        |> Map.put(:info, %{"banner" => banner, "locked" => locked})
 
       actor
       |> User.upgrade_changeset(update_data)
@@ -467,15 +492,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     end
   end
 
-  # TODO: Make secure.
+  # TODO: We presently assume that any actor on the same origin domain as the object being
+  # deleted has the rights to delete that object.  A better way to validate whether or not
+  # the object should be deleted is to refetch the object URI, which should return either
+  # an error or a tombstone.  This would allow us to verify that a deletion actually took
+  # place.
   def handle_incoming(
-        %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = data
+        %{"type" => "Delete", "object" => object_id, "actor" => _actor, "id" => _id} = data
       ) do
     object_id = Utils.get_ap_id(object_id)
 
     with actor <- get_actor(data),
-         %User{} = _actor <- User.get_or_fetch_by_ap_id(actor),
+         %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
          {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id),
+         :ok <- contain_origin(actor.ap_id, object.data),
          {:ok, activity} <- ActivityPub.delete(object, false) do
       {:ok, activity}
     else
@@ -487,7 +517,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
         %{
           "type" => "Undo",
           "object" => %{"type" => "Announce", "object" => object_id},
-          "actor" => actor,
+          "actor" => _actor,
           "id" => id
         } = data
       ) do
@@ -515,7 +545,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
       User.unfollow(follower, followed)
       {:ok, activity}
     else
-      e -> :error
+      _e -> :error
     end
   end
 
@@ -534,12 +564,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
       User.unblock(blocker, blocked)
       {:ok, activity}
     else
-      e -> :error
+      _e -> :error
     end
   end
 
   def handle_incoming(
-        %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = data
+        %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = _data
       ) do
     with true <- Pleroma.Config.get([:activitypub, :accept_blocks]),
          %User{local: true} = blocked = User.get_cached_by_ap_id(blocked),
@@ -549,7 +579,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
       User.block(blocker, blocked)
       {:ok, activity}
     else
-      e -> :error
+      _e -> :error
     end
   end
 
@@ -557,7 +587,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
         %{
           "type" => "Undo",
           "object" => %{"type" => "Like", "object" => object_id},
-          "actor" => actor,
+          "actor" => _actor,
           "id" => id
         } = data
       ) do
@@ -845,10 +875,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   def upgrade_user_from_ap_id(ap_id, async \\ true) do
     with %User{local: false} = user <- User.get_by_ap_id(ap_id),
          {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id) do
-      data =
-        data
-        |> Map.put(:info, Map.merge(user.info, data[:info]))
-
       already_ap = User.ap_enabled?(user)
 
       {:ok, user} =