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.ex60
-rw-r--r--lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex57
-rw-r--r--lib/pleroma/web/activity_pub/relay.ex2
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex52
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex24
-rw-r--r--lib/pleroma/web/activity_pub/views/object_view.ex2
-rw-r--r--lib/pleroma/web/activity_pub/views/user_view.ex4
7 files changed, 163 insertions, 38 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 487d4c84a..6b4682e35 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -100,7 +100,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def stream_out(activity) do
public = "https://www.w3.org/ns/activitystreams#Public"
- if activity.data["type"] in ["Create", "Announce"] do
+ if activity.data["type"] in ["Create", "Announce", "Delete"] do
Pleroma.Web.Streamer.stream("user", activity)
Pleroma.Web.Streamer.stream("list", activity)
@@ -111,16 +111,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
Pleroma.Web.Streamer.stream("public:local", activity)
end
- activity.data["object"]
- |> Map.get("tag", [])
- |> Enum.filter(fn tag -> is_bitstring(tag) end)
- |> Enum.map(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
+ if activity.data["type"] in ["Create"] do
+ activity.data["object"]
+ |> Map.get("tag", [])
+ |> Enum.filter(fn tag -> is_bitstring(tag) end)
+ |> Enum.map(fn tag -> Pleroma.Web.Streamer.stream("hashtag:" <> tag, activity) end)
- if activity.data["object"]["attachment"] != [] do
- Pleroma.Web.Streamer.stream("public:media", activity)
+ if activity.data["object"]["attachment"] != [] do
+ Pleroma.Web.Streamer.stream("public:media", activity)
- if activity.local do
- Pleroma.Web.Streamer.stream("public:local:media", activity)
+ if activity.local do
+ Pleroma.Web.Streamer.stream("public:local:media", activity)
+ end
end
end
else
@@ -146,8 +148,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
additional
),
{:ok, activity} <- insert(create_data, local),
- :ok <- maybe_federate(activity),
- {:ok, _actor} <- User.increase_note_count(actor) do
+ # Changing note count prior to enqueuing federation task in order to avoid race conditions on updating user.info
+ {:ok, _actor} <- User.increase_note_count(actor),
+ :ok <- maybe_federate(activity) do
{:ok, activity}
end
end
@@ -232,10 +235,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
%User{ap_id: _} = user,
%Object{data: %{"id" => _}} = object,
activity_id \\ nil,
- local \\ true
+ local \\ true,
+ public \\ true
) do
with true <- is_public?(object),
- announce_data <- make_announce_data(user, object, activity_id),
+ announce_data <- make_announce_data(user, object, activity_id, public),
{:ok, activity} <- insert(announce_data, local),
{:ok, object} <- add_announce_to_object(activity, object),
:ok <- maybe_federate(activity) do
@@ -293,8 +297,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
with {:ok, _} <- Object.delete(object),
{:ok, activity} <- insert(data, local),
- :ok <- maybe_federate(activity),
- {:ok, _actor} <- User.decrease_note_count(user) do
+ # Changing note count prior to enqueuing federation task in order to avoid race conditions on updating user.info
+ {:ok, _actor} <- User.decrease_note_count(user),
+ :ok <- maybe_federate(activity) do
{:ok, activity}
end
end
@@ -413,6 +418,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Enum.reverse()
end
+ defp restrict_since(query, %{"since_id" => ""}), do: query
+
defp restrict_since(query, %{"since_id" => since_id}) do
from(activity in query, where: activity.id > ^since_id)
end
@@ -468,6 +475,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_local(query, _), do: query
+ defp restrict_max(query, %{"max_id" => ""}), do: query
+
defp restrict_max(query, %{"max_id" => max_id}) do
from(activity in query, where: activity.id < ^max_id)
end
@@ -804,13 +813,24 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- def is_public?(%Object{data: %{"type" => "Tombstone"}}) do
- false
+ 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?(%{"directMessage" => true}), do: false
+
+ 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_public?(activity) do
- "https://www.w3.org/ns/activitystreams#Public" in (activity.data["to"] ++
- (activity.data["cc"] || []))
+ def is_direct?(%Activity{data: %{"directMessage" => true}}), do: true
+ def is_direct?(%Object{data: %{"directMessage" => true}}), do: true
+
+ def is_direct?(activity) do
+ !is_public?(activity) && !is_private?(activity)
end
def visible_for_user?(activity, nil) do
diff --git a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
new file mode 100644
index 000000000..7c6ad582a
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
@@ -0,0 +1,57 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
+ alias Pleroma.User
+
+ @behaviour Pleroma.Web.ActivityPub.MRF
+
+ # XXX: this should become User.normalize_by_ap_id() or similar, really.
+ defp normalize_by_ap_id(%{"id" => id}), do: User.get_cached_by_ap_id(id)
+ defp normalize_by_ap_id(uri) when is_binary(uri), do: User.get_cached_by_ap_id(uri)
+ defp normalize_by_ap_id(_), do: nil
+
+ defp score_nickname("followbot@" <> _), do: 1.0
+ defp score_nickname("federationbot@" <> _), do: 1.0
+ defp score_nickname("federation_bot@" <> _), do: 1.0
+ defp score_nickname(_), do: 0.0
+
+ defp score_displayname("federation bot"), do: 1.0
+ defp score_displayname("federationbot"), do: 1.0
+ defp score_displayname("fedibot"), do: 1.0
+ defp score_displayname(_), do: 0.0
+
+ defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do
+ nick_score =
+ nickname
+ |> String.downcase()
+ |> score_nickname()
+
+ name_score =
+ displayname
+ |> String.downcase()
+ |> score_displayname()
+
+ nick_score + name_score
+ end
+
+ defp determine_if_followbot(_), do: 0.0
+
+ @impl true
+ def filter(%{"type" => "Follow", "actor" => actor_id} = message) do
+ %User{} = actor = normalize_by_ap_id(actor_id)
+
+ score = determine_if_followbot(actor)
+
+ # TODO: scan biography data for keywords and score it somehow.
+ if score < 0.8 do
+ {:ok, message}
+ else
+ {:reject, nil}
+ end
+ end
+
+ @impl true
+ def filter(message), do: {:ok, message}
+end
diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex
index abddbc790..c0a52e349 100644
--- a/lib/pleroma/web/activity_pub/relay.ex
+++ b/lib/pleroma/web/activity_pub/relay.ex
@@ -40,7 +40,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
with %User{} = user <- get_actor(),
%Object{} = object <- Object.normalize(activity.data["object"]["id"]) do
- ActivityPub.announce(user, object)
+ ActivityPub.announce(user, object, nil, true, false)
else
e -> Logger.error("error: #{inspect(e)}")
end
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 86d11c874..6656a11c6 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -93,12 +93,47 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
end
- def fix_addressing(map) do
- map
+ def fix_explicit_addressing(%{"to" => to, "cc" => cc} = object, explicit_mentions) do
+ explicit_to =
+ to
+ |> Enum.filter(fn x -> x in explicit_mentions end)
+
+ explicit_cc =
+ to
+ |> Enum.filter(fn x -> x not in explicit_mentions end)
+
+ final_cc =
+ (cc ++ explicit_cc)
+ |> Enum.uniq()
+
+ object
+ |> Map.put("to", explicit_to)
+ |> Map.put("cc", final_cc)
+ end
+
+ def fix_explicit_addressing(object, _explicit_mentions), do: object
+
+ # if directMessage flag is set to true, leave the addressing alone
+ def fix_explicit_addressing(%{"directMessage" => true} = object), do: object
+
+ def fix_explicit_addressing(object) do
+ explicit_mentions =
+ object
+ |> Utils.determine_explicit_mentions()
+
+ explicit_mentions = explicit_mentions ++ ["https://www.w3.org/ns/activitystreams#Public"]
+
+ object
+ |> fix_explicit_addressing(explicit_mentions)
+ end
+
+ def fix_addressing(object) do
+ object
|> fix_addressing_list("to")
|> fix_addressing_list("cc")
|> fix_addressing_list("bto")
|> fix_addressing_list("bcc")
+ |> fix_explicit_addressing
end
def fix_actor(%{"attributedTo" => actor} = object) do
@@ -141,7 +176,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
case fetch_obj_helper(in_reply_to_id) do
{:ok, replied_object} ->
with %Activity{} = activity <-
- Activity.get_create_activity_by_object_ap_id(replied_object.data["id"]) do
+ Activity.get_create_by_object_ap_id(replied_object.data["id"]) do
object
|> Map.put("inReplyTo", replied_object.data["id"])
|> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
@@ -334,7 +369,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
Map.put(data, "actor", actor)
|> fix_addressing
- with nil <- Activity.get_create_activity_by_object_ap_id(object["id"]),
+ with nil <- Activity.get_create_by_object_ap_id(object["id"]),
%User{} = user <- User.get_or_fetch_by_ap_id(data["actor"]) do
object = fix_object(data["object"])
@@ -348,6 +383,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
additional:
Map.take(data, [
"cc",
+ "directMessage",
"id"
])
}
@@ -451,7 +487,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
with actor <- get_actor(data),
%User{} = actor <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id),
- {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false) do
+ public <- ActivityPub.is_public?(data),
+ {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
{:ok, activity}
else
_e -> :error
@@ -863,15 +900,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
maybe_retire_websub(user.ap_id)
- # Only do this for recent activties, don't go through the whole db.
- # Only look at the last 1000 activities.
- since = (Repo.aggregate(Activity, :max, :id) || 0) - 1_000
-
q =
from(
a in Activity,
where: ^old_follower_address in a.recipients,
- where: a.id > ^since,
update: [
set: [
recipients:
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 6ecab773c..e40d05fcd 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -25,6 +25,20 @@ defmodule Pleroma.Web.ActivityPub.Utils do
Map.put(params, "actor", get_ap_id(params["actor"]))
end
+ def determine_explicit_mentions(%{"tag" => tag} = _object) when is_list(tag) do
+ tag
+ |> Enum.filter(fn x -> is_map(x) end)
+ |> Enum.filter(fn x -> x["type"] == "Mention" end)
+ |> Enum.map(fn x -> x["href"] end)
+ end
+
+ def determine_explicit_mentions(%{"tag" => tag} = object) when is_map(tag) do
+ Map.put(object, "tag", [tag])
+ |> determine_explicit_mentions()
+ end
+
+ def determine_explicit_mentions(_), do: []
+
defp recipient_in_collection(ap_id, coll) when is_binary(coll), do: ap_id == coll
defp recipient_in_collection(ap_id, coll) when is_list(coll), do: ap_id in coll
defp recipient_in_collection(_, _), do: false
@@ -198,7 +212,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
# Update activities that already had this. Could be done in a seperate process.
# Alternatively, just don't do this and fetch the current object each time. Most
# could probably be taken from cache.
- relevant_activities = Activity.all_by_object_ap_id(id)
+ relevant_activities = Activity.get_all_create_by_object_ap_id(id)
Enum.map(relevant_activities, fn activity ->
new_activity_data = activity.data |> Map.put("object", object.data)
@@ -386,9 +400,10 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"""
# for relayed messages, we only want to send to subscribers
def make_announce_data(
- %User{ap_id: ap_id, nickname: nil} = user,
+ %User{ap_id: ap_id} = user,
%Object{data: %{"id" => id}} = object,
- activity_id
+ activity_id,
+ false
) do
data = %{
"type" => "Announce",
@@ -405,7 +420,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
def make_announce_data(
%User{ap_id: ap_id} = user,
%Object{data: %{"id" => id}} = object,
- activity_id
+ activity_id,
+ true
) do
data = %{
"type" => "Announce",
diff --git a/lib/pleroma/web/activity_pub/views/object_view.ex b/lib/pleroma/web/activity_pub/views/object_view.ex
index 193042056..394d82fbc 100644
--- a/lib/pleroma/web/activity_pub/views/object_view.ex
+++ b/lib/pleroma/web/activity_pub/views/object_view.ex
@@ -46,7 +46,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
"id" => "#{ap_id}/likes",
"type" => "OrderedCollection",
"totalItems" => length(likes),
- "first" => collection(likes, "#{ap_id}/followers", 1)
+ "first" => collection(likes, "#{ap_id}/likes", 1)
}
|> Map.merge(Pleroma.Web.ActivityPub.Utils.make_json_ld_header())
end
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index fe8248107..dcf681b6d 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -160,7 +160,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"partOf" => iri,
"totalItems" => info.note_count,
"orderedItems" => collection,
- "next" => "#{iri}?max_id=#{min_id - 1}"
+ "next" => "#{iri}?max_id=#{min_id}"
}
if max_qid == nil do
@@ -207,7 +207,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"partOf" => iri,
"totalItems" => -1,
"orderedItems" => collection,
- "next" => "#{iri}?max_id=#{min_id - 1}"
+ "next" => "#{iri}?max_id=#{min_id}"
}
if max_qid == nil do