Merge branch 'develop' into issue/1218
[akkoma] / lib / pleroma / web / twitter_api / controllers / util_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.TwitterAPI.UtilController do
6 use Pleroma.Web, :controller
7
8 require Logger
9
10 alias Pleroma.Activity
11 alias Pleroma.Config
12 alias Pleroma.Emoji
13 alias Pleroma.Healthcheck
14 alias Pleroma.Notification
15 alias Pleroma.Plugs.AuthenticationPlug
16 alias Pleroma.User
17 alias Pleroma.Web
18 alias Pleroma.Web.CommonAPI
19 alias Pleroma.Web.WebFinger
20
21 plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version])
22
23 def help_test(conn, _params) do
24 json(conn, "ok")
25 end
26
27 def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
28 with %User{} = user <- User.get_cached_by_nickname(nick),
29 avatar = User.avatar_url(user) do
30 conn
31 |> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
32 else
33 _e ->
34 render(conn, "subscribe.html", %{
35 nickname: nick,
36 avatar: nil,
37 error: "Could not find user"
38 })
39 end
40 end
41
42 def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
43 with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
44 %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
45 conn
46 |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
47 else
48 _e ->
49 render(conn, "subscribe.html", %{
50 nickname: nick,
51 avatar: nil,
52 error: "Something went wrong."
53 })
54 end
55 end
56
57 def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
58 if is_status?(acct) do
59 {:ok, object} = Pleroma.Object.Fetcher.fetch_object_from_id(acct)
60 %Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])
61 redirect(conn, to: "/notice/#{activity_id}")
62 else
63 with {:ok, followee} <- User.get_or_fetch(acct) do
64 conn
65 |> render(follow_template(user), %{
66 error: false,
67 acct: acct,
68 avatar: User.avatar_url(followee),
69 name: followee.nickname,
70 id: followee.id
71 })
72 else
73 {:error, _reason} ->
74 render(conn, follow_template(user), %{error: :error})
75 end
76 end
77 end
78
79 defp follow_template(%User{} = _user), do: "follow.html"
80 defp follow_template(_), do: "follow_login.html"
81
82 defp is_status?(acct) do
83 case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do
84 {:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] ->
85 true
86
87 _ ->
88 false
89 end
90 end
91
92 def do_remote_follow(conn, %{
93 "authorization" => %{"name" => username, "password" => password, "id" => id}
94 }) do
95 with %User{} = followee <- User.get_cached_by_id(id),
96 {_, %User{} = user, _} <- {:auth, User.get_cached_by_nickname(username), followee},
97 {_, true, _} <- {
98 :auth,
99 AuthenticationPlug.checkpw(password, user.password_hash),
100 followee
101 },
102 {:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
103 conn
104 |> render("followed.html", %{error: false})
105 else
106 # Was already following user
107 {:error, "Could not follow user:" <> _rest} ->
108 render(conn, "followed.html", %{error: "Error following account"})
109
110 {:auth, _, followee} ->
111 conn
112 |> render("follow_login.html", %{
113 error: "Wrong username or password",
114 id: id,
115 name: followee.nickname,
116 avatar: User.avatar_url(followee)
117 })
118
119 e ->
120 Logger.debug("Remote follow failed with error #{inspect(e)}")
121 render(conn, "followed.html", %{error: "Something went wrong."})
122 end
123 end
124
125 def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
126 with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
127 {:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
128 conn
129 |> render("followed.html", %{error: false})
130 else
131 # Was already following user
132 {:error, "Could not follow user:" <> _rest} ->
133 render(conn, "followed.html", %{error: "Error following account"})
134
135 {:fetch_user, error} ->
136 Logger.debug("Remote follow failed with error #{inspect(error)}")
137 render(conn, "followed.html", %{error: "Could not find user"})
138
139 e ->
140 Logger.debug("Remote follow failed with error #{inspect(e)}")
141 render(conn, "followed.html", %{error: "Something went wrong."})
142 end
143 end
144
145 def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
146 with {:ok, _} <- Notification.read_one(user, notification_id) do
147 json(conn, %{status: "success"})
148 else
149 {:error, message} ->
150 conn
151 |> put_resp_content_type("application/json")
152 |> send_resp(403, Jason.encode!(%{"error" => message}))
153 end
154 end
155
156 def config(%{assigns: %{format: "xml"}} = conn, _params) do
157 instance = Pleroma.Config.get(:instance)
158
159 response = """
160 <config>
161 <site>
162 <name>#{Keyword.get(instance, :name)}</name>
163 <site>#{Web.base_url()}</site>
164 <textlimit>#{Keyword.get(instance, :limit)}</textlimit>
165 <closed>#{!Keyword.get(instance, :registrations_open)}</closed>
166 </site>
167 </config>
168 """
169
170 conn
171 |> put_resp_content_type("application/xml")
172 |> send_resp(200, response)
173 end
174
175 def config(conn, _params) do
176 instance = Pleroma.Config.get(:instance)
177
178 vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
179
180 uploadlimit = %{
181 uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
182 avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
183 backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
184 bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
185 }
186
187 data = %{
188 name: Keyword.get(instance, :name),
189 description: Keyword.get(instance, :description),
190 server: Web.base_url(),
191 textlimit: to_string(Keyword.get(instance, :limit)),
192 uploadlimit: uploadlimit,
193 closed: bool_to_val(Keyword.get(instance, :registrations_open), "0", "1"),
194 private: bool_to_val(Keyword.get(instance, :public, true), "0", "1"),
195 vapidPublicKey: vapid_public_key,
196 accountActivationRequired:
197 bool_to_val(Keyword.get(instance, :account_activation_required, false)),
198 invitesEnabled: bool_to_val(Keyword.get(instance, :invites_enabled, false)),
199 safeDMMentionsEnabled: bool_to_val(Pleroma.Config.get([:instance, :safe_dm_mentions]))
200 }
201
202 managed_config = Keyword.get(instance, :managed_config)
203
204 data =
205 if managed_config do
206 pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
207 Map.put(data, "pleromafe", pleroma_fe)
208 else
209 data
210 end
211
212 json(conn, %{site: data})
213 end
214
215 defp bool_to_val(true), do: "1"
216 defp bool_to_val(_), do: "0"
217 defp bool_to_val(true, val, _), do: val
218 defp bool_to_val(_, _, val), do: val
219
220 def frontend_configurations(conn, _params) do
221 config =
222 Pleroma.Config.get(:frontend_configurations, %{})
223 |> Enum.into(%{})
224
225 json(conn, config)
226 end
227
228 def version(%{assigns: %{format: "xml"}} = conn, _params) do
229 version = Pleroma.Application.named_version()
230
231 conn
232 |> put_resp_content_type("application/xml")
233 |> send_resp(200, "<version>#{version}</version>")
234 end
235
236 def version(conn, _params) do
237 json(conn, Pleroma.Application.named_version())
238 end
239
240 def emoji(conn, _params) do
241 emoji =
242 Enum.reduce(Emoji.get_all(), %{}, fn {code, %Emoji{file: file, tags: tags}}, acc ->
243 Map.put(acc, code, %{image_url: file, tags: tags})
244 end)
245
246 json(conn, emoji)
247 end
248
249 def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
250 with {:ok, _} <- User.update_notification_settings(user, params) do
251 json(conn, %{status: "success"})
252 end
253 end
254
255 def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
256 follow_import(conn, %{"list" => File.read!(listfile.path)})
257 end
258
259 def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
260 with lines <- String.split(list, "\n"),
261 followed_identifiers <-
262 Enum.map(lines, fn line ->
263 String.split(line, ",") |> List.first()
264 end)
265 |> List.delete("Account address") do
266 PleromaJobQueue.enqueue(:background, User, [
267 :follow_import,
268 follower,
269 followed_identifiers
270 ])
271
272 json(conn, "job started")
273 end
274 end
275
276 def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
277 blocks_import(conn, %{"list" => File.read!(listfile.path)})
278 end
279
280 def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
281 with blocked_identifiers <- String.split(list) do
282 PleromaJobQueue.enqueue(:background, User, [
283 :blocks_import,
284 blocker,
285 blocked_identifiers
286 ])
287
288 json(conn, "job started")
289 end
290 end
291
292 def change_password(%{assigns: %{user: user}} = conn, params) do
293 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
294 {:ok, user} ->
295 with {:ok, _user} <-
296 User.reset_password(user, %{
297 password: params["new_password"],
298 password_confirmation: params["new_password_confirmation"]
299 }) do
300 json(conn, %{status: "success"})
301 else
302 {:error, changeset} ->
303 {_, {error, _}} = Enum.at(changeset.errors, 0)
304 json(conn, %{error: "New password #{error}."})
305
306 _ ->
307 json(conn, %{error: "Unable to change password."})
308 end
309
310 {:error, msg} ->
311 json(conn, %{error: msg})
312 end
313 end
314
315 def delete_account(%{assigns: %{user: user}} = conn, params) do
316 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
317 {:ok, user} ->
318 User.delete(user)
319 json(conn, %{status: "success"})
320
321 {:error, msg} ->
322 json(conn, %{error: msg})
323 end
324 end
325
326 def disable_account(%{assigns: %{user: user}} = conn, params) do
327 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
328 {:ok, user} ->
329 User.deactivate_async(user)
330 json(conn, %{status: "success"})
331
332 {:error, msg} ->
333 json(conn, %{error: msg})
334 end
335 end
336
337 def captcha(conn, _params) do
338 json(conn, Pleroma.Captcha.new())
339 end
340
341 def healthcheck(conn, _params) do
342 with true <- Config.get([:instance, :healthcheck]),
343 %{healthy: true} = info <- Healthcheck.system_info() do
344 json(conn, info)
345 else
346 %{healthy: false} = info ->
347 service_unavailable(conn, info)
348
349 _ ->
350 service_unavailable(conn, %{})
351 end
352 end
353
354 defp service_unavailable(conn, info) do
355 conn
356 |> put_status(:service_unavailable)
357 |> json(info)
358 end
359 end