Merge branch 'develop' into feature/relay-list
[akkoma] / lib / pleroma / web / ostatus / ostatus_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 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 Fallback.RedirectController
9 alias Pleroma.Activity
10 alias Pleroma.Object
11 alias Pleroma.User
12 alias Pleroma.Web.ActivityPub.ActivityPubController
13 alias Pleroma.Web.ActivityPub.ObjectView
14 alias Pleroma.Web.ActivityPub.Visibility
15 alias Pleroma.Web.Endpoint
16 alias Pleroma.Web.Metadata.PlayerView
17 alias Pleroma.Web.Router
18
19 plug(
20 Pleroma.Plugs.RateLimiter,
21 {:ap_routes, params: ["uuid"]} when action in [:object, :activity]
22 )
23
24 plug(
25 Pleroma.Plugs.SetFormatPlug
26 when action in [:object, :activity, :notice]
27 )
28
29 action_fallback(:errors)
30
31 def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
32 when format in ["json", "activity+json"] do
33 ActivityPubController.call(conn, :object)
34 end
35
36 def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
37 with id <- o_status_url(conn, :object, uuid),
38 {_, %Activity{} = activity} <-
39 {:activity, Activity.get_create_by_object_ap_id_with_object(id)},
40 {_, true} <- {:public?, Visibility.is_public?(activity)},
41 %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
42 case format do
43 "html" -> redirect(conn, to: "/notice/#{activity.id}")
44 _ -> represent_activity(conn, nil, activity, user)
45 end
46 else
47 reason when reason in [{:public?, false}, {:activity, nil}] ->
48 {:error, :not_found}
49
50 e ->
51 e
52 end
53 end
54
55 def activity(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
56 when format in ["json", "activity+json"] do
57 ActivityPubController.call(conn, :activity)
58 end
59
60 def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
61 with id <- o_status_url(conn, :activity, uuid),
62 {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
63 {_, true} <- {:public?, Visibility.is_public?(activity)},
64 %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
65 case format do
66 "html" -> redirect(conn, to: "/notice/#{activity.id}")
67 _ -> represent_activity(conn, format, activity, user)
68 end
69 else
70 reason when reason in [{:public?, false}, {:activity, nil}] ->
71 {:error, :not_found}
72
73 e ->
74 e
75 end
76 end
77
78 def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do
79 with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id_with_object(id)},
80 {_, true} <- {:public?, Visibility.is_public?(activity)},
81 %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
82 cond do
83 format == "html" && activity.data["type"] == "Create" ->
84 %Object{} = object = Object.normalize(activity)
85
86 RedirectController.redirector_with_meta(
87 conn,
88 %{
89 activity_id: activity.id,
90 object: object,
91 url: Router.Helpers.o_status_url(Endpoint, :notice, activity.id),
92 user: user
93 }
94 )
95
96 format == "html" ->
97 RedirectController.redirector(conn, nil)
98
99 true ->
100 represent_activity(conn, format, activity, user)
101 end
102 else
103 reason when reason in [{:public?, false}, {:activity, nil}] ->
104 conn
105 |> put_status(404)
106 |> RedirectController.redirector(nil, 404)
107
108 e ->
109 e
110 end
111 end
112
113 # Returns an HTML embedded <audio> or <video> player suitable for embed iframes.
114 def notice_player(conn, %{"id" => id}) do
115 with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
116 true <- Visibility.is_public?(activity),
117 %Object{} = object <- Object.normalize(activity),
118 %{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
119 true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
120 conn
121 |> put_layout(:metadata_player)
122 |> put_resp_header("x-frame-options", "ALLOW")
123 |> put_resp_header(
124 "content-security-policy",
125 "default-src 'none';style-src 'self' 'unsafe-inline';img-src 'self' data: https:; media-src 'self' https:;"
126 )
127 |> put_view(PlayerView)
128 |> render("player.html", url)
129 else
130 _error ->
131 conn
132 |> put_status(404)
133 |> RedirectController.redirector(nil, 404)
134 end
135 end
136
137 defp represent_activity(
138 conn,
139 "activity+json",
140 %Activity{data: %{"type" => "Create"}} = activity,
141 _user
142 ) do
143 object = Object.normalize(activity)
144
145 conn
146 |> put_resp_header("content-type", "application/activity+json")
147 |> put_view(ObjectView)
148 |> render("object.json", %{object: object})
149 end
150
151 defp represent_activity(_conn, _, _, _) do
152 {:error, :not_found}
153 end
154
155 def errors(conn, {:error, :not_found}) do
156 render_error(conn, :not_found, "Not found")
157 end
158
159 def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
160
161 def errors(conn, _) do
162 render_error(conn, :internal_server_error, "Something went wrong")
163 end
164 end