- 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
-
- def publish(actor, activity) do
- followers =
- if actor.follower_address in activity.recipients do
- {:ok, followers} = User.get_followers(actor)
- followers |> Enum.filter(&(!&1.local))
- else
- []
- end
-
- public = is_public?(activity)
-
- remote_inboxes =
- (Pleroma.Web.Salmon.remote_users(activity) ++ followers)
- |> 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)
-
- {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
- json = Jason.encode!(data)
-
- Enum.each(remote_inboxes, fn inbox ->
- Federator.enqueue(:publish_single_ap, %{
- inbox: inbox,
- json: json,
- actor: actor,
- id: activity.data["id"]
- })
- end)
- end
-
- def publish_one(%{inbox: inbox, json: json, actor: actor, id: id}) do
- Logger.info("Federating #{id} to #{inbox}")
- host = URI.parse(inbox).host
-
- digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
-
- signature =
- Pleroma.Web.HTTPSignatures.sign(actor, %{
- host: host,
- "content-length": byte_size(json),
- digest: digest
- })
-
- @httpoison.post(
- inbox,
- json,
- [
- {"Content-Type", "application/activity+json"},
- {"signature", signature},
- {"digest", digest}
- ]
- )
- end
-
- # TODO:
- # This will create a Create activity, which we need internally at the moment.
- def fetch_object_from_id(id) do
- if object = Object.get_cached_by_ap_id(id) do
- {:ok, object}
- else
- Logger.info("Fetching #{id} via AP")
-
- with {:ok, data} <- fetch_and_contain_remote_object_from_id(id),
- nil <- Object.normalize(data),
- params <- %{
- "type" => "Create",
- "to" => data["to"],
- "cc" => data["cc"],
- "actor" => data["actor"] || data["attributedTo"],
- "object" => data
- },
- :ok <- Transmogrifier.contain_origin(id, params),
- {:ok, activity} <- Transmogrifier.handle_incoming(params) do
- {:ok, Object.normalize(activity.data["object"])}
- else
- {:error, {:reject, nil}} ->
- {:reject, nil}
-
- object = %Object{} ->
- {:ok, object}
-
- _e ->
- Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
-
- case OStatus.fetch_activity_from_url(id) do
- {:ok, [activity | _]} -> {:ok, Object.normalize(activity.data["object"])}
- e -> e
- end
- end
- end
- end
-
- def fetch_and_contain_remote_object_from_id(id) do
- Logger.info("Fetching #{id} via AP")
-
- with true <- String.starts_with?(id, "http"),
- {:ok, %{body: body, status: code}} when code in 200..299 <-
- @httpoison.get(
- id,
- [{:Accept, "application/activity+json"}]
- ),
- {:ok, data} <- Jason.decode(body),
- :ok <- Transmogrifier.contain_origin_from_id(id, data) do
- {:ok, data}
- else
- e ->
- {:error, e}
- end
- end
-
- def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
- def is_public?(%Object{data: data}), do: is_public?(data)
- def is_public?(%Activity{data: data}), do: is_public?(data)
-
- def is_public?(data) do
- "https://www.w3.org/ns/activitystreams#Public" in (data["to"] ++ (data["cc"] || []))
- end
-
- def is_private?(activity) do
- !is_public?(activity) && Enum.any?(activity.data["to"], &String.contains?(&1, "/followers"))
- end
-
- def is_direct?(activity) do
- !is_public?(activity) && !is_private?(activity)
- end
-
- def visible_for_user?(activity, nil) do
- is_public?(activity)
- end
-
- def visible_for_user?(activity, user) do
- x = [user.ap_id | user.following]
- y = activity.data["to"] ++ (activity.data["cc"] || [])
- visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y))
- end
-
- # guard
- def entire_thread_visible_for_user?(nil, _user), do: false
-
- # child
- def entire_thread_visible_for_user?(
- %Activity{data: %{"object" => %{"inReplyTo" => parent_id}}} = tail,
- user
- )
- when is_binary(parent_id) do
- parent = Activity.get_in_reply_to_activity(tail)
- visible_for_user?(tail, user) && entire_thread_visible_for_user?(parent, user)
- end
-
- # root
- def entire_thread_visible_for_user?(tail, user), do: visible_for_user?(tail, user)
-