First
[anni] / lib / pleroma / web / o_status / o_status_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.OStatus.OStatusController do
6   use Pleroma.Web, :controller
7
8   alias Pleroma.Activity
9   alias Pleroma.Object
10   alias Pleroma.User
11   alias Pleroma.Web.ActivityPub.ActivityPubController
12   alias Pleroma.Web.ActivityPub.Visibility
13   alias Pleroma.Web.Endpoint
14   alias Pleroma.Web.Fallback.RedirectController
15   alias Pleroma.Web.Metadata.PlayerView
16   alias Pleroma.Web.Plugs.RateLimiter
17   alias Pleroma.Web.Router
18
19   plug(
20     RateLimiter,
21     [name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
22   )
23
24   plug(
25     Pleroma.Web.Plugs.SetFormatPlug
26     when action in [:object, :activity, :notice]
27   )
28
29   action_fallback(:errors)
30
31   def object(%{assigns: %{format: format}} = conn, _params)
32       when format in ["json", "activity+json"] do
33     ActivityPubController.call(conn, :object)
34   end
35
36   def object(conn, _params) do
37     with id <- Endpoint.url() <> conn.request_path,
38          {_, %Activity{} = activity} <-
39            {:activity, Activity.get_create_by_object_ap_id_with_object(id)},
40          {_, true} <- {:public?, Visibility.is_public?(activity)} do
41       redirect(conn, to: "/notice/#{activity.id}")
42     else
43       reason when reason in [{:public?, false}, {:activity, nil}] ->
44         {:error, :not_found}
45
46       e ->
47         e
48     end
49   end
50
51   def activity(%{assigns: %{format: format}} = conn, _params)
52       when format in ["json", "activity+json"] do
53     ActivityPubController.call(conn, :activity)
54   end
55
56   def activity(conn, _params) do
57     with id <- Endpoint.url() <> conn.request_path,
58          {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
59          {_, true} <- {:public?, Visibility.is_public?(activity)} do
60       redirect(conn, to: "/notice/#{activity.id}")
61     else
62       reason when reason in [{:public?, false}, {:activity, nil}] ->
63         {:error, :not_found}
64
65       e ->
66         e
67     end
68   end
69
70   def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do
71     with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id_with_object(id)},
72          {_, true} <- {:public?, Visibility.is_public?(activity)},
73          %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
74       cond do
75         format in ["json", "activity+json"] ->
76           %{data: %{"id" => redirect_url}} = Object.normalize(activity, fetch: false)
77           redirect(conn, external: redirect_url)
78
79         activity.data["type"] == "Create" ->
80           %Object{} = object = Object.normalize(activity, fetch: false)
81
82           RedirectController.redirector_with_meta(
83             conn,
84             %{
85               activity_id: activity.id,
86               object: object,
87               url: Router.Helpers.o_status_url(Endpoint, :notice, activity.id),
88               user: user
89             }
90           )
91
92         true ->
93           RedirectController.redirector(conn, nil)
94       end
95     else
96       reason when reason in [{:public?, false}, {:activity, nil}] ->
97         conn
98         |> put_status(404)
99         |> RedirectController.redirector(nil, 404)
100
101       e ->
102         e
103     end
104   end
105
106   # Returns an HTML embedded <audio> or <video> player suitable for embed iframes.
107   def notice_player(conn, %{"id" => id}) do
108     with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
109          true <- Visibility.is_public?(activity),
110          {_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
111          %Object{} = object <- Object.normalize(activity, fetch: false),
112          %{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
113          true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
114       conn
115       |> put_layout(:metadata_player)
116       |> put_resp_header("x-frame-options", "ALLOW")
117       |> put_resp_header(
118         "content-security-policy",
119         "default-src 'none';style-src 'self' 'unsafe-inline';img-src 'self' data: https:; media-src 'self' https:;"
120       )
121       |> put_view(PlayerView)
122       |> render("player.html", url)
123     else
124       _error ->
125         conn
126         |> put_status(404)
127         |> RedirectController.redirector(nil, 404)
128     end
129   end
130
131   defp errors(conn, {:error, :not_found}) do
132     render_error(conn, :not_found, "Not found")
133   end
134
135   defp errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
136
137   defp errors(conn, _) do
138     render_error(conn, :internal_server_error, "Something went wrong")
139   end
140 end