1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.Publisher do
7 alias Pleroma.Instances
9 alias Pleroma.Web.ActivityPub.Transmogrifier
11 import Pleroma.Web.ActivityPub.Visibility
13 @behaviour Pleroma.Web.Federator.Publisher
17 @httpoison Application.get_env(:pleroma, :httpoison)
20 ActivityPub outgoing federation module.
24 Determine if an activity can be represented by running it through Transmogrifier.
26 def is_representable?(%Activity{} = activity) do
27 with %{} = _data <- Transmogrifier.prepare_outgoing(activity.data) do
35 Publish a single message to a peer. Takes a struct with the following
38 * `inbox`: the inbox to publish to
39 * `json`: the JSON message body representing the ActivityPub message
40 * `actor`: the actor which is signing the message
41 * `id`: the ActivityStreams URI of the message
43 def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do
44 Logger.info("Federating #{id} to #{inbox}")
45 host = URI.parse(inbox).host
47 digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
50 NaiveDateTime.utc_now()
51 |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
54 Pleroma.Web.HTTPSignatures.sign(actor, %{
56 "content-length": byte_size(json),
61 with {:ok, %{status: code}} when code in 200..299 <-
67 {"Content-Type", "application/activity+json"},
69 {"signature", signature},
73 if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
74 do: Instances.set_reachable(inbox)
78 {_post_result, response} ->
79 unless params[:unreachable_since], do: Instances.set_unreachable(inbox)
84 defp should_federate?(inbox, public) do
88 inbox_info = URI.parse(inbox)
89 !Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host)
94 Publishes an activity to all relevant peers.
96 def publish(%User{} = actor, %Activity{} = activity) do
98 if actor.follower_address in activity.recipients do
99 {:ok, followers} = User.get_followers(actor)
100 followers |> Enum.filter(&(!&1.local))
105 public = is_public?(activity)
107 {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
108 json = Jason.encode!(data)
110 (Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers)
111 |> Enum.filter(fn user -> User.ap_enabled?(user) end)
112 |> Enum.map(fn %{info: %{source_data: data}} ->
113 (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
116 |> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
117 |> Instances.filter_reachable()
118 |> Enum.each(fn {inbox, unreachable_since} ->
119 Pleroma.Web.Federator.Publisher.enqueue_one(
125 id: activity.data["id"],
126 unreachable_since: unreachable_since