aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/mix/tasks/deactivate_user.ex13
-rw-r--r--lib/mix/tasks/fix_ap_users.ex1
-rw-r--r--lib/mix/tasks/generate_password_reset.ex3
-rw-r--r--lib/mix/tasks/register_user.ex1
-rw-r--r--lib/mix/tasks/rm_user.ex3
-rw-r--r--lib/mix/tasks/sample_psql.eex1
-rw-r--r--lib/pleroma/application.ex16
-rw-r--r--lib/pleroma/formatter.ex1
-rw-r--r--lib/pleroma/gopher/server.ex12
-rw-r--r--lib/pleroma/notification.ex3
-rw-r--r--lib/pleroma/object.ex20
-rw-r--r--lib/pleroma/plugs/http_signature.ex7
-rw-r--r--lib/pleroma/stats.ex2
-rw-r--r--lib/pleroma/user.ex37
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex130
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex6
-rw-r--r--lib/pleroma/web/activity_pub/mrf.ex24
-rw-r--r--lib/pleroma/web/activity_pub/mrf/drop_policy.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf/noop_policy.ex3
-rw-r--r--lib/pleroma/web/activity_pub/mrf/simple_policy.ex46
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex133
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex133
-rw-r--r--lib/pleroma/web/activity_pub/views/user_view.ex47
-rw-r--r--lib/pleroma/web/channels/user_socket.ex1
-rw-r--r--lib/pleroma/web/common_api/common_api.ex12
-rw-r--r--lib/pleroma/web/common_api/utils.ex19
-rw-r--r--lib/pleroma/web/federator/federator.ex15
-rw-r--r--lib/pleroma/web/http_signatures/http_signatures.ex7
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex63
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex52
-rw-r--r--lib/pleroma/web/nodeinfo/nodeinfo.ex1
-rw-r--r--lib/pleroma/web/nodeinfo/nodeinfo_controller.ex62
-rw-r--r--lib/pleroma/web/oauth/authorization.ex2
-rw-r--r--lib/pleroma/web/oauth/token.ex2
-rw-r--r--lib/pleroma/web/ostatus/activity_representer.ex10
-rw-r--r--lib/pleroma/web/ostatus/handlers/note_handler.ex2
-rw-r--r--lib/pleroma/web/ostatus/handlers/unfollow_handler.ex17
-rw-r--r--lib/pleroma/web/ostatus/ostatus.ex5
-rw-r--r--lib/pleroma/web/ostatus/ostatus_controller.ex1
-rw-r--r--lib/pleroma/web/router.ex8
-rw-r--r--lib/pleroma/web/salmon/salmon.ex11
-rw-r--r--lib/pleroma/web/streamer.ex38
-rw-r--r--lib/pleroma/web/twitter_api/controllers/util_controller.ex37
-rw-r--r--lib/pleroma/web/twitter_api/representers/activity_representer.ex5
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api.ex42
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api_controller.ex15
-rw-r--r--lib/pleroma/web/twitter_api/views/activity_view.ex5
-rw-r--r--lib/pleroma/web/twitter_api/views/notification_view.ex1
-rw-r--r--lib/pleroma/web/web_finger/web_finger.ex25
-rw-r--r--lib/pleroma/web/xml/xml.ex2
50 files changed, 844 insertions, 260 deletions
diff --git a/lib/mix/tasks/deactivate_user.ex b/lib/mix/tasks/deactivate_user.ex
new file mode 100644
index 000000000..96b3db6e4
--- /dev/null
+++ b/lib/mix/tasks/deactivate_user.ex
@@ -0,0 +1,13 @@
+defmodule Mix.Tasks.DeactivateUser do
+ use Mix.Task
+ alias Pleroma.User
+
+ @shortdoc "Toggle deactivation status for a user"
+ def run([nickname]) do
+ Mix.Task.run("app.start")
+
+ with user <- User.get_by_nickname(nickname) do
+ User.deactivate(user)
+ end
+ end
+end
diff --git a/lib/mix/tasks/fix_ap_users.ex b/lib/mix/tasks/fix_ap_users.ex
index 2523cdbcb..7e970850e 100644
--- a/lib/mix/tasks/fix_ap_users.ex
+++ b/lib/mix/tasks/fix_ap_users.ex
@@ -1,6 +1,5 @@
defmodule Mix.Tasks.FixApUsers do
use Mix.Task
- import Mix.Ecto
import Ecto.Query
alias Pleroma.{Repo, User}
diff --git a/lib/mix/tasks/generate_password_reset.ex b/lib/mix/tasks/generate_password_reset.ex
index e39134007..6bf640150 100644
--- a/lib/mix/tasks/generate_password_reset.ex
+++ b/lib/mix/tasks/generate_password_reset.ex
@@ -1,7 +1,6 @@
defmodule Mix.Tasks.GeneratePasswordReset do
use Mix.Task
- import Mix.Ecto
- alias Pleroma.{Repo, User}
+ alias Pleroma.User
@shortdoc "Generate password reset link for user"
def run([nickname]) do
diff --git a/lib/mix/tasks/register_user.ex b/lib/mix/tasks/register_user.ex
index 4bec6b9d9..e74721c49 100644
--- a/lib/mix/tasks/register_user.ex
+++ b/lib/mix/tasks/register_user.ex
@@ -1,6 +1,5 @@
defmodule Mix.Tasks.RegisterUser do
use Mix.Task
- import Mix.Ecto
alias Pleroma.{Repo, User}
@shortdoc "Register user"
diff --git a/lib/mix/tasks/rm_user.ex b/lib/mix/tasks/rm_user.ex
index 6a698f360..27521b745 100644
--- a/lib/mix/tasks/rm_user.ex
+++ b/lib/mix/tasks/rm_user.ex
@@ -1,7 +1,6 @@
defmodule Mix.Tasks.RmUser do
use Mix.Task
- import Mix.Ecto
- alias Pleroma.{User, Repo}
+ alias Pleroma.User
@shortdoc "Permanently delete a user"
def run([nickname]) do
diff --git a/lib/mix/tasks/sample_psql.eex b/lib/mix/tasks/sample_psql.eex
index 18e322efc..bc22f166c 100644
--- a/lib/mix/tasks/sample_psql.eex
+++ b/lib/mix/tasks/sample_psql.eex
@@ -6,3 +6,4 @@ ALTER DATABASE pleroma_dev OWNER TO pleroma;
\c pleroma_dev;
--Extensions made by ecto.migrate that need superuser access
CREATE EXTENSION IF NOT EXISTS citext;
+CREATE EXTENSION IF NOT EXISTS pg_trgm;
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 89826f515..a89728471 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -5,6 +5,7 @@ defmodule Pleroma.Application do
# for more information on OTP Applications
def start(_type, _args) do
import Supervisor.Spec
+ import Cachex.Spec
# Define workers and child supervisors to be supervised
children =
@@ -23,6 +24,21 @@ defmodule Pleroma.Application do
limit: 2500
]
]),
+ worker(
+ Cachex,
+ [
+ :idempotency_cache,
+ [
+ expiration:
+ expiration(
+ default: :timer.seconds(6 * 60 * 60),
+ interval: :timer.seconds(60)
+ ),
+ limit: 2500
+ ]
+ ],
+ id: :cachex_idem
+ ),
worker(Pleroma.Web.Federator, []),
worker(Pleroma.Gopher.Server, []),
worker(Pleroma.Stats, [])
diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex
index 456416fbd..53e2c204f 100644
--- a/lib/pleroma/formatter.ex
+++ b/lib/pleroma/formatter.ex
@@ -160,6 +160,7 @@ defmodule Pleroma.Formatter do
links =
Regex.scan(@link_regex, text)
|> Enum.map(fn [url] -> {Ecto.UUID.generate(), url} end)
+ |> Enum.sort_by(fn {_, url} -> -String.length(url) end)
uuid_text =
links
diff --git a/lib/pleroma/gopher/server.ex b/lib/pleroma/gopher/server.ex
index 82e241f21..f6abcd4d0 100644
--- a/lib/pleroma/gopher/server.ex
+++ b/lib/pleroma/gopher/server.ex
@@ -65,12 +65,6 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
"#{type}#{name}\t#{selector}\t#{address}\t#{port}\r\n"
end
- def response("") do
- info("Welcome to #{Keyword.get(@instance, :name, "Pleroma")}!") <>
- link("Public Timeline", "/main/public") <>
- link("Federated Timeline", "/main/all") <> ".\r\n"
- end
-
def render_activities(activities) do
activities
|> Enum.reverse()
@@ -93,6 +87,12 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
|> Enum.join("\r\n")
end
+ def response("") do
+ info("Welcome to #{Keyword.get(@instance, :name, "Pleroma")}!") <>
+ link("Public Timeline", "/main/public") <>
+ link("Federated Timeline", "/main/all") <> ".\r\n"
+ end
+
def response("/main/public") do
posts =
ActivityPub.fetch_public_activities(%{"type" => ["Create"], "local_only" => true})
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index e26e49c8c..e0dcd9823 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -91,7 +91,8 @@ defmodule Pleroma.Notification do
# TODO move to sql, too.
def create_notification(%Activity{} = activity, %User{} = user) do
- unless User.blocks?(user, %{ap_id: activity.data["actor"]}) do
+ unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or
+ user.ap_id == activity.data["actor"] do
notification = %Notification{user_id: user.id, activity: activity}
{:ok, notification} = Repo.insert(notification)
Pleroma.Web.Streamer.stream("user", notification)
diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex
index 558e151b0..ff2af4a6f 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -33,19 +33,15 @@ defmodule Pleroma.Object do
else
key = "object:#{ap_id}"
- Cachex.get!(
- :user_cache,
- key,
- fallback: fn _ ->
- object = get_by_ap_id(ap_id)
-
- if object do
- {:commit, object}
- else
- {:ignore, object}
- end
+ Cachex.fetch!(:user_cache, key, fn _ ->
+ object = get_by_ap_id(ap_id)
+
+ if object do
+ {:commit, object}
+ else
+ {:ignore, object}
end
- )
+ end)
end
end
diff --git a/lib/pleroma/plugs/http_signature.ex b/lib/pleroma/plugs/http_signature.ex
index 8b9ccdd2d..2d0e10cad 100644
--- a/lib/pleroma/plugs/http_signature.ex
+++ b/lib/pleroma/plugs/http_signature.ex
@@ -1,5 +1,6 @@
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
alias Pleroma.Web.HTTPSignatures
+ alias Pleroma.Web.ActivityPub.Utils
import Plug.Conn
require Logger
@@ -7,12 +8,12 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
options
end
- def call(%{assigns: %{valid_signature: true}} = conn, opts) do
+ def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
conn
end
- def call(conn, opts) do
- user = conn.params["actor"]
+ def call(conn, _opts) do
+ user = Utils.normalize_actor(conn.params["actor"])
Logger.debug("Checking sig for #{user}")
[signature | _] = get_req_header(conn, "signature")
diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex
index 83b896a16..8478fe4ce 100644
--- a/lib/pleroma/stats.ex
+++ b/lib/pleroma/stats.ex
@@ -1,6 +1,6 @@
defmodule Pleroma.Stats do
import Ecto.Query
- alias Pleroma.{User, Repo, Activity}
+ alias Pleroma.{User, Repo}
def start_link do
agent = Agent.start_link(fn -> {[], %{}} end, name: __MODULE__)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 207674999..690cc7cf3 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -21,6 +21,7 @@ defmodule Pleroma.User do
field(:local, :boolean, default: true)
field(:info, :map, default: %{})
field(:follower_address, :string)
+ field(:search_distance, :float, virtual: true)
has_many(:notifications, Notification)
timestamps()
@@ -222,9 +223,9 @@ defmodule Pleroma.User do
def update_and_set_cache(changeset) do
with {:ok, user} <- Repo.update(changeset) do
- Cachex.set(:user_cache, "ap_id:#{user.ap_id}", user)
- Cachex.set(:user_cache, "nickname:#{user.nickname}", user)
- Cachex.set(:user_cache, "user_info:#{user.id}", user_info(user))
+ Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
+ Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
+ Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user))
{:ok, user}
else
e -> e
@@ -238,12 +239,12 @@ defmodule Pleroma.User do
def get_cached_by_ap_id(ap_id) do
key = "ap_id:#{ap_id}"
- Cachex.get!(:user_cache, key, fallback: fn _ -> get_by_ap_id(ap_id) end)
+ Cachex.fetch!(:user_cache, key, fn _ -> get_by_ap_id(ap_id) end)
end
def get_cached_by_nickname(nickname) do
key = "nickname:#{nickname}"
- Cachex.get!(:user_cache, key, fallback: fn _ -> get_or_fetch_by_nickname(nickname) end)
+ Cachex.fetch!(:user_cache, key, fn _ -> get_or_fetch_by_nickname(nickname) end)
end
def get_by_nickname(nickname) do
@@ -259,7 +260,7 @@ defmodule Pleroma.User do
def get_cached_user_info(user) do
key = "user_info:#{user.id}"
- Cachex.get!(:user_cache, key, fallback: fn _ -> user_info(user) end)
+ Cachex.fetch!(:user_cache, key, fn _ -> user_info(user) end)
end
def fetch_by_nickname(nickname) do
@@ -399,16 +400,24 @@ defmodule Pleroma.User do
User.get_or_fetch_by_nickname(query)
end
- q =
+ inner =
from(
u in User,
- where:
- fragment(
- "(to_tsvector('english', ?) || to_tsvector('english', ?)) @@ plainto_tsquery('english', ?)",
- u.nickname,
- u.name,
- ^query
- ),
+ select_merge: %{
+ search_distance:
+ fragment(
+ "? <-> (? || ?)",
+ ^query,
+ u.nickname,
+ u.name
+ )
+ }
+ )
+
+ q =
+ from(
+ s in subquery(inner),
+ order_by: s.search_distance,
limit: 20
)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 3ddc009a1..8485a8009 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -1,6 +1,6 @@
defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.{Activity, Repo, Object, Upload, User, Notification}
- alias Pleroma.Web.ActivityPub.Transmogrifier
+ alias Pleroma.Web.ActivityPub.{Transmogrifier, MRF}
alias Pleroma.Web.WebFinger
alias Pleroma.Web.Federator
alias Pleroma.Web.OStatus
@@ -11,16 +11,29 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
@httpoison Application.get_env(:pleroma, :httpoison)
@instance Application.get_env(:pleroma, :instance)
- @rewrite_policy Keyword.get(@instance, :rewrite_policy)
def get_recipients(data) do
(data["to"] || []) ++ (data["cc"] || [])
end
+ defp check_actor_is_active(actor) do
+ if not is_nil(actor) do
+ with user <- User.get_cached_by_ap_id(actor),
+ nil <- user.info["deactivated"] do
+ :ok
+ else
+ _e -> :reject
+ end
+ else
+ :ok
+ end
+ end
+
def insert(map, local \\ true) when is_map(map) do
with nil <- Activity.get_by_ap_id(map["id"]),
map <- lazy_put_activity_defaults(map),
- {:ok, map} <- @rewrite_policy.filter(map),
+ :ok <- check_actor_is_active(map["actor"]),
+ {:ok, map} <- MRF.filter(map),
:ok <- insert_full_object(map) do
{:ok, activity} =
Repo.insert(%Activity{
@@ -66,7 +79,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
),
{:ok, activity} <- insert(create_data, local),
:ok <- maybe_federate(activity),
- {:ok, actor} <- User.increase_note_count(actor) do
+ {:ok, _actor} <- User.increase_note_count(actor) do
{:ok, activity}
end
end
@@ -118,11 +131,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- def unlike(%User{} = actor, %Object{} = object) do
- with %Activity{} = activity <- get_existing_like(actor.ap_id, object),
- {:ok, _activity} <- Repo.delete(activity),
- {:ok, object} <- remove_like_from_object(activity, object) do
- {:ok, object}
+ def unlike(
+ %User{} = actor,
+ %Object{} = object,
+ activity_id \\ nil,
+ local \\ true
+ ) do
+ with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
+ unlike_data <- make_unlike_data(actor, like_activity, activity_id),
+ {:ok, unlike_activity} <- insert(unlike_data, local),
+ {:ok, _activity} <- Repo.delete(like_activity),
+ {:ok, object} <- remove_like_from_object(like_activity, object),
+ :ok <- maybe_federate(unlike_activity) do
+ {:ok, unlike_activity, like_activity, object}
else
_e -> {:ok, object}
end
@@ -145,6 +166,24 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
+ def unannounce(
+ %User{} = actor,
+ %Object{} = object,
+ activity_id \\ nil,
+ local \\ true
+ ) do
+ with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
+ unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
+ {:ok, unannounce_activity} <- insert(unannounce_data, local),
+ :ok <- maybe_federate(unannounce_activity),
+ {:ok, _activity} <- Repo.delete(announce_activity),
+ {:ok, object} <- remove_announce_from_object(announce_activity, object) do
+ {:ok, unannounce_activity, announce_activity, object}
+ else
+ _e -> {:ok, object}
+ end
+ end
+
def follow(follower, followed, activity_id \\ nil, local \\ true) do
with data <- make_follow_data(follower, followed, activity_id),
{:ok, activity} <- insert(data, local),
@@ -153,12 +192,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- def unfollow(follower, followed, local \\ true) do
+ def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
- unfollow_data <- make_unfollow_data(follower, followed, follow_activity),
+ unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
{:ok, activity} <- insert(unfollow_data, local),
- :ok,
- maybe_federate(activity) do
+ :ok <- maybe_federate(activity) do
{:ok, activity}
end
end
@@ -177,7 +215,30 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
Repo.delete_all(Activity.all_non_create_by_object_ap_id_q(id)),
{:ok, activity} <- insert(data, local),
:ok <- maybe_federate(activity),
- {:ok, actor} <- User.decrease_note_count(user) do
+ {:ok, _actor} <- User.decrease_note_count(user) do
+ {:ok, activity}
+ end
+ end
+
+ def block(blocker, blocked, activity_id \\ nil, local \\ true) do
+ follow_activity = fetch_latest_follow(blocker, blocked)
+
+ if follow_activity do
+ unfollow(blocker, blocked, nil, local)
+ end
+
+ with block_data <- make_block_data(blocker, blocked, activity_id),
+ {:ok, activity} <- insert(block_data, local),
+ :ok <- maybe_federate(activity) do
+ {:ok, activity}
+ end
+ end
+
+ def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
+ with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
+ unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
+ {:ok, activity} <- insert(unblock_data, local),
+ :ok <- maybe_federate(activity) do
{:ok, activity}
end
end
@@ -212,15 +273,34 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
Repo.all(query)
end
- # TODO: Make this work properly with unlisted.
def fetch_public_activities(opts \\ %{}) do
q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts)
q
+ |> restrict_unlisted()
|> Repo.all()
|> Enum.reverse()
end
+ def fetch_user_activities(user, reading_user, params \\ %{}) do
+ params =
+ params
+ |> Map.put("type", ["Create", "Announce"])
+ |> Map.put("actor_id", user.ap_id)
+ |> Map.put("whole_db", true)
+
+ recipients =
+ if reading_user do
+ ["https://www.w3.org/ns/activitystreams#Public"] ++
+ [reading_user.ap_id | reading_user.following]
+ else
+ ["https://www.w3.org/ns/activitystreams#Public"]
+ end
+
+ fetch_activities(recipients, params)
+ |> Enum.reverse()
+ end
+
defp restrict_since(query, %{"since_id" => since_id}) do
from(activity in query, where: activity.id > ^since_id)
end
@@ -236,7 +316,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_tag(query, _), do: query
- defp restrict_recipients(query, [], user), do: query
+ defp restrict_recipients(query, [], _user), do: query
defp restrict_recipients(query, recipients, nil) do
from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
@@ -323,6 +403,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_blocked(query, _), do: query
+ defp restrict_unlisted(query) do
+ from(
+ activity in query,
+ where:
+ fragment(
+ "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
+ activity.data,
+ ^["https://www.w3.org/ns/activitystreams#Public"]
+ )
+ )
+ end
+
def fetch_activities_query(recipients, opts \\ %{}) do
base_query =
from(
@@ -372,6 +464,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
"url" => [%{"href" => data["image"]["url"]}]
}
+ data = Transmogrifier.maybe_fix_user_object(data)
+
user_data = %{
ap_id: data["id"],
info: %{
@@ -400,7 +494,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
def make_user_from_ap_id(ap_id) do
- if user = User.get_by_ap_id(ap_id) do
+ if _user = User.get_by_ap_id(ap_id) do
Transmogrifier.upgrade_user_from_ap_id(ap_id)
else
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
@@ -496,7 +590,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
object = %Object{} ->
{:ok, object}
- e ->
+ _e ->
Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
case OStatus.fetch_activity_from_url(id) do
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 12f61f5f0..c7d50893f 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -1,7 +1,7 @@
defmodule Pleroma.Web.ActivityPub.ActivityPubController do
use Pleroma.Web, :controller
- alias Pleroma.{User, Repo, Object, Activity}
- alias Pleroma.Web.ActivityPub.{ObjectView, UserView, Transmogrifier}
+ alias Pleroma.{User, Object}
+ alias Pleroma.Web.ActivityPub.{ObjectView, UserView}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.Federator
@@ -93,7 +93,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
Logger.info("Signature not from author, relayed message, fetching from source")
ActivityPub.fetch_object_from_id(params["object"]["id"])
else
- Logger.info("Signature error")
+ Logger.info("Signature error - make sure you are forwarding the HTTP Host header!")
Logger.info("Could not validate #{params["actor"]}")
Logger.info(inspect(conn.req_headers))
end
diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex
new file mode 100644
index 000000000..0a4e2bf80
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/mrf.ex
@@ -0,0 +1,24 @@
+defmodule Pleroma.Web.ActivityPub.MRF do
+ @callback filter(Map.t()) :: {:ok | :reject, Map.t()}
+
+ def filter(object) do
+ get_policies()
+ |> Enum.reduce({:ok, object}, fn
+ policy, {:ok, object} ->
+ policy.filter(object)
+
+ _, error ->
+ error
+ end)
+ end
+
+ def get_policies() do
+ Application.get_env(:pleroma, :instance, [])
+ |> Keyword.get(:rewrite_policy, [])
+ |> get_policies()
+ end
+
+ defp get_policies(policy) when is_atom(policy), do: [policy]
+ defp get_policies(policies) when is_list(policies), do: policies
+ defp get_policies(_), do: []
+end
diff --git a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
index 4333bca28..811947943 100644
--- a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
@@ -1,6 +1,8 @@
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
require Logger
+ @behaviour Pleroma.Web.ActivityPub.MRF
+ @impl true
def filter(object) do
Logger.info("REJECTING #{inspect(object)}")
{:reject, object}
diff --git a/lib/pleroma/web/activity_pub/mrf/noop_policy.ex b/lib/pleroma/web/activity_pub/mrf/noop_policy.ex
index 9dd3acb04..e26f60d26 100644
--- a/lib/pleroma/web/activity_pub/mrf/noop_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/noop_policy.ex
@@ -1,4 +1,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
+ @behaviour Pleroma.Web.ActivityPub.MRF
+
+ @impl true
def filter(object) do
{:ok, object}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
index ea1af0f4d..8d770387d 100644
--- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
@@ -1,5 +1,6 @@
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
alias Pleroma.User
+ @behaviour Pleroma.Web.ActivityPub.MRF
@mrf_policy Application.get_env(:pleroma, :mrf_simple)
@@ -17,9 +18,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
if actor_info.host in @media_removal do
child_object = Map.delete(object["object"], "attachment")
object = Map.put(object, "object", child_object)
+ {:ok, object}
+ else
+ {:ok, object}
end
-
- {:ok, object}
end
@media_nsfw Keyword.get(@mrf_policy, :media_nsfw)
@@ -32,9 +34,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
child_object = Map.put(child_object, "tags", tags)
child_object = Map.put(child_object, "sensitive", true)
object = Map.put(object, "object", child_object)
+ {:ok, object}
+ else
+ {:ok, object}
end
-
- {:ok, object}
end
@ftl_removal Keyword.get(@mrf_policy, :federated_timeline_removal)
@@ -43,24 +46,31 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
user = User.get_by_ap_id(object["actor"])
# flip to/cc relationship to make the post unlisted
- if "https://www.w3.org/ns/activitystreams#Public" in object["to"] and
- user.follower_address in object["cc"] do
- to =
- List.delete(object["to"], "https://www.w3.org/ns/activitystreams#Public") ++
- [user.follower_address]
+ object =
+ if "https://www.w3.org/ns/activitystreams#Public" in object["to"] and
+ user.follower_address in object["cc"] do
+ to =
+ List.delete(object["to"], "https://www.w3.org/ns/activitystreams#Public") ++
+ [user.follower_address]
- cc =
- List.delete(object["cc"], user.follower_address) ++
- ["https://www.w3.org/ns/activitystreams#Public"]
+ cc =
+ List.delete(object["cc"], user.follower_address) ++
+ ["https://www.w3.org/ns/activitystreams#Public"]
- object = Map.put(object, "to", to)
- object = Map.put(object, "cc", cc)
- end
- end
+ object
+ |> Map.put("to", to)
+ |> Map.put("cc", cc)
+ else
+ object
+ end
- {:ok, object}
+ {:ok, object}
+ else
+ {:ok, object}
+ end
end
+ @impl true
def filter(object) do
actor_info = URI.parse(object["actor"])
@@ -70,7 +80,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
{:ok, object} <- check_ftl_removal(actor_info, object) do
{:ok, object}
else
- e -> {:reject, nil}
+ _e -> {:reject, nil}
end
end
end
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 2871a2544..803445011 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -72,9 +72,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Enum.reduce(%{}, fn data, mapping ->
name = data["name"]
- if String.starts_with?(name, ":") do
- name = name |> String.slice(1..-2)
- end
+ name =
+ if String.starts_with?(name, ":") do
+ name |> String.slice(1..-2)
+ else
+ name
+ end
mapping |> Map.put(name, data["icon"]["url"])
end)
@@ -143,12 +146,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
- %{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = data
+ %{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = _data
) do
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <-
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
- {:ok, activity, object} <- ActivityPub.like(actor, object, id, false) do
+ {:ok, activity, _object} <- ActivityPub.like(actor, object, id, false) do
{:ok, activity}
else
_e -> :error
@@ -156,12 +159,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
- %{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = data
+ %{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = _data
) do
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <-
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
- {:ok, activity, object} <- ActivityPub.announce(actor, object, id, false) do
+ {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false) do
{:ok, activity}
else
_e -> :error
@@ -202,7 +205,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# TODO: Make secure.
def handle_incoming(
- %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => id} = data
+ %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = _data
) do
object_id =
case object_id do
@@ -210,19 +213,104 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
id -> id
end
- with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
+ with %User{} = _actor <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <-
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
{:ok, activity} <- ActivityPub.delete(object, false) do
{:ok, activity}
else
+ _e -> :error
+ end
+ end
+
+ def handle_incoming(
+ %{
+ "type" => "Undo",
+ "object" => %{"type" => "Announce", "object" => object_id},
+ "actor" => actor,
+ "id" => id
+ } = _data
+ ) do
+ with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
+ {:ok, object} <-
+ get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
+ {:ok, activity, _, _} <- ActivityPub.unannounce(actor, object, id, false) do
+ {:ok, activity}
+ else
+ _e -> :error
+ end
+ end
+
+ def handle_incoming(
+ %{
+ "type" => "Undo",
+ "object" => %{"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, activity} <- ActivityPub.unfollow(follower, followed, id, false) do
+ User.unfollow(follower, followed)
+ {:ok, activity}
+ else
e -> :error
end
end
+ def handle_incoming(
+ %{
+ "type" => "Undo",
+ "object" => %{"type" => "Block", "object" => blocked},
+ "actor" => blocker,
+ "id" => id
+ } = _data
+ ) do
+ with %User{local: true} = blocked <- User.get_cached_by_ap_id(blocked),
+ %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}
+ else
+ e -> :error
+ end
+ end
+
+ def handle_incoming(
+ %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = data
+ ) do
+ with %User{local: true} = blocked = User.get_cached_by_ap_id(blocked),
+ %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)
+ {:ok, activity}
+ else
+ e -> :error
+ end
+ end
+
+ def handle_incoming(
+ %{
+ "type" => "Undo",
+ "object" => %{"type" => "Like", "object" => object_id},
+ "actor" => actor,
+ "id" => id
+ } = _data
+ ) do
+ with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
+ {:ok, object} <-
+ get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
+ {:ok, activity, _, _} <- ActivityPub.unlike(actor, object, id, false) do
+ {:ok, activity}
+ else
+ _e -> :error
+ end
+ end
+
# TODO
# Accept
- # Undo
def handle_incoming(_), do: :error
@@ -254,10 +342,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> set_reply_to_uri
end
- @doc
- """
- internal -> Mastodon
- """
+ # @doc
+ # """
+ # internal -> Mastodon
+ # """
def prepare_outgoing(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do
object =
@@ -272,7 +360,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, data}
end
- def prepare_outgoing(%{"type" => type} = data) do
+ def prepare_outgoing(%{"type" => _type} = data) do
data =
data
|> maybe_fix_object_url
@@ -286,7 +374,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
case ActivityPub.fetch_object_from_id(data["object"]) do
{:ok, relative_object} ->
if relative_object.data["external_url"] do
- data =
+ _data =
data
|> Map.put("object", relative_object.data["external_url"])
else
@@ -474,4 +562,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
Repo.delete_all(q)
end
end
+
+ def maybe_fix_user_url(data) do
+ if is_map(data["url"]) do
+ Map.put(data, "url", data["url"]["href"])
+ else
+ data
+ end
+ end
+
+ def maybe_fix_user_object(data) do
+ data
+ |> maybe_fix_user_url
+ end
end
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 7b2bf8fa7..831e13b7e 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -5,6 +5,22 @@ defmodule Pleroma.Web.ActivityPub.Utils do
alias Ecto.{Changeset, UUID}
import Ecto.Query
+ # Some implementations send the actor URI as the actor field, others send the entire actor object,
+ # so figure out what the actor's URI is based on what we have.
+ def normalize_actor(actor) do
+ cond do
+ is_binary(actor) ->
+ actor
+
+ is_map(actor) ->
+ actor["id"]
+ end
+ end
+
+ def normalize_params(params) do
+ Map.put(params, "actor", normalize_actor(params["actor"]))
+ end
+
def make_json_ld_header do
%{
"@context" => [
@@ -226,8 +242,9 @@ defmodule Pleroma.Web.ActivityPub.Utils do
fragment(
"? @> ?",
activity.data,
- ^%{type: "Follow", actor: follower_id, object: followed_id}
+ ^%{type: "Follow", object: followed_id}
),
+ where: activity.actor == ^follower_id,
order_by: [desc: :id],
limit: 1
)
@@ -238,6 +255,28 @@ defmodule Pleroma.Web.ActivityPub.Utils do
#### Announce-related helpers
@doc """
+ Retruns an existing announce activity if the notice has already been announced
+ """
+ def get_existing_announce(actor, %{data: %{"id" => id}}) do
+ query =
+ from(
+ activity in Activity,
+ where: activity.actor == ^actor,
+ # this is to use the index
+ where:
+ fragment(
+ "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
+ activity.data,
+ activity.data,
+ ^id
+ ),
+ where: fragment("(?)->>'type' = 'Announce'", activity.data)
+ )
+
+ Repo.one(query)
+ end
+
+ @doc """
Make announce activity data for the given actor and object
"""
def make_announce_data(
@@ -257,21 +296,107 @@ defmodule Pleroma.Web.ActivityPub.Utils do
if activity_id, do: Map.put(data, "id", activity_id), else: data
end
+ @doc """
+ Make unannounce activity data for the given actor and object
+ """
+ def make_unannounce_data(
+ %User{ap_id: ap_id} = user,
+ %Activity{data: %{"context" => context}} = activity,
+ activity_id
+ ) do
+ data = %{
+ "type" => "Undo",
+ "actor" => ap_id,
+ "object" => activity.data,
+ "to" => [user.follower_address, activity.data["actor"]],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "context" => context
+ }
+
+ if activity_id, do: Map.put(data, "id", activity_id), else: data
+ end
+
+ def make_unlike_data(
+ %User{ap_id: ap_id} = user,
+ %Activity{data: %{"context" => context}} = activity,
+ activity_id
+ ) do
+ data = %{
+ "type" => "Undo",
+ "actor" => ap_id,
+ "object" => activity.data,
+ "to" => [user.follower_address, activity.data["actor"]],
+ "cc" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "context" => context
+ }
+
+ if activity_id, do: Map.put(data, "id", activity_id), else: data
+ end
+
def add_announce_to_object(%Activity{data: %{"actor" => actor}}, object) do
with announcements <- [actor | object.data["announcements"] || []] |> Enum.uniq() do
update_element_in_object("announcement", announcements, object)
end
end
+ def remove_announce_from_object(%Activity{data: %{"actor" => actor}}, object) do
+ with announcements <- (object.data["announcements"] || []) |> List.delete(actor) do
+ update_element_in_object("announcement", announcements, object)
+ end
+ end
+
#### Unfollow-related helpers
- def make_unfollow_data(follower, followed, follow_activity) do
- %{
+ def make_unfollow_data(follower, followed, follow_activity, activity_id) do
+ data = %{
"type" => "Undo",
"actor" => follower.ap_id,
"to" => [followed.ap_id],
- "object" => follow_activity.data["id"]
+ "object" => follow_activity.data
+ }
+
+ if activity_id, do: Map.put(data, "id", activity_id), else: data
+ end
+
+ #### Block-related helpers
+ def fetch_latest_block(%User{ap_id: blocker_id}, %User{ap_id: blocked_id}) do
+ query =
+ from(
+ activity in Activity,
+ where:
+ fragment(
+ "? @> ?",
+ activity.data,
+ ^%{type: "Block", object: blocked_id}
+ ),
+ where: activity.actor == ^blocker_id,
+ order_by: [desc: :id],
+ limit: 1
+ )
+
+ Repo.one(query)
+ end
+
+ def make_block_data(blocker, blocked, activity_id) do
+ data = %{
+ "type" => "Block",
+ "actor" => blocker.ap_id,
+ "to" => [blocked.ap_id],
+ "object" => blocked.ap_id
+ }
+
+ if activity_id, do: Map.put(data, "id", activity_id), else: data
+ end
+
+ def make_unblock_data(blocker, blocked, block_activity, activity_id) do
+ data = %{
+ "type" => "Undo",
+ "actor" => blocker.ap_id,
+ "to" => [blocked.ap_id],
+ "object" => block_activity.data
}
+
+ if activity_id, do: Map.put(data, "id", activity_id), else: data
end
#### Create-related helpers
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index 92afd0872..ffd76b529 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -47,25 +47,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|> Map.merge(Utils.make_json_ld_header())
end
- def collection(collection, iri, page, total \\ nil) do
- offset = (page - 1) * 10
- items = Enum.slice(collection, offset, 10)
- items = Enum.map(items, fn user -> user.ap_id end)
- total = total || length(collection)
-
- map = %{
- "id" => "#{iri}?page=#{page}",
- "type" => "OrderedCollectionPage",
- "partOf" => iri,
- "totalItems" => length(collection),
- "orderedItems" => items
- }
-
- if offset < length(collection) do
- Map.put(map, "next", "#{iri}?page=#{page + 1}")
- end
- end
-
def render("following.json", %{user: user, page: page}) do
query = User.get_friends_query(user)
query = from(user in query, select: [:ap_id])
@@ -123,9 +104,12 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"limit" => "10"
}
- if max_qid != nil do
- params = Map.put(params, "max_id", max_qid)
- end
+ params =
+ if max_qid != nil do
+ Map.put(params, "max_id", max_qid)
+ else
+ params
+ end
activities = ActivityPub.fetch_public_activities(params)
min_id = Enum.at(activities, 0).id
@@ -162,4 +146,23 @@ defmodule Pleroma.Web.ActivityPub.UserView do
page |> Map.merge(Utils.make_json_ld_header())
end
end
+
+ def collection(collection, iri, page, total \\ nil) do
+ offset = (page - 1) * 10
+ items = Enum.slice(collection, offset, 10)
+ items = Enum.map(items, fn user -> user.ap_id end)
+ total = total || length(collection)
+
+ map = %{
+ "id" => "#{iri}?page=#{page}",
+ "type" => "OrderedCollectionPage",
+ "partOf" => iri,
+ "totalItems" => total,
+ "orderedItems" => items
+ }
+
+ if offset < total do
+ Map.put(map, "next", "#{iri}?page=#{page + 1}")
+ end
+ end
end
diff --git a/lib/pleroma/web/channels/user_socket.ex b/lib/pleroma/web/channels/user_socket.ex
index fd8918a7d..21b22b409 100644
--- a/lib/pleroma/web/channels/user_socket.ex
+++ b/lib/pleroma/web/channels/user_socket.ex
@@ -1,7 +1,6 @@
defmodule Pleroma.Web.UserSocket do
use Phoenix.Socket
alias Pleroma.User
- alias Comeonin.Pbkdf2
## Channels
# channel "room:*", Pleroma.Web.RoomChannel
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 2c4b591d4..8845419c2 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -1,5 +1,5 @@
defmodule Pleroma.Web.CommonAPI do
- alias Pleroma.{Repo, Activity, Object, User}
+ alias Pleroma.{Repo, Activity, Object}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Formatter
@@ -24,6 +24,16 @@ defmodule Pleroma.Web.CommonAPI do
end
end
+ def unrepeat(id_or_ap_id, user) do
+ with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
+ object <- Object.get_by_ap_id(activity.data["object"]["id"]) do
+ ActivityPub.unannounce(user, object)
+ else
+ _ ->
+ {:error, "Could not unrepeat"}
+ end
+ end
+
def favorite(id_or_ap_id, user) do
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
false <- activity.data["actor"] == user.ap_id,
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 3c092d524..9c9951371 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -1,7 +1,9 @@
defmodule Pleroma.Web.CommonAPI.Utils do
- alias Pleroma.{Repo, Object, Formatter, User, Activity}
+ alias Pleroma.{Repo, Object, Formatter, Activity}
alias Pleroma.Web.ActivityPub.Utils
+ alias Pleroma.User
alias Calendar.Strftime
+ alias Comeonin.Pbkdf2
# This is a hack for twidere.
def get_by_id_or_ap_id(id) do
@@ -49,7 +51,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
{[user.follower_address | to], cc}
end
- def to_for_user_and_mentions(user, mentions, inReplyTo, "direct") do
+ def to_for_user_and_mentions(_user, mentions, inReplyTo, "direct") do
mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end)
if inReplyTo do
@@ -69,7 +71,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def make_context(%Activity{data: %{"context" => context}}), do: context
def make_context(_), do: Utils.generate_context_id()
- def maybe_add_attachments(text, attachments, _no_links = true), do: text
+ def maybe_add_attachments(text, _attachments, _no_links = true), do: text
def maybe_add_attachments(text, attachments, _no_links) do
add_attachments(text, attachments)
@@ -131,7 +133,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
"context" => context,
"attachment" => attachments,
"actor" => actor,
- "tag" => tags |> Enum.map(fn {_, tag} -> tag end)
+ "tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq()
}
if inReplyTo do
@@ -184,4 +186,13 @@ defmodule Pleroma.Web.CommonAPI.Utils do
String.slice(name, 0..30) <> "…"
end
end
+
+ def confirm_current_password(user, password) do
+ with %User{local: true} = db_user <- Repo.get(User, user.id),
+ true <- Pbkdf2.checkpw(password, db_user.password_hash) do
+ {:ok, db_user}
+ else
+ _ -> {:error, "Invalid password."}
+ end
+ end
end
diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex
index 8335add9c..8ca530031 100644
--- a/lib/pleroma/web/federator/federator.ex
+++ b/lib/pleroma/web/federator/federator.ex
@@ -5,6 +5,7 @@ defmodule Pleroma.Web.Federator do
alias Pleroma.Web.{WebFinger, Websub}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Transmogrifier
+ alias Pleroma.Web.ActivityPub.Utils
require Logger
@websub Application.get_env(:pleroma, :websub)
@@ -14,6 +15,10 @@ defmodule Pleroma.Web.Federator do
@federating Keyword.get(@instance, :federating)
@max_jobs 20
+ def init(args) do
+ {:ok, args}
+ end
+
def start_link do
spawn(fn ->
# 1 minute
@@ -87,14 +92,16 @@ defmodule Pleroma.Web.Federator do
def handle(:incoming_ap_doc, params) do
Logger.info("Handling incoming AP activity")
+ params = Utils.normalize_params(params)
+
with {:ok, _user} <- ap_enabled_actor(params["actor"]),
nil <- Activity.get_by_ap_id(params["id"]),
- {:ok, activity} <- Transmogrifier.handle_incoming(params) do
+ {:ok, _activity} <- Transmogrifier.handle_incoming(params) do
else
%Activity{} ->
Logger.info("Already had #{params["id"]}")
- e ->
+ _e ->
# Just drop those for now
Logger.info("Unhandled activity")
Logger.info(Poison.encode!(params, pretty: 2))
@@ -154,7 +161,7 @@ defmodule Pleroma.Web.Federator do
end
end
- def handle_cast({:enqueue, type, payload, priority}, state)
+ def handle_cast({:enqueue, type, payload, _priority}, state)
when type in [:incoming_doc, :incoming_ap_doc] do
%{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state
i_queue = enqueue_sorted(i_queue, {type, payload}, 1)
@@ -162,7 +169,7 @@ defmodule Pleroma.Web.Federator do
{:noreply, %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}}}
end
- def handle_cast({:enqueue, type, payload, priority}, state) do
+ def handle_cast({:enqueue, type, payload, _priority}, state) do
%{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state
o_queue = enqueue_sorted(o_queue, {type, payload}, 1)
{o_running_jobs, o_queue} = maybe_start_job(o_running_jobs, o_queue)
diff --git a/lib/pleroma/web/http_signatures/http_signatures.ex b/lib/pleroma/web/http_signatures/http_signatures.ex
index 0ea3b7554..4e0adbc1d 100644
--- a/lib/pleroma/web/http_signatures/http_signatures.ex
+++ b/lib/pleroma/web/http_signatures/http_signatures.ex
@@ -1,6 +1,7 @@
# https://tools.ietf.org/html/draft-cavage-http-signatures-08
defmodule Pleroma.Web.HTTPSignatures do
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.ActivityPub
require Logger
@@ -31,21 +32,21 @@ defmodule Pleroma.Web.HTTPSignatures do
def validate_conn(conn) do
# TODO: How to get the right key and see if it is actually valid for that request.
# For now, fetch the key for the actor.
- with actor_id <- conn.params["actor"],
+ with actor_id <- Utils.normalize_actor(conn.params["actor"]),
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
if validate_conn(conn, public_key) do
true
else
Logger.debug("Could not validate, re-fetching user and trying one more time")
# Fetch user anew and try one more time
- with actor_id <- conn.params["actor"],
+ with actor_id <- Utils.normalize_actor(conn.params["actor"]),
{:ok, _user} <- ActivityPub.make_user_from_ap_id(actor_id),
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
validate_conn(conn, public_key)
end
end
else
- e ->
+ _e ->
Logger.debug("Could not public key!")
false
end
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 82ddb9a5d..460942f1a 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -112,7 +112,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
version: "#{@mastodon_api_level} (compatible; #{Keyword.get(@instance, :version)})",
email: Keyword.get(@instance, :email),
urls: %{
- streaming_api: String.replace(Web.base_url(), ["http", "https"], "wss")
+ streaming_api: String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws")
},
stats: Stats.get_stats(),
thumbnail: Web.base_url() <> "/instance/thumbnail.jpeg",
@@ -204,22 +204,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
end
- def user_statuses(%{assigns: %{user: user}} = conn, params) do
- with %User{ap_id: ap_id} <- Repo.get(User, params["id"]) do
- params =
- params
- |> Map.put("type", ["Create", "Announce"])
- |> Map.put("actor_id", ap_id)
- |> Map.put("whole_db", true)
-
- if params["pinned"] == "true" do
- # Since Pleroma has no "pinned" posts feature, we'll just set an empty list here
- activities = []
- else
- activities =
- ActivityPub.fetch_public_activities(params)
- |> Enum.reverse()
- end
+ def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
+ with %User{} = user <- Repo.get(User, params["id"]) do
+ # Since Pleroma has no "pinned" posts feature, we'll just set an empty list here
+ activities =
+ if params["pinned"] == "true" do
+ []
+ else
+ ActivityPub.fetch_user_activities(user, reading_user, params)
+ end
conn
|> add_link_headers(:user_statuses, activities, params["id"])
@@ -275,7 +268,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> Map.put("in_reply_to_status_id", params["in_reply_to_id"])
|> Map.put("no_attachment_links", true)
- {:ok, activity} = CommonAPI.post(user, params)
+ idempotency_key =
+ case get_req_header(conn, "idempotency-key") do
+ [key] -> key
+ _ -> Ecto.UUID.generate()
+ end
+
+ {:ok, activity} =
+ Cachex.fetch!(:idempotency_cache, idempotency_key, fn _ -> CommonAPI.post(user, params) end)
+
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
end
@@ -296,6 +297,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
+ def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
+ with {:ok, _, _, %{data: %{"id" => id}}} = CommonAPI.unrepeat(ap_id_or_id, user),
+ %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
+ render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
+ end
+ end
+
def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
with {:ok, _fav, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
@@ -304,7 +312,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
- with {:ok, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
+ with {:ok, _, _, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
end
@@ -445,24 +453,18 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
- # TODO: Clean up and unify
def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
with %User{} = followed <- Repo.get(User, id),
- {:ok, follower, follow_activity} <- User.unfollow(follower, followed),
- {:ok, _activity} <-
- ActivityPub.insert(%{
- "type" => "Undo",
- "actor" => follower.ap_id,
- # get latest Follow for these users
- "object" => follow_activity.data["id"]
- }) do
+ {:ok, _activity} <- ActivityPub.unfollow(follower, followed),
+ {:ok, follower, _} <- User.unfollow(follower, followed) do
render(conn, AccountView, "relationship.json", %{user: follower, target: followed})
end
end
def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
with %User{} = blocked <- Repo.get(User, id),
- {:ok, blocker} <- User.block(blocker, blocked) do
+ {:ok, blocker} <- User.block(blocker, blocked),
+ {:ok, _activity} <- ActivityPub.block(blocker, blocked) do
render(conn, AccountView, "relationship.json", %{user: blocker, target: blocked})
else
{:error, message} ->
@@ -474,7 +476,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
with %User{} = blocked <- Repo.get(User, id),
- {:ok, blocker} <- User.unblock(blocker, blocked) do
+ {:ok, blocker} <- User.unblock(blocker, blocked),
+ {:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do
render(conn, AccountView, "relationship.json", %{user: blocker, target: blocked})
else
{:error, message} ->
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index df360644a..5c6fd05f3 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -82,19 +82,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
}
end
- def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
- id = activity.data["object"]["inReplyTo"]
- replied_to_activities[activity.data["object"]["inReplyTo"]]
- end
-
- def get_reply_to(%{data: %{"object" => object}}, _) do
- if object["inReplyTo"] && object["inReplyTo"] != "" do
- Activity.get_create_activity_by_object_ap_id(object["inReplyTo"])
- else
- nil
- end
- end
-
def render("status.json", %{activity: %{data: %{"object" => object}} = activity} = opts) do
user = User.get_cached_by_ap_id(activity.data["actor"])
@@ -164,19 +151,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
}
end
- def get_visibility(object) do
- public = "https://www.w3.org/ns/activitystreams#Public"
- to = object["to"] || []
- cc = object["cc"] || []
-
- cond do
- public in to -> "public"
- public in cc -> "unlisted"
- Enum.any?(to, &String.contains?(&1, "/followers")) -> "private"
- true -> "direct"
- end
- end
-
def render("attachment.json", %{attachment: attachment}) do
[%{"mediaType" => media_type, "href" => href} | _] = attachment["url"]
@@ -199,4 +173,30 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
type: type
}
end
+
+ def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
+ _id = activity.data["object"]["inReplyTo"]
+ replied_to_activities[activity.data["object"]["inReplyTo"]]
+ end
+
+ def get_reply_to(%{data: %{"object" => object}}, _) do
+ if object["inReplyTo"] && object["inReplyTo"] != "" do
+ Activity.get_create_activity_by_object_ap_id(object["inReplyTo"])
+ else
+ nil
+ end
+ end
+
+ def get_visibility(object) do
+ public = "https://www.w3.org/ns/activitystreams#Public"
+ to = object["to"] || []
+ cc = object["cc"] || []
+
+ cond do
+ public in to -> "public"
+ public in cc -> "unlisted"
+ Enum.any?(to, &String.contains?(&1, "/followers")) -> "private"
+ true -> "direct"
+ end
+ end
end
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo.ex b/lib/pleroma/web/nodeinfo/nodeinfo.ex
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/lib/pleroma/web/nodeinfo/nodeinfo.ex
@@ -0,0 +1 @@
+
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
new file mode 100644
index 000000000..aec77168a
--- /dev/null
+++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
@@ -0,0 +1,62 @@
+defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
+ use Pleroma.Web, :controller
+
+ alias Pleroma.Stats
+ alias Pleroma.Web
+
+ @instance Application.get_env(:pleroma, :instance)
+
+ def schemas(conn, _params) do
+ response = %{
+ links: [
+ %{
+ rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
+ href: Web.base_url() <> "/nodeinfo/2.0.json"
+ }
+ ]
+ }
+
+ json(conn, response)
+ end
+
+ # Schema definition: https://github.com/jhass/nodeinfo/blob/master/schemas/2.0/schema.json
+ def nodeinfo(conn, %{"version" => "2.0"}) do
+ stats = Stats.get_stats()
+
+ response = %{
+ version: "2.0",
+ software: %{
+ name: "pleroma",
+ version: Keyword.get(@instance, :version)
+ },
+ protocols: ["ostatus", "activitypub"],
+ services: %{
+ inbound: [],
+ outbound: []
+ },
+ openRegistrations: Keyword.get(@instance, :registrations_open),
+ usage: %{
+ users: %{
+ total: stats.user_count || 0
+ },
+ localPosts: stats.status_count || 0
+ },
+ metadata: %{
+ nodeName: Keyword.get(@instance, :name)
+ }
+ }
+
+ conn
+ |> put_resp_header(
+ "content-type",
+ "application/json; profile=http://nodeinfo.diaspora.software/ns/schema/2.0#; charset=utf-8"
+ )
+ |> json(response)
+ end
+
+ def nodeinfo(conn, _) do
+ conn
+ |> put_status(404)
+ |> json(%{error: "Nodeinfo schema version not handled"})
+ end
+end
diff --git a/lib/pleroma/web/oauth/authorization.ex b/lib/pleroma/web/oauth/authorization.ex
index 94f44c9f2..23e8eb7b1 100644
--- a/lib/pleroma/web/oauth/authorization.ex
+++ b/lib/pleroma/web/oauth/authorization.ex
@@ -11,7 +11,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
field(:valid_until, :naive_datetime)
field(:used, :boolean, default: false)
belongs_to(:user, Pleroma.User)
- belongs_to(:app, Pleroma.App)
+ belongs_to(:app, App)
timestamps()
end
diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex
index 65abd78c8..343fc0c45 100644
--- a/lib/pleroma/web/oauth/token.ex
+++ b/lib/pleroma/web/oauth/token.ex
@@ -9,7 +9,7 @@ defmodule Pleroma.Web.OAuth.Token do
field(:refresh_token, :string)
field(:valid_until, :naive_datetime)
belongs_to(:user, Pleroma.User)
- belongs_to(:app, Pleroma.App)
+ belongs_to(:app, App)
timestamps()
end
diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex
index 921a89bd0..4179d86c9 100644
--- a/lib/pleroma/web/ostatus/activity_representer.ex
+++ b/lib/pleroma/web/ostatus/activity_representer.ex
@@ -1,7 +1,6 @@
defmodule Pleroma.Web.OStatus.ActivityRepresenter do
alias Pleroma.{Activity, User, Object}
alias Pleroma.Web.OStatus.UserRepresenter
- alias Pleroma.Formatter
require Logger
defp get_href(id) do
@@ -233,16 +232,21 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
end
# Only undos of follow for now. Will need to get redone once there are more
- def to_simple_form(%{data: %{"type" => "Undo"}} = activity, user, with_author) do
+ def to_simple_form(
+ %{data: %{"type" => "Undo", "object" => %{"type" => "Follow"} = follow_activity}} =
+ activity,
+ user,
+ with_author
+ ) do
h = fn str -> [to_charlist(str)] end
updated_at = activity.data["published"]
inserted_at = activity.data["published"]
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
- follow_activity = Activity.get_by_ap_id(activity.data["object"])
mentions = (activity.recipients || []) |> get_mentions
+ follow_activity = Activity.get_by_ap_id(follow_activity["id"])
[
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
diff --git a/lib/pleroma/web/ostatus/handlers/note_handler.ex b/lib/pleroma/web/ostatus/handlers/note_handler.ex
index bd6e92238..0d4080291 100644
--- a/lib/pleroma/web/ostatus/handlers/note_handler.ex
+++ b/lib/pleroma/web/ostatus/handlers/note_handler.ex
@@ -1,7 +1,7 @@
defmodule Pleroma.Web.OStatus.NoteHandler do
require Logger
alias Pleroma.Web.{XML, OStatus}
- alias Pleroma.{Object, User, Activity}
+ alias Pleroma.{Object, Activity}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI
diff --git a/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex
new file mode 100644
index 000000000..a115bf4c8
--- /dev/null
+++ b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex
@@ -0,0 +1,17 @@
+defmodule Pleroma.Web.OStatus.UnfollowHandler do
+ alias Pleroma.Web.{XML, OStatus}
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.User
+
+ def handle(entry, doc) do
+ with {:ok, actor} <- OStatus.find_make_or_update_user(doc),
+ id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
+ followed_uri when not is_nil(followed_uri) <-
+ XML.string_from_xpath("/entry/activity:object/id", entry),
+ {:ok, followed} <- OStatus.find_or_make_user(followed_uri),
+ {:ok, activity} <- ActivityPub.unfollow(actor, followed, id, false) do
+ User.unfollow(actor, followed)
+ {:ok, activity}
+ end
+ end
+end
diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex
index 5c4a1fd69..f0ff0624f 100644
--- a/lib/pleroma/web/ostatus/ostatus.ex
+++ b/lib/pleroma/web/ostatus/ostatus.ex
@@ -8,7 +8,7 @@ defmodule Pleroma.Web.OStatus do
alias Pleroma.{Repo, User, Web, Object, Activity}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.{WebFinger, Websub}
- alias Pleroma.Web.OStatus.{FollowHandler, NoteHandler, DeleteHandler}
+ alias Pleroma.Web.OStatus.{FollowHandler, UnfollowHandler, NoteHandler, DeleteHandler}
alias Pleroma.Web.ActivityPub.Transmogrifier
def feed_path(user) do
@@ -47,6 +47,9 @@ defmodule Pleroma.Web.OStatus do
'http://activitystrea.ms/schema/1.0/follow' ->
with {:ok, activity} <- FollowHandler.handle(entry, doc), do: activity
+ 'http://activitystrea.ms/schema/1.0/unfollow' ->
+ with {:ok, activity} <- UnfollowHandler.handle(entry, doc), do: activity
+
'http://activitystrea.ms/schema/1.0/share' ->
with {:ok, activity, retweeted_activity} <- handle_share(entry, doc),
do: [activity, retweeted_activity]
diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex
index a02f55fe6..f39ebaf2b 100644
--- a/lib/pleroma/web/ostatus/ostatus_controller.ex
+++ b/lib/pleroma/web/ostatus/ostatus_controller.ex
@@ -8,7 +8,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
alias Pleroma.Web.XML
alias Pleroma.Web.ActivityPub.ActivityPubController
alias Pleroma.Web.ActivityPub.ActivityPub
- import Ecto.Query
def feed_redirect(conn, %{"nickname" => nickname} = params) do
user = User.get_cached_by_nickname(nickname)
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index c58f77817..726275158 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -73,6 +73,8 @@ defmodule Pleroma.Web.Router do
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
pipe_through(:authenticated_api)
post("/follow_import", UtilController, :follow_import)
+ post("/change_password", UtilController, :change_password)
+ post("/delete_account", UtilController, :delete_account)
end
scope "/oauth", Pleroma.Web.OAuth do
@@ -111,6 +113,7 @@ defmodule Pleroma.Web.Router do
delete("/statuses/:id", MastodonAPIController, :delete_status)
post("/statuses/:id/reblog", MastodonAPIController, :reblog_status)
+ post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)
post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
@@ -304,6 +307,11 @@ defmodule Pleroma.Web.Router do
get("/host-meta", WebFinger.WebFingerController, :host_meta)
get("/webfinger", WebFinger.WebFingerController, :webfinger)
+ get("/nodeinfo", Nodeinfo.NodeinfoController, :schemas)
+ end
+
+ scope "/nodeinfo", Pleroma.Web do
+ get("/:version", Nodeinfo.NodeinfoController, :nodeinfo)
end
end
diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex
index 10542fd00..562ec3d9c 100644
--- a/lib/pleroma/web/salmon/salmon.ex
+++ b/lib/pleroma/web/salmon/salmon.ex
@@ -187,13 +187,14 @@ defmodule Pleroma.Web.Salmon do
def publish(%{info: %{"keys" => keys}} = user, %{data: %{"type" => type}} = activity, poster)
when type in @supported_activities do
- feed =
- ActivityRepresenter.to_simple_form(activity, user, true)
- |> ActivityRepresenter.wrap_with_entry()
- |> :xmerl.export_simple(:xmerl_xml)
- |> to_string
+ feed = ActivityRepresenter.to_simple_form(activity, user, true)
if feed do
+ feed =
+ ActivityRepresenter.wrap_with_entry(feed)
+ |> :xmerl.export_simple(:xmerl_xml)
+ |> to_string
+
{:ok, private, _} = keys_from_pem(keys)
{:ok, feed} = encode(private, feed)
diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex
index 10670e71f..33041ec12 100644
--- a/lib/pleroma/web/streamer.ex
+++ b/lib/pleroma/web/streamer.ex
@@ -3,6 +3,10 @@ defmodule Pleroma.Web.Streamer do
require Logger
alias Pleroma.{User, Notification}
+ def init(args) do
+ {:ok, args}
+ end
+
def start_link do
spawn(fn ->
# 30 seconds
@@ -110,20 +114,26 @@ defmodule Pleroma.Web.Streamer do
def push_to_socket(topics, topic, item) do
Enum.each(topics[topic] || [], fn socket ->
- json =
- %{
- event: "update",
- payload:
- Pleroma.Web.MastodonAPI.StatusView.render(
- "status.json",
- activity: item,
- for: socket.assigns[:user]
- )
- |> Jason.encode!()
- }
- |> Jason.encode!()
-
- send(socket.transport_pid, {:text, json})
+ # Get the current user so we have up-to-date blocks etc.
+ user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
+ blocks = user.info["blocks"] || []
+
+ unless item.actor in blocks do
+ json =
+ %{
+ event: "update",
+ payload:
+ Pleroma.Web.MastodonAPI.StatusView.render(
+ "status.json",
+ activity: item,
+ for: user
+ )
+ |> Jason.encode!()
+ }
+ |> Jason.encode!()
+
+ send(socket.transport_pid, {:text, json})
+ end
end)
end
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index c7b1a5b95..cc5146566 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -4,6 +4,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
alias Pleroma.Web
alias Pleroma.Web.OStatus
alias Pleroma.Web.WebFinger
+ alias Pleroma.Web.CommonAPI
alias Comeonin.Pbkdf2
alias Pleroma.Formatter
alias Pleroma.Web.ActivityPub.ActivityPub
@@ -92,7 +93,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
with %User{} = user <- User.get_cached_by_nickname(username),
true <- Pbkdf2.checkpw(password, user.password_hash),
- %User{} = followed <- Repo.get(User, id),
+ %User{} = _followed <- Repo.get(User, id),
{:ok, follower} <- User.follow(user, followee),
{:ok, _activity} <- ActivityPub.follow(follower, followee) do
conn
@@ -195,4 +196,38 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
json(conn, "job started")
end
+
+ def change_password(%{assigns: %{user: user}} = conn, params) do
+ case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
+ {:ok, user} ->
+ with {:ok, _user} <-
+ User.reset_password(user, %{
+ password: params["new_password"],
+ password_confirmation: params["new_password_confirmation"]
+ }) do
+ json(conn, %{status: "success"})
+ else
+ {:error, changeset} ->
+ {_, {error, _}} = Enum.at(changeset.errors, 0)
+ json(conn, %{error: "New password #{error}."})
+
+ _ ->
+ json(conn, %{error: "Unable to change password."})
+ end
+
+ {:error, msg} ->
+ json(conn, %{error: msg})
+ end
+ end
+
+ def delete_account(%{assigns: %{user: user}} = conn, params) do
+ case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
+ {:ok, user} ->
+ Task.start(fn -> User.delete(user) end)
+ json(conn, %{status: "success"})
+
+ {:error, msg} ->
+ json(conn, %{error: msg})
+ end
+ end
end
diff --git a/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex
index 9a4954de8..57837205e 100644
--- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex
+++ b/lib/pleroma/web/twitter_api/representers/activity_representer.ex
@@ -99,7 +99,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
) do
created_at = created_at |> Utils.date_to_asctime()
- text = "#{user.nickname} undid the action at #{undid_activity}"
+ text = "#{user.nickname} undid the action at #{undid_activity["id"]}"
%{
"id" => activity.id,
@@ -197,7 +197,8 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
"external_url" => object["external_url"] || object["id"],
"tags" => tags,
"activity_type" => "post",
- "possibly_sensitive" => possibly_sensitive
+ "possibly_sensitive" => possibly_sensitive,
+ "visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object)
}
end
diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex
index c12cd7f8a..3ccdaed6f 100644
--- a/lib/pleroma/web/twitter_api/twitter_api.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api.ex
@@ -1,7 +1,6 @@
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
alias Pleroma.{User, Activity, Repo, Object}
alias Pleroma.Web.ActivityPub.ActivityPub
- alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter
alias Pleroma.Web.TwitterAPI.UserView
alias Pleroma.Web.{OStatus, CommonAPI}
import Ecto.Query
@@ -12,6 +11,18 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
CommonAPI.post(user, data)
end
+ def delete(%User{} = user, id) do
+ # TwitterAPI does not have an "unretweet" endpoint; instead this is done
+ # via the "destroy" endpoint. Therefore, we need to handle
+ # when the status to "delete" is actually an Announce (repeat) object.
+ with %Activity{data: %{"type" => type}} <- Repo.get(Activity, id) do
+ case type do
+ "Announce" -> unrepeat(user, id)
+ _ -> CommonAPI.delete(id, user)
+ end
+ end
+ end
+
def follow(%User{} = follower, params) do
with {:ok, %User{} = followed} <- get_user(params),
{:ok, follower} <- User.follow(follower, followed),
@@ -25,14 +36,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
def unfollow(%User{} = follower, params) do
with {:ok, %User{} = unfollowed} <- get_user(params),
{:ok, follower, follow_activity} <- User.unfollow(follower, unfollowed),
- {:ok, _activity} <-
- ActivityPub.insert(%{
- "type" => "Undo",
- "actor" => follower.ap_id,
- # get latest Follow for these users
- "object" => follow_activity.data["id"],
- "published" => make_date()
- }) do
+ {:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed) do
{:ok, follower, unfollowed}
else
err -> err
@@ -41,7 +45,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
def block(%User{} = blocker, params) do
with {:ok, %User{} = blocked} <- get_user(params),
- {:ok, blocker} <- User.block(blocker, blocked) do
+ {:ok, blocker} <- User.block(blocker, blocked),
+ {:ok, _activity} <- ActivityPub.block(blocker, blocked) do
{:ok, blocker, blocked}
else
err -> err
@@ -50,7 +55,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
def unblock(%User{} = blocker, params) do
with {:ok, %User{} = blocked} <- get_user(params),
- {:ok, blocker} <- User.unblock(blocker, blocked) do
+ {:ok, blocker} <- User.unblock(blocker, blocked),
+ {:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do
{:ok, blocker, blocked}
else
err -> err
@@ -64,15 +70,21 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end
end
+ defp unrepeat(%User{} = user, ap_id_or_id) do
+ with {:ok, _unannounce, activity, _object} <- CommonAPI.unrepeat(ap_id_or_id, user) do
+ {:ok, activity}
+ end
+ end
+
def fav(%User{} = user, ap_id_or_id) do
- with {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),
+ with {:ok, _fav, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
{:ok, activity}
end
end
def unfav(%User{} = user, ap_id_or_id) do
- with {:ok, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
+ with {:ok, _unfav, _fav, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
{:ok, activity}
end
@@ -184,7 +196,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
defp parse_int(_, default), do: default
- def search(user, %{"q" => query} = params) do
+ def search(_user, %{"q" => query} = params) do
limit = parse_int(params["rpp"], 20)
page = parse_int(params["page"], 1)
offset = (page - 1) * limit
@@ -206,7 +218,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
order_by: [desc: :inserted_at]
)
- activities = Repo.all(q)
+ _activities = Repo.all(q)
end
defp make_date do
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index 6cf8682b8..dd1dc241d 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -96,13 +96,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def user_timeline(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.get_user(user, params) do
{:ok, target_user} ->
- params =
- params
- |> Map.put("type", ["Create", "Announce"])
- |> Map.put("actor_id", target_user.ap_id)
- |> Map.put("whole_db", true)
-
- activities = ActivityPub.fetch_public_activities(params)
+ activities = ActivityPub.fetch_user_activities(target_user, user, params)
conn
|> render(ActivityView, "index.json", %{activities: activities, for: user})
@@ -157,8 +151,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end
def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do
- with {:ok, delete} <- CommonAPI.delete(id, user) do
- render(conn, ActivityView, "activity.json", %{activity: delete, for: user})
+ with {:ok, activity} <- TwitterAPI.delete(user, id) do
+ render(conn, ActivityView, "activity.json", %{activity: activity, for: user})
end
end
@@ -347,7 +341,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def update_profile(%{assigns: %{user: user}} = conn, params) do
params =
if bio = params["description"] do
- Map.put(params, "bio", bio)
+ bio_brs = Regex.replace(~r/\r?\n/, bio, "<br>")
+ Map.put(params, "bio", bio_brs)
else
params
end
diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex
index 4e4433aed..62ce3b7b5 100644
--- a/lib/pleroma/web/twitter_api/views/activity_view.ex
+++ b/lib/pleroma/web/twitter_api/views/activity_view.ex
@@ -31,7 +31,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
end
defp collect_context_ids(activities) do
- contexts =
+ _contexts =
activities
|> Enum.reject(& &1.data["context_id"])
|> Enum.map(fn %{data: data} ->
@@ -262,7 +262,8 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
"external_url" => object["external_url"] || object["id"],
"tags" => tags,
"activity_type" => "post",
- "possibly_sensitive" => possibly_sensitive
+ "possibly_sensitive" => possibly_sensitive,
+ "visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object)
}
end
end
diff --git a/lib/pleroma/web/twitter_api/views/notification_view.ex b/lib/pleroma/web/twitter_api/views/notification_view.ex
index f41edea0b..9eeb3afdc 100644
--- a/lib/pleroma/web/twitter_api/views/notification_view.ex
+++ b/lib/pleroma/web/twitter_api/views/notification_view.ex
@@ -2,7 +2,6 @@ defmodule Pleroma.Web.TwitterAPI.NotificationView do
use Pleroma.Web, :view
alias Pleroma.{Notification, User}
alias Pleroma.Web.CommonAPI.Utils
- alias Pleroma.Web.MediaProxy
alias Pleroma.Web.TwitterAPI.UserView
alias Pleroma.Web.TwitterAPI.ActivityView
diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex
index dc9ad2014..9c6f1cb68 100644
--- a/lib/pleroma/web/web_finger/web_finger.ex
+++ b/lib/pleroma/web/web_finger/web_finger.ex
@@ -1,7 +1,7 @@
defmodule Pleroma.Web.WebFinger do
@httpoison Application.get_env(:pleroma, :httpoison)
- alias Pleroma.{Repo, User, XmlBuilder}
+ alias Pleroma.{User, XmlBuilder}
alias Pleroma.Web
alias Pleroma.Web.{XML, Salmon, OStatus}
require Jason
@@ -87,6 +87,11 @@ defmodule Pleroma.Web.WebFinger 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
+ },
+ %{
"rel" => "http://ostatus.org/schema/1.0/subscribe",
"template" => OStatus.remote_follow_path()
}
@@ -183,6 +188,9 @@ defmodule Pleroma.Web.WebFinger do
{"application/activity+json", "self"} ->
Map.put(data, "ap_id", link["href"])
+ {"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} ->
+ Map.put(data, "ap_id", link["href"])
+
{_, "magic-public-key"} ->
"data:application/magic-public-key," <> magic_key = link["href"]
Map.put(data, "magic_key", magic_key)
@@ -206,7 +214,7 @@ defmodule Pleroma.Web.WebFinger do
end
def get_template_from_xml(body) do
- xpath = "//Link[@rel='lrdd' and @type='application/xrd+xml']/@template"
+ xpath = "//Link[@rel='lrdd']/@template"
with doc when doc != :error <- XML.parse_document(body),
template when template != nil <- XML.string_from_xpath(xpath, doc) do
@@ -239,13 +247,14 @@ defmodule Pleroma.Web.WebFinger do
URI.parse(account).host
end
- case find_lrdd_template(domain) do
- {:ok, template} ->
- address = String.replace(template, "{uri}", URI.encode(account))
+ address =
+ case find_lrdd_template(domain) do
+ {:ok, template} ->
+ String.replace(template, "{uri}", URI.encode(account))
- _ ->
- address = "http://#{domain}/.well-known/webfinger?resource=acct:#{account}"
- end
+ _ ->
+ "http://#{domain}/.well-known/webfinger?resource=acct:#{account}"
+ end
with response <-
@httpoison.get(
diff --git a/lib/pleroma/web/xml/xml.ex b/lib/pleroma/web/xml/xml.ex
index 8b609f695..36430a3fa 100644
--- a/lib/pleroma/web/xml/xml.ex
+++ b/lib/pleroma/web/xml/xml.ex
@@ -14,7 +14,7 @@ defmodule Pleroma.Web.XML do
if res == "", do: nil, else: res
catch
- e ->
+ _e ->
Logger.debug("Couldn't find xpath #{xpath} in XML doc")
nil
end