diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/pleroma/list.ex | 23 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 57 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/publisher.ex | 63 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/transmogrifier.ex | 5 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/common_api.ex | 29 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/utils.ex | 9 | ||||
| -rw-r--r-- | lib/pleroma/web/salmon/salmon.ex | 25 | 
7 files changed, 164 insertions, 47 deletions
| diff --git a/lib/pleroma/list.ex b/lib/pleroma/list.ex index a5b1cad68..16955b3b5 100644 --- a/lib/pleroma/list.ex +++ b/lib/pleroma/list.ex @@ -16,6 +16,7 @@ defmodule Pleroma.List do      belongs_to(:user, User, type: Pleroma.FlakeId)      field(:title, :string)      field(:following, {:array, :string}, default: []) +    field(:ap_id, :string)      timestamps()    end @@ -55,6 +56,10 @@ defmodule Pleroma.List do      Repo.one(query)    end +  def get_by_ap_id(ap_id) do +    Repo.get_by(__MODULE__, ap_id: ap_id) +  end +    def get_following(%Pleroma.List{following: following} = _list) do      q =        from( @@ -105,7 +110,14 @@ defmodule Pleroma.List do    def create(title, %User{} = creator) do      list = %Pleroma.List{user_id: creator.id, title: title} -    Repo.insert(list) + +    Repo.transaction(fn -> +      list = Repo.insert!(list) + +      list +      |> change(ap_id: "#{creator.ap_id}/lists/#{list.id}") +      |> Repo.update!() +    end)    end    def follow(%Pleroma.List{following: following} = list, %User{} = followed) do @@ -125,4 +137,13 @@ defmodule Pleroma.List do      |> follow_changeset(attrs)      |> Repo.update()    end + +  def memberships(%User{follower_address: follower_address}) do +    Pleroma.List +    |> where([l], ^follower_address in l.following) +    |> select([l], l.ap_id) +    |> Repo.all() +  end + +  def memberships(_), do: []  end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 41b55bbab..20f72e676 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -26,19 +26,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    # For Announce activities, we filter the recipients based on following status for any actors    # that match actual users.  See issue #164 for more information about why this is necessary.    defp get_recipients(%{"type" => "Announce"} = data) do -    to = data["to"] || [] -    cc = data["cc"] || [] +    to = Map.get(data, "to", []) +    cc = Map.get(data, "cc", []) +    bcc = Map.get(data, "bcc", [])      actor = User.get_cached_by_ap_id(data["actor"])      recipients = -      (to ++ cc) -      |> Enum.filter(fn recipient -> +      Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->          case User.get_cached_by_ap_id(recipient) do -          nil -> -            true - -          user -> -            User.following?(user, actor) +          nil -> true +          user -> User.following?(user, actor)          end        end) @@ -46,17 +43,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    end    defp get_recipients(%{"type" => "Create"} = data) do -    to = data["to"] || [] -    cc = data["cc"] || [] -    actor = data["actor"] || [] -    recipients = (to ++ cc ++ [actor]) |> Enum.uniq() +    to = Map.get(data, "to", []) +    cc = Map.get(data, "cc", []) +    bcc = Map.get(data, "bcc", []) +    actor = Map.get(data, "actor", []) +    recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()      {recipients, to, cc}    end    defp get_recipients(data) do -    to = data["to"] || [] -    cc = data["cc"] || [] -    recipients = to ++ cc +    to = Map.get(data, "to", []) +    cc = Map.get(data, "cc", []) +    bcc = Map.get(data, "bcc", []) +    recipients = Enum.concat([to, cc, bcc])      {recipients, to, cc}    end @@ -896,13 +895,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    defp maybe_order(query, _), do: query    def fetch_activities_query(recipients, opts \\ %{}) do -    base_query = from(activity in Activity) -      config = %{        skip_thread_containment: Config.get([:instance, :skip_thread_containment])      } -    base_query +    Activity      |> maybe_preload_objects(opts)      |> maybe_preload_bookmarks(opts)      |> maybe_set_thread_muted_field(opts) @@ -931,11 +928,31 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    end    def fetch_activities(recipients, opts \\ %{}) do -    fetch_activities_query(recipients, opts) +    list_memberships = Pleroma.List.memberships(opts["user"]) + +    fetch_activities_query(recipients ++ list_memberships, opts)      |> Pagination.fetch_paginated(opts)      |> Enum.reverse() +    |> maybe_update_cc(list_memberships, opts["user"])    end +  defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id}) +       when is_list(list_memberships) and length(list_memberships) > 0 do +    Enum.map(activities, fn +      %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 -> +        if Enum.any?(bcc, &(&1 in list_memberships)) do +          update_in(activity.data["cc"], &[user_ap_id | &1]) +        else +          activity +        end + +      activity -> +        activity +    end) +  end + +  defp maybe_update_cc(activities, _, _), do: activities +    def fetch_activities_bounded_query(query, recipients, recipients_with_public) do      from(activity in query,        where: diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index a05e03263..b7dc90caa 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -92,18 +92,67 @@ defmodule Pleroma.Web.ActivityPub.Publisher do      end    end -  @doc """ -  Publishes an activity to all relevant peers. -  """ -  def publish(%User{} = actor, %Activity{} = activity) do -    remote_followers = +  defp recipients(actor, activity) do +    followers =        if actor.follower_address in activity.recipients do          {:ok, followers} = User.get_followers(actor) -        followers |> Enum.filter(&(!&1.local)) +        Enum.filter(followers, &(!&1.local))        else          []        end +    Pleroma.Web.Salmon.remote_users(actor, activity) ++ followers +  end + +  defp get_cc_ap_ids(ap_id, recipients) do +    host = Map.get(URI.parse(ap_id), :host) + +    recipients +    |> Enum.filter(fn %User{ap_id: ap_id} -> Map.get(URI.parse(ap_id), :host) == host end) +    |> Enum.map(& &1.ap_id) +  end + +  @doc """ +  Publishes an activity with BCC to all relevant peers. +  """ + +  def publish(actor, %{data: %{"bcc" => bcc}} = activity) when is_list(bcc) and bcc != [] do +    public = is_public?(activity) +    {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) + +    recipients = recipients(actor, activity) + +    recipients +    |> Enum.filter(&User.ap_enabled?/1) +    |> Enum.map(fn %{info: %{source_data: data}} -> data["inbox"] end) +    |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) +    |> Instances.filter_reachable() +    |> Enum.each(fn {inbox, unreachable_since} -> +      %User{ap_id: ap_id} = +        Enum.find(recipients, fn %{info: %{source_data: data}} -> data["inbox"] == inbox end) + +      cc = get_cc_ap_ids(ap_id, recipients) + +      json = +        data +        |> Map.put("cc", cc) +        |> Map.put("directMessage", true) +        |> Jason.encode!() + +      Pleroma.Web.Federator.Publisher.enqueue_one(__MODULE__, %{ +        inbox: inbox, +        json: json, +        actor: actor, +        id: activity.data["id"], +        unreachable_since: unreachable_since +      }) +    end) +  end + +  @doc """ +  Publishes an activity to all relevant peers. +  """ +  def publish(%User{} = actor, %Activity{} = activity) do      public = is_public?(activity)      if public && Config.get([:instance, :allow_relay]) do @@ -114,7 +163,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do      {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)      json = Jason.encode!(data) -    (Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers) +    recipients(actor, activity)      |> Enum.filter(fn user -> User.ap_enabled?(user) end)      |> Enum.map(fn %{info: %{source_data: data}} ->        (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index e34fe6611..ad741122f 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -814,13 +814,16 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do    def prepare_outgoing(%{"type" => "Create", "object" => object_id} = data) do      object = -      Object.normalize(object_id).data +      object_id +      |> Object.normalize() +      |> Map.get(:data)        |> prepare_object      data =        data        |> Map.put("object", object)        |> Map.merge(Utils.make_json_ld_header()) +      |> Map.delete("bcc")      {:ok, data}    end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index f1450b113..8e3892bdf 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -175,6 +175,11 @@ defmodule Pleroma.Web.CommonAPI do        when visibility in ~w{public unlisted private direct},        do: {visibility, get_replied_to_visibility(in_reply_to)} +  def get_visibility(%{"visibility" => "list:" <> list_id}, in_reply_to) do +    visibility = {:list, String.to_integer(list_id)} +    {visibility, get_replied_to_visibility(in_reply_to)} +  end +    def get_visibility(_, in_reply_to) when not is_nil(in_reply_to) do      visibility = get_replied_to_visibility(in_reply_to)      {visibility, visibility} @@ -210,6 +215,7 @@ defmodule Pleroma.Web.CommonAPI do           addressed_users <- get_addressed_users(mentioned_users, data["to"]),           {poll, poll_emoji} <- make_poll_data(data),           {to, cc} <- get_to_and_cc(user, addressed_users, in_reply_to, visibility), +         bcc <- bcc_for_list(user, visibility),           context <- make_context(in_reply_to),           cw <- data["spoiler_text"] || "",           sensitive <- data["sensitive"] || Enum.member?(tags, {"#nsfw", "nsfw"}), @@ -235,19 +241,16 @@ defmodule Pleroma.Web.CommonAPI do               "emoji",               Map.merge(Formatter.get_emoji_map(full_payload), poll_emoji)             ) do -      res = -        ActivityPub.create( -          %{ -            to: to, -            actor: user, -            context: context, -            object: object, -            additional: %{"cc" => cc, "directMessage" => visibility == "direct"} -          }, -          Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false -        ) - -      res +      ActivityPub.create( +        %{ +          to: to, +          actor: user, +          context: context, +          object: object, +          additional: %{"cc" => cc, "bcc" => bcc, "directMessage" => visibility == "direct"} +        }, +        Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false +      )      else        {:private_to_public, true} ->          {:error, dgettext("errors", "The message visibility must be direct")} diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 8e482eef7..d4bfdd7e4 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -100,12 +100,21 @@ defmodule Pleroma.Web.CommonAPI.Utils do      end    end +  def get_to_and_cc(_user, _mentions, _inReplyTo, _), do: {[], []} +    def get_addressed_users(_, to) when is_list(to) do      User.get_ap_ids_by_nicknames(to)    end    def get_addressed_users(mentioned_users, _), do: mentioned_users +  def bcc_for_list(user, {:list, list_id}) do +    list = Pleroma.List.get(list_id, user) +    [list.ap_id] +  end + +  def bcc_for_list(_, _), do: [] +    def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_in}} = data)        when is_list(options) do      %{max_expiration: max_expiration, min_expiration: min_expiration} = diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index e96e4e1e4..9b01ebcc6 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -123,11 +123,26 @@ defmodule Pleroma.Web.Salmon do      {:ok, salmon}    end -  def remote_users(%{data: %{"to" => to} = data}) do -    to = to ++ (data["cc"] || []) +  def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do +    cc = Map.get(data, "cc", []) + +    bcc = +      data +      |> Map.get("bcc", []) +      |> Enum.reduce([], fn ap_id, bcc -> +        case Pleroma.List.get_by_ap_id(ap_id) do +          %Pleroma.List{user_id: ^user_id} = list -> +            {:ok, following} = Pleroma.List.get_following(list) +            bcc ++ Enum.map(following, & &1.ap_id) + +          _ -> +            bcc +        end +      end) -    to -    |> Enum.map(fn id -> User.get_cached_by_ap_id(id) end) +    [to, cc, bcc] +    |> Enum.concat() +    |> Enum.map(&User.get_cached_by_ap_id/1)      |> Enum.filter(fn user -> user && !user.local end)    end @@ -191,7 +206,7 @@ defmodule Pleroma.Web.Salmon do        {:ok, private, _} = Keys.keys_from_pem(keys)        {:ok, feed} = encode(private, feed) -      remote_users = remote_users(activity) +      remote_users = remote_users(user, activity)        salmon_urls = Enum.map(remote_users, & &1.info.salmon)        reachable_urls_metadata = Instances.filter_reachable(salmon_urls) | 
