aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/healthcheck.ex4
-rw-r--r--lib/mix/tasks/pleroma/database.ex75
-rw-r--r--lib/mix/tasks/pleroma/user.ex19
-rw-r--r--lib/pleroma/activity.ex33
-rw-r--r--lib/pleroma/application.ex20
-rw-r--r--lib/pleroma/bbs/handler.ex1
-rw-r--r--lib/pleroma/conversation.ex21
-rw-r--r--lib/pleroma/conversation/participation.ex10
-rw-r--r--lib/pleroma/emails/admin_email.ex2
-rw-r--r--lib/pleroma/emoji.ex4
-rw-r--r--lib/pleroma/filter.ex3
-rw-r--r--lib/pleroma/formatter.ex2
-rw-r--r--lib/pleroma/html.ex20
-rw-r--r--lib/pleroma/http/connection.ex4
-rw-r--r--lib/pleroma/http/http.ex7
-rw-r--r--lib/pleroma/http/request_builder.ex11
-rw-r--r--lib/pleroma/keys.ex44
-rw-r--r--lib/pleroma/object.ex7
-rw-r--r--lib/pleroma/object/fetcher.ex25
-rw-r--r--lib/pleroma/plugs/federating_plug.ex2
-rw-r--r--lib/pleroma/plugs/http_security_plug.ex31
-rw-r--r--lib/pleroma/reverse_proxy.ex7
-rw-r--r--lib/pleroma/signature.ex7
-rw-r--r--lib/pleroma/uploaders/mdii.ex5
-rw-r--r--lib/pleroma/user.ex81
-rw-r--r--lib/pleroma/user/info.ex11
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex106
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex16
-rw-r--r--lib/pleroma/web/activity_pub/mrf.ex4
-rw-r--r--lib/pleroma/web/activity_pub/mrf/simple_policy.ex61
-rw-r--r--lib/pleroma/web/activity_pub/mrf/tag_policy.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf/user_allowlist.ex6
-rw-r--r--lib/pleroma/web/activity_pub/publisher.ex5
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex6
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex78
-rw-r--r--lib/pleroma/web/activity_pub/views/user_view.ex11
-rw-r--r--lib/pleroma/web/activity_pub/visibility.ex35
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex81
-rw-r--r--lib/pleroma/web/admin_api/views/report_view.ex41
-rw-r--r--lib/pleroma/web/common_api/common_api.ex67
-rw-r--r--lib/pleroma/web/common_api/utils.ex14
-rw-r--r--lib/pleroma/web/endpoint.ex21
-rw-r--r--lib/pleroma/web/federator/federator.ex14
-rw-r--r--lib/pleroma/web/federator/publisher.ex10
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex87
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex4
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex12
-rw-r--r--lib/pleroma/web/media_proxy/media_proxy.ex30
-rw-r--r--lib/pleroma/web/mongooseim/mongoose_im_controller.ex41
-rw-r--r--lib/pleroma/web/nodeinfo/nodeinfo_controller.ex68
-rw-r--r--lib/pleroma/web/oauth/authorization.ex2
-rw-r--r--lib/pleroma/web/oauth/token.ex38
-rw-r--r--lib/pleroma/web/oauth/token/clean_worker.ex41
-rw-r--r--lib/pleroma/web/oauth/token/query.ex55
-rw-r--r--lib/pleroma/web/ostatus/ostatus.ex7
-rw-r--r--lib/pleroma/web/rich_media/helpers.ex1
-rw-r--r--lib/pleroma/web/rich_media/parser.ex5
-rw-r--r--lib/pleroma/web/router.ex23
-rw-r--r--lib/pleroma/web/salmon/salmon.ex49
-rw-r--r--lib/pleroma/web/templates/layout/app.html.eex4
-rw-r--r--lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex2
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api_controller.ex6
-rw-r--r--lib/pleroma/web/twitter_api/views/activity_view.ex8
-rw-r--r--lib/pleroma/web/web_finger/web_finger.ex35
-rw-r--r--lib/pleroma/web/websub/websub.ex11
65 files changed, 1163 insertions, 400 deletions
diff --git a/lib/healthcheck.ex b/lib/healthcheck.ex
index 646fb3b9d..32aafc210 100644
--- a/lib/healthcheck.ex
+++ b/lib/healthcheck.ex
@@ -29,13 +29,13 @@ defmodule Pleroma.Healthcheck do
end
defp assign_db_info(healthcheck) do
- database = Application.get_env(:pleroma, Repo)[:database]
+ database = Pleroma.Config.get([Repo, :database])
query =
"select state, count(pid) from pg_stat_activity where datname = '#{database}' group by state;"
result = Repo.query!(query)
- pool_size = Application.get_env(:pleroma, Repo)[:pool_size]
+ pool_size = Pleroma.Config.get([Repo, :pool_size])
db_info =
Enum.reduce(result.rows, %{active: 0, idle: 0}, fn [state, cnt], states ->
diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex
index ab9a3a7ff..4d480ac3f 100644
--- a/lib/mix/tasks/pleroma/database.ex
+++ b/lib/mix/tasks/pleroma/database.ex
@@ -4,6 +4,10 @@
defmodule Mix.Tasks.Pleroma.Database do
alias Mix.Tasks.Pleroma.Common
+ alias Pleroma.Conversation
+ alias Pleroma.Object
+ alias Pleroma.Repo
+ alias Pleroma.User
require Logger
use Mix.Task
@@ -19,6 +23,18 @@ defmodule Mix.Tasks.Pleroma.Database do
Options:
- `--vacuum` - run `VACUUM FULL` after the embedded objects are replaced with their references
+
+ ## Prune old objects from the database
+
+ mix pleroma.database prune_objects
+
+ ## Create a conversation for all existing DMs. Can be safely re-run.
+
+ mix pleroma.database bump_all_conversations
+
+ ## Remove duplicated items from following and update followers count for all users
+
+ mix pleroma.database update_users_following_followers_counts
"""
def run(["remove_embedded_objects" | args]) do
{options, [], []} =
@@ -32,7 +48,7 @@ defmodule Mix.Tasks.Pleroma.Database do
Common.start_pleroma()
Logger.info("Removing embedded objects")
- Pleroma.Repo.query!(
+ Repo.query!(
"update activities set data = jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;",
[],
timeout: :infinity
@@ -41,7 +57,62 @@ defmodule Mix.Tasks.Pleroma.Database do
if Keyword.get(options, :vacuum) do
Logger.info("Runnning VACUUM FULL")
- Pleroma.Repo.query!(
+ Repo.query!(
+ "vacuum full;",
+ [],
+ timeout: :infinity
+ )
+ end
+ end
+
+ def run(["bump_all_conversations"]) do
+ Common.start_pleroma()
+ Conversation.bump_for_all_activities()
+ end
+
+ def run(["update_users_following_followers_counts"]) do
+ Common.start_pleroma()
+
+ users = Repo.all(User)
+ Enum.each(users, &User.remove_duplicated_following/1)
+ Enum.each(users, &User.update_follower_count/1)
+ end
+
+ def run(["prune_objects" | args]) do
+ import Ecto.Query
+
+ {options, [], []} =
+ OptionParser.parse(
+ args,
+ strict: [
+ vacuum: :boolean
+ ]
+ )
+
+ Common.start_pleroma()
+
+ deadline = Pleroma.Config.get([:instance, :remote_post_retention_days])
+
+ Logger.info("Pruning objects older than #{deadline} days")
+
+ time_deadline =
+ NaiveDateTime.utc_now()
+ |> NaiveDateTime.add(-(deadline * 86_400))
+
+ public = "https://www.w3.org/ns/activitystreams#Public"
+
+ from(o in Object,
+ where: fragment("?->'to' \\? ? OR ?->'cc' \\? ?", o.data, ^public, o.data, ^public),
+ where: o.inserted_at < ^time_deadline,
+ where:
+ fragment("split_part(?->>'actor', '/', 3) != ?", o.data, ^Pleroma.Web.Endpoint.host())
+ )
+ |> Repo.delete_all(timeout: :infinity)
+
+ if Keyword.get(options, :vacuum) do
+ Logger.info("Runnning VACUUM FULL")
+
+ Repo.query!(
"vacuum full;",
[],
timeout: :infinity
diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex
index d130ff8c9..25fc40ea7 100644
--- a/lib/mix/tasks/pleroma/user.ex
+++ b/lib/mix/tasks/pleroma/user.ex
@@ -77,6 +77,10 @@ defmodule Mix.Tasks.Pleroma.User do
## Delete tags from a user.
mix pleroma.user untag NICKNAME TAGS
+
+ ## Toggle confirmation of the user's account.
+
+ mix pleroma.user toggle_confirmed NICKNAME
"""
def run(["new", nickname, email | rest]) do
{options, [], []} =
@@ -388,6 +392,21 @@ defmodule Mix.Tasks.Pleroma.User do
end
end
+ def run(["toggle_confirmed", nickname]) do
+ Common.start_pleroma()
+
+ with %User{} = user <- User.get_cached_by_nickname(nickname) do
+ {:ok, user} = User.toggle_confirmation(user)
+
+ message = if user.info.confirmation_pending, do: "needs", else: "doesn't need"
+
+ Mix.shell().info("#{nickname} #{message} confirmation.")
+ else
+ _ ->
+ Mix.shell().error("No local user #{nickname}")
+ end
+ end
+
defp set_moderator(user, value) do
info_cng = User.Info.admin_api_update(user.info, %{is_moderator: value})
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index 4a0919478..99589590c 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -10,6 +10,7 @@ defmodule Pleroma.Activity do
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Repo
+ alias Pleroma.ThreadMute
alias Pleroma.User
import Ecto.Changeset
@@ -37,6 +38,7 @@ defmodule Pleroma.Activity do
field(:local, :boolean, default: true)
field(:actor, :string)
field(:recipients, {:array, :string}, default: [])
+ field(:thread_muted?, :boolean, virtual: true)
# This is a fake relation, do not use outside of with_preloaded_bookmark/get_bookmark
has_one(:bookmark, Bookmark)
has_many(:notifications, Notification, on_delete: :delete_all)
@@ -60,21 +62,24 @@ defmodule Pleroma.Activity do
timestamps()
end
- def with_preloaded_object(query) do
- query
- |> join(
- :inner,
- [activity],
- o in Object,
+ def with_joined_object(query) do
+ join(query, :inner, [activity], o in Object,
on:
fragment(
"(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
o.data,
activity.data,
activity.data
- )
+ ),
+ as: :object
)
- |> preload([activity, object], object: object)
+ end
+
+ def with_preloaded_object(query) do
+ query
+ |> has_named_binding?(:object)
+ |> if(do: query, else: with_joined_object(query))
+ |> preload([activity, object: object], object: object)
end
def with_preloaded_bookmark(query, %User{} = user) do
@@ -87,6 +92,16 @@ defmodule Pleroma.Activity do
def with_preloaded_bookmark(query, _), do: query
+ def with_set_thread_muted_field(query, %User{} = user) do
+ from([a] in query,
+ left_join: tm in ThreadMute,
+ on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data),
+ select: %Activity{a | thread_muted?: not is_nil(tm.id)}
+ )
+ end
+
+ def with_set_thread_muted_field(query, _), do: query
+
def get_by_ap_id(ap_id) do
Repo.one(
from(
@@ -108,7 +123,7 @@ defmodule Pleroma.Activity do
def change(struct, params \\ %{}) do
struct
- |> cast(params, [:data])
+ |> cast(params, [:data, :recipients])
|> validate_required([:data])
|> unique_constraint(:ap_id, name: :activities_unique_apid_index)
end
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index eeb415084..76df3945e 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -110,6 +110,7 @@ defmodule Pleroma.Application do
hackney_pool_children() ++
[
worker(Pleroma.Web.Federator.RetryQueue, []),
+ worker(Pleroma.Web.OAuth.Token.CleanWorker, []),
worker(Pleroma.Stats, []),
worker(Task, [&Pleroma.Web.Push.init/0], restart: :temporary, id: :web_push_init),
worker(Task, [&Pleroma.Web.Federator.init/0], restart: :temporary, id: :federator_init)
@@ -131,19 +132,22 @@ defmodule Pleroma.Application do
defp setup_instrumenters do
require Prometheus.Registry
- :ok =
- :telemetry.attach(
- "prometheus-ecto",
- [:pleroma, :repo, :query],
- &Pleroma.Repo.Instrumenter.handle_event/4,
- %{}
- )
+ if Application.get_env(:prometheus, Pleroma.Repo.Instrumenter) do
+ :ok =
+ :telemetry.attach(
+ "prometheus-ecto",
+ [:pleroma, :repo, :query],
+ &Pleroma.Repo.Instrumenter.handle_event/4,
+ %{}
+ )
+
+ Pleroma.Repo.Instrumenter.setup()
+ end
Prometheus.Registry.register_collector(:prometheus_process_collector)
Pleroma.Web.Endpoint.MetricsExporter.setup()
Pleroma.Web.Endpoint.PipelineInstrumenter.setup()
Pleroma.Web.Endpoint.Instrumenter.setup()
- Pleroma.Repo.Instrumenter.setup()
end
def enabled_hackney_pools do
diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex
index 106fe5d18..f34be961f 100644
--- a/lib/pleroma/bbs/handler.ex
+++ b/lib/pleroma/bbs/handler.ex
@@ -95,7 +95,6 @@ defmodule Pleroma.BBS.Handler do
activities =
[user.ap_id | user.following]
|> ActivityPub.fetch_activities(params)
- |> ActivityPub.contain_timeline(user)
Enum.each(activities, fn activity ->
puts_activity(activity)
diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex
index 0db195988..238c1acf2 100644
--- a/lib/pleroma/conversation.ex
+++ b/lib/pleroma/conversation.ex
@@ -45,7 +45,7 @@ defmodule Pleroma.Conversation do
2. Create a participation for all the people involved who don't have one already
3. Bump all relevant participations to 'unread'
"""
- def create_or_bump_for(activity) do
+ def create_or_bump_for(activity, opts \\ []) do
with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity),
"Create" <- activity.data["type"],
object <- Pleroma.Object.normalize(activity),
@@ -58,7 +58,7 @@ defmodule Pleroma.Conversation do
participations =
Enum.map(users, fn user ->
{:ok, participation} =
- Participation.create_for_user_and_conversation(user, conversation)
+ Participation.create_for_user_and_conversation(user, conversation, opts)
participation
end)
@@ -72,4 +72,21 @@ defmodule Pleroma.Conversation do
e -> {:error, e}
end
end
+
+ @doc """
+ This is only meant to be run by a mix task. It creates conversations/participations for all direct messages in the database.
+ """
+ def bump_for_all_activities do
+ stream =
+ Pleroma.Web.ActivityPub.ActivityPub.fetch_direct_messages_query()
+ |> Repo.stream()
+
+ Repo.transaction(
+ fn ->
+ stream
+ |> Enum.each(fn a -> create_or_bump_for(a, read: true) end)
+ end,
+ timeout: :infinity
+ )
+ end
end
diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex
index 61021fb18..2a11f9069 100644
--- a/lib/pleroma/conversation/participation.ex
+++ b/lib/pleroma/conversation/participation.ex
@@ -22,15 +22,17 @@ defmodule Pleroma.Conversation.Participation do
def creation_cng(struct, params) do
struct
- |> cast(params, [:user_id, :conversation_id])
+ |> cast(params, [:user_id, :conversation_id, :read])
|> validate_required([:user_id, :conversation_id])
end
- def create_for_user_and_conversation(user, conversation) do
+ def create_for_user_and_conversation(user, conversation, opts \\ []) do
+ read = !!opts[:read]
+
%__MODULE__{}
- |> creation_cng(%{user_id: user.id, conversation_id: conversation.id})
+ |> creation_cng(%{user_id: user.id, conversation_id: conversation.id, read: read})
|> Repo.insert(
- on_conflict: [set: [read: false, updated_at: NaiveDateTime.utc_now()]],
+ on_conflict: [set: [read: read, updated_at: NaiveDateTime.utc_now()]],
returning: true,
conflict_target: [:user_id, :conversation_id]
)
diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex
index df0f72f96..d0e254362 100644
--- a/lib/pleroma/emails/admin_email.ex
+++ b/lib/pleroma/emails/admin_email.ex
@@ -29,7 +29,7 @@ defmodule Pleroma.Emails.AdminEmail do
end
statuses_html =
- if length(statuses) > 0 do
+ if is_list(statuses) && length(statuses) > 0 do
statuses_list_html =
statuses
|> Enum.map(fn
diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex
index 6390cce4c..7d12eff7f 100644
--- a/lib/pleroma/emoji.ex
+++ b/lib/pleroma/emoji.ex
@@ -22,7 +22,7 @@ defmodule Pleroma.Emoji do
@ets __MODULE__.Ets
@ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}]
- @groups Application.get_env(:pleroma, :emoji)[:groups]
+ @groups Pleroma.Config.get([:emoji, :groups])
@doc false
def start_link do
@@ -112,7 +112,7 @@ defmodule Pleroma.Emoji do
# Compat thing for old custom emoji handling & default emoji,
# it should run even if there are no emoji packs
- shortcode_globs = Application.get_env(:pleroma, :emoji)[:shortcode_globs] || []
+ shortcode_globs = Pleroma.Config.get([:emoji, :shortcode_globs], [])
emojis =
(load_from_file("config/emoji.txt") ++
diff --git a/lib/pleroma/filter.ex b/lib/pleroma/filter.ex
index 79efc29f0..90457dadf 100644
--- a/lib/pleroma/filter.ex
+++ b/lib/pleroma/filter.ex
@@ -38,7 +38,8 @@ defmodule Pleroma.Filter do
query =
from(
f in Pleroma.Filter,
- where: f.user_id == ^user_id
+ where: f.user_id == ^user_id,
+ order_by: [desc: :id]
)
Repo.all(query)
diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex
index 3d7c36d21..607843a5b 100644
--- a/lib/pleroma/formatter.ex
+++ b/lib/pleroma/formatter.ex
@@ -8,7 +8,7 @@ defmodule Pleroma.Formatter do
alias Pleroma.User
alias Pleroma.Web.MediaProxy
- @safe_mention_regex ~r/^(\s*(?<mentions>@.+?\s+)+)(?<rest>.*)/
+ @safe_mention_regex ~r/^(\s*(?<mentions>(@.+?\s+){1,})+)(?<rest>.*)/s
@link_regex ~r"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+"ui
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex
index d1da746de..e5e78ee4f 100644
--- a/lib/pleroma/html.ex
+++ b/lib/pleroma/html.ex
@@ -104,7 +104,6 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
paragraphs, breaks and links are allowed through the filter.
"""
- @markup Application.get_env(:pleroma, :markup)
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
require HtmlSanitizeEx.Scrubber.Meta
@@ -142,9 +141,7 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
Meta.allow_tag_with_these_attributes("span", [])
# allow inline images for custom emoji
- @allow_inline_images Keyword.get(@markup, :allow_inline_images)
-
- if @allow_inline_images do
+ if Pleroma.Config.get([:markup, :allow_inline_images]) do
# restrict img tags to http/https only, because of MediaProxy.
Meta.allow_tag_with_uri_attributes("img", ["src"], ["http", "https"])
@@ -168,7 +165,6 @@ defmodule Pleroma.HTML.Scrubber.Default do
# credo:disable-for-previous-line
# No idea how to fix this one…
- @markup Application.get_env(:pleroma, :markup)
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
Meta.remove_cdata_sections_before_scrub()
@@ -213,7 +209,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_this_attribute_values("span", "class", ["h-card"])
Meta.allow_tag_with_these_attributes("span", [])
- @allow_inline_images Keyword.get(@markup, :allow_inline_images)
+ @allow_inline_images Pleroma.Config.get([:markup, :allow_inline_images])
if @allow_inline_images do
# restrict img tags to http/https only, because of MediaProxy.
@@ -228,9 +224,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
])
end
- @allow_tables Keyword.get(@markup, :allow_tables)
-
- if @allow_tables do
+ if Pleroma.Config.get([:markup, :allow_tables]) do
Meta.allow_tag_with_these_attributes("table", [])
Meta.allow_tag_with_these_attributes("tbody", [])
Meta.allow_tag_with_these_attributes("td", [])
@@ -239,9 +233,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_these_attributes("tr", [])
end
- @allow_headings Keyword.get(@markup, :allow_headings)
-
- if @allow_headings do
+ if Pleroma.Config.get([:markup, :allow_headings]) do
Meta.allow_tag_with_these_attributes("h1", [])
Meta.allow_tag_with_these_attributes("h2", [])
Meta.allow_tag_with_these_attributes("h3", [])
@@ -249,9 +241,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_these_attributes("h5", [])
end
- @allow_fonts Keyword.get(@markup, :allow_fonts)
-
- if @allow_fonts do
+ if Pleroma.Config.get([:markup, :allow_fonts]) do
Meta.allow_tag_with_these_attributes("font", ["face"])
end
diff --git a/lib/pleroma/http/connection.ex b/lib/pleroma/http/connection.ex
index c0173465a..c216cdcb1 100644
--- a/lib/pleroma/http/connection.ex
+++ b/lib/pleroma/http/connection.ex
@@ -8,7 +8,7 @@ defmodule Pleroma.HTTP.Connection do
"""
@hackney_options [
- connect_timeout: 2_000,
+ connect_timeout: 10_000,
recv_timeout: 20_000,
follow_redirect: true,
pool: :federation
@@ -32,9 +32,11 @@ defmodule Pleroma.HTTP.Connection do
defp hackney_options(opts) do
options = Keyword.get(opts, :adapter, [])
adapter_options = Pleroma.Config.get([:http, :adapter], [])
+ proxy_url = Pleroma.Config.get([:http, :proxy_url], nil)
@hackney_options
|> Keyword.merge(adapter_options)
|> Keyword.merge(options)
+ |> Keyword.merge(proxy: proxy_url)
end
end
diff --git a/lib/pleroma/http/http.ex b/lib/pleroma/http/http.ex
index c5f720bc9..c96ee7353 100644
--- a/lib/pleroma/http/http.ex
+++ b/lib/pleroma/http/http.ex
@@ -65,12 +65,9 @@ defmodule Pleroma.HTTP do
end
def process_request_options(options) do
- config = Application.get_env(:pleroma, :http, [])
- proxy = Keyword.get(config, :proxy_url, nil)
-
- case proxy do
+ case Pleroma.Config.get([:http, :proxy_url]) do
nil -> options
- _ -> options ++ [proxy: proxy]
+ proxy -> options ++ [proxy: proxy]
end
end
diff --git a/lib/pleroma/http/request_builder.ex b/lib/pleroma/http/request_builder.ex
index 5f2cff2c0..e23457999 100644
--- a/lib/pleroma/http/request_builder.ex
+++ b/lib/pleroma/http/request_builder.ex
@@ -45,8 +45,15 @@ defmodule Pleroma.HTTP.RequestBuilder do
Add headers to the request
"""
@spec headers(map(), list(tuple)) :: map()
- def headers(request, h) do
- Map.put_new(request, :headers, h)
+ def headers(request, header_list) do
+ header_list =
+ if Pleroma.Config.get([:http, :send_user_agent]) do
+ header_list ++ [{"User-Agent", Pleroma.Application.user_agent()}]
+ else
+ header_list
+ end
+
+ Map.put_new(request, :headers, header_list)
end
@doc """
diff --git a/lib/pleroma/keys.ex b/lib/pleroma/keys.ex
new file mode 100644
index 000000000..b7bc7a4da
--- /dev/null
+++ b/lib/pleroma/keys.ex
@@ -0,0 +1,44 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Keys do
+ # Native generation of RSA keys is only available since OTP 20+ and in default build conditions
+ # We try at compile time to generate natively an RSA key otherwise we fallback on the old way.
+ try do
+ _ = :public_key.generate_key({:rsa, 2048, 65_537})
+
+ def generate_rsa_pem do
+ key = :public_key.generate_key({:rsa, 2048, 65_537})
+ entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
+ pem = :public_key.pem_encode([entry]) |> String.trim_trailing()
+ {:ok, pem}
+ end
+ rescue
+ _ ->
+ def generate_rsa_pem do
+ port = Port.open({:spawn, "openssl genrsa"}, [:binary])
+
+ {:ok, pem} =
+ receive do
+ {^port, {:data, pem}} -> {:ok, pem}
+ end
+
+ Port.close(port)
+
+ if Regex.match?(~r/RSA PRIVATE KEY/, pem) do
+ {:ok, pem}
+ else
+ :error
+ end
+ end
+ end
+
+ def keys_from_pem(pem) do
+ [private_key_code] = :public_key.pem_decode(pem)
+ private_key = :public_key.pem_entry_decode(private_key_code)
+ {:RSAPrivateKey, _, modulus, exponent, _, _, _, _, _, _, _} = private_key
+ public_key = {:RSAPublicKey, modulus, exponent}
+ {:ok, private_key, public_key}
+ end
+end
diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex
index 740d687a3..cc6fc9c5d 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -130,6 +130,13 @@ defmodule Pleroma.Object do
end
end
+ def prune(%Object{data: %{"id" => id}} = object) do
+ with {:ok, object} <- Repo.delete(object),
+ {:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do
+ {:ok, object}
+ end
+ end
+
def set_cache(%Object{data: %{"id" => ap_id}} = object) do
Cachex.put(:object_cache, "object:#{ap_id}", object)
{:ok, object}
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
index 8d4bcc95e..ca980c629 100644
--- a/lib/pleroma/object/fetcher.ex
+++ b/lib/pleroma/object/fetcher.ex
@@ -1,4 +1,5 @@
defmodule Pleroma.Object.Fetcher do
+ alias Pleroma.HTTP
alias Pleroma.Object
alias Pleroma.Object.Containment
alias Pleroma.Web.ActivityPub.Transmogrifier
@@ -6,7 +7,18 @@ defmodule Pleroma.Object.Fetcher do
require Logger
- @httpoison Application.get_env(:pleroma, :httpoison)
+ defp reinject_object(data) do
+ Logger.debug("Reinjecting object #{data["id"]}")
+
+ with data <- Transmogrifier.fix_object(data),
+ {:ok, object} <- Object.create(data) do
+ {:ok, object}
+ else
+ e ->
+ Logger.error("Error while processing object: #{inspect(e)}")
+ {:error, e}
+ end
+ end
# TODO:
# This will create a Create activity, which we need internally at the moment.
@@ -26,12 +38,17 @@ defmodule Pleroma.Object.Fetcher do
"object" => data
},
:ok <- Containment.contain_origin(id, params),
- {:ok, activity} <- Transmogrifier.handle_incoming(params) do
- {:ok, Object.normalize(activity, false)}
+ {:ok, activity} <- Transmogrifier.handle_incoming(params),
+ {:object, _data, %Object{} = object} <-
+ {:object, data, Object.normalize(activity, false)} do
+ {:ok, object}
else
{:error, {:reject, nil}} ->
{:reject, nil}
+ {:object, data, nil} ->
+ reinject_object(data)
+
object = %Object{} ->
{:ok, object}
@@ -60,7 +77,7 @@ defmodule Pleroma.Object.Fetcher do
with true <- String.starts_with?(id, "http"),
{:ok, %{body: body, status: code}} when code in 200..299 <-
- @httpoison.get(
+ HTTP.get(
id,
[{:Accept, "application/activity+json"}]
),
diff --git a/lib/pleroma/plugs/federating_plug.ex b/lib/pleroma/plugs/federating_plug.ex
index effc154bf..4dc4e9279 100644
--- a/lib/pleroma/plugs/federating_plug.ex
+++ b/lib/pleroma/plugs/federating_plug.ex
@@ -10,7 +10,7 @@ defmodule Pleroma.Web.FederatingPlug do
end
def call(conn, _opts) do
- if Keyword.get(Application.get_env(:pleroma, :instance), :federating) do
+ if Pleroma.Config.get([:instance, :federating]) do
conn
else
conn
diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex
index a476f1d49..485ddfbc7 100644
--- a/lib/pleroma/plugs/http_security_plug.ex
+++ b/lib/pleroma/plugs/http_security_plug.ex
@@ -20,8 +20,9 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
defp headers do
referrer_policy = Config.get([:http_security, :referrer_policy])
+ report_uri = Config.get([:http_security, :report_uri])
- [
+ headers = [
{"x-xss-protection", "1; mode=block"},
{"x-permitted-cross-domain-policies", "none"},
{"x-frame-options", "DENY"},
@@ -30,12 +31,27 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
{"x-download-options", "noopen"},
{"content-security-policy", csp_string() <> ";"}
]
+
+ if report_uri do
+ report_group = %{
+ "group" => "csp-endpoint",
+ "max-age" => 10_886_400,
+ "endpoints" => [
+ %{"url" => report_uri}
+ ]
+ }
+
+ headers ++ [{"reply-to", Jason.encode!(report_group)}]
+ else
+ headers
+ end
end
defp csp_string do
scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme]
static_url = Pleroma.Web.Endpoint.static_url()
websocket_url = Pleroma.Web.Endpoint.websocket_url()
+ report_uri = Config.get([:http_security, :report_uri])
connect_src = "connect-src 'self' #{static_url} #{websocket_url}"
@@ -53,7 +69,7 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
"script-src 'self'"
end
- [
+ main_part = [
"default-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
@@ -63,11 +79,14 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
"font-src 'self'",
"manifest-src 'self'",
connect_src,
- script_src,
- if scheme == "https" do
- "upgrade-insecure-requests"
- end
+ script_src
]
+
+ report = if report_uri, do: ["report-uri #{report_uri}; report-to csp-endpoint"], else: []
+
+ insecure = if scheme == "https", do: ["upgrade-insecure-requests"], else: []
+
+ (main_part ++ report ++ insecure)
|> Enum.join("; ")
end
diff --git a/lib/pleroma/reverse_proxy.ex b/lib/pleroma/reverse_proxy.ex
index a3f177fec..983e156f5 100644
--- a/lib/pleroma/reverse_proxy.ex
+++ b/lib/pleroma/reverse_proxy.ex
@@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReverseProxy do
+ alias Pleroma.HTTP
+
@keep_req_headers ~w(accept user-agent accept-encoding cache-control if-modified-since) ++
~w(if-unmodified-since if-none-match if-range range)
@resp_cache_headers ~w(etag date last-modified cache-control)
@@ -59,8 +61,7 @@ defmodule Pleroma.ReverseProxy do
* `http`: options for [hackney](https://github.com/benoitc/hackney).
"""
- @hackney Application.get_env(:pleroma, :hackney, :hackney)
- @httpoison Application.get_env(:pleroma, :httpoison, HTTPoison)
+ @hackney Pleroma.Config.get(:hackney, :hackney)
@default_hackney_options []
@@ -97,7 +98,7 @@ defmodule Pleroma.ReverseProxy do
hackney_opts =
@default_hackney_options
|> Keyword.merge(Keyword.get(opts, :http, []))
- |> @httpoison.process_request_options()
+ |> HTTP.process_request_options()
req_headers = build_req_headers(conn.req_headers, opts)
diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex
index b7ecf00a0..1a4d54c62 100644
--- a/lib/pleroma/signature.ex
+++ b/lib/pleroma/signature.ex
@@ -5,11 +5,10 @@
defmodule Pleroma.Signature do
@behaviour HTTPSignatures.Adapter
+ alias Pleroma.Keys
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
- alias Pleroma.Web.Salmon
- alias Pleroma.Web.WebFinger
def fetch_public_key(conn) do
with actor_id <- Utils.get_ap_id(conn.params["actor"]),
@@ -33,8 +32,8 @@ defmodule Pleroma.Signature do
end
def sign(%User{} = user, headers) do
- with {:ok, %{info: %{keys: keys}}} <- WebFinger.ensure_keys_present(user),
- {:ok, private_key, _} <- Salmon.keys_from_pem(keys) do
+ with {:ok, %{info: %{keys: keys}}} <- User.ensure_keys_present(user),
+ {:ok, private_key, _} <- Keys.keys_from_pem(keys) do
HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)
end
end
diff --git a/lib/pleroma/uploaders/mdii.ex b/lib/pleroma/uploaders/mdii.ex
index 190ed9f3a..237544337 100644
--- a/lib/pleroma/uploaders/mdii.ex
+++ b/lib/pleroma/uploaders/mdii.ex
@@ -4,11 +4,10 @@
defmodule Pleroma.Uploaders.MDII do
alias Pleroma.Config
+ alias Pleroma.HTTP
@behaviour Pleroma.Uploaders.Uploader
- @httpoison Application.get_env(:pleroma, :httpoison)
-
# MDII-hosted images are never passed through the MediaPlug; only local media.
# Delegate to Pleroma.Uploaders.Local
def get_file(file) do
@@ -25,7 +24,7 @@ defmodule Pleroma.Uploaders.MDII do
query = "#{cgi}?#{extension}"
with {:ok, %{status: 200, body: body}} <-
- @httpoison.post(query, file_data, [], adapter: [pool: :default]) do
+ HTTP.post(query, file_data, [], adapter: [pool: :default]) do
remote_file_name = String.split(body) |> List.first()
public_url = "#{files}/#{remote_file_name}.#{extension}"
{:ok, {:url, public_url}}
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 722e8ff6b..6abcb7288 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -10,6 +10,7 @@ defmodule Pleroma.User do
alias Comeonin.Pbkdf2
alias Pleroma.Activity
+ alias Pleroma.Keys
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Registration
@@ -55,7 +56,7 @@ defmodule Pleroma.User do
field(:last_refreshed_at, :naive_datetime_usec)
has_many(:notifications, Notification)
has_many(:registrations, Registration)
- embeds_one(:info, Pleroma.User.Info)
+ embeds_one(:info, User.Info)
timestamps()
end
@@ -166,7 +167,7 @@ defmodule Pleroma.User do
def update_changeset(struct, params \\ %{}) do
struct
- |> cast(params, [:bio, :name, :avatar])
+ |> cast(params, [:bio, :name, :avatar, :following])
|> unique_constraint(:nickname)
|> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: 5000)
@@ -233,7 +234,7 @@ defmodule Pleroma.User do
|> validate_confirmation(:password)
|> unique_constraint(:email)
|> unique_constraint(:nickname)
- |> validate_exclusion(:nickname, Pleroma.Config.get([Pleroma.User, :restricted_nicknames]))
+ |> validate_exclusion(:nickname, Pleroma.Config.get([User, :restricted_nicknames]))
|> validate_format(:nickname, local_nickname_regex())
|> validate_format(:email, @email_regex)
|> validate_length(:bio, max: 1000)
@@ -284,7 +285,7 @@ defmodule Pleroma.User do
def post_register_action(%User{} = user) do
with {:ok, user} <- autofollow_users(user),
{:ok, user} <- set_cache(user),
- {:ok, _} <- Pleroma.User.WelcomeMessage.post_welcome_message_to_user(user),
+ {:ok, _} <- User.WelcomeMessage.post_welcome_message_to_user(user),
{:ok, _} <- try_send_confirmation_email(user) do
{:ok, user}
end
@@ -371,9 +372,7 @@ defmodule Pleroma.User do
end
def follow(%User{} = follower, %User{info: info} = followed) do
- user_config = Application.get_env(:pleroma, :user)
- deny_follow_blocked = Keyword.get(user_config, :deny_follow_blocked)
-
+ deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
ap_followers = followed.follower_address
cond do
@@ -715,6 +714,18 @@ defmodule Pleroma.User do
end
end
+ def remove_duplicated_following(%User{following: following} = user) do
+ uniq_following = Enum.uniq(following)
+
+ if length(following) == length(uniq_following) do
+ {:ok, user}
+ else
+ user
+ |> update_changeset(%{following: uniq_following})
+ |> update_and_set_cache()
+ end
+ end
+
@spec get_users_from_set([String.t()], boolean()) :: [User.t()]
def get_users_from_set(ap_ids, local_only \\ true) do
criteria = %{ap_id: ap_ids, deactivated: false}
@@ -753,7 +764,7 @@ defmodule Pleroma.User do
from(s in subquery(boost_search_rank_query(distinct_query, for_user)),
order_by: [desc: s.search_rank],
- limit: 20
+ limit: 40
)
end
@@ -1138,7 +1149,6 @@ defmodule Pleroma.User do
stream =
ap_id
|> Activity.query_by_actor()
- |> Activity.with_preloaded_object()
|> Repo.stream()
Repo.transaction(fn -> Enum.each(stream, &delete_activity(&1)) end, timeout: :infinity)
@@ -1384,4 +1394,57 @@ defmodule Pleroma.User do
def showing_reblogs?(%User{} = user, %User{} = target) do
target.ap_id not in user.info.muted_reblogs
end
+
+ @spec toggle_confirmation(User.t()) :: {:ok, User.t()} | {:error, Changeset.t()}
+ def toggle_confirmation(%User{} = user) do
+ need_confirmation? = !user.info.confirmation_pending
+
+ info_changeset =
+ User.Info.confirmation_changeset(user.info, need_confirmation: need_confirmation?)
+
+ user
+ |> change()
+ |> put_embed(:info, info_changeset)
+ |> update_and_set_cache()
+ end
+
+ def get_mascot(%{info: %{mascot: %{} = mascot}}) when not is_nil(mascot) do
+ mascot
+ end
+
+ def get_mascot(%{info: %{mascot: mascot}}) when is_nil(mascot) do
+ # use instance-default
+ config = Pleroma.Config.get([:assets, :mascots])
+ default_mascot = Pleroma.Config.get([:assets, :default_mascot])
+ mascot = Keyword.get(config, default_mascot)
+
+ %{
+ "id" => "default-mascot",
+ "url" => mascot[:url],
+ "preview_url" => mascot[:url],
+ "pleroma" => %{
+ "mime_type" => mascot[:mime_type]
+ }
+ }
+ end
+
+ def ensure_keys_present(user) do
+ info = user.info
+
+ if info.keys do
+ {:ok, user}
+ else
+ {:ok, pem} = Keys.generate_rsa_pem()
+
+ info_cng =
+ info
+ |> User.Info.set_keys(pem)
+
+ cng =
+ Ecto.Changeset.change(user)
+ |> Ecto.Changeset.put_embed(:info, info_cng)
+
+ update_and_set_cache(cng)
+ end
+ end
end
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 5a50ee639..6397e2737 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -43,6 +43,7 @@ defmodule Pleroma.User.Info do
field(:hide_favorites, :boolean, default: true)
field(:pinned_activities, {:array, :string}, default: [])
field(:flavour, :string, default: nil)
+ field(:mascot, :map, default: nil)
field(:emoji, {:array, :map}, default: [])
field(:notification_settings, :map,
@@ -212,7 +213,7 @@ defmodule Pleroma.User.Info do
])
end
- @spec confirmation_changeset(Info.t(), keyword()) :: Ecto.Changerset.t()
+ @spec confirmation_changeset(Info.t(), keyword()) :: Changeset.t()
def confirmation_changeset(info, opts) do
need_confirmation? = Keyword.get(opts, :need_confirmation)
@@ -248,6 +249,14 @@ defmodule Pleroma.User.Info do
|> validate_required([:flavour])
end
+ def mascot_update(info, url) do
+ params = %{mascot: url}
+
+ info
+ |> cast(params, [:mascot])
+ |> validate_required([:mascot])
+ end
+
def set_source_data(info, source_data) do
params = %{source_data: source_data}
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 233fee4fa..8add62406 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -399,16 +399,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
def block(blocker, blocked, activity_id \\ nil, local \\ true) do
- ap_config = Application.get_env(:pleroma, :activitypub)
- unfollow_blocked = Keyword.get(ap_config, :unfollow_blocked)
- outgoing_blocks = Keyword.get(ap_config, :outgoing_blocks)
+ outgoing_blocks = Pleroma.Config.get([:activitypub, :outgoing_blocks])
+ unfollow_blocked = Pleroma.Config.get([:activitypub, :unfollow_blocked])
- with true <- unfollow_blocked do
+ if unfollow_blocked do
follow_activity = fetch_latest_follow(blocker, blocked)
-
- if follow_activity do
- unfollow(blocker, blocked, nil, local)
- end
+ if follow_activity, do: unfollow(blocker, blocked, nil, local)
end
with true <- outgoing_blocks,
@@ -540,8 +536,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
)
)
- Ecto.Adapters.SQL.to_sql(:all, Repo, query)
-
query
else
Logger.error("Could not restrict visibility to #{visibility}")
@@ -557,8 +551,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
)
- Ecto.Adapters.SQL.to_sql(:all, Repo, query)
-
query
end
@@ -569,6 +561,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_visibility(query, _visibility), do: query
+ defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}) do
+ query =
+ from(
+ a in query,
+ where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
+ )
+
+ query
+ end
+
+ defp restrict_thread_visibility(query, _), do: query
+
def fetch_user_activities(user, reading_user, params \\ %{}) do
params =
params
@@ -645,20 +649,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_tag(query, _), do: query
- defp restrict_to_cc(query, recipients_to, recipients_cc) do
- from(
- activity in query,
- where:
- fragment(
- "(?->'to' \\?| ?) or (?->'cc' \\?| ?)",
- activity.data,
- ^recipients_to,
- activity.data,
- ^recipients_cc
- )
- )
- end
-
defp restrict_recipients(query, [], _user), do: query
defp restrict_recipients(query, recipients, nil) do
@@ -695,6 +685,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_type(query, _), do: query
+ defp restrict_state(query, %{"state" => state}) do
+ from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
+ end
+
+ defp restrict_state(query, _), do: query
+
defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
from(
activity in query,
@@ -750,8 +746,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
blocks = info.blocks || []
domain_blocks = info.domain_blocks || []
+ query =
+ if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
+
from(
- activity in query,
+ [activity, object: o] in query,
where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
where: fragment("not (? && ?)", activity.recipients, ^blocks),
where:
@@ -761,7 +760,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
activity.data,
^blocks
),
- where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks)
+ where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
+ where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
)
end
@@ -816,6 +816,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Activity.with_preloaded_bookmark(opts["user"])
end
+ defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
+
+ defp maybe_set_thread_muted_field(query, opts) do
+ query
+ |> Activity.with_set_thread_muted_field(opts["user"])
+ end
+
defp maybe_order(query, %{order: :desc}) do
query
|> order_by(desc: :id)
@@ -834,6 +841,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
base_query
|> maybe_preload_objects(opts)
|> maybe_preload_bookmarks(opts)
+ |> maybe_set_thread_muted_field(opts)
|> maybe_order(opts)
|> restrict_recipients(recipients, opts["user"])
|> restrict_tag(opts)
@@ -843,11 +851,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_local(opts)
|> restrict_actor(opts)
|> restrict_type(opts)
+ |> restrict_state(opts)
|> restrict_favorited_by(opts)
|> restrict_blocked(opts)
|> restrict_muted(opts)
|> restrict_media(opts)
|> restrict_visibility(opts)
+ |> restrict_thread_visibility(opts)
|> restrict_replies(opts)
|> restrict_reblogs(opts)
|> restrict_pinned(opts)
@@ -861,9 +871,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Enum.reverse()
end
- def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do
+ def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
+ from(activity in query,
+ where:
+ fragment("? && ?", activity.recipients, ^recipients) or
+ (fragment("? && ?", activity.recipients, ^recipients_with_public) and
+ "https://www.w3.org/ns/activitystreams#Public" in activity.recipients)
+ )
+ end
+
+ def fetch_activities_bounded(recipients, recipients_with_public, opts \\ %{}) do
fetch_activities_query([], opts)
- |> restrict_to_cc(recipients_to, recipients_cc)
+ |> fetch_activities_bounded_query(recipients, recipients_with_public)
|> Pagination.fetch_paginated(opts)
|> Enum.reverse()
end
@@ -881,7 +900,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- def user_data_from_user_object(data) do
+ defp object_to_user_data(data) do
avatar =
data["icon"]["url"] &&
%{
@@ -928,9 +947,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
{:ok, user_data}
end
+ def user_data_from_user_object(data) do
+ with {:ok, data} <- MRF.filter(data),
+ {:ok, data} <- object_to_user_data(data) do
+ {:ok, data}
+ else
+ e -> {:error, e}
+ end
+ end
+
def fetch_and_prepare_user_from_ap_id(ap_id) do
- with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do
- user_data_from_user_object(data)
+ with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
+ {:ok, data} <- user_data_from_user_object(data) do
+ {:ok, data}
else
e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
end
@@ -966,11 +995,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
contain_broken_threads(activity, user)
end
- # do post-processing on a timeline
- def contain_timeline(timeline, user) do
- timeline
- |> Enum.filter(fn activity ->
- contain_activity(activity, user)
- end)
+ def fetch_direct_messages_query do
+ Activity
+ |> restrict_type(%{"type" => "Create"})
+ |> restrict_visibility(%{visibility: "direct"})
+ |> order_by([activity], asc: activity.id)
end
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index c967ab7a9..0182bda46 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -27,7 +27,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
plug(:relay_active? when action in [:relay])
def relay_active?(conn, _) do
- if Keyword.get(Application.get_env(:pleroma, :instance), :allow_relay) do
+ if Pleroma.Config.get([:instance, :allow_relay]) do
conn
else
conn
@@ -39,7 +39,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def user(conn, %{"nickname" => nickname}) do
with %User{} = user <- User.get_cached_by_nickname(nickname),
- {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
+ {:ok, user} <- User.ensure_keys_present(user) do
conn
|> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("user.json", %{user: user}))
@@ -106,7 +106,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def following(conn, %{"nickname" => nickname, "page" => page}) do
with %User{} = user <- User.get_cached_by_nickname(nickname),
- {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
+ {:ok, user} <- User.ensure_keys_present(user) do
{page, _} = Integer.parse(page)
conn
@@ -117,7 +117,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def following(conn, %{"nickname" => nickname}) do
with %User{} = user <- User.get_cached_by_nickname(nickname),
- {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
+ {:ok, user} <- User.ensure_keys_present(user) do
conn
|> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("following.json", %{user: user}))
@@ -126,7 +126,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def followers(conn, %{"nickname" => nickname, "page" => page}) do
with %User{} = user <- User.get_cached_by_nickname(nickname),
- {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
+ {:ok, user} <- User.ensure_keys_present(user) do
{page, _} = Integer.parse(page)
conn
@@ -137,7 +137,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def followers(conn, %{"nickname" => nickname}) do
with %User{} = user <- User.get_cached_by_nickname(nickname),
- {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
+ {:ok, user} <- User.ensure_keys_present(user) do
conn
|> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("followers.json", %{user: user}))
@@ -146,7 +146,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def outbox(conn, %{"nickname" => nickname} = params) do
with %User{} = user <- User.get_cached_by_nickname(nickname),
- {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
+ {:ok, user} <- User.ensure_keys_present(user) do
conn
|> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("outbox.json", %{user: user, max_id: params["max_id"]}))
@@ -195,7 +195,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def relay(conn, _params) do
with %User{} = user <- Relay.get_actor(),
- {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
+ {:ok, user} <- User.ensure_keys_present(user) do
conn
|> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("user.json", %{user: user}))
diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex
index 1aaa20050..3bf7955f3 100644
--- a/lib/pleroma/web/activity_pub/mrf.ex
+++ b/lib/pleroma/web/activity_pub/mrf.ex
@@ -17,9 +17,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
end
def get_policies do
- Application.get_env(:pleroma, :instance, [])
- |> Keyword.get(:rewrite_policy, [])
- |> get_policies()
+ Pleroma.Config.get([:instance, :rewrite_policy], []) |> get_policies()
end
defp get_policies(policy) when is_atom(policy), do: [policy]
diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
index 2f105700b..433d23c5f 100644
--- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
@@ -48,14 +48,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
%{host: actor_host} = _actor_info,
%{
"type" => "Create",
- "object" => %{"attachment" => child_attachment} = child_object
+ "object" => child_object
} = object
- )
- when length(child_attachment) > 0 do
+ ) do
object =
if Enum.member?(Pleroma.Config.get([:mrf_simple, :media_nsfw]), actor_host) do
tags = (child_object["tag"] || []) ++ ["nsfw"]
- child_object = Map.put(child_object, "tags", tags)
+ child_object = Map.put(child_object, "tag", tags)
child_object = Map.put(child_object, "sensitive", true)
Map.put(object, "object", child_object)
else
@@ -75,8 +74,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
actor_host
),
user <- User.get_cached_by_ap_id(object["actor"]),
- true <- "https://www.w3.org/ns/activitystreams#Public" in object["to"],
- true <- user.follower_address in object["cc"] do
+ true <- "https://www.w3.org/ns/activitystreams#Public" in object["to"] do
to =
List.delete(object["to"], "https://www.w3.org/ns/activitystreams#Public") ++
[user.follower_address]
@@ -95,18 +93,63 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
{:ok, object}
end
+ defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do
+ if actor_host in Pleroma.Config.get([:mrf_simple, :report_removal]) do
+ {:reject, nil}
+ else
+ {:ok, object}
+ end
+ end
+
+ defp check_report_removal(_actor_info, object), do: {:ok, object}
+
+ defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do
+ if actor_host in Pleroma.Config.get([:mrf_simple, :avatar_removal]) do
+ {:ok, Map.delete(object, "icon")}
+ else
+ {:ok, object}
+ end
+ end
+
+ defp check_avatar_removal(_actor_info, object), do: {:ok, object}
+
+ defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do
+ if actor_host in Pleroma.Config.get([:mrf_simple, :banner_removal]) do
+ {:ok, Map.delete(object, "image")}
+ else
+ {:ok, object}
+ end
+ end
+
+ defp check_banner_removal(_actor_info, object), do: {:ok, object}
+
@impl true
- def filter(object) do
- actor_info = URI.parse(object["actor"])
+ def filter(%{"actor" => actor} = object) do
+ actor_info = URI.parse(actor)
with {:ok, object} <- check_accept(actor_info, object),
{:ok, object} <- check_reject(actor_info, object),
{:ok, object} <- check_media_removal(actor_info, object),
{:ok, object} <- check_media_nsfw(actor_info, object),
- {:ok, object} <- check_ftl_removal(actor_info, object) do
+ {:ok, object} <- check_ftl_removal(actor_info, object),
+ {:ok, object} <- check_report_removal(actor_info, object) do
+ {:ok, object}
+ else
+ _e -> {:reject, nil}
+ end
+ end
+
+ def filter(%{"id" => actor, "type" => obj_type} = object)
+ when obj_type in ["Application", "Group", "Organization", "Person", "Service"] do
+ actor_info = URI.parse(actor)
+
+ with {:ok, object} <- check_avatar_removal(actor_info, object),
+ {:ok, object} <- check_banner_removal(actor_info, object) do
{:ok, object}
else
_e -> {:reject, nil}
end
end
+
+ def filter(object), do: {:ok, object}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
index b52be30e7..6683b8d8e 100644
--- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
@@ -31,7 +31,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
object =
object
- |> Map.put("tags", tags)
+ |> Map.put("tag", tags)
|> Map.put("sensitive", true)
message = Map.put(message, "object", object)
diff --git a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex b/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex
index f5078d818..47663414a 100644
--- a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex
+++ b/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex
@@ -19,10 +19,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
end
@impl true
- def filter(object) do
- actor_info = URI.parse(object["actor"])
+ def filter(%{"actor" => actor} = object) do
+ actor_info = URI.parse(actor)
allow_list = Config.get([:mrf_user_allowlist, String.to_atom(actor_info.host)], [])
filter_by_list(object, allow_list)
end
+
+ def filter(object), do: {:ok, object}
end
diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex
index 11dba87de..8f1399ce6 100644
--- a/lib/pleroma/web/activity_pub/publisher.ex
+++ b/lib/pleroma/web/activity_pub/publisher.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.ActivityPub.Publisher do
alias Pleroma.Activity
alias Pleroma.Config
+ alias Pleroma.HTTP
alias Pleroma.Instances
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Relay
@@ -16,8 +17,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
require Logger
- @httpoison Application.get_env(:pleroma, :httpoison)
-
@moduledoc """
ActivityPub outgoing federation module.
"""
@@ -63,7 +62,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
with {:ok, %{status: code}} when code in 200..299 <-
result =
- @httpoison.post(
+ HTTP.post(
inbox,
json,
[
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 508f3532f..d8fa2728d 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -11,7 +11,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
alias Pleroma.Object.Containment
alias Pleroma.Repo
alias Pleroma.User
- alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
@@ -94,7 +93,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
object
|> Utils.determine_explicit_mentions()
- explicit_mentions = explicit_mentions ++ ["https://www.w3.org/ns/activitystreams#Public"]
+ follower_collection = User.get_cached_by_ap_id(Containment.get_actor(object)).follower_address
+
+ explicit_mentions =
+ explicit_mentions ++ ["https://www.w3.org/ns/activitystreams#Public", follower_collection]
object
|> fix_explicit_addressing(explicit_mentions)
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 236d1b4ac..ca8a0844b 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -20,6 +20,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
require Logger
@supported_object_types ["Article", "Note", "Video", "Page"]
+ @supported_report_states ~w(open closed resolved)
+ @valid_visibilities ~w(public unlisted private direct)
# 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.
@@ -670,7 +672,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"actor" => params.actor.ap_id,
"content" => params.content,
"object" => object,
- "context" => params.context
+ "context" => params.context,
+ "state" => "open"
}
|> Map.merge(additional)
end
@@ -713,4 +716,77 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end
end
end
+
+ #### Report-related helpers
+
+ def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do
+ with new_data <- Map.put(activity.data, "state", state),
+ changeset <- Changeset.change(activity, data: new_data),
+ {:ok, activity} <- Repo.update(changeset) do
+ {:ok, activity}
+ end
+ end
+
+ def update_report_state(_, _), do: {:error, "Unsupported state"}
+
+ def update_activity_visibility(activity, visibility) when visibility in @valid_visibilities do
+ [to, cc, recipients] =
+ activity
+ |> get_updated_targets(visibility)
+ |> Enum.map(&Enum.uniq/1)
+
+ object_data =
+ activity.object.data
+ |> Map.put("to", to)
+ |> Map.put("cc", cc)
+
+ {:ok, object} =
+ activity.object
+ |> Object.change(%{data: object_data})
+ |> Object.update_and_set_cache()
+
+ activity_data =
+ activity.data
+ |> Map.put("to", to)
+ |> Map.put("cc", cc)
+
+ activity
+ |> Map.put(:object, object)
+ |> Activity.change(%{data: activity_data, recipients: recipients})
+ |> Repo.update()
+ end
+
+ def update_activity_visibility(_, _), do: {:error, "Unsupported visibility"}
+
+ defp get_updated_targets(
+ %Activity{data: %{"to" => to} = data, recipients: recipients},
+ visibility
+ ) do
+ cc = Map.get(data, "cc", [])
+ follower_address = User.get_cached_by_ap_id(data["actor"]).follower_address
+ public = "https://www.w3.org/ns/activitystreams#Public"
+
+ case visibility do
+ "public" ->
+ to = [public | List.delete(to, follower_address)]
+ cc = [follower_address | List.delete(cc, public)]
+ recipients = [public | recipients]
+ [to, cc, recipients]
+
+ "private" ->
+ to = [follower_address | List.delete(to, public)]
+ cc = List.delete(cc, public)
+ recipients = List.delete(recipients, public)
+ [to, cc, recipients]
+
+ "unlisted" ->
+ to = [follower_address | List.delete(to, public)]
+ cc = [public | List.delete(cc, follower_address)]
+ recipients = recipients ++ [follower_address, public]
+ [to, cc, recipients]
+
+ _ ->
+ [to, cc, recipients]
+ end
+ end
end
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index 1254fdf6c..327e0e05b 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.ActivityPub.UserView do
use Pleroma.Web, :view
+ alias Pleroma.Keys
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
@@ -12,8 +13,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.Endpoint
alias Pleroma.Web.Router.Helpers
- alias Pleroma.Web.Salmon
- alias Pleroma.Web.WebFinger
import Ecto.Query
@@ -34,8 +33,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
# the instance itself is not a Person, but instead an Application
def render("user.json", %{user: %{nickname: nil} = user}) do
- {:ok, user} = WebFinger.ensure_keys_present(user)
- {:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
+ {:ok, user} = User.ensure_keys_present(user)
+ {:ok, _, public_key} = Keys.keys_from_pem(user.info.keys)
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key])
@@ -62,8 +61,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
end
def render("user.json", %{user: user}) do
- {:ok, user} = WebFinger.ensure_keys_present(user)
- {:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
+ {:ok, user} = User.ensure_keys_present(user)
+ {:ok, _, public_key} = Keys.keys_from_pem(user.info.keys)
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key])
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
index b38ee0442..93b50ee47 100644
--- a/lib/pleroma/web/activity_pub/visibility.ex
+++ b/lib/pleroma/web/activity_pub/visibility.ex
@@ -1,6 +1,7 @@
defmodule Pleroma.Web.ActivityPub.Visibility do
alias Pleroma.Activity
alias Pleroma.Object
+ alias Pleroma.Repo
alias Pleroma.User
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
@@ -13,11 +14,12 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
end
def is_private?(activity) do
- unless is_public?(activity) do
- follower_address = User.get_cached_by_ap_id(activity.data["actor"]).follower_address
- Enum.any?(activity.data["to"], &(&1 == follower_address))
+ with false <- is_public?(activity),
+ %User{follower_address: follower_address} <-
+ User.get_cached_by_ap_id(activity.data["actor"]) do
+ follower_address in activity.data["to"]
else
- false
+ _ -> false
end
end
@@ -38,25 +40,14 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y))
end
- # guard
- def entire_thread_visible_for_user?(nil, _user), do: false
+ def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do
+ {:ok, %{rows: [[result]]}} =
+ Ecto.Adapters.SQL.query(Repo, "SELECT thread_visibility($1, $2)", [
+ user.ap_id,
+ activity.data["id"]
+ ])
- # XXX: Probably even more inefficient than the previous implementation intended to be a placeholder untill https://git.pleroma.social/pleroma/pleroma/merge_requests/971 is in develop
- # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
-
- def entire_thread_visible_for_user?(
- %Activity{} = tail,
- # %Activity{data: %{"object" => %{"inReplyTo" => parent_id}}} = tail,
- user
- ) do
- case Object.normalize(tail) do
- %{data: %{"inReplyTo" => parent_id}} when is_binary(parent_id) ->
- parent = Activity.get_in_reply_to_activity(tail)
- visible_for_user?(tail, user) && entire_thread_visible_for_user?(parent, user)
-
- _ ->
- visible_for_user?(tail, user)
- end
+ result
end
def get_visibility(object) do
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index 60fd4e571..479fd5829 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -4,11 +4,16 @@
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
use Pleroma.Web, :controller
+ alias Pleroma.Activity
alias Pleroma.User
alias Pleroma.UserInviteToken
+ alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.AdminAPI.AccountView
+ alias Pleroma.Web.AdminAPI.ReportView
alias Pleroma.Web.AdminAPI.Search
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.MastodonAPI.StatusView
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
@@ -315,12 +320,88 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|> json(token.token)
end
+ def list_reports(conn, params) do
+ params =
+ params
+ |> Map.put("type", "Flag")
+ |> Map.put("skip_preload", true)
+
+ reports =
+ []
+ |> ActivityPub.fetch_activities(params)
+ |> Enum.reverse()
+
+ conn
+ |> put_view(ReportView)
+ |> render("index.json", %{reports: reports})
+ end
+
+ def report_show(conn, %{"id" => id}) do
+ with %Activity{} = report <- Activity.get_by_id(id) do
+ conn
+ |> put_view(ReportView)
+ |> render("show.json", %{report: report})
+ else
+ _ -> {:error, :not_found}
+ end
+ end
+
+ def report_update_state(conn, %{"id" => id, "state" => state}) do
+ with {:ok, report} <- CommonAPI.update_report_state(id, state) do
+ conn
+ |> put_view(ReportView)
+ |> render("show.json", %{report: report})
+ end
+ end
+
+ def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
+ with false <- is_nil(params["status"]),
+ %Activity{} <- Activity.get_by_id(id) do
+ params =
+ params
+ |> Map.put("in_reply_to_status_id", id)
+ |> Map.put("visibility", "direct")
+
+ {:ok, activity} = CommonAPI.post(user, params)
+
+ conn
+ |> put_view(StatusView)
+ |> render("status.json", %{activity: activity})
+ else
+ true ->
+ {:param_cast, nil}
+
+ nil ->
+ {:error, :not_found}
+ end
+ end
+
+ def status_update(conn, %{"id" => id} = params) do
+ with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
+ conn
+ |> put_view(StatusView)
+ |> render("status.json", %{activity: activity})
+ end
+ end
+
+ def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
+ json(conn, %{})
+ end
+ end
+
def errors(conn, {:error, :not_found}) do
conn
|> put_status(404)
|> json("Not found")
end
+ def errors(conn, {:error, reason}) do
+ conn
+ |> put_status(400)
+ |> json(reason)
+ end
+
def errors(conn, {:param_cast, _}) do
conn
|> put_status(400)
diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex
new file mode 100644
index 000000000..47a73dc7e
--- /dev/null
+++ b/lib/pleroma/web/admin_api/views/report_view.ex
@@ -0,0 +1,41 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.AdminAPI.ReportView do
+ use Pleroma.Web, :view
+ alias Pleroma.Activity
+ alias Pleroma.User
+ alias Pleroma.Web.CommonAPI.Utils
+ alias Pleroma.Web.MastodonAPI.AccountView
+ alias Pleroma.Web.MastodonAPI.StatusView
+
+ def render("index.json", %{reports: reports}) do
+ %{
+ reports: render_many(reports, __MODULE__, "show.json", as: :report)
+ }
+ end
+
+ def render("show.json", %{report: report}) do
+ user = User.get_cached_by_ap_id(report.data["actor"])
+ created_at = Utils.to_masto_date(report.data["published"])
+
+ [account_ap_id | status_ap_ids] = report.data["object"]
+ account = User.get_cached_by_ap_id(account_ap_id)
+
+ statuses =
+ Enum.map(status_ap_ids, fn ap_id ->
+ Activity.get_by_ap_id_with_object(ap_id)
+ end)
+
+ %{
+ id: report.id,
+ account: AccountView.render("account.json", %{user: account}),
+ actor: AccountView.render("account.json", %{user: user}),
+ content: report.data["content"],
+ created_at: created_at,
+ statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}),
+ state: report.data["state"]
+ }
+ end
+end
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 29c4c1014..5a312d673 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -71,6 +71,9 @@ defmodule Pleroma.Web.CommonAPI do
{:ok, _} <- unpin(activity_id, user),
{:ok, delete} <- ActivityPub.delete(object) do
{:ok, delete}
+ else
+ _ ->
+ {:error, "Could not delete"}
end
end
@@ -154,6 +157,7 @@ defmodule Pleroma.Web.CommonAPI do
{to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility),
context <- make_context(in_reply_to),
cw <- data["spoiler_text"] || "",
+ sensitive <- data["sensitive"] || Enum.member?(tags, {"#nsfw", "nsfw"}),
full_payload <- String.trim(status <> cw),
length when length in 1..limit <- String.length(full_payload),
object <-
@@ -166,7 +170,8 @@ defmodule Pleroma.Web.CommonAPI do
in_reply_to,
tags,
cw,
- cc
+ cc,
+ sensitive
),
object <-
Map.put(
@@ -197,7 +202,7 @@ defmodule Pleroma.Web.CommonAPI do
user =
with emoji <- emoji_from_profile(user),
source_data <- (user.info.source_data || %{}) |> Map.put("tag", emoji),
- info_cng <- Pleroma.User.Info.set_source_data(user.info, source_data),
+ info_cng <- User.Info.set_source_data(user.info, source_data),
change <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
{:ok, user} <- User.update_and_set_cache(change) do
user
@@ -230,7 +235,7 @@ defmodule Pleroma.Web.CommonAPI do
} = activity <- get_by_id_or_ap_id(id_or_ap_id),
true <- Enum.member?(object_to, "https://www.w3.org/ns/activitystreams#Public"),
%{valid?: true} = info_changeset <-
- Pleroma.User.Info.add_pinnned_activity(user.info, activity),
+ User.Info.add_pinnned_activity(user.info, activity),
changeset <-
Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset),
{:ok, _user} <- User.update_and_set_cache(changeset) do
@@ -247,7 +252,7 @@ defmodule Pleroma.Web.CommonAPI do
def unpin(id_or_ap_id, user) do
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
%{valid?: true} = info_changeset <-
- Pleroma.User.Info.remove_pinnned_activity(user.info, activity),
+ User.Info.remove_pinnned_activity(user.info, activity),
changeset <-
Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset),
{:ok, _user} <- User.update_and_set_cache(changeset) do
@@ -315,6 +320,60 @@ defmodule Pleroma.Web.CommonAPI do
end
end
+ def update_report_state(activity_id, state) do
+ with %Activity{} = activity <- Activity.get_by_id(activity_id),
+ {:ok, activity} <- Utils.update_report_state(activity, state) do
+ {:ok, activity}
+ else
+ nil ->
+ {:error, :not_found}
+
+ {:error, reason} ->
+ {:error, reason}
+
+ _ ->
+ {:error, "Could not update state"}
+ end
+ end
+
+ def update_activity_scope(activity_id, opts \\ %{}) do
+ with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
+ {:ok, activity} <- toggle_sensitive(activity, opts),
+ {:ok, activity} <- set_visibility(activity, opts) do
+ {:ok, activity}
+ else
+ nil ->
+ {:error, :not_found}
+
+ {:error, reason} ->
+ {:error, reason}
+ end
+ end
+
+ defp toggle_sensitive(activity, %{"sensitive" => sensitive}) when sensitive in ~w(true false) do
+ toggle_sensitive(activity, %{"sensitive" => String.to_existing_atom(sensitive)})
+ end
+
+ defp toggle_sensitive(%Activity{object: object} = activity, %{"sensitive" => sensitive})
+ when is_boolean(sensitive) do
+ new_data = Map.put(object.data, "sensitive", sensitive)
+
+ {:ok, object} =
+ object
+ |> Object.change(%{data: new_data})
+ |> Object.update_and_set_cache()
+
+ {:ok, Map.put(activity, :object, object)}
+ end
+
+ defp toggle_sensitive(activity, _), do: {:ok, activity}
+
+ defp set_visibility(activity, %{"visibility" => visibility}) do
+ Utils.update_activity_visibility(activity, visibility)
+ end
+
+ defp set_visibility(activity, _), do: {:ok, activity}
+
def hide_reblogs(user, muted) do
ap_id = muted.ap_id
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 1dfe50b40..d93c0d46e 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -223,7 +223,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
in_reply_to,
tags,
cw \\ nil,
- cc \\ []
+ cc \\ [],
+ sensitive \\ false
) do
object = %{
"type" => "Note",
@@ -231,19 +232,18 @@ defmodule Pleroma.Web.CommonAPI.Utils do
"cc" => cc,
"content" => content_html,
"summary" => cw,
+ "sensitive" => !Enum.member?(["false", "False", "0", false], sensitive),
"context" => context,
"attachment" => attachments,
"actor" => actor,
"tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq()
}
- if in_reply_to do
- in_reply_to_object = Object.normalize(in_reply_to)
-
- object
- |> Map.put("inReplyTo", in_reply_to_object.data["id"])
+ with false <- is_nil(in_reply_to),
+ %Object{} = in_reply_to_object <- Object.normalize(in_reply_to) do
+ Map.put(object, "inReplyTo", in_reply_to_object.data["id"])
else
- object
+ _ -> object
end
end
diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex
index 9ef30e885..bd76e4295 100644
--- a/lib/pleroma/web/endpoint.ex
+++ b/lib/pleroma/web/endpoint.ex
@@ -16,17 +16,32 @@ defmodule Pleroma.Web.Endpoint do
plug(Pleroma.Plugs.UploadedMedia)
+ @static_cache_control "public, no-cache"
+
# InstanceStatic needs to be before Plug.Static to be able to override shipped-static files
# If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well
- plug(Pleroma.Plugs.InstanceStatic, at: "/")
+ # Cache-control headers are duplicated in case we turn off etags in the future
+ plug(Pleroma.Plugs.InstanceStatic,
+ at: "/",
+ gzip: true,
+ cache_control_for_etags: @static_cache_control,
+ headers: %{
+ "cache-control" => @static_cache_control
+ }
+ )
plug(
Plug.Static,
at: "/",
from: :pleroma,
only:
- ~w(index.html robots.txt static finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc)
+ ~w(index.html robots.txt static finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc),
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
+ gzip: true,
+ cache_control_for_etags: @static_cache_control,
+ headers: %{
+ "cache-control" => @static_cache_control
+ }
)
plug(Plug.Static.IndexHtml, at: "/pleroma/admin/")
@@ -51,7 +66,7 @@ defmodule Pleroma.Web.Endpoint do
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Jason,
- length: Application.get_env(:pleroma, :instance) |> Keyword.get(:upload_limit),
+ length: Pleroma.Config.get([:instance, :upload_limit]),
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
)
diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex
index 169fdf4dc..f4c9fe284 100644
--- a/lib/pleroma/web/federator/federator.ex
+++ b/lib/pleroma/web/federator/federator.ex
@@ -11,14 +11,11 @@ defmodule Pleroma.Web.Federator do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.Federator.Publisher
alias Pleroma.Web.Federator.RetryQueue
- alias Pleroma.Web.WebFinger
+ alias Pleroma.Web.OStatus
alias Pleroma.Web.Websub
require Logger
- @websub Application.get_env(:pleroma, :websub)
- @ostatus Application.get_env(:pleroma, :ostatus)
-
def init do
# 1 minute
Process.sleep(1000 * 60)
@@ -77,9 +74,8 @@ defmodule Pleroma.Web.Federator do
def perform(:publish, activity) do
Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end)
- with actor when not is_nil(actor) <- User.get_cached_by_ap_id(activity.data["actor"]) do
- {:ok, actor} = WebFinger.ensure_keys_present(actor)
-
+ with %User{} = actor <- User.get_cached_by_ap_id(activity.data["actor"]),
+ {:ok, actor} <- User.ensure_keys_present(actor) do
Publisher.publish(actor, activity)
end
end
@@ -89,12 +85,12 @@ defmodule Pleroma.Web.Federator do
"Running WebSub verification for #{websub.id} (#{websub.topic}, #{websub.callback})"
end)
- @websub.verify(websub)
+ Websub.verify(websub)
end
def perform(:incoming_doc, doc) do
Logger.info("Got document, trying to parse")
- @ostatus.handle_incoming(doc)
+ OStatus.handle_incoming(doc)
end
def perform(:incoming_ap_doc, params) do
diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex
index 916bcdcba..70f870244 100644
--- a/lib/pleroma/web/federator/publisher.ex
+++ b/lib/pleroma/web/federator/publisher.ex
@@ -31,7 +31,7 @@ defmodule Pleroma.Web.Federator.Publisher do
"""
@spec enqueue_one(module(), Map.t()) :: :ok
def enqueue_one(module, %{} = params),
- do: PleromaJobQueue.enqueue(:federation_outgoing, __MODULE__, [:publish_one, module, params])
+ do: PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_one, module, params])
@spec perform(atom(), module(), any()) :: {:ok, any()} | {:error, any()}
def perform(:publish_one, module, params) do
@@ -52,9 +52,9 @@ defmodule Pleroma.Web.Federator.Publisher do
@doc """
Relays an activity to all specified peers.
"""
- @callback publish(Pleroma.User.t(), Pleroma.Activity.t()) :: :ok | {:error, any()}
+ @callback publish(User.t(), Activity.t()) :: :ok | {:error, any()}
- @spec publish(Pleroma.User.t(), Pleroma.Activity.t()) :: :ok
+ @spec publish(User.t(), Activity.t()) :: :ok
def publish(%User{} = user, %Activity{} = activity) do
Config.get([:instance, :federation_publisher_modules])
|> Enum.each(fn module ->
@@ -70,9 +70,9 @@ defmodule Pleroma.Web.Federator.Publisher do
@doc """
Gathers links used by an outgoing federation module for WebFinger output.
"""
- @callback gather_webfinger_links(Pleroma.User.t()) :: list()
+ @callback gather_webfinger_links(User.t()) :: list()
- @spec gather_webfinger_links(Pleroma.User.t()) :: list()
+ @spec gather_webfinger_links(User.t()) :: list()
def gather_webfinger_links(%User{} = user) do
Config.get([:instance, :federation_publisher_modules])
|> Enum.reduce([], fn module, links ->
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 87e597074..2110027c3 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -11,6 +11,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
alias Pleroma.Conversation.Participation
alias Pleroma.Filter
alias Pleroma.Formatter
+ alias Pleroma.HTTP
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Object.Fetcher
@@ -55,7 +56,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
when action in [:account_register]
)
- @httpoison Application.get_env(:pleroma, :httpoison)
@local_mastodon_name "Mastodon-Local"
action_fallback(:errors)
@@ -303,7 +303,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
activities =
[user.ap_id | user.following]
|> ActivityPub.fetch_activities(params)
- |> ActivityPub.contain_timeline(user)
|> Enum.reverse()
conn
@@ -708,6 +707,41 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
+ def set_mascot(%{assigns: %{user: user}} = conn, %{"file" => file}) do
+ with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)),
+ %{} = attachment_data <- Map.put(object.data, "id", object.id),
+ %{type: type} = rendered <-
+ StatusView.render("attachment.json", %{attachment: attachment_data}) do
+ # Reject if not an image
+ if type == "image" do
+ # Sure!
+ # Save to the user's info
+ info_changeset = User.Info.mascot_update(user.info, rendered)
+
+ user_changeset =
+ user
+ |> Ecto.Changeset.change()
+ |> Ecto.Changeset.put_embed(:info, info_changeset)
+
+ {:ok, _user} = User.update_and_set_cache(user_changeset)
+
+ conn
+ |> json(rendered)
+ else
+ conn
+ |> put_resp_content_type("application/json")
+ |> send_resp(415, Jason.encode!(%{"error" => "mascots can only be images"}))
+ end
+ end
+ end
+
+ def get_mascot(%{assigns: %{user: user}} = conn, _params) do
+ mascot = User.get_mascot(user)
+
+ conn
+ |> json(mascot)
+ end
+
def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{data: %{"object" => object}} <- Repo.get(Activity, id),
%Object{data: %{"likes" => likes}} <- Object.normalize(object) do
@@ -1010,6 +1044,30 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
+ def status_search_query_with_gin(q, query) do
+ from([a, o] in q,
+ where:
+ fragment(
+ "to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)",
+ o.data,
+ ^query
+ ),
+ order_by: [desc: :id]
+ )
+ end
+
+ def status_search_query_with_rum(q, query) do
+ from([a, o] in q,
+ where:
+ fragment(
+ "? @@ plainto_tsquery('english', ?)",
+ o.fts_content,
+ ^query
+ ),
+ order_by: [fragment("? <=> now()::date", o.inserted_at)]
+ )
+ end
+
def status_search(user, query) do
fetched =
if Regex.match?(~r/https?:/, query) do
@@ -1023,20 +1081,19 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end || []
q =
- from(
- [a, o] in Activity.with_preloaded_object(Activity),
+ from([a, o] in Activity.with_preloaded_object(Activity),
where: fragment("?->>'type' = 'Create'", a.data),
where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
- where:
- fragment(
- "to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)",
- o.data,
- ^query
- ),
- limit: 20,
- order_by: [desc: :id]
+ limit: 40
)
+ q =
+ if Pleroma.Config.get([:database, :rum_enabled]) do
+ status_search_query_with_rum(q, query)
+ else
+ status_search_query_with_gin(q, query)
+ end
+
Repo.all(q) ++ fetched
end
@@ -1223,7 +1280,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
accounts
|> Enum.each(fn account_id ->
with %Pleroma.List{} = list <- Pleroma.List.get(id, user),
- %User{} = followed <- Pleroma.User.get_cached_by_id(account_id) do
+ %User{} = followed <- User.get_cached_by_id(account_id) do
Pleroma.List.unfollow(list, followed)
end
end)
@@ -1307,7 +1364,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
display_sensitive_media: false,
reduce_motion: false,
max_toot_chars: limit,
- mascot: "/images/pleroma-fox-tan-smol.png"
+ mascot: User.get_mascot(user)["url"]
},
rights: %{
delete_others_notice: present?(user.info.is_moderator),
@@ -1634,7 +1691,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> String.replace("{{user}}", user)
with {:ok, %{status: 200, body: body}} <-
- @httpoison.get(
+ HTTP.get(
url,
[],
adapter: [
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 779b9a382..b82d3319b 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -40,7 +40,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target)
requested =
- if follow_activity do
+ if follow_activity && !User.following?(target, user) do
follow_activity.data["state"] == "pending"
else
false
@@ -112,7 +112,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
fields: fields,
bot: bot,
source: %{
- note: "",
+ note: HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
sensitive: false,
pleroma: %{}
},
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index c93d915e5..84ab20a1c 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -157,6 +157,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
bookmarked = Activity.get_bookmark(activity, opts[:for]) != nil
+ thread_muted? =
+ case activity.thread_muted? do
+ thread_muted? when is_boolean(thread_muted?) -> thread_muted?
+ nil -> CommonAPI.thread_muted?(user, activity)
+ end
+
attachment_data = object.data["attachment"] || []
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
@@ -228,7 +234,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
reblogged: reblogged?(activity, opts[:for]),
favourited: present?(favorited),
bookmarked: present?(bookmarked),
- muted: CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user),
+ muted: thread_muted? || User.mutes?(opts[:for], user),
pinned: pinned?(activity, user),
sensitive: sensitive,
spoiler_text: summary_html,
@@ -284,8 +290,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
provider_url: page_url_data.scheme <> "://" <> page_url_data.host,
url: page_url,
image: image_url |> MediaProxy.url(),
- title: rich_media[:title],
- description: rich_media[:description],
+ title: rich_media[:title] || "",
+ description: rich_media[:description] || "",
pleroma: %{
opengraph: rich_media
}
diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex
index 5762e767b..cee6d8481 100644
--- a/lib/pleroma/web/media_proxy/media_proxy.ex
+++ b/lib/pleroma/web/media_proxy/media_proxy.ex
@@ -12,25 +12,27 @@ defmodule Pleroma.Web.MediaProxy do
def url("/" <> _ = url), do: url
def url(url) do
- config = Application.get_env(:pleroma, :media_proxy, [])
- domain = URI.parse(url).host
+ if !enabled?() or local?(url) or whitelisted?(url) do
+ url
+ else
+ encode_url(url)
+ end
+ end
- cond do
- !Keyword.get(config, :enabled, false) or String.starts_with?(url, Pleroma.Web.base_url()) ->
- url
+ defp enabled?, do: Pleroma.Config.get([:media_proxy, :enabled], false)
- Enum.any?(Pleroma.Config.get([:media_proxy, :whitelist]), fn pattern ->
- String.equivalent?(domain, pattern)
- end) ->
- url
+ defp local?(url), do: String.starts_with?(url, Pleroma.Web.base_url())
- true ->
- encode_url(url)
- end
+ defp whitelisted?(url) do
+ %{host: domain} = URI.parse(url)
+
+ Enum.any?(Pleroma.Config.get([:media_proxy, :whitelist]), fn pattern ->
+ String.equivalent?(domain, pattern)
+ end)
end
def encode_url(url) do
- secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
+ secret = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
# Must preserve `%2F` for compatibility with S3
# https://git.pleroma.social/pleroma/pleroma/issues/580
@@ -52,7 +54,7 @@ defmodule Pleroma.Web.MediaProxy do
end
def decode_url(sig, url) do
- secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
+ secret = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
sig = Base.url_decode64!(sig, @base64_opts)
local_sig = :crypto.hmac(:sha, secret, url)
diff --git a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex
new file mode 100644
index 000000000..489d5d3a5
--- /dev/null
+++ b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex
@@ -0,0 +1,41 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MongooseIM.MongooseIMController do
+ use Pleroma.Web, :controller
+ alias Comeonin.Pbkdf2
+ alias Pleroma.Repo
+ alias Pleroma.User
+
+ def user_exists(conn, %{"user" => username}) do
+ with %User{} <- Repo.get_by(User, nickname: username, local: true) do
+ conn
+ |> json(true)
+ else
+ _ ->
+ conn
+ |> put_status(:not_found)
+ |> json(false)
+ end
+ end
+
+ def check_password(conn, %{"user" => username, "pass" => password}) do
+ with %User{password_hash: password_hash} <-
+ Repo.get_by(User, nickname: username, local: true),
+ true <- Pbkdf2.checkpw(password, password_hash) do
+ conn
+ |> json(true)
+ else
+ false ->
+ conn
+ |> put_status(403)
+ |> json(false)
+
+ _ ->
+ conn
+ |> put_status(:not_found)
+ |> json(false)
+ end
+ end
+end
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
index 3bf2a0fbc..59f3d4e11 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
@@ -12,8 +12,6 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.Federator.Publisher
- plug(Pleroma.Web.FederatingPlug)
-
def schemas(conn, _params) do
response = %{
links: [
@@ -34,20 +32,15 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
# returns a nodeinfo 2.0 map, since 2.1 just adds a repository field
# under software.
def raw_nodeinfo do
- instance = Application.get_env(:pleroma, :instance)
- media_proxy = Application.get_env(:pleroma, :media_proxy)
- suggestions = Application.get_env(:pleroma, :suggestions)
- chat = Application.get_env(:pleroma, :chat)
- gopher = Application.get_env(:pleroma, :gopher)
stats = Stats.get_stats()
mrf_simple =
- Application.get_env(:pleroma, :mrf_simple)
+ Config.get(:mrf_simple)
|> Enum.into(%{})
# This horror is needed to convert regex sigils to strings
mrf_keyword =
- Application.get_env(:pleroma, :mrf_keyword, [])
+ Config.get(:mrf_keyword, [])
|> Enum.map(fn {key, value} ->
{key,
Enum.map(value, fn
@@ -76,14 +69,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
MRF.get_policies()
|> Enum.map(fn policy -> to_string(policy) |> String.split(".") |> List.last() end)
- quarantined = Keyword.get(instance, :quarantined_instances)
-
- quarantined =
- if is_list(quarantined) do
- quarantined
- else
- []
- end
+ quarantined = Config.get([:instance, :quarantined_instances], [])
staff_accounts =
User.all_superusers()
@@ -94,7 +80,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|> Enum.into(%{}, fn {k, v} -> {k, length(v)} end)
federation_response =
- if Keyword.get(instance, :mrf_transparency) do
+ if Config.get([:instance, :mrf_transparency]) do
%{
mrf_policies: mrf_policies,
mrf_simple: mrf_simple,
@@ -111,22 +97,22 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
"pleroma_api",
"mastodon_api",
"mastodon_api_streaming",
- if Keyword.get(media_proxy, :enabled) do
+ if Config.get([:media_proxy, :enabled]) do
"media_proxy"
end,
- if Keyword.get(gopher, :enabled) do
+ if Config.get([:gopher, :enabled]) do
"gopher"
end,
- if Keyword.get(chat, :enabled) do
+ if Config.get([:chat, :enabled]) do
"chat"
end,
- if Keyword.get(suggestions, :enabled) do
+ if Config.get([:suggestions, :enabled]) do
"suggestions"
end,
- if Keyword.get(instance, :allow_relay) do
+ if Config.get([:instance, :allow_relay]) do
"relay"
end,
- if Keyword.get(instance, :safe_dm_mentions) do
+ if Config.get([:instance, :safe_dm_mentions]) do
"safe_dm_mentions"
end
]
@@ -143,7 +129,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
inbound: [],
outbound: []
},
- openRegistrations: Keyword.get(instance, :registrations_open),
+ openRegistrations: Config.get([:instance, :registrations_open]),
usage: %{
users: %{
total: stats.user_count || 0
@@ -151,29 +137,29 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
localPosts: stats.status_count || 0
},
metadata: %{
- nodeName: Keyword.get(instance, :name),
- nodeDescription: Keyword.get(instance, :description),
- private: !Keyword.get(instance, :public, true),
+ nodeName: Config.get([:instance, :name]),
+ nodeDescription: Config.get([:instance, :description]),
+ private: !Config.get([:instance, :public], true),
suggestions: %{
- enabled: Keyword.get(suggestions, :enabled, false),
- thirdPartyEngine: Keyword.get(suggestions, :third_party_engine, ""),
- timeout: Keyword.get(suggestions, :timeout, 5000),
- limit: Keyword.get(suggestions, :limit, 23),
- web: Keyword.get(suggestions, :web, "")
+ enabled: Config.get([:suggestions, :enabled], false),
+ thirdPartyEngine: Config.get([:suggestions, :third_party_engine], ""),
+ timeout: Config.get([:suggestions, :timeout], 5000),
+ limit: Config.get([:suggestions, :limit], 23),
+ web: Config.get([:suggestions, :web], "")
},
staffAccounts: staff_accounts,
federation: federation_response,
- postFormats: Keyword.get(instance, :allowed_post_formats),
+ postFormats: Config.get([:instance, :allowed_post_formats]),
uploadLimits: %{
- general: Keyword.get(instance, :upload_limit),
- avatar: Keyword.get(instance, :avatar_upload_limit),
- banner: Keyword.get(instance, :banner_upload_limit),
- background: Keyword.get(instance, :background_upload_limit)
+ general: Config.get([:instance, :upload_limit]),
+ avatar: Config.get([:instance, :avatar_upload_limit]),
+ banner: Config.get([:instance, :banner_upload_limit]),
+ background: Config.get([:instance, :background_upload_limit])
},
- accountActivationRequired: Keyword.get(instance, :account_activation_required, false),
- invitesEnabled: Keyword.get(instance, :invites_enabled, false),
+ accountActivationRequired: Config.get([:instance, :account_activation_required], false),
+ invitesEnabled: Config.get([:instance, :invites_enabled], false),
features: features,
- restrictedNicknames: Pleroma.Config.get([Pleroma.User, :restricted_nicknames])
+ restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames])
}
}
end
diff --git a/lib/pleroma/web/oauth/authorization.ex b/lib/pleroma/web/oauth/authorization.ex
index b47688de1..18973413e 100644
--- a/lib/pleroma/web/oauth/authorization.ex
+++ b/lib/pleroma/web/oauth/authorization.ex
@@ -20,7 +20,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
field(:scopes, {:array, :string}, default: [])
field(:valid_until, :naive_datetime_usec)
field(:used, :boolean, default: false)
- belongs_to(:user, Pleroma.User, type: Pleroma.FlakeId)
+ belongs_to(:user, User, type: Pleroma.FlakeId)
belongs_to(:app, App)
timestamps()
diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex
index ef047d565..f412f7eb2 100644
--- a/lib/pleroma/web/oauth/token.ex
+++ b/lib/pleroma/web/oauth/token.ex
@@ -5,7 +5,6 @@
defmodule Pleroma.Web.OAuth.Token do
use Ecto.Schema
- import Ecto.Query
import Ecto.Changeset
alias Pleroma.Repo
@@ -13,6 +12,7 @@ defmodule Pleroma.Web.OAuth.Token do
alias Pleroma.Web.OAuth.App
alias Pleroma.Web.OAuth.Authorization
alias Pleroma.Web.OAuth.Token
+ alias Pleroma.Web.OAuth.Token.Query
@expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600)
@type t :: %__MODULE__{}
@@ -22,7 +22,7 @@ defmodule Pleroma.Web.OAuth.Token do
field(:refresh_token, :string)
field(:scopes, {:array, :string}, default: [])
field(:valid_until, :naive_datetime_usec)
- belongs_to(:user, Pleroma.User, type: Pleroma.FlakeId)
+ belongs_to(:user, User, type: Pleroma.FlakeId)
belongs_to(:app, App)
timestamps()
@@ -31,17 +31,17 @@ defmodule Pleroma.Web.OAuth.Token do
@doc "Gets token for app by access token"
@spec get_by_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
def get_by_token(%App{id: app_id} = _app, token) do
- from(t in __MODULE__, where: t.app_id == ^app_id and t.token == ^token)
+ Query.get_by_app(app_id)
+ |> Query.get_by_token(token)
|> Repo.find_resource()
end
@doc "Gets token for app by refresh token"
@spec get_by_refresh_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
def get_by_refresh_token(%App{id: app_id} = _app, token) do
- from(t in __MODULE__,
- where: t.app_id == ^app_id and t.refresh_token == ^token,
- preload: [:user]
- )
+ Query.get_by_app(app_id)
+ |> Query.get_by_refresh_token(token)
+ |> Query.preload([:user])
|> Repo.find_resource()
end
@@ -97,29 +97,25 @@ defmodule Pleroma.Web.OAuth.Token do
end
def delete_user_tokens(%User{id: user_id}) do
- from(
- t in Token,
- where: t.user_id == ^user_id
- )
+ Query.get_by_user(user_id)
|> Repo.delete_all()
end
def delete_user_token(%User{id: user_id}, token_id) do
- from(
- t in Token,
- where: t.user_id == ^user_id,
- where: t.id == ^token_id
- )
+ Query.get_by_user(user_id)
+ |> Query.get_by_id(token_id)
+ |> Repo.delete_all()
+ end
+
+ def delete_expired_tokens do
+ Query.get_expired_tokens()
|> Repo.delete_all()
end
def get_user_tokens(%User{id: user_id}) do
- from(
- t in Token,
- where: t.user_id == ^user_id
- )
+ Query.get_by_user(user_id)
+ |> Query.preload([:app])
|> Repo.all()
- |> Repo.preload(:app)
end
def is_expired?(%__MODULE__{valid_until: valid_until}) do
diff --git a/lib/pleroma/web/oauth/token/clean_worker.ex b/lib/pleroma/web/oauth/token/clean_worker.ex
new file mode 100644
index 000000000..dca852449
--- /dev/null
+++ b/lib/pleroma/web/oauth/token/clean_worker.ex
@@ -0,0 +1,41 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.OAuth.Token.CleanWorker do
+ @moduledoc """
+ The module represents functions to clean an expired oauth tokens.
+ """
+
+ # 10 seconds
+ @start_interval 10_000
+ @interval Pleroma.Config.get(
+ # 24 hours
+ [:oauth2, :clean_expired_tokens_interval],
+ 86_400_000
+ )
+ @queue :background
+
+ alias Pleroma.Web.OAuth.Token
+
+ def start_link, do: GenServer.start_link(__MODULE__, nil)
+
+ def init(_) do
+ if Pleroma.Config.get([:oauth2, :clean_expired_tokens], false) do
+ Process.send_after(self(), :perform, @start_interval)
+ {:ok, nil}
+ else
+ :ignore
+ end
+ end
+
+ @doc false
+ def handle_info(:perform, state) do
+ Process.send_after(self(), :perform, @interval)
+ PleromaJobQueue.enqueue(@queue, __MODULE__, [:clean])
+ {:noreply, state}
+ end
+
+ # Job Worker Callbacks
+ def perform(:clean), do: Token.delete_expired_tokens()
+end
diff --git a/lib/pleroma/web/oauth/token/query.ex b/lib/pleroma/web/oauth/token/query.ex
new file mode 100644
index 000000000..d92e1f071
--- /dev/null
+++ b/lib/pleroma/web/oauth/token/query.ex
@@ -0,0 +1,55 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.OAuth.Token.Query do
+ @moduledoc """
+ Contains queries for OAuth Token.
+ """
+
+ import Ecto.Query, only: [from: 2]
+
+ @type query :: Ecto.Queryable.t() | Token.t()
+
+ alias Pleroma.Web.OAuth.Token
+
+ @spec get_by_refresh_token(query, String.t()) :: query
+ def get_by_refresh_token(query \\ Token, refresh_token) do
+ from(q in query, where: q.refresh_token == ^refresh_token)
+ end
+
+ @spec get_by_token(query, String.t()) :: query
+ def get_by_token(query \\ Token, token) do
+ from(q in query, where: q.token == ^token)
+ end
+
+ @spec get_by_app(query, String.t()) :: query
+ def get_by_app(query \\ Token, app_id) do
+ from(q in query, where: q.app_id == ^app_id)
+ end
+
+ @spec get_by_id(query, String.t()) :: query
+ def get_by_id(query \\ Token, id) do
+ from(q in query, where: q.id == ^id)
+ end
+
+ @spec get_expired_tokens(query, DateTime.t() | nil) :: query
+ def get_expired_tokens(query \\ Token, date \\ nil) do
+ expired_date = date || Timex.now()
+ from(q in query, where: fragment("?", q.valid_until) < ^expired_date)
+ end
+
+ @spec get_by_user(query, String.t()) :: query
+ def get_by_user(query \\ Token, user_id) do
+ from(q in query, where: q.user_id == ^user_id)
+ end
+
+ @spec preload(query, any) :: query
+ def preload(query \\ Token, assoc_preload \\ [])
+
+ def preload(query, assoc_preload) when is_list(assoc_preload) do
+ from(q in query, preload: ^assoc_preload)
+ end
+
+ def preload(query, _assoc_preload), do: query
+end
diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex
index 61515b31e..6ed089d84 100644
--- a/lib/pleroma/web/ostatus/ostatus.ex
+++ b/lib/pleroma/web/ostatus/ostatus.ex
@@ -3,13 +3,12 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OStatus do
- @httpoison Application.get_env(:pleroma, :httpoison)
-
import Ecto.Query
import Pleroma.Web.XML
require Logger
alias Pleroma.Activity
+ alias Pleroma.HTTP
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
@@ -363,7 +362,7 @@ defmodule Pleroma.Web.OStatus do
def fetch_activity_from_atom_url(url) do
with true <- String.starts_with?(url, "http"),
{:ok, %{body: body, status: code}} when code in 200..299 <-
- @httpoison.get(
+ HTTP.get(
url,
[{:Accept, "application/atom+xml"}]
) do
@@ -380,7 +379,7 @@ defmodule Pleroma.Web.OStatus do
Logger.debug("Trying to fetch #{url}")
with true <- String.starts_with?(url, "http"),
- {:ok, %{body: body}} <- @httpoison.get(url, []),
+ {:ok, %{body: body}} <- HTTP.get(url, []),
{:ok, atom_url} <- get_atom_url(body) do
fetch_activity_from_atom_url(atom_url)
else
diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex
index 0162a5be9..9bc8f2559 100644
--- a/lib/pleroma/web/rich_media/helpers.ex
+++ b/lib/pleroma/web/rich_media/helpers.ex
@@ -24,6 +24,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do
with true <- Pleroma.Config.get([:rich_media, :enabled]),
%Object{} = object <- Object.normalize(activity),
+ false <- object.data["sensitive"] || false,
{:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
:ok <- validate_page_url(page_url),
{:ok, rich_media} <- Parser.parse(page_url) do
diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex
index 62e8fa610..e4595800c 100644
--- a/lib/pleroma/web/rich_media/parser.ex
+++ b/lib/pleroma/web/rich_media/parser.ex
@@ -37,7 +37,10 @@ defmodule Pleroma.Web.RichMedia.Parser do
try do
{:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options)
- html |> maybe_parse() |> clean_parsed_data() |> check_parsed_data()
+ html
+ |> maybe_parse()
+ |> clean_parsed_data()
+ |> check_parsed_data()
rescue
e ->
{:error, "Parsing error: #{inspect(e)}"}
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index bbc2fda9b..eb3ee03f3 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -194,6 +194,14 @@ defmodule Pleroma.Web.Router do
get("/users", AdminAPIController, :list_users)
get("/users/:nickname", AdminAPIController, :user_show)
+
+ get("/reports", AdminAPIController, :list_reports)
+ get("/reports/:id", AdminAPIController, :report_show)
+ put("/reports/:id", AdminAPIController, :report_update_state)
+ post("/reports/:id/respond", AdminAPIController, :report_respond)
+
+ put("/statuses/:id", AdminAPIController, :status_update)
+ delete("/statuses/:id", AdminAPIController, :status_delete)
end
scope "/", Pleroma.Web.TwitterAPI do
@@ -344,6 +352,9 @@ defmodule Pleroma.Web.Router do
post("/pleroma/flavour/:flavour", MastodonAPIController, :set_flavour)
+ get("/pleroma/mascot", MastodonAPIController, :get_mascot)
+ put("/pleroma/mascot", MastodonAPIController, :set_mascot)
+
post("/reports", MastodonAPIController, :reports)
end
@@ -696,9 +707,15 @@ defmodule Pleroma.Web.Router do
end
end
+ scope "/", Pleroma.Web.MongooseIM do
+ get("/user_exists", MongooseIMController, :user_exists)
+ get("/check_password", MongooseIMController, :check_password)
+ end
+
scope "/", Fallback do
get("/registration/:token", RedirectController, :registration_page)
get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta)
+ get("/api*path", RedirectController, :api_not_implemented)
get("/*path", RedirectController, :redirector)
options("/*path", RedirectController, :empty)
@@ -710,6 +727,12 @@ defmodule Fallback.RedirectController do
alias Pleroma.User
alias Pleroma.Web.Metadata
+ def api_not_implemented(conn, _params) do
+ conn
+ |> put_status(404)
+ |> json(%{error: "Not implemented"})
+ end
+
def redirector(conn, _params, code \\ 200) do
conn
|> put_resp_content_type("text/html")
diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex
index 42709ab47..9e91a5a40 100644
--- a/lib/pleroma/web/salmon/salmon.ex
+++ b/lib/pleroma/web/salmon/salmon.ex
@@ -5,12 +5,12 @@
defmodule Pleroma.Web.Salmon do
@behaviour Pleroma.Web.Federator.Publisher
- @httpoison Application.get_env(:pleroma, :httpoison)
-
use Bitwise
alias Pleroma.Activity
+ alias Pleroma.HTTP
alias Pleroma.Instances
+ alias Pleroma.Keys
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Federator.Publisher
@@ -89,45 +89,6 @@ defmodule Pleroma.Web.Salmon do
"RSA.#{modulus_enc}.#{exponent_enc}"
end
- # Native generation of RSA keys is only available since OTP 20+ and in default build conditions
- # We try at compile time to generate natively an RSA key otherwise we fallback on the old way.
- try do
- _ = :public_key.generate_key({:rsa, 2048, 65_537})
-
- def generate_rsa_pem do
- key = :public_key.generate_key({:rsa, 2048, 65_537})
- entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
- pem = :public_key.pem_encode([entry]) |> String.trim_trailing()
- {:ok, pem}
- end
- rescue
- _ ->
- def generate_rsa_pem do
- port = Port.open({:spawn, "openssl genrsa"}, [:binary])
-
- {:ok, pem} =
- receive do
- {^port, {:data, pem}} -> {:ok, pem}
- end
-
- Port.close(port)
-
- if Regex.match?(~r/RSA PRIVATE KEY/, pem) do
- {:ok, pem}
- else
- :error
- end
- end
- end
-
- def keys_from_pem(pem) do
- [private_key_code] = :public_key.pem_decode(pem)
- private_key = :public_key.pem_entry_decode(private_key_code)
- {:RSAPrivateKey, _, modulus, exponent, _, _, _, _, _, _, _} = private_key
- public_key = {:RSAPublicKey, modulus, exponent}
- {:ok, private_key, public_key}
- end
-
def encode(private_key, doc) do
type = "application/atom+xml"
encoding = "base64url"
@@ -176,7 +137,7 @@ defmodule Pleroma.Web.Salmon do
def publish_one(%{recipient: url, feed: feed} = params) when is_binary(url) do
with {:ok, %{status: code}} when code in 200..299 <-
- @httpoison.post(
+ HTTP.post(
url,
feed,
[{"Content-Type", "application/magic-envelope+xml"}]
@@ -227,7 +188,7 @@ defmodule Pleroma.Web.Salmon do
|> :xmerl.export_simple(:xmerl_xml)
|> to_string
- {:ok, private, _} = keys_from_pem(keys)
+ {:ok, private, _} = Keys.keys_from_pem(keys)
{:ok, feed} = encode(private, feed)
remote_users = remote_users(activity)
@@ -253,7 +214,7 @@ defmodule Pleroma.Web.Salmon do
def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end)
def gather_webfinger_links(%User{} = user) do
- {:ok, _private, public} = keys_from_pem(user.info.keys)
+ {:ok, _private, public} = Keys.keys_from_pem(user.info.keys)
magic_key = encode_key(public)
[
diff --git a/lib/pleroma/web/templates/layout/app.html.eex b/lib/pleroma/web/templates/layout/app.html.eex
index 3389c91cc..85ec4d76c 100644
--- a/lib/pleroma/web/templates/layout/app.html.eex
+++ b/lib/pleroma/web/templates/layout/app.html.eex
@@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui" />
<title>
- <%= Application.get_env(:pleroma, :instance)[:name] %>
+ <%= Pleroma.Config.get([:instance, :name]) %>
</title>
<style>
body {
@@ -194,7 +194,7 @@
</head>
<body>
<div class="container">
- <h1><%= Application.get_env(:pleroma, :instance)[:name] %></h1>
+ <h1><%= Pleroma.Config.get([:instance, :name]) %></h1>
<%= render @view_module, @view_template, assigns %>
</div>
</body>
diff --git a/lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex b/lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex
index 5659c7828..ac63811d1 100644
--- a/lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex
+++ b/lib/pleroma/web/templates/mastodon_api/mastodon/index.html.eex
@@ -4,7 +4,7 @@
<meta charset='utf-8'>
<meta content='width=device-width, initial-scale=1' name='viewport'>
<title>
-<%= Application.get_env(:pleroma, :instance)[:name] %>
+<%= Pleroma.Config.get([:instance, :name]) %>
</title>
<link rel="icon" type="image/png" href="/favicon.png"/>
<script crossorigin='anonymous' src="/packs/locales.js"></script>
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index 3c5a70be9..1b6b33e69 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -101,9 +101,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|> Map.put("blocking_user", user)
|> Map.put("user", user)
- activities =
- ActivityPub.fetch_activities([user.ap_id | user.following], params)
- |> ActivityPub.contain_timeline(user)
+ activities = ActivityPub.fetch_activities([user.ap_id | user.following], params)
conn
|> put_view(ActivityView)
@@ -730,7 +728,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn
def only_if_public_instance(conn, _) do
- if Keyword.get(Application.get_env(:pleroma, :instance), :public) do
+ if Pleroma.Config.get([:instance, :public]) do
conn
else
conn
diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex
index 44bcafe0e..e84af84dc 100644
--- a/lib/pleroma/web/twitter_api/views/activity_view.ex
+++ b/lib/pleroma/web/twitter_api/views/activity_view.ex
@@ -284,6 +284,12 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
)
+ thread_muted? =
+ case activity.thread_muted? do
+ thread_muted? when is_boolean(thread_muted?) -> thread_muted?
+ nil -> CommonAPI.thread_muted?(user, activity)
+ end
+
%{
"id" => activity.id,
"uri" => object.data["id"],
@@ -314,7 +320,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
"summary" => summary,
"summary_html" => summary |> Formatter.emojify(object.data["emoji"]),
"card" => card,
- "muted" => CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user)
+ "muted" => thread_muted? || User.mutes?(opts[:for], user)
}
end
diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex
index 3a3b98a10..3fca72de8 100644
--- a/lib/pleroma/web/web_finger/web_finger.ex
+++ b/lib/pleroma/web/web_finger/web_finger.ex
@@ -3,12 +3,10 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.WebFinger do
- @httpoison Application.get_env(:pleroma, :httpoison)
-
+ alias Pleroma.HTTP
alias Pleroma.User
alias Pleroma.Web
alias Pleroma.Web.Federator.Publisher
- alias Pleroma.Web.Salmon
alias Pleroma.Web.XML
alias Pleroma.XmlBuilder
require Jason
@@ -61,7 +59,7 @@ defmodule Pleroma.Web.WebFinger do
end
def represent_user(user, "JSON") do
- {:ok, user} = ensure_keys_present(user)
+ {:ok, user} = User.ensure_keys_present(user)
%{
"subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}",
@@ -71,7 +69,7 @@ defmodule Pleroma.Web.WebFinger do
end
def represent_user(user, "XML") do
- {:ok, user} = ensure_keys_present(user)
+ {:ok, user} = User.ensure_keys_present(user)
links =
gather_links(user)
@@ -88,27 +86,6 @@ defmodule Pleroma.Web.WebFinger do
|> XmlBuilder.to_doc()
end
- # This seems a better fit in Salmon
- def ensure_keys_present(user) do
- info = user.info
-
- if info.keys do
- {:ok, user}
- else
- {:ok, pem} = Salmon.generate_rsa_pem()
-
- info_cng =
- info
- |> Pleroma.User.Info.set_keys(pem)
-
- cng =
- Ecto.Changeset.change(user)
- |> Ecto.Changeset.put_embed(:info, info_cng)
-
- User.update_and_set_cache(cng)
- end
- end
-
defp get_magic_key(magic_key) do
"data:application/magic-public-key," <> magic_key = magic_key
{:ok, magic_key}
@@ -198,11 +175,11 @@ defmodule Pleroma.Web.WebFinger do
def find_lrdd_template(domain) do
with {:ok, %{status: status, body: body}} when status in 200..299 <-
- @httpoison.get("http://#{domain}/.well-known/host-meta", []) do
+ HTTP.get("http://#{domain}/.well-known/host-meta", []) do
get_template_from_xml(body)
else
_ ->
- with {:ok, %{body: body}} <- @httpoison.get("https://#{domain}/.well-known/host-meta", []) do
+ with {:ok, %{body: body}} <- HTTP.get("https://#{domain}/.well-known/host-meta", []) do
get_template_from_xml(body)
else
e -> {:error, "Can't find LRDD template: #{inspect(e)}"}
@@ -231,7 +208,7 @@ defmodule Pleroma.Web.WebFinger do
end
with response <-
- @httpoison.get(
+ HTTP.get(
address,
Accept: "application/xrd+xml,application/jrd+json"
),
diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex
index 7ad0414ab..b61f388b8 100644
--- a/lib/pleroma/web/websub/websub.ex
+++ b/lib/pleroma/web/websub/websub.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.Websub do
alias Ecto.Changeset
alias Pleroma.Activity
+ alias Pleroma.HTTP
alias Pleroma.Instances
alias Pleroma.Repo
alias Pleroma.User
@@ -24,9 +25,7 @@ defmodule Pleroma.Web.Websub do
@behaviour Pleroma.Web.Federator.Publisher
- @httpoison Application.get_env(:pleroma, :httpoison)
-
- def verify(subscription, getter \\ &@httpoison.get/3) do
+ def verify(subscription, getter \\ &HTTP.get/3) do
challenge = Base.encode16(:crypto.strong_rand_bytes(8))
lease_seconds = NaiveDateTime.diff(subscription.valid_until, subscription.updated_at)
lease_seconds = lease_seconds |> to_string
@@ -207,7 +206,7 @@ defmodule Pleroma.Web.Websub do
requester.(subscription)
end
- def gather_feed_data(topic, getter \\ &@httpoison.get/1) do
+ def gather_feed_data(topic, getter \\ &HTTP.get/1) do
with {:ok, response} <- getter.(topic),
status when status in 200..299 <- response.status,
body <- response.body,
@@ -236,7 +235,7 @@ defmodule Pleroma.Web.Websub do
end
end
- def request_subscription(websub, poster \\ &@httpoison.post/3, timeout \\ 10_000) do
+ def request_subscription(websub, poster \\ &HTTP.post/3, timeout \\ 10_000) do
data = [
"hub.mode": "subscribe",
"hub.topic": websub.topic,
@@ -294,7 +293,7 @@ defmodule Pleroma.Web.Websub do
Logger.info(fn -> "Pushing #{topic} to #{callback}" end)
with {:ok, %{status: code}} when code in 200..299 <-
- @httpoison.post(
+ HTTP.post(
callback,
xml,
[