diff --git a/.dialyzer_ignore.exs b/.dialyzer_ignore.exs new file mode 100644 index 000000000..865e7d782 --- /dev/null +++ b/.dialyzer_ignore.exs @@ -0,0 +1,6 @@ +[ +{"lib/cachex.ex", "Unknown type: Spec.cache/0."}, +{"lib/pleroma/web/plugs/rate_limiter.ex", "The pattern can never match the type {:commit, _} | {:ignore, _}."}, +{"lib/pleroma/web/plugs/rate_limiter.ex", "Function get_scale/2 will never be called."}, +{"lib/pleroma/web/plugs/rate_limiter.ex", "Function initialize_buckets!/1 will never be called."} +] diff --git a/.gitignore b/.gitignore index 4009bd844..3b672184e 100644 --- a/.gitignore +++ b/.gitignore @@ -57,5 +57,6 @@ pleroma.iml .tool-versions # Editor temp files -/*~ -/*# +*~ +*# +*.swp diff --git a/CHANGELOG.md b/CHANGELOG.md index e071fe693..063d51d4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## 2.6.2 + +### Security +- MRF StealEmojiPolicy: Sanitize shortcodes (thanks to Hazel K for the report + ## 2.6.1 ### Changed - - Document maximum supported version of Erlang & Elixir diff --git a/changelog.d/atom-leak.skip b/changelog.d/atom-leak.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/bandit.change b/changelog.d/bandit.change new file mode 100644 index 000000000..7a1104314 --- /dev/null +++ b/changelog.d/bandit.change @@ -0,0 +1 @@ +Support Bandit as an alternative to Cowboy for the HTTP server. diff --git a/changelog.d/bugfix-ccworks.fix b/changelog.d/bugfix-ccworks.fix new file mode 100644 index 000000000..658e27b86 --- /dev/null +++ b/changelog.d/bugfix-ccworks.fix @@ -0,0 +1 @@ +Fix federation with Convergence AP Bridge \ No newline at end of file diff --git a/changelog.d/config-stat-symlink.fix b/changelog.d/config-stat-symlink.fix new file mode 100644 index 000000000..c8b98225d --- /dev/null +++ b/changelog.d/config-stat-symlink.fix @@ -0,0 +1 @@ +- Config: Check the permissions of the linked file instead of the symlink diff --git a/changelog.d/content-length.fix b/changelog.d/content-length.fix new file mode 100644 index 000000000..dee906a9d --- /dev/null +++ b/changelog.d/content-length.fix @@ -0,0 +1 @@ +MediaProxy was setting the content-length header which is not permitted by RFC9112§6.2 when we are chunking the reply as it conflicts with the existence of the transfer-encoding header. diff --git a/changelog.d/deps-bump-2024-01-25.skip b/changelog.d/deps-bump-2024-01-25.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/dialyzer2.skip b/changelog.d/dialyzer2.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/dialyzer3.skip b/changelog.d/dialyzer3.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/dialyzer4.skip b/changelog.d/dialyzer4.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/exile-bsds.skip b/changelog.d/exile-bsds.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/exile-macos.skip b/changelog.d/exile-macos.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/exile.skip b/changelog.d/exile.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/gun-logs.skip b/changelog.d/gun-logs.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/gun_pool.fix b/changelog.d/gun_pool.fix new file mode 100644 index 000000000..94ec9103d --- /dev/null +++ b/changelog.d/gun_pool.fix @@ -0,0 +1 @@ +Fix logic error in Gun connection pooling which prevented retries even when the worker was launched with retry = true diff --git a/changelog.d/mastodon_directory.fix b/changelog.d/mastodon_directory.fix new file mode 100644 index 000000000..937c8f864 --- /dev/null +++ b/changelog.d/mastodon_directory.fix @@ -0,0 +1 @@ +Mastodon API /api/v1/directory: Fix listing directory contents when not authenticated diff --git a/changelog.d/memleak.fix b/changelog.d/memleak.fix new file mode 100644 index 000000000..2465921c0 --- /dev/null +++ b/changelog.d/memleak.fix @@ -0,0 +1 @@ +Fix a memory leak caused by Websocket connections that would not enter a state where a full garbage collection run could be triggered. diff --git a/changelog.d/mergeback-2.6.2.skip b/changelog.d/mergeback-2.6.2.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/mrf_hashtags.fix b/changelog.d/mrf_hashtags.fix new file mode 100644 index 000000000..c44c2376b --- /dev/null +++ b/changelog.d/mrf_hashtags.fix @@ -0,0 +1 @@ +Federated timeline removal of hashtags via MRF HashtagPolicy diff --git a/changelog.d/notifications-index.fix b/changelog.d/notifications-index.fix new file mode 100644 index 000000000..4617cbec0 --- /dev/null +++ b/changelog.d/notifications-index.fix @@ -0,0 +1 @@ +Fix notifications query which was not using the index properly diff --git a/changelog.d/oauth-nickname.skip b/changelog.d/oauth-nickname.skip new file mode 100644 index 000000000..02f16e06c --- /dev/null +++ b/changelog.d/oauth-nickname.skip @@ -0,0 +1 @@ +Use User.full_nickname/1 in oauth html template \ No newline at end of file diff --git a/changelog.d/rich_media.fix b/changelog.d/rich_media.fix new file mode 100644 index 000000000..08f119550 --- /dev/null +++ b/changelog.d/rich_media.fix @@ -0,0 +1 @@ +Rich Media Preview cache eviction when the activity is updated. diff --git a/changelog.d/rich_media_tests.skip b/changelog.d/rich_media_tests.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/tesla.deps b/changelog.d/tesla.deps new file mode 100644 index 000000000..799bbc670 --- /dev/null +++ b/changelog.d/tesla.deps @@ -0,0 +1 @@ +Update Tesla HTTP client middleware to 1.8.0 diff --git a/changelog.d/websocket-refactor.change b/changelog.d/websocket-refactor.change new file mode 100644 index 000000000..3c447832b --- /dev/null +++ b/changelog.d/websocket-refactor.change @@ -0,0 +1 @@ +Refactor the Mastodon /api/v1/streaming websocket handler to use Phoenix.Socket.Transport diff --git a/ci/elixir-1.15-otp25/Dockerfile b/ci/elixir-1.15-otp25/Dockerfile index a2b566873..3335c6e36 100644 --- a/ci/elixir-1.15-otp25/Dockerfile +++ b/ci/elixir-1.15-otp25/Dockerfile @@ -1,4 +1,4 @@ -FROM elixir:1.12.3 +FROM elixir:1.15.7-otp-25 # Single RUN statement, otherwise intermediate images are created # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run diff --git a/config/config.exs b/config/config.exs index 7ff3aaa22..435387a64 100644 --- a/config/config.exs +++ b/config/config.exs @@ -114,14 +114,7 @@ config :pleroma, :uri_schemes, config :pleroma, Pleroma.Web.Endpoint, url: [host: "localhost"], http: [ - ip: {127, 0, 0, 1}, - dispatch: [ - {:_, - [ - {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, - {:_, Plug.Cowboy.Handler, {Pleroma.Web.Endpoint, []}} - ]} - ] + ip: {127, 0, 0, 1} ], protocol: "https", secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl", @@ -911,6 +904,8 @@ config :pleroma, Pleroma.Application, max_restarts: 3, streamer_registry: true +config :pleroma, Pleroma.Uploaders.Uploader, timeout: 30_000 + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{Mix.env()}.exs" diff --git a/config/dev.exs b/config/dev.exs index ab3e83c12..fe8de5045 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -8,8 +8,7 @@ import Config # with brunch.io to recompile .js and .css sources. config :pleroma, Pleroma.Web.Endpoint, http: [ - port: 4000, - protocol_options: [max_request_line_length: 8192, max_header_value_length: 8192] + port: 4000 ], protocol: "http", debug_errors: true, diff --git a/config/test.exs b/config/test.exs index 28d0364c6..9d752bdf8 100644 --- a/config/test.exs +++ b/config/test.exs @@ -170,6 +170,10 @@ config :pleroma, Pleroma.Application, streamer_registry: false, test_http_pools: true +config :pleroma, Pleroma.Uploaders.Uploader, timeout: 1_000 + +config :pleroma, Pleroma.Emoji.Loader, test_emoji: true + if File.exists?("./config/test.secret.exs") do import_config "test.secret.exs" else diff --git a/lib/mix/tasks/pleroma/emoji.ex b/lib/mix/tasks/pleroma/emoji.ex index 537f0715e..8b9c921c8 100644 --- a/lib/mix/tasks/pleroma/emoji.ex +++ b/lib/mix/tasks/pleroma/emoji.ex @@ -111,7 +111,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do {:ok, _} = :zip.unzip(binary_archive, - cwd: pack_path, + cwd: String.to_charlist(pack_path), file_list: files_to_unzip ) diff --git a/lib/phoenix/transports/web_socket/raw.ex b/lib/phoenix/transports/web_socket/raw.ex deleted file mode 100644 index cf4fda79f..000000000 --- a/lib/phoenix/transports/web_socket/raw.ex +++ /dev/null @@ -1,93 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Phoenix.Transports.WebSocket.Raw do - import Plug.Conn, - only: [ - fetch_query_params: 1, - send_resp: 3 - ] - - alias Phoenix.Socket.Transport - - def default_config do - [ - timeout: 60_000, - transport_log: false, - cowboy: Phoenix.Endpoint.CowboyWebSocket - ] - end - - def init(%Plug.Conn{method: "GET"} = conn, {endpoint, handler, transport}) do - {_, opts} = handler.__transport__(transport) - - conn = - conn - |> fetch_query_params - |> Transport.transport_log(opts[:transport_log]) - |> Transport.check_origin(handler, endpoint, opts) - - case conn do - %{halted: false} = conn -> - case handler.connect(%{ - endpoint: endpoint, - transport: transport, - options: [serializer: nil], - params: conn.params - }) do - {:ok, socket} -> - {:ok, conn, {__MODULE__, {socket, opts}}} - - :error -> - send_resp(conn, :forbidden, "") - {:error, conn} - end - - _ -> - {:error, conn} - end - end - - def init(conn, _) do - send_resp(conn, :bad_request, "") - {:error, conn} - end - - def ws_init({socket, config}) do - Process.flag(:trap_exit, true) - {:ok, %{socket: socket}, config[:timeout]} - end - - def ws_handle(op, data, state) do - state.socket.handler - |> apply(:handle, [op, data, state]) - |> case do - {op, data} -> - {:reply, {op, data}, state} - - {op, data, state} -> - {:reply, {op, data}, state} - - %{} = state -> - {:ok, state} - - _ -> - {:ok, state} - end - end - - def ws_info({_, _} = tuple, state) do - {:reply, tuple, state} - end - - def ws_info(_tuple, state), do: {:ok, state} - - def ws_close(state) do - ws_handle(:closed, :normal, state) - end - - def ws_terminate(reason, state) do - ws_handle(:closed, reason, state) - end -end diff --git a/lib/pleroma/activity/html.ex b/lib/pleroma/activity/html.ex index 706b2d36c..ba284b4d5 100644 --- a/lib/pleroma/activity/html.ex +++ b/lib/pleroma/activity/html.ex @@ -28,7 +28,7 @@ defmodule Pleroma.Activity.HTML do end end - defp add_cache_key_for(activity_id, additional_key) do + def add_cache_key_for(activity_id, additional_key) do current = get_cache_keys_for(activity_id) unless additional_key in current do diff --git a/lib/pleroma/announcement.ex b/lib/pleroma/announcement.ex index d97c5e728..5a3c710e8 100644 --- a/lib/pleroma/announcement.ex +++ b/lib/pleroma/announcement.ex @@ -23,19 +23,21 @@ defmodule Pleroma.Announcement do timestamps(type: :utc_datetime) end - def change(struct, params \\ %{}) do - struct - |> cast(validate_params(struct, params), [:data, :starts_at, :ends_at, :rendered]) + @doc "Generates changeset for %Pleroma.Announcement{}" + @spec changeset(%__MODULE__{}, map()) :: %Ecto.Changeset{} + def changeset(announcement \\ %__MODULE__{}, params \\ %{data: %{}}) do + announcement + |> cast(validate_params(announcement, params), [:data, :starts_at, :ends_at, :rendered]) |> validate_required([:data]) end - defp validate_params(struct, params) do + defp validate_params(announcement, params) do base_data = %{ "content" => "", "all_day" => false } - |> Map.merge((struct && struct.data) || %{}) + |> Map.merge((announcement && announcement.data) || %{}) merged_data = Map.merge(base_data, params.data) @@ -61,13 +63,13 @@ defmodule Pleroma.Announcement do end def add(params) do - changeset = change(%__MODULE__{}, params) + changeset = changeset(%__MODULE__{}, params) Repo.insert(changeset) end def update(announcement, params) do - changeset = change(announcement, params) + changeset = changeset(announcement, params) Repo.update(changeset) end diff --git a/lib/pleroma/caching.ex b/lib/pleroma/caching.ex index eb0588708..796a465af 100644 --- a/lib/pleroma/caching.ex +++ b/lib/pleroma/caching.ex @@ -8,10 +8,13 @@ defmodule Pleroma.Caching do @callback put(Cachex.cache(), any(), any(), Keyword.t()) :: {Cachex.status(), boolean()} @callback put(Cachex.cache(), any(), any()) :: {Cachex.status(), boolean()} @callback fetch!(Cachex.cache(), any(), function() | nil) :: any() + @callback fetch(Cachex.cache(), any(), function() | nil) :: + {atom(), any()} | {atom(), any(), any()} # @callback del(Cachex.cache(), any(), Keyword.t()) :: {Cachex.status(), boolean()} @callback del(Cachex.cache(), any()) :: {Cachex.status(), boolean()} @callback stream!(Cachex.cache(), any()) :: Enumerable.t() @callback expire_at(Cachex.cache(), binary(), number()) :: {Cachex.status(), boolean()} + @callback expire(Cachex.cache(), binary(), number()) :: {Cachex.status(), boolean()} @callback exists?(Cachex.cache(), any()) :: {Cachex.status(), boolean()} @callback execute!(Cachex.cache(), function()) :: any() @callback get_and_update(Cachex.cache(), any(), function()) :: diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index a77923264..58d164dc7 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -256,7 +256,7 @@ defmodule Pleroma.Config.DeprecationWarnings do move_namespace_and_warn(@mrf_config_map, warning_preface) end - @spec move_namespace_and_warn([config_map()], String.t()) :: :ok | nil + @spec move_namespace_and_warn([config_map()], String.t()) :: :ok | :error def move_namespace_and_warn(config_map, warning_preface) do warning = Enum.reduce(config_map, "", fn @@ -279,7 +279,7 @@ defmodule Pleroma.Config.DeprecationWarnings do end end - @spec check_media_proxy_whitelist_config() :: :ok | nil + @spec check_media_proxy_whitelist_config() :: :ok | :error def check_media_proxy_whitelist_config do whitelist = Config.get([:media_proxy, :whitelist]) @@ -340,7 +340,7 @@ defmodule Pleroma.Config.DeprecationWarnings do end end - @spec check_activity_expiration_config() :: :ok | nil + @spec check_activity_expiration_config() :: :ok | :error def check_activity_expiration_config do warning_preface = """ !!!DEPRECATION WARNING!!! @@ -356,7 +356,7 @@ defmodule Pleroma.Config.DeprecationWarnings do ) end - @spec check_remote_ip_plug_name() :: :ok | nil + @spec check_remote_ip_plug_name() :: :ok | :error def check_remote_ip_plug_name do warning_preface = """ !!!DEPRECATION WARNING!!! @@ -372,7 +372,7 @@ defmodule Pleroma.Config.DeprecationWarnings do ) end - @spec check_uploaders_s3_public_endpoint() :: :ok | nil + @spec check_uploaders_s3_public_endpoint() :: :ok | :error def check_uploaders_s3_public_endpoint do s3_config = Pleroma.Config.get([Pleroma.Uploaders.S3]) @@ -393,7 +393,7 @@ defmodule Pleroma.Config.DeprecationWarnings do end end - @spec check_old_chat_shoutbox() :: :ok | nil + @spec check_old_chat_shoutbox() :: :ok | :error def check_old_chat_shoutbox do instance_config = Pleroma.Config.get([:instance]) chat_config = Pleroma.Config.get([:chat]) || [] diff --git a/lib/pleroma/config/release_runtime_provider.ex b/lib/pleroma/config/release_runtime_provider.ex index 9ec0f975e..351639836 100644 --- a/lib/pleroma/config/release_runtime_provider.ex +++ b/lib/pleroma/config/release_runtime_provider.ex @@ -21,7 +21,7 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do with_runtime_config = if File.exists?(config_path) do # - %File.Stat{mode: mode} = File.lstat!(config_path) + %File.Stat{mode: mode} = File.stat!(config_path) if Bitwise.band(mode, 0o007) > 0 do raise "Configuration at #{config_path} has world-permissions, execute the following: chmod o= #{config_path}" diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index 42028aa51..0be609a22 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -57,7 +57,7 @@ defmodule Pleroma.Conversation do 3. Bump all relevant participations to 'unread' """ def create_or_bump_for(activity, opts \\ []) do - with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), + with true <- Pleroma.Web.ActivityPub.Visibility.direct?(activity), "Create" <- activity.data["type"], %Object{} = object <- Object.normalize(activity, fetch: false), true <- object.data["type"] in ["Note", "Question"], diff --git a/lib/pleroma/data_migration.ex b/lib/pleroma/data_migration.ex index 8451678fc..be4bf6489 100644 --- a/lib/pleroma/data_migration.ex +++ b/lib/pleroma/data_migration.ex @@ -12,6 +12,8 @@ defmodule Pleroma.DataMigration do import Ecto.Changeset import Ecto.Query + @type t :: %__MODULE__{} + schema "data_migrations" do field(:name, :string) field(:state, State, default: :pending) diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex index 48302f396..21bcb0111 100644 --- a/lib/pleroma/emoji.ex +++ b/lib/pleroma/emoji.ex @@ -51,12 +51,12 @@ defmodule Pleroma.Emoji do end @doc "Returns the path of the emoji `name`." - @spec get(String.t()) :: String.t() | nil + @spec get(String.t()) :: Pleroma.Emoji.t() | nil def get(name) do name = maybe_strip_name(name) case :ets.lookup(@ets, name) do - [{_, path}] -> path + [{_, emoji}] -> emoji _ -> nil end end @@ -138,23 +138,23 @@ defmodule Pleroma.Emoji do emojis = emojis ++ regional_indicators for emoji <- emojis do - def is_unicode_emoji?(unquote(emoji)), do: true + def unicode?(unquote(emoji)), do: true end - def is_unicode_emoji?(_), do: false + def unicode?(_), do: false @emoji_regex ~r/:[A-Za-z0-9_-]+(@.+)?:/ - def is_custom_emoji?(s) when is_binary(s), do: Regex.match?(@emoji_regex, s) + def custom?(s) when is_binary(s), do: Regex.match?(@emoji_regex, s) - def is_custom_emoji?(_), do: false + def custom?(_), do: false def maybe_strip_name(name) when is_binary(name), do: String.trim(name, ":") def maybe_strip_name(name), do: name def maybe_quote(name) when is_binary(name) do - if is_unicode_emoji?(name) do + if unicode?(name) do name else if String.starts_with?(name, ":") do diff --git a/lib/pleroma/emoji/loader.ex b/lib/pleroma/emoji/loader.ex index eb6f6816b..b6e544323 100644 --- a/lib/pleroma/emoji/loader.ex +++ b/lib/pleroma/emoji/loader.ex @@ -15,8 +15,6 @@ defmodule Pleroma.Emoji.Loader do require Logger - @mix_env Mix.env() - @type pattern :: Regex.t() | module() | String.t() @type patterns :: pattern() | [pattern()] @type group_patterns :: keyword(patterns()) @@ -79,7 +77,7 @@ defmodule Pleroma.Emoji.Loader do # for testing emoji.txt entries we do not want exposed in normal operation test_emoji = - if @mix_env == :test do + if Application.get_env(:pleroma, __MODULE__)[:test_emoji] do load_from_file("test/config/emoji.txt", emoji_groups) else [] diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex index 85f7e1877..afc341853 100644 --- a/lib/pleroma/emoji/pack.ex +++ b/lib/pleroma/emoji/pack.ex @@ -100,7 +100,7 @@ defmodule Pleroma.Emoji.Pack do {:ok, _emoji_files} = :zip.unzip( to_charlist(file.path), - [{:file_list, Enum.map(emojies, & &1[:path])}, {:cwd, tmp_dir}] + [{:file_list, Enum.map(emojies, & &1[:path])}, {:cwd, String.to_charlist(tmp_dir)}] ) {_, updated_pack} = diff --git a/lib/pleroma/filter.ex b/lib/pleroma/filter.ex index db88bc021..e827d3cbc 100644 --- a/lib/pleroma/filter.ex +++ b/lib/pleroma/filter.ex @@ -216,9 +216,6 @@ defmodule Pleroma.Filter do :re -> ~r/\b#{phrases}\b/i - - _ -> - nil end end diff --git a/lib/pleroma/gopher/server.ex b/lib/pleroma/gopher/server.ex index 0fde0adcf..54245c9fa 100644 --- a/lib/pleroma/gopher/server.ex +++ b/lib/pleroma/gopher/server.ex @@ -114,7 +114,7 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do def response("/notices/" <> id) do with %Activity{} = activity <- Activity.get_by_id(id), - true <- Visibility.is_public?(activity) do + true <- Visibility.public?(activity) do activities = ActivityPub.fetch_activities_for_context(activity.data["context"]) |> render_activities diff --git a/lib/pleroma/gun/connection_pool/reclaimer.ex b/lib/pleroma/gun/connection_pool/reclaimer.ex index efd5c9fb8..35e7f4b2e 100644 --- a/lib/pleroma/gun/connection_pool/reclaimer.ex +++ b/lib/pleroma/gun/connection_pool/reclaimer.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do def start_monitor do pid = - case :gen_server.start(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do + case GenServer.start_link(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do {:ok, pid} -> pid diff --git a/lib/pleroma/gun/connection_pool/worker_supervisor.ex b/lib/pleroma/gun/connection_pool/worker_supervisor.ex index d26a70be3..24ad61117 100644 --- a/lib/pleroma/gun/connection_pool/worker_supervisor.ex +++ b/lib/pleroma/gun/connection_pool/worker_supervisor.ex @@ -21,7 +21,9 @@ defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do def start_worker(opts, retry \\ false) do case DynamicSupervisor.start_child(__MODULE__, {Pleroma.Gun.ConnectionPool.Worker, opts}) do {:error, :max_children} -> - if retry or free_pool() == :error do + funs = [fn -> !retry end, fn -> match?(:error, free_pool()) end] + + if Enum.any?(funs, fn fun -> fun.() end) do :telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{opts: opts}) {:error, :pool_full} else diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex index 07dfea55b..1a414b37f 100644 --- a/lib/pleroma/helpers/media_helper.ex +++ b/lib/pleroma/helpers/media_helper.ex @@ -43,89 +43,28 @@ defmodule Pleroma.Helpers.MediaHelper do def video_framegrab(url) do with executable when is_binary(executable) <- System.find_executable("ffmpeg"), {:ok, env} <- HTTP.get(url, [], pool: :media), - {:ok, fifo_path} <- mkfifo(), - args = [ - "-y", - "-i", - fifo_path, - "-vframes", - "1", - "-f", - "mjpeg", - "-loglevel", - "error", - "-" - ] do - run_fifo(fifo_path, env, executable, args) + {:ok, pid} <- StringIO.open(env.body) do + body_stream = IO.binstream(pid, 1) + + Exile.stream!( + [ + executable, + "-i", + "pipe:0", + "-vframes", + "1", + "-f", + "mjpeg", + "pipe:1" + ], + input: body_stream, + ignore_epipe: true, + stderr: :disable + ) + |> Enum.into(<<>>) else nil -> {:error, {:ffmpeg, :command_not_found}} {:error, _} = error -> error end end - - defp run_fifo(fifo_path, env, executable, args) do - pid = - Port.open({:spawn_executable, executable}, [ - :use_stdio, - :stream, - :exit_status, - :binary, - args: args - ]) - - fifo = Port.open(to_charlist(fifo_path), [:eof, :binary, :stream, :out]) - fix = Pleroma.Helpers.QtFastStart.fix(env.body) - true = Port.command(fifo, fix) - :erlang.port_close(fifo) - loop_recv(pid) - after - File.rm(fifo_path) - end - - defp mkfifo do - path = Path.join(System.tmp_dir!(), "pleroma-media-preview-pipe-#{Ecto.UUID.generate()}") - - case System.cmd("mkfifo", [path]) do - {_, 0} -> - spawn(fifo_guard(path)) - {:ok, path} - - {_, err} -> - {:error, {:fifo_failed, err}} - end - end - - defp fifo_guard(path) do - pid = self() - - fn -> - ref = Process.monitor(pid) - - receive do - {:DOWN, ^ref, :process, ^pid, _} -> - File.rm(path) - end - end - end - - defp loop_recv(pid) do - loop_recv(pid, <<>>) - end - - defp loop_recv(pid, acc) do - receive do - {^pid, {:data, data}} -> - loop_recv(pid, acc <> data) - - {^pid, {:exit_status, 0}} -> - {:ok, acc} - - {^pid, {:exit_status, status}} -> - {:error, status} - after - 5000 -> - :erlang.port_close(pid) - {:error, :timeout} - end - end end diff --git a/lib/pleroma/helpers/qt_fast_start.ex b/lib/pleroma/helpers/qt_fast_start.ex index 919fec84a..0b200b234 100644 --- a/lib/pleroma/helpers/qt_fast_start.ex +++ b/lib/pleroma/helpers/qt_fast_start.ex @@ -126,9 +126,15 @@ defmodule Pleroma.Helpers.QtFastStart do <>, acc ) do - rewrite_entries(unquote(size), offset, rest, [ - acc | <> - ]) + rewrite_entries( + unquote(size), + offset, + rest, + acc ++ + [ + <> + ] + ) end end diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index 5bf735c4f..84ff2f129 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -6,8 +6,6 @@ defmodule Pleroma.HTML do # Scrubbers are compiled on boot so they can be configured in OTP releases # @on_load :compile_scrubbers - @cachex Pleroma.Config.get([:cachex, :provider], Cachex) - def compile_scrubbers do dir = Path.join(:code.priv_dir(:pleroma), "scrubbers") @@ -67,27 +65,20 @@ defmodule Pleroma.HTML do end end - def extract_first_external_url_from_object(%{data: %{"content" => content}} = object) + @spec extract_first_external_url_from_object(Pleroma.Object.t()) :: + {:ok, String.t()} | {:error, :no_content} + def extract_first_external_url_from_object(%{data: %{"content" => content}}) when is_binary(content) do - unless object.data["fake"] do - key = "URL|#{object.id}" + url = + content + |> Floki.parse_fragment!() + |> Floki.find("a:not(.mention,.hashtag,.attachment,[rel~=\"tag\"])") + |> Enum.take(1) + |> Floki.attribute("href") + |> Enum.at(0) - @cachex.fetch!(:scrubber_cache, key, fn _key -> - {:commit, {:ok, extract_first_external_url(content)}} - end) - else - {:ok, extract_first_external_url(content)} - end + {:ok, url} end def extract_first_external_url_from_object(_), do: {:error, :no_content} - - def extract_first_external_url(content) do - content - |> Floki.parse_fragment!() - |> Floki.find("a:not(.mention,.hashtag,.attachment,[rel~=\"tag\"])") - |> Enum.take(1) - |> Floki.attribute("href") - |> Enum.at(0) - end end diff --git a/lib/pleroma/http/adapter_helper.ex b/lib/pleroma/http/adapter_helper.ex index e9bb2023a..dcb27a29d 100644 --- a/lib/pleroma/http/adapter_helper.ex +++ b/lib/pleroma/http/adapter_helper.ex @@ -15,8 +15,8 @@ defmodule Pleroma.HTTP.AdapterHelper do require Logger @type proxy :: - {Connection.host(), pos_integer()} - | {Connection.proxy_type(), Connection.host(), pos_integer()} + {host(), pos_integer()} + | {proxy_type(), host(), pos_integer()} @callback options(keyword(), URI.t()) :: keyword() diff --git a/lib/pleroma/http/request_builder.ex b/lib/pleroma/http/request_builder.ex index f16fb3b35..0a028a64c 100644 --- a/lib/pleroma/http/request_builder.ex +++ b/lib/pleroma/http/request_builder.ex @@ -54,12 +54,12 @@ defmodule Pleroma.HTTP.RequestBuilder do @doc """ Add optional parameters to the request """ - @spec add_param(Request.t(), atom(), atom(), any()) :: Request.t() + @spec add_param(Request.t(), atom(), atom() | String.t(), any()) :: Request.t() def add_param(request, :query, :query, values), do: %{request | query: values} def add_param(request, :body, :body, value), do: %{request | body: value} - def add_param(request, :body, key, value) do + def add_param(request, :body, key, value) when is_binary(key) do request |> Map.put(:body, Multipart.new()) |> Map.update!( diff --git a/lib/pleroma/maps.ex b/lib/pleroma/maps.ex index 6d586e53e..5020a8ff8 100644 --- a/lib/pleroma/maps.ex +++ b/lib/pleroma/maps.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors +# Copyright © 2017-2024 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Maps do @@ -18,4 +18,17 @@ defmodule Pleroma.Maps do rescue _ -> data end + + def filter_empty_values(data) do + # TODO: Change to Map.filter in Elixir 1.13+ + data + |> Enum.filter(fn + {_k, nil} -> false + {_k, ""} -> false + {_k, []} -> false + {_k, %{} = v} -> Map.keys(v) != [] + {_k, _v} -> true + end) + |> Map.new() + end end diff --git a/lib/pleroma/mfa.ex b/lib/pleroma/mfa.ex index 01b730c76..ce30cd4ad 100644 --- a/lib/pleroma/mfa.ex +++ b/lib/pleroma/mfa.ex @@ -77,7 +77,7 @@ defmodule Pleroma.MFA do {:ok, codes} else {:error, msg} -> - %{error: msg} + {:error, msg} end end diff --git a/lib/pleroma/mfa/totp.ex b/lib/pleroma/mfa/totp.ex index 429c4b700..96fa8d71d 100644 --- a/lib/pleroma/mfa/totp.ex +++ b/lib/pleroma/mfa/totp.ex @@ -14,6 +14,7 @@ defmodule Pleroma.MFA.TOTP do @doc """ https://github.com/google/google-authenticator/wiki/Key-Uri-Format """ + @spec provisioning_uri(String.t(), String.t(), list()) :: String.t() def provisioning_uri(secret, label, opts \\ []) do query = %{ @@ -27,7 +28,7 @@ defmodule Pleroma.MFA.TOTP do |> URI.encode_query() %URI{scheme: "otpauth", host: "totp", path: "/" <> label, query: query} - |> URI.to_string() + |> to_string() end defp default_period, do: Config.get(@config_ns ++ [:period]) diff --git a/lib/pleroma/migrators/support/base_migrator.ex b/lib/pleroma/migrators/support/base_migrator.ex index ce88caac7..76a5d4590 100644 --- a/lib/pleroma/migrators/support/base_migrator.ex +++ b/lib/pleroma/migrators/support/base_migrator.ex @@ -188,10 +188,11 @@ defmodule Pleroma.Migrators.Support.BaseMigrator do end defp fault_rate do - with failures_count when is_integer(failures_count) <- failures_count() do + with failures_count when is_integer(failures_count) <- failures_count(), + true <- failures_count > 0 do failures_count / Enum.max([get_stat(:affected_count, 0), 1]) else - _ -> :error + _ -> 0 end end diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index 7203423e2..5c3ca58b0 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -121,7 +121,7 @@ defmodule Pleroma.ModerationLog do defp prepare_log_data(attrs), do: attrs - @spec insert_log(log_params()) :: {:ok, ModerationLog} | {:error, any} + @spec insert_log(log_params()) :: {:ok, ModerationLog.t()} | {:error, any} def insert_log(%{actor: %User{}, subject: subjects, permission: permission} = attrs) do data = attrs @@ -248,7 +248,8 @@ defmodule Pleroma.ModerationLog do |> insert_log_entry_with_message() end - @spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any} + @spec insert_log_entry_with_message(ModerationLog.t()) :: + {:ok, ModerationLog.t()} | {:error, any} defp insert_log_entry_with_message(entry) do entry.data["message"] |> put_in(get_log_entry_message(entry)) diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 48d467c59..368e609d2 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -88,7 +88,7 @@ defmodule Pleroma.Notification do where: q.seen == true, select: type(q.id, :string), limit: 1, - order_by: [desc: :id] + order_by: fragment("? desc nulls last", q.id) ) end diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index f2ba24abf..55b646b12 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -242,17 +242,17 @@ defmodule Pleroma.Object do {:ok, _} <- invalid_object_cache(object) do cleanup_attachments( Config.get([:instance, :cleanup_attachments]), - %{"object" => object} + object ) {:ok, object, deleted_activity} end end - @spec cleanup_attachments(boolean(), %{required(:object) => map()}) :: + @spec cleanup_attachments(boolean(), Object.t()) :: {:ok, Oban.Job.t() | nil} - def cleanup_attachments(true, %{"object" => _} = params) do - AttachmentsCleanupWorker.enqueue("cleanup_attachments", params) + def cleanup_attachments(true, %Object{} = object) do + AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{"object" => object}) end def cleanup_attachments(_, _), do: {:ok, nil} diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex index f12ca2819..8db732cc9 100644 --- a/lib/pleroma/pagination.ex +++ b/lib/pleroma/pagination.ex @@ -61,15 +61,16 @@ defmodule Pleroma.Pagination do |> Repo.all() end - @spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()] - def paginate(query, options, method \\ :keyset, table_binding \\ nil) - - def paginate(list, options, _method, _table_binding) when is_list(list) do + @spec paginate_list(list(), keyword()) :: list() + def paginate_list(list, options) do offset = options[:offset] || 0 limit = options[:limit] || 0 Enum.slice(list, offset, limit) end + @spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()] + def paginate(query, options, method \\ :keyset, table_binding \\ nil) + def paginate(query, options, :keyset, table_binding) do query |> restrict(:min_id, options, table_binding) diff --git a/lib/pleroma/password/pbkdf2.ex b/lib/pleroma/password/pbkdf2.ex index 92e9e1952..9c6d2e381 100644 --- a/lib/pleroma/password/pbkdf2.ex +++ b/lib/pleroma/password/pbkdf2.ex @@ -28,7 +28,7 @@ defmodule Pleroma.Password.Pbkdf2 do iterations = String.to_integer(iterations) - digest = String.to_atom(digest) + digest = String.to_existing_atom(digest) binary_hash = KeyGenerator.generate(password, salt, digest: digest, iterations: iterations, length: 64) diff --git a/lib/pleroma/reverse_proxy.ex b/lib/pleroma/reverse_proxy.ex index ec7732946..4d13e51fc 100644 --- a/lib/pleroma/reverse_proxy.ex +++ b/lib/pleroma/reverse_proxy.ex @@ -8,7 +8,7 @@ defmodule Pleroma.ReverseProxy do ~w(if-unmodified-since if-none-match) ++ @range_headers @resp_cache_headers ~w(etag date last-modified) @keep_resp_headers @resp_cache_headers ++ - ~w(content-length content-type content-disposition content-encoding) ++ + ~w(content-type content-disposition content-encoding) ++ ~w(content-range accept-ranges vary) @default_cache_control_header "public, max-age=1209600" @valid_resp_codes [200, 206, 304] @@ -84,13 +84,13 @@ defmodule Pleroma.ReverseProxy do {:max_read_duration, non_neg_integer() | :infinity} | {:max_body_length, non_neg_integer() | :infinity} | {:failed_request_ttl, non_neg_integer() | :infinity} - | {:http, []} + | {:http, keyword()} | {:req_headers, [{String.t(), String.t()}]} | {:resp_headers, [{String.t(), String.t()}]} - | {:inline_content_types, boolean() | [String.t()]} + | {:inline_content_types, boolean() | list(String.t())} | {:redirect_on_failure, boolean()} - @spec call(Plug.Conn.t(), url :: String.t(), [option()]) :: Plug.Conn.t() + @spec call(Plug.Conn.t(), String.t(), list(option())) :: Plug.Conn.t() def call(_conn, _url, _opts \\ []) def call(conn = %{method: method}, url, opts) when method in @methods do @@ -388,8 +388,6 @@ defmodule Pleroma.ReverseProxy do defp body_size_constraint(_, _), do: :ok - defp check_read_duration(nil = _duration, max), do: check_read_duration(@max_read_duration, max) - defp check_read_duration(duration, max) when is_integer(duration) and is_integer(max) and max > 0 do if duration > max do @@ -407,10 +405,6 @@ defmodule Pleroma.ReverseProxy do {:ok, previous_duration + duration} end - defp increase_read_duration(_) do - {:ok, :no_duration_limit, :no_duration_limit} - end - defp client, do: Pleroma.ReverseProxy.Client.Wrapper defp track_failed_url(url, error, opts) do diff --git a/lib/pleroma/search/search_backend.ex b/lib/pleroma/search/search_backend.ex index 46be84551..68bc48cec 100644 --- a/lib/pleroma/search/search_backend.ex +++ b/lib/pleroma/search/search_backend.ex @@ -20,5 +20,5 @@ defmodule Pleroma.Search.SearchBackend do is what contains the actual content and there is no need for filtering when removing from index. """ - @callback remove_from_index(object :: Pleroma.Object.t()) :: {:ok, any()} | {:error, any()} + @callback remove_from_index(object :: Pleroma.Object.t()) :: :ok | {:error, any()} end diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index 5cfdae051..8fd422a6e 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -27,7 +27,7 @@ defmodule Pleroma.Signature do _ -> case Pleroma.Web.WebFinger.finger(maybe_ap_id) do - %{"ap_id" => ap_id} -> {:ok, ap_id} + {:ok, %{"ap_id" => ap_id}} -> {:ok, ap_id} _ -> {:error, maybe_ap_id} end end diff --git a/lib/pleroma/telemetry/logger.ex b/lib/pleroma/telemetry/logger.ex index 92d395394..9998d8185 100644 --- a/lib/pleroma/telemetry/logger.ex +++ b/lib/pleroma/telemetry/logger.ex @@ -59,7 +59,7 @@ defmodule Pleroma.Telemetry.Logger do _, _ ) do - Logger.error(fn -> + Logger.debug(fn -> "Connection pool had to refuse opening a connection to #{key} due to connection limit exhaustion" end) end @@ -81,7 +81,7 @@ defmodule Pleroma.Telemetry.Logger do %{key: key, protocol: :http}, _ ) do - Logger.info(fn -> + Logger.debug(fn -> "Pool worker for #{key}: #{length(clients)} clients are using an HTTP1 connection at the same time, head-of-line blocking might occur." end) end diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 3c355e64d..e6c484548 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -51,6 +51,7 @@ defmodule Pleroma.Upload do | {:size_limit, nil | non_neg_integer()} | {:uploader, module()} | {:filters, [module()]} + | {:actor, String.t()} @type t :: %__MODULE__{ id: String.t(), @@ -175,7 +176,7 @@ defmodule Pleroma.Upload do defp prepare_upload(%{img: "data:image/" <> image_data}, opts) do parsed = Regex.named_captures(~r/(?jpeg|png|gif);base64,(?.*)/, image_data) data = Base.decode64!(parsed["data"], ignore: :whitespace) - hash = Base.encode16(:crypto.hash(:sha256, data), lower: true) + hash = Base.encode16(:crypto.hash(:sha256, data), case: :upper) with :ok <- check_binary_size(data, opts.size_limit), tmp_path <- tempfile_for_image(data), diff --git a/lib/pleroma/uploaders/uploader.ex b/lib/pleroma/uploaders/uploader.ex index 23caaff1a..3396fe06a 100644 --- a/lib/pleroma/uploaders/uploader.ex +++ b/lib/pleroma/uploaders/uploader.ex @@ -5,8 +5,6 @@ defmodule Pleroma.Uploaders.Uploader do import Pleroma.Web.Gettext - @mix_env Mix.env() - @moduledoc """ Defines the contract to put and get an uploaded file to any backend. """ @@ -75,10 +73,5 @@ defmodule Pleroma.Uploaders.Uploader do end end - defp callback_timeout do - case @mix_env do - :test -> 1_000 - _ -> 30_000 - end - end + defp callback_timeout, do: Application.get_env(:pleroma, __MODULE__)[:timeout] end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 89a95c435..0773434c5 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1787,7 +1787,10 @@ defmodule Pleroma.User do @spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()} def set_activation(users, status) when is_list(users) do Repo.transaction(fn -> - for user <- users, do: set_activation(user, status) + for user <- users do + {:ok, user} = set_activation(user, status) + user + end end) end @@ -2404,9 +2407,9 @@ defmodule Pleroma.User do defp put_password_hash(changeset), do: changeset - def is_internal_user?(%User{nickname: nil}), do: true - def is_internal_user?(%User{local: true, nickname: "internal." <> _}), do: true - def is_internal_user?(_), do: false + def internal?(%User{nickname: nil}), do: true + def internal?(%User{local: true, nickname: "internal." <> _}), do: true + def internal?(_), do: false # A hack because user delete activities have a fake id for whatever reason # TODO: Get rid of this @@ -2556,9 +2559,9 @@ defmodule Pleroma.User do cast(user, params, [:is_confirmed, :confirmation_token]) end - @spec approval_changeset(User.t(), keyword()) :: Ecto.Changeset.t() - def approval_changeset(user, set_approval: approved?) do - cast(user, %{is_approved: approved?}, [:is_approved]) + @spec approval_changeset(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t() + def approval_changeset(changeset, set_approval: approved?) do + cast(changeset, %{is_approved: approved?}, [:is_approved]) end @spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()} diff --git a/lib/pleroma/user/backup.ex b/lib/pleroma/user/backup.ex index 74e0ec073..b7f00bbf7 100644 --- a/lib/pleroma/user/backup.ex +++ b/lib/pleroma/user/backup.ex @@ -22,6 +22,8 @@ defmodule Pleroma.User.Backup do alias Pleroma.Web.ActivityPub.UserView alias Pleroma.Workers.BackupWorker + @type t :: %__MODULE__{} + schema "backups" do field(:content_type, :string) field(:file_name, :string) @@ -195,6 +197,7 @@ defmodule Pleroma.User.Backup do end @files ['actor.json', 'outbox.json', 'likes.json', 'bookmarks.json'] + @spec export(Pleroma.User.Backup.t(), pid()) :: {:ok, String.t()} | :error def export(%__MODULE__{} = backup, caller_pid) do backup = Repo.preload(backup, :user) dir = backup_tempdir(backup) @@ -204,9 +207,11 @@ defmodule Pleroma.User.Backup do :ok <- statuses(dir, backup.user, caller_pid), :ok <- likes(dir, backup.user, caller_pid), :ok <- bookmarks(dir, backup.user, caller_pid), - {:ok, zip_path} <- :zip.create(String.to_charlist(dir <> ".zip"), @files, cwd: dir), + {:ok, zip_path} <- :zip.create(backup.file_name, @files, cwd: dir), {:ok, _} <- File.rm_rf(dir) do - {:ok, to_string(zip_path)} + {:ok, zip_path} + else + _ -> :error end end @@ -382,6 +387,8 @@ defmodule Pleroma.User.Backup.Processor do [:file_size, :processed, :state] ) |> Repo.update() + else + e -> {:error, e} end end end diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index 874e8ec2b..cd9586452 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -71,7 +71,7 @@ defmodule Pleroma.User.Query do @equal_criteria [:email] @contains_criteria [:ap_id, :nickname] - @spec build(Query.t(), criteria()) :: Query.t() + @spec build(Ecto.Query.t(), criteria()) :: Ecto.Query.t() def build(query \\ base_query(), criteria) do prepare_query(query, criteria) end diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex index fbecf3129..82fcc1cdd 100644 --- a/lib/pleroma/user_relationship.ex +++ b/lib/pleroma/user_relationship.ex @@ -14,6 +14,8 @@ defmodule Pleroma.UserRelationship do alias Pleroma.User alias Pleroma.UserRelationship + @type t :: %__MODULE__{} + schema "user_relationships" do belongs_to(:source, User, type: FlakeId.Ecto.CompatType) belongs_to(:target, User, type: FlakeId.Ecto.CompatType) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index a12438f56..2017c696d 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -74,22 +74,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp check_remote_limit(_), do: true def increase_note_count_if_public(actor, object) do - if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor} + if public?(object), do: User.increase_note_count(actor), else: {:ok, actor} end def decrease_note_count_if_public(actor, object) do - if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor} + if public?(object), do: User.decrease_note_count(actor), else: {:ok, actor} end def update_last_status_at_if_public(actor, object) do - if is_public?(object), do: User.update_last_status_at(actor), else: {:ok, actor} + if public?(object), do: User.update_last_status_at(actor), else: {:ok, actor} end defp increase_replies_count_if_reply(%{ "object" => %{"inReplyTo" => reply_ap_id} = object, "type" => "Create" }) do - if is_public?(object) do + if public?(object) do Object.increase_replies_count(reply_ap_id) end end @@ -100,7 +100,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do "object" => %{"quoteUrl" => quote_ap_id} = object, "type" => "Create" }) do - if is_public?(object) do + if public?(object) do Object.increase_quotes_count(quote_ap_id) end end diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index eb0bb0e33..2a1e56278 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do This module encodes our addressing policies and general shape of our objects. """ + alias Pleroma.Activity alias Pleroma.Emoji alias Pleroma.Object alias Pleroma.User @@ -131,7 +132,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do def emoji_react(actor, object, emoji) do with {:ok, data, meta} <- object_action(actor, object) do data = - if Emoji.is_unicode_emoji?(emoji) do + if Emoji.unicode?(emoji) do unicode_emoji_react(object, data, emoji) else custom_emoji_react(object, data, emoji) @@ -347,7 +348,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do actor.ap_id == Relay.ap_id() -> [actor.follower_address] - public? and Visibility.is_local_public?(object) -> + public? and Visibility.local_public?(object) -> [actor.follower_address, object.data["actor"], Utils.as_local_public()] public? -> @@ -375,7 +376,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do # Address the actor of the object, and our actor's follower collection if the post is public. to = - if Visibility.is_public?(object) do + if Visibility.public?(object) do [actor.follower_address, object.data["actor"]] else [object.data["actor"]] diff --git a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex index d13d980cc..fdb9a9dba 100644 --- a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex @@ -84,7 +84,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do if hashtags != [] do with {:ok, message} <- check_reject(message, hashtags), {:ok, message} <- - (if "type" == "Create" do + (if type == "Create" do check_ftl_removal(message, hashtags) else {:ok, message} diff --git a/lib/pleroma/web/activity_pub/mrf/inline_quote_policy.ex b/lib/pleroma/web/activity_pub/mrf/inline_quote_policy.ex index 171b22c5e..b7a01c27c 100644 --- a/lib/pleroma/web/activity_pub/mrf/inline_quote_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/inline_quote_policy.ex @@ -62,7 +62,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do key: :mrf_inline_quote, related_policy: "Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy", label: "MRF Inline Quote Policy", - type: :group, description: "Force quote url to appear in post content.", children: [ %{ diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex index 874fe9ab9..729da4e9c 100644 --- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex @@ -10,15 +10,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do @moduledoc "Reject or Word-Replace messages with a keyword or regex" @behaviour Pleroma.Web.ActivityPub.MRF.Policy - defp string_matches?(string, _) when not is_binary(string) do - false - end defp string_matches?(string, pattern) when is_binary(pattern) do String.contains?(string, pattern) end - defp string_matches?(string, pattern) do + defp string_matches?(string, %Regex{} = pattern) do String.match?(string, pattern) end diff --git a/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex index 855cda3b9..12bf4ddd2 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex @@ -10,9 +10,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do @impl true def filter(%{"actor" => actor} = object) do - with true <- is_local?(actor), - true <- is_eligible_type?(object), - true <- is_note?(object), + with true <- local?(actor), + true <- eligible_type?(object), + true <- note?(object), false <- has_attachment?(object), true <- only_mentions?(object) do {:reject, "[NoEmptyPolicy]"} @@ -24,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do def filter(object), do: {:ok, object} - defp is_local?(actor) do + defp local?(actor) do if actor |> String.starts_with?("#{Endpoint.url()}") do true else @@ -59,11 +59,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do defp only_mentions?(_), do: false - defp is_note?(%{"object" => %{"type" => "Note"}}), do: true - defp is_note?(_), do: false + defp note?(%{"object" => %{"type" => "Note"}}), do: true + defp note?(_), do: false - defp is_eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true - defp is_eligible_type?(_), do: false + defp eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true + defp eligible_type?(_), do: false @impl true def describe, do: {:ok, %{}} diff --git a/lib/pleroma/web/activity_pub/mrf/quote_to_link_tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/quote_to_link_tag_policy.ex index f1c573d1b..ac353f03f 100644 --- a/lib/pleroma/web/activity_pub/mrf/quote_to_link_tag_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/quote_to_link_tag_policy.ex @@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.QuoteToLinkTagPolicy do tags = object["tag"] || [] if Enum.any?(tags, fn tag -> - CommonFixes.is_object_link_tag(tag) and tag["href"] == quote_url + CommonFixes.object_link_tag?(tag) and tag["href"] == quote_url end) do object else diff --git a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex index 237dfefa5..fa6b595ea 100644 --- a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex @@ -36,6 +36,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do extension = if extension == "", do: ".png", else: extension + shortcode = Path.basename(shortcode) file_path = Path.join(emoji_dir_path, shortcode <> extension) case File.write(file_path, response.body) do @@ -78,6 +79,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do new_emojis = foreign_emojis |> Enum.reject(fn {shortcode, _url} -> shortcode in installed_emoji end) + |> Enum.reject(fn {shortcode, _url} -> String.contains?(shortcode, ["/", "\\"]) end) |> Enum.filter(fn {shortcode, _url} -> reject_emoji? = [:mrf_steal_emoji, :rejected_shortcodes] diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 5e0d1aa8e..b3043b93a 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -173,6 +173,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do {:object_validation, e} -> e + + {:error, %Ecto.Changeset{} = e} -> + {:error, e} end end diff --git a/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex index c2c7ba1a8..d0218583e 100644 --- a/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex @@ -82,7 +82,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do object when is_binary(object) <- get_field(cng, :object), %User{} = actor <- User.get_cached_by_ap_id(actor), %Object{} = object <- Object.get_cached_by_ap_id(object), - false <- Visibility.is_public?(object) do + false <- Visibility.public?(object) do same_actor = object.data["actor"] == actor.ap_id recipients = get_field(cng, :to) ++ get_field(cng, :cc) local_public = Utils.as_local_public() diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex index 4d9be0bdd..4699029d4 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do alias Pleroma.EctoType.ActivityPub.ObjectValidators + alias Pleroma.Maps alias Pleroma.Object alias Pleroma.Object.Containment alias Pleroma.User @@ -24,6 +25,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do end def fix_object_defaults(data) do + data = Maps.filter_empty_values(data) + context = Utils.maybe_create_context( data["context"] || data["conversation"] || data["inReplyTo"] || data["id"] @@ -99,7 +102,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do end def fix_quote_url(%{"tag" => [_ | _] = tags} = data) do - tag = Enum.find(tags, &is_object_link_tag/1) + tag = Enum.find(tags, &object_link_tag?/1) if not is_nil(tag) do data @@ -112,7 +115,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do def fix_quote_url(data), do: data # https://codeberg.org/fediverse/fep/src/branch/main/fep/e232/fep-e232.md - def is_object_link_tag(%{ + def object_link_tag?(%{ "type" => "Link", "mediaType" => media_type, "href" => href @@ -121,5 +124,5 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do true end - def is_object_link_tag(_), do: false + def object_link_tag?(_), do: false end diff --git a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex index a0b82b325..65ba047e6 100644 --- a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex @@ -74,10 +74,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji) cond do - Pleroma.Emoji.is_unicode_emoji?(emoji) -> + Pleroma.Emoji.unicode?(emoji) -> data - Pleroma.Emoji.is_unicode_emoji?(new_emoji) -> + Pleroma.Emoji.unicode?(new_emoji) -> data |> Map.put("content", new_emoji) true -> @@ -90,7 +90,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do defp validate_emoji(cng) do content = get_field(cng, :content) - if Emoji.is_unicode_emoji?(content) || Emoji.is_custom_emoji?(content) do + if Emoji.unicode?(content) || Emoji.custom?(content) do cng else cng @@ -101,7 +101,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do defp maybe_validate_tag_presence(cng) do content = get_field(cng, :content) - if Emoji.is_unicode_emoji?(content) do + if Emoji.unicode?(content) do cng else tag = get_field(cng, :tag) diff --git a/lib/pleroma/web/activity_pub/pipeline.ex b/lib/pleroma/web/activity_pub/pipeline.ex index ca8653ab1..40184bd97 100644 --- a/lib/pleroma/web/activity_pub/pipeline.ex +++ b/lib/pleroma/web/activity_pub/pipeline.ex @@ -62,7 +62,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do with {:ok, local} <- Keyword.fetch(meta, :local) do do_not_federate = meta[:do_not_federate] || !config().get([:instance, :federating]) - if !do_not_federate and local and not Visibility.is_local_public?(activity) do + if !do_not_federate and local and not Visibility.local_public?(activity) do activity = if object = Keyword.get(meta, :object_data) do %{activity | data: Map.put(activity.data, "object", object)} diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 2449a3a3e..c27612697 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -66,7 +66,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do @doc """ Determine if an activity can be represented by running it through Transmogrifier. """ - def is_representable?(%Activity{} = activity) do + def representable?(%Activity{} = activity) do with {:ok, _data} <- Transmogrifier.prepare_outgoing(activity.data) do true else @@ -246,7 +246,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity) when is_list(bcc) and bcc != [] do - public = is_public?(activity) + public = public?(activity) {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) [priority_recipients, recipients] = recipients(actor, activity) @@ -291,7 +291,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do # Publishes an activity to all relevant peers. def publish(%User{} = actor, %Activity{} = activity) do - public = is_public?(activity) + public = public?(activity) if public && Config.get([:instance, :allow_relay]) do Logger.debug(fn -> "Relaying #{activity.data["id"]} out" end) diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index 2010351d1..91a647f29 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -58,7 +58,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do @spec publish(any()) :: {:ok, Activity.t()} | {:error, any()} def publish(%Activity{data: %{"type" => "Create"}} = activity) do with %User{} = user <- get_actor(), - true <- Visibility.is_public?(activity) do + true <- Visibility.public?(activity) do CommonAPI.repeat(activity.id, user) else error -> format_error(error) diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 59b19180d..5cb8a9700 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -258,7 +258,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do Utils.add_announce_to_object(object, announced_object) - if !User.is_internal_user?(user) do + if !User.internal?(user) do Notification.create_notifications(object) ap_streamer().stream_out(object) @@ -304,9 +304,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do result = case deleted_object do %Object{} -> - with {:ok, deleted_object, _activity} <- Object.delete(deleted_object), + with {_, {:ok, deleted_object, _activity}} <- {:object, Object.delete(deleted_object)}, {_, actor} when is_binary(actor) <- {:actor, deleted_object.data["actor"]}, - %User{} = user <- User.get_cached_by_ap_id(actor) do + {_, %User{} = user} <- {:user, User.get_cached_by_ap_id(actor)} do User.remove_pinned_object_id(user, deleted_object.data["id"]) {:ok, user} = ActivityPub.decrease_note_count_if_public(user, deleted_object) @@ -328,6 +328,17 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do {:actor, _} -> @logger.error("The object doesn't have an actor: #{inspect(deleted_object)}") :no_object_actor + + {:user, _} -> + @logger.error( + "The object's actor could not be resolved to a user: #{inspect(deleted_object)}" + ) + + :no_object_user + + {:object, _} -> + @logger.error("The object could not be deleted: #{inspect(deleted_object)}") + {:error, object} end %User{} -> @@ -569,7 +580,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do def handle_undoing(object), do: {:error, ["don't know how to handle", object]} - @spec delete_object(Object.t()) :: :ok | {:error, Ecto.Changeset.t()} + @spec delete_object(Activity.t()) :: :ok | {:error, Ecto.Changeset.t()} defp delete_object(object) do with {:ok, _} <- Repo.delete(object), do: :ok end diff --git a/lib/pleroma/web/activity_pub/side_effects/handling.ex b/lib/pleroma/web/activity_pub/side_effects/handling.ex index eb012f576..4751bb4ce 100644 --- a/lib/pleroma/web/activity_pub/side_effects/handling.ex +++ b/lib/pleroma/web/activity_pub/side_effects/handling.ex @@ -4,5 +4,5 @@ defmodule Pleroma.Web.ActivityPub.SideEffects.Handling do @callback handle(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} - @callback handle_after_transaction(map()) :: map() + @callback handle_after_transaction(keyword()) :: keyword() end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index a4cf395f2..edfe73a25 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -336,10 +336,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_tag(object), do: object - def fix_content_map(%{"contentMap" => nil} = object) do - Map.drop(object, ["contentMap"]) - end - # content map usually only has one language so this will do for now. def fix_content_map(%{"contentMap" => content_map} = object) do content_groups = Map.to_list(content_map) @@ -783,7 +779,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> Object.normalize(fetch: false) data = - if Visibility.is_private?(object) && object.data["actor"] == ap_id do + if Visibility.private?(object) && object.data["actor"] == ap_id do data |> Map.put("object", object |> Map.get(:data) |> prepare_object) else data |> maybe_fix_object_url diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index e2fc2640d..52cb64fc5 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -167,7 +167,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do with true <- Config.get!([:instance, :federating]), true <- type != "Block" || outgoing_blocks, - false <- Visibility.is_local_public?(activity) do + false <- Visibility.local_public?(activity) do Pleroma.Web.Federator.publish(activity) end @@ -277,7 +277,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do object_actor = User.get_cached_by_ap_id(object_actor_id) to = - if Visibility.is_public?(object) do + if Visibility.public?(object) do [actor.follower_address, object.data["actor"]] else [object.data["actor"]] @@ -776,10 +776,9 @@ defmodule Pleroma.Web.ActivityPub.Utils do build_flag_object(object) nil -> - if %Object{} = object = Object.get_by_ap_id(id) do - build_flag_object(object) - else - %{"id" => id, "deleted" => true} + case Object.get_by_ap_id(id) do + %Object{} = object -> build_flag_object(object) + _ -> %{"id" => id, "deleted" => true} end end end diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex index 7c57f88f9..97fc7fa1b 100644 --- a/lib/pleroma/web/activity_pub/visibility.ex +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -11,28 +11,28 @@ defmodule Pleroma.Web.ActivityPub.Visibility do require Pleroma.Constants - @spec is_public?(Object.t() | Activity.t() | map()) :: boolean() - def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false - def is_public?(%Object{data: data}), do: is_public?(data) - def is_public?(%Activity{data: %{"type" => "Move"}}), do: true - def is_public?(%Activity{data: data}), do: is_public?(data) - def is_public?(%{"directMessage" => true}), do: false + @spec public?(Object.t() | Activity.t() | map()) :: boolean() + def public?(%Object{data: %{"type" => "Tombstone"}}), do: false + def public?(%Object{data: data}), do: public?(data) + def public?(%Activity{data: %{"type" => "Move"}}), do: true + def public?(%Activity{data: data}), do: public?(data) + def public?(%{"directMessage" => true}), do: false - def is_public?(data) do + def public?(data) do Utils.label_in_message?(Pleroma.Constants.as_public(), data) or Utils.label_in_message?(Utils.as_local_public(), data) end - def is_local_public?(%Object{data: data}), do: is_local_public?(data) - def is_local_public?(%Activity{data: data}), do: is_local_public?(data) + def local_public?(%Object{data: data}), do: local_public?(data) + def local_public?(%Activity{data: data}), do: local_public?(data) - def is_local_public?(data) do + def local_public?(data) do Utils.label_in_message?(Utils.as_local_public(), data) and not Utils.label_in_message?(Pleroma.Constants.as_public(), data) end - def is_private?(activity) do - with false <- is_public?(activity), + def private?(activity) do + with false <- public?(activity), %User{follower_address: follower_address} <- User.get_cached_by_ap_id(activity.data["actor"]) do follower_address in activity.data["to"] @@ -41,20 +41,20 @@ defmodule Pleroma.Web.ActivityPub.Visibility do end end - def is_announceable?(activity, user, public \\ true) do - is_public?(activity) || - (!public && is_private?(activity) && activity.data["actor"] == user.ap_id) + def announceable?(activity, user, public \\ true) do + public?(activity) || + (!public && private?(activity) && activity.data["actor"] == user.ap_id) end - def is_direct?(%Activity{data: %{"directMessage" => true}}), do: true - def is_direct?(%Object{data: %{"directMessage" => true}}), do: true + def direct?(%Activity{data: %{"directMessage" => true}}), do: true + def direct?(%Object{data: %{"directMessage" => true}}), do: true - def is_direct?(activity) do - !is_public?(activity) && !is_private?(activity) + def direct?(activity) do + !public?(activity) && !private?(activity) end - def is_list?(%{data: %{"listMessage" => _}}), do: true - def is_list?(_), do: false + def list?(%{data: %{"listMessage" => _}}), do: true + def list?(_), do: false @spec visible_for_user?(Object.t() | Activity.t() | nil, User.t() | nil) :: boolean() def visible_for_user?(%Object{data: %{"type" => "Tombstone"}}, _), do: false @@ -77,7 +77,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do when module in [Activity, Object] do if restrict_unauthenticated_access?(message), do: false, - else: is_public?(message) and not is_local_public?(message) + else: public?(message) and not local_public?(message) end def visible_for_user?(%{__struct__: module} = message, user) @@ -86,8 +86,8 @@ defmodule Pleroma.Web.ActivityPub.Visibility do y = [message.data["actor"]] ++ message.data["to"] ++ (message.data["cc"] || []) user_is_local = user.local - federatable = not is_local_public?(message) - (is_public?(message) || Enum.any?(x, &(&1 in y))) and (user_is_local || federatable) + federatable = not local_public?(message) + (public?(message) || Enum.any?(x, &(&1 in y))) and (user_is_local || federatable) end def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do diff --git a/lib/pleroma/web/admin_api/controllers/config_controller.ex b/lib/pleroma/web/admin_api/controllers/config_controller.ex index a03318c0e..2c9c27294 100644 --- a/lib/pleroma/web/admin_api/controllers/config_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/config_controller.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do alias Pleroma.ConfigDB alias Pleroma.Web.Plugs.OAuthScopesPlug - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action == :update) plug( @@ -76,7 +76,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do json(conn, translate_descriptions(descriptions)) end - def show(conn, %{only_db: true}) do + def show(%{private: %{open_api_spex: %{params: %{only_db: true}}}} = conn, _) do with :ok <- configurable_from_database() do configs = Pleroma.Repo.all(ConfigDB) @@ -128,7 +128,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do end end - def update(%{body_params: %{configs: configs}} = conn, _) do + def update(%{private: %{open_api_spex: %{body_params: %{configs: configs}}}} = conn, _) do with :ok <- configurable_from_database() do results = configs diff --git a/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex b/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex index 990a94313..d76a95960 100644 --- a/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do alias Pleroma.Web.Plugs.InstanceStatic alias Pleroma.Web.Plugs.OAuthScopesPlug - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) action_fallback(Pleroma.Web.AdminAPI.FallbackController) @@ -18,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action == :show) plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action in [:update, :delete]) - def show(conn, %{name: document_name}) do + def show(%{private: %{open_api_spex: %{params: %{name: document_name}}}} = conn, _) do with {:ok, url} <- InstanceDocument.get(document_name), {:ok, content} <- File.read(InstanceStatic.file_path(url)) do conn @@ -27,13 +27,18 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do end end - def update(%{body_params: %{file: file}} = conn, %{name: document_name}) do + def update( + %{ + private: %{open_api_spex: %{body_params: %{file: file}, params: %{name: document_name}}} + } = conn, + _ + ) do with {:ok, url} <- InstanceDocument.put(document_name, file.path) do json(conn, %{"url" => url}) end end - def delete(conn, %{name: document_name}) do + def delete(%{private: %{open_api_spex: %{params: %{name: document_name}}}} = conn, _) do with :ok <- InstanceDocument.delete(document_name) do json(conn, %{}) end diff --git a/lib/pleroma/web/admin_api/controllers/invite_controller.ex b/lib/pleroma/web/admin_api/controllers/invite_controller.ex index c5d759bb5..7e3020f28 100644 --- a/lib/pleroma/web/admin_api/controllers/invite_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/invite_controller.ex @@ -13,7 +13,7 @@ defmodule Pleroma.Web.AdminAPI.InviteController do require Logger - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug(OAuthScopesPlug, %{scopes: ["admin:read:invites"]} when action == :index) plug( @@ -33,14 +33,14 @@ defmodule Pleroma.Web.AdminAPI.InviteController do end @doc "Create an account registration invite token" - def create(%{body_params: params} = conn, _) do + def create(%{private: %{open_api_spex: %{body_params: params}}} = conn, _) do {:ok, invite} = UserInviteToken.create_invite(params) render(conn, "show.json", invite: invite) end @doc "Revokes invite by token" - def revoke(%{body_params: %{token: token}} = conn, _) do + def revoke(%{private: %{open_api_spex: %{body_params: %{token: token}}}} = conn, _) do with {:ok, invite} <- UserInviteToken.find_by_token(token), {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do render(conn, "show.json", invite: updated_invite) @@ -51,7 +51,13 @@ defmodule Pleroma.Web.AdminAPI.InviteController do end @doc "Sends registration invite via email" - def email(%{assigns: %{user: user}, body_params: %{email: email} = params} = conn, _) do + def email( + %{ + assigns: %{user: user}, + private: %{open_api_spex: %{body_params: %{email: email} = params}} + } = conn, + _ + ) do with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])}, {_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])}, {:ok, invite_token} <- UserInviteToken.create_invite(), diff --git a/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex b/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex index 4d53f5451..8b43ea90f 100644 --- a/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex @@ -11,7 +11,7 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do @cachex Pleroma.Config.get([:cachex, :provider], Cachex) - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug( OAuthScopesPlug, @@ -27,7 +27,7 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do defdelegate open_api_operation(action), to: Spec.MediaProxyCacheOperation - def index(%{assigns: %{user: _}} = conn, params) do + def index(%{assigns: %{user: _}, private: %{open_api_spex: %{params: params}}} = conn, _) do entries = fetch_entries(params) urls = paginate_entries(entries, params.page, params.page_size) @@ -59,12 +59,19 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do Enum.slice(entries, offset, page_size) end - def delete(%{assigns: %{user: _}, body_params: %{urls: urls}} = conn, _) do + def delete( + %{assigns: %{user: _}, private: %{open_api_spex: %{body_params: %{urls: urls}}}} = conn, + _ + ) do MediaProxy.remove_from_banned_urls(urls) json(conn, %{}) end - def purge(%{assigns: %{user: _}, body_params: %{urls: urls, ban: ban}} = conn, _) do + def purge( + %{assigns: %{user: _}, private: %{open_api_spex: %{body_params: %{urls: urls, ban: ban}}}} = + conn, + _ + ) do MediaProxy.Invalidation.purge(urls) if ban do diff --git a/lib/pleroma/web/admin_api/controllers/relay_controller.ex b/lib/pleroma/web/admin_api/controllers/relay_controller.ex index 2e83fe139..1f36d3be5 100644 --- a/lib/pleroma/web/admin_api/controllers/relay_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/relay_controller.ex @@ -11,7 +11,7 @@ defmodule Pleroma.Web.AdminAPI.RelayController do require Logger - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug( OAuthScopesPlug, @@ -31,7 +31,13 @@ defmodule Pleroma.Web.AdminAPI.RelayController do end end - def follow(%{assigns: %{user: admin}, body_params: %{relay_url: target}} = conn, _) do + def follow( + %{ + assigns: %{user: admin}, + private: %{open_api_spex: %{body_params: %{relay_url: target}}} + } = conn, + _ + ) do with {:ok, _message} <- Relay.follow(target) do ModerationLog.insert_log(%{action: "relay_follow", actor: admin, target: target}) @@ -44,7 +50,13 @@ defmodule Pleroma.Web.AdminAPI.RelayController do end end - def unfollow(%{assigns: %{user: admin}, body_params: %{relay_url: target} = params} = conn, _) do + def unfollow( + %{ + assigns: %{user: admin}, + private: %{open_api_spex: %{body_params: %{relay_url: target} = params}} + } = conn, + _ + ) do with {:ok, _message} <- Relay.unfollow(target, %{force: params[:force]}) do ModerationLog.insert_log(%{action: "relay_unfollow", actor: admin, target: target}) diff --git a/lib/pleroma/web/admin_api/controllers/report_controller.ex b/lib/pleroma/web/admin_api/controllers/report_controller.ex index 15cbbcc3e..89d8cc820 100644 --- a/lib/pleroma/web/admin_api/controllers/report_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/report_controller.ex @@ -18,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.ReportController do require Logger - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug(OAuthScopesPlug, %{scopes: ["admin:read:reports"]} when action in [:index, :show]) plug( @@ -31,13 +31,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ReportOperation - def index(conn, params) do + def index(%{private: %{open_api_spex: %{params: params}}} = conn, _) do reports = Utils.get_reports(params, params.page, params.page_size) render(conn, "index.json", reports: reports) end - def show(conn, %{id: id}) do + def show(%{private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do with %Activity{} = report <- Activity.get_report(id) do render(conn, "show.json", Report.extract_report_info(report)) else @@ -45,7 +45,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do end end - def update(%{assigns: %{user: admin}, body_params: %{reports: reports}} = conn, _) do + def update( + %{ + assigns: %{user: admin}, + private: %{open_api_spex: %{body_params: %{reports: reports}}} + } = conn, + _ + ) do result = Enum.map(reports, fn report -> case CommonAPI.update_report_state(report.id, report.state) do @@ -73,9 +79,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do end end - def notes_create(%{assigns: %{user: user}, body_params: %{content: content}} = conn, %{ - id: report_id - }) do + def notes_create( + %{ + assigns: %{user: user}, + private: %{open_api_spex: %{body_params: %{content: content}, params: %{id: report_id}}} + } = conn, + _ + ) do with {:ok, _} <- ReportNote.create(user.id, report_id, content), report <- Activity.get_by_id_with_user_actor(report_id) do ModerationLog.insert_log(%{ @@ -92,10 +102,20 @@ defmodule Pleroma.Web.AdminAPI.ReportController do end end - def notes_delete(%{assigns: %{user: user}} = conn, %{ - id: note_id, - report_id: report_id - }) do + def notes_delete( + %{ + assigns: %{user: user}, + private: %{ + open_api_spex: %{ + params: %{ + id: note_id, + report_id: report_id + } + } + } + } = conn, + _ + ) do with {:ok, note} <- ReportNote.destroy(note_id), report <- Activity.get_by_id_with_user_actor(report_id) do ModerationLog.insert_log(%{ diff --git a/lib/pleroma/web/admin_api/controllers/user_controller.ex b/lib/pleroma/web/admin_api/controllers/user_controller.ex index 7b4ee46a4..9ac275396 100644 --- a/lib/pleroma/web/admin_api/controllers/user_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/user_controller.ex @@ -18,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.UserController do @users_page_size 50 - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug( OAuthScopesPlug, @@ -51,13 +51,22 @@ defmodule Pleroma.Web.AdminAPI.UserController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.UserOperation - def delete(conn, %{nickname: nickname}) do + def delete(%{private: %{open_api_spex: %{params: %{nickname: nickname}}}} = conn, _) do conn - |> Map.put(:body_params, %{nicknames: [nickname]}) - |> delete(%{}) + |> do_deletes([nickname]) end - def delete(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do + def delete( + %{ + private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}} + } = conn, + _ + ) do + conn + |> do_deletes(nicknames) + end + + defp do_deletes(%{assigns: %{user: admin}} = conn, nicknames) when is_list(nicknames) do users = Enum.map(nicknames, &User.get_cached_by_nickname/1) Enum.each(users, fn user -> @@ -77,9 +86,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do def follow( %{ assigns: %{user: admin}, - body_params: %{ - follower: follower_nick, - followed: followed_nick + private: %{ + open_api_spex: %{ + body_params: %{ + follower: follower_nick, + followed: followed_nick + } + } } } = conn, _ @@ -102,9 +115,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do def unfollow( %{ assigns: %{user: admin}, - body_params: %{ - follower: follower_nick, - followed: followed_nick + private: %{ + open_api_spex: %{ + body_params: %{ + follower: follower_nick, + followed: followed_nick + } + } } } = conn, _ @@ -124,7 +141,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do json(conn, "ok") end - def create(%{assigns: %{user: admin}, body_params: %{users: users}} = conn, _) do + def create( + %{ + assigns: %{user: admin}, + private: %{open_api_spex: %{body_params: %{users: users}}} + } = conn, + _ + ) do changesets = users |> Enum.map(fn %{nickname: nickname, email: email, password: password} -> @@ -178,7 +201,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do end end - def show(%{assigns: %{user: admin}} = conn, %{nickname: nickname}) do + def show( + %{ + assigns: %{user: admin}, + private: %{open_api_spex: %{params: %{nickname: nickname}}} + } = conn, + _ + ) do with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do render(conn, "show.json", %{user: user}) else @@ -186,7 +215,11 @@ defmodule Pleroma.Web.AdminAPI.UserController do end end - def toggle_activation(%{assigns: %{user: admin}} = conn, %{nickname: nickname}) do + def toggle_activation( + %{assigns: %{user: admin}, private: %{open_api_spex: %{params: %{nickname: nickname}}}} = + conn, + _ + ) do user = User.get_cached_by_nickname(nickname) {:ok, updated_user} = User.set_activation(user, !user.is_active) @@ -202,7 +235,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do render(conn, "show.json", user: updated_user) end - def activate(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do + def activate( + %{ + assigns: %{user: admin}, + private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}} + } = conn, + _ + ) do users = Enum.map(nicknames, &User.get_cached_by_nickname/1) {:ok, updated_users} = User.set_activation(users, true) @@ -212,10 +251,16 @@ defmodule Pleroma.Web.AdminAPI.UserController do action: "activate" }) - render(conn, "index.json", users: Keyword.values(updated_users)) + render(conn, "index.json", users: updated_users) end - def deactivate(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do + def deactivate( + %{ + assigns: %{user: admin}, + private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}} + } = conn, + _ + ) do users = Enum.map(nicknames, &User.get_cached_by_nickname/1) {:ok, updated_users} = User.set_activation(users, false) @@ -225,10 +270,16 @@ defmodule Pleroma.Web.AdminAPI.UserController do action: "deactivate" }) - render(conn, "index.json", users: Keyword.values(updated_users)) + render(conn, "index.json", users: updated_users) end - def approve(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do + def approve( + %{ + assigns: %{user: admin}, + private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}} + } = conn, + _ + ) do users = Enum.map(nicknames, &User.get_cached_by_nickname/1) {:ok, updated_users} = User.approve(users) @@ -241,7 +292,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do render(conn, "index.json", users: updated_users) end - def suggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do + def suggest( + %{ + assigns: %{user: admin}, + private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}} + } = conn, + _ + ) do users = Enum.map(nicknames, &User.get_cached_by_nickname/1) {:ok, updated_users} = User.set_suggestion(users, true) @@ -254,7 +311,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do render(conn, "index.json", users: updated_users) end - def unsuggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do + def unsuggest( + %{ + assigns: %{user: admin}, + private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}} + } = conn, + _ + ) do users = Enum.map(nicknames, &User.get_cached_by_nickname/1) {:ok, updated_users} = User.set_suggestion(users, false) @@ -267,7 +330,7 @@ defmodule Pleroma.Web.AdminAPI.UserController do render(conn, "index.json", users: updated_users) end - def index(conn, params) do + def index(%{private: %{open_api_spex: %{params: params}}} = conn, _) do {page, page_size} = page_params(params) filters = maybe_parse_filters(params[:filters]) diff --git a/lib/pleroma/web/api_spec/cast_and_validate.ex b/lib/pleroma/web/api_spec/cast_and_validate.ex index add59eb88..f3e8e093e 100644 --- a/lib/pleroma/web/api_spec/cast_and_validate.ex +++ b/lib/pleroma/web/api_spec/cast_and_validate.ex @@ -27,10 +27,12 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do @impl Plug - def call(conn, %{operation_id: operation_id, render_error: render_error}) do + def call(conn, %{operation_id: operation_id, render_error: render_error} = opts) do {spec, operation_lookup} = PutApiSpec.get_spec_and_operation_lookup(conn) operation = operation_lookup[operation_id] + cast_opts = opts |> Map.take([:replace_params]) |> Map.to_list() + content_type = case Conn.get_req_header(conn, "content-type") do [header_value | _] -> @@ -44,7 +46,7 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do conn = Conn.put_private(conn, :operation_id, operation_id) - case cast_and_validate(spec, operation, conn, content_type, strict?()) do + case cast_and_validate(spec, operation, conn, content_type, strict?(), cast_opts) do {:ok, conn} -> conn @@ -94,11 +96,11 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do def call(conn, opts), do: OpenApiSpex.Plug.CastAndValidate.call(conn, opts) - defp cast_and_validate(spec, operation, conn, content_type, true = _strict) do - OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) + defp cast_and_validate(spec, operation, conn, content_type, true = _strict, cast_opts) do + OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts) end - defp cast_and_validate(spec, operation, conn, content_type, false = _strict) do + defp cast_and_validate(spec, operation, conn, content_type, false = _strict, cast_opts) do case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do {:ok, conn} -> {:ok, conn} @@ -123,7 +125,7 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do end) conn = %Conn{conn | query_params: query_params} - OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) + OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts) end end diff --git a/lib/pleroma/web/api_spec/helpers.ex b/lib/pleroma/web/api_spec/helpers.ex index f20a9163d..7257253ba 100644 --- a/lib/pleroma/web/api_spec/helpers.ex +++ b/lib/pleroma/web/api_spec/helpers.ex @@ -62,7 +62,7 @@ defmodule Pleroma.Web.ApiSpec.Helpers do Operation.parameter( :with_relationships, :query, - BooleanLike, + BooleanLike.schema(), "Embed relationships into accounts. **If this parameter is not set account's `pleroma.relationship` is going to be `null`.**" ) end diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 75cea2184..36025e47a 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -122,22 +122,27 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do parameters: [ %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, - Operation.parameter(:pinned, :query, BooleanLike, "Include only pinned statuses"), + Operation.parameter( + :pinned, + :query, + BooleanLike.schema(), + "Include only pinned statuses" + ), Operation.parameter(:tagged, :query, :string, "With tag"), Operation.parameter( :only_media, :query, - BooleanLike, + BooleanLike.schema(), "Include only statuses with media attached" ), Operation.parameter( :with_muted, :query, - BooleanLike, + BooleanLike.schema(), "Include statuses from muted accounts." ), - Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"), - Operation.parameter(:exclude_replies, :query, BooleanLike, "Exclude replies"), + Operation.parameter(:exclude_reblogs, :query, BooleanLike.schema(), "Exclude reblogs"), + Operation.parameter(:exclude_replies, :query, BooleanLike.schema(), "Exclude replies"), Operation.parameter( :exclude_visibilities, :query, @@ -147,7 +152,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do Operation.parameter( :with_muted, :query, - BooleanLike, + BooleanLike.schema(), "Include reactions from muted accounts." ) ] ++ pagination_params(), diff --git a/lib/pleroma/web/api_spec/operations/admin/report_operation.ex b/lib/pleroma/web/api_spec/operations/admin/report_operation.ex index d7a74f665..fbb6896a9 100644 --- a/lib/pleroma/web/api_spec/operations/admin/report_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/report_operation.ex @@ -141,7 +141,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do end def id_param do - Operation.parameter(:id, :path, FlakeID, "Report ID", + Operation.parameter(:id, :path, FlakeID.schema(), "Report ID", example: "9umDrYheeY451cQnEe", required: true ) diff --git a/lib/pleroma/web/api_spec/operations/chat_operation.ex b/lib/pleroma/web/api_spec/operations/chat_operation.ex index cf6a055fc..f56e57a41 100644 --- a/lib/pleroma/web/api_spec/operations/chat_operation.ex +++ b/lib/pleroma/web/api_spec/operations/chat_operation.ex @@ -137,7 +137,12 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do "Deprecated due to no support for pagination. Using [/api/v2/pleroma/chats](#operation/ChatController.index2) instead is recommended.", operationId: "ChatController.index", parameters: [ - Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users") + Operation.parameter( + :with_muted, + :query, + BooleanLike.schema(), + "Include chats from muted users" + ) ], responses: %{ 200 => Operation.response("The chats of the user", "application/json", chats_response()) @@ -156,7 +161,12 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do summary: "Retrieve list of chats", operationId: "ChatController.index2", parameters: [ - Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users") + Operation.parameter( + :with_muted, + :query, + BooleanLike.schema(), + "Include chats from muted users" + ) | pagination_params() ], responses: %{ diff --git a/lib/pleroma/web/api_spec/operations/directory_operation.ex b/lib/pleroma/web/api_spec/operations/directory_operation.ex index 23fa84dff..2eca17664 100644 --- a/lib/pleroma/web/api_spec/operations/directory_operation.ex +++ b/lib/pleroma/web/api_spec/operations/directory_operation.ex @@ -29,7 +29,7 @@ defmodule Pleroma.Web.ApiSpec.DirectoryOperation do "Order by recent activity or account creation", required: nil ), - Operation.parameter(:local, :query, BooleanLike, "Include local users only") + Operation.parameter(:local, :query, BooleanLike.schema(), "Include local users only") ] ++ pagination_params(), responses: %{ 200 => diff --git a/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex b/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex index 74341d64f..8d6be89a7 100644 --- a/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex +++ b/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex @@ -21,7 +21,7 @@ defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do summary: "Get an object of emoji to account mappings with accounts that reacted to the post", parameters: [ - Operation.parameter(:id, :path, FlakeID, "Status ID", required: true), + Operation.parameter(:id, :path, FlakeID.schema(), "Status ID", required: true), Operation.parameter(:emoji, :path, :string, "Filter by a single unicode emoji", required: nil ), @@ -45,7 +45,7 @@ defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do tags: ["Emoji reactions"], summary: "React to a post with a unicode emoji", parameters: [ - Operation.parameter(:id, :path, FlakeID, "Status ID", required: true), + Operation.parameter(:id, :path, FlakeID.schema(), "Status ID", required: true), Operation.parameter(:emoji, :path, :string, "A single character unicode emoji", required: true ) @@ -64,7 +64,7 @@ defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do tags: ["Emoji reactions"], summary: "Remove a reaction to a post with a unicode emoji", parameters: [ - Operation.parameter(:id, :path, FlakeID, "Status ID", required: true), + Operation.parameter(:id, :path, FlakeID.schema(), "Status ID", required: true), Operation.parameter(:emoji, :path, :string, "A single character unicode emoji", required: true ) diff --git a/lib/pleroma/web/api_spec/operations/notification_operation.ex b/lib/pleroma/web/api_spec/operations/notification_operation.ex index 56aa129d2..757429d12 100644 --- a/lib/pleroma/web/api_spec/operations/notification_operation.ex +++ b/lib/pleroma/web/api_spec/operations/notification_operation.ex @@ -62,7 +62,7 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do Operation.parameter( :with_muted, :query, - BooleanLike, + BooleanLike.schema(), "Include the notifications from muted users" ) ] ++ pagination_params(), diff --git a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex index 5375c5b15..7340653fb 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex @@ -142,7 +142,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do end defp id_param do - Operation.parameter(:id, :path, FlakeID, "Account ID", + Operation.parameter(:id, :path, FlakeID.schema(), "Account ID", example: "9umDrYheeY451cQnEe", required: true ) diff --git a/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex index 6e69c5269..77c604952 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex @@ -37,7 +37,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaStatusOperation do end def id_param do - Operation.parameter(:id, :path, FlakeID, "Status ID", + Operation.parameter(:id, :path, FlakeID.schema(), "Status ID", example: "9umDrYheeY451cQnEe", required: true ) diff --git a/lib/pleroma/web/api_spec/operations/poll_operation.ex b/lib/pleroma/web/api_spec/operations/poll_operation.ex index efd784f03..6dd251743 100644 --- a/lib/pleroma/web/api_spec/operations/poll_operation.ex +++ b/lib/pleroma/web/api_spec/operations/poll_operation.ex @@ -47,7 +47,7 @@ defmodule Pleroma.Web.ApiSpec.PollOperation do end defp id_param do - Operation.parameter(:id, :path, FlakeID, "Poll ID", + Operation.parameter(:id, :path, FlakeID.schema(), "Poll ID", example: "123", required: true ) diff --git a/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex b/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex index 802d3b6dd..c7ed02ff3 100644 --- a/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex +++ b/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex @@ -88,7 +88,7 @@ defmodule Pleroma.Web.ApiSpec.ScheduledActivityOperation do end defp id_param do - Operation.parameter(:id, :path, FlakeID, "Poll ID", + Operation.parameter(:id, :path, FlakeID.schema(), "Poll ID", example: "123", required: true ) diff --git a/lib/pleroma/web/api_spec/operations/search_operation.ex b/lib/pleroma/web/api_spec/operations/search_operation.ex index 1a7e49be4..539743ba3 100644 --- a/lib/pleroma/web/api_spec/operations/search_operation.ex +++ b/lib/pleroma/web/api_spec/operations/search_operation.ex @@ -70,7 +70,7 @@ defmodule Pleroma.Web.ApiSpec.SearchOperation do Operation.parameter( :account_id, :query, - FlakeID, + FlakeID.schema(), "If provided, statuses returned will be authored only by this account" ), Operation.parameter( @@ -116,7 +116,7 @@ defmodule Pleroma.Web.ApiSpec.SearchOperation do Operation.parameter( :account_id, :query, - FlakeID, + FlakeID.schema(), "If provided, statuses returned will be authored only by this account" ), Operation.parameter( diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index f52372c18..ef4e34044 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -39,7 +39,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do Operation.parameter( :with_muted, :query, - BooleanLike, + BooleanLike.schema(), "Include reactions from muted acccounts." ) ], @@ -82,7 +82,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do Operation.parameter( :with_muted, :query, - BooleanLike, + BooleanLike.schema(), "Include reactions from muted acccounts." ) ], @@ -685,7 +685,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do end def id_param do - Operation.parameter(:id, :path, FlakeID, "Status ID", + Operation.parameter(:id, :path, FlakeID.schema(), "Status ID", example: "9umDrYheeY451cQnEe", required: true ) diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex index fbe3f763a..f55e59805 100644 --- a/lib/pleroma/web/api_spec/operations/timeline_operation.ex +++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex @@ -176,7 +176,12 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do end defp with_muted_param do - Operation.parameter(:with_muted, :query, BooleanLike, "Include activities by muted users") + Operation.parameter( + :with_muted, + :query, + BooleanLike.schema(), + "Include activities by muted users" + ) end defp exclude_visibilities_param do diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex index 87af0d526..724d873c0 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -146,13 +146,13 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do Operation.parameter( :block_from_strangers, :query, - BooleanLike, + BooleanLike.schema(), "blocks notifications from accounts you do not follow" ), Operation.parameter( :hide_notification_contents, :query, - BooleanLike, + BooleanLike.schema(), "removes the contents of a message from the push notification" ) ], diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index dfc0a625d..27e82ecc8 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -372,7 +372,7 @@ defmodule Pleroma.Web.CommonAPI do do: visibility in ~w(public unlisted) def public_announce?(object, _) do - Visibility.is_public?(object) + Visibility.public?(object) end def get_visibility(_, _, %Participation{}), do: {"direct", "direct"} @@ -500,12 +500,12 @@ defmodule Pleroma.Web.CommonAPI do end defp activity_is_public(activity) do - with false <- Visibility.is_public?(activity) do + with false <- Visibility.public?(activity) do {:error, :visibility_error} end end - @spec unpin(String.t(), User.t()) :: {:ok, User.t()} | {:error, term()} + @spec unpin(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, term()} def unpin(id, user) do with %Activity{} = activity <- create_activity_by_id(id), {:ok, unpin_data, _} <- Builder.unpin(user, activity.object), diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index 8910ad5b8..bc46a8a36 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -14,6 +14,8 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do import Pleroma.Web.Gettext import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1] + @type t :: %__MODULE__{} + defstruct valid?: true, errors: [], user: nil, diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index dcda3e0e8..52c08f00f 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -109,7 +109,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do def get_to_and_cc(%{visibility: "direct"} = draft) do # If the OP is a DM already, add the implicit actor. - if draft.in_reply_to && Visibility.is_direct?(draft.in_reply_to) do + if draft.in_reply_to && Visibility.direct?(draft.in_reply_to) do {Enum.uniq([draft.in_reply_to.data["actor"] | draft.mentions]), []} else {draft.mentions, []} diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index 0c7fc17f4..1caf0f7e6 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -20,7 +20,7 @@ defmodule Pleroma.Web.ControllerHelper do |> json(json) end - @spec fetch_integer_param(map(), String.t(), integer() | nil) :: integer() | nil + @spec fetch_integer_param(map(), String.t() | atom(), integer() | nil) :: integer() | nil def fetch_integer_param(params, name, default \\ nil) do params |> Map.get(name, default) @@ -53,10 +53,15 @@ defmodule Pleroma.Web.ControllerHelper do end end + # TODO: Only fetch the params from open_api_spex when everything is converted @id_keys Pagination.page_keys() -- ["limit", "order"] defp build_pagination_fields(conn, min_id, max_id, extra_params) do params = - conn.params + if Map.has_key?(conn.private, :open_api_spex) do + get_in(conn, [Access.key(:private), Access.key(:open_api_spex), Access.key(:params)]) + else + conn.params + end |> Map.drop(Map.keys(conn.path_params) |> Enum.map(&String.to_existing_atom/1)) |> Map.merge(extra_params) |> Map.drop(@id_keys) @@ -85,18 +90,15 @@ defmodule Pleroma.Web.ControllerHelper do end end - def assign_account_by_id(conn, _) do - case Pleroma.User.get_cached_by_id(conn.params.id) do + def assign_account_by_id(%{private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do + case Pleroma.User.get_cached_by_id(id) do %Pleroma.User{} = account -> assign(conn, :account, account) nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt() end end def try_render(conn, target, params) when is_binary(target) do - case render(conn, target, params) do - nil -> render_error(conn, :not_implemented, "Can't display this activity") - res -> res - end + render(conn, target, params) end def try_render(conn, _, _) do diff --git a/lib/pleroma/web/embed_controller.ex b/lib/pleroma/web/embed_controller.ex index 8b9f0a051..2ca4501a6 100644 --- a/lib/pleroma/web/embed_controller.ex +++ b/lib/pleroma/web/embed_controller.ex @@ -11,12 +11,10 @@ defmodule Pleroma.Web.EmbedController do alias Pleroma.Web.ActivityPub.Visibility - plug(:put_layout, :embed) - def show(conn, %{"id" => id}) do with %Activity{local: true} = activity <- Activity.get_by_id_with_object(id), - true <- Visibility.is_public?(activity.object) do + true <- Visibility.public?(activity.object) do {:ok, author} = User.get_or_fetch(activity.object.data["actor"]) conn diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index 307fa069e..2e2104904 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -9,6 +9,16 @@ defmodule Pleroma.Web.Endpoint do alias Pleroma.Config + socket("/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, + longpoll: false, + websocket: [ + path: "/", + compress: false, + error_handler: {Pleroma.Web.MastodonAPI.WebsocketHandler, :handle_error, []}, + fullsweep_after: 20 + ] + ) + socket("/socket", Pleroma.Web.UserSocket, websocket: [ path: "/websocket", @@ -18,7 +28,8 @@ defmodule Pleroma.Web.Endpoint do ], timeout: 60_000, transport_log: false, - compress: false + compress: false, + fullsweep_after: 20 ], longpoll: false ) @@ -32,7 +43,8 @@ defmodule Pleroma.Web.Endpoint do plug(Pleroma.Web.Plugs.HTTPSecurityPlug) plug(Pleroma.Web.Plugs.UploadedMedia) - @static_cache_control "public, no-cache" + @static_cache_control "public, max-age=1209600" + @static_cache_disabled "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 @@ -43,22 +55,32 @@ defmodule Pleroma.Web.Endpoint do from: :pleroma, only: ["emoji", "images"], gzip: true, - cache_control_for_etags: "public, max-age=1209600", - headers: %{ - "cache-control" => "public, max-age=1209600" - } - ) - - plug(Pleroma.Web.Plugs.InstanceStatic, - at: "/", - gzip: true, cache_control_for_etags: @static_cache_control, headers: %{ "cache-control" => @static_cache_control } ) - # Careful! No `only` restriction here, as we don't know what frontends contain. + plug(Pleroma.Web.Plugs.InstanceStatic, + at: "/", + gzip: true, + cache_control_for_etags: @static_cache_disabled, + headers: %{ + "cache-control" => @static_cache_disabled + } + ) + + plug(Pleroma.Web.Plugs.FrontendStatic, + at: "/", + frontend_type: :primary, + only: ["index.html"], + gzip: true, + cache_control_for_etags: @static_cache_disabled, + headers: %{ + "cache-control" => @static_cache_disabled + } + ) + plug(Pleroma.Web.Plugs.FrontendStatic, at: "/", frontend_type: :primary, @@ -75,9 +97,9 @@ defmodule Pleroma.Web.Endpoint do at: "/pleroma/admin", frontend_type: :admin, gzip: true, - cache_control_for_etags: @static_cache_control, + cache_control_for_etags: @static_cache_disabled, headers: %{ - "cache-control" => @static_cache_control + "cache-control" => @static_cache_disabled } ) @@ -92,9 +114,9 @@ defmodule Pleroma.Web.Endpoint do only: Pleroma.Constants.static_only_files(), # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength gzip: true, - cache_control_for_etags: @static_cache_control, + cache_control_for_etags: @static_cache_disabled, headers: %{ - "cache-control" => @static_cache_control + "cache-control" => @static_cache_disabled } ) diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index 5ef49d841..1fa3f9768 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -85,12 +85,12 @@ defmodule Pleroma.Web.Gettext do Process.get({Pleroma.Web.Gettext, :locales}, []) end - def is_locale_list(locales) do + def locale_list?(locales) do Enum.all?(locales, &is_binary/1) end def put_locales(locales) do - if is_locale_list(locales) do + if locale_list?(locales) do Process.put({Pleroma.Web.Gettext, :locales}, Enum.uniq(locales)) Gettext.put_locale(Enum.at(locales, 0, Gettext.get_locale())) :ok diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 1b5de4b45..9226a2deb 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -30,7 +30,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do alias Pleroma.Web.TwitterAPI.TwitterAPI alias Pleroma.Web.Utils.Params - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug(:skip_auth when action in [:create, :lookup]) @@ -92,7 +92,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( RateLimiter, - [name: :relation_id_action, params: [:id, :uri]] when action in @relationship_actions + [name: :relation_id_action, params: ["id", "uri"]] when action in @relationship_actions ) plug(RateLimiter, [name: :relations_actions] when action in @relationship_actions) @@ -104,7 +104,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AccountOperation @doc "POST /api/v1/accounts" - def create(%{assigns: %{app: app}, body_params: params} = conn, _params) do + def create( + %{assigns: %{app: app}, private: %{open_api_spex: %{body_params: params}}} = conn, + _params + ) do with :ok <- validate_email_param(params), :ok <- TwitterAPI.validate_captcha(app, params), {:ok, user} <- TwitterAPI.register_user(params), @@ -168,7 +171,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "PATCH /api/v1/accounts/update_credentials" - def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _params) do + def update_credentials( + %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: params}}} = conn, + _params + ) do params = params |> Enum.filter(fn {_, value} -> not is_nil(value) end) @@ -289,7 +295,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "GET /api/v1/accounts/relationships" - def relationships(%{assigns: %{user: user}} = conn, %{id: id}) do + def relationships( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, + _ + ) do targets = User.get_all_by_ids(List.wrap(id)) render(conn, "relationships.json", user: user, targets: targets) @@ -299,7 +308,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, []) @doc "GET /api/v1/accounts/:id" - def show(%{assigns: %{user: for_user}} = conn, %{id: nickname_or_id} = params) do + def show( + %{ + assigns: %{user: for_user}, + private: %{open_api_spex: %{params: %{id: nickname_or_id} = params}} + } = conn, + _params + ) do with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user), :visible <- User.visible_for(user, for_user) do render(conn, "show.json", @@ -313,7 +328,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "GET /api/v1/accounts/:id/statuses" - def statuses(%{assigns: %{user: reading_user}} = conn, params) do + def statuses( + %{assigns: %{user: reading_user}, private: %{open_api_spex: %{params: params}}} = conn, + _params + ) do with %User{} = user <- User.get_cached_by_nickname_or_id(params.id, for: reading_user), :visible <- User.visible_for(user, reading_user) do params = @@ -348,7 +366,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "GET /api/v1/accounts/:id/followers" - def followers(%{assigns: %{user: for_user, account: user}} = conn, params) do + def followers( + %{assigns: %{user: for_user, account: user}, private: %{open_api_spex: %{params: params}}} = + conn, + _params + ) do params = params |> Enum.map(fn {key, value} -> {to_string(key), value} end) @@ -373,7 +395,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "GET /api/v1/accounts/:id/following" - def following(%{assigns: %{user: for_user, account: user}} = conn, params) do + def following( + %{assigns: %{user: for_user, account: user}, private: %{open_api_spex: %{params: params}}} = + conn, + _params + ) do params = params |> Enum.map(fn {key, value} -> {to_string(key), value} end) @@ -411,7 +437,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do {:error, "Can not follow yourself"} end - def follow(%{body_params: params, assigns: %{user: follower, account: followed}} = conn, _) do + def follow( + %{ + assigns: %{user: follower, account: followed}, + private: %{open_api_spex: %{body_params: params}} + } = conn, + _ + ) do with {:ok, follower} <- MastodonAPI.follow(follower, followed, params) do render(conn, "relationship.json", user: follower, target: followed) else @@ -431,7 +463,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "POST /api/v1/accounts/:id/mute" - def mute(%{assigns: %{user: muter, account: muted}, body_params: params} = conn, _params) do + def mute( + %{ + assigns: %{user: muter, account: muted}, + private: %{open_api_spex: %{body_params: params}} + } = conn, + _params + ) do params = params |> Map.put_new(:duration, Map.get(params, :expires_in, 0)) @@ -472,7 +510,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/note" def note( - %{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn, + %{ + assigns: %{user: noter, account: target}, + private: %{open_api_spex: %{body_params: %{comment: comment}}} + } = conn, _params ) do with {:ok, _user_note} <- UserNote.create(noter, target, comment) do @@ -513,7 +554,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "POST /api/v1/follows" - def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do + def follow_by_uri(%{private: %{open_api_spex: %{body_params: %{uri: uri}}}} = conn, _) do case User.get_cached_by_nickname(uri) do %User{} = user -> conn @@ -561,7 +602,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "GET /api/v1/accounts/lookup" - def lookup(conn, %{acct: nickname} = _params) do + def lookup(%{private: %{open_api_spex: %{params: %{acct: nickname}}}} = conn, _params) do with %User{} = user <- User.get_by_nickname(nickname) do render(conn, "show.json", user: user, diff --git a/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex b/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex index 253f06cfb..f89425966 100644 --- a/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex @@ -15,7 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.DirectoryController do plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(:skip_auth when action == "index") + plug(:skip_auth when action == :index) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DirectoryOperation diff --git a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex index b2e347ed9..4615794a1 100644 --- a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex @@ -8,7 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do alias Pleroma.User alias Pleroma.Web.Plugs.OAuthScopesPlug - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DomainBlockOperation plug( @@ -27,23 +27,31 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do end @doc "POST /api/v1/domain_blocks" - def create(%{assigns: %{user: blocker}, body_params: %{domain: domain}} = conn, _params) do + def create( + %{assigns: %{user: blocker}, private: %{open_api_spex: %{body_params: %{domain: domain}}}} = + conn, + _params + ) do User.block_domain(blocker, domain) json(conn, %{}) end - def create(%{assigns: %{user: blocker}} = conn, %{domain: domain}) do + def create(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do User.block_domain(blocker, domain) json(conn, %{}) end @doc "DELETE /api/v1/domain_blocks" - def delete(%{assigns: %{user: blocker}, body_params: %{domain: domain}} = conn, _params) do + def delete( + %{assigns: %{user: blocker}, private: %{open_api_spex: %{body_params: %{domain: domain}}}} = + conn, + _params + ) do User.unblock_domain(blocker, domain) json(conn, %{}) end - def delete(%{assigns: %{user: blocker}} = conn, %{domain: domain}) do + def delete(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do User.unblock_domain(blocker, domain) json(conn, %{}) end diff --git a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex index ba6d074cc..6eee55d1b 100644 --- a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do alias Pleroma.Web.CommonAPI alias Pleroma.Web.Plugs.OAuthScopesPlug - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug(:assign_follower when action != :index) action_fallback(:errors) @@ -44,7 +44,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do end end - defp assign_follower(%{params: %{id: id}} = conn, _) do + defp assign_follower(%{private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do case User.get_cached_by_id(id) do %User{} = follower -> assign(conn, :follower, follower) nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt() diff --git a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex index 2117aae3a..3bfc365a5 100644 --- a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex @@ -11,7 +11,7 @@ defmodule Pleroma.Web.MastodonAPI.ListController do @oauth_read_actions [:index, :show, :list_accounts] - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug(:list_by_id_and_user when action not in [:index, :create]) plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in @oauth_read_actions) plug(OAuthScopesPlug, %{scopes: ["write:lists"]} when action not in @oauth_read_actions) @@ -21,25 +21,33 @@ defmodule Pleroma.Web.MastodonAPI.ListController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ListOperation # GET /api/v1/lists - def index(%{assigns: %{user: user}} = conn, opts) do - lists = Pleroma.List.for_user(user, opts) + def index(%{assigns: %{user: user}, private: %{open_api_spex: %{params: params}}} = conn, _) do + lists = Pleroma.List.for_user(user, params) render(conn, "index.json", lists: lists) end # POST /api/v1/lists - def create(%{assigns: %{user: user}, body_params: %{title: title}} = conn, _) do + def create( + %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: %{title: title}}}} = + conn, + _ + ) do with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(title, user) do render(conn, "show.json", list: list) end end - # GET /api/v1/lists/:id + # GET /api/v1/lists/:idOB def show(%{assigns: %{list: list}} = conn, _) do render(conn, "show.json", list: list) end # PUT /api/v1/lists/:id - def update(%{assigns: %{list: list}, body_params: %{title: title}} = conn, _) do + def update( + %{assigns: %{list: list}, private: %{open_api_spex: %{body_params: %{title: title}}}} = + conn, + _ + ) do with {:ok, list} <- Pleroma.List.rename(list, title) do render(conn, "show.json", list: list) end @@ -62,7 +70,13 @@ defmodule Pleroma.Web.MastodonAPI.ListController do end # POST /api/v1/lists/:id/accounts - def add_to_list(%{assigns: %{list: list}, body_params: %{account_ids: account_ids}} = conn, _) do + def add_to_list( + %{ + assigns: %{list: list}, + private: %{open_api_spex: %{body_params: %{account_ids: account_ids}}} + } = conn, + _ + ) do Enum.each(account_ids, fn account_id -> with %User{} = followed <- User.get_cached_by_id(account_id) do Pleroma.List.follow(list, followed) @@ -74,9 +88,22 @@ defmodule Pleroma.Web.MastodonAPI.ListController do # DELETE /api/v1/lists/:id/accounts def remove_from_list( - %{assigns: %{list: list}, params: %{account_ids: account_ids}} = conn, + %{ + private: %{open_api_spex: %{params: %{account_ids: account_ids}}} + } = conn, _ ) do + do_remove_from_list(conn, account_ids) + end + + def remove_from_list( + %{private: %{open_api_spex: %{body_params: %{account_ids: account_ids}}}} = conn, + _ + ) do + do_remove_from_list(conn, account_ids) + end + + defp do_remove_from_list(%{assigns: %{list: list}} = conn, account_ids) do Enum.each(account_ids, fn account_id -> with %User{} = followed <- User.get_cached_by_id(account_id) do Pleroma.List.unfollow(list, followed) @@ -86,11 +113,10 @@ defmodule Pleroma.Web.MastodonAPI.ListController do json(conn, %{}) end - def remove_from_list(%{body_params: params} = conn, _) do - remove_from_list(%{conn | params: params}, %{}) - end - - defp list_by_id_and_user(%{assigns: %{user: user}, params: %{id: id}} = conn, _) do + defp list_by_id_and_user( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, + _ + ) do case Pleroma.List.get(id, user) do %Pleroma.List{} = list -> assign(conn, :list, list) nil -> conn |> render_error(:not_found, "List not found") |> halt() diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex index 7d9a63cf4..056bad844 100644 --- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex @@ -12,7 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(Majic.Plug, [pool: Pleroma.MajicPool] when action in [:create, :create2]) - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug(OAuthScopesPlug, %{scopes: ["read:media"]} when action == :show) plug(OAuthScopesPlug, %{scopes: ["write:media"]} when action != :show) @@ -20,7 +20,11 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.MediaOperation @doc "POST /api/v1/media" - def create(%{assigns: %{user: user}, body_params: %{file: file} = data} = conn, _) do + def create( + %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: %{file: file} = data}}} = + conn, + _ + ) do with {:ok, object} <- ActivityPub.upload( file, @@ -36,7 +40,11 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do def create(_conn, _data), do: {:error, :bad_request} @doc "POST /api/v2/media" - def create2(%{assigns: %{user: user}, body_params: %{file: file} = data} = conn, _) do + def create2( + %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: %{file: file} = data}}} = + conn, + _ + ) do with {:ok, object} <- ActivityPub.upload( file, @@ -54,7 +62,15 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do def create2(_conn, _data), do: {:error, :bad_request} @doc "PUT /api/v1/media/:id" - def update(%{assigns: %{user: user}, body_params: %{description: description}} = conn, %{id: id}) do + def update( + %{ + assigns: %{user: user}, + private: %{ + open_api_spex: %{body_params: %{description: description}, params: %{id: id}} + } + } = conn, + _ + ) do with %Object{} = object <- Object.get_by_id(id), :ok <- Object.authorize_access(object, user), {:ok, %Object{data: data}} <- Object.update_data(object, %{"name" => description}) do @@ -67,7 +83,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do def update(conn, data), do: show(conn, data) @doc "GET /api/v1/media/:id" - def show(%{assigns: %{user: user}} = conn, %{id: id}) do + def show(%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do with %Object{data: data, id: object_id} = object <- Object.get_by_id(id), :ok <- Object.authorize_access(object, user) do attachment_data = Map.put(data, "id", object_id) diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index a490e8319..e305aea94 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -13,7 +13,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do @oauth_read_actions [:show, :index] - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug( OAuthScopesPlug, @@ -24,8 +24,20 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.NotificationOperation + @default_notification_types ~w{ + mention + follow + follow_request + reblog + favourite + move + pleroma:emoji_reaction + poll + update + } + # GET /api/v1/notifications - def index(conn, %{account_id: account_id} = params) do + def index(%{private: %{open_api_spex: %{params: %{account_id: account_id} = params}}} = conn, _) do case Pleroma.User.get_cached_by_id(account_id) do %{ap_id: account_ap_id} -> params = @@ -33,7 +45,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do |> Map.delete(:account_id) |> Map.put(:account_ap_id, account_ap_id) - index(conn, params) + do_get_notifications(conn, params) _ -> conn @@ -42,18 +54,11 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do end end - @default_notification_types ~w{ - mention - follow - follow_request - reblog - favourite - move - pleroma:emoji_reaction - poll - update - } - def index(%{assigns: %{user: user}} = conn, params) do + def index(%{private: %{open_api_spex: %{params: params}}} = conn, _) do + do_get_notifications(conn, params) + end + + defp do_get_notifications(%{assigns: %{user: user}} = conn, params) do params = Map.new(params, fn {k, v} -> {to_string(k), v} end) |> Map.put_new("types", Map.get(params, :include_types, @default_notification_types)) @@ -69,7 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do end # GET /api/v1/notifications/:id - def show(%{assigns: %{user: user}} = conn, %{id: id}) do + def show(%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do with {:ok, notification} <- Notification.get(user, id) do render(conn, "show.json", notification: notification, for: user) else @@ -88,8 +93,20 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do # POST /api/v1/notifications/:id/dismiss - def dismiss(%{assigns: %{user: user}} = conn, %{id: id} = _params) do - with {:ok, _notif} <- Notification.dismiss(user, id) do + def dismiss(%{private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do + do_dismiss(conn, id) + end + + # POST /api/v1/notifications/dismiss (deprecated) + def dismiss_via_body( + %{private: %{open_api_spex: %{body_params: %{id: id}}}} = conn, + _ + ) do + do_dismiss(conn, id) + end + + defp do_dismiss(%{assigns: %{user: user}} = conn, notification_id) do + with {:ok, _notif} <- Notification.dismiss(user, notification_id) do json(conn, %{}) else {:error, reason} -> @@ -99,13 +116,11 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do end end - # POST /api/v1/notifications/dismiss (deprecated) - def dismiss_via_body(%{body_params: params} = conn, _) do - dismiss(conn, params) - end - # DELETE /api/v1/notifications/destroy_multiple - def destroy_multiple(%{assigns: %{user: user}} = conn, %{ids: ids} = _params) do + def destroy_multiple( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{ids: ids}}}} = conn, + _ + ) do Notification.destroy_multiple(user, ids) json(conn, %{}) end diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index 002c210d2..b074ee405 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -15,7 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.PollController do action_fallback(Pleroma.Web.MastodonAPI.FallbackController) - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug( OAuthScopesPlug, @@ -29,7 +29,7 @@ defmodule Pleroma.Web.MastodonAPI.PollController do @cachex Pleroma.Config.get([:cachex, :provider], Cachex) @doc "GET /api/v1/polls/:id" - def show(%{assigns: %{user: user}} = conn, %{id: id}) do + def show(%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60), %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), true <- Visibility.visible_for_user?(activity, user) do @@ -41,7 +41,13 @@ defmodule Pleroma.Web.MastodonAPI.PollController do end @doc "POST /api/v1/polls/:id/votes" - def vote(%{assigns: %{user: user}, body_params: %{choices: choices}} = conn, %{id: id}) do + def vote( + %{ + assigns: %{user: user}, + private: %{open_api_spex: %{body_params: %{choices: choices}, params: %{id: id}}} + } = conn, + _ + ) do with %Object{data: %{"type" => "Question"}} = object <- Object.get_by_id(id), %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), true <- Visibility.visible_for_user?(activity, user), diff --git a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex index 0392fcef1..1b7095ec5 100644 --- a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex @@ -13,7 +13,7 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do @oauth_read_actions [:show, :index] - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in @oauth_read_actions) plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action not in @oauth_read_actions) plug(:assign_scheduled_activity when action != :index) @@ -23,7 +23,7 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ScheduledActivityOperation @doc "GET /api/v1/scheduled_statuses" - def index(%{assigns: %{user: user}} = conn, params) do + def index(%{assigns: %{user: user}, private: %{open_api_spex: %{params: params}}} = conn, _) do params = Map.new(params, fn {key, value} -> {to_string(key), value} end) with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do @@ -39,7 +39,13 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do end @doc "PUT /api/v1/scheduled_statuses/:id" - def update(%{assigns: %{scheduled_activity: scheduled_activity}, body_params: params} = conn, _) do + def update( + %{ + assigns: %{scheduled_activity: scheduled_activity}, + private: %{open_api_spex: %{body_params: params}} + } = conn, + _ + ) do with {:ok, scheduled_activity} <- ScheduledActivity.update(scheduled_activity, params) do render(conn, "show.json", scheduled_activity: scheduled_activity) end @@ -52,7 +58,10 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do end end - defp assign_scheduled_activity(%{assigns: %{user: user}, params: %{id: id}} = conn, _) do + defp assign_scheduled_activity( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, + _ + ) do case ScheduledActivity.get(user, id) do %ScheduledActivity{} = activity -> assign(conn, :scheduled_activity, activity) nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt() diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index e4acba226..628aa311b 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -18,7 +18,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do @search_limit 40 - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) # Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search) plug(OAuthScopesPlug, %{scopes: ["read:search"], fallback: :proceed_unauthenticated}) @@ -29,7 +29,11 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.SearchOperation - def account_search(%{assigns: %{user: user}} = conn, %{q: query} = params) do + def account_search( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{q: query} = params}}} = + conn, + _ + ) do accounts = User.search(query, search_options(params, user)) conn @@ -44,7 +48,12 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do def search2(conn, params), do: do_search(:v2, conn, params) def search(conn, params), do: do_search(:v1, conn, params) - defp do_search(version, %{assigns: %{user: user}} = conn, %{q: query} = params) do + defp do_search( + version, + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{q: query} = params}}} = + conn, + _ + ) do query = String.trim(query) options = search_options(params, user) timeout = Keyword.get(Repo.config(), :timeout, 15_000) @@ -147,7 +156,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do tags end - Pleroma.Pagination.paginate(tags, options) + Pleroma.Pagination.paginate_list(tags, options) end defp add_joined_tag(tags) do diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index e594ea491..5aa7bddf0 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -25,7 +25,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.RateLimiter - plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false) plug(:skip_public_check when action in [:index, :show]) @@ -110,7 +110,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do `ids` query param is required """ - def index(%{assigns: %{user: user}} = conn, %{ids: ids} = params) do + def index( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{ids: ids} = params}}} = + conn, + _ + ) do limit = 100 activities = @@ -134,7 +138,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do def create( %{ assigns: %{user: user}, - body_params: %{status: _, scheduled_at: scheduled_at} = params + private: %{ + open_api_spex: %{body_params: %{status: _, scheduled_at: scheduled_at} = params} + } } = conn, _ ) @@ -156,7 +162,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do else {:far_enough, _} -> params = Map.drop(params, [:scheduled_at]) - create(%Plug.Conn{conn | body_params: params}, %{}) + + put_in( + conn, + [Access.key(:private), Access.key(:open_api_spex), Access.key(:body_params)], + params + ) + |> do_create error -> error @@ -164,7 +176,35 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end # Creates a regular status - def create(%{assigns: %{user: user}, body_params: %{status: _} = params} = conn, _) do + def create( + %{ + private: %{open_api_spex: %{body_params: %{status: _}}} + } = conn, + _ + ) do + do_create(conn) + end + + def create( + %{ + assigns: %{user: _user}, + private: %{open_api_spex: %{body_params: %{media_ids: _} = params}} + } = conn, + _ + ) do + params = Map.put(params, :status, "") + + put_in( + conn, + [Access.key(:private), Access.key(:open_api_spex), Access.key(:body_params)], + params + ) + |> do_create + end + + defp do_create( + %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: params}}} = conn + ) do params = Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id]) |> put_application(conn) @@ -189,13 +229,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end end - def create(%{assigns: %{user: _user}, body_params: %{media_ids: _} = params} = conn, _) do - params = Map.put(params, :status, "") - create(%Plug.Conn{conn | body_params: params}, %{}) - end - @doc "GET /api/v1/statuses/:id/history" - def show_history(%{assigns: assigns} = conn, %{id: id} = params) do + def show_history( + %{assigns: assigns, private: %{open_api_spex: %{params: %{id: id} = params}}} = conn, + _ + ) do with user = assigns[:user], %Activity{} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.visible_for_user?(activity, user) do @@ -211,7 +249,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/statuses/:id/source" - def show_source(%{assigns: assigns} = conn, %{id: id} = _params) do + def show_source(%{assigns: assigns, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do with user = assigns[:user], %Activity{} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.visible_for_user?(activity, user) do @@ -225,7 +263,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "PUT /api/v1/statuses/:id" - def update(%{assigns: %{user: user}, body_params: body_params} = conn, %{id: id} = params) do + def update( + %{ + assigns: %{user: user}, + private: %{open_api_spex: %{body_params: body_params, params: %{id: id} = params}} + } = conn, + _ + ) do with {_, %Activity{}} = {_, activity} <- {:activity, Activity.get_by_id_with_object(id)}, {_, true} <- {:visible, Visibility.visible_for_user?(activity, user)}, {_, true} <- {:is_create, activity.data["type"] == "Create"}, @@ -248,7 +292,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/statuses/:id" - def show(%{assigns: %{user: user}} = conn, %{id: id} = params) do + def show( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id} = params}}} = + conn, + _ + ) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.visible_for_user?(activity, user) do try_render(conn, "show.json", @@ -263,7 +311,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "DELETE /api/v1/statuses/:id" - def delete(%{assigns: %{user: user}} = conn, %{id: id}) do + def delete(%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), {:ok, %Activity{}} <- CommonAPI.delete(id, user) do try_render(conn, "show.json", @@ -278,7 +326,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/reblog" - def reblog(%{assigns: %{user: user}, body_params: params} = conn, %{id: ap_id_or_id}) do + def reblog( + %{ + assigns: %{user: user}, + private: %{open_api_spex: %{body_params: params, params: %{id: ap_id_or_id}}} + } = conn, + _ + ) do with {:ok, announce} <- CommonAPI.repeat(ap_id_or_id, user, params), %Activity{} = announce <- Activity.normalize(announce.data) do try_render(conn, "show.json", %{activity: announce, for: user, as: :activity}) @@ -286,7 +340,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/unreblog" - def unreblog(%{assigns: %{user: user}} = conn, %{id: activity_id}) do + def unreblog( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: activity_id}}}} = + conn, + _ + ) do with {:ok, _unannounce} <- CommonAPI.unrepeat(activity_id, user), %Activity{} = activity <- Activity.get_by_id(activity_id) do try_render(conn, "show.json", %{activity: activity, for: user, as: :activity}) @@ -294,7 +352,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/favourite" - def favourite(%{assigns: %{user: user}} = conn, %{id: activity_id}) do + def favourite( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: activity_id}}}} = + conn, + _ + ) do with {:ok, _fav} <- CommonAPI.favorite(user, activity_id), %Activity{} = activity <- Activity.get_by_id(activity_id) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) @@ -302,7 +364,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/unfavourite" - def unfavourite(%{assigns: %{user: user}} = conn, %{id: activity_id}) do + def unfavourite( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: activity_id}}}} = + conn, + _ + ) do with {:ok, _unfav} <- CommonAPI.unfavorite(activity_id, user), %Activity{} = activity <- Activity.get_by_id(activity_id) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) @@ -310,7 +376,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/pin" - def pin(%{assigns: %{user: user}} = conn, %{id: ap_id_or_id}) do + def pin( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: ap_id_or_id}}}} = + conn, + _ + ) do with {:ok, activity} <- CommonAPI.pin(ap_id_or_id, user) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) else @@ -329,14 +399,21 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/unpin" - def unpin(%{assigns: %{user: user}} = conn, %{id: ap_id_or_id}) do + def unpin( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: ap_id_or_id}}}} = + conn, + _ + ) do with {:ok, activity} <- CommonAPI.unpin(ap_id_or_id, user) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) end end @doc "POST /api/v1/statuses/:id/bookmark" - def bookmark(%{assigns: %{user: user}} = conn, %{id: id}) do + def bookmark( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, + _ + ) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), @@ -346,7 +423,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/unbookmark" - def unbookmark(%{assigns: %{user: user}} = conn, %{id: id}) do + def unbookmark( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, + _ + ) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), @@ -356,7 +436,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/mute" - def mute_conversation(%{assigns: %{user: user}, body_params: params} = conn, %{id: id}) do + def mute_conversation( + %{ + assigns: %{user: user}, + private: %{open_api_spex: %{body_params: params, params: %{id: id}}} + } = conn, + _ + ) do with %Activity{} = activity <- Activity.get_by_id(id), {:ok, activity} <- CommonAPI.add_mute(user, activity, params) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) @@ -364,7 +450,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/unmute" - def unmute_conversation(%{assigns: %{user: user}} = conn, %{id: id}) do + def unmute_conversation( + %{ + assigns: %{user: user}, + private: %{open_api_spex: %{params: %{id: id}}} + } = conn, + _ + ) do with %Activity{} = activity <- Activity.get_by_id(id), {:ok, activity} <- CommonAPI.remove_mute(user, activity) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) @@ -373,7 +465,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do @doc "GET /api/v1/statuses/:id/card" @deprecated "https://github.com/tootsuite/mastodon/pull/11213" - def card(%{assigns: %{user: user}} = conn, %{id: status_id}) do + def card( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: status_id}}}} = conn, + _ + ) do with %Activity{} = activity <- Activity.get_by_id(status_id), true <- Visibility.visible_for_user?(activity, user) do data = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) @@ -384,7 +479,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/statuses/:id/favourited_by" - def favourited_by(%{assigns: %{user: user}} = conn, %{id: id}) do + def favourited_by( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, + _ + ) do with true <- Pleroma.Config.get([:instance, :show_reactions]), %Activity{} = activity <- Activity.get_by_id_with_object(id), {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)}, @@ -405,7 +503,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/statuses/:id/reblogged_by" - def reblogged_by(%{assigns: %{user: user}} = conn, %{id: id}) do + def reblogged_by( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, + _ + ) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)}, %Object{data: %{"announcements" => announces, "id" => ap_id}} <- @@ -437,7 +538,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/statuses/:id/context" - def context(%{assigns: %{user: user}} = conn, %{id: id}) do + def context( + %{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, + _ + ) do with %Activity{} = activity <- Activity.get_by_id(id) do activities = ActivityPub.fetch_activities_for_context(activity.data["context"], %{ @@ -451,7 +555,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/favourites" - def favourites(%{assigns: %{user: %User{} = user}} = conn, params) do + def favourites( + %{assigns: %{user: %User{} = user}, private: %{open_api_spex: %{params: params}}} = conn, + _ + ) do activities = ActivityPub.fetch_favourites(user, params) conn @@ -464,7 +571,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "GET /api/v1/bookmarks" - def bookmarks(%{assigns: %{user: user}} = conn, params) do + def bookmarks(%{assigns: %{user: user}, private: %{open_api_spex: %{params: params}}} = conn, _) do user = User.get_cached_by_id(user.id) bookmarks = diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index df8fdc8b8..267c3e3ed 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -214,7 +214,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do do: user.follower_count, else: 0 - bot = is_bot?(user) + bot = bot?(user) emojis = Enum.map(user.emoji, fn {shortcode, raw_url} -> @@ -471,7 +471,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do defp image_url(%{"url" => [%{"href" => href} | _]}), do: href defp image_url(_), do: nil - defp is_bot?(user) do + defp bot?(user) do # Because older and/or Mastodon clients may not recognize a Group actor properly, # and currently the group actor can only boost things, we should let these clients # think groups are bots. diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 0e2e604f5..6303e72ce 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -796,8 +796,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do URI.merge(page_url_data, image_url_data) |> to_string end - defp build_image_url(_, _), do: nil - defp get_source_text(%{"content" => content} = _source) do content end diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 07c2b62e3..bb27d806d 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -11,28 +11,21 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do alias Pleroma.Web.Streamer alias Pleroma.Web.StreamerView - @behaviour :cowboy_websocket + @behaviour Phoenix.Socket.Transport # Client ping period. @tick :timer.seconds(30) - # Cowboy timeout period. - @timeout :timer.seconds(60) - # Hibernate every X messages - @hibernate_every 100 - def init(%{qs: qs} = req, state) do - with params <- Enum.into(:cow_qs.parse_qs(qs), %{}), - sec_websocket <- :cowboy_req.header("sec-websocket-protocol", req, nil), - access_token <- Map.get(params, "access_token"), - {:ok, user, oauth_token} <- authenticate_request(access_token, sec_websocket), - {:ok, topic} <- Streamer.get_topic(params["stream"], user, oauth_token, params) do - req = - if sec_websocket do - :cowboy_req.set_resp_header("sec-websocket-protocol", sec_websocket, req) - else - req - end + @impl Phoenix.Socket.Transport + def child_spec(_opts), do: :ignore + # This only prepares the connection and is not in the process yet + @impl Phoenix.Socket.Transport + def connect(%{params: params} = transport_info) do + with access_token <- Map.get(params, "access_token"), + {:ok, user, oauth_token} <- authenticate_request(access_token), + {:ok, topic} <- + Streamer.get_topic(params["stream"], user, oauth_token, params) do topics = if topic do [topic] @@ -40,41 +33,40 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do [] end - {:cowboy_websocket, req, - %{user: user, topics: topics, oauth_token: oauth_token, count: 0, timer: nil}, - %{idle_timeout: @timeout}} + state = %{ + user: user, + topics: topics, + oauth_token: oauth_token, + count: 0, + timer: nil + } + + {:ok, state} else {:error, :bad_topic} -> - Logger.debug("#{__MODULE__} bad topic #{inspect(req)}") - req = :cowboy_req.reply(404, req) - {:ok, req, state} + Logger.debug("#{__MODULE__} bad topic #{inspect(transport_info)}") + + {:error, :bad_topic} {:error, :unauthorized} -> - Logger.debug("#{__MODULE__} authentication error: #{inspect(req)}") - req = :cowboy_req.reply(401, req) - {:ok, req, state} + Logger.debug("#{__MODULE__} authentication error: #{inspect(transport_info)}") + {:error, :unauthorized} end end - def websocket_init(state) do - Logger.debug( - "#{__MODULE__} accepted websocket connection for user #{(state.user || %{id: "anonymous"}).id}, topics #{state.topics}" - ) - + # All subscriptions/links and messages cannot be created + # until the processed is launched with init/1 + @impl Phoenix.Socket.Transport + def init(state) do Enum.each(state.topics, fn topic -> Streamer.add_socket(topic, state.oauth_token) end) - {:ok, %{state | timer: timer()}} + + Process.send_after(self(), :ping, @tick) + + {:ok, state} end - # Client's Pong frame. - def websocket_handle(:pong, state) do - if state.timer, do: Process.cancel_timer(state.timer) - {:ok, %{state | timer: timer()}} - end - - # We only receive pings for now - def websocket_handle(:ping, state), do: {:ok, state} - - def websocket_handle({:text, text}, state) do + @impl Phoenix.Socket.Transport + def handle_in({text, [opcode: :text]}, state) do with {:ok, %{} = event} <- Jason.decode(text) do handle_client_event(event, state) else @@ -84,50 +76,47 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do end end - def websocket_handle(frame, state) do + def handle_in(frame, state) do Logger.error("#{__MODULE__} received frame: #{inspect(frame)}") {:ok, state} end - def websocket_info({:render_with_user, view, template, item, topic}, state) do + @impl Phoenix.Socket.Transport + def handle_info({:render_with_user, view, template, item, topic}, state) do user = %User{} = User.get_cached_by_ap_id(state.user.ap_id) unless Streamer.filtered_by_user?(user, item) do - websocket_info({:text, view.render(template, item, user, topic)}, %{state | user: user}) + message = view.render(template, item, user, topic) + {:push, {:text, message}, %{state | user: user}} else {:ok, state} end end - def websocket_info({:text, message}, state) do - # If the websocket processed X messages, force an hibernate/GC. - # We don't hibernate at every message to balance CPU usage/latency with RAM usage. - if state.count > @hibernate_every do - {:reply, {:text, message}, %{state | count: 0}, :hibernate} - else - {:reply, {:text, message}, %{state | count: state.count + 1}} - end + def handle_info({:text, text}, state) do + {:push, {:text, text}, state} end - # Ping tick. We don't re-queue a timer there, it is instead queued when :pong is received. - # As we hibernate there, reset the count to 0. - # If the client misses :pong, Cowboy will automatically timeout the connection after - # `@idle_timeout`. - def websocket_info(:tick, state) do - {:reply, :ping, %{state | timer: nil, count: 0}, :hibernate} + def handle_info(:ping, state) do + Process.send_after(self(), :ping, @tick) + + {:push, {:ping, ""}, state} end - def websocket_info(:close, state) do - {:stop, state} + def handle_info(:close, state) do + {:stop, {:closed, 'connection closed by server'}, state} end - # State can be `[]` only in case we terminate before switching to websocket, - # we already log errors for these cases in `init/1`, so just do nothing here - def terminate(_reason, _req, []), do: :ok + def handle_info(msg, state) do + Logger.debug("#{__MODULE__} received info: #{inspect(msg)}") - def terminate(reason, _req, state) do + {:ok, state} + end + + @impl Phoenix.Socket.Transport + def terminate(reason, state) do Logger.debug( - "#{__MODULE__} terminating websocket connection for user #{(state.user || %{id: "anonymous"}).id}, topics #{state.topics || "?"}: #{inspect(reason)}" + "#{__MODULE__} terminating websocket connection for user #{(state.user || %{id: "anonymous"}).id}, topics #{state.topics || "?"}: #{inspect(reason)})" ) Enum.each(state.topics, fn topic -> Streamer.remove_socket(topic) end) @@ -135,16 +124,13 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do end # Public streams without authentication. - defp authenticate_request(nil, nil) do + defp authenticate_request(nil) do {:ok, nil, nil} end # Authenticated streams. - defp authenticate_request(access_token, sec_websocket) do - token = access_token || sec_websocket - - with true <- is_bitstring(token), - oauth_token = %Token{user_id: user_id} <- Repo.get_by(Token, token: token), + defp authenticate_request(access_token) do + with oauth_token = %Token{user_id: user_id} <- Repo.get_by(Token, token: access_token), user = %User{} <- User.get_cached_by_id(user_id) do {:ok, user, oauth_token} else @@ -152,36 +138,32 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do end end - defp timer do - Process.send_after(self(), :tick, @tick) - end - defp handle_client_event(%{"type" => "subscribe", "stream" => _topic} = params, state) do with {_, {:ok, topic}} <- {:topic, Streamer.get_topic(params["stream"], state.user, state.oauth_token, params)}, {_, false} <- {:subscribed, topic in state.topics} do Streamer.add_socket(topic, state.oauth_token) - {[ - {:text, - StreamerView.render("pleroma_respond.json", %{type: "subscribe", result: "success"})} - ], %{state | topics: [topic | state.topics]}} + message = + StreamerView.render("pleroma_respond.json", %{type: "subscribe", result: "success"}) + + {:reply, :ok, {:text, message}, %{state | topics: [topic | state.topics]}} else {:subscribed, true} -> - {[ - {:text, - StreamerView.render("pleroma_respond.json", %{type: "subscribe", result: "ignored"})} - ], state} + message = + StreamerView.render("pleroma_respond.json", %{type: "subscribe", result: "ignored"}) + + {:reply, :error, {:text, message}, state} {:topic, {:error, error}} -> - {[ - {:text, - StreamerView.render("pleroma_respond.json", %{ - type: "subscribe", - result: "error", - error: error - })} - ], state} + message = + StreamerView.render("pleroma_respond.json", %{ + type: "subscribe", + result: "error", + error: error + }) + + {:reply, :error, {:text, message}, state} end end @@ -191,26 +173,26 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do {_, true} <- {:subscribed, topic in state.topics} do Streamer.remove_socket(topic) - {[ - {:text, - StreamerView.render("pleroma_respond.json", %{type: "unsubscribe", result: "success"})} - ], %{state | topics: List.delete(state.topics, topic)}} + message = + StreamerView.render("pleroma_respond.json", %{type: "unsubscribe", result: "success"}) + + {:reply, :ok, {:text, message}, %{state | topics: List.delete(state.topics, topic)}} else {:subscribed, false} -> - {[ - {:text, - StreamerView.render("pleroma_respond.json", %{type: "unsubscribe", result: "ignored"})} - ], state} + message = + StreamerView.render("pleroma_respond.json", %{type: "unsubscribe", result: "ignored"}) + + {:reply, :error, {:text, message}, state} {:topic, {:error, error}} -> - {[ - {:text, - StreamerView.render("pleroma_respond.json", %{ - type: "unsubscribe", - result: "error", - error: error - })} - ], state} + message = + StreamerView.render("pleroma_respond.json", %{ + type: "unsubscribe", + result: "error", + error: error + }) + + {:reply, :error, {:text, message}, state} end end @@ -219,39 +201,47 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do state ) do with {:auth, nil, nil} <- {:auth, state.user, state.oauth_token}, - {:ok, user, oauth_token} <- authenticate_request(access_token, nil) do - {[ - {:text, - StreamerView.render("pleroma_respond.json", %{ - type: "pleroma:authenticate", - result: "success" - })} - ], %{state | user: user, oauth_token: oauth_token}} + {:ok, user, oauth_token} <- authenticate_request(access_token) do + message = + StreamerView.render("pleroma_respond.json", %{ + type: "pleroma:authenticate", + result: "success" + }) + + {:reply, :ok, {:text, message}, %{state | user: user, oauth_token: oauth_token}} else {:auth, _, _} -> - {[ - {:text, - StreamerView.render("pleroma_respond.json", %{ - type: "pleroma:authenticate", - result: "error", - error: :already_authenticated - })} - ], state} + message = + StreamerView.render("pleroma_respond.json", %{ + type: "pleroma:authenticate", + result: "error", + error: :already_authenticated + }) + + {:reply, :error, {:text, message}, state} _ -> - {[ - {:text, - StreamerView.render("pleroma_respond.json", %{ - type: "pleroma:authenticate", - result: "error", - error: :unauthorized - })} - ], state} + message = + StreamerView.render("pleroma_respond.json", %{ + type: "pleroma:authenticate", + result: "error", + error: :unauthorized + }) + + {:reply, :error, {:text, message}, state} end end defp handle_client_event(params, state) do Logger.error("#{__MODULE__} received unknown event: #{inspect(params)}") - {[], state} + {:ok, state} + end + + def handle_error(conn, :unauthorized) do + Plug.Conn.send_resp(conn, 401, "Unauthorized") + end + + def handle_error(conn, _reason) do + Plug.Conn.send_resp(conn, 404, "Not Found") end end diff --git a/lib/pleroma/web/media_proxy/media_proxy_controller.ex b/lib/pleroma/web/media_proxy/media_proxy_controller.ex index bda5b36ed..c11484ecb 100644 --- a/lib/pleroma/web/media_proxy/media_proxy_controller.ex +++ b/lib/pleroma/web/media_proxy/media_proxy_controller.ex @@ -56,7 +56,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do media_proxy_url = MediaProxy.url(url) with {:ok, %{status: status} = head_response} when status in 200..299 <- - Pleroma.HTTP.request("HEAD", media_proxy_url, [], [], pool: :media) do + Pleroma.HTTP.request(:head, media_proxy_url, "", [], pool: :media) do content_type = Tesla.get_header(head_response, "content-type") content_length = Tesla.get_header(head_response, "content-length") content_length = content_length && String.to_integer(content_length) diff --git a/lib/pleroma/web/o_auth/o_auth_controller.ex b/lib/pleroma/web/o_auth/o_auth_controller.ex index 2bbe5d5fa..47b03215f 100644 --- a/lib/pleroma/web/o_auth/o_auth_controller.ex +++ b/lib/pleroma/web/o_auth/o_auth_controller.ex @@ -610,13 +610,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do end end - @spec validate_scopes(App.t(), map() | list()) :: + @spec validate_scopes(App.t(), list()) :: {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes} - defp validate_scopes(%App{} = app, params) when is_map(params) do - requested_scopes = Scopes.fetch_scopes(params, app.scopes) - validate_scopes(app, requested_scopes) - end - defp validate_scopes(%App{} = app, requested_scopes) when is_list(requested_scopes) do Scopes.validate(requested_scopes, app.scopes) end diff --git a/lib/pleroma/web/o_auth/token.ex b/lib/pleroma/web/o_auth/token.ex index d4a7e8999..a5ad2e909 100644 --- a/lib/pleroma/web/o_auth/token.ex +++ b/lib/pleroma/web/o_auth/token.ex @@ -138,9 +138,9 @@ defmodule Pleroma.Web.OAuth.Token do |> Repo.all() end - def is_expired?(%__MODULE__{valid_until: valid_until}) do + def expired?(%__MODULE__{valid_until: valid_until}) do NaiveDateTime.diff(NaiveDateTime.utc_now(), valid_until) > 0 end - def is_expired?(_), do: false + def expired?(_), do: false end diff --git a/lib/pleroma/web/o_status/o_status_controller.ex b/lib/pleroma/web/o_status/o_status_controller.ex index ea4994bd0..ee7ef4a5d 100644 --- a/lib/pleroma/web/o_status/o_status_controller.ex +++ b/lib/pleroma/web/o_status/o_status_controller.ex @@ -37,7 +37,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do with id <- Endpoint.url() <> conn.request_path, {_, %Activity{} = activity} <- {:activity, Activity.get_create_by_object_ap_id_with_object(id)}, - {_, true} <- {:public?, Visibility.is_public?(activity)} do + {_, true} <- {:public?, Visibility.public?(activity)} do redirect(conn, to: "/notice/#{activity.id}") else reason when reason in [{:public?, false}, {:activity, nil}] -> @@ -56,7 +56,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do def activity(conn, _params) do with id <- Endpoint.url() <> conn.request_path, {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)}, - {_, true} <- {:public?, Visibility.is_public?(activity)} do + {_, true} <- {:public?, Visibility.public?(activity)} do redirect(conn, to: "/notice/#{activity.id}") else reason when reason in [{:public?, false}, {:activity, nil}] -> @@ -69,7 +69,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id_with_object(id)}, - {_, true} <- {:public?, Visibility.is_public?(activity)}, + {_, true} <- {:public?, Visibility.public?(activity)}, %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do cond do format in ["json", "activity+json"] -> @@ -106,13 +106,12 @@ defmodule Pleroma.Web.OStatus.OStatusController do # Returns an HTML embedded