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