summaryrefslogtreecommitdiff
path: root/lib/pleroma/web/activity_pub
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pleroma/web/activity_pub')
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex231
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf/drop_policy.ex1
-rw-r--r--lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex1
-rw-r--r--lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf/keyword_policy.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex1
-rw-r--r--lib/pleroma/web/activity_pub/mrf/noop_policy.ex1
-rw-r--r--lib/pleroma/web/activity_pub/mrf/normalize_markup.ex1
-rw-r--r--lib/pleroma/web/activity_pub/mrf/reject_non_public.ex1
-rw-r--r--lib/pleroma/web/activity_pub/mrf/simple_policy.ex1
-rw-r--r--lib/pleroma/web/activity_pub/mrf/tag_policy.ex13
-rw-r--r--lib/pleroma/web/activity_pub/mrf/user_allowlist.ex1
-rw-r--r--lib/pleroma/web/activity_pub/publisher.ex201
-rw-r--r--lib/pleroma/web/activity_pub/relay.ex4
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex40
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex2
-rw-r--r--lib/pleroma/web/activity_pub/views/user_view.ex7
19 files changed, 339 insertions, 175 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 3b71e0369..eba8f8018 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -4,7 +4,7 @@
defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.Activity
- alias Pleroma.Instances
+ alias Pleroma.Conversation
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Object.Fetcher
@@ -14,7 +14,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.User
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.Transmogrifier
- alias Pleroma.Web.Federator
alias Pleroma.Web.WebFinger
import Ecto.Query
@@ -23,8 +22,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
require Logger
- @httpoison Application.get_env(:pleroma, :httpoison)
-
# 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
@@ -140,7 +137,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end)
Notification.create_notifications(activity)
+
+ participations =
+ activity
+ |> Conversation.create_or_bump_for()
+ |> get_participations()
+
stream_out(activity)
+ stream_out_participations(participations)
{:ok, activity}
else
%Activity{} = activity ->
@@ -163,11 +167,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
+ defp get_participations({:ok, %{participations: participations}}), do: participations
+ defp get_participations(_), do: []
+
+ def stream_out_participations(participations) do
+ participations =
+ participations
+ |> Repo.preload(:user)
+
+ Enum.each(participations, fn participation ->
+ Pleroma.Web.Streamer.stream("participation", participation)
+ end)
+ end
+
def stream_out(activity) do
public = "https://www.w3.org/ns/activitystreams#Public"
if activity.data["type"] in ["Create", "Announce", "Delete"] do
- object = Object.normalize(activity)
Pleroma.Web.Streamer.stream("user", activity)
Pleroma.Web.Streamer.stream("list", activity)
@@ -179,6 +195,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
if activity.data["type"] in ["Create"] do
+ object = Object.normalize(activity)
+
object.data
|> Map.get("tag", [])
|> Enum.filter(fn tag -> is_bitstring(tag) end)
@@ -193,6 +211,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
else
+ # TODO: Write test, replace with visibility test
if !Enum.member?(activity.data["cc"] || [], public) &&
!Enum.member?(
activity.data["to"],
@@ -455,35 +474,44 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- def fetch_activities_for_context(context, opts \\ %{}) do
+ defp fetch_activities_for_context_query(context, opts) do
public = ["https://www.w3.org/ns/activitystreams#Public"]
recipients =
if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
- query = from(activity in Activity)
-
- query =
- query
- |> restrict_blocked(opts)
- |> restrict_recipients(recipients, opts["user"])
-
- query =
- from(
- activity in query,
- where:
- fragment(
- "?->>'type' = ? and ?->>'context' = ?",
- activity.data,
- "Create",
- activity.data,
- ^context
- ),
- order_by: [desc: :id]
+ from(activity in Activity)
+ |> restrict_blocked(opts)
+ |> restrict_recipients(recipients, opts["user"])
+ |> where(
+ [activity],
+ fragment(
+ "?->>'type' = ? and ?->>'context' = ?",
+ activity.data,
+ "Create",
+ activity.data,
+ ^context
)
- |> Activity.with_preloaded_object()
+ )
+ |> order_by([activity], desc: activity.id)
+ end
+
+ @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
+ def fetch_activities_for_context(context, opts \\ %{}) do
+ context
+ |> fetch_activities_for_context_query(opts)
+ |> Activity.with_preloaded_object()
+ |> Repo.all()
+ end
- Repo.all(query)
+ @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
+ Pleroma.FlakeId.t() | nil
+ def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
+ context
+ |> fetch_activities_for_context_query(opts)
+ |> limit(1)
+ |> select([a], a.id)
+ |> Repo.one()
end
def fetch_public_activities(opts \\ %{}) do
@@ -782,9 +810,30 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Activity.with_preloaded_object()
end
+ defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
+
+ defp maybe_preload_bookmarks(query, opts) do
+ query
+ |> Activity.with_preloaded_bookmark(opts["user"])
+ end
+
+ defp maybe_order(query, %{order: :desc}) do
+ query
+ |> order_by(desc: :id)
+ end
+
+ defp maybe_order(query, %{order: :asc}) do
+ query
+ |> order_by(asc: :id)
+ end
+
+ defp maybe_order(query, _), do: query
+
def fetch_activities_query(recipients, opts \\ %{}) do
Activity
|> maybe_preload_objects(opts)
+ |> maybe_preload_bookmarks(opts)
+ |> maybe_order(opts)
|> restrict_recipients(recipients, opts["user"])
|> restrict_tag(opts)
|> restrict_tag_reject(opts)
@@ -925,134 +974,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- def should_federate?(inbox, public) do
- if public do
- true
- else
- inbox_info = URI.parse(inbox)
- !Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host)
- end
- end
-
- defp recipients(actor, activity) do
- followers =
- if actor.follower_address in activity.recipients do
- {:ok, followers} = User.get_followers(actor)
- 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
-
- 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!()
-
- Federator.publish_single_ap(%{
- inbox: inbox,
- json: json,
- actor: actor,
- id: activity.data["id"],
- unreachable_since: unreachable_since
- })
- end)
- end
-
- def publish(actor, activity) do
- public = is_public?(activity)
- {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
-
- json = Jason.encode!(data)
-
- recipients(actor, activity)
- |> Enum.filter(&User.ap_enabled?/1)
- |> Enum.map(fn %{info: %{source_data: data}} ->
- (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
- end)
- |> Enum.uniq()
- |> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
- |> Instances.filter_reachable()
- |> Enum.each(fn {inbox, unreachable_since} ->
- Federator.publish_single_ap(%{
- inbox: inbox,
- json: json,
- actor: actor,
- id: activity.data["id"],
- unreachable_since: unreachable_since
- })
- end)
- end
-
- def publish_one(%{inbox: inbox, json: json, actor: actor, id: id} = params) do
- Logger.info("Federating #{id} to #{inbox}")
- host = URI.parse(inbox).host
-
- digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
-
- date =
- NaiveDateTime.utc_now()
- |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
-
- signature =
- Pleroma.Web.HTTPSignatures.sign(actor, %{
- host: host,
- "content-length": byte_size(json),
- digest: digest,
- date: date
- })
-
- with {:ok, %{status: code}} when code in 200..299 <-
- result =
- @httpoison.post(
- inbox,
- json,
- [
- {"Content-Type", "application/activity+json"},
- {"Date", date},
- {"signature", signature},
- {"digest", digest}
- ]
- ) do
- if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
- do: Instances.set_reachable(inbox)
-
- result
- else
- {_post_result, response} ->
- unless params[:unreachable_since], do: Instances.set_unreachable(inbox)
- {:error, response}
- end
- end
-
# filter out broken threads
def contain_broken_threads(%Activity{} = activity, %User{} = user) do
entire_thread_visible_for_user?(activity, user)
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 0b80566bf..c967ab7a9 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -155,7 +155,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do
with %User{} = recipient <- User.get_cached_by_nickname(nickname),
- %User{} = actor <- User.get_or_fetch_by_ap_id(params["actor"]),
+ {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(params["actor"]),
true <- Utils.recipient_in_message(recipient, actor, params),
params <- Utils.maybe_splice_recipient(recipient.ap_id, params) do
Federator.incoming_ap_doc(params)
diff --git a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
index 34665a3a6..87fa514c3 100644
--- a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
@@ -5,6 +5,8 @@
defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
alias Pleroma.User
+ @moduledoc "Prevent followbots from following with a bit of heuristic"
+
@behaviour Pleroma.Web.ActivityPub.MRF
# XXX: this should become User.normalize_by_ap_id() or similar, really.
diff --git a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
index a93ccf386..b8d38aae6 100644
--- a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
require Logger
+ @moduledoc "Drop and log everything received"
@behaviour Pleroma.Web.ActivityPub.MRF
@impl true
diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
index 895376c9d..15d8514be 100644
--- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
+++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
alias Pleroma.Object
+ @moduledoc "Ensure a re: is prepended on replies to a post with a Subject"
@behaviour Pleroma.Web.ActivityPub.MRF
@reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
index 6736f3cb9..a699f6a7e 100644
--- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
@@ -4,6 +4,8 @@
defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
alias Pleroma.User
+ @moduledoc "Block messages with too much mentions (configurable)"
+
@behaviour Pleroma.Web.ActivityPub.MRF
defp delist_message(message, threshold) when threshold > 0 do
diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
index e8dfba672..d5c341433 100644
--- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
@@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
+ @moduledoc "Reject or Word-Replace messages with a keyword or regex"
+
@behaviour Pleroma.Web.ActivityPub.MRF
defp string_matches?(string, _) when not is_binary(string) do
false
diff --git a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
index 081456046..f30fee0d5 100644
--- a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
+ @moduledoc "Ensure no content placeholder is present (such as the dot from mastodon)"
@behaviour Pleroma.Web.ActivityPub.MRF
@impl true
diff --git a/lib/pleroma/web/activity_pub/mrf/noop_policy.ex b/lib/pleroma/web/activity_pub/mrf/noop_policy.ex
index 40f37bdb1..c47cb3298 100644
--- a/lib/pleroma/web/activity_pub/mrf/noop_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/noop_policy.ex
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
+ @moduledoc "Does nothing (lets the messages go through unmodified)"
@behaviour Pleroma.Web.ActivityPub.MRF
@impl true
diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
index 3d13cdb32..9c87c6963 100644
--- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
+++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
+ @moduledoc "Scrub configured hypertext markup"
alias Pleroma.HTML
@behaviour Pleroma.Web.ActivityPub.MRF
diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
index 4197be847..ea3df1b4d 100644
--- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
+++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
alias Pleroma.User
+ @moduledoc "Rejects non-public (followers-only, direct) activities"
@behaviour Pleroma.Web.ActivityPub.MRF
@impl true
diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
index 798ba9687..2f105700b 100644
--- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
alias Pleroma.User
+ @moduledoc "Filter activities depending on their origin instance"
@behaviour Pleroma.Web.ActivityPub.MRF
defp check_accept(%{host: actor_host} = _actor_info, object) do
diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
index b242e44e6..b52be30e7 100644
--- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
@@ -5,6 +5,19 @@
defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF
+ @moduledoc """
+ Apply policies based on user tags
+
+ This policy applies policies on a user activities depending on their tags
+ on your instance.
+
+ - `mrf_tag:media-force-nsfw`: Mark as sensitive on presence of attachments
+ - `mrf_tag:media-strip`: Remove attachments
+ - `mrf_tag:force-unlisted`: Mark as unlisted (removes from the federated timeline)
+ - `mrf_tag:sandbox`: Remove from public (local and federated) timelines
+ - `mrf_tag:disable-remote-subscription`: Reject non-local follow requests
+ - `mrf_tag:disable-any-subscription`: Reject any follow requests
+ """
defp get_tags(%User{tags: tags}) when is_list(tags), do: tags
defp get_tags(_), do: []
diff --git a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex b/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex
index a3b1f8aa0..f5078d818 100644
--- a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex
+++ b/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
alias Pleroma.Config
+ @moduledoc "Accept-list of users from specified instances"
@behaviour Pleroma.Web.ActivityPub.MRF
defp filter_by_list(object, []), do: {:ok, object}
diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex
new file mode 100644
index 000000000..036ac892e
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/publisher.ex
@@ -0,0 +1,201 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.Publisher do
+ alias Pleroma.Activity
+ alias Pleroma.Config
+ alias Pleroma.Instances
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.Relay
+ alias Pleroma.Web.ActivityPub.Transmogrifier
+
+ import Pleroma.Web.ActivityPub.Visibility
+
+ @behaviour Pleroma.Web.Federator.Publisher
+
+ require Logger
+
+ @httpoison Application.get_env(:pleroma, :httpoison)
+
+ @moduledoc """
+ ActivityPub outgoing federation module.
+ """
+
+ @doc """
+ Determine if an activity can be represented by running it through Transmogrifier.
+ """
+ def is_representable?(%Activity{} = activity) do
+ with {:ok, _data} <- Transmogrifier.prepare_outgoing(activity.data) do
+ true
+ else
+ _e ->
+ false
+ end
+ end
+
+ @doc """
+ Publish a single message to a peer. Takes a struct with the following
+ parameters set:
+
+ * `inbox`: the inbox to publish to
+ * `json`: the JSON message body representing the ActivityPub message
+ * `actor`: the actor which is signing the message
+ * `id`: the ActivityStreams URI of the message
+ """
+ def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do
+ Logger.info("Federating #{id} to #{inbox}")
+ host = URI.parse(inbox).host
+
+ digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
+
+ date =
+ NaiveDateTime.utc_now()
+ |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
+
+ signature =
+ Pleroma.Web.HTTPSignatures.sign(actor, %{
+ host: host,
+ "content-length": byte_size(json),
+ digest: digest,
+ date: date
+ })
+
+ with {:ok, %{status: code}} when code in 200..299 <-
+ result =
+ @httpoison.post(
+ inbox,
+ json,
+ [
+ {"Content-Type", "application/activity+json"},
+ {"Date", date},
+ {"signature", signature},
+ {"digest", digest}
+ ]
+ ) do
+ if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
+ do: Instances.set_reachable(inbox)
+
+ result
+ else
+ {_post_result, response} ->
+ unless params[:unreachable_since], do: Instances.set_unreachable(inbox)
+ {:error, response}
+ end
+ end
+
+ defp should_federate?(inbox, public) do
+ if public do
+ true
+ else
+ inbox_info = URI.parse(inbox)
+ !Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host)
+ end
+ end
+
+ defp recipients(actor, activity) do
+ followers =
+ if actor.follower_address in activity.recipients do
+ {:ok, followers} = User.get_followers(actor)
+ 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
+ Logger.info(fn -> "Relaying #{activity.data["id"]} out" end)
+ Relay.publish(activity)
+ end
+
+ {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
+ json = Jason.encode!(data)
+
+ 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"]
+ end)
+ |> Enum.uniq()
+ |> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
+ |> Instances.filter_reachable()
+ |> Enum.each(fn {inbox, unreachable_since} ->
+ Pleroma.Web.Federator.Publisher.enqueue_one(
+ __MODULE__,
+ %{
+ inbox: inbox,
+ json: json,
+ actor: actor,
+ id: activity.data["id"],
+ unreachable_since: unreachable_since
+ }
+ )
+ end)
+ end
+
+ def gather_webfinger_links(%User{} = user) do
+ [
+ %{"rel" => "self", "type" => "application/activity+json", "href" => user.ap_id},
+ %{
+ "rel" => "self",
+ "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
+ "href" => user.ap_id
+ }
+ ]
+ end
+
+ def gather_nodeinfo_protocol_names, do: ["activitypub"]
+end
diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex
index a7a20ca37..93808517b 100644
--- a/lib/pleroma/web/activity_pub/relay.ex
+++ b/lib/pleroma/web/activity_pub/relay.ex
@@ -15,7 +15,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do
def follow(target_instance) do
with %User{} = local_user <- get_actor(),
- %User{} = target_user <- User.get_or_fetch_by_ap_id(target_instance),
+ {:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_instance),
{:ok, activity} <- ActivityPub.follow(local_user, target_user) do
Logger.info("relay: followed instance: #{target_instance}; id=#{activity.data["id"]}")
{:ok, activity}
@@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do
def unfollow(target_instance) do
with %User{} = local_user <- get_actor(),
- %User{} = target_user <- User.get_or_fetch_by_ap_id(target_instance),
+ {:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_instance),
{:ok, activity} <- ActivityPub.unfollow(local_user, target_user) do
Logger.info("relay: unfollowed instance: #{target_instance}: id=#{activity.data["id"]}")
{:ok, activity}
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 3b7193eaa..c4cc77a95 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -126,7 +126,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def fix_implicit_addressing(object, _), do: object
def fix_addressing(object) do
- %User{} = user = User.get_or_fetch_by_ap_id(object["actor"])
+ {:ok, %User{} = user} = User.get_or_fetch_by_ap_id(object["actor"])
followers_collection = User.ap_followers(user)
object
@@ -407,7 +407,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> fix_addressing
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
- %User{} = user <- User.get_or_fetch_by_ap_id(data["actor"]) do
+ {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do
object = fix_object(data["object"])
params = %{
@@ -436,7 +436,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data
) do
with %User{local: true} = followed <- User.get_cached_by_ap_id(followed),
- %User{} = follower <- User.get_or_fetch_by_ap_id(follower),
+ {:ok, %User{} = follower} <- User.get_or_fetch_by_ap_id(follower),
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
with deny_follow_blocked <- Pleroma.Config.get([:user, :deny_follow_blocked]),
{:user_blocked, false} <-
@@ -485,7 +485,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
%{"type" => "Accept", "object" => follow_object, "actor" => _actor, "id" => _id} = data
) do
with actor <- Containment.get_actor(data),
- %User{} = followed <- User.get_or_fetch_by_ap_id(actor),
+ {:ok, %User{} = followed} <- User.get_or_fetch_by_ap_id(actor),
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
@@ -511,7 +511,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
%{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => _id} = data
) do
with actor <- Containment.get_actor(data),
- %User{} = followed <- User.get_or_fetch_by_ap_id(actor),
+ {:ok, %User{} = followed} <- User.get_or_fetch_by_ap_id(actor),
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
@@ -535,7 +535,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
%{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data
) do
with actor <- Containment.get_actor(data),
- %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
+ {: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
{:ok, activity}
@@ -548,7 +548,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
%{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data
) do
with actor <- Containment.get_actor(data),
- %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
+ {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <- get_obj_helper(object_id),
public <- Visibility.is_public?(data),
{:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
@@ -603,7 +603,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
object_id = Utils.get_ap_id(object_id)
with actor <- Containment.get_actor(data),
- %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
+ {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <- get_obj_helper(object_id),
:ok <- Containment.contain_origin(actor.ap_id, object.data),
{:ok, activity} <- ActivityPub.delete(object, false) do
@@ -622,7 +622,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
} = data
) do
with actor <- Containment.get_actor(data),
- %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
+ {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <- get_obj_helper(object_id),
{:ok, activity, _} <- ActivityPub.unannounce(actor, object, id, false) do
{:ok, activity}
@@ -640,7 +640,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
} = _data
) do
with %User{local: true} = followed <- User.get_cached_by_ap_id(followed),
- %User{} = follower <- User.get_or_fetch_by_ap_id(follower),
+ {:ok, %User{} = follower} <- User.get_or_fetch_by_ap_id(follower),
{:ok, activity} <- ActivityPub.unfollow(follower, followed, id, false) do
User.unfollow(follower, followed)
{:ok, activity}
@@ -659,7 +659,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
) do
with true <- Pleroma.Config.get([:activitypub, :accept_blocks]),
%User{local: true} = blocked <- User.get_cached_by_ap_id(blocked),
- %User{} = blocker <- User.get_or_fetch_by_ap_id(blocker),
+ {:ok, %User{} = blocker} <- User.get_or_fetch_by_ap_id(blocker),
{:ok, activity} <- ActivityPub.unblock(blocker, blocked, id, false) do
User.unblock(blocker, blocked)
{:ok, activity}
@@ -673,7 +673,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
) do
with true <- Pleroma.Config.get([:activitypub, :accept_blocks]),
%User{local: true} = blocked = User.get_cached_by_ap_id(blocked),
- %User{} = blocker = User.get_or_fetch_by_ap_id(blocker),
+ {:ok, %User{} = blocker} = User.get_or_fetch_by_ap_id(blocker),
{:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do
User.unfollow(blocker, blocked)
User.block(blocker, blocked)
@@ -692,7 +692,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
} = data
) do
with actor <- Containment.get_actor(data),
- %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
+ {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <- get_obj_helper(object_id),
{:ok, activity, _, _} <- ActivityPub.unlike(actor, object, id, false) do
{:ok, activity}
@@ -859,10 +859,16 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Map.put("tag", tags ++ mentions)
end
+ def add_emoji_tags(%User{info: %{"emoji" => _emoji} = user_info} = object) do
+ user_info = add_emoji_tags(user_info)
+
+ object
+ |> Map.put(:info, user_info)
+ end
+
# TODO: we should probably send mtime instead of unix epoch time for updated
- def add_emoji_tags(object) do
+ def add_emoji_tags(%{"emoji" => emoji} = object) do
tags = object["tag"] || []
- emoji = object["emoji"] || []
out =
emoji
@@ -880,6 +886,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Map.put("tag", tags ++ out)
end
+ def add_emoji_tags(object) do
+ object
+ end
+
def set_conversation(object) do
Map.put(object, "conversation", object["context"])
end
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 581b9d1ab..236d1b4ac 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -682,7 +682,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"""
def fetch_ordered_collection(from, pages_left, acc \\ []) do
with {:ok, response} <- Tesla.get(from),
- {:ok, collection} <- Poison.decode(response.body) do
+ {:ok, collection} <- Jason.decode(response.body) do
case collection["type"] do
"OrderedCollection" ->
# If we've encountered the OrderedCollection and not the page,
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index 5926a3294..1254fdf6c 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -69,6 +69,11 @@ defmodule Pleroma.Web.ActivityPub.UserView do
endpoints = render("endpoints.json", %{user: user})
+ user_tags =
+ user
+ |> Transmogrifier.add_emoji_tags()
+ |> Map.get("tag", [])
+
%{
"id" => user.ap_id,
"type" => "Person",
@@ -87,7 +92,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"publicKeyPem" => public_key
},
"endpoints" => endpoints,
- "tag" => user.info.source_data["tag"] || []
+ "tag" => (user.info.source_data["tag"] || []) ++ user_tags
}
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))