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