Merge remote-tracking branch 'remotes/origin/develop' into 1560-non-federating-instan...
[akkoma] / lib / pleroma / web / twitter_api / controllers / util_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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.Config
11 alias Pleroma.Emoji
12 alias Pleroma.Healthcheck
13 alias Pleroma.Notification
14 alias Pleroma.Plugs.OAuthScopesPlug
15 alias Pleroma.User
16 alias Pleroma.Web
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.WebFinger
19
20 plug(Pleroma.Web.FederatingPlug when action == :remote_subscribe)
21
22 plug(
23 OAuthScopesPlug,
24 %{scopes: ["follow", "write:follows"]}
25 when action == :follow_import
26 )
27
28 # Note: follower can submit the form (with password auth) not being signed in (having no token)
29 plug(
30 OAuthScopesPlug,
31 %{fallback: :proceed_unauthenticated, scopes: ["follow", "write:follows"]}
32 when action == :do_remote_follow
33 )
34
35 plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks_import)
36
37 plug(
38 OAuthScopesPlug,
39 %{scopes: ["write:accounts"]}
40 when action in [
41 :change_email,
42 :change_password,
43 :delete_account,
44 :update_notificaton_settings,
45 :disable_account
46 ]
47 )
48
49 plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read)
50
51 plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version])
52
53 def help_test(conn, _params) do
54 json(conn, "ok")
55 end
56
57 def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
58 with %User{} = user <- User.get_cached_by_nickname(nick),
59 avatar = User.avatar_url(user) do
60 conn
61 |> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
62 else
63 _e ->
64 render(conn, "subscribe.html", %{
65 nickname: nick,
66 avatar: nil,
67 error: "Could not find user"
68 })
69 end
70 end
71
72 def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
73 with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
74 %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
75 conn
76 |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
77 else
78 _e ->
79 render(conn, "subscribe.html", %{
80 nickname: nick,
81 avatar: nil,
82 error: "Something went wrong."
83 })
84 end
85 end
86
87 def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
88 with {:ok, _} <- Notification.read_one(user, notification_id) do
89 json(conn, %{status: "success"})
90 else
91 {:error, message} ->
92 conn
93 |> put_resp_content_type("application/json")
94 |> send_resp(403, Jason.encode!(%{"error" => message}))
95 end
96 end
97
98 def config(%{assigns: %{format: "xml"}} = conn, _params) do
99 instance = Pleroma.Config.get(:instance)
100
101 response = """
102 <config>
103 <site>
104 <name>#{Keyword.get(instance, :name)}</name>
105 <site>#{Web.base_url()}</site>
106 <textlimit>#{Keyword.get(instance, :limit)}</textlimit>
107 <closed>#{!Keyword.get(instance, :registrations_open)}</closed>
108 </site>
109 </config>
110 """
111
112 conn
113 |> put_resp_content_type("application/xml")
114 |> send_resp(200, response)
115 end
116
117 def config(conn, _params) do
118 instance = Pleroma.Config.get(:instance)
119
120 vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
121
122 uploadlimit = %{
123 uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
124 avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
125 backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
126 bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
127 }
128
129 data = %{
130 name: Keyword.get(instance, :name),
131 description: Keyword.get(instance, :description),
132 server: Web.base_url(),
133 textlimit: to_string(Keyword.get(instance, :limit)),
134 uploadlimit: uploadlimit,
135 closed: bool_to_val(Keyword.get(instance, :registrations_open), "0", "1"),
136 private: bool_to_val(Keyword.get(instance, :public, true), "0", "1"),
137 vapidPublicKey: vapid_public_key,
138 accountActivationRequired:
139 bool_to_val(Keyword.get(instance, :account_activation_required, false)),
140 invitesEnabled: bool_to_val(Keyword.get(instance, :invites_enabled, false)),
141 safeDMMentionsEnabled: bool_to_val(Pleroma.Config.get([:instance, :safe_dm_mentions]))
142 }
143
144 managed_config = Keyword.get(instance, :managed_config)
145
146 data =
147 if managed_config do
148 pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
149 Map.put(data, "pleromafe", pleroma_fe)
150 else
151 data
152 end
153
154 json(conn, %{site: data})
155 end
156
157 defp bool_to_val(true), do: "1"
158 defp bool_to_val(_), do: "0"
159 defp bool_to_val(true, val, _), do: val
160 defp bool_to_val(_, _, val), do: val
161
162 def frontend_configurations(conn, _params) do
163 config =
164 Pleroma.Config.get(:frontend_configurations, %{})
165 |> Enum.into(%{})
166
167 json(conn, config)
168 end
169
170 def version(%{assigns: %{format: "xml"}} = conn, _params) do
171 version = Pleroma.Application.named_version()
172
173 conn
174 |> put_resp_content_type("application/xml")
175 |> send_resp(200, "<version>#{version}</version>")
176 end
177
178 def version(conn, _params) do
179 json(conn, Pleroma.Application.named_version())
180 end
181
182 def emoji(conn, _params) do
183 emoji =
184 Enum.reduce(Emoji.get_all(), %{}, fn {code, %Emoji{file: file, tags: tags}}, acc ->
185 Map.put(acc, code, %{image_url: file, tags: tags})
186 end)
187
188 json(conn, emoji)
189 end
190
191 def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
192 with {:ok, _} <- User.update_notification_settings(user, params) do
193 json(conn, %{status: "success"})
194 end
195 end
196
197 def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
198 follow_import(conn, %{"list" => File.read!(listfile.path)})
199 end
200
201 def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
202 with lines <- String.split(list, "\n"),
203 followed_identifiers <-
204 Enum.map(lines, fn line ->
205 String.split(line, ",") |> List.first()
206 end)
207 |> List.delete("Account address") do
208 User.follow_import(follower, followed_identifiers)
209 json(conn, "job started")
210 end
211 end
212
213 def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
214 blocks_import(conn, %{"list" => File.read!(listfile.path)})
215 end
216
217 def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
218 with blocked_identifiers <- String.split(list) do
219 User.blocks_import(blocker, blocked_identifiers)
220 json(conn, "job started")
221 end
222 end
223
224 def change_password(%{assigns: %{user: user}} = conn, params) do
225 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
226 {:ok, user} ->
227 with {:ok, _user} <-
228 User.reset_password(user, %{
229 password: params["new_password"],
230 password_confirmation: params["new_password_confirmation"]
231 }) do
232 json(conn, %{status: "success"})
233 else
234 {:error, changeset} ->
235 {_, {error, _}} = Enum.at(changeset.errors, 0)
236 json(conn, %{error: "New password #{error}."})
237
238 _ ->
239 json(conn, %{error: "Unable to change password."})
240 end
241
242 {:error, msg} ->
243 json(conn, %{error: msg})
244 end
245 end
246
247 def change_email(%{assigns: %{user: user}} = conn, params) do
248 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
249 {:ok, user} ->
250 with {:ok, _user} <- User.change_email(user, params["email"]) do
251 json(conn, %{status: "success"})
252 else
253 {:error, changeset} ->
254 {_, {error, _}} = Enum.at(changeset.errors, 0)
255 json(conn, %{error: "Email #{error}."})
256
257 _ ->
258 json(conn, %{error: "Unable to change email."})
259 end
260
261 {:error, msg} ->
262 json(conn, %{error: msg})
263 end
264 end
265
266 def delete_account(%{assigns: %{user: user}} = conn, params) do
267 password = params["password"] || ""
268
269 case CommonAPI.Utils.confirm_current_password(user, password) do
270 {:ok, user} ->
271 User.delete(user)
272 json(conn, %{status: "success"})
273
274 {:error, msg} ->
275 json(conn, %{error: msg})
276 end
277 end
278
279 def disable_account(%{assigns: %{user: user}} = conn, params) do
280 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
281 {:ok, user} ->
282 User.deactivate_async(user)
283 json(conn, %{status: "success"})
284
285 {:error, msg} ->
286 json(conn, %{error: msg})
287 end
288 end
289
290 def captcha(conn, _params) do
291 json(conn, Pleroma.Captcha.new())
292 end
293
294 def healthcheck(conn, _params) do
295 with true <- Config.get([:instance, :healthcheck]),
296 %{healthy: true} = info <- Healthcheck.system_info() do
297 json(conn, info)
298 else
299 %{healthy: false} = info ->
300 service_unavailable(conn, info)
301
302 _ ->
303 service_unavailable(conn, %{})
304 end
305 end
306
307 defp service_unavailable(conn, info) do
308 conn
309 |> put_status(:service_unavailable)
310 |> json(info)
311 end
312 end