Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into feature/jobs
[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 Pleroma.Activity
9 alias Pleroma.Object
10 alias Pleroma.User
11 alias Pleroma.Web.ActivityPub.ActivityPub
12 alias Pleroma.Web.ActivityPub.ActivityPubController
13 alias Pleroma.Web.ActivityPub.ObjectView
14 alias Pleroma.Web.OStatus.ActivityRepresenter
15 alias Pleroma.Web.OStatus.FeedRepresenter
16 alias Pleroma.Web.Federator
17 alias Pleroma.Web.OStatus
18 alias Pleroma.Web.XML
19
20 plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming])
21
22 action_fallback(:errors)
23
24 def feed_redirect(conn, %{"nickname" => nickname}) do
25 case get_format(conn) do
26 "html" ->
27 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
28 Fallback.RedirectController.redirector_with_meta(conn, %{user: user})
29 else
30 nil -> {:error, :not_found}
31 end
32
33 "activity+json" ->
34 ActivityPubController.call(conn, :user)
35
36 _ ->
37 with %User{} = user <- User.get_cached_by_nickname(nickname) do
38 redirect(conn, external: OStatus.feed_path(user))
39 else
40 nil -> {:error, :not_found}
41 end
42 end
43 end
44
45 def feed(conn, %{"nickname" => nickname} = params) do
46 with %User{} = user <- User.get_cached_by_nickname(nickname) do
47 query_params =
48 Map.take(params, ["max_id"])
49 |> Map.merge(%{"whole_db" => true, "actor_id" => user.ap_id})
50
51 activities =
52 ActivityPub.fetch_public_activities(query_params)
53 |> Enum.reverse()
54
55 response =
56 user
57 |> FeedRepresenter.to_simple_form(activities, [user])
58 |> :xmerl.export_simple(:xmerl_xml)
59 |> to_string
60
61 conn
62 |> put_resp_content_type("application/atom+xml")
63 |> send_resp(200, response)
64 else
65 nil -> {:error, :not_found}
66 end
67 end
68
69 defp decode_or_retry(body) do
70 with {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body),
71 {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do
72 {:ok, doc}
73 else
74 _e ->
75 with [decoded | _] <- Pleroma.Web.Salmon.decode(body),
76 doc <- XML.parse_document(decoded),
77 uri when not is_nil(uri) <- XML.string_from_xpath("/entry/author[1]/uri", doc),
78 {:ok, _} <- Pleroma.Web.OStatus.make_user(uri, true),
79 {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body),
80 {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do
81 {:ok, doc}
82 end
83 end
84 end
85
86 def salmon_incoming(conn, _) do
87 {:ok, body, _conn} = read_body(conn)
88 {:ok, doc} = decode_or_retry(body)
89
90 Federator.incoming_doc(doc)
91
92 conn
93 |> send_resp(200, "")
94 end
95
96 def object(conn, %{"uuid" => uuid}) do
97 if get_format(conn) == "activity+json" do
98 ActivityPubController.call(conn, :object)
99 else
100 with id <- o_status_url(conn, :object, uuid),
101 {_, %Activity{} = activity} <- {:activity, Activity.get_create_by_object_ap_id(id)},
102 {_, true} <- {:public?, ActivityPub.is_public?(activity)},
103 %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
104 case get_format(conn) do
105 "html" -> redirect(conn, to: "/notice/#{activity.id}")
106 _ -> represent_activity(conn, nil, activity, user)
107 end
108 else
109 {:public?, false} ->
110 {:error, :not_found}
111
112 {:activity, nil} ->
113 {:error, :not_found}
114
115 e ->
116 e
117 end
118 end
119 end
120
121 def activity(conn, %{"uuid" => uuid}) do
122 if get_format(conn) == "activity+json" do
123 ActivityPubController.call(conn, :activity)
124 else
125 with id <- o_status_url(conn, :activity, uuid),
126 {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
127 {_, true} <- {:public?, ActivityPub.is_public?(activity)},
128 %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
129 case format = get_format(conn) do
130 "html" -> redirect(conn, to: "/notice/#{activity.id}")
131 _ -> represent_activity(conn, format, activity, user)
132 end
133 else
134 {:public?, false} ->
135 {:error, :not_found}
136
137 {:activity, nil} ->
138 {:error, :not_found}
139
140 e ->
141 e
142 end
143 end
144 end
145
146 def notice(conn, %{"id" => id}) do
147 with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(id)},
148 {_, true} <- {:public?, ActivityPub.is_public?(activity)},
149 %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
150 case format = get_format(conn) do
151 "html" ->
152 if activity.data["type"] == "Create" do
153 %Object{} = object = Object.normalize(activity.data["object"])
154
155 Fallback.RedirectController.redirector_with_meta(conn, %{
156 object: object,
157 url:
158 Pleroma.Web.Router.Helpers.o_status_url(
159 Pleroma.Web.Endpoint,
160 :notice,
161 activity.id
162 ),
163 user: user
164 })
165 else
166 Fallback.RedirectController.redirector(conn, nil)
167 end
168
169 _ ->
170 represent_activity(conn, format, activity, user)
171 end
172 else
173 {:public?, false} ->
174 conn
175 |> put_status(404)
176 |> Fallback.RedirectController.redirector(nil, 404)
177
178 {:activity, nil} ->
179 conn
180 |> Fallback.RedirectController.redirector(nil, 404)
181
182 e ->
183 e
184 end
185 end
186
187 defp represent_activity(
188 conn,
189 "activity+json",
190 %Activity{data: %{"type" => "Create"}} = activity,
191 _user
192 ) do
193 object = Object.normalize(activity.data["object"])
194
195 conn
196 |> put_resp_header("content-type", "application/activity+json")
197 |> json(ObjectView.render("object.json", %{object: object}))
198 end
199
200 defp represent_activity(_conn, "activity+json", _, _) do
201 {:error, :not_found}
202 end
203
204 defp represent_activity(conn, _, activity, user) do
205 response =
206 activity
207 |> ActivityRepresenter.to_simple_form(user, true)
208 |> ActivityRepresenter.wrap_with_entry()
209 |> :xmerl.export_simple(:xmerl_xml)
210 |> to_string
211
212 conn
213 |> put_resp_content_type("application/atom+xml")
214 |> send_resp(200, response)
215 end
216
217 def errors(conn, {:error, :not_found}) do
218 conn
219 |> put_status(404)
220 |> text("Not found")
221 end
222
223 def errors(conn, _) do
224 conn
225 |> put_status(500)
226 |> text("Something went wrong")
227 end
228 end