Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma 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 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 Emoji.get_all()
243 |> Enum.map(fn {short_code, path, tags} ->
244 {short_code, %{image_url: path, tags: tags}}
245 end)
246 |> Enum.into(%{})
247
248 json(conn, emoji)
249 end
250
251 def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
252 with {:ok, _} <- User.update_notification_settings(user, params) do
253 json(conn, %{status: "success"})
254 end
255 end
256
257 def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
258 follow_import(conn, %{"list" => File.read!(listfile.path)})
259 end
260
261 def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
262 with lines <- String.split(list, "\n"),
263 followed_identifiers <-
264 Enum.map(lines, fn line ->
265 String.split(line, ",") |> List.first()
266 end)
267 |> List.delete("Account address") do
268 PleromaJobQueue.enqueue(:background, User, [
269 :follow_import,
270 follower,
271 followed_identifiers
272 ])
273
274 json(conn, "job started")
275 end
276 end
277
278 def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
279 blocks_import(conn, %{"list" => File.read!(listfile.path)})
280 end
281
282 def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
283 with blocked_identifiers <- String.split(list) do
284 PleromaJobQueue.enqueue(:background, User, [
285 :blocks_import,
286 blocker,
287 blocked_identifiers
288 ])
289
290 json(conn, "job started")
291 end
292 end
293
294 def change_password(%{assigns: %{user: user}} = conn, params) do
295 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
296 {:ok, user} ->
297 with {:ok, _user} <-
298 User.reset_password(user, %{
299 password: params["new_password"],
300 password_confirmation: params["new_password_confirmation"]
301 }) do
302 json(conn, %{status: "success"})
303 else
304 {:error, changeset} ->
305 {_, {error, _}} = Enum.at(changeset.errors, 0)
306 json(conn, %{error: "New password #{error}."})
307
308 _ ->
309 json(conn, %{error: "Unable to change password."})
310 end
311
312 {:error, msg} ->
313 json(conn, %{error: msg})
314 end
315 end
316
317 def delete_account(%{assigns: %{user: user}} = conn, params) do
318 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
319 {:ok, user} ->
320 User.delete(user)
321 json(conn, %{status: "success"})
322
323 {:error, msg} ->
324 json(conn, %{error: msg})
325 end
326 end
327
328 def disable_account(%{assigns: %{user: user}} = conn, params) do
329 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
330 {:ok, user} ->
331 User.deactivate_async(user)
332 json(conn, %{status: "success"})
333
334 {:error, msg} ->
335 json(conn, %{error: msg})
336 end
337 end
338
339 def captcha(conn, _params) do
340 json(conn, Pleroma.Captcha.new())
341 end
342
343 def healthcheck(conn, _params) do
344 with true <- Config.get([:instance, :healthcheck]),
345 %{healthy: true} = info <- Healthcheck.system_info() do
346 json(conn, info)
347 else
348 %{healthy: false} = info ->
349 service_unavailable(conn, info)
350
351 _ ->
352 service_unavailable(conn, %{})
353 end
354 end
355
356 defp service_unavailable(conn, info) do
357 conn
358 |> put_status(:service_unavailable)
359 |> json(info)
360 end
361 end